Загрузка файлов в Яндекс Облако на Joomla Cobalt

Author Автор: Роман Чернышов    Опубликовано: 16 февраля 2020

Яндекс Облако и Joomla CobaltДобрый день друзья! Сегодня я хочу рассказать о доработке компонента Cobalt(конструктор контента) для Joomla, а именно добавление возможности загрузки файлов в Яндекс Облако(Yandex Object Storage) и удаления при необходимости. Загрузка файлов в облако позволяет существенно сэкономить место на хостинге и как следствие немало денежных средств, ведь место в облаке стоит гораздо дешевле. Для загрузки файлов используется JS скрипт MooUpload. При загрузке файлов обеспечивается возможность выбора типа хранилища в облаке: холодное(Cold) или горячее(Standart). Кейс также интересен тем, что по условиям задачи, требуется минимальное внесение изменений в файлы компонента Cobalt, это необходимо для дальнейшего его обновления без риска потери этих изменений.

Настройка полей в Cobalt

Для настройки, переходим в соответствующий раздел: Админпанель -> Настройки -> Кобальт -> Типы статей. Выбираем нужный тип статьи, где мы планируем добавить возможность использования функции загрузки файлов в Облако. Создаем четыре поля, два поля нам будет необходимы для загрузки изображений, другие два для всех остальных файлов.

Типы создаваемых полей:

В полях Textarea, будут храниться пути загруженных файлов(массив в формате JSON, преобразованный в строку) на Yandex Storage. При этом сами поля будут скрыты от пользователей. Рядом с ними, с помощью JS, будут выведена форма для загрузки файлов(загрузчик MooUpload).

Для загрузки изображений и выбора типа хранилища.
1) Название — Загрузка изображений (jpeg, jpg, png, gif). Тип поля Simple Form Eliments -> Textarea. Класс стилей CSS — yandexImg.
2) Название — Тип хранилища, для изображений. Тип поля Simple Form Eliments -> Select. Класс стилей CSS — yandexTypeImg. Список значений — Холодный. Первый элемент выбора — Горячий.

Для загрузки файлов и выбора типа хранилища.
3) Название — Загрузка файлов (zip, rar). Тип поля Simple Form Eliments -> Textarea. Класс стилей CSS — yandexFile.
4) Название — Тип хранилища, для файлов. Тип поля Simple Form Eliments -> Select. Класс стилей CSS — yandexTypeFile. Список значений — Холодный. Первый элемент выбора — Горячий.

Типы создаваемых полей

Доработка MooUpload

В JS скрипт загрузчика MooUpload version: 1.1, который идет в стандартной поставке Joomla Cobalt, вносим небольшие изменения.

Расположение ./media/mint/js/mooupload/MooUpload.js

Метод populateFileList, после строки номер  544(может отличаться в зависимости от версии скрипта), добавляем код с обработкой нового статуса ответа со значением «3» при удалении файла(так как MooUpload будет слать загружаемые файлы на наш новый серверный скрипт на PHP).

Код который нужно добавить:

if(json.success == 3) {
  $(json.id).slide('out');
  setTimeout(function() {
    $(json.id).destroy();
  }, 500);
  this.filelist[j].checked = false;
  this.fireEvent('onFileDelete', ['0', file.filename]);
}

