1 | /** |
||
2 | * Nextcloud - Gallery |
||
3 | * |
||
4 | * |
||
5 | * This file is licensed under the Affero General Public License version 3 or |
||
6 | * later. See the COPYING file. |
||
7 | * |
||
8 | * @author Olivier Paroz <[email protected]> |
||
9 | * |
||
10 | * @copyright Olivier Paroz 2017 |
||
11 | */ |
||
12 | /* global DOMPurify, oc_requesttoken, Gallery */ |
||
13 | |||
14 | // The Utility class can also be loaded by the Files app |
||
15 | window.Gallery = window.Gallery || {}; |
||
16 | |||
17 | (function ($, OC, t, oc_requesttoken, Gallery) { |
||
18 | "use strict"; |
||
19 | /** |
||
20 | * Contains utility methods |
||
21 | * |
||
22 | * @fixme OC.generateUrl, OC.buildQueryString, OC.Notification are private APIs |
||
23 | * |
||
24 | * @constructor |
||
25 | */ |
||
26 | var Utility = function () { |
||
27 | }; |
||
28 | |||
29 | Utility.prototype = { |
||
30 | /** |
||
31 | * Detects if the browser is a recent or an old version of Internet Explorer |
||
32 | * |
||
33 | * @returns {string|boolean} |
||
34 | */ |
||
35 | getIeVersion: function () { |
||
36 | // Blocking IE8 |
||
37 | if ($('html').is('.ie8')) { |
||
38 | return 'unsupportedIe'; |
||
39 | } else if (navigator.userAgent.indexOf("MSIE") > 0) { |
||
40 | return 'unsupportedIe'; |
||
41 | } else if (!!navigator.userAgent.match(/Trident.*rv[ :]*11\./)) { |
||
42 | return 'modernIe'; |
||
43 | } else if (navigator.userAgent.indexOf("Edge/") > 0) { |
||
44 | return 'edge'; |
||
45 | } |
||
46 | |||
47 | return false; |
||
48 | }, |
||
49 | |||
50 | /** |
||
51 | * Shows a notification to IE users, letting them know that they should use another browser |
||
52 | * in order to get the best experience |
||
53 | * |
||
54 | * @param {string} version |
||
55 | */ |
||
56 | showIeWarning: function (version) { |
||
57 | var line1 = t('gallery', 'This application may not work properly on your browser.'); |
||
58 | var line2 = t('gallery', |
||
59 | 'For an improved experience, please install one of the following alternatives'); |
||
60 | var timeout = 15; |
||
61 | if (version === 'unsupportedIe') { |
||
62 | line1 = t('gallery', 'Your browser is not supported!'); |
||
63 | line2 = t('gallery', 'please install one of the following alternatives'); |
||
64 | timeout = 60; |
||
65 | } |
||
66 | |||
67 | var recommendedBrowsers = '</br>' + |
||
68 | '<a href="http://www.getfirefox.com"><strong>Mozilla Firefox</strong></a> or ' + |
||
69 | '<a href="https://www.google.com/chrome/"><strong>Google Chrome</strong></a>' + |
||
70 | '</br>'; |
||
71 | |||
72 | var text = '<strong>' + line1 + '</strong></br>' + line2 + recommendedBrowsers; |
||
73 | this.showHtmlNotification(text, timeout); |
||
74 | }, |
||
75 | |||
76 | /** |
||
77 | * Shows a notification at the top of the screen |
||
78 | * |
||
79 | * @param {string} text |
||
80 | * @param {int} timeout |
||
81 | */ |
||
82 | showHtmlNotification: function (text, timeout) { |
||
83 | var options = { |
||
84 | timeout: timeout, |
||
85 | isHTML: true |
||
86 | }; |
||
87 | OC.Notification.showTemporary(text, options); |
||
88 | }, |
||
89 | |||
90 | /** |
||
91 | * Returns the token allowing access to files shared via link |
||
92 | * |
||
93 | * @returns {string} |
||
94 | */ |
||
95 | getPublicToken: function () { |
||
96 | var element = $('#gallery'); |
||
97 | var token; |
||
98 | |||
99 | if (element.data('token')) { |
||
100 | token = element.data('token'); |
||
101 | } |
||
102 | |||
103 | if (element.data('requesttoken')) { |
||
104 | /* jshint camelcase: false */ |
||
105 | oc_requesttoken = element.data('requesttoken'); |
||
106 | } |
||
107 | |||
108 | return token; |
||
109 | }, |
||
110 | |||
111 | /** |
||
112 | * Returns the host we can use for WebDAV |
||
113 | * |
||
114 | * On public galleries, we need to provide the token as authorization |
||
115 | * |
||
116 | * @returns {string} |
||
117 | */ |
||
118 | getWebdavHost: function () { |
||
119 | var host = OC.getHost(); |
||
120 | if (Gallery.token) { |
||
121 | host = Gallery.token + '@' + host; |
||
122 | } |
||
123 | |||
124 | return host; |
||
125 | }, |
||
126 | |||
127 | /** |
||
128 | * Returns the WebDAV endpoint we can use for files operations |
||
129 | * |
||
130 | * @returns {string} |
||
131 | */ |
||
132 | getWebdavRoot: function () { |
||
133 | var root = OC.linkToRemoteBase('webdav'); |
||
134 | if (Gallery.token) { |
||
135 | root = root.replace('remote.php', 'public.php'); |
||
136 | } |
||
137 | |||
138 | return root; |
||
139 | }, |
||
140 | |||
141 | /** |
||
142 | * Builds the URL which will retrieve a large preview of the file |
||
143 | * |
||
144 | * @fixme we cannot get rid of oc_requesttoken parameter as it's missing from the headers |
||
145 | * |
||
146 | * @param {number} fileId |
||
147 | * @param {number} etag |
||
148 | * |
||
149 | * @return {string} |
||
150 | */ |
||
151 | getPreviewUrl: function (fileId, etag) { |
||
152 | var width = Math.ceil(screen.width * window.devicePixelRatio); |
||
153 | var height = Math.ceil(screen.height * window.devicePixelRatio); |
||
154 | |||
155 | /* Find value of longest edge. */ |
||
156 | var longEdge = Math.max(width, height); |
||
157 | |||
158 | /* Find the next larger image size. */ |
||
159 | if (longEdge % 100 !== 0) { |
||
160 | longEdge = ( longEdge + 100 ) - ( longEdge % 100 ); |
||
161 | } |
||
162 | |||
163 | /* jshint camelcase: false */ |
||
164 | var params = { |
||
165 | width: longEdge, |
||
166 | height: longEdge, |
||
167 | }; |
||
168 | return this.buildGalleryUrl('preview', '/' + fileId, params); |
||
169 | }, |
||
170 | |||
171 | /** |
||
172 | * Builds a URL pointing to one of the app's controllers |
||
173 | * |
||
174 | * @param {string} endPoint |
||
175 | * @param {undefined|string} path |
||
176 | * @param params |
||
177 | * |
||
178 | * @returns {string} |
||
179 | */ |
||
180 | buildGalleryUrl: function (endPoint, path, params) { |
||
181 | if (path === undefined) { |
||
182 | path = ''; |
||
183 | } |
||
184 | var extension = ''; |
||
185 | if (Gallery.token) { |
||
186 | params.token = Gallery.token; |
||
187 | extension = '.public'; |
||
188 | } |
||
189 | var query = OC.buildQueryString(params); |
||
190 | return OC.generateUrl('apps/' + Gallery.appName + '/' + endPoint + extension + path, |
||
191 | null) + |
||
192 | '?' + |
||
193 | query; |
||
194 | }, |
||
195 | |||
196 | /** |
||
197 | * Builds a URL pointing to one of the files' controllers |
||
198 | * |
||
199 | * @param {string} path |
||
200 | * @param {string} files |
||
201 | * |
||
202 | * @returns {string} |
||
203 | */ |
||
204 | buildFilesUrl: function (path, files) { |
||
205 | var subUrl = ''; |
||
206 | var params = { |
||
207 | path: path, |
||
208 | files: files |
||
209 | }; |
||
210 | |||
211 | if (Gallery.token) { |
||
212 | params.token = Gallery.token; |
||
213 | subUrl = 's/{token}/download?dir={path}&files={files}'; |
||
214 | } else { |
||
215 | subUrl = 'apps/files/ajax/download.php?dir={path}&files={files}'; |
||
216 | } |
||
217 | |||
218 | return OC.generateUrl(subUrl, params); |
||
219 | }, |
||
220 | |||
221 | /** |
||
222 | * Sorts arrays based on name or date |
||
223 | * |
||
224 | * @param {string} sortType |
||
225 | * @param {string} sortOrder |
||
226 | * |
||
227 | * @returns {Function} |
||
228 | */ |
||
229 | sortBy: function (sortType, sortOrder) { |
||
230 | if (sortType === 'name') { |
||
231 | if (sortOrder === 'asc') { |
||
232 | //sortByNameAsc |
||
233 | return function (a, b) { |
||
234 | return OC.Util.naturalSortCompare(a.path, b.path); |
||
235 | }; |
||
236 | } |
||
237 | //sortByNameDes |
||
238 | return function (a, b) { |
||
239 | return -OC.Util.naturalSortCompare(a.path, b.path); |
||
240 | }; |
||
241 | } |
||
242 | if (sortType === 'date') { |
||
0 ignored issues
–
show
|
|||
243 | if (sortOrder === 'asc') { |
||
244 | //sortByDateAsc |
||
245 | return function (a, b) { |
||
246 | return b.mTime - a.mTime; |
||
247 | }; |
||
248 | } |
||
249 | //sortByDateDes |
||
250 | return function (a, b) { |
||
251 | return a.mTime - b.mTime; |
||
252 | }; |
||
253 | } |
||
254 | }, |
||
255 | |||
256 | /** |
||
257 | * Adds hooks to DOMPurify |
||
258 | */ |
||
259 | addDomPurifyHooks: function () { |
||
260 | // allowed URI schemes |
||
261 | var whitelist = ['http', 'https']; |
||
262 | |||
263 | // build fitting regex |
||
264 | var regex = new RegExp('^(' + whitelist.join('|') + '):', 'gim'); |
||
265 | |||
266 | DOMPurify.addHook('afterSanitizeAttributes', function (node) { |
||
267 | // This hook enforces URI scheme whitelist |
||
268 | // @link |
||
269 | // https://github.com/cure53/DOMPurify/blob/master/demos/hooks-scheme-whitelist.html |
||
270 | |||
271 | // build an anchor to map URLs to |
||
272 | var anchor = document.createElement('a'); |
||
273 | |||
274 | // check all href attributes for validity |
||
275 | if (node.hasAttribute('href')) { |
||
276 | anchor.href = node.getAttribute('href'); |
||
277 | if (anchor.protocol && !anchor.protocol.match(regex)) { |
||
278 | node.removeAttribute('href'); |
||
279 | } |
||
280 | } |
||
281 | // check all action attributes for validity |
||
282 | if (node.hasAttribute('action')) { |
||
283 | anchor.href = node.getAttribute('action'); |
||
284 | if (anchor.protocol && !anchor.protocol.match(regex)) { |
||
285 | node.removeAttribute('action'); |
||
286 | } |
||
287 | } |
||
288 | // check all xlink:href attributes for validity |
||
289 | if (node.hasAttribute('xlink:href')) { |
||
290 | anchor.href = node.getAttribute('xlink:href'); |
||
291 | if (anchor.protocol && !anchor.protocol.match(regex)) { |
||
292 | node.removeAttribute('xlink:href'); |
||
293 | } |
||
294 | } |
||
295 | |||
296 | // This hook restores the proper, standard namespace in SVG files |
||
297 | var encodedXmlns, decodedXmlns; |
||
298 | |||
299 | // Restores namespaces which were put in the DOCTYPE by Illustrator |
||
300 | if (node.hasAttribute('xmlns') && node.getAttribute('xmlns') === '&ns_svg;') { |
||
301 | encodedXmlns = node.getAttribute('xmlns'); |
||
302 | decodedXmlns = encodedXmlns.replace('&ns_svg;', 'http://www.w3.org/2000/svg'); |
||
303 | node.setAttribute('xmlns', decodedXmlns); |
||
304 | } |
||
305 | if (node.hasAttribute('xmlns:xlink') && |
||
306 | node.getAttribute('xmlns:xlink') === '&ns_xlink;') { |
||
307 | encodedXmlns = node.getAttribute('xmlns:xlink'); |
||
308 | decodedXmlns = |
||
309 | encodedXmlns.replace('&ns_xlink;', 'http://www.w3.org/1999/xlink'); |
||
310 | node.setAttribute('xmlns:xlink', decodedXmlns); |
||
311 | } |
||
312 | }); |
||
313 | } |
||
314 | }; |
||
315 | |||
316 | Gallery.Utility = Utility; |
||
317 | })(jQuery, OC, t, oc_requesttoken, Gallery); |
||
318 |
This check looks for functions where a
return
statement is found in some execution paths, but not in all.Consider this little piece of code
The function
isBig
will only return a specific value when its parameter is bigger than 5000. In any other case, it will implicitly returnundefined
.This behaviour may not be what you had intended. In any case, you can add a
return undefined
to the other execution path to make the return value explicit.