Source: admin/javascript/engine/widgets/filemanager.js

/* --------------------------------------------------------------
 filemanager.js 2017-09-18
 Gambio GmbH
 http://www.gambio.de
 Copyright (c) 2017 Gambio GmbH
 Released under the GNU General Public License (Version 2)
 [http://www.gnu.org/licenses/gpl-2.0.html]
 --------------------------------------------------------------
 */

/**
 * ## Filemanager Widget
 *
 * Creates an input field and a button in order to make it possible to upload files or to select already
 * uploaded files. This widget checks if the responsive filemanager is present, if it is, the responsive
 * filemanager will be used, else a fallback will be used, which is an input field of type 'file'.
 *
 * ### Options
 *
 * **Type | `data-filemanager-type` | String | Optional**
 *
 * Provide the allowed file upload type. Currently there are 3 options which are:
 *    * 'all' - All file types are allowed
 *    * 'videos' - Only video files are allowed
 *    * 'images' - Only images are allowed
 * If you don't provide any type, the filemanager will default to 'all'.
 *
 * **Content Directory | `data-filemanager-content-directory` | String | Required**
 *
 * Provide the directory which should be opened when the filemanager gets opened, e.g. 'media'.
 * You can also provide a path from the root of your shop e.g 'images/slider_images'.
 *
 * **Name | `data-filemanager-name` | String | Required**
 *
 * The name of the input field. It will be set as the HTML name attribute.
 *
 * **Previous File | `data-filemanager-previous-file` | String | Optional**
 *
 * Name of the previous file. The name will be used in order to auto fill the input field.
 *
 * **Page | `data-filemanager-page` | String | Optional**
 *
 * The name of the current page in snake case, for example: new_category or responsive_filemanager.
 * This option will be used in order to load custom configuration files for the responsive file manager like
 * responsive_filemanager.config.php. These custom configuration files will be available or should be created
 * in the 'page' directory of the responsive file manager.
 *
 * **Page Active | `data-filemanager-page-active` | Boolean | Required**
 *
 * This option is required in order to check whether the file manager module is active, and if the configuration
 * option from the file manager is set to active, for the current page. If the module is not active, or active
 * in general but not active for the current page, the fallback will be used, which is a standard input field.
 *
 * ### Example
 *
 * ```html
 * <div data-gx-widget="filemanager"
 *     data-filemanager-name="categories_icon" // Required
 *     data-filemanager-type="images" // Optional
 *     data-filemanager-content-directory="images/categories/icons" // Required
 *     data-filemanager-previous-file="filename.extension" // Optional
 *     data-filemanager-page="responsive_filemanager"> // Optional
 *     data-filemanager-page-active="true" // Required
 * </div>
 * ```
 *
 * @module Admin/Widgets/filemanager
 */