Листинг всего метода:

	populateFileList: function(maincontainer) {
		var subcontainer = document.id(maincontainer.get('id') + '_listView').getElement('ul');
		var maincontainer_id = maincontainer.get('id');
		var options = this.options;
 
		var size = 0, key;
		for(key in this.options.files) {
			if(this.options.files.hasOwnProperty(key)) size++;
		}
 
		if(!size) {
			return;
		}
 
		if(this.options.maxfiles) {
			this.filesCount = size;
		}
		for(var i = 0, file = null; file = this.options.files[i]; i++) {
			this.filelist[i] = {
				id: String.uniqueID(),
				checked: true,
				name: file.filename,
				type: file.ext,
				size: file.size,
				uploaded: true,
				uploading: false,
				error: false
			};
			this.filenum++;
 
			var liid = file.filename.toLowerCase();
			liid = liid.replace('.' + file.ext.toLowerCase(), '');
 
			var elementcontainer = new Element('li', {
				'class': 'item mooupload_readonly',
				id: liid
			}).inject(subcontainer);
 
			var hiddenInput = new Element('input', {
				'type': 'hidden',
				'name': this.options.formname,
				'value': file.filename
			}).inject(elementcontainer);
 
			var optionsel = new Element('div', {
				'class': 'optionsel'
			}).inject(elementcontainer);
 
			var f = file;
 
			if(this.options.canDelete) {
				var optionremove = new Element('a', {
					'class': 'remove'
				}).inject(optionsel);
 
				var func = function(file, j) {
					if(!confirm(this.options.texts.sure)) {
						return;
					}
					$$('#' + file.filename.replace('.' + file.ext, '') + ' div.result').set('html', this.options.texts.deleting).setStyle('background', 'url( "' + this.options.url_root + '/media/mint/js/mooupload/imgs/load_bg_red.gif")').setStyle('color', 'maroon');
 
					var req = new Request.JSON({
						url: this.options.action_remove_file,
						method: 'post',
						autoCancel: true,
						data: { filename: file.filename },
						onComplete: function(json) {
							//console.log(json);
							if(json.success == 1) {
								$(json.id).slide('out');
								setTimeout(function() {
									$(json.id).destroy();
								}, 500);
								this.filelist[j].checked = false;
							}
							if(json.success == 0) {
								this.fireEvent('onFileDelete', ['1016', file.filename]);
							}
							if(json.success == 2) {
								this.fireEvent('onFileDelete', ['1017', file.filename]);
							}
 
							if(json.success == 3) {
								$(json.id).slide('out');
								setTimeout(function() {
									$(json.id).destroy();
								}, 500);
								this.filelist[j].checked = false;
 
								this.fireEvent('onFileDelete', ['0', file.filename]);
							}
 
						}.bind(this)
					}).send();
				};
 
				optionremove.addEvent('click', func.pass([file, i], this));
			}
 
			var title = file.realname;
 
			if(this.options.allowEditTitle && file.title) {
				title = file.title;
			}
 
			var css_class = 'filename';
			if(this.options.allowEditTitle) {
				css_class = 'filename  filenameedit';
			}
 
			var filename_div = new Element('div', {
				'rel': f.id,
				'id': maincontainer.get('id') + '_file' + i,
				'class': css_class,
				'title': this.options.texts.edit_title,
				html: title,
				styles: {
					width: '55%'
//			        width: this.namewidth + 'px',
				}
			}).inject(elementcontainer);
 
			if(this.options.allowAddDescr) {
				this.addDescriptionInterface(elementcontainer, f.id, f.description);
			}
 
			if(this.options.allowEditTitle) {
				this.addTitleInterface(filename_div, filename_div.get('rel'));
			}
 
			new Element('div', {
				'class': 'filesize',
				html: this.formatSize(file.size)
			}).inject(elementcontainer);
 
			new Element('div', {
				id: maincontainer_id + '_file_' + i,
				'class': 'result',
				'html': this.options.texts.uploaded
			}).inject(elementcontainer);
 
			elementcontainer.highlight('#FFF', '#E3E3E3');
		}
	},

Вывод формы загрузки файлов на страницу сайта

Загрузка файлов в MooUpload

Для того, чтобы модифицировать ранее добавленные поля Textarea и вывести на странице, для пользователя, функционал загрузки файлов с использованием MooUpload, в шаблон движка Joomla, в блок HEAD добавляем строку:

<script src="/templates/sitetheme/js/yandex.storage.js"></script>

Содержание файла yandex.storage.js:

jQuery(document).ready(function($) {
 
	$('.yandexFile .controls').hide(); // текстовое поле, сюда сохраняется результат загрузки
	$('.yandexFile .controls').after('<div class="controls" id="yandexFileMooUpload"></div><p class="controls"><span class="small">Разрешенные типы файлов: zip, rar</p></p>')
 
	var filesdata = $('.yandexFile textarea').val();
	if(filesdata) filesdata = JSON.parse(filesdata); else filesdata = [];
 
	var Newfilesdata = [];
 
	$(filesdata).each(function(i, v) {
	        var obj = {};
		obj.id = i;
		obj.filename = v.upload_name;
		obj.size = v.size;
		obj.realname = v.name;
		obj.fullpath = 'files/' + v.upload_name;
		obj.title = null;
		obj.description =null; 
		obj.width = 0; 
		obj.height = 0;
 
		var ext = v.upload_name.split('.');
                obj.ext = ext[ext.length - 1];
 
                Newfilesdata.push(obj);
	});
 
	if($('#yandexFileMooUpload').length > 0) {
 
				var myUpload = new MooUpload('yandexFileMooUpload', {
					action: '/storage/yandex.php?action=upload&type=files',
					action_remove_file: '/storage/yandex.php?action=delete',
					method: 'auto',
					tempname: 'ya.file',
					files: Newfilesdata,
					formname:'jform[fields][ya.file][]',
					autostart:1,
					field_id:16,
    	    				record_id:0,
					maxfilesize: 1048576,
					exts: ['zip','rar'],
					maxfiles: 0,
					canDelete: 1,
					allowEditTitle: 0,
					allowAddDescr: 0,
					url_root: '',
					flash: {
				      movie: '/media/mint/js//Moo.Uploader.swf'
				    },
				    texts: {
					    error      : 'Ошибка',
					    file       : 'Файл',
					    filesize   : 'Размер файла',
					    filetype   : 'Тип файла',
					    nohtml5    : 'Нет поддержки загрузки файлов HTML5!',
					    noflash    : 'Пожалуйста, установите Flash 8.5 или более новой версии (У вас отключен FlashBlock или AdBlock?)',
					    sel        : 'Действие',
					    selectfile : 'Добавить файлы',
					    status     : 'Статус',
					    startupload: 'Начать загрузку',
					    uploaded   : 'Загружено',
					    sure	   : 'Вы уверены, что хотите удалить?',
					    edit_descr : 'Редактировать описание',
					    edit_title : 'Редактировать заголовок',
					    deleting   : 'Удаление'
				    },
 
				    onFileDelete: function( error, filename){ //
				    	        console.log(filename);
 
						var filesdata = $('.yandexFile textarea').val();
						if(filesdata) filesdata = JSON.parse(filesdata); else filesdata = [];
 
						var Newfilesdata = [];
 
						$(filesdata).each(function(i, v) {
 
							if(filename != v.upload_name) Newfilesdata.push(v);
 
 
						});
 
                                    	        filesdata = JSON.stringify(Newfilesdata);
                                                $('.yandexFile textarea').val(filesdata);
 
 
					},
 
                                    onFileUpload: function(error, filename){
 
						var filesdata = $('.yandexFile textarea').val();
						if(filesdata) filesdata = JSON.parse(filesdata); else filesdata = [];
                                                filesdata.push(filename);
                                    	        filesdata = JSON.stringify(filesdata);
                                                $('.yandexFile textarea').val(filesdata);
 
				    }
 
			});
 
	}
 
 
	if($('.tab-content dd.yandexFile').length > 0) {
 
		var filesdata = $('.tab-content dd.yandexFile').text().trim();
		if(filesdata) filesdata = JSON.parse(filesdata); else filesdata = [];
 
		if(filesdata.length > 0) {
 
	                $('.tab-content dd.yandexFile').html('<ul class="unstyled"></ul>');
 
			$(filesdata).each(function(i, v) {
 
		                $('.tab-content .yandexFile ul').append('<li><a target="_blank" href="https://storage.yandexcloud.net/farming-ls19/files/' + 
					decodeURI(v.upload_name) + '" rel="noopener noreferrer">' + decodeURI(v.name) + '</a> <small>Размер: <span style="color:green">' + v.size + ' Bytes</span></small></li>');
 
			});
		}
 
	}	
 
 
});
 
 
jQuery(document).ready(function($) {
 
	$('.yandexImg .controls').hide(); // текстовое поле, сюда сохраняется результат загрузки
	$('.yandexImg .controls').after('<div class="controls" id="yandexImgMooUpload"></div><p class="controls"><span class="small">Разрешенные типы файлов: jpg, png, gif</p></p>')
 
	var filesdata = $('.yandexImg textarea').val();
	if(filesdata) filesdata = JSON.parse(filesdata); else filesdata = [];
 
	var Newfilesdata = [];
 
	$(filesdata).each(function(i, v) {
	        var obj = {};
		obj.id = i;
		obj.filename = v.upload_name;
		obj.size = v.size;
		obj.realname = v.name;
		obj.fullpath = 'files/' + v.upload_name;
		obj.title = null;
		obj.description =null; 
		obj.width = 0; 
		obj.height = 0;
 
		var ext = v.upload_name.split('.');
                obj.ext = ext[ext.length - 1];
 
                Newfilesdata.push(obj);
	});
 
	if($('#yandexImgMooUpload').length > 0) {
 
				var myUpload = new MooUpload('yandexImgMooUpload', {
					action: '/storage/yandex.php?action=upload&type=images',
					action_remove_file: '/storage/yandex.php?action=delete',
					method: 'auto',
					tempname: 'ya.image',
					files: Newfilesdata,
					formname:'jform[fields][ya.images][]',
					autostart:1,
					field_id:16,
    	    				record_id:0,
					maxfilesize: 1048576,
					exts: ['jpg','jpeg','png','gif'],
					maxfiles: 0,
					canDelete: 1,
					allowEditTitle: 0,
					allowAddDescr: 0,
					url_root: '',
					flash: {
				      movie: '/media/mint/js//Moo.Uploader.swf'
				    },
				    texts: {
					    error      : 'Ошибка',
					    file       : 'Файл',
					    filesize   : 'Размер файла',
					    filetype   : 'Тип файла',
					    nohtml5    : 'Нет поддержки загрузки файлов HTML5!',
					    noflash    : 'Пожалуйста, установите Flash 8.5 или более новой версии (У вас отключен FlashBlock или AdBlock?)',
					    sel        : 'Действие',
					    selectfile : 'Добавить файлы',
					    status     : 'Статус',
					    startupload: 'Начать загрузку',
					    uploaded   : 'Загружено',
					    sure	   : 'Вы уверены, что хотите удалить?',
					    edit_descr : 'Редактировать описание',
					    edit_title : 'Редактировать заголовок',
					    deleting   : 'Удаление'
				    },
 
				    onFileDelete: function( error, filename){ //
				    	        console.log(filename);
 
						var filesdata = $('.yandexImg textarea').val();
						if(filesdata) filesdata = JSON.parse(filesdata); else filesdata = [];
 
						var Newfilesdata = [];
 
						$(filesdata).each(function(i, v) {
 
							if(filename != v.upload_name) Newfilesdata.push(v);
 
 
						});
 
                                    	        filesdata = JSON.stringify(Newfilesdata);
                                                $('.yandexImg textarea').val(filesdata);
 
 
					},
 
                                    onFileUpload: function(error, filename){
 
						var filesdata = $('.yandexImg textarea').val();
						if(filesdata) filesdata = JSON.parse(filesdata); else filesdata = [];
                                                filesdata.push(filename);
                                    	        filesdata = JSON.stringify(filesdata);
                                                $('.yandexImg textarea').val(filesdata);
 
				    }
			});
 
	}
 
 
	if($('.tab-content dd.yandexImg').length > 0) {
 
		var filesdata = $('.tab-content dd.yandexImg').text().trim();
		if(filesdata) filesdata = JSON.parse(filesdata); else filesdata = [];
 
		if(filesdata.length > 0) {
 
	                //$('.tab-content dd.yandexImg').html('<ul class="unstyled"></ul>');
			$('.tab-content dd.yandexImg').html('');
 
			$(filesdata).each(function(i, v) {
		                $('.tab-content dd.yandexImg').append('<a target="_blank" href="https://storage.yandexcloud.net/farming-ls19/images/' + decodeURI(v.upload_name) + '" rel="noopener noreferrer"><img src="https://storage.yandexcloud.net/farming-ls19/images/' + decodeURI(v.upload_name) + '" width="150" alt="' + 
					decodeURI(v.name) + ' Размер: ' + v.size + ' Bytes" /></a>');
 
			});
		}
 
	}
});

