migrate therinaldos.com data
Build & Deploy to DigitalOcean Space / build (push) Failing after 2m38s

This commit is contained in:
2024-05-05 15:50:45 -04:00
commit ef1ff240d4
23182 changed files with 3801898 additions and 0 deletions
+29
View File
@@ -0,0 +1,29 @@
/**
* ownCloud
*
* @author Jakob Sack
* @copyright 2012 Jakob Sack owncloud@jakobsack.de
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
* License as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
// start worker once page has loaded
window.addEventListener('DOMContentLoaded', function(){
$.get( OC.getRootPath()+'/cron.php' );
$('.section .icon-info').tooltip({
placement: 'right'
});
});
+11
View File
@@ -0,0 +1,11 @@
{
"libraries": [
"core-common.js"
],
"modules": [
"../core/js/public/publicpage.js",
"../core/js/setupchecks.js",
"../core/js/mimetype.js",
"../core/js/mimetypelist.js"
]
}
+15
View File
@@ -0,0 +1,15 @@
jQuery(document).ready(function() {
$('#app-token-login').click(function (e) {
e.preventDefault();
$(this).addClass('hidden');
$('#redirect-link').addClass('hidden');
$('#app-token-login-field').removeClass('hidden');
});
document.getElementById('login-form').addEventListener('submit', function (e) {
e.preventDefault();
document.location.href = e.target.attributes.action.value
})
$('#login-form input').removeAttr('disabled');
})
+11
View File
@@ -0,0 +1,11 @@
document.querySelector('form').addEventListener('submit', function(e) {
const wrapper = document.getElementById('submit-wrapper')
if (wrapper === null) {
return
}
Array.from(wrapper.getElementsByClassName('icon-confirm-white')).forEach(function(el) {
el.classList.remove('icon-confirm-white')
el.classList.add(OCA.Theming && OCA.Theming.inverted ? 'icon-loading-small' : 'icon-loading-small-dark')
el.disabled = true
})
})
@@ -0,0 +1,5 @@
[
"mimetype.js",
"mimetypelist.js",
"select2-toggleselect.js"
]
+119
View File
@@ -0,0 +1,119 @@
/**
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
/**
* Namespace to hold functions related to convert mimetype to icons
*
* @namespace
*/
OC.MimeType = {
/**
* Cache that maps mimeTypes to icon urls
*/
_mimeTypeIcons: {},
/**
* Return the file icon we want to use for the given mimeType.
* The file needs to be present in the supplied file list
*
* @param {string} mimeType The mimeType we want an icon for
* @param {array} files The available icons in this theme
* @return {string} The icon to use or null if there is no match
*/
_getFile: function(mimeType, files) {
var icon = mimeType.replace(new RegExp('/', 'g'), '-');
// Generate path
if (mimeType === 'dir' && files.includes('folder')) {
return 'folder';
} else if (mimeType === 'dir-encrypted' && files.includes('folder-encrypted')) {
return 'folder-encrypted';
} else if (mimeType === 'dir-shared' && files.includes('folder-shared')) {
return 'folder-shared';
} else if (mimeType === 'dir-public' && files.includes('folder-public')) {
return 'folder-public';
} else if ((mimeType === 'dir-external' || mimeType === 'dir-external-root') && files.includes('folder-external')) {
return 'folder-external';
} else if (files.includes(icon)) {
return icon;
} else if (files.includes(icon.split('-')[0])) {
return icon.split('-')[0];
} else if (files.includes('file')) {
return 'file';
}
return null;
},
/**
* Return the url to icon of the given mimeType
*
* @param {string} mimeType The mimeType to get the icon for
* @return {string} Url to the icon for mimeType
*/
getIconUrl: function(mimeType) {
if (typeof mimeType === 'undefined') {
return undefined;
}
while (mimeType in OC.MimeTypeList.aliases) {
mimeType = OC.MimeTypeList.aliases[mimeType];
}
if (mimeType in OC.MimeType._mimeTypeIcons) {
return OC.MimeType._mimeTypeIcons[mimeType];
}
// First try to get the correct icon from the current theme
var gotIcon = null;
var path = '';
if (OC.theme.folder !== '' && Array.isArray(OC.MimeTypeList.themes[OC.theme.folder])) {
path = OC.getRootPath() + '/themes/' + OC.theme.folder + '/core/img/filetypes/';
var icon = OC.MimeType._getFile(mimeType, OC.MimeTypeList.themes[OC.theme.folder]);
if (icon !== null) {
gotIcon = true;
path += icon;
}
}
if(OCA.Theming && gotIcon === null) {
path = OC.generateUrl('/apps/theming/img/core/filetypes/');
path += OC.MimeType._getFile(mimeType, OC.MimeTypeList.files);
gotIcon = true;
}
// If we do not yet have an icon fall back to the default
if (gotIcon === null) {
path = OC.getRootPath() + '/core/img/filetypes/';
path += OC.MimeType._getFile(mimeType, OC.MimeTypeList.files);
}
path += '.svg';
if(OCA.Theming) {
path += "?v=" + OCA.Theming.cacheBuster;
}
// Cache the result
OC.MimeType._mimeTypeIcons[mimeType] = path;
return path;
}
};
+150
View File
@@ -0,0 +1,150 @@
/**
* This file is automatically generated
* DO NOT EDIT MANUALLY!
*
* You can update the list of MimeType Aliases in config/mimetypealiases.json
* The list of files is fetched from core/img/filetypes
* To regenerate this file run ./occ maintenance:mimetype:update-js
*/
OC.MimeTypeList={
aliases: {
"application/coreldraw": "image",
"application/epub+zip": "text",
"application/font-sfnt": "font",
"application/font-woff": "font",
"application/gpx+xml": "location",
"application/illustrator": "image",
"application/javascript": "text/code",
"application/json": "text/code",
"application/msaccess": "file",
"application/msexcel": "x-office/spreadsheet",
"application/msonenote": "x-office/document",
"application/mspowerpoint": "x-office/presentation",
"application/msword": "x-office/document",
"application/octet-stream": "file",
"application/postscript": "image",
"application/rss+xml": "application/xml",
"application/vnd.android.package-archive": "package/x-generic",
"application/vnd.lotus-wordpro": "x-office/document",
"application/vnd.garmin.tcx+xml": "location",
"application/vnd.google-earth.kml+xml": "location",
"application/vnd.google-earth.kmz": "location",
"application/vnd.ms-excel": "x-office/spreadsheet",
"application/vnd.ms-excel.addin.macroEnabled.12": "x-office/spreadsheet",
"application/vnd.ms-excel.sheet.binary.macroEnabled.12": "x-office/spreadsheet",
"application/vnd.ms-excel.sheet.macroEnabled.12": "x-office/spreadsheet",
"application/vnd.ms-excel.template.macroEnabled.12": "x-office/spreadsheet",
"application/vnd.ms-fontobject": "font",
"application/vnd.ms-powerpoint": "x-office/presentation",
"application/vnd.ms-powerpoint.addin.macroEnabled.12": "x-office/presentation",
"application/vnd.ms-powerpoint.presentation.macroEnabled.12": "x-office/presentation",
"application/vnd.ms-powerpoint.slideshow.macroEnabled.12": "x-office/presentation",
"application/vnd.ms-powerpoint.template.macroEnabled.12": "x-office/presentation",
"application/vnd.ms-visio.drawing.macroEnabled.12": "application/vnd.visio",
"application/vnd.ms-visio.drawing": "application/vnd.visio",
"application/vnd.ms-visio.stencil.macroEnabled.12": "application/vnd.visio",
"application/vnd.ms-visio.stencil": "application/vnd.visio",
"application/vnd.ms-visio.template.macroEnabled.12": "application/vnd.visio",
"application/vnd.ms-visio.template": "application/vnd.visio",
"application/vnd.ms-word.document.macroEnabled.12": "x-office/document",
"application/vnd.ms-word.template.macroEnabled.12": "x-office/document",
"application/vnd.oasis.opendocument.presentation": "x-office/presentation",
"application/vnd.oasis.opendocument.presentation-template": "x-office/presentation",
"application/vnd.oasis.opendocument.spreadsheet": "x-office/spreadsheet",
"application/vnd.oasis.opendocument.spreadsheet-template": "x-office/spreadsheet",
"application/vnd.oasis.opendocument.text": "x-office/document",
"application/vnd.oasis.opendocument.text-master": "x-office/document",
"application/vnd.oasis.opendocument.text-template": "x-office/document",
"application/vnd.oasis.opendocument.graphics": "x-office/drawing",
"application/vnd.oasis.opendocument.graphics-template": "x-office/drawing",
"application/vnd.oasis.opendocument.text-web": "x-office/document",
"application/vnd.oasis.opendocument.text-flat-xml": "x-office/document",
"application/vnd.oasis.opendocument.spreadsheet-flat-xml": "x-office/spreadsheet",
"application/vnd.oasis.opendocument.graphics-flat-xml": "x-office/drawing",
"application/vnd.oasis.opendocument.presentation-flat-xml": "x-office/presentation",
"application/vnd.openxmlformats-officedocument.presentationml.presentation": "x-office/presentation",
"application/vnd.openxmlformats-officedocument.presentationml.slideshow": "x-office/presentation",
"application/vnd.openxmlformats-officedocument.presentationml.template": "x-office/presentation",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "x-office/spreadsheet",
"application/vnd.openxmlformats-officedocument.spreadsheetml.template": "x-office/spreadsheet",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document": "x-office/document",
"application/vnd.openxmlformats-officedocument.wordprocessingml.template": "x-office/document",
"application/vnd.visio": "x-office/document",
"application/vnd.wordperfect": "x-office/document",
"application/x-7z-compressed": "package/x-generic",
"application/x-bzip2": "package/x-generic",
"application/x-cbr": "text",
"application/x-compressed": "package/x-generic",
"application/x-dcraw": "image",
"application/x-deb": "package/x-generic",
"application/x-fictionbook+xml": "text",
"application/x-font": "font",
"application/x-gimp": "image",
"application/x-gzip": "package/x-generic",
"application/x-iwork-keynote-sffkey": "x-office/presentation",
"application/x-iwork-numbers-sffnumbers": "x-office/spreadsheet",
"application/x-iwork-pages-sffpages": "x-office/document",
"application/x-mobipocket-ebook": "text",
"application/x-perl": "text/code",
"application/x-photoshop": "image",
"application/x-php": "text/code",
"application/x-rar-compressed": "package/x-generic",
"application/x-tar": "package/x-generic",
"application/x-tex": "text",
"application/xml": "text/html",
"application/yaml": "text/code",
"application/zip": "package/x-generic",
"database": "file",
"httpd/unix-directory": "dir",
"text/css": "text/code",
"text/csv": "x-office/spreadsheet",
"text/html": "text/code",
"text/x-c": "text/code",
"text/x-c++src": "text/code",
"text/x-h": "text/code",
"text/x-java-source": "text/code",
"text/x-ldif": "text/code",
"text/x-python": "text/code",
"text/x-shellscript": "text/code",
"web": "text/code",
"application/internet-shortcut": "link",
"application/km": "mindmap",
"application/x-freemind": "mindmap",
"application/vnd.xmind.workbook": "mindmap",
"image/targa": "image/tga",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document.oform": "x-office/form",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document.docxf": "x-office/form-template",
"image/x-emf": "image/emf"
},
files: [
"application",
"application-pdf",
"audio",
"file",
"folder",
"folder-drag-accept",
"folder-encrypted",
"folder-external",
"folder-public",
"folder-shared",
"folder-starred",
"font",
"image",
"link",
"location",
"mindmap",
"package-x-generic",
"text",
"text-calendar",
"text-code",
"text-vcard",
"video",
"x-office-document",
"x-office-drawing",
"x-office-form",
"x-office-form-template",
"x-office-presentation",
"x-office-spreadsheet"
],
themes: []
};
+47
View File
@@ -0,0 +1,47 @@
/*
* @copyright Copyright (c) 2018 Julius Härtl <jus@bitgrid.net>
*
* @author Julius Härtl <jus@bitgrid.net>
*
* @license AGPL-3.0-or-later
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
window.addEventListener('DOMContentLoaded', function () {
$('#body-public').find('.header-right .menutoggle').click(function() {
$(this).next('.popovermenu').toggleClass('open');
});
$('#save-external-share').click(function () {
$('#external-share-menu-item').toggleClass('hidden')
$('#remote_address').focus();
});
$(document).mouseup(function(e) {
var toggle = $('#body-public').find('.header-right .menutoggle');
var container = toggle.next('.popovermenu');
// if the target of the click isn't the menu toggle, nor a descendant of the
// menu toggle, nor the container nor a descendant of the container
if (!toggle.is(e.target) && toggle.has(e.target).length === 0 &&
!container.is(e.target) && container.has(e.target).length === 0) {
container.removeClass('open');
}
});
});
+54
View File
@@ -0,0 +1,54 @@
function showEmailAddressPromptForm() {
// Shows email prompt
var emailInput = document.getElementById('email-input-form');
emailInput.style.display="block";
// Shows back button
var backButton = document.getElementById('request-password-back-button');
backButton.style.display="block";
// Hides password prompt and 'request password' button
var passwordRequestButton = document.getElementById('request-password-button-not-talk');
var passwordInput = document.getElementById('password-input-form');
passwordRequestButton.style.display="none";
passwordInput.style.display="none";
// Hides identification result messages, if any
var identificationResultSuccess = document.getElementById('identification-success');
var identificationResultFailure = document.getElementById('identification-failure');
if (identificationResultSuccess) {
identificationResultSuccess.style.display="none";
}
if (identificationResultFailure) {
identificationResultFailure.style.display="none";
}
}
document.addEventListener('DOMContentLoaded', function() {
// Enables password submit button only when user has typed something in the password field
var passwordInput = document.getElementById('password');
var passwordButton = document.getElementById('password-submit');
var eventListener = function() {
passwordButton.disabled = passwordInput.value.length === 0;
};
passwordInput.addEventListener('click', eventListener);
passwordInput.addEventListener('keyup', eventListener);
passwordInput.addEventListener('change', eventListener);
// Enables email request button only when user has typed something in the email field
var emailInput = document.getElementById('email');
var emailButton = document.getElementById('password-request');
eventListener = function() {
emailButton.disabled = emailInput.value.length === 0;
};
emailInput.addEventListener('click', eventListener);
emailInput.addEventListener('keyup', eventListener);
emailInput.addEventListener('change', eventListener);
// Adds functionality to the request password button
var passwordRequestButton = document.getElementById('request-password-button-not-talk');
if (passwordRequestButton) {
passwordRequestButton.addEventListener('click', showEmailAddressPromptForm);
}
});
@@ -0,0 +1,54 @@
/*
* Copyright (c) 2015
*
* This file is licensed under the Affero General Public License version 3
* or later.
*
* See the COPYING-README file.
*
*/
/* global Select2 */
/**
* Select2 extension for toggling values in a multi-select dropdown
*/
(function(Select2) {
var Select2FindHighlightableChoices = Select2.class.multi.prototype.findHighlightableChoices;
Select2.class.multi.prototype.findHighlightableChoices = function () {
if (this.opts.toggleSelect) {
return this.results.find('.select2-result-selectable:not(.select2-disabled)');
}
return Select2FindHighlightableChoices.apply(this, arguments);
};
var Select2TriggerSelect = Select2.class.multi.prototype.triggerSelect;
Select2.class.multi.prototype.triggerSelect = function (data) {
if (this.opts.toggleSelect && this.val().indexOf(this.id(data)) !== -1) {
var self = this;
var val = this.id(data);
var selectionEls = this.container.find('.select2-search-choice').filter(function() {
return (self.id($(this).data('select2-data')) === val);
});
if (this.unselect(selectionEls)) {
// also unselect in dropdown
this.results.find('.select2-result.select2-selected').each(function () {
var $this = $(this);
if (self.id($this.data('select2-data')) === val) {
$this.removeClass('select2-selected');
}
});
this.clearSearch();
}
return false;
} else {
return Select2TriggerSelect.apply(this, arguments);
}
};
})(Select2);
+520
View File
@@ -0,0 +1,520 @@
/*
* Copyright (c) 2014
*
* This file is licensed under the Affero General Public License version 3
* or later.
*
* See the COPYING-README file.
*
*/
(function() {
OC.SetupChecks = {
/* Message types */
MESSAGE_TYPE_INFO:0,
MESSAGE_TYPE_WARNING:1,
MESSAGE_TYPE_ERROR:2,
/**
* Check whether the WebDAV connection works.
*
* @return $.Deferred object resolved with an array of error messages
*/
checkWebDAV: function() {
var deferred = $.Deferred();
var afterCall = function(xhr) {
var messages = [];
if (xhr.status !== 207 && xhr.status !== 401) {
messages.push({
msg: t('core', 'Your web server is not yet properly set up to allow file synchronization, because the WebDAV interface seems to be broken.'),
type: OC.SetupChecks.MESSAGE_TYPE_ERROR
});
}
deferred.resolve(messages);
};
$.ajax({
type: 'PROPFIND',
url: OC.linkToRemoteBase('webdav'),
data: '<?xml version="1.0"?>' +
'<d:propfind xmlns:d="DAV:">' +
'<d:prop><d:resourcetype/></d:prop>' +
'</d:propfind>',
contentType: 'application/xml; charset=utf-8',
complete: afterCall,
allowAuthErrors: true
});
return deferred.promise();
},
/**
* Check whether the .well-known URLs works.
*
* @param url the URL to test
* @param placeholderUrl the placeholder URL - can be found at OC.theme.docPlaceholderUrl
* @param {boolean} runCheck if this is set to false the check is skipped and no error is returned
* @param {int|int[]} expectedStatus the expected HTTP status to be returned by the URL, 207 by default
* @return $.Deferred object resolved with an array of error messages
*/
checkWellKnownUrl: function(verb, url, placeholderUrl, runCheck, expectedStatus, checkCustomHeader) {
if (expectedStatus === undefined) {
expectedStatus = [207];
}
if (!Array.isArray(expectedStatus)) {
expectedStatus = [expectedStatus];
}
var deferred = $.Deferred();
if(runCheck === false) {
deferred.resolve([]);
return deferred.promise();
}
var afterCall = function(xhr) {
var messages = [];
var customWellKnown = xhr.getResponseHeader('X-NEXTCLOUD-WELL-KNOWN')
if (expectedStatus.indexOf(xhr.status) === -1 || (checkCustomHeader && !customWellKnown)) {
var docUrl = placeholderUrl.replace('PLACEHOLDER', 'admin-setup-well-known-URL');
messages.push({
msg: t('core', 'Your web server is not properly set up to resolve "{url}". Further information can be found in the {linkstart}documentation ↗{linkend}.', { url: url })
.replace('{linkstart}', '<a target="_blank" rel="noreferrer noopener" class="external" href="' + docUrl + '">')
.replace('{linkend}', '</a>'),
type: OC.SetupChecks.MESSAGE_TYPE_INFO
});
}
deferred.resolve(messages);
};
$.ajax({
type: verb,
url: url,
complete: afterCall,
allowAuthErrors: true
});
return deferred.promise();
},
/**
* Check whether the .well-known URLs works.
*
* @param url the URL to test
* @param placeholderUrl the placeholder URL - can be found at OC.theme.docPlaceholderUrl
* @param {boolean} runCheck if this is set to false the check is skipped and no error is returned
*
* @return $.Deferred object resolved with an array of error messages
*/
checkProviderUrl: function(url, placeholderUrl, runCheck) {
var expectedStatus = [200];
var deferred = $.Deferred();
if(runCheck === false) {
deferred.resolve([]);
return deferred.promise();
}
var afterCall = function(xhr) {
var messages = [];
if (expectedStatus.indexOf(xhr.status) === -1) {
var docUrl = placeholderUrl.replace('PLACEHOLDER', 'admin-nginx');
messages.push({
msg: t('core', 'Your web server is not properly set up to resolve "{url}". This is most likely related to a web server configuration that was not updated to deliver this folder directly. Please compare your configuration against the shipped rewrite rules in ".htaccess" for Apache or the provided one in the documentation for Nginx at it\'s {linkstart}documentation page ↗{linkend}. On Nginx those are typically the lines starting with "location ~" that need an update.', { docLink: docUrl, url: url })
.replace('{linkstart}', '<a target="_blank" rel="noreferrer noopener" class="external" href="' + docUrl + '">')
.replace('{linkend}', '</a>'),
type: OC.SetupChecks.MESSAGE_TYPE_WARNING
});
}
deferred.resolve(messages);
};
$.ajax({
type: 'GET',
url: url,
complete: afterCall,
allowAuthErrors: true
});
return deferred.promise();
},
/**
* Check whether the WOFF2 URLs works.
*
* @param url the URL to test
* @param placeholderUrl the placeholder URL - can be found at OC.theme.docPlaceholderUrl
* @return $.Deferred object resolved with an array of error messages
*/
checkWOFF2Loading: function(url, placeholderUrl) {
var deferred = $.Deferred();
var afterCall = function(xhr) {
var messages = [];
if (xhr.status !== 200) {
var docUrl = placeholderUrl.replace('PLACEHOLDER', 'admin-nginx');
messages.push({
msg: t('core', 'Your web server is not properly set up to deliver .woff2 files. This is typically an issue with the Nginx configuration. For Nextcloud 15 it needs an adjustement to also deliver .woff2 files. Compare your Nginx configuration to the recommended configuration in our {linkstart}documentation ↗{linkend}.', { docLink: docUrl, url: url })
.replace('{linkstart}', '<a target="_blank" rel="noreferrer noopener" class="external" href="' + docUrl + '">')
.replace('{linkend}', '</a>'),
type: OC.SetupChecks.MESSAGE_TYPE_WARNING
});
}
deferred.resolve(messages);
};
$.ajax({
type: 'GET',
url: url,
complete: afterCall,
allowAuthErrors: true
});
return deferred.promise();
},
/**
* Runs setup checks on the server side
*
* @return $.Deferred object resolved with an array of error messages
*/
checkSetup: function() {
var deferred = $.Deferred();
var afterCall = function(data, statusText, xhr) {
var messages = [];
if (xhr.status === 200 && data) {
if (!data.isFairUseOfFreePushService) {
messages.push({
msg: t('core', 'This is the unsupported community build of Nextcloud. Given the size of this instance, performance, reliability and scalability cannot be guaranteed. Push notifications are limited to avoid overloading our free service. Learn more about the benefits of Nextcloud Enterprise at {linkstart}https://nextcloud.com/enterprise{linkend}.')
.replace('{linkstart}', '<a target="_blank" rel="noreferrer noopener" class="external" href="https://nextcloud.com/enterprise">')
.replace('{linkend}', '</a>'),
type: OC.SetupChecks.MESSAGE_TYPE_ERROR
});
}
if(!data.isCorrectMemcachedPHPModuleInstalled) {
messages.push({
msg: t('core', 'Memcached is configured as distributed cache, but the wrong PHP module "memcache" is installed. \\OC\\Memcache\\Memcached only supports "memcached" and not "memcache". See the {linkstart}memcached wiki about both modules ↗{linkend}.')
.replace('{linkstart}', '<a target="_blank" rel="noreferrer noopener" class="external" href="https://code.google.com/p/memcached/wiki/PHPClientComparison">')
.replace('{linkend}', '</a>'),
type: OC.SetupChecks.MESSAGE_TYPE_WARNING
});
}
if(!data.isSettimelimitAvailable) {
messages.push({
msg: t('core', 'The PHP function "set_time_limit" is not available. This could result in scripts being halted mid-execution, breaking your installation. Enabling this function is strongly recommended.'),
type: OC.SetupChecks.MESSAGE_TYPE_WARNING
});
}
if (!data.areWebauthnExtensionsEnabled) {
messages.push({
msg: t(
'core',
'The PHP modules "gmp" and/or "bcmath" are not enabled. If you use WebAuthn passwordless authentication, these modules are required.'
),
type: OC.SetupChecks.MESSAGE_TYPE_INFO
})
}
if (data.isMysqlUsedWithoutUTF8MB4) {
messages.push({
msg: t('core', 'MySQL is used as database but does not support 4-byte characters. To be able to handle 4-byte characters (like emojis) without issues in filenames or comments for example it is recommended to enable the 4-byte support in MySQL. For further details read {linkstart}the documentation page about this ↗{linkend}.')
.replace('{linkstart}', '<a target="_blank" rel="noreferrer noopener" class="external" href="' + OC.theme.docPlaceholderUrl.replace('PLACEHOLDER', 'admin-mysql-utf8mb4') + '">')
.replace('{linkend}', '</a>'),
type: OC.SetupChecks.MESSAGE_TYPE_WARNING
})
}
if (!data.isEnoughTempSpaceAvailableIfS3PrimaryStorageIsUsed) {
messages.push({
msg: t('core', 'This instance uses an S3 based object store as primary storage. The uploaded files are stored temporarily on the server and thus it is recommended to have 50 GB of free space available in the temp directory of PHP. Check the logs for full details about the path and the available space. To improve this please change the temporary directory in the php.ini or make more space available in that path.'),
type: OC.SetupChecks.MESSAGE_TYPE_WARNING
})
}
if (!data.temporaryDirectoryWritable) {
messages.push({
msg: t('core', 'The temporary directory of this instance points to an either non-existing or non-writable directory.'),
type: OC.SetupChecks.MESSAGE_TYPE_WARNING
})
}
if (window.location.protocol === 'https:' && data.reverseProxyGeneratedURL.split('/')[0] !== 'https:') {
messages.push({
msg: t('core', 'You are accessing your instance over a secure connection, however your instance is generating insecure URLs. This most likely means that you are behind a reverse proxy and the overwrite config variables are not set correctly. Please read {linkstart}the documentation page about this ↗{linkend}.')
.replace('{linkstart}', '<a target="_blank" rel="noreferrer noopener" class="external" href="' + data.reverseProxyDocs + '">')
.replace('{linkend}', '</a>'),
type: OC.SetupChecks.MESSAGE_TYPE_WARNING
})
}
if (window.oc_debug) {
messages.push({
msg: t('core', 'This instance is running in debug mode. Only enable this for local development and not in production environments.'),
type: OC.SetupChecks.MESSAGE_TYPE_WARNING
})
}
if (Object.keys(data.generic).length > 0) {
Object.keys(data.generic).forEach(function(key){
Object.keys(data.generic[key]).forEach(function(title){
if (data.generic[key][title].severity != 'success') {
data.generic[key][title].pass = false;
OC.SetupChecks.addGenericSetupCheck(data.generic[key], title, messages);
}
});
});
}
} else {
messages.push({
msg: t('core', 'Error occurred while checking server setup'),
type: OC.SetupChecks.MESSAGE_TYPE_ERROR
});
}
deferred.resolve(messages);
};
$.ajax({
type: 'GET',
url: OC.generateUrl('settings/ajax/checksetup'),
allowAuthErrors: true
}).then(afterCall, afterCall);
return deferred.promise();
},
escapeHTML: function(text) {
return text.toString()
.split('&').join('&amp;')
.split('<').join('&lt;')
.split('>').join('&gt;')
.split('"').join('&quot;')
.split('\'').join('&#039;')
},
/**
* @param message The message string containing placeholders.
* @param parameters An object with keys as placeholders and values as their replacements.
*
* @return The message with placeholders replaced by values.
*/
richToParsed: function (message, parameters) {
for (var [placeholder, parameter] of Object.entries(parameters)) {
var replacement;
if (parameter.type === 'user') {
replacement = '@' + this.escapeHTML(parameter.name);
} else if (parameter.type === 'file') {
replacement = this.escapeHTML(parameter.path) || this.escapeHTML(parameter.name);
} else if (parameter.type === 'highlight') {
replacement = '<a href="' + encodeURI(parameter.link) + '">' + this.escapeHTML(parameter.name) + '</a>';
} else {
replacement = this.escapeHTML(parameter.name);
}
message = message.replace('{' + placeholder + '}', replacement);
}
return message;
},
addGenericSetupCheck: function(data, check, messages) {
var setupCheck = data[check] || { pass: true, description: '', severity: 'info', linkToDoc: null}
var type = OC.SetupChecks.MESSAGE_TYPE_INFO
if (setupCheck.severity === 'warning') {
type = OC.SetupChecks.MESSAGE_TYPE_WARNING
} else if (setupCheck.severity === 'error') {
type = OC.SetupChecks.MESSAGE_TYPE_ERROR
}
var message = setupCheck.description;
if (message) {
message = this.escapeHTML(message)
}
if (setupCheck.descriptionParameters) {
message = this.richToParsed(message, setupCheck.descriptionParameters);
}
if (setupCheck.linkToDoc) {
message += ' ' + t('core', 'For more details see the {linkstart}documentation ↗{linkend}.')
.replace('{linkstart}', '<a target="_blank" rel="noreferrer noopener" class="external" href="' + setupCheck.linkToDoc + '">')
.replace('{linkend}', '</a>');
}
if (setupCheck.elements) {
message += '<br><ul>'
setupCheck.elements.forEach(function(element){
message += '<li>';
message += element
message += '</li>';
});
message += '</ul>'
}
if (!setupCheck.pass) {
messages.push({
msg: message,
type: type,
})
}
},
/**
* Runs generic checks on the server side, the difference to dedicated
* methods is that we use the same XHR object for all checks to save
* requests.
*
* @return $.Deferred object resolved with an array of error messages
*/
checkGeneric: function() {
var self = this;
var deferred = $.Deferred();
var afterCall = function(data, statusText, xhr) {
var messages = [];
messages = messages.concat(self._checkSecurityHeaders(xhr));
messages = messages.concat(self._checkSSL(xhr));
deferred.resolve(messages);
};
$.ajax({
type: 'GET',
url: OC.generateUrl('heartbeat'),
allowAuthErrors: true
}).then(afterCall, afterCall);
return deferred.promise();
},
checkDataProtected: function() {
var deferred = $.Deferred();
if(oc_dataURL === false){
return deferred.resolve([]);
}
var afterCall = function(xhr) {
var messages = [];
// .ocdata is an empty file in the data directory - if this is readable then the data dir is not protected
if (xhr.status === 200 && xhr.responseText === '') {
messages.push({
msg: t('core', 'Your data directory and files are probably accessible from the internet. The .htaccess file is not working. It is strongly recommended that you configure your web server so that the data directory is no longer accessible, or move the data directory outside the web server document root.'),
type: OC.SetupChecks.MESSAGE_TYPE_ERROR
});
}
deferred.resolve(messages);
};
$.ajax({
type: 'GET',
url: OC.linkTo('', oc_dataURL+'/.ocdata?t=' + (new Date()).getTime()),
complete: afterCall,
allowAuthErrors: true
});
return deferred.promise();
},
/**
* Runs check for some generic security headers on the server side
*
* @param {Object} xhr
* @return {Array} Array with error messages
*/
_checkSecurityHeaders: function(xhr) {
var messages = [];
if (xhr.status === 200) {
var securityHeaders = {
'X-Content-Type-Options': ['nosniff'],
'X-Robots-Tag': ['noindex, nofollow'],
'X-Frame-Options': ['SAMEORIGIN', 'DENY'],
'X-Permitted-Cross-Domain-Policies': ['none'],
};
for (var header in securityHeaders) {
var option = securityHeaders[header][0];
if(!xhr.getResponseHeader(header) || xhr.getResponseHeader(header).replace(/, /, ',').toLowerCase() !== option.replace(/, /, ',').toLowerCase()) {
var msg = t('core', 'The "{header}" HTTP header is not set to "{expected}". This is a potential security or privacy risk, as it is recommended to adjust this setting accordingly.', {header: header, expected: option});
if(xhr.getResponseHeader(header) && securityHeaders[header].length > 1 && xhr.getResponseHeader(header).toLowerCase() === securityHeaders[header][1].toLowerCase()) {
msg = t('core', 'The "{header}" HTTP header is not set to "{expected}". Some features might not work correctly, as it is recommended to adjust this setting accordingly.', {header: header, expected: option});
}
messages.push({
msg: msg,
type: OC.SetupChecks.MESSAGE_TYPE_WARNING
});
}
}
var xssfields = xhr.getResponseHeader('X-XSS-Protection') ? xhr.getResponseHeader('X-XSS-Protection').split(';').map(function(item) { return item.trim(); }) : [];
if (xssfields.length === 0 || xssfields.indexOf('1') === -1 || xssfields.indexOf('mode=block') === -1) {
messages.push({
msg: t('core', 'The "{header}" HTTP header does not contain "{expected}". This is a potential security or privacy risk, as it is recommended to adjust this setting accordingly.',
{
header: 'X-XSS-Protection',
expected: '1; mode=block'
}),
type: OC.SetupChecks.MESSAGE_TYPE_WARNING
});
}
const referrerPolicy = xhr.getResponseHeader('Referrer-Policy')
if (referrerPolicy === null || !/(no-referrer(-when-downgrade)?|strict-origin(-when-cross-origin)?|same-origin)(,|$)/.test(referrerPolicy)) {
messages.push({
msg: t('core', 'The "{header}" HTTP header is not set to "{val1}", "{val2}", "{val3}", "{val4}" or "{val5}". This can leak referer information. See the {linkstart}W3C Recommendation ↗{linkend}.',
{
header: 'Referrer-Policy',
val1: 'no-referrer',
val2: 'no-referrer-when-downgrade',
val3: 'strict-origin',
val4: 'strict-origin-when-cross-origin',
val5: 'same-origin'
})
.replace('{linkstart}', '<a target="_blank" rel="noreferrer noopener" class="external" href="https://www.w3.org/TR/referrer-policy/">')
.replace('{linkend}', '</a>'),
type: OC.SetupChecks.MESSAGE_TYPE_INFO
})
}
} else {
messages.push({
msg: t('core', 'Error occurred while checking server setup'),
type: OC.SetupChecks.MESSAGE_TYPE_ERROR
});
}
return messages;
},
/**
* Runs check for some SSL configuration issues on the server side
*
* @param {Object} xhr
* @return {Array} Array with error messages
*/
_checkSSL: function(xhr) {
var messages = [];
if (xhr.status === 200) {
var tipsUrl = OC.theme.docPlaceholderUrl.replace('PLACEHOLDER', 'admin-security');
if(OC.getProtocol() === 'https') {
// Extract the value of 'Strict-Transport-Security'
var transportSecurityValidity = xhr.getResponseHeader('Strict-Transport-Security');
if(transportSecurityValidity !== null && transportSecurityValidity.length > 8) {
var firstComma = transportSecurityValidity.indexOf(";");
if(firstComma !== -1) {
transportSecurityValidity = transportSecurityValidity.substring(8, firstComma);
} else {
transportSecurityValidity = transportSecurityValidity.substring(8);
}
}
var minimumSeconds = 15552000;
if(isNaN(transportSecurityValidity) || transportSecurityValidity <= (minimumSeconds - 1)) {
messages.push({
msg: t('core', 'The "Strict-Transport-Security" HTTP header is not set to at least "{seconds}" seconds. For enhanced security, it is recommended to enable HSTS as described in the {linkstart}security tips ↗{linkend}.', {'seconds': minimumSeconds})
.replace('{linkstart}', '<a target="_blank" rel="noreferrer noopener" class="external" href="' + tipsUrl + '">')
.replace('{linkend}', '</a>'),
type: OC.SetupChecks.MESSAGE_TYPE_WARNING
});
}
} else if (!/(?:^(?:localhost|127\.0\.0\.1|::1)|\.onion)$/.exec(window.location.hostname)) {
messages.push({
msg: t('core', 'Accessing site insecurely via HTTP. You are strongly advised to set up your server to require HTTPS instead, as described in the {linkstart}security tips ↗{linkend}. Without it some important web functionality like "copy to clipboard" or "service workers" will not work!')
.replace('{linkstart}', '<a target="_blank" rel="noreferrer noopener" class="external" href="' + tipsUrl + '">')
.replace('{linkend}', '</a>'),
type: OC.SetupChecks.MESSAGE_TYPE_ERROR
});
}
} else {
messages.push({
msg: t('core', 'Error occurred while checking server setup'),
type: OC.SetupChecks.MESSAGE_TYPE_ERROR
});
}
return messages;
}
};
})();
@@ -0,0 +1,46 @@
/*
* DOMParser HTML extension
* 2012-09-04
*
* By Eli Grey, http://eligrey.com
* Public domain.
* NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
*/
/*! @source https://gist.github.com/1129031 */
/*global document, DOMParser*/
(function(DOMParser) {
"use strict";
var
DOMParser_proto = DOMParser.prototype
, real_parseFromString = DOMParser_proto.parseFromString
;
// Firefox/Opera/IE throw errors on unsupported types
try {
// WebKit returns null on unsupported types
if ((new DOMParser).parseFromString("", "text/html")) {
// text/html parsing is natively supported
return;
}
} catch (ex) {}
DOMParser_proto.parseFromString = function(markup, type) {
if (/^\s*text\/html\s*(?:;|$)/i.test(type)) {
var
doc = document.implementation.createHTMLDocument("")
;
if (markup.toLowerCase().indexOf('<!doctype') > -1) {
doc.documentElement.innerHTML = markup;
}
else {
doc.body.innerHTML = markup;
}
return doc;
} else {
return real_parseFromString.apply(this, arguments);
}
};
}(DOMParser));
+200
View File
@@ -0,0 +1,200 @@
/**
* ownCloud
*
* @author Vincent Petry
* @copyright 2014 Vincent Petry <pvince81@owncloud.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
* License as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
/**
* Simulate the variables that are normally set by PHP code
*/
// from core/js/config.php
window.TESTING = true;
window.datepickerFormatDate = 'MM d, yy';
window.dayNames = [
'Sunday',
'Monday',
'Tuesday',
'Wednesday',
'Thursday',
'Friday',
'Saturday'
];
window.dayNamesShort = [
'Sun.',
'Mon.',
'Tue.',
'Wed.',
'Thu.',
'Fri.',
'Sat.'
];
window.dayNamesMin = [
'Su',
'Mo',
'Tu',
'We',
'Th',
'Fr',
'Sa'
];
window.monthNames = [
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December'
];
window.monthNamesShort = [
'Jan.',
'Feb.',
'Mar.',
'Apr.',
'May.',
'Jun.',
'Jul.',
'Aug.',
'Sep.',
'Oct.',
'Nov.',
'Dec.'
];
window.firstDay = 0;
// setup dummy webroots
/* jshint camelcase: false */
window.oc_debug = true;
// Mock @nextcloud/capabilities
window._oc_capabilities = {
files_sharing: {
api_enabled: true
}
}
// FIXME: OC.webroot is supposed to be only the path!!!
window._oc_webroot = location.href + '/';
window._oc_appswebroots = {
"files": window.webroot + '/apps/files/',
"files_sharing": window.webroot + '/apps/files_sharing/'
};
OC.config = {
session_lifetime: 600 * 1000,
session_keepalive: false,
blacklist_files_regex: '\.(part|filepart)$',
};
OC.appConfig = {
core: {}
};
OC.theme = {
docPlaceholderUrl: 'https://docs.example.org/PLACEHOLDER'
};
window.oc_capabilities = {
}
/* jshint camelcase: true */
// mock for Snap.js plugin
window.Snap = function() {};
window.Snap.prototype = {
enable: function() {},
disable: function() {},
close: function() {}
};
window.isPhantom = /phantom/i.test(navigator.userAgent);
document.documentElement.lang = navigator.language;
// global setup for all tests
(function setupTests() {
var fakeServer = null,
$testArea = null,
ajaxErrorStub = null;
/**
* Utility functions for testing
*/
var TestUtil = {
/**
* Returns the image URL set on the given element
* @param $el element
* @return {String} image URL
*/
getImageUrl: function($el) {
// might be slightly different cross-browser
var url = $el.css('background-image');
var r = url.match(/url\(['"]?([^'")]*)['"]?\)/);
if (!r) {
return url;
}
return r[1];
}
};
beforeEach(function() {
// test area for elements that need absolute selector access or measure widths/heights
// which wouldn't work for detached or hidden elements
$testArea = $('<div id="testArea" style="position: absolute; width: 1280px; height: 800px; top: -3000px; left: -3000px; opacity: 0;"></div>');
$('body').append($testArea);
// enforce fake XHR, tests should not depend on the server and
// must use fake responses for expected calls
fakeServer = sinon.fakeServer.create();
// make it globally available, so that other tests can define
// custom responses
window.fakeServer = fakeServer;
if (!OC.TestUtil) {
OC.TestUtil = TestUtil;
}
moment.locale('en');
// reset plugins
OC.Plugins._plugins = [];
// dummy select2 (which isn't loaded during the tests)
$.fn.select2 = function() { return this; };
ajaxErrorStub = sinon.stub(OC, '_processAjaxError');
});
afterEach(function() {
// uncomment this to log requests
// console.log(window.fakeServer.requests);
fakeServer.restore();
$testArea.remove();
delete($.fn.select2);
ajaxErrorStub.restore();
// reset pop state handlers
OC.Util.History._handlers = [];
});
})();
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,866 @@
/**
* ownCloud
*
* @author Vincent Petry
* @copyright 2015 Vincent Petry <pvince81@owncloud.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
* License as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
/* global dav */
describe('OC.Files.Client tests', function() {
var Client = OC.Files.Client;
var baseUrl;
var client;
var requestStub;
var requestDeferred;
beforeEach(function() {
requestDeferred = new $.Deferred();
requestStub = sinon.stub(dav.Client.prototype, 'request').returns(requestDeferred.promise());
baseUrl = 'https://testhost/owncloud/remote.php/webdav/';
client = new Client({
host: 'testhost',
root: '/owncloud/remote.php/webdav',
useHTTPS: true
});
});
afterEach(function() {
client = null;
requestStub.restore();
});
/**
* Send an status response and check that the given
* promise gets its success handler called with the error
* status code
*
* @param {Promise} promise promise
* @param {number} status status to test
*/
function respondAndCheckStatus(promise, status) {
var successHandler = sinon.stub();
var failHandler = sinon.stub();
promise.done(successHandler);
promise.fail(failHandler);
requestDeferred.resolve({
status: status,
body: ''
});
promise.then(function() {
expect(successHandler.calledOnce).toEqual(true);
expect(successHandler.getCall(0).args[0]).toEqual(status);
expect(failHandler.notCalled).toEqual(true);
});
return promise;
}
/**
* Send an error response and check that the given
* promise gets its fail handler called with the error
* status code
*
* @param {Promise} promise promise object
* @param {number} status error status to test
*/
function respondAndCheckError(promise, status) {
var successHandler = sinon.stub();
var failHandler = sinon.stub();
promise.done(successHandler);
promise.fail(failHandler);
var errorXml =
'<d:error xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns">' +
' <s:exception>Sabre\\DAV\\Exception\\SomeException</s:exception>' +
' <s:message>Some error message</s:message>' +
'</d:error>';
var parser = new DOMParser();
requestDeferred.resolve({
status: status,
body: errorXml,
xhr: {
responseXML: parser.parseFromString(errorXml, 'application/xml')
}
});
promise.then(function() {
expect(failHandler.calledOnce).toEqual(true);
expect(failHandler.getCall(0).args[0]).toEqual(status);
expect(failHandler.getCall(0).args[1].status).toEqual(status);
expect(failHandler.getCall(0).args[1].message).toEqual('Some error message');
expect(failHandler.getCall(0).args[1].exception).toEqual('Sabre\\DAV\\Exception\\SomeException');
expect(successHandler.notCalled).toEqual(true);
});
return promise;
}
/**
* Returns a list of request properties parsed from the given request body.
*
* @param {string} requestBody request XML
*
* @return {Array.<String>} array of request properties in the format
* "{NS:}propname"
*/
function getRequestedProperties(requestBody) {
var doc = (new window.DOMParser()).parseFromString(
requestBody,
'application/xml'
);
var propRoots = doc.getElementsByTagNameNS('DAV:', 'prop');
var propsList = propRoots.item(0).childNodes;
return _.map(propsList, function(propNode) {
return '{' + propNode.namespaceURI + '}' + propNode.localName;
});
}
function makePropBlock(props) {
var s = '<d:prop>\n';
_.each(props, function(value, key) {
s += '<' + key + '>' + value + '</' + key + '>\n';
});
return s + '</d:prop>\n';
}
function makeResponseBlock(href, props, failedProps) {
var s = '<d:response>\n';
s += '<d:href>' + href + '</d:href>\n';
s += '<d:propstat>\n';
s += makePropBlock(props);
s += '<d:status>HTTP/1.1 200 OK</d:status>';
s += '</d:propstat>\n';
if (failedProps) {
s += '<d:propstat>\n';
_.each(failedProps, function(prop) {
s += '<' + prop + '/>\n';
});
s += '<d:status>HTTP/1.1 404 Not Found</d:status>\n';
s += '</d:propstat>\n';
}
return s + '</d:response>\n';
}
describe('file listing', function() {
// TODO: switch this to the already parsed structure
var folderContentsXml = dav.Client.prototype.parseMultiStatus(
'<?xml version="1.0" encoding="utf-8"?>' +
'<d:multistatus xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns" xmlns:oc="http://owncloud.org/ns">' +
makeResponseBlock(
'/owncloud/remote.php/webdav/path/to%20space/%E6%96%87%E4%BB%B6%E5%A4%B9/',
{
'd:getlastmodified': 'Fri, 10 Jul 2015 10:00:05 GMT',
'd:getetag': '"56cfcabd79abb"',
'd:resourcetype': '<d:collection/>',
'oc:id': '00000011oc2d13a6a068',
'oc:fileid': '11',
'oc:permissions': 'GRDNVCK',
'oc:size': '120'
},
[
'd:getcontenttype',
'd:getcontentlength'
]
) +
makeResponseBlock(
'/owncloud/remote.php/webdav/path/to%20space/%E6%96%87%E4%BB%B6%E5%A4%B9/One.txt',
{
'd:getlastmodified': 'Fri, 10 Jul 2015 13:38:05 GMT',
'd:getetag': '"559fcabd79a38"',
'd:getcontenttype': 'text/plain',
'd:getcontentlength': 250,
'd:resourcetype': '',
'oc:id': '00000051oc2d13a6a068',
'oc:fileid': '51',
'oc:permissions': 'RDNVW'
},
[
'oc:size',
]
) +
makeResponseBlock(
'/owncloud/remote.php/webdav/path/to%20space/%E6%96%87%E4%BB%B6%E5%A4%B9/sub',
{
'd:getlastmodified': 'Fri, 10 Jul 2015 14:00:00 GMT',
'd:getetag': '"66cfcabd79abb"',
'd:resourcetype': '<d:collection/>',
'oc:id': '00000015oc2d13a6a068',
'oc:fileid': '15',
'oc:permissions': 'GRDNVCK',
'oc:size': '100'
},
[
'd:getcontenttype',
'd:getcontentlength'
]
) +
'</d:multistatus>'
);
it('sends PROPFIND with explicit properties to get file list', function() {
client.getFolderContents('path/to space/文件夹');
expect(requestStub.calledOnce).toEqual(true);
expect(requestStub.lastCall.args[0]).toEqual('PROPFIND');
expect(requestStub.lastCall.args[1]).toEqual(baseUrl + 'path/to%20space/%E6%96%87%E4%BB%B6%E5%A4%B9');
expect(requestStub.lastCall.args[2].Depth).toEqual('1');
var props = getRequestedProperties(requestStub.lastCall.args[3]);
expect(props).toContain('{DAV:}getlastmodified');
expect(props).toContain('{DAV:}getcontentlength');
expect(props).toContain('{DAV:}getcontenttype');
expect(props).toContain('{DAV:}getetag');
expect(props).toContain('{DAV:}resourcetype');
expect(props).toContain('{http://owncloud.org/ns}fileid');
expect(props).toContain('{http://owncloud.org/ns}size');
expect(props).toContain('{http://owncloud.org/ns}permissions');
expect(props).toContain('{http://nextcloud.org/ns}is-encrypted');
});
it('sends PROPFIND to base url when empty path given', function() {
client.getFolderContents('');
expect(requestStub.calledOnce).toEqual(true);
expect(requestStub.lastCall.args[1]).toEqual(baseUrl);
});
it('sends PROPFIND to base url when root path given', function() {
client.getFolderContents('/');
expect(requestStub.calledOnce).toEqual(true);
expect(requestStub.lastCall.args[1]).toEqual(baseUrl);
});
it('parses the result list into a FileInfo array', function() {
var promise = client.getFolderContents('path/to space/文件夹');
expect(requestStub.calledOnce).toEqual(true);
requestDeferred.resolve({
status: 207,
body: folderContentsXml
});
promise.then(function(status, response) {
expect(status).toEqual(207);
expect(_.isArray(response)).toEqual(true);
expect(response.length).toEqual(2);
// file entry
var info = response[0];
expect(info instanceof OC.Files.FileInfo).toEqual(true);
expect(info.id).toEqual(51);
expect(info.path).toEqual('/path/to space/文件夹');
expect(info.name).toEqual('One.txt');
expect(info.permissions).toEqual(26);
expect(info.size).toEqual(250);
expect(info.mtime).toEqual(1436535485000);
expect(info.mimetype).toEqual('text/plain');
expect(info.etag).toEqual('559fcabd79a38');
expect(info.isEncrypted).toEqual(false);
// sub entry
info = response[1];
expect(info instanceof OC.Files.FileInfo).toEqual(true);
expect(info.id).toEqual(15);
expect(info.path).toEqual('/path/to space/文件夹');
expect(info.name).toEqual('sub');
expect(info.permissions).toEqual(31);
expect(info.size).toEqual(100);
expect(info.mtime).toEqual(1436536800000);
expect(info.mimetype).toEqual('httpd/unix-directory');
expect(info.etag).toEqual('66cfcabd79abb');
expect(info.isEncrypted).toEqual(false);
});
});
it('returns parent node in result if specified', function() {
var promise = client.getFolderContents('path/to space/文件夹', {includeParent: true});
expect(requestStub.calledOnce).toEqual(true);
requestDeferred.resolve({
status: 207,
body: folderContentsXml
});
promise.then(function(status, response) {
expect(status).toEqual(207);
expect(_.isArray(response)).toEqual(true);
expect(response.length).toEqual(3);
// root entry
var info = response[0];
expect(info instanceof OC.Files.FileInfo).toEqual(true);
expect(info.id).toEqual(11);
expect(info.path).toEqual('/path/to space');
expect(info.name).toEqual('文件夹');
expect(info.permissions).toEqual(31);
expect(info.size).toEqual(120);
expect(info.mtime).toEqual(1436522405000);
expect(info.mimetype).toEqual('httpd/unix-directory');
expect(info.etag).toEqual('56cfcabd79abb');
expect(info.isEncrypted).toEqual(false);
// the two other entries follow
expect(response[1].id).toEqual(51);
expect(response[2].id).toEqual(15);
});
});
it('rejects promise when an error occurred', function() {
var promise = client.getFolderContents('path/to space/文件夹', {includeParent: true});
respondAndCheckError(promise, 404);
});
it('throws exception if arguments are missing', function() {
// TODO
});
});
describe('file filtering', function() {
// TODO: switch this to the already parsed structure
var folderContentsXml = dav.Client.prototype.parseMultiStatus(
'<?xml version="1.0" encoding="utf-8"?>' +
'<d:multistatus xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns" xmlns:oc="http://owncloud.org/ns">' +
makeResponseBlock(
'/owncloud/remote.php/webdav/path/to%20space/%E6%96%87%E4%BB%B6%E5%A4%B9/',
{
'd:getlastmodified': 'Fri, 10 Jul 2015 10:00:05 GMT',
'd:getetag': '"56cfcabd79abb"',
'd:resourcetype': '<d:collection/>',
'oc:id': '00000011oc2d13a6a068',
'oc:fileid': '11',
'oc:permissions': 'RDNVCK',
'oc:size': '120'
},
[
'd:getcontenttype',
'd:getcontentlength'
]
) +
makeResponseBlock(
'/owncloud/remote.php/webdav/path/to%20space/%E6%96%87%E4%BB%B6%E5%A4%B9/One.txt',
{
'd:getlastmodified': 'Fri, 10 Jul 2015 13:38:05 GMT',
'd:getetag': '"559fcabd79a38"',
'd:getcontenttype': 'text/plain',
'd:getcontentlength': 250,
'd:resourcetype': '',
'oc:id': '00000051oc2d13a6a068',
'oc:fileid': '51',
'oc:permissions': 'RDNVW'
},
[
'oc:size',
]
) +
makeResponseBlock(
'/owncloud/remote.php/webdav/path/to%20space/%E6%96%87%E4%BB%B6%E5%A4%B9/sub',
{
'd:getlastmodified': 'Fri, 10 Jul 2015 14:00:00 GMT',
'd:getetag': '"66cfcabd79abb"',
'd:resourcetype': '<d:collection/>',
'oc:id': '00000015oc2d13a6a068',
'oc:fileid': '15',
'oc:permissions': 'RDNVCK',
'oc:size': '100'
},
[
'd:getcontenttype',
'd:getcontentlength'
]
) +
'</d:multistatus>'
);
it('sends REPORT with filter information', function() {
client.getFilteredFiles({
systemTagIds: ['123', '456']
});
expect(requestStub.calledOnce).toEqual(true);
expect(requestStub.lastCall.args[0]).toEqual('REPORT');
expect(requestStub.lastCall.args[1]).toEqual(baseUrl);
var body = requestStub.lastCall.args[3];
var doc = (new window.DOMParser()).parseFromString(
body,
'application/xml'
);
var ns = 'http://owncloud.org/ns';
expect(doc.documentElement.localName).toEqual('filter-files');
expect(doc.documentElement.namespaceURI).toEqual(ns);
var filterRoots = doc.getElementsByTagNameNS(ns, 'filter-rules');
var rulesList = filterRoots[0] = doc.getElementsByTagNameNS(ns, 'systemtag');
expect(rulesList.length).toEqual(2);
expect(rulesList[0].localName).toEqual('systemtag');
expect(rulesList[0].namespaceURI).toEqual(ns);
expect(rulesList[0].textContent).toEqual('123');
expect(rulesList[1].localName).toEqual('systemtag');
expect(rulesList[1].namespaceURI).toEqual(ns);
expect(rulesList[1].textContent).toEqual('456');
});
it('sends REPORT with explicit properties to filter file list', function() {
client.getFilteredFiles({
systemTagIds: ['123', '456']
});
expect(requestStub.calledOnce).toEqual(true);
expect(requestStub.lastCall.args[0]).toEqual('REPORT');
expect(requestStub.lastCall.args[1]).toEqual(baseUrl);
var props = getRequestedProperties(requestStub.lastCall.args[3]);
expect(props).toContain('{DAV:}getlastmodified');
expect(props).toContain('{DAV:}getcontentlength');
expect(props).toContain('{DAV:}getcontenttype');
expect(props).toContain('{DAV:}getetag');
expect(props).toContain('{DAV:}resourcetype');
expect(props).toContain('{http://owncloud.org/ns}fileid');
expect(props).toContain('{http://owncloud.org/ns}size');
expect(props).toContain('{http://owncloud.org/ns}permissions');
expect(props).toContain('{http://nextcloud.org/ns}is-encrypted');
});
it('parses the result list into a FileInfo array', function() {
var promise = client.getFilteredFiles({
systemTagIds: ['123', '456']
});
expect(requestStub.calledOnce).toEqual(true);
requestDeferred.resolve({
status: 207,
body: folderContentsXml
});
promise.then(function(status, response) {
expect(status).toEqual(207);
expect(_.isArray(response)).toEqual(true);
// returns all entries
expect(response.length).toEqual(3);
// file entry
var info = response[0];
expect(info instanceof OC.Files.FileInfo).toEqual(true);
expect(info.id).toEqual(11);
// file entry
info = response[1];
expect(info instanceof OC.Files.FileInfo).toEqual(true);
expect(info.id).toEqual(51);
// sub entry
info = response[2];
expect(info instanceof OC.Files.FileInfo).toEqual(true);
expect(info.id).toEqual(15);
});
});
it('throws exception if arguments are missing', function() {
var thrown = null;
try {
client.getFilteredFiles({});
} catch (e) {
thrown = true;
}
expect(thrown).toEqual(true);
});
});
describe('file info', function() {
var responseXml = dav.Client.prototype.parseMultiStatus(
'<?xml version="1.0" encoding="utf-8"?>' +
'<d:multistatus xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns" xmlns:oc="http://owncloud.org/ns" xmlns:nc="http://nextcloud.org/ns">' +
makeResponseBlock(
'/owncloud/remote.php/webdav/path/to%20space/%E6%96%87%E4%BB%B6%E5%A4%B9/',
{
'd:getlastmodified': 'Fri, 10 Jul 2015 10:00:05 GMT',
'd:getetag': '"56cfcabd79abb"',
'd:resourcetype': '<d:collection/>',
'oc:id': '00000011oc2d13a6a068',
'oc:fileid': '11',
'oc:permissions': 'GRDNVCK',
'oc:size': '120',
'nc:is-encrypted': '1'
},
[
'd:getcontenttype',
'd:getcontentlength'
]
) +
'</d:multistatus>'
);
it('sends PROPFIND with zero depth to get single file info', function() {
client.getFileInfo('path/to space/文件夹');
expect(requestStub.calledOnce).toEqual(true);
expect(requestStub.lastCall.args[0]).toEqual('PROPFIND');
expect(requestStub.lastCall.args[1]).toEqual(baseUrl + 'path/to%20space/%E6%96%87%E4%BB%B6%E5%A4%B9');
expect(requestStub.lastCall.args[2].Depth).toEqual('0');
var props = getRequestedProperties(requestStub.lastCall.args[3]);
expect(props).toContain('{DAV:}getlastmodified');
expect(props).toContain('{DAV:}getcontentlength');
expect(props).toContain('{DAV:}getcontenttype');
expect(props).toContain('{DAV:}getetag');
expect(props).toContain('{DAV:}resourcetype');
expect(props).toContain('{http://owncloud.org/ns}fileid');
expect(props).toContain('{http://owncloud.org/ns}size');
expect(props).toContain('{http://owncloud.org/ns}permissions');
expect(props).toContain('{http://nextcloud.org/ns}is-encrypted');
});
it('parses the result into a FileInfo', function() {
var promise = client.getFileInfo('path/to space/文件夹');
expect(requestStub.calledOnce).toEqual(true);
requestDeferred.resolve({
status: 207,
body: responseXml
});
promise.then(function(status, response) {
expect(status).toEqual(207);
expect(_.isArray(response)).toEqual(false);
var info = response;
expect(info instanceof OC.Files.FileInfo).toEqual(true);
expect(info.id).toEqual(11);
expect(info.path).toEqual('/path/to space');
expect(info.name).toEqual('文件夹');
expect(info.permissions).toEqual(31);
expect(info.size).toEqual(120);
expect(info.mtime).toEqual(1436522405000);
expect(info.mimetype).toEqual('httpd/unix-directory');
expect(info.etag).toEqual('56cfcabd79abb');
expect(info.isEncrypted).toEqual(true);
});
});
it('properly parses entry inside root', function() {
var responseXml = dav.Client.prototype.parseMultiStatus(
'<?xml version="1.0" encoding="utf-8"?>' +
'<d:multistatus xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns" xmlns:oc="http://owncloud.org/ns">' +
makeResponseBlock(
'/owncloud/remote.php/webdav/in%20root',
{
'd:getlastmodified': 'Fri, 10 Jul 2015 10:00:05 GMT',
'd:getetag': '"56cfcabd79abb"',
'd:resourcetype': '<d:collection/>',
'oc:id': '00000011oc2d13a6a068',
'oc:fileid': '11',
'oc:permissions': 'GRDNVCK',
'oc:size': '120'
},
[
'd:getcontenttype',
'd:getcontentlength'
]
) +
'</d:multistatus>'
);
var promise = client.getFileInfo('in root');
expect(requestStub.calledOnce).toEqual(true);
requestDeferred.resolve({
status: 207,
body: responseXml
});
promise.then(function(status, response) {
expect(status).toEqual(207);
expect(_.isArray(response)).toEqual(false);
var info = response;
expect(info instanceof OC.Files.FileInfo).toEqual(true);
expect(info.id).toEqual(11);
expect(info.path).toEqual('/');
expect(info.name).toEqual('in root');
expect(info.permissions).toEqual(31);
expect(info.size).toEqual(120);
expect(info.mtime).toEqual(1436522405000);
expect(info.mimetype).toEqual('httpd/unix-directory');
expect(info.etag).toEqual('56cfcabd79abb');
expect(info.isEncrypted).toEqual(false);
});
});
it('rejects promise when an error occurred', function() {
var promise = client.getFileInfo('path/to space/文件夹');
respondAndCheckError(promise, 404);
});
it('throws exception if arguments are missing', function() {
// TODO
});
});
describe('permissions', function() {
function getFileInfoWithPermission(webdavPerm, isFile) {
var props = {
'd:getlastmodified': 'Fri, 10 Jul 2015 13:38:05 GMT',
'd:getetag': '"559fcabd79a38"',
'd:getcontentlength': 250,
'oc:id': '00000051oc2d13a6a068',
'oc:fileid': '51',
'oc:permissions': webdavPerm,
};
if (isFile) {
props['d:getcontenttype'] = 'text/plain';
} else {
props['d:resourcetype'] = '<d:collection/>';
}
var def = new $.Deferred();
requestStub.reset();
requestStub.returns(def);
var responseXml = dav.Client.prototype.parseMultiStatus(
'<?xml version="1.0" encoding="utf-8"?>' +
'<d:multistatus xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns" xmlns:oc="http://owncloud.org/ns">' +
makeResponseBlock(
'/owncloud/remote.php/webdav/file.txt',
props
) +
'</d:multistatus>'
);
var promise = client.getFileInfo('file.txt');
expect(requestStub.calledOnce).toEqual(true);
def.resolve({
status: 207,
body: responseXml
});
return promise;
}
function testPermission(permission, isFile, expectedPermissions) {
var promise = getFileInfoWithPermission(permission, isFile);
promise.then(function(status, result) {
expect(result.permissions).toEqual(expectedPermissions);
});
}
function testMountType(permission, isFile, expectedMountType) {
var promise = getFileInfoWithPermission(permission, isFile);
promise.then(function(status, result) {
expect(result.mountType).toEqual(expectedMountType);
});
}
it('properly parses file permissions', function() {
// permission, isFile, expectedPermissions
var testCases = [
['', true, OC.PERMISSION_NONE],
['C', true, OC.PERMISSION_CREATE],
['K', true, OC.PERMISSION_CREATE],
['G', true, OC.PERMISSION_READ],
['W', true, OC.PERMISSION_UPDATE],
['D', true, OC.PERMISSION_DELETE],
['R', true, OC.PERMISSION_SHARE],
['CKGWDR', true, OC.PERMISSION_ALL]
];
_.each(testCases, function(testCase) {
return testPermission.apply(this, testCase);
});
});
it('properly parses mount types', function() {
var testCases = [
['CKGWDR', false, null],
['M', false, 'external'],
['S', false, 'shared'],
['SM', false, 'shared']
];
_.each(testCases, function(testCase) {
return testMountType.apply(this, testCase);
});
});
});
describe('get file contents', function() {
it('returns file contents', function() {
var promise = client.getFileContents('path/to space/文件夹/One.txt');
expect(requestStub.calledOnce).toEqual(true);
expect(requestStub.lastCall.args[0]).toEqual('GET');
expect(requestStub.lastCall.args[1]).toEqual(baseUrl + 'path/to%20space/%E6%96%87%E4%BB%B6%E5%A4%B9/One.txt');
requestDeferred.resolve({
status: 200,
body: 'some contents'
});
promise.then(function(status, response) {
expect(status).toEqual(200);
expect(response).toEqual('some contents');
});
});
it('rejects promise when an error occurred', function() {
var promise = client.getFileContents('path/to space/文件夹/One.txt');
respondAndCheckError(promise, 409);
});
it('throws exception if arguments are missing', function() {
// TODO
});
});
describe('put file contents', function() {
it('sends PUT with file contents', function() {
var promise = client.putFileContents(
'path/to space/文件夹/One.txt',
'some contents'
);
expect(requestStub.calledOnce).toEqual(true);
expect(requestStub.lastCall.args[0]).toEqual('PUT');
expect(requestStub.lastCall.args[1]).toEqual(baseUrl + 'path/to%20space/%E6%96%87%E4%BB%B6%E5%A4%B9/One.txt');
expect(requestStub.lastCall.args[2]['If-None-Match']).toEqual('*');
expect(requestStub.lastCall.args[2]['Content-Type']).toEqual('text/plain;charset=utf-8');
expect(requestStub.lastCall.args[3]).toEqual('some contents');
respondAndCheckStatus(promise, 201);
});
it('sends PUT with file contents with headers matching options', function() {
var promise = client.putFileContents(
'path/to space/文件夹/One.txt',
'some contents',
{
overwrite: false,
contentType: 'text/markdown'
}
);
expect(requestStub.calledOnce).toEqual(true);
expect(requestStub.lastCall.args[0]).toEqual('PUT');
expect(requestStub.lastCall.args[1]).toEqual(baseUrl + 'path/to%20space/%E6%96%87%E4%BB%B6%E5%A4%B9/One.txt');
expect(requestStub.lastCall.args[2]['If-None-Match']).not.toBeDefined();
expect(requestStub.lastCall.args[2]['Content-Type']).toEqual('text/markdown');
expect(requestStub.lastCall.args[3]).toEqual('some contents');
respondAndCheckStatus(promise, 201);
});
it('rejects promise when an error occurred', function() {
var promise = client.putFileContents(
'path/to space/文件夹/One.txt',
'some contents'
);
respondAndCheckError(promise, 409);
});
it('throws exception if arguments are missing', function() {
// TODO
});
});
describe('create directory', function() {
it('sends MKCOL with specified path', function() {
var promise = client.createDirectory('path/to space/文件夹/new dir');
expect(requestStub.calledOnce).toEqual(true);
expect(requestStub.lastCall.args[0]).toEqual('MKCOL');
expect(requestStub.lastCall.args[1]).toEqual(baseUrl + 'path/to%20space/%E6%96%87%E4%BB%B6%E5%A4%B9/new%20dir');
respondAndCheckStatus(promise, 201);
});
it('rejects promise when an error occurred', function() {
var promise = client.createDirectory('path/to space/文件夹/new dir');
respondAndCheckError(promise, 404);
});
it('throws exception if arguments are missing', function() {
// TODO
});
});
describe('deletion', function() {
it('sends DELETE with specified path', function() {
var promise = client.remove('path/to space/文件夹');
expect(requestStub.calledOnce).toEqual(true);
expect(requestStub.lastCall.args[0]).toEqual('DELETE');
expect(requestStub.lastCall.args[1]).toEqual(baseUrl + 'path/to%20space/%E6%96%87%E4%BB%B6%E5%A4%B9');
respondAndCheckStatus(promise, 201);
});
it('rejects promise when an error occurred', function() {
var promise = client.remove('path/to space/文件夹');
respondAndCheckError(promise, 404);
});
it('throws exception if arguments are missing', function() {
// TODO
});
});
describe('move', function() {
it('sends MOVE with specified paths with fail on overwrite by default', function() {
var promise = client.move(
'path/to space/文件夹',
'path/to space/anotherdir/文件夹'
);
expect(requestStub.calledOnce).toEqual(true);
expect(requestStub.lastCall.args[0]).toEqual('MOVE');
expect(requestStub.lastCall.args[1]).toEqual(baseUrl + 'path/to%20space/%E6%96%87%E4%BB%B6%E5%A4%B9');
expect(requestStub.lastCall.args[2].Destination)
.toEqual(baseUrl + 'path/to%20space/anotherdir/%E6%96%87%E4%BB%B6%E5%A4%B9');
expect(requestStub.lastCall.args[2].Overwrite)
.toEqual('F');
respondAndCheckStatus(promise, 201);
});
it('sends MOVE with silent overwrite mode when specified', function() {
var promise = client.move(
'path/to space/文件夹',
'path/to space/anotherdir/文件夹',
{allowOverwrite: true}
);
expect(requestStub.calledOnce).toEqual(true);
expect(requestStub.lastCall.args[0]).toEqual('MOVE');
expect(requestStub.lastCall.args[1]).toEqual(baseUrl + 'path/to%20space/%E6%96%87%E4%BB%B6%E5%A4%B9');
expect(requestStub.lastCall.args[2].Destination)
.toEqual(baseUrl + 'path/to%20space/anotherdir/%E6%96%87%E4%BB%B6%E5%A4%B9');
expect(requestStub.lastCall.args[2].Overwrite)
.not.toBeDefined();
respondAndCheckStatus(promise, 201);
});
it('rejects promise when an error occurred', function() {
var promise = client.move(
'path/to space/文件夹',
'path/to space/anotherdir/文件夹',
{allowOverwrite: true}
);
respondAndCheckError(promise, 404);
});
it('throws exception if arguments are missing', function() {
// TODO
});
});
});
@@ -0,0 +1,225 @@
/**
* Copyright (c) 2015 Roeland Jago Douma <roeland@famdouma.nl>
*
* This file is licensed under the Affero General Public License version 3
* or later.
*
* See the COPYING-README file.
*
*/
describe('jquery.avatar tests', function() {
var $div;
var devicePixelRatio;
beforeEach(function() {
$('#testArea').append($('<div id="avatardiv">'));
$div = $('#avatardiv');
devicePixelRatio = window.devicePixelRatio;
window.devicePixelRatio = 1;
spyOn(window, 'Image').and.returnValue({
onload: function() {
},
onerror: function() {
}
});
});
afterEach(function() {
$div.remove();
window.devicePixelRatio = devicePixelRatio;
});
describe('size', function() {
it('undefined', function() {
$div.avatar('foo');
expect(Math.round($div.height())).toEqual(64);
expect(Math.round($div.width())).toEqual(64);
});
it('undefined but div has height', function() {
$div.height(9);
$div.avatar('foo');
expect(window.Image).toHaveBeenCalled();
window.Image().onerror();
expect(Math.round($div.height())).toEqual(9);
expect(Math.round($div.width())).toEqual(9);
});
it('undefined but data size is set', function() {
$div.data('size', 10);
$div.avatar('foo');
expect(window.Image).toHaveBeenCalled();
window.Image().onerror();
expect(Math.round($div.height())).toEqual(10);
expect(Math.round($div.width())).toEqual(10);
});
it('defined', function() {
$div.avatar('foo', 8);
expect(window.Image).toHaveBeenCalled();
window.Image().onerror();
expect(Math.round($div.height())).toEqual(8);
expect(Math.round($div.width())).toEqual(8);
});
});
it('undefined user', function() {
spyOn($div, 'imageplaceholder');
spyOn($div, 'css');
$div.avatar();
expect($div.imageplaceholder).toHaveBeenCalledWith('?');
expect($div.css).toHaveBeenCalledWith('background-color', '#b9b9b9');
});
describe('no avatar', function() {
it('show placeholder for existing user', function() {
spyOn($div, 'imageplaceholder');
$div.avatar('foo', undefined, undefined, undefined, undefined, 'bar');
expect(window.Image).toHaveBeenCalled();
window.Image().onerror();
expect($div.imageplaceholder).toHaveBeenCalledWith('foo', 'bar');
});
it('show placeholder for non existing user', function() {
spyOn($div, 'imageplaceholder');
spyOn($div, 'css');
$div.avatar('foo');
expect(window.Image).toHaveBeenCalled();
window.Image().onerror();
expect($div.imageplaceholder).toHaveBeenCalledWith('?');
expect($div.css).toHaveBeenCalledWith('background-color', '#b9b9b9');
});
it('show no placeholder is ignored', function() {
spyOn($div, 'imageplaceholder');
spyOn($div, 'css');
$div.avatar('foo', undefined, undefined, true);
expect(window.Image).toHaveBeenCalled();
window.Image().onerror();
expect($div.imageplaceholder).toHaveBeenCalledWith('?');
expect($div.css).toHaveBeenCalledWith('background-color', '#b9b9b9');
});
});
describe('url generation', function() {
beforeEach(function() {
window.devicePixelRatio = 1;
});
it('default', function() {
window.devicePixelRatio = 1;
$div.avatar('foo', 32);
expect(window.Image).toHaveBeenCalled();
expect(window.Image().src).toEqual('http://localhost/index.php/avatar/foo/32');
});
it('high DPI icon', function() {
window.devicePixelRatio = 4;
$div.avatar('foo', 32);
expect(window.Image).toHaveBeenCalled();
expect(window.Image().src).toEqual('http://localhost/index.php/avatar/foo/128');
});
it('high DPI icon round up size', function() {
window.devicePixelRatio = 1.9;
$div.avatar('foo', 32);
expect(window.Image).toHaveBeenCalled();
expect(window.Image().src).toEqual('http://localhost/index.php/avatar/foo/61');
});
});
describe('valid avatar', function() {
beforeEach(function() {
window.devicePixelRatio = 1;
});
it('default (no ie8 fix)', function() {
$div.avatar('foo', 32);
expect(window.Image).toHaveBeenCalled();
window.Image().onload();
expect(window.Image().height).toEqual(32);
expect(window.Image().width).toEqual(32);
expect(window.Image().src).toEqual('http://localhost/index.php/avatar/foo/32');
});
it('default high DPI icon', function() {
window.devicePixelRatio = 1.9;
$div.avatar('foo', 32);
expect(window.Image).toHaveBeenCalled();
window.Image().onload();
expect(window.Image().height).toEqual(32);
expect(window.Image().width).toEqual(32);
expect(window.Image().src).toEqual('http://localhost/index.php/avatar/foo/61');
});
it('with ie8 fix (ignored)', function() {
$div.avatar('foo', 32, true);
expect(window.Image).toHaveBeenCalled();
window.Image().onload();
expect(window.Image().height).toEqual(32);
expect(window.Image().width).toEqual(32);
expect(window.Image().src).toEqual('http://localhost/index.php/avatar/foo/32');
});
it('unhide div', function() {
$div.hide();
$div.avatar('foo', 32);
expect(window.Image).toHaveBeenCalled();
window.Image().onload();
expect(window.Image().height).toEqual(32);
expect(window.Image().width).toEqual(32);
expect(window.Image().src).toEqual('http://localhost/index.php/avatar/foo/32');
});
it('callback called', function() {
var observer = {callback: function() { dump("FOO"); }};
spyOn(observer, 'callback');
$div.avatar('foo', 32, undefined, undefined, function() {
observer.callback();
});
expect(window.Image).toHaveBeenCalled();
window.Image().onload();
expect(window.Image().height).toEqual(32);
expect(window.Image().width).toEqual(32);
expect(window.Image().src).toEqual('http://localhost/index.php/avatar/foo/32');
expect(observer.callback).toHaveBeenCalled();
});
});
});
@@ -0,0 +1,240 @@
/**
* Copyright (c) 2017 Georg Ehrke <oc.list@georgehrke.com>
*
* This file is licensed under the Affero General Public License version 3
* or later.
*
* See the COPYING-README file.
*
*/
describe('jquery.contactsMenu tests', function() {
var $selector1, $selector2, $appendTo;
beforeEach(function() {
$('#testArea').append($('<div id="selector1">'));
$('#testArea').append($('<div id="selector2">'));
$('#testArea').append($('<div id="appendTo">'));
$selector1 = $('#selector1');
$selector2 = $('#selector2');
$appendTo = $('#appendTo');
});
afterEach(function() {
$selector1.off();
$selector1.remove();
$selector2.off();
$selector2.remove();
$appendTo.remove();
});
describe('shareType', function() {
it('stops if type not supported', function() {
$selector1.contactsMenu('user', 1, $appendTo);
expect($appendTo.children().length).toEqual(0);
$selector1.contactsMenu('user', 2, $appendTo);
expect($appendTo.children().length).toEqual(0);
$selector1.contactsMenu('user', 3, $appendTo);
expect($appendTo.children().length).toEqual(0);
$selector1.contactsMenu('user', 5, $appendTo);
expect($appendTo.children().length).toEqual(0);
});
it('append list if shareType supported', function() {
$selector1.contactsMenu('user', 0, $appendTo);
expect($appendTo.children().length).toEqual(1);
expect($appendTo.html().replace(/[\r\n\t]?(\ \ +)?/g, '')).toEqual('<div class="menu popovermenu menu-left hidden contactsmenu-popover"><ul><li><a><span class="icon-loading-small"></span></a></li></ul></div>');
});
});
describe('open on click', function() {
it('with one selector', function() {
$selector1.contactsMenu('user', 0, $appendTo);
expect($appendTo.children().length).toEqual(1);
expect($appendTo.find('div.contactsmenu-popover').hasClass('hidden')).toEqual(true);
$selector1.click();
expect($appendTo.find('div.contactsmenu-popover').hasClass('hidden')).toEqual(false);
});
it('with multiple selectors - 1', function() {
$('#selector1, #selector2').contactsMenu('user', 0, $appendTo);
expect($appendTo.children().length).toEqual(1);
expect($appendTo.find('div.contactsmenu-popover').hasClass('hidden')).toEqual(true);
$selector1.click();
expect($appendTo.find('div.contactsmenu-popover').hasClass('hidden')).toEqual(false);
});
it('with multiple selectors - 2', function() {
$('#selector1, #selector2').contactsMenu('user', 0, $appendTo);
expect($appendTo.children().length).toEqual(1);
expect($appendTo.find('div.contactsmenu-popover').hasClass('hidden')).toEqual(true);
$selector2.click();
expect($appendTo.find('div.contactsmenu-popover').hasClass('hidden')).toEqual(false);
});
it ('should close when clicking the selector again - 1', function() {
$('#selector1, #selector2').contactsMenu('user', 0, $appendTo);
expect($appendTo.children().length).toEqual(1);
expect($appendTo.find('div').hasClass('hidden')).toEqual(true);
$selector1.click();
expect($appendTo.find('div').hasClass('hidden')).toEqual(false);
$selector1.click();
expect($appendTo.find('div').hasClass('hidden')).toEqual(true);
});
it ('should close when clicking the selector again - 1', function() {
$('#selector1, #selector2').contactsMenu('user', 0, $appendTo);
expect($appendTo.children().length).toEqual(1);
expect($appendTo.find('div').hasClass('hidden')).toEqual(true);
$selector1.click();
expect($appendTo.find('div').hasClass('hidden')).toEqual(false);
$selector2.click();
expect($appendTo.find('div').hasClass('hidden')).toEqual(true);
});
});
describe('send requests to the server and render', function() {
it('load a topaction only', function(done) {
$('#selector1, #selector2').contactsMenu('user', 0, $appendTo);
$selector1.click();
expect(fakeServer.requests[0].method).toEqual('POST');
expect(fakeServer.requests[0].url).toEqual('http://localhost/index.php/contactsmenu/findOne');
fakeServer.requests[0].respond(
200,
{ 'Content-Type': 'application/json; charset=utf-8' },
JSON.stringify({
"id": null,
"fullName": "Name 123",
"topAction": {
"title": "bar@baz.wtf",
"icon": "foo.svg",
"hyperlink": "mailto:bar%40baz.wtf"},
"actions": []
})
);
$selector1.on('load', function() {
// FIXME: don't compare HTML one to one but check specific text in the output
expect($appendTo.html().replace(/[\r\n\t]?(\ \ +)?/g, '')).toEqual('<div class="menu popovermenu menu-left contactsmenu-popover loaded" style="display: block;"><ul><li class="hidden"><a><span class="icon-loading-small"></span></a></li><li><a href="mailto:bar%40baz.wtf"><img src="foo.svg"><span>bar@baz.wtf</span></a></li></ul></div>');
done();
});
});
it('load topaction and more actions', function(done) {
$('#selector1, #selector2').contactsMenu('user', 0, $appendTo);
$selector1.click();
fakeServer.requests[0].respond(
200,
{ 'Content-Type': 'application/json; charset=utf-8' },
JSON.stringify({
"id": null,
"fullName": "Name 123",
"topAction": {
"title": "bar@baz.wtf",
"icon": "foo.svg",
"hyperlink": "mailto:bar%40baz.wtf"},
"actions": [{
"title": "Details",
"icon": "details.svg",
"hyperlink": "http:\/\/localhost\/index.php\/apps\/contacts"
}]
})
);
expect(fakeServer.requests[0].method).toEqual('POST');
expect(fakeServer.requests[0].url).toEqual('http://localhost/index.php/contactsmenu/findOne');
$selector1.on('load', function() {
// FIXME: don't compare HTML one to one but check specific text in the output
expect($appendTo.html().replace(/[\r\n\t]?(\ \ +)?/g, '')).toEqual('<div class="menu popovermenu menu-left contactsmenu-popover loaded" style="display: block;"><ul><li class="hidden"><a><span class="icon-loading-small"></span></a></li><li><a href="mailto:bar%40baz.wtf"><img src="foo.svg"><span>bar@baz.wtf</span></a></li><li><a href="http://localhost/index.php/apps/contacts"><img src="details.svg"><span>Details</span></a></li></ul></div>');
done();
});
});
it('load no actions', function(done) {
$('#selector1, #selector2').contactsMenu('user', 0, $appendTo);
$selector1.click();
fakeServer.requests[0].respond(
200,
{ 'Content-Type': 'application/json; charset=utf-8' },
JSON.stringify({
"id": null,
"fullName": "Name 123",
"topAction": null,
"actions": []
})
);
expect(fakeServer.requests[0].method).toEqual('POST');
expect(fakeServer.requests[0].url).toEqual('http://localhost/index.php/contactsmenu/findOne');
$selector1.on('load', function() {
// FIXME: don't compare HTML one to one but check specific text in the output
expect($appendTo.html().replace(/[\r\n\t]?(\ \ +)?/g, '')).toEqual('<div class="menu popovermenu menu-left contactsmenu-popover loaded" style="display: block;"><ul><li class="hidden"><a><span class="icon-loading-small"></span></a></li><li><a href="#"><span>No action available</span></a></li></ul></div>');
done();
});
});
it('should throw an error', function(done) {
$('#selector1, #selector2').contactsMenu('user', 0, $appendTo);
$selector1.click();
fakeServer.requests[0].respond(
400,
{ 'Content-Type': 'application/json; charset=utf-8' },
JSON.stringify([])
);
expect(fakeServer.requests[0].method).toEqual('POST');
expect(fakeServer.requests[0].url).toEqual('http://localhost/index.php/contactsmenu/findOne');
$selector1.on('loaderror', function() {
// FIXME: don't compare HTML one to one but check specific text in the output
expect($appendTo.html().replace(/[\r\n\t]?(\ \ +)?/g, '')).toEqual('<div class="menu popovermenu menu-left contactsmenu-popover loaded" style="display: block;"><ul><li class="hidden"><a><span class="icon-loading-small"></span></a></li><li><a href="#"><span>Error fetching contact actions</span></a></li></ul></div>');
done();
});
});
it('should handle 404', function(done) {
$('#selector1, #selector2').contactsMenu('user', 0, $appendTo);
$selector1.click();
fakeServer.requests[0].respond(
404,
{ 'Content-Type': 'application/json; charset=utf-8' },
JSON.stringify([])
);
expect(fakeServer.requests[0].method).toEqual('POST');
expect(fakeServer.requests[0].url).toEqual('http://localhost/index.php/contactsmenu/findOne');
$selector1.on('loaderror', function() {
// FIXME: don't compare HTML one to one but check specific text in the output
expect($appendTo.html().replace(/[\r\n\t]?(\ \ +)?/g, '')).toEqual('<div class="menu popovermenu menu-left contactsmenu-popover loaded" style="display: block;"><ul><li class="hidden"><a><span class="icon-loading-small"></span></a></li><li><a href="#"><span>No action available</span></a></li></ul></div>');
done();
});
});
it('click anywhere else to close the menu', function() {
$('#selector1, #selector2').contactsMenu('user', 0, $appendTo);
expect($appendTo.find('div').hasClass('hidden')).toEqual(true);
$selector1.click();
expect($appendTo.find('div').hasClass('hidden')).toEqual(false);
$(document).click();
expect($appendTo.find('div').hasClass('hidden')).toEqual(true);
});
});
});
@@ -0,0 +1,43 @@
/**
* Copyright (c) 2019 Serhii Shliakhov <shlyakhov.up@gmail.com>
*
* This file is licensed under the Affero General Public License version 3
* or later.
*
* See the COPYING-README file.
*
*/
describe('jquery.placeholder tests', function() {
var $div;
beforeEach(function() {
$('#testArea').append($('<div id="placeholderdiv">'));
$div = $('#placeholderdiv');
});
afterEach(function() {
$div.remove();
});
describe('placeholder text', function() {
it('shows one first letter if one word in a input text', function() {
spyOn($div, 'html');
$div.imageplaceholder('Seed', 'Name')
expect($div.html).toHaveBeenCalledWith('N');
});
it('shows two first letters if two words in a input text', function() {
spyOn($div, 'html');
$div.imageplaceholder('Seed', 'First Second')
expect($div.html).toHaveBeenCalledWith('FS');
});
it('shows two first letters if more then two words in a input text', function() {
spyOn($div, 'html');
$div.imageplaceholder('Seed', 'First Second Middle')
expect($div.html).toHaveBeenCalledWith('FS');
});
});
});
@@ -0,0 +1,174 @@
/**
* Copyright (c) 2014 Vincent Petry <pvince81@owncloud.com>
*
* This file is licensed under the Affero General Public License version 3
* or later.
*
* See the COPYING-README file.
*
*/
describe('OC.L10N tests', function() {
var TEST_APP = 'jsunittestapp';
beforeEach(function() {
window._oc_appswebroots[TEST_APP] = OC.getRootPath() + '/apps3/jsunittestapp';
});
afterEach(function() {
OC.L10N._unregister(TEST_APP);
delete window._oc_appswebroots[TEST_APP];
});
describe('text translation', function() {
beforeEach(function() {
spyOn(console, 'warn');
OC.L10N.register(TEST_APP, {
'Hello world!': 'Hallo Welt!',
'Hello {name}, the weather is {weather}': 'Hallo {name}, das Wetter ist {weather}',
'sunny': 'sonnig'
});
});
it('returns untranslated text when no bundle exists', function() {
OC.L10N._unregister(TEST_APP);
expect(t(TEST_APP, 'unknown text')).toEqual('unknown text');
});
it('returns untranslated text when no key exists', function() {
expect(t(TEST_APP, 'unknown text')).toEqual('unknown text');
});
it('returns translated text when key exists', function() {
expect(t(TEST_APP, 'Hello world!')).toEqual('Hallo Welt!');
});
it('returns translated text with placeholder', function() {
expect(
t(TEST_APP, 'Hello {name}, the weather is {weather}', {name: 'Steve', weather: t(TEST_APP, 'sunny')})
).toEqual('Hallo Steve, das Wetter ist sonnig');
});
it('returns text with escaped placeholder', function() {
expect(
t(TEST_APP, 'Hello {name}', {name: '<strong>Steve</strong>'})
).toEqual('Hello &lt;strong&gt;Steve&lt;/strong&gt;');
});
it('returns text with not escaped placeholder', function() {
expect(
t(TEST_APP, 'Hello {name}', {name: '<strong>Steve</strong>'}, null, {escape: false})
).toEqual('Hello <strong>Steve</strong>');
});
it('uses DOMPurify to escape the text', function() {
expect(
t(TEST_APP, '<strong>These are your search results<script>alert(1)</script></strong>', null, {escape: false})
).toEqual('<strong>These are your search results</strong>');
});
it('keeps old texts when registering existing bundle', function() {
OC.L10N.register(TEST_APP, {
'sunny': 'sonnig',
'new': 'neu'
});
expect(t(TEST_APP, 'sunny')).toEqual('sonnig');
expect(t(TEST_APP, 'new')).toEqual('neu');
});
});
describe('plurals', function() {
function checkPlurals() {
expect(
n(TEST_APP, 'download %n file', 'download %n files', 0)
).toEqual('0 Dateien herunterladen');
expect(
n(TEST_APP, 'download %n file', 'download %n files', 1)
).toEqual('1 Datei herunterladen');
expect(
n(TEST_APP, 'download %n file', 'download %n files', 2)
).toEqual('2 Dateien herunterladen');
expect(
n(TEST_APP, 'download %n file', 'download %n files', 1024)
).toEqual('1024 Dateien herunterladen');
}
it('generates plural for default text when translation does not exist', function() {
spyOn(console, 'warn');
OC.L10N.register(TEST_APP, {
});
expect(
n(TEST_APP, 'download %n file', 'download %n files', 0)
).toEqual('download 0 files');
expect(
n(TEST_APP, 'download %n file', 'download %n files', 1)
).toEqual('download 1 file');
expect(
n(TEST_APP, 'download %n file', 'download %n files', 2)
).toEqual('download 2 files');
expect(
n(TEST_APP, 'download %n file', 'download %n files', 1024)
).toEqual('download 1024 files');
});
it('generates plural with default function when no forms specified', function() {
spyOn(console, 'warn');
OC.L10N.register(TEST_APP, {
'_download %n file_::_download %n files_':
['%n Datei herunterladen', '%n Dateien herunterladen']
});
checkPlurals();
});
});
describe('async loading of translations', function() {
afterEach(() => {
document.documentElement.removeAttribute('data-locale')
})
it('loads bundle for given app and calls callback', function(done) {
document.documentElement.setAttribute('data-locale', 'zh_CN')
var callbackStub = sinon.stub();
var promiseStub = sinon.stub();
var loading = OC.L10N.load(TEST_APP, callbackStub);
expect(callbackStub.notCalled).toEqual(true);
var req = fakeServer.requests[0];
loading
.then(promiseStub)
.then(function() {
expect(fakeServer.requests.length).toEqual(1);
expect(req.url).toEqual(
OC.getRootPath() + '/apps3/' + TEST_APP + '/l10n/zh_CN.json'
);
expect(callbackStub.calledOnce).toEqual(true);
expect(promiseStub.calledOnce).toEqual(true);
expect(t(TEST_APP, 'Hello world!')).toEqual('你好世界!');
})
.then(done)
.catch(e => expect(e).toBe('No error expected!'));
expect(promiseStub.notCalled).toEqual(true);
req.respond(
200,
{ 'Content-Type': 'application/json' },
JSON.stringify({
translations: {'Hello world!': '你好世界!'},
pluralForm: 'nplurals=2; plural=(n != 1);'
})
);
});
it('calls callback if translation already available', function(done) {
var callbackStub = sinon.stub();
spyOn(console, 'warn');
OC.L10N.register(TEST_APP, {
'Hello world!': 'Hallo Welt!'
});
OC.L10N.load(TEST_APP, callbackStub)
.then(function() {
expect(callbackStub.calledOnce).toEqual(true);
expect(fakeServer.requests.length).toEqual(0);
})
.then(done);
});
it('calls callback if locale is en', function(done) {
var callbackStub = sinon.stub();
OC.L10N.load(TEST_APP, callbackStub)
.then(function() {
expect(callbackStub.calledOnce).toEqual(true);
expect(fakeServer.requests.length).toEqual(0);
})
.then(done)
.catch(done);
});
});
});
@@ -0,0 +1,151 @@
/**
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
describe('MimeType tests', function() {
var _files;
var _aliases;
var _theme;
beforeEach(function() {
_files = OC.MimeTypeList.files;
_aliases = OC.MimeTypeList.aliases;
_theme = OC.MimeTypeList.themes.abc;
OC.MimeTypeList.files = ['folder', 'folder-shared', 'folder-external', 'foo-bar', 'foo', 'file'];
OC.MimeTypeList.aliases = {'app/foobar': 'foo/bar'};
OC.MimeTypeList.themes.abc = ['folder'];
});
afterEach(function() {
OC.MimeTypeList.files = _files;
OC.MimeTypeList.aliases = _aliases;
OC.MimeTypeList.themes.abc = _theme;
});
describe('_getFile', function() {
it('returns the correct icon for "dir"', function() {
var res = OC.MimeType._getFile('dir', OC.MimeTypeList.files);
expect(res).toEqual('folder');
});
it('returns the correct icon for "dir-shared"', function() {
var res = OC.MimeType._getFile('dir-shared', OC.MimeTypeList.files);
expect(res).toEqual('folder-shared');
});
it('returns the correct icon for "dir-external"', function() {
var res = OC.MimeType._getFile('dir-external', OC.MimeTypeList.files);
expect(res).toEqual('folder-external');
});
it('returns the correct icon for a mimetype for which we have an icon', function() {
var res = OC.MimeType._getFile('foo/bar', OC.MimeTypeList.files);
expect(res).toEqual('foo-bar');
});
it('returns the correct icon for a mimetype for which we only have a general mimetype icon', function() {
var res = OC.MimeType._getFile('foo/baz', OC.MimeTypeList.files);
expect(res).toEqual('foo');
});
it('return the file mimetype if we have no matching icon but do have a file icon', function() {
var res = OC.MimeType._getFile('foobar', OC.MimeTypeList.files);
expect(res).toEqual('file');
});
it('return null if we do not have a matching icon', function() {
var res = OC.MimeType._getFile('xyz', []);
expect(res).toEqual(null);
});
});
describe('getIconUrl', function() {
describe('no theme', function() {
var _themeFolder;
beforeEach(function() {
_themeFolder = OC.theme.folder;
OC.theme.folder = '';
//Clear mimetypeIcons caches
OC.MimeType._mimeTypeIcons = {};
});
afterEach(function() {
OC.theme.folder = _themeFolder;
});
it('return undefined if the an icon for undefined is requested', function() {
var res = OC.MimeType.getIconUrl(undefined);
expect(res).toEqual(undefined);
});
it('return the url for the mimetype file', function() {
var res = OC.MimeType.getIconUrl('file');
expect(res).toEqual(OC.getRootPath() + '/core/img/filetypes/file.svg');
});
it('test if the cache works correctly', function() {
OC.MimeType._mimeTypeIcons = {};
expect(Object.keys(OC.MimeType._mimeTypeIcons).length).toEqual(0);
var res = OC.MimeType.getIconUrl('dir');
expect(Object.keys(OC.MimeType._mimeTypeIcons).length).toEqual(1);
expect(OC.MimeType._mimeTypeIcons.dir).toEqual(res);
res = OC.MimeType.getIconUrl('dir-shared');
expect(Object.keys(OC.MimeType._mimeTypeIcons).length).toEqual(2);
expect(OC.MimeType._mimeTypeIcons['dir-shared']).toEqual(res);
});
it('test if alaiases are converted correctly', function() {
var res = OC.MimeType.getIconUrl('app/foobar');
expect(res).toEqual(OC.getRootPath() + '/core/img/filetypes/foo-bar.svg');
expect(OC.MimeType._mimeTypeIcons['foo/bar']).toEqual(res);
});
});
describe('themes', function() {
var _themeFolder;
beforeEach(function() {
_themeFolder = OC.theme.folder;
OC.theme.folder = 'abc';
//Clear mimetypeIcons caches
OC.MimeType._mimeTypeIcons = {};
});
afterEach(function() {
OC.theme.folder = _themeFolder;
});
it('test if theme path is used if a theme icon is availble', function() {
var res = OC.MimeType.getIconUrl('dir');
expect(res).toEqual(OC.getRootPath() + '/themes/abc/core/img/filetypes/folder.svg');
});
it('test if we fallback to the default theme if no icon is available in the theme', function() {
var res = OC.MimeType.getIconUrl('dir-shared');
expect(res).toEqual(OC.getRootPath() + '/core/img/filetypes/folder-shared.svg');
});
});
});
});
@@ -0,0 +1,410 @@
/**
* ownCloud
*
* @author Vincent Petry
* @copyright 2014 Vincent Petry <pvince81@owncloud.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
* License as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
/* global dav */
describe('Backbone Webdav extension', function() {
var davClientRequestStub;
var davClientPropPatchStub;
var davClientPropFindStub;
var deferredRequest;
beforeEach(function() {
deferredRequest = $.Deferred();
davClientRequestStub = sinon.stub(dav.Client.prototype, 'request');
davClientPropPatchStub = sinon.stub(dav.Client.prototype, 'propPatch');
davClientPropFindStub = sinon.stub(dav.Client.prototype, 'propFind');
davClientRequestStub.returns(deferredRequest.promise());
davClientPropPatchStub.returns(deferredRequest.promise());
davClientPropFindStub.returns(deferredRequest.promise());
});
afterEach(function() {
davClientRequestStub.restore();
davClientPropPatchStub.restore();
davClientPropFindStub.restore();
});
describe('collections', function() {
var TestModel;
var TestCollection;
beforeEach(function() {
TestModel = OC.Backbone.Model.extend({
sync: OC.Backbone.davSync,
davProperties: {
'firstName': '{http://owncloud.org/ns}first-name',
'lastName': '{http://owncloud.org/ns}last-name',
'age': '{http://owncloud.org/ns}age',
'married': '{http://owncloud.org/ns}married'
},
parse: function(data) {
return {
id: data.id,
firstName: data.firstName,
lastName: data.lastName,
age: parseInt(data.age, 10),
married: data.married === 'true' || data.married === true
};
}
});
TestCollection = OC.Backbone.Collection.extend({
sync: OC.Backbone.davSync,
model: TestModel,
url: 'http://example.com/owncloud/remote.php/test/'
});
});
it('makes a POST request to create model into collection', function(done) {
var collection = new TestCollection();
var model = collection.create({
firstName: 'Hello',
lastName: 'World'
});
expect(davClientRequestStub.calledOnce).toEqual(true);
expect(davClientRequestStub.getCall(0).args[0])
.toEqual('POST');
expect(davClientRequestStub.getCall(0).args[1])
.toEqual('http://example.com/owncloud/remote.php/test/');
expect(davClientRequestStub.getCall(0).args[2]['Content-Type'])
.toEqual('application/json');
expect(davClientRequestStub.getCall(0).args[2]['X-Requested-With'])
.toEqual('XMLHttpRequest');
expect(davClientRequestStub.getCall(0).args[3])
.toEqual(JSON.stringify({
'firstName': 'Hello',
'lastName': 'World'
}));
var responseHeaderStub = sinon.stub()
.withArgs('Content-Location')
.returns('http://example.com/owncloud/remote.php/test/123');
deferredRequest.resolve({
status: 201,
body: '',
xhr: {
getResponseHeader: responseHeaderStub
}
});
setTimeout(function() {
expect(model.id).toEqual('123');
done();
}, 0)
});
it('uses PROPFIND to retrieve collection', function(done) {
var successStub = sinon.stub();
var errorStub = sinon.stub();
var collection = new TestCollection();
collection.fetch({
success: successStub,
error: errorStub
});
expect(davClientPropFindStub.calledOnce).toEqual(true);
expect(davClientPropFindStub.getCall(0).args[0])
.toEqual('http://example.com/owncloud/remote.php/test/');
expect(davClientPropFindStub.getCall(0).args[1])
.toEqual([
'{http://owncloud.org/ns}first-name',
'{http://owncloud.org/ns}last-name',
'{http://owncloud.org/ns}age',
'{http://owncloud.org/ns}married'
]);
expect(davClientPropFindStub.getCall(0).args[2])
.toEqual(1);
expect(davClientPropFindStub.getCall(0).args[3]['X-Requested-With'])
.toEqual('XMLHttpRequest');
deferredRequest.resolve({
status: 207,
body: [
// root element
{
href: 'http://example.org/owncloud/remote.php/test/',
propStat: []
},
// first model
{
href: 'http://example.org/owncloud/remote.php/test/123',
propStat: [{
status: 'HTTP/1.1 200 OK',
properties: {
'{http://owncloud.org/ns}first-name': 'Hello',
'{http://owncloud.org/ns}last-name': 'World'
}
}]
},
// second model
{
href: 'http://example.org/owncloud/remote.php/test/456',
propStat: [{
status: 'HTTP/1.1 200 OK',
properties: {
'{http://owncloud.org/ns}first-name': 'Test',
'{http://owncloud.org/ns}last-name': 'Person'
}
}]
}
]
});
setTimeout(function() {
expect(collection.length).toEqual(2);
var model = collection.get('123');
expect(model.id).toEqual('123');
expect(model.get('firstName')).toEqual('Hello');
expect(model.get('lastName')).toEqual('World');
model = collection.get('456');
expect(model.id).toEqual('456');
expect(model.get('firstName')).toEqual('Test');
expect(model.get('lastName')).toEqual('Person');
expect(successStub.calledOnce).toEqual(true);
expect(errorStub.notCalled).toEqual(true);
done();
}, 0)
});
function testMethodError(doCall, done) {
var successStub = sinon.stub();
var errorStub = sinon.stub();
doCall(successStub, errorStub);
deferredRequest.resolve({
status: 404,
body: ''
});
setTimeout(function() {
expect(successStub.notCalled).toEqual(true);
expect(errorStub.calledOnce).toEqual(true);
done();
}, 0)
}
it('calls error handler if error status in PROPFIND response', function(done) {
testMethodError(function(success, error) {
var collection = new TestCollection();
collection.fetch({
success: success,
error: error
});
}, done);
});
it('calls error handler if error status in POST response', function(done) {
testMethodError(function(success, error) {
var collection = new TestCollection();
collection.create({
firstName: 'Hello',
lastName: 'World'
}, {
success: success,
error: error
});
}, done);
});
});
describe('models', function() {
var TestModel;
beforeEach(function() {
TestModel = OC.Backbone.Model.extend({
sync: OC.Backbone.davSync,
davProperties: {
'firstName': '{http://owncloud.org/ns}first-name',
'lastName': '{http://owncloud.org/ns}last-name',
'age': '{http://owncloud.org/ns}age', // int
'married': '{http://owncloud.org/ns}married', // bool
},
url: function() {
return 'http://example.com/owncloud/remote.php/test/' + this.id;
},
parse: function(data) {
return {
id: data.id,
firstName: data.firstName,
lastName: data.lastName,
age: parseInt(data.age, 10),
married: data.married === 'true' || data.married === true
};
}
});
});
it('makes a PROPPATCH request to update model', function() {
var model = new TestModel({
id: '123',
firstName: 'Hello',
lastName: 'World',
age: 32,
married: false
});
model.save({
firstName: 'Hey',
age: 33,
married: true
});
expect(davClientPropPatchStub.calledOnce).toEqual(true);
expect(davClientPropPatchStub.getCall(0).args[0])
.toEqual('http://example.com/owncloud/remote.php/test/123');
expect(davClientPropPatchStub.getCall(0).args[1])
.toEqual({
'{http://owncloud.org/ns}first-name': 'Hey',
'{http://owncloud.org/ns}age': '33',
'{http://owncloud.org/ns}married': 'true'
});
expect(davClientPropPatchStub.getCall(0).args[2]['X-Requested-With'])
.toEqual('XMLHttpRequest');
deferredRequest.resolve({
status: 201,
body: ''
});
expect(model.id).toEqual('123');
expect(model.get('firstName')).toEqual('Hey');
expect(model.get('age')).toEqual(33);
expect(model.get('married')).toEqual(true);
});
it('uses PROPFIND to fetch single model', function(done) {
var model = new TestModel({
id: '123'
});
model.fetch();
expect(davClientPropFindStub.calledOnce).toEqual(true);
expect(davClientPropFindStub.getCall(0).args[0])
.toEqual('http://example.com/owncloud/remote.php/test/123');
expect(davClientPropFindStub.getCall(0).args[1])
.toEqual([
'{http://owncloud.org/ns}first-name',
'{http://owncloud.org/ns}last-name',
'{http://owncloud.org/ns}age',
'{http://owncloud.org/ns}married'
]);
expect(davClientPropFindStub.getCall(0).args[2])
.toEqual(0);
expect(davClientPropFindStub.getCall(0).args[3]['X-Requested-With'])
.toEqual('XMLHttpRequest');
deferredRequest.resolve({
status: 207,
body: {
href: 'http://example.org/owncloud/remote.php/test/123',
propStat: [{
status: 'HTTP/1.1 200 OK',
properties: {
'{http://owncloud.org/ns}first-name': 'Hello',
'{http://owncloud.org/ns}last-name': 'World',
'{http://owncloud.org/ns}age': '35',
'{http://owncloud.org/ns}married': 'true'
}
}]
}
});
setTimeout(function() {
expect(model.id).toEqual('123');
expect(model.get('firstName')).toEqual('Hello');
expect(model.get('lastName')).toEqual('World');
expect(model.get('age')).toEqual(35);
expect(model.get('married')).toEqual(true);
done();
});
});
it('makes a DELETE request to destroy model', function() {
var model = new TestModel({
id: '123',
firstName: 'Hello',
lastName: 'World'
});
model.destroy();
expect(davClientRequestStub.calledOnce).toEqual(true);
expect(davClientRequestStub.getCall(0).args[0])
.toEqual('DELETE');
expect(davClientRequestStub.getCall(0).args[1])
.toEqual('http://example.com/owncloud/remote.php/test/123');
expect(davClientRequestStub.getCall(0).args[2]['X-Requested-With'])
.toEqual('XMLHttpRequest');
expect(davClientRequestStub.getCall(0).args[3])
.toBeFalsy();
deferredRequest.resolve({
status: 200,
body: ''
});
});
function testMethodError(doCall, done) {
var successStub = sinon.stub();
var errorStub = sinon.stub();
doCall(successStub, errorStub);
deferredRequest.resolve({
status: 404,
body: ''
});
setTimeout(function() {
expect(successStub.notCalled).toEqual(true);
expect(errorStub.calledOnce).toEqual(true);
done();
});
}
it('calls error handler if error status in PROPFIND response', function(done) {
testMethodError(function(success, error) {
var model = new TestModel();
model.fetch({
success: success,
error: error
});
}, done);
});
it('calls error handler if error status in PROPPATCH response', function(done) {
testMethodError(function(success, error) {
var model = new TestModel();
model.save({
firstName: 'Hey'
}, {
success: success,
error: error
});
}, done);
});
});
});
@@ -0,0 +1,51 @@
/**
* @copyright 2018 Joas Schilling <nickvergessen@owncloud.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
* License as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
describe('OCP.Comments tests', function() {
function dataProvider() {
return [
{input: 'nextcloud.com', expected: 'nextcloud.com'},
{input: 'http://nextcloud.com', expected: '<a class="external" target="_blank" rel="noopener noreferrer" href="http://nextcloud.com">http://nextcloud.com</a>'},
{input: 'https://nextcloud.com', expected: '<a class="external" target="_blank" rel="noopener noreferrer" href="https://nextcloud.com">nextcloud.com</a>'},
{input: 'hi nextcloud.com', expected: 'hi nextcloud.com'},
{input: 'hi http://nextcloud.com', expected: 'hi <a class="external" target="_blank" rel="noopener noreferrer" href="http://nextcloud.com">http://nextcloud.com</a>'},
{input: 'hi https://nextcloud.com', expected: 'hi <a class="external" target="_blank" rel="noopener noreferrer" href="https://nextcloud.com">nextcloud.com</a>'},
{input: 'nextcloud.com foobar', expected: 'nextcloud.com foobar'},
{input: 'http://nextcloud.com foobar', expected: '<a class="external" target="_blank" rel="noopener noreferrer" href="http://nextcloud.com">http://nextcloud.com</a> foobar'},
{input: 'https://nextcloud.com foobar', expected: '<a class="external" target="_blank" rel="noopener noreferrer" href="https://nextcloud.com">nextcloud.com</a> foobar'},
{input: 'hi nextcloud.com foobar', expected: 'hi nextcloud.com foobar'},
{input: 'hi http://nextcloud.com foobar', expected: 'hi <a class="external" target="_blank" rel="noopener noreferrer" href="http://nextcloud.com">http://nextcloud.com</a> foobar'},
{input: 'hi https://nextcloud.com foobar', expected: 'hi <a class="external" target="_blank" rel="noopener noreferrer" href="https://nextcloud.com">nextcloud.com</a> foobar'},
{input: 'hi help.nextcloud.com/category/topic foobar', expected: 'hi help.nextcloud.com/category/topic foobar'},
{input: 'hi http://help.nextcloud.com/category/topic foobar', expected: 'hi <a class="external" target="_blank" rel="noopener noreferrer" href="http://help.nextcloud.com/category/topic">http://help.nextcloud.com/category/topic</a> foobar'},
{input: 'hi https://help.nextcloud.com/category/topic foobar', expected: 'hi <a class="external" target="_blank" rel="noopener noreferrer" href="https://help.nextcloud.com/category/topic">help.nextcloud.com/category/topic</a> foobar'},
{input: 'noreply@nextcloud.com', expected: 'noreply@nextcloud.com'},
{input: 'hi noreply@nextcloud.com', expected: 'hi noreply@nextcloud.com'},
{input: 'hi <noreply@nextcloud.com>', expected: 'hi <noreply@nextcloud.com>'},
{input: 'FirebaseInstanceId.getInstance().deleteInstanceId()', expected: 'FirebaseInstanceId.getInstance().deleteInstanceId()'},
{input: 'I mean...it', expected: 'I mean...it'},
];
}
it('should parse URLs only', function () {
dataProvider().forEach(function(data) {
var result = OCP.Comments.plainToRich(data.input);
expect(result).toEqual(data.expected);
});
});
});
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,69 @@
/**
* ownCloud
*
* @author Joas Schilling
* @copyright 2016 Joas Schilling <nickvergessen@owncloud.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
* License as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
describe('OC.SystemTags tests', function() {
it('describes non existing tag', function() {
var $return = OC.SystemTags.getDescriptiveTag('23');
expect($return.textContent).toEqual('Non-existing tag #23');
expect($return.classList.contains('non-existing-tag')).toEqual(true);
});
it('describes SystemTagModel', function() {
var tag = new OC.SystemTags.SystemTagModel({
id: 23,
name: 'Twenty Three',
userAssignable: true,
userVisible: true
});
var $return = OC.SystemTags.getDescriptiveTag(tag);
expect($return.textContent).toEqual('Twenty Three');
expect($return.classList.contains('non-existing-tag')).toEqual(false);
});
it('describes JSON tag object', function() {
var $return = OC.SystemTags.getDescriptiveTag({
id: 42,
name: 'Fourty Two',
userAssignable: true,
userVisible: true
});
expect($return.textContent).toEqual('Fourty Two');
expect($return.classList.contains('non-existing-tag')).toEqual(false);
});
it('scope', function() {
function testScope(userVisible, userAssignable, expectedText) {
var $return = OC.SystemTags.getDescriptiveTag({
id: 42,
name: 'Fourty Two',
userAssignable: userAssignable,
userVisible: userVisible
});
expect($return.textContent).toEqual(expectedText);
expect($return.classList.contains('non-existing-tag')).toEqual(false);
}
testScope(true, true, 'Fourty Two');
testScope(false, true, 'Fourty Two (Invisible)');
testScope(false, false, 'Fourty Two (Invisible)');
testScope(true, false, 'Fourty Two (Restricted)');
});
});
@@ -0,0 +1,84 @@
/**
* ownCloud
*
* @author Vincent Petry
* @copyright 2016 Vincent Petry <pvince81@owncloud.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
* License as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
describe('OC.SystemTags.SystemTagsCollection tests', function() {
var collection;
beforeEach(function() {
collection = new OC.SystemTags.SystemTagsCollection();
});
it('fetches only once, until reset', function() {
var syncStub = sinon.stub(collection, 'sync');
var callback = sinon.stub();
var callback2 = sinon.stub();
var callback3 = sinon.stub();
var eventHandler = sinon.stub();
collection.on('sync', eventHandler);
collection.fetch({
success: callback
});
expect(callback.notCalled).toEqual(true);
expect(syncStub.calledOnce).toEqual(true);
expect(eventHandler.notCalled).toEqual(true);
syncStub.yieldTo('success', collection);
expect(callback.calledOnce).toEqual(true);
expect(callback.firstCall.args[0]).toEqual(collection);
expect(eventHandler.calledOnce).toEqual(true);
expect(eventHandler.firstCall.args[0]).toEqual(collection);
collection.fetch({
success: callback2
});
expect(eventHandler.calledTwice).toEqual(true);
expect(eventHandler.secondCall.args[0]).toEqual(collection);
// not re-called
expect(syncStub.calledOnce).toEqual(true);
expect(callback.calledOnce).toEqual(true);
expect(callback2.calledOnce).toEqual(true);
expect(callback2.firstCall.args[0]).toEqual(collection);
expect(collection.fetched).toEqual(true);
collection.reset();
expect(collection.fetched).toEqual(false);
collection.fetch({
success: callback3
});
expect(syncStub.calledTwice).toEqual(true);
syncStub.yieldTo('success', collection);
expect(callback3.calledOnce).toEqual(true);
expect(callback3.firstCall.args[0]).toEqual(collection);
syncStub.restore();
});
});
@@ -0,0 +1,629 @@
/**
* ownCloud
*
* @author Vincent Petry
* @copyright 2016 Vincent Petry <pvince81@owncloud.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
* License as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
describe('OC.SystemTags.SystemTagsInputField tests', function() {
var view, select2Stub, clock;
beforeEach(function() {
clock = sinon.useFakeTimers();
var $container = $('<div class="testInputContainer"></div>');
select2Stub = sinon.stub($.fn, 'select2');
select2Stub.returnsThis();
$('#testArea').append($container);
});
afterEach(function() {
select2Stub.restore();
OC.SystemTags.collection.reset();
clock.restore();
view.remove();
view = undefined;
});
describe('general behavior', function() {
var $dropdown;
beforeEach(function() {
view = new OC.SystemTags.SystemTagsInputField();
$('.testInputContainer').append(view.$el);
$dropdown = $('<div class="select2-dropdown"></div>');
select2Stub.withArgs('dropdown').returns($dropdown);
$('#testArea').append($dropdown);
view.render();
});
describe('rendering', function() {
it('calls select2 on rendering', function() {
expect(view.$el.find('input[name=tags]').length).toEqual(1);
expect(select2Stub.called).toEqual(true);
});
it('formatResult renders rename button', function() {
var opts = select2Stub.getCall(0).args[0];
var $el = $(opts.formatResult({id: '1', name: 'test'}));
expect($el.find('.rename').length).toEqual(1);
});
});
describe('tag selection', function() {
beforeEach(function() {
var $el = view.$el.find('input');
$el.val('1');
view.collection.add([
new OC.SystemTags.SystemTagModel({id: '1', name: 'abc'}),
new OC.SystemTags.SystemTagModel({id: '2', name: 'def'}),
new OC.SystemTags.SystemTagModel({id: '3', name: 'abd', userAssignable: false, canAssign: false}),
]);
});
it('does not create dummy tag when user types non-matching name', function() {
var opts = select2Stub.getCall(0).args[0];
var result = opts.createSearchChoice('abc');
expect(result).not.toBeDefined();
});
it('creates dummy tag when user types non-matching name', function() {
var opts = select2Stub.getCall(0).args[0];
var result = opts.createSearchChoice('abnew');
expect(result.id).toEqual(-1);
expect(result.name).toEqual('abnew');
expect(result.isNew).toEqual(true);
expect(result.userVisible).toEqual(true);
expect(result.userAssignable).toEqual(true);
expect(result.canAssign).toEqual(true);
});
it('creates dummy tag when user types non-matching name even with prefix of existing tag', function() {
var opts = select2Stub.getCall(0).args[0];
var result = opts.createSearchChoice('ab');
expect(result.id).toEqual(-1);
expect(result.name).toEqual('ab');
expect(result.isNew).toEqual(true);
expect(result.userVisible).toEqual(true);
expect(result.userAssignable).toEqual(true);
expect(result.canAssign).toEqual(true);
});
it('creates the real tag and fires select event after user selects the dummy tag', function() {
var selectHandler = sinon.stub();
view.on('select', selectHandler);
var createStub = sinon.stub(OC.SystemTags.SystemTagsCollection.prototype, 'create');
view.$el.find('input').trigger(new $.Event('select2-selecting', {
object: {
id: -1,
name: 'newname',
isNew: true
}
}));
expect(createStub.calledOnce).toEqual(true);
expect(createStub.getCall(0).args[0]).toEqual({
name: 'newname',
userVisible: true,
userAssignable: true,
canAssign: true
});
var newModel = new OC.SystemTags.SystemTagModel({
id: '123',
name: 'newname',
userVisible: true,
userAssignable: true,
canAssign: true
});
// not called yet
expect(selectHandler.notCalled).toEqual(true);
select2Stub.withArgs('data').returns([{
id: '1',
name: 'abc'
}]);
createStub.yieldTo('success', newModel);
expect(select2Stub.lastCall.args[0]).toEqual('data');
expect(select2Stub.lastCall.args[1]).toEqual([{
id: '1',
name: 'abc'
},
newModel.toJSON()
]);
expect(selectHandler.calledOnce).toEqual(true);
expect(selectHandler.getCall(0).args[0]).toEqual(newModel);
createStub.restore();
});
it('triggers select event after selecting an existing tag', function() {
var selectHandler = sinon.stub();
view.on('select', selectHandler);
view.$el.find('input').trigger(new $.Event('select2-selecting', {
object: {
id: '2',
name: 'def'
}
}));
expect(selectHandler.calledOnce).toEqual(true);
expect(selectHandler.getCall(0).args[0]).toEqual(view.collection.get('2'));
});
it('triggers deselect event after deselecting an existing tag', function() {
var selectHandler = sinon.stub();
view.on('deselect', selectHandler);
view.$el.find('input').trigger(new $.Event('select2-removing', {
choice: {
id: '2',
name: 'def'
}
}));
expect(selectHandler.calledOnce).toEqual(true);
expect(selectHandler.getCall(0).args[0]).toEqual('2');
});
it('triggers select event and still adds to list even in case of conflict', function() {
var selectHandler = sinon.stub();
view.on('select', selectHandler);
var fetchStub = sinon.stub(OC.SystemTags.SystemTagsCollection.prototype, 'fetch');
var createStub = sinon.stub(OC.SystemTags.SystemTagsCollection.prototype, 'create');
view.$el.find('input').trigger(new $.Event('select2-selecting', {
object: {
id: -1,
name: 'newname',
isNew: true
}
}));
expect(createStub.calledOnce).toEqual(true);
expect(createStub.getCall(0).args[0]).toEqual({
name: 'newname',
userVisible: true,
userAssignable: true,
canAssign: true
});
var newModel = new OC.SystemTags.SystemTagModel({
id: '123',
name: 'newname',
userVisible: true,
userAssignable: true
});
// not called yet
expect(selectHandler.notCalled).toEqual(true);
select2Stub.withArgs('data').returns([{
id: '1',
name: 'abc'
}]);
// simulate conflict response for tag creation
createStub.yieldTo('error', view.collection, {status: 409});
// at this point it fetches from the server
expect(fetchStub.calledOnce).toEqual(true);
// simulate fetch result by adding model to the collection
view.collection.add(newModel);
fetchStub.yieldTo('success', view.collection);
expect(select2Stub.lastCall.args[0]).toEqual('data');
expect(select2Stub.lastCall.args[1]).toEqual([{
id: '1',
name: 'abc'
},
newModel.toJSON()
]);
// select event still called
expect(selectHandler.calledOnce).toEqual(true);
expect(selectHandler.getCall(0).args[0]).toEqual(newModel);
createStub.restore();
fetchStub.restore();
});
});
describe('tag actions', function() {
var opts;
beforeEach(function() {
opts = select2Stub.getCall(0).args[0];
view.collection.add([
new OC.SystemTags.SystemTagModel({id: '1', name: 'abc'}),
]);
$dropdown.append(opts.formatResult(view.collection.get('1').toJSON()));
});
it('displays rename form when clicking rename', function() {
$dropdown.find('.rename').mouseup();
expect($dropdown.find('form.systemtags-rename-form').length).toEqual(1);
expect($dropdown.find('form.systemtags-rename-form input').val()).toEqual('abc');
});
it('renames model and submits change when submitting form', function() {
var saveStub = sinon.stub(OC.SystemTags.SystemTagModel.prototype, 'save');
$dropdown.find('.rename').mouseup();
$dropdown.find('form input').val('abc_renamed');
$dropdown.find('form').trigger(new $.Event('submit'));
expect(saveStub.calledOnce).toEqual(true);
expect(saveStub.getCall(0).args[0]).toEqual({'name': 'abc_renamed'});
expect($dropdown.find('.label').text()).toEqual('abc_renamed');
expect($dropdown.find('form').length).toEqual(0);
saveStub.restore();
});
});
describe('setting data', function() {
it('sets value when calling setValues', function() {
var vals = ['1', '2'];
view.setValues(vals);
expect(select2Stub.lastCall.args[0]).toEqual('val');
expect(select2Stub.lastCall.args[1]).toEqual(vals);
});
it('sets data when calling setData', function() {
var vals = [{id: '1', name: 'test1'}, {id: '2', name: 'test2'}];
view.setData(vals);
expect(select2Stub.lastCall.args[0]).toEqual('data');
expect(select2Stub.lastCall.args[1]).toEqual(vals);
});
});
});
describe('as admin', function() {
var $dropdown;
beforeEach(function() {
view = new OC.SystemTags.SystemTagsInputField({
isAdmin: true
});
$('.testInputContainer').append(view.$el);
$dropdown = $('<div class="select2-dropdown"></div>');
select2Stub.withArgs('dropdown').returns($dropdown);
$('#testArea').append($dropdown);
view.render();
});
it('formatResult renders tag name with visibility', function() {
var opts = select2Stub.getCall(0).args[0];
var $el = $(opts.formatResult({id: '1', name: 'test', userVisible: false, userAssignable: false}));
expect($el.find('.label').text()).toEqual('test (Invisible)');
});
it('formatSelection renders tag name with visibility', function() {
var opts = select2Stub.getCall(0).args[0];
var $el = $(opts.formatSelection({id: '1', name: 'test', userVisible: false, userAssignable: false}));
expect($el.text().trim()).toEqual('test (Invisible)');
});
describe('initSelection', function() {
var fetchStub;
var testTags;
beforeEach(function() {
fetchStub = sinon.stub(OC.SystemTags.SystemTagsCollection.prototype, 'fetch');
testTags = [
new OC.SystemTags.SystemTagModel({id: '1', name: 'test1'}),
new OC.SystemTags.SystemTagModel({id: '2', name: 'test2'}),
new OC.SystemTags.SystemTagModel({id: '3', name: 'test3', userAssignable: false, canAssign: false}),
new OC.SystemTags.SystemTagModel({id: '4', name: 'test4', userAssignable: false, canAssign: true})
];
});
afterEach(function() {
fetchStub.restore();
});
it('grabs values from the full collection', function() {
var $el = view.$el.find('input');
$el.val('1,3,4');
var opts = select2Stub.getCall(0).args[0];
var callback = sinon.stub();
opts.initSelection($el, callback);
expect(fetchStub.calledOnce).toEqual(true);
view.collection.add(testTags);
fetchStub.yieldTo('success', view.collection);
expect(callback.calledOnce).toEqual(true);
var models = callback.getCall(0).args[0];
expect(models.length).toEqual(3);
expect(models[0].id).toEqual('1');
expect(models[0].name).toEqual('test1');
expect(models[0].locked).toBeFalsy();
expect(models[1].id).toEqual('3');
expect(models[1].name).toEqual('test3');
expect(models[1].locked).toBeFalsy();
expect(models[2].id).toEqual('4');
expect(models[2].name).toEqual('test4');
expect(models[2].locked).toBeFalsy();
});
});
describe('autocomplete', function() {
var fetchStub, opts;
beforeEach(function() {
fetchStub = sinon.stub(OC.SystemTags.SystemTagsCollection.prototype, 'fetch');
opts = select2Stub.getCall(0).args[0];
view.collection.add([
new OC.SystemTags.SystemTagModel({id: '1', name: 'abc'}),
new OC.SystemTags.SystemTagModel({id: '2', name: 'def'}),
new OC.SystemTags.SystemTagModel({id: '3', name: 'abd', userAssignable: false, canAssign: false}),
new OC.SystemTags.SystemTagModel({id: '4', name: 'Deg'}),
]);
});
afterEach(function() {
fetchStub.restore();
});
it('completes results', function() {
var callback = sinon.stub();
opts.query({
term: 'ab',
callback: callback
});
expect(fetchStub.calledOnce).toEqual(true);
fetchStub.yieldTo('success', view.collection);
expect(callback.calledOnce).toEqual(true);
expect(callback.getCall(0).args[0].results).toEqual([
{
id: '1',
name: 'abc',
userVisible: true,
userAssignable: true,
canAssign: true
},
{
id: '3',
name: 'abd',
userVisible: true,
userAssignable: false,
canAssign: false
}
]);
});
it('completes case insensitive', function() {
var callback = sinon.stub();
opts.query({
term: 'de',
callback: callback
});
expect(fetchStub.calledOnce).toEqual(true);
fetchStub.yieldTo('success', view.collection);
expect(callback.calledOnce).toEqual(true);
expect(callback.getCall(0).args[0].results).toEqual([
{
id: '2',
name: 'def',
userVisible: true,
userAssignable: true,
canAssign: true
},
{
id: '4',
name: 'Deg',
userVisible: true,
userAssignable: true,
canAssign: true
}
]);
});
});
describe('tag actions', function() {
var opts;
beforeEach(function() {
opts = select2Stub.getCall(0).args[0];
view.collection.add([
new OC.SystemTags.SystemTagModel({id: '1', name: 'abc'}),
]);
$dropdown.append(opts.formatResult(view.collection.get('1').toJSON()));
});
it('deletes model and submits change when clicking delete', function() {
var destroyStub = sinon.stub(OC.SystemTags.SystemTagModel.prototype, 'destroy');
expect($dropdown.find('.delete').length).toEqual(0);
$dropdown.find('.rename').mouseup();
// delete button appears
expect($dropdown.find('.delete').length).toEqual(1);
$dropdown.find('.delete').mouseup();
expect(destroyStub.calledOnce).toEqual(true);
expect(destroyStub.calledOn(view.collection.get('1')));
destroyStub.restore();
});
});
});
describe('as user', function() {
var $dropdown;
beforeEach(function() {
view = new OC.SystemTags.SystemTagsInputField({
isAdmin: false
});
$('.testInputContainer').append(view.$el);
$dropdown = $('<div class="select2-dropdown"></div>');
select2Stub.withArgs('dropdown').returns($dropdown);
$('#testArea').append($dropdown);
view.render();
});
it('formatResult renders tag name only', function() {
var opts = select2Stub.getCall(0).args[0];
var $el = $(opts.formatResult({id: '1', name: 'test'}));
expect($el.find('.label').text()).toEqual('test');
});
it('formatSelection renders tag name only', function() {
var opts = select2Stub.getCall(0).args[0];
var $el = $(opts.formatSelection({id: '1', name: 'test'}));
expect($el.text().trim()).toEqual('test');
});
describe('initSelection', function() {
var fetchStub;
var testTags;
beforeEach(function() {
fetchStub = sinon.stub(OC.SystemTags.SystemTagsCollection.prototype, 'fetch');
testTags = [
new OC.SystemTags.SystemTagModel({id: '1', name: 'test1'}),
new OC.SystemTags.SystemTagModel({id: '2', name: 'test2'}),
new OC.SystemTags.SystemTagModel({id: '3', name: 'test3', userAssignable: false, canAssign: false}),
new OC.SystemTags.SystemTagModel({id: '4', name: 'test4', userAssignable: false, canAssign: true})
];
view.render();
});
afterEach(function() {
fetchStub.restore();
});
it('grabs values from the full collection', function() {
var $el = view.$el.find('input');
$el.val('1,3,4');
var opts = select2Stub.getCall(0).args[0];
var callback = sinon.stub();
opts.initSelection($el, callback);
expect(fetchStub.calledOnce).toEqual(true);
view.collection.add(testTags);
fetchStub.yieldTo('success', view.collection);
expect(callback.calledOnce).toEqual(true);
var models = callback.getCall(0).args[0];
expect(models.length).toEqual(3);
expect(models[0].id).toEqual('1');
expect(models[0].name).toEqual('test1');
expect(models[0].locked).toBeFalsy();
expect(models[1].id).toEqual('3');
expect(models[1].name).toEqual('test3');
// restricted / cannot assign locks the entry
expect(models[1].locked).toEqual(true);
expect(models[2].id).toEqual('4');
expect(models[2].name).toEqual('test4');
expect(models[2].locked).toBeFalsy();
});
});
describe('autocomplete', function() {
var fetchStub, opts;
beforeEach(function() {
fetchStub = sinon.stub(OC.SystemTags.SystemTagsCollection.prototype, 'fetch');
view.render();
opts = select2Stub.getCall(0).args[0];
view.collection.add([
new OC.SystemTags.SystemTagModel({id: '1', name: 'abc'}),
new OC.SystemTags.SystemTagModel({id: '2', name: 'def'}),
new OC.SystemTags.SystemTagModel({id: '3', name: 'abd', userAssignable: false, canAssign: false}),
new OC.SystemTags.SystemTagModel({id: '4', name: 'Deg'}),
new OC.SystemTags.SystemTagModel({id: '5', name: 'abe', userAssignable: false, canAssign: true})
]);
});
afterEach(function() {
fetchStub.restore();
});
it('completes results excluding non-assignable tags', function() {
var callback = sinon.stub();
opts.query({
term: 'ab',
callback: callback
});
expect(fetchStub.calledOnce).toEqual(true);
fetchStub.yieldTo('success', view.collection);
expect(callback.calledOnce).toEqual(true);
expect(callback.getCall(0).args[0].results).toEqual([
{
id: '1',
name: 'abc',
userVisible: true,
userAssignable: true,
canAssign: true
},
{
id: '5',
name: 'abe',
userVisible: true,
userAssignable: false,
canAssign: true
}
]);
});
it('completes case insensitive', function() {
var callback = sinon.stub();
opts.query({
term: 'de',
callback: callback
});
expect(fetchStub.calledOnce).toEqual(true);
fetchStub.yieldTo('success', view.collection);
expect(callback.calledOnce).toEqual(true);
expect(callback.getCall(0).args[0].results).toEqual([
{
id: '2',
name: 'def',
userVisible: true,
userAssignable: true,
canAssign: true
},
{
id: '4',
name: 'Deg',
userVisible: true,
userAssignable: true,
canAssign: true
}
]);
});
});
describe('tag actions', function() {
var opts;
beforeEach(function() {
opts = select2Stub.getCall(0).args[0];
view.collection.add([
new OC.SystemTags.SystemTagModel({id: '1', name: 'abc'}),
]);
$dropdown.append(opts.formatResult(view.collection.get('1').toJSON()));
});
it('deletes model and submits change when clicking delete', function() {
var destroyStub = sinon.stub(OC.SystemTags.SystemTagModel.prototype, 'destroy');
expect($dropdown.find('.delete').length).toEqual(0);
$dropdown.find('.rename').mouseup();
// delete button appears only for admins
expect($dropdown.find('.delete').length).toEqual(0);
$dropdown.find('.delete').mouseup();
expect(destroyStub.notCalled).toEqual(true);
destroyStub.restore();
});
});
});
});
+168
View File
@@ -0,0 +1,168 @@
/*
* Copyright (c) 2014
*
* This file is licensed under the Affero General Public License version 3
* or later.
*
* See the COPYING-README file.
*
*/
(function() {
OC.Update = {
_started : false,
options: {},
/**
* Start the update process.
*
* @param $el progress list element
*/
start: function($el, options) {
if (this._started) {
return;
}
this.options = options;
var hasWarnings = false;
this.$el = $el;
this._started = true;
var self = this;
$(window).on('beforeunload.inprogress', function () {
return t('core', 'The update is in progress, leaving this page might interrupt the process in some environments.');
});
$('#update-progress-title').html(t(
'core',
'Update to {version}', {
version: options.version
})
);
var updateEventSource = new OC.EventSource(OC.getRootPath()+'/core/ajax/update.php');
updateEventSource.listen('success', function(message) {
self.setMessage(message);
});
updateEventSource.listen('notice', function(message) {
self.setPermanentMessage(message);
hasWarnings = true;
});
updateEventSource.listen('error', function(message) {
$('#update-progress-message').hide();
$('#update-progress-icon')
.addClass('icon-error-white')
.removeClass('icon-loading-dark');
message = message || t('core', 'An error occurred.');
$(window).off('beforeunload.inprogress');
self.setErrorMessage(message);
message = t('core', 'Please reload the page.');
$('<p>').append('<a href=".">'+message+'</a>').appendTo($el);
updateEventSource.close();
});
updateEventSource.listen('failure', function(message) {
$(window).off('beforeunload.inprogress');
$('#update-progress-message').hide();
$('#update-progress-icon')
.addClass('icon-error-white')
.removeClass('icon-loading-dark');
self.setErrorMessage(message);
var updateUnsuccessful = $('<p>');
if(message === 'Exception: Updates between multiple major versions and downgrades are unsupported.') {
updateUnsuccessful.append(t('core', 'The update was unsuccessful. For more information <a href="{url}">check our forum post</a> covering this issue.', {'url': 'https://help.nextcloud.com/t/updates-between-multiple-major-versions-are-unsupported/7094'}));
} else if (OC.Update.options.productName === 'Nextcloud') {
updateUnsuccessful.append(t('core', 'The update was unsuccessful. ' +
'Please report this issue to the ' +
'<a href="https://github.com/nextcloud/server/issues" target="_blank">Nextcloud community</a>.'));
}
updateUnsuccessful.appendTo($el);
});
updateEventSource.listen('done', function() {
$(window).off('beforeunload.inprogress');
$('#update-progress-message').hide();
$('#update-progress-icon')
.addClass('icon-checkmark-white')
.removeClass('icon-loading-dark');
if (hasWarnings) {
$el.find('.update-show-detailed').before(
$('<input type="button" class="primary" value="'+t('core', 'Continue to {productName}', OC.Update.options)+'">').on('click', function() {
window.location.reload();
})
);
} else {
$el.find('.update-show-detailed').before(
$('<p id="redirect-countdown"></p>')
);
for(var i = 0; i <= 4; i++){
self.updateCountdown(i, 4);
}
setTimeout(function () {
OC.redirect(window.location.href);
}, 3000);
}
});
},
updateCountdown: function (i, total) {
setTimeout(function(){
$("#redirect-countdown").text(
n('core', 'The update was successful. Redirecting you to {productName} in %n second.', 'The update was successful. Redirecting you to {productName} in %n seconds.', i, OC.Update.options)
);
}, (total - i) * 1000);
},
setMessage: function(message) {
$('#update-progress-message').html(message);
$('#update-progress-detailed')
.append('<p>' + message + '</p>');
},
setPermanentMessage: function(message) {
$('#update-progress-message').html(message);
$('#update-progress-message-warnings')
.show()
.append($('<ul>').append(message));
$('#update-progress-detailed')
.append('<p>' + message + '</p>');
},
setErrorMessage: function (message) {
$('#update-progress-message-error')
.show()
.html(message);
$('#update-progress-detailed')
.append('<p>' + message + '</p>');
}
};
})();
window.addEventListener('DOMContentLoaded', function() {
$('.updateButton').on('click', function() {
var $updateEl = $('.update');
var $progressEl = $('.update-progress');
$progressEl.removeClass('hidden');
$('.updateOverview').addClass('hidden');
$('#update-progress-message-error').hide();
$('#update-progress-message-warnings').hide();
OC.Update.start($progressEl, {
productName: $updateEl.attr('data-productname'),
version: $updateEl.attr('data-version')
});
return false;
});
$('.update-show-detailed').on('click', function() {
$('#update-progress-detailed').toggleClass('hidden');
return false;
});
});