gx.widgets.module(
	'filemanager',
	
	['xhr', 'modal'],
	
	/** @lends module:Widgets/filemanager */
	
	function (data) {
		
		'use strict';
		
		// ------------------------------------------------------------------------
		// VARIABLE DEFINITION
		// ------------------------------------------------------------------------
		
		/**
		 * Widget Reference
		 *
		 * @type {object}
		 */
		const $this = $(this);
		
		/**
		 * Default Widget Options
		 *
		 * @type {object}
		 */
		const defaults = {};
		
		/**
		 * Final Widget Options
		 *
		 * @type {object}
		 */
		const options = $.extend(true, {}, defaults, data);
		
		/**
		 * Module Object
		 *
		 * @type {object}
		 */
		const module = {};
		
		/**
		 * Id of the file manager input field.
		 *
		 * @type {string}
		 */
		let fieldId;
		
		/**
		 * Ajax request url fetch the file managers configuration settings.
		 *
		 * @type {string}
		 */
		const fileManagerConfigurationUrl = jse.core.config.get('appUrl')
		                            + '/admin/admin.php?do=ResponsiveFileManagerModuleCenterModule/GetConfiguration';
		
		/**
		 *  Cache key.
		 *
		 * @type {string}
		 */
		const cacheKey = 'responsiveFileManager';
		
		
		// ------------------------------------------------------------------------
		// FUNCTIONS
		// ------------------------------------------------------------------------
		
		// ------------------------------------------------------------------------
		// FILE MANAGER CONFIGURATIONS
		// ------------------------------------------------------------------------
		/**
		 * Returns the allowed file type as an integer, which is mapped
		 * for the external Responsive Filemanager plugin. It will be used
		 * as a GET parameter in the URL for the file manager.
		 *
		 * @returns {number} Flag integer value between 0 and 3.
		 */
		const _getFMType = () => {
			switch (options.type) {
				case 'images':
					return 1;
				case 'all':
					return 2;
				case 'videos':
					return 3;
				default:
					return 0;
			}
		};
		
		/**
		 * File managers request url.
		 *
		 * @returns {string} Request url of file manager.
		 */
		const _getFMUrl = () => {
			// Language parameter used for the file manager
			const lang = jse.core.registry.get('languageId') === 2 ? 'de' : 'en_EN';
			
			// Don't use the popup mode if the file manager will be opened in a modal.
			const popUp = _isCompatibilityModeEnabled() ? 1 : '';
			
			return jse.core.config.get('appUrl') + '/'
			       + 'ResponsiveFilemanager/filemanager/filemanager.php?type=' + _getFMType()
			       + _getSubDirectoryQueryString() + '&field_id=' + fieldId
			       + '&popup=' + popUp + '&relative_url=1&lang=' + lang
						 + _getPageQueryString();
		};
		
		/**
		 * Returns the 'sub_folder' query argument for the file manager request.
		 *
		 * @returns {string} Query parameter for file manager request to set the root directory.
		 */
		const _getSubDirectoryQueryString = () => {
			if (options.contentDirectory !== undefined) {
				return '&sub_folder=' + options.contentDirectory;
			}
			
			return '';
		};
		
		/**
		 * Returns the 'page' query string for the file manager request.
		 *
		 * @returns {string} Query parameter for the file manager request to load a custom configuration file.
		 */
		const _getPageQueryString = () => {
			if (options.page !== undefined)	 {
				return '&page=' + options.page;
			}
			
			return '';
		};
		
		/**
		 * Generates a global unique identifier for each input that is generated by this widget.
		 * This ID will be used in order to identify an input fields. With the help of this ID,
		 * the widget knows, in which input field the file name of the chose file should be entered.
		 *
		 * @returns {string} Global unique identifier as string.
		 */
		const guidGenerator = () => {
			const s4 = () => (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
			
			return (s4() + s4() + "-" + s4() + "-" + s4() + "-" + s4() + "-" + s4() + s4() + s4());
		};
		
		// ------------------------------------------------------------------------
		// CREATING THE FILE MANAGER
		// ------------------------------------------------------------------------
		/**
		 * Factory, which creates either the responsive file manager or the fallback,
		 * which is a standard input field of the type 'file'.
		 *
		 * @type {{responsive: (function()), fallback: (function())}}
		 */
		const factory = {
			responsive: () => {
				const $icon = $('<i/>', {
					'class': 'fa fa-upload',
					'aria-hidden': true
				});
				
				const $input = $('<input/>', {
					'type': 'text',
					'name': options.name,
					'id': fieldId,
					'class': 'form-control',
					'readonly': 'readonly'
				});
				
				// Auto fill the input field with the previous file name
				if (options.previousFile) {
					$input.val(options.previousFile);
				}
				
				const $btn = $('<a/>', {
					'class': 'btn responsive-file-manager',
					'type': 'button',
					'html': $icon,
					'on': {
						'click': () => _openFileManager()
					}
				});
				
				const $span = $('<span/>', {
					'class': 'input-group-btn'
				});
				
				const $container = $('<div/>', {
					'class': 'input-group responsive-file-manager'
				});
				
				$span.append($btn);
				$container.append($input).append($span);
				$this.append($container);
			},
			
			fallback: () => {
				const $input = $('<input/>', {
					'name': options.name,
					'type': 'file'
				});
				
				$this.append($input);
			}
		};
		
		/**
		/**
		 * Creates the widget after the request the responsive file manager
		 * request is being made. After the request, either the 'responsive'
		 * widget is created or the fallback, depending on if the file manager
		 * is available.
		 *
		 * @param done Done callback function for module.init.
		 */
		const _createWidget = done => {
			jse.libs.xhr.get({url: fileManagerConfigurationUrl})
				.done(response => jse.core.registry.set(cacheKey, response.isInstalled ? 'responsive' : 'fallback'))
				.fail(() => jse.core.registry.set(cacheKey, 'fallback'))
				.always(() => {
					// Create the file manager or fallback.
					factory[jse.core.registry.get(cacheKey)]();
					done();
				});
		};
		
		/**
		 * Creates the widget when the cache key changes from pending.
		 * After the cache key changed to either the 'responsive' or 'fallback',
		 * the according widget will be created, depending on if the file manager
		 * is available.
		 *
		 * @param done Done callback function for module.init.
		 */
		const _createWidgetWhenCacheKeyAvailable = done => {
			const interval = setInterval(() => {
				if (jse.core.registry.get(cacheKey) !== 'pending') {
					clearInterval(interval);
					
					// Create the file manager or fallback.
					factory[jse.core.registry.get(cacheKey)]();
					done();
				}
			}, 100);
		};
		
		// ------------------------------------------------------------------------
		// OPENING THE FILE MANAGER
		// ------------------------------------------------------------------------
		/**
		 * Opens the file manager in a new window popup.
		 */
		const _openFMPopup = () => {
			const w = 880;
			const h = 570;
			const l = Math.floor((screen.width - w) / 2);
			const t = Math.floor((screen.height - h) / 2);
			
			window.open(_getFMUrl(), 'ResponsiveFilemanager', "scrollbars=1,width=" + w + ",height=" + h
			                                                  + ",top="
			                                                  + t + ",left=" + l);
		};
		
		/**
		 * Opens the file manager in a bootstrap modal.
		 */
		const _openFMModal = () => {
			
			// Use the fallback if bootstraps modal function is not available.
			if ($.fn.modal === undefined) {
				return _openFMPopup();
			}
			
			const iFrame = `<iframe src="${_getFMUrl()}" width="100%" height="550" frameborder="0"
															class="responsive-filemanager"></iframe>`;
			
			jse.libs.modal.showMessage('Filemanager', iFrame);
			_makeModalLarge();
		};
		
		/**
		 * Makes the modal large by adding the modal-lg css class.
		 */
		const _makeModalLarge = () => {
			$('.modal-dialog').addClass('modal-lg');
		}
		
		/**
		 * Checks if compatibility mode is active.
		 *
		 * @returns {boolean} True on compatibility mode, false otherwise.
		 */
		const _isCompatibilityModeEnabled = () => {
			return $('body.gx-compatibility').length !== 0;
		};
		
		/**
		 * Opens the file manager in a modal, dialog or window with the priority in
		 * the same order. If bootstrap is not available, the file
		 * manager will be opened in a new window.
		 */
		const _openFileManager = () => {
			if (_isCompatibilityModeEnabled()) {
				return _openFMPopup();
			}
			
			_openFMModal();
		};
		
		
		// ------------------------------------------------------------------------
		// INITIALIZATION
		// ------------------------------------------------------------------------
		
		/**
		 * Initialize method of the widget, called by the engine.
		 */
		module.init = done => {
			fieldId = guidGenerator();
			
			// Required option not provided
			if (options.contentDirectory === undefined || options.contentDirectory === '') {
				jse.core.debug.error('Content directory was not provided for the "filemanager" widget.');
				return;
			}
			
			// Required option not provided
			if (options.name === undefined || options.name === '') {
				jse.core.debug.error('Name attribute was not provided for the "filemanager" widget.');
				return;
			}
			
			// Required option not provided
			if (options.pageActive === undefined) {
				jse.core.debug.error('page-active attribute was not provided for the "filemanager" widget.');
				return;
			}
			
			// Module is not active at all or not active for the used page.
			if (!options.pageActive) {
				factory.fallback();
				done();
				return;
			}
			
			// No cache key available yet. Create the widget and set the cache key to 'fallback' or 'responsive'
			// after the responsive has arrived (done by the _createWidget function).
			if (jse.core.registry.get(cacheKey) === undefined) {
				jse.core.registry.set(cacheKey, 'pending');
				_createWidget(done);
				return;
			}
			
			// Cache key is on 'pending' which means we have to wait until the key changes (done by the _createWidget function).
			// Afterwards we can create the correct widget.
			if (jse.core.registry.get(cacheKey) === 'pending') {
				_createWidgetWhenCacheKeyAvailable(done);
				return;
			}
			
			// Build the fallback or responsive file manager.
			factory[jse.core.registry.get(cacheKey)]();
		};

		// Return data to module engine.
		return module;
	});