Обработчик файлов на PHP

Для загрузки файлов с использованием MooUpload, нужна своя серверная часть, новый скрипт на PHP для приема загружаемых файлов. При этом загрузка файлов происходит частями, скрипт MooUpload делит загружаемый файл на куски и отправляет данные частями. По этому PHP скрипт должен поддерживать такую загрузку, прием данных частями и их последующую склейку. По итогу загрузки скрипт на PHP будет загружать файл целиком в Яндекс Облако(Yandex Storage). Также PHP скрипт должен поддерживать запросы от MooUpload на удаление файлов с Яндекс Облака.

Пример структуры PHP скрипта ./stotage/yandex.php (из листинга убран код работы с входящими переменными и базой данных):

use Aws\S3\S3Client;
 
// работа с Yandex Storage
 
if($_GET['action'] == 'upload') {
   // Загрузка файлов в Облако
 
			// заливаем на Storage
 
			$sharedConfig = [
			  'credentials' => [
			    'key'      => $registry['ya.store']['key'],
			    'secret'   => $registry['ya.store']['secret'],
			  ],
			  'region'   => 'us-east-1',
			  'endpoint' => 'https://storage.yandexcloud.net',
			  'version'  => 'latest',
			];
 
			$sdk = new Aws\Sdk($sharedConfig);
			$s3Client = $sdk->createS3();
 
			    $s3Client->putObject([
			        'Bucket' => $registry['ya.store']['basket'],
			        'Key'    => $fileData['type'] . '/' . $fileData['uname'],
			        'Body'   => $fileData['body'],
			        'StorageClass'   => (($_GET['class1']&&$_GET['type']=='files') || ($_GET['class2']&&$_GET['type']=='images')?'COLD':'STANDARD'),
			    ]);
 
}
 
