/** * Nextcloud - Gallery * * * This file is licensed under the Affero General Public License version 3 or * later. See the COPYING file. * * @author Olivier Paroz * * @copyright Olivier Paroz 2017 */ /* global DOMPurify, oc_requesttoken, Gallery */ // The Utility class can also be loaded by the Files app window.Gallery = window.Gallery || {}; (function ($, OC, t, oc_requesttoken, Gallery) { "use strict"; /** * Contains utility methods * * @fixme OC.generateUrl, OC.buildQueryString, OC.Notification are private APIs * * @constructor */ var Utility = function () { }; Utility.prototype = { /** * Detects if the browser is a recent or an old version of Internet Explorer * * @returns {string|boolean} */ getIeVersion: function () { // Blocking IE8 if ($('html').is('.ie8')) { return 'unsupportedIe'; } else if (navigator.userAgent.indexOf("MSIE") > 0) { return 'unsupportedIe'; } else if (!!navigator.userAgent.match(/Trident.*rv[ :]*11\./)) { return 'modernIe'; } else if (navigator.userAgent.indexOf("Edge/") > 0) { return 'edge'; } return false; }, /** * Shows a notification to IE users, letting them know that they should use another browser * in order to get the best experience * * @param {string} version */ showIeWarning: function (version) { var line1 = t('gallery', 'This application may not work properly on your browser.'); var line2 = t('gallery', 'For an improved experience, please install one of the following alternatives'); var timeout = 15; if (version === 'unsupportedIe') { line1 = t('gallery', 'Your browser is not supported!'); line2 = t('gallery', 'please install one of the following alternatives'); timeout = 60; } var recommendedBrowsers = '
' + 'Mozilla Firefox or ' + 'Google Chrome' + '
'; var text = '' + line1 + '
' + line2 + recommendedBrowsers; this.showHtmlNotification(text, timeout); }, /** * Shows a notification at the top of the screen * * @param {string} text * @param {int} timeout */ showHtmlNotification: function (text, timeout) { var options = { timeout: timeout, isHTML: true }; OC.Notification.showTemporary(text, options); }, /** * Returns the token allowing access to files shared via link * * @returns {string} */ getPublicToken: function () { var element = $('#gallery'); var token; if (element.data('token')) { token = element.data('token'); } if (element.data('requesttoken')) { /* jshint camelcase: false */ oc_requesttoken = element.data('requesttoken'); } return token; }, /** * Returns the host we can use for WebDAV * * On public galleries, we need to provide the token as authorization * * @returns {string} */ getWebdavHost: function () { var host = OC.getHost(); if (Gallery.token) { host = Gallery.token + '@' + host; } return host; }, /** * Returns the WebDAV endpoint we can use for files operations * * @returns {string} */ getWebdavRoot: function () { var root = OC.linkToRemoteBase('webdav'); if (Gallery.token) { root = root.replace('remote.php', 'public.php'); } return root; }, /** * Builds the URL which will retrieve a large preview of the file * * @fixme we cannot get rid of oc_requesttoken parameter as it's missing from the headers * * @param {number} fileId * @param {number} etag * * @return {string} */ getPreviewUrl: function (fileId, etag) { var width = Math.ceil(screen.width * window.devicePixelRatio); var height = Math.ceil(screen.height * window.devicePixelRatio); /* Find value of longest edge. */ var longEdge = Math.max(width, height); /* Find the next larger image size. */ if (longEdge % 100 !== 0) { longEdge = ( longEdge + 100 ) - ( longEdge % 100 ); } /* jshint camelcase: false */ var params = { width: longEdge, height: longEdge, }; return this.buildGalleryUrl('preview', '/' + fileId, params); }, /** * Builds a URL pointing to one of the app's controllers * * @param {string} endPoint * @param {undefined|string} path * @param params * * @returns {string} */ buildGalleryUrl: function (endPoint, path, params) { if (path === undefined) { path = ''; } var extension = ''; if (Gallery.token) { params.token = Gallery.token; extension = '.public'; } var query = OC.buildQueryString(params); return OC.generateUrl('apps/' + Gallery.appName + '/' + endPoint + extension + path, null) + '?' + query; }, /** * Builds a URL pointing to one of the files' controllers * * @param {string} path * @param {string} files * * @returns {string} */ buildFilesUrl: function (path, files) { var subUrl = ''; var params = { path: path, files: files }; if (Gallery.token) { params.token = Gallery.token; subUrl = 's/{token}/download?dir={path}&files={files}'; } else { subUrl = 'apps/files/ajax/download.php?dir={path}&files={files}'; } return OC.generateUrl(subUrl, params); }, /** * Sorts arrays based on name or date * * @param {string} sortType * @param {string} sortOrder * * @returns {Function} */ sortBy: function (sortType, sortOrder) { if (sortType === 'name') { if (sortOrder === 'asc') { //sortByNameAsc return function (a, b) { return OC.Util.naturalSortCompare(a.path, b.path); }; } //sortByNameDes return function (a, b) { return -OC.Util.naturalSortCompare(a.path, b.path); }; } if (sortType === 'date') { if (sortOrder === 'asc') { //sortByDateAsc return function (a, b) { return b.mTime - a.mTime; }; } //sortByDateDes return function (a, b) { return a.mTime - b.mTime; }; } }, /** * Adds hooks to DOMPurify */ addDomPurifyHooks: function () { // allowed URI schemes var whitelist = ['http', 'https']; // build fitting regex var regex = new RegExp('^(' + whitelist.join('|') + '):', 'gim'); DOMPurify.addHook('afterSanitizeAttributes', function (node) { // This hook enforces URI scheme whitelist // @link // https://github.com/cure53/DOMPurify/blob/master/demos/hooks-scheme-whitelist.html // build an anchor to map URLs to var anchor = document.createElement('a'); // check all href attributes for validity if (node.hasAttribute('href')) { anchor.href = node.getAttribute('href'); if (anchor.protocol && !anchor.protocol.match(regex)) { node.removeAttribute('href'); } } // check all action attributes for validity if (node.hasAttribute('action')) { anchor.href = node.getAttribute('action'); if (anchor.protocol && !anchor.protocol.match(regex)) { node.removeAttribute('action'); } } // check all xlink:href attributes for validity if (node.hasAttribute('xlink:href')) { anchor.href = node.getAttribute('xlink:href'); if (anchor.protocol && !anchor.protocol.match(regex)) { node.removeAttribute('xlink:href'); } } // This hook restores the proper, standard namespace in SVG files var encodedXmlns, decodedXmlns; // Restores namespaces which were put in the DOCTYPE by Illustrator if (node.hasAttribute('xmlns') && node.getAttribute('xmlns') === '&ns_svg;') { encodedXmlns = node.getAttribute('xmlns'); decodedXmlns = encodedXmlns.replace('&ns_svg;', 'http://www.w3.org/2000/svg'); node.setAttribute('xmlns', decodedXmlns); } if (node.hasAttribute('xmlns:xlink') && node.getAttribute('xmlns:xlink') === '&ns_xlink;') { encodedXmlns = node.getAttribute('xmlns:xlink'); decodedXmlns = encodedXmlns.replace('&ns_xlink;', 'http://www.w3.org/1999/xlink'); node.setAttribute('xmlns:xlink', decodedXmlns); } }); } }; Gallery.Utility = Utility; })(jQuery, OC, t, oc_requesttoken, Gallery);