if($_GET['action'] == 'delete') {
   // Удаление файлов из Облака
 
		// удаляем со Storage
 
		$sharedConfig = [
		  'credentials' => [
		    'key'      => $registry['ya.store']['key'],
		    'secret'   => $registry['ya.store']['secret'],
		  ],
		  'region'   => 'us-east-1',
		  'endpoint' => 'https://storage.yandexcloud.net',
		  'version'  => 'latest',
		];
 
		$sdk = new Aws\Sdk($sharedConfig);
		$s3Client = $sdk->createS3();
 
		try {
		    $s3Client->deleteObject([
		        'Bucket' => $registry['ya.store']['basket'],
		        'Key'    => $testFile[0]['storage_path'] . '/' . $testFile[0]['upload_name'],
		    ]);
		} catch (Aws\S3\Exception\S3Exception $e) {
 
		}
 
}
 
<h2>База данных</h2>
 
Для учета загруженных файлов используется своя таблица в базе данных, данные в которую заносятся скриптом ./stotage/yandex.php (в листинге PHP скрипта, строки для работы с таблицей БД убраны). Хранение в базе данных не обязательно, эти данные мы храним для себя и не пользуемся таблицей для вывода загруженных файлов на страницах статей доступных пользователям, т.к. для этого потребовалось бы модифицировать файлы компонента Cobalt(чего мы хотим избежать), в частности для получения данных из БД.
 
<pre lang="SQL" colla="-">
CREATE TABLE `yandex_storage` (
  `id` int(11) NOT NULL,
  `file_id` varchar(50) NOT NULL,
  `file_name` varchar(200) NOT NULL,
  `file_size` int(11) NOT NULL,
  `upload_name` varchar(200) NOT NULL,
  `date` int(11) NOT NULL,
  `storage_path` varchar(1000) NOT NULL,
  `status` int(11) NOT NULL COMMENT '0 - процесс, 1 - ок'
) ENGINE=InnoDB DEFAULT CHARSET=cp1251 COMMENT='Файлы загруженные на Яндекс Облако';

Вывод загруженных файлов на странице с контентом

Так как данные о загруженных файлах, хранятся в полях Textarea(а БД для получения мы не используем, по выше описанной причине) в формате JSON в массиве преобразованном в строку, все что нужно нам сделать, это получить эти данные с помощью jQuery и преобразовать в HTML код для отображения на странице в удобном для пользователя виде(сами изображения и ссылки на скачивания файлов), работа с выводом данных происходит в файле ./templates/sitetheme/js/yandex.storage.js листинг которого преставлен выше.

Так выглядят данные о загруженных файлах в формате JSON, хранящиеся в полях Teatarea:
данные о загруженных файлах в формате JSON
Так выглядят уже преобразованные данные в HTML:
преобразованные данные в HTML

Заключение

Условие запрета на изменения файлов компонента Cobalt потребовало отказаться от внесения доработок непосредственно в сам компонент, благодаря чему можно было бы расширить функционал компонента и обойтись без создания дополнительных внешних скриптов на JS, в том числе не потребовалось бы вносить изменения в файл скрипта MooUpload. Решение получилось не однозначным, но мне удалось с ним справиться и достичь поставленной задачи. Более того, такой внешний скрипт можно интегрировать в любой сайт независимо от CMS на которой он работает.

Если вам требуется помощь в доработке Cobalt или интеграция сайта в сервисами Яндекса, обращайтесь — буду рад помочь!

Оставить комментарий

Автор блога
Чернышов Роман
Роман Чернышов
Веб-разработчик, Full Stack
Senior, Architector
PHP, JavaScript, Node.JS, Python, HTML 5, CSS 3, MySQL, Bash, Linux Admin
Заказать работу
предложить оффер

Последние вопросы
Список вопросов
Последние комментарии
Меню

Archive

Мои проекты
Insurance CMS Love Crm CMS Совместные покупки Мой PHP Framework Хостинг для моих клиентов Лицензии на мой софт и поддержка