diff --git a/.scrutinizer.yml b/.scrutinizer.yml index 57b885688543fa39b7d618f82d3a830d77f82ac4..d1dbb20139fc968d08681cc9f2aa08f0d3fac767 100644 --- a/.scrutinizer.yml +++ b/.scrutinizer.yml @@ -8,7 +8,9 @@ filter: - 'lib/l10n/*' - 'core/js/tests/lib/*.js' - 'core/js/tests/specs/*.js' + - 'core/js/jquery-1.10.0.js' - 'core/js/jquery-1.10.0.min.js' + - 'core/js/jquery-migrate-1.2.1.js' - 'core/js/jquery-migrate-1.2.1.min.js' - 'core/js/jquery-showpassword.js' - 'core/js/jquery-tipsy.js' @@ -17,6 +19,7 @@ filter: - 'core/js/jquery.inview.js' - 'core/js/jquery.placeholder.js' - 'core/js/underscore.js' + - 'core/js/jquery.multiselect.js' imports: diff --git a/README.md b/README.md index 032a7e987f288a9be16cec29c398cda3359037e4..0b13f11e76803e46019218525941e769a8f8d9fe 100644 --- a/README.md +++ b/README.md @@ -9,14 +9,14 @@ Git master: [![Build Status](https://ci.owncloud.org/job/server-master-linux/bad Quality: [![Scrutinizer Quality Score](https://scrutinizer-ci.com/g/owncloud/core/badges/quality-score.png?s=ce2f5ded03d4ac628e9ee5c767243fa7412e644f)](https://scrutinizer-ci.com/g/owncloud/core/) ### Installation instructions -http://doc.owncloud.org/server/6.0/developer_manual/app/index.html +http://doc.owncloud.org/server/7.0/developer_manual/app/index.html ### Contribution Guidelines -http://owncloud.org/dev/contribute/ +http://owncloud.org/contribute/ ### Get in touch * [Forum](http://forum.owncloud.org) -* [Mailing list](https://mail.kde.org/mailman/listinfo/owncloud) +* [Mailing list](http://mailman.owncloud.org/mailman/listinfo) * [IRC channel](https://webchat.freenode.net/?channels=owncloud) * [Twitter](https://twitter.com/ownClouders) @@ -25,4 +25,4 @@ Please submit translations via Transifex: https://www.transifex.com/projects/p/owncloud/ For more detailed information about translations: -http://owncloud.org/dev/translation/ +http://doc.owncloud.org/server/7.0/developer_manual/core/translation.html diff --git a/apps/files/ajax/upload.php b/apps/files/ajax/upload.php index b21a9dfba2ed256e077233d94272201c9516c2ff..a5ce7b257da3db8de85ac94fc19f95b500b0d29c 100644 --- a/apps/files/ajax/upload.php +++ b/apps/files/ajax/upload.php @@ -111,22 +111,32 @@ if ($maxUploadFileSize >= 0 and $totalSize > $maxUploadFileSize) { } $result = array(); +$directory = ''; if (strpos($dir, '..') === false) { $fileCount = count($files['name']); for ($i = 0; $i < $fileCount; $i++) { + + // Get the files directory + if(isset($_POST['file_directory']) === true) { + $directory = '/'.$_POST['file_directory']; + } + // $path needs to be normalized - this failed within drag'n'drop upload to a sub-folder if (isset($_POST['resolution']) && $_POST['resolution']==='autorename') { // append a number in brackets like 'filename (2).ext' - $target = OCP\Files::buildNotExistingFileName(stripslashes($dir), $files['name'][$i]); + $target = OCP\Files::buildNotExistingFileName(stripslashes($dir.$directory), $files['name'][$i]); } else { - $target = \OC\Files\Filesystem::normalizePath(stripslashes($dir).'/'.$files['name'][$i]); + $target = \OC\Files\Filesystem::normalizePath(stripslashes($dir.$directory).'/'.$files['name'][$i]); } - - $directory = \OC\Files\Filesystem::normalizePath(stripslashes($dir)); - if (isset($public_directory)) { - // If we are uploading from the public app, - // we want to send the relative path in the ajax request. - $directory = $public_directory; + + if(empty($directory) === true) + { + $directory = \OC\Files\Filesystem::normalizePath(stripslashes($dir)); + if (isset($public_directory)) { + // If we are uploading from the public app, + // we want to send the relative path in the ajax request. + $directory = $public_directory; + } } if ( ! \OC\Files\Filesystem::file_exists($target) @@ -186,7 +196,6 @@ if (strpos($dir, '..') === false) { if ($error === false) { OCP\JSON::encodedPrint($result); - exit(); } else { OCP\JSON::error(array(array('data' => array_merge(array('message' => $error, 'code' => $errorCode), $storageStats)))); } diff --git a/apps/files/appinfo/remote.php b/apps/files/appinfo/remote.php index a8acfdb6e6e3c52ced7bf1c8603d97844c8905eb..c1baee4f1ef985d99aa84caa45f15cb9a19d9708 100644 --- a/apps/files/appinfo/remote.php +++ b/apps/files/appinfo/remote.php @@ -28,11 +28,8 @@ $authBackend = new OC_Connector_Sabre_Auth(); $lockBackend = new OC_Connector_Sabre_Locks(); $requestBackend = new OC_Connector_Sabre_Request(); -// Create ownCloud Dir -$rootDir = new OC_Connector_Sabre_Directory(''); -$objectTree = new \OC\Connector\Sabre\ObjectTree($rootDir); - // Fire up server +$objectTree = new \OC\Connector\Sabre\ObjectTree(); $server = new OC_Connector_Sabre_Server($objectTree); $server->httpRequest = $requestBackend; $server->setBaseUri($baseuri); @@ -41,11 +38,23 @@ $server->setBaseUri($baseuri); $defaults = new OC_Defaults(); $server->addPlugin(new Sabre_DAV_Auth_Plugin($authBackend, $defaults->getName())); $server->addPlugin(new Sabre_DAV_Locks_Plugin($lockBackend)); +$server->addPlugin(new Sabre_DAV_Browser_Plugin(false)); $server->addPlugin(new OC_Connector_Sabre_FilesPlugin()); -$server->addPlugin(new OC_Connector_Sabre_AbortedUploadDetectionPlugin()); -$server->addPlugin(new OC_Connector_Sabre_QuotaPlugin()); $server->addPlugin(new OC_Connector_Sabre_MaintenancePlugin()); $server->addPlugin(new OC_Connector_Sabre_ExceptionLoggerPlugin('webdav')); +// wait with registering these until auth is handled and the filesystem is setup +$server->subscribeEvent('beforeMethod', function () use ($server, $objectTree) { + $view = \OC\Files\Filesystem::getView(); + $rootInfo = $view->getFileInfo(''); + + // Create ownCloud Dir + $rootDir = new OC_Connector_Sabre_Directory($view, $rootInfo); + $objectTree->init($rootDir, $view); + + $server->addPlugin(new OC_Connector_Sabre_AbortedUploadDetectionPlugin($view)); + $server->addPlugin(new OC_Connector_Sabre_QuotaPlugin($view)); +}, 30); // priority 30: after auth (10) and acl(20), before lock(50) and handling the request + // And off we go! $server->exec(); diff --git a/apps/files/css/files.css b/apps/files/css/files.css index 1bac5d2b7db26bdb2857d5d7c794debed1a67489..474f1af0720f52fd73b9e1deee3a151825644a49 100644 --- a/apps/files/css/files.css +++ b/apps/files/css/files.css @@ -51,7 +51,7 @@ margin: 5px; padding-left: 42px; padding-bottom: 2px; - background-position: initial; + background-position: left center; cursor: pointer; } #new > ul > li > p { diff --git a/apps/files/js/file-upload.js b/apps/files/js/file-upload.js index e5d1eacbd149f2ad52db522e66ed71f0f701086e..03ebdccb32df25ad3b85d83e972e9508ca5db202 100644 --- a/apps/files/js/file-upload.js +++ b/apps/files/js/file-upload.js @@ -235,18 +235,26 @@ OC.Upload = { var file = data.files[0]; try { // FIXME: not so elegant... need to refactor that method to return a value - Files.isFileNameValid(file.name, FileList.getCurrentDirectory()); + Files.isFileNameValid(file.name); } catch (errorMessage) { data.textStatus = 'invalidcharacters'; data.errorThrown = errorMessage; } - if (file.type === '' && file.size === 4096) { - data.textStatus = 'dirorzero'; - data.errorThrown = t('files', 'Unable to upload {filename} as it is a directory or has 0 bytes', - {filename: file.name} - ); + // in case folder drag and drop is not supported file will point to a directory + // http://stackoverflow.com/a/20448357 + if (!file.type && file.size%4096 === 0 && file.size <= 102400) { + try { + reader = new FileReader(); + reader.readAsBinaryString(f); + } catch (NS_ERROR_FILE_ACCESS_DENIED) { + //file is a directory + data.textStatus = 'dirorzero'; + data.errorThrown = t('files', 'Unable to upload {filename} as it is a directory or has 0 bytes', + {filename: file.name} + ); + } } // add size @@ -326,10 +334,15 @@ OC.Upload = { submit: function(e, data) { OC.Upload.rememberUpload(data); if ( ! data.formData ) { + var fileDirectory = ''; + if(typeof data.files[0].relativePath !== 'undefined') { + fileDirectory = data.files[0].relativePath; + } // noone set update parameters, we set the minimum data.formData = { requesttoken: oc_requesttoken, - dir: $('#dir').val() + dir: $('#dir').val(), + file_directory: fileDirectory }; } }, @@ -542,8 +555,6 @@ OC.Upload = { throw t('files', 'URL cannot be empty'); } else if (type !== 'web' && !Files.isFileNameValid(filename)) { // Files.isFileNameValid(filename) throws an exception itself - } else if (FileList.getCurrentDirectory() === '/' && filename.toLowerCase() === 'shared') { - throw t('files', 'In the home folder \'Shared\' is a reserved filename'); } else if (FileList.inList(filename)) { throw t('files', '{new_name} already exists', {new_name: filename}); } else { @@ -683,3 +694,4 @@ $(document).ready(function() { OC.Upload.init(); }); + diff --git a/apps/files/js/fileactions.js b/apps/files/js/fileactions.js index 631aebea954d12d0319d242ea8cbeb9c18dd007f..ecdfa72a477eb00d7b8f0b07a892ec8b6e6d35b0 100644 --- a/apps/files/js/fileactions.js +++ b/apps/files/js/fileactions.js @@ -118,10 +118,6 @@ var FileActions = { }; var addAction = function (name, action, displayName) { - // NOTE: Temporary fix to prevent rename action in root of Shared directory - if (name === 'Rename' && $('#dir').val() === '/Shared') { - return true; - } if ((name === 'Download' || action !== defaultAction) && name !== 'Delete') { @@ -160,7 +156,7 @@ var FileActions = { addAction(name, ah, displayName); } }); - if(actions.Share && !($('#dir').val() === '/' && file === 'Shared')){ + if(actions.Share){ displayName = t('files', 'Share'); addAction('Share', actions.Share, displayName); } @@ -223,7 +219,7 @@ $(document).ready(function () { $('#fileList tr').each(function () { FileActions.display($(this).children('td.filename')); }); - + $('#fileList').trigger(jQuery.Event("fileActionsReady")); }); diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 506741eb6eabee9d6498d3f9a15596d12591b55d..c33b638b5a6520cdc98cdda4fd2a0318da725806 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -9,7 +9,7 @@ */ /* global OC, t, n, FileList, FileActions, Files, BreadCrumb */ -/* global procesSelection, dragOptions */ +/* global procesSelection, dragOptions, folderDropOptions */ window.FileList = { appName: t('files', 'Files'), isEmpty: true, @@ -178,6 +178,13 @@ window.FileList = { if (type === 'dir') { mime = mime || 'httpd/unix-directory'; } + + // user should always be able to rename a share mount point + var allowRename = 0; + if (fileData.isShareMountPoint) { + allowRename = OC.PERMISSION_UPDATE; + } + //containing tr var tr = $('').attr({ "data-id" : fileData.id, @@ -187,7 +194,7 @@ window.FileList = { "data-mime": mime, "data-mtime": mtime, "data-etag": fileData.etag, - "data-permissions": fileData.permissions || this.getDirectoryPermissions() + "data-permissions": fileData.permissions | allowRename || this.getDirectoryPermissions() }); if (type === 'dir') { @@ -212,7 +219,7 @@ window.FileList = { linkUrl = Files.getDownloadUrl(name, FileList.getCurrentDirectory()); } td.append(''); - var link_elem = $('').attr({ + var linkElem = $('').attr({ "class": "name", "href": linkUrl }); @@ -228,19 +235,19 @@ window.FileList = { basename = name; extension = false; } - var name_span=$('').addClass('nametext').text(basename); - link_elem.append(name_span); + var nameSpan=$('').addClass('nametext').text(basename); + linkElem.append(nameSpan); if (extension) { - name_span.append($('').addClass('extension').text(extension)); + nameSpan.append($('').addClass('extension').text(extension)); } // dirs can show the number of uploaded files if (type === 'dir') { - link_elem.append($('').attr({ + linkElem.append($('').attr({ 'class': 'uploadtext', 'currentUploads': 0 })); } - td.append(link_elem); + td.append(linkElem); tr.append(td); // size column @@ -250,7 +257,7 @@ window.FileList = { } else { simpleSize = t('files', 'Pending'); } - var lastModifiedTime = Math.round(mtime / 1000); + td = $('').attr({ "class": "filesize", "style": 'color:rgb(' + sizeColor + ',' + sizeColor + ',' + sizeColor + ')' @@ -283,6 +290,10 @@ window.FileList = { mime = fileData.mimetype, permissions = parseInt(fileData.permissions, 10) || 0; + if (fileData.isShareMountPoint) { + permissions = permissions | OC.PERMISSION_UPDATE; + } + if (type === 'dir') { mime = mime || 'httpd/unix-directory'; } @@ -437,8 +448,6 @@ window.FileList = { }); }, reloadCallback: function(result) { - var $controls = $('#controls'); - delete this._reloadCall; this.hideMask(); @@ -574,7 +583,8 @@ window.FileList = { input.focus(); //preselect input var len = input.val().lastIndexOf('.'); - if (len === -1) { + if ( len === -1 || + tr.data('type') === 'dir' ) { len = input.val().length; } input.selectRange(0, len); @@ -582,7 +592,7 @@ window.FileList = { var filename = input.val(); if (filename !== oldname) { // Files.isFileNameValid(filename) throws an exception itself - Files.isFileNameValid(filename, FileList.getCurrentDirectory()); + Files.isFileNameValid(filename); if (FileList.inList(filename)) { throw t('files', '{new_name} already exists', {new_name: filename}); } @@ -810,10 +820,9 @@ window.FileList = { var info = t('files', '{dirs} and {files}', infoVars); // don't show the filesize column, if filesize is NaN (e.g. in trashbin) - if (isNaN(summary.totalSize)) { - var fileSize = ''; - } else { - var fileSize = ''+humanFileSize(summary.totalSize)+''; + var fileSize = ''; + if (!isNaN(summary.totalSize)) { + fileSize = ''+humanFileSize(summary.totalSize)+''; } var $summary = $(''+info+''+fileSize+''); @@ -899,7 +908,6 @@ window.FileList = { } }, updateEmptyContent: function() { - var $fileList = $('#fileList'); var permissions = $('#permissions').val(); var isCreatable = (permissions & OC.PERMISSION_CREATE) !== 0; $('#emptycontent').toggleClass('hidden', !isCreatable || !FileList.isEmpty); @@ -937,13 +945,13 @@ window.FileList = { }, scrollTo:function(file) { //scroll to and highlight preselected file - var $scrolltorow = FileList.findFileEl(file); - if ($scrolltorow.exists()) { - $scrolltorow.addClass('searchresult'); - $(window).scrollTop($scrolltorow.position().top); + var $scrollToRow = FileList.findFileEl(file); + if ($scrollToRow.exists()) { + $scrollToRow.addClass('searchresult'); + $(window).scrollTop($scrollToRow.position().top); //remove highlight when hovered over - $scrolltorow.one('hover', function() { - $scrolltorow.removeClass('searchresult'); + $scrollToRow.one('hover', function() { + $scrollToRow.removeClass('searchresult'); }); } }, @@ -979,9 +987,9 @@ $(document).ready(function() { FileList.initialize(); // handle upload events - var file_upload_start = $('#file_upload_start'); + var fileUploadStart = $('#file_upload_start'); - file_upload_start.on('fileuploaddrop', function(e, data) { + fileUploadStart.on('fileuploaddrop', function(e, data) { OC.Upload.log('filelist handle fileuploaddrop', e, data); var dropTarget = $(e.originalEvent.target).closest('tr, .crumb'); @@ -1008,7 +1016,8 @@ $(document).ready(function() { data.formData = function(form) { return [ {name: 'dir', value: dir}, - {name: 'requesttoken', value: oc_requesttoken} + {name: 'requesttoken', value: oc_requesttoken}, + {name: 'file_directory', value: data.files[0].relativePath} ]; }; } else { @@ -1019,7 +1028,7 @@ $(document).ready(function() { } } }); - file_upload_start.on('fileuploadadd', function(e, data) { + fileUploadStart.on('fileuploadadd', function(e, data) { OC.Upload.log('filelist handle fileuploadadd', e, data); //finish delete if we are uploading a deleted file @@ -1032,19 +1041,19 @@ $(document).ready(function() { // add to existing folder // update upload counter ui - var uploadtext = data.context.find('.uploadtext'); - var currentUploads = parseInt(uploadtext.attr('currentUploads')); + var uploadText = data.context.find('.uploadtext'); + var currentUploads = parseInt(uploadText.attr('currentUploads'), 10); currentUploads += 1; - uploadtext.attr('currentUploads', currentUploads); + uploadText.attr('currentUploads', currentUploads); var translatedText = n('files', 'Uploading %n file', 'Uploading %n files', currentUploads); if (currentUploads === 1) { var img = OC.imagePath('core', 'loading.gif'); data.context.find('td.filename').attr('style','background-image:url('+img+')'); - uploadtext.text(translatedText); - uploadtext.show(); + uploadText.text(translatedText); + uploadText.show(); } else { - uploadtext.text(translatedText); + uploadText.text(translatedText); } } @@ -1053,7 +1062,7 @@ $(document).ready(function() { * when file upload done successfully add row to filelist * update counter when uploading to sub folder */ - file_upload_start.on('fileuploaddone', function(e, data) { + fileUploadStart.on('fileuploaddone', function(e, data) { OC.Upload.log('filelist handle fileuploaddone', e, data); var response; @@ -1067,38 +1076,69 @@ $(document).ready(function() { if (typeof result[0] !== 'undefined' && result[0].status === 'success') { var file = result[0]; + var size = 0; if (data.context && data.context.data('type') === 'dir') { // update upload counter ui - var uploadtext = data.context.find('.uploadtext'); - var currentUploads = parseInt(uploadtext.attr('currentUploads')); + var uploadText = data.context.find('.uploadtext'); + var currentUploads = parseInt(uploadText.attr('currentUploads'), 10); currentUploads -= 1; - uploadtext.attr('currentUploads', currentUploads); + uploadText.attr('currentUploads', currentUploads); var translatedText = n('files', 'Uploading %n file', 'Uploading %n files', currentUploads); if (currentUploads === 0) { var img = OC.imagePath('core', 'filetypes/folder'); data.context.find('td.filename').attr('style','background-image:url('+img+')'); - uploadtext.text(translatedText); - uploadtext.hide(); + uploadText.text(translatedText); + uploadText.hide(); } else { - uploadtext.text(translatedText); + uploadText.text(translatedText); } // update folder size - var size = parseInt(data.context.data('size')); - size += parseInt(file.size); + size = parseInt(data.context.data('size'), 10); + size += parseInt(file.size, 10); data.context.attr('data-size', size); data.context.find('td.filesize').text(humanFileSize(size)); - } else { // only append new file if uploaded into the current folder - if (file.directory !== FileList.getCurrentDirectory()) { + if (file.directory !== '/' && file.directory !== FileList.getCurrentDirectory()) { + + var fileDirectory = file.directory.replace('/','').replace(/\/$/, "").split('/'); + + if (fileDirectory.length === 1) { + fileDirectory = fileDirectory[0]; + + // Get the directory + var fd = FileList.findFileEl(fileDirectory); + if (fd.length === 0) { + var dir = { + name: fileDirectory, + type: 'dir', + mimetype: 'httpd/unix-directory', + permissions: file.permissions, + size: 0, + id: file.parentId + }; + FileList.add(dir, {insert: true}); + } + } else { + fileDirectory = fileDirectory[0]; + } + + fileDirectory = FileList.findFileEl(fileDirectory); + + // update folder size + size = parseInt(fileDirectory.attr('data-size'), 10); + size += parseInt(file.size, 10); + fileDirectory.attr('data-size', size); + fileDirectory.find('td.filesize').text(humanFileSize(size)); + return; } // add as stand-alone row to filelist - var size=t('files', 'Pending'); + size = t('files', 'Pending'); if (data.files[0].size>=0) { size=data.files[0].size; } @@ -1110,37 +1150,40 @@ $(document).ready(function() { } } }); - file_upload_start.on('fileuploadstop', function(e, data) { + fileUploadStart.on('fileuploadstop', function(e, data) { OC.Upload.log('filelist handle fileuploadstop', e, data); //if user pressed cancel hide upload chrome if (data.errorThrown === 'abort') { //cleanup uploading to a dir - var uploadtext = $('tr .uploadtext'); + var uploadText = $('tr .uploadtext'); var img = OC.imagePath('core', 'filetypes/folder'); - uploadtext.parents('td.filename').attr('style','background-image:url('+img+')'); - uploadtext.fadeOut(); - uploadtext.attr('currentUploads', 0); + uploadText.parents('td.filename').attr('style','background-image:url('+img+')'); + uploadText.fadeOut(); + uploadText.attr('currentUploads', 0); } }); - file_upload_start.on('fileuploadfail', function(e, data) { + fileUploadStart.on('fileuploadfail', function(e, data) { OC.Upload.log('filelist handle fileuploadfail', e, data); //if user pressed cancel hide upload chrome if (data.errorThrown === 'abort') { //cleanup uploading to a dir - var uploadtext = $('tr .uploadtext'); + var uploadText = $('tr .uploadtext'); var img = OC.imagePath('core', 'filetypes/folder'); - uploadtext.parents('td.filename').attr('style','background-image:url('+img+')'); - uploadtext.fadeOut(); - uploadtext.attr('currentUploads', 0); + uploadText.parents('td.filename').attr('style','background-image:url('+img+')'); + uploadText.fadeOut(); + uploadText.attr('currentUploads', 0); } }); $('#notification').hide(); $('#notification:first-child').on('click', '.replace', function() { OC.Notification.hide(function() { - FileList.replace($('#notification > span').attr('data-oldName'), $('#notification > span').attr('data-newName'), $('#notification > span').attr('data-isNewFile')); + FileList.replace( + $('#notification > span').attr('data-oldName'), + $('#notification > span').attr('data-newName'), + $('#notification > span').attr('data-isNewFile')); }); }); $('#notification:first-child').on('click', '.suggest', function() { @@ -1170,8 +1213,7 @@ $(document).ready(function() { function parseHashQuery() { var hash = window.location.hash, - pos = hash.indexOf('?'), - query; + pos = hash.indexOf('?'); if (pos >= 0) { return hash.substr(pos + 1); } @@ -1180,8 +1222,7 @@ $(document).ready(function() { function parseCurrentDirFromUrl() { var query = parseHashQuery(), - params, - dir = '/'; + params; // try and parse from URL hash first if (query) { params = OC.parseQueryString(decodeQuery(query)); diff --git a/apps/files/js/files.js b/apps/files/js/files.js index 5b58cdcb1ba3af5c80129eaa25f1eb41e9a95817..5e669a796a9efdb95f95f0f69cebb59822408619 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -87,11 +87,9 @@ var Files = { * Throws a string exception with an error message if * the file name is not valid */ - isFileNameValid: function (name, root) { + isFileNameValid: function (name) { var trimmedName = name.trim(); - if (trimmedName === '.' - || trimmedName === '..' - || (root === '/' && trimmedName.toLowerCase() === 'shared')) + if (trimmedName === '.' || trimmedName === '..') { throw t('files', '"{name}" is an invalid file name.', {name: name}); } else if (trimmedName.length === 0) { @@ -131,15 +129,15 @@ var Files = { var encryptedFiles = $('#encryptedFiles').val(); var initStatus = $('#encryptionInitStatus').val(); if (initStatus === '0') { // enc not initialized, but should be - OC.Notification.show(t('files_encryption', 'Encryption App is enabled but your keys are not initialized, please log-out and log-in again')); + OC.Notification.show(t('files', 'Encryption App is enabled but your keys are not initialized, please log-out and log-in again')); return; } if (initStatus === '1') { // encryption tried to init but failed - OC.Notification.showHtml(t('files_encryption', 'Invalid private key for Encryption App. Please update your private key password in your personal settings to recover access to your encrypted files.')); + OC.Notification.show(t('files', 'Invalid private key for Encryption App. Please update your private key password in your personal settings to recover access to your encrypted files.')); return; } if (encryptedFiles === '1') { - OC.Notification.show(t('files_encryption', 'Encryption was disabled but your files are still encrypted. Please go to your personal settings to decrypt your files.')); + OC.Notification.show(t('files', 'Encryption was disabled but your files are still encrypted. Please go to your personal settings to decrypt your files.')); return; } }, diff --git a/apps/files/l10n/ar.php b/apps/files/l10n/ar.php index e7c081b1c47e1d066cb758ae316bf8507687780e..9a78e3bbff365f73c5c4142a84f262ecaa76a918 100644 --- a/apps/files/l10n/ar.php +++ b/apps/files/l10n/ar.php @@ -44,7 +44,6 @@ $TRANSLATIONS = array( "Size" => "حجم", "Modified" => "معدل", "%s could not be renamed" => "%s لا يمكن إعادة تسميته. ", -"Upload" => "رفع", "File handling" => "التعامل مع الملف", "Maximum upload size" => "الحد الأقصى لحجم الملفات التي يمكن رفعها", "max. possible: " => "الحد الأقصى المسموح به", diff --git a/apps/files/l10n/ast.php b/apps/files/l10n/ast.php index 3e1eb722655d6c2d22a56fb6e8b9196f72fa7018..5c6af60be3ddc6fd1b99f3926608eb5c89d02344 100644 --- a/apps/files/l10n/ast.php +++ b/apps/files/l10n/ast.php @@ -13,13 +13,15 @@ $TRANSLATIONS = array( "Files" => "Ficheros", "Share" => "Compartir", "Rename" => "Renomar", +"Error" => "Fallu", "_%n folder_::_%n folders_" => array("",""), "_%n file_::_%n files_" => array("",""), "_Uploading %n file_::_Uploading %n files_" => array("",""), "Name" => "Nome", "Size" => "Tamañu", -"Upload" => "Xubir", "Save" => "Guardar", +"New folder" => "Nueva carpeta", +"Folder" => "Carpeta", "Cancel upload" => "Encaboxar xuba", "Download" => "Descargar", "Delete" => "Desaniciar" diff --git a/apps/files/l10n/bg_BG.php b/apps/files/l10n/bg_BG.php index 2418010cdd9a747a4b80074e1ac771cc8e0f455b..d9cad21e79e986f09da4dbcf5ee9b5c4f63eeae1 100644 --- a/apps/files/l10n/bg_BG.php +++ b/apps/files/l10n/bg_BG.php @@ -20,7 +20,6 @@ $TRANSLATIONS = array( "Name" => "Име", "Size" => "Размер", "Modified" => "Променено", -"Upload" => "Качване", "Maximum upload size" => "Максимален размер за качване", "0 is unlimited" => "Ползвайте 0 за без ограничения", "Save" => "Запис", diff --git a/apps/files/l10n/bn_BD.php b/apps/files/l10n/bn_BD.php index 667a68bb6277fa42f923697741d373666c26b9c9..4d71d7dacd386e988619829a623f3d3c9df81056 100644 --- a/apps/files/l10n/bn_BD.php +++ b/apps/files/l10n/bn_BD.php @@ -27,7 +27,6 @@ $TRANSLATIONS = array( "Name" => "রাম", "Size" => "আকার", "Modified" => "পরিবর্তিত", -"Upload" => "আপলোড", "File handling" => "ফাইল হ্যার্ডলিং", "Maximum upload size" => "আপলোডের সর্বোচ্চ আকার", "max. possible: " => "অনুমোদিত সর্বোচ্চ আকার", diff --git a/apps/files/l10n/ca.php b/apps/files/l10n/ca.php index 8ef9b764484519005a77d986580a44cbf710e52b..6938da220b270f23b0791e0f73c12a053321bb58 100644 --- a/apps/files/l10n/ca.php +++ b/apps/files/l10n/ca.php @@ -35,7 +35,6 @@ $TRANSLATIONS = array( "Could not get result from server." => "No hi ha resposta del servidor.", "File upload is in progress. Leaving the page now will cancel the upload." => "Hi ha una pujada en curs. Si abandoneu la pàgina la pujada es cancel·larà.", "URL cannot be empty" => "L'URL no pot ser buit", -"In the home folder 'Shared' is a reserved filename" => "A la carpeta inici 'Compartit' és un nom de fitxer reservat", "{new_name} already exists" => "{new_name} ja existeix", "Could not create file" => "No s'ha pogut crear el fitxer", "Could not create folder" => "No s'ha pogut crear la carpeta", @@ -62,9 +61,7 @@ $TRANSLATIONS = array( "Name" => "Nom", "Size" => "Mida", "Modified" => "Modificat", -"Invalid folder name. Usage of 'Shared' is reserved." => "Nom de carpeta no vàlid. L'ús de 'Shared' és reservat", "%s could not be renamed" => "%s no es pot canviar el nom", -"Upload" => "Puja", "File handling" => "Gestió de fitxers", "Maximum upload size" => "Mida màxima de pujada", "max. possible: " => "màxim possible:", diff --git a/apps/files/l10n/cs_CZ.php b/apps/files/l10n/cs_CZ.php index 8aea17a7051bc5ba3947db4ad29f809c670d1b3a..40bb288ca1a1fee9201d1e9795cb2831b295f954 100644 --- a/apps/files/l10n/cs_CZ.php +++ b/apps/files/l10n/cs_CZ.php @@ -35,7 +35,6 @@ $TRANSLATIONS = array( "Could not get result from server." => "Nepodařilo se získat výsledek ze serveru.", "File upload is in progress. Leaving the page now will cancel the upload." => "Probíhá odesílání souboru. Opuštění stránky způsobí zrušení nahrávání.", "URL cannot be empty" => "URL nemůže zůstat prázdná", -"In the home folder 'Shared' is a reserved filename" => "V osobní složce je název 'Shared' rezervovaný", "{new_name} already exists" => "{new_name} již existuje", "Could not create file" => "Nepodařilo se vytvořit soubor", "Could not create folder" => "Nepodařilo se vytvořit složku", @@ -62,9 +61,8 @@ $TRANSLATIONS = array( "Name" => "Název", "Size" => "Velikost", "Modified" => "Upraveno", -"Invalid folder name. Usage of 'Shared' is reserved." => "Neplatný název složky. Použití 'Shared' je rezervováno.", "%s could not be renamed" => "%s nemůže být přejmenován", -"Upload" => "Odeslat", +"Upload (max. %s)" => "Nahrát (max. %s)", "File handling" => "Zacházení se soubory", "Maximum upload size" => "Maximální velikost pro odesílání", "max. possible: " => "největší možná: ", diff --git a/apps/files/l10n/cy_GB.php b/apps/files/l10n/cy_GB.php index b27e4c3bfc2a53350eb17118d59fd72ef7200949..f0c12b2fdebe569ab139d181c50d9163e9b7488f 100644 --- a/apps/files/l10n/cy_GB.php +++ b/apps/files/l10n/cy_GB.php @@ -32,7 +32,6 @@ $TRANSLATIONS = array( "Name" => "Enw", "Size" => "Maint", "Modified" => "Addaswyd", -"Upload" => "Llwytho i fyny", "File handling" => "Trafod ffeiliau", "Maximum upload size" => "Maint mwyaf llwytho i fyny", "max. possible: " => "mwyaf. posib:", diff --git a/apps/files/l10n/da.php b/apps/files/l10n/da.php index 6a7ea4745ccbfc8da1f6f13202b0f32a6bbc0355..9a6ea9c0dce30dba61ad14a0c03327fbb4d73181 100644 --- a/apps/files/l10n/da.php +++ b/apps/files/l10n/da.php @@ -35,7 +35,6 @@ $TRANSLATIONS = array( "Could not get result from server." => "Kunne ikke hente resultat fra server.", "File upload is in progress. Leaving the page now will cancel the upload." => "Fil upload kører. Hvis du forlader siden nu, vil uploadet blive annuleret.", "URL cannot be empty" => "URL kan ikke være tom", -"In the home folder 'Shared' is a reserved filename" => "Navnet 'Shared' er reserveret i hjemmemappen.", "{new_name} already exists" => "{new_name} eksisterer allerede", "Could not create file" => "Kunne ikke oprette fil", "Could not create folder" => "Kunne ikke oprette mappe", @@ -62,9 +61,7 @@ $TRANSLATIONS = array( "Name" => "Navn", "Size" => "Størrelse", "Modified" => "Ændret", -"Invalid folder name. Usage of 'Shared' is reserved." => "Ugyldig mappenavn. 'Shared' er reserveret.", "%s could not be renamed" => "%s kunne ikke omdøbes", -"Upload" => "Upload", "File handling" => "Filhåndtering", "Maximum upload size" => "Maksimal upload-størrelse", "max. possible: " => "max. mulige: ", diff --git a/apps/files/l10n/de.php b/apps/files/l10n/de.php index 401ee243f2894af26ed0d30d9417b292f1daf506..1d15469dac62431a5a5034844b59d8d1efd476e0 100644 --- a/apps/files/l10n/de.php +++ b/apps/files/l10n/de.php @@ -35,7 +35,6 @@ $TRANSLATIONS = array( "Could not get result from server." => "Ergebnis konnte nicht vom Server abgerufen werden.", "File upload is in progress. Leaving the page now will cancel the upload." => "Dateiupload läuft. Wenn Du die Seite jetzt verlässt, wird der Upload abgebrochen.", "URL cannot be empty" => "Die URL darf nicht leer sein", -"In the home folder 'Shared' is a reserved filename" => "Das Benutzerverzeichnis 'Shared' ist ein reservierter Dateiname", "{new_name} already exists" => "{new_name} existiert bereits", "Could not create file" => "Die Datei konnte nicht erstellt werden", "Could not create folder" => "Der Ordner konnte nicht erstellt werden", @@ -62,9 +61,8 @@ $TRANSLATIONS = array( "Name" => "Name", "Size" => "Größe", "Modified" => "Geändert", -"Invalid folder name. Usage of 'Shared' is reserved." => "Ungültiger Verzeichnisname. Die Nutzung von 'Shared' ist reserviert.", "%s could not be renamed" => "%s konnte nicht umbenannt werden", -"Upload" => "Hochladen", +"Upload (max. %s)" => "Hochladen (max. %s)", "File handling" => "Dateibehandlung", "Maximum upload size" => "Maximale Upload-Größe", "max. possible: " => "maximal möglich:", diff --git a/apps/files/l10n/de_CH.php b/apps/files/l10n/de_CH.php index f797be99e9829d20e1f19c6f381b3319d0324f7f..907b9e1b67ed380b51ff7359cb5ff1dc05a386c3 100644 --- a/apps/files/l10n/de_CH.php +++ b/apps/files/l10n/de_CH.php @@ -36,7 +36,6 @@ $TRANSLATIONS = array( "Size" => "Grösse", "Modified" => "Geändert", "%s could not be renamed" => "%s konnte nicht umbenannt werden", -"Upload" => "Hochladen", "File handling" => "Dateibehandlung", "Maximum upload size" => "Maximale Upload-Grösse", "max. possible: " => "maximal möglich:", diff --git a/apps/files/l10n/de_DE.php b/apps/files/l10n/de_DE.php index 4768faa97da79daf1c117d6dcd0d1342ad06aa4b..41ca83e6f8780a8740572dc2d99252dd8e3cecee 100644 --- a/apps/files/l10n/de_DE.php +++ b/apps/files/l10n/de_DE.php @@ -35,7 +35,6 @@ $TRANSLATIONS = array( "Could not get result from server." => "Ergebnis konnte nicht vom Server abgerufen werden.", "File upload is in progress. Leaving the page now will cancel the upload." => "Dateiupload läuft. Wenn Sie die Seite jetzt verlassen, wird der Upload abgebrochen.", "URL cannot be empty" => "Die URL darf nicht leer sein", -"In the home folder 'Shared' is a reserved filename" => "Das Benutzerverzeichnis 'Shared' ist ein reservierter Dateiname", "{new_name} already exists" => "{new_name} existiert bereits", "Could not create file" => "Die Datei konnte nicht erstellt werden", "Could not create folder" => "Der Ordner konnte nicht erstellt werden", @@ -62,9 +61,8 @@ $TRANSLATIONS = array( "Name" => "Name", "Size" => "Größe", "Modified" => "Geändert", -"Invalid folder name. Usage of 'Shared' is reserved." => "Ungültiger Verzeichnisname. Die Nutzung von 'Shared' ist reserviert.", "%s could not be renamed" => "%s konnte nicht umbenannt werden", -"Upload" => "Hochladen", +"Upload (max. %s)" => "Hochladen (max. %s)", "File handling" => "Dateibehandlung", "Maximum upload size" => "Maximale Upload-Größe", "max. possible: " => "maximal möglich:", @@ -76,7 +74,7 @@ $TRANSLATIONS = array( "New" => "Neu", "New text file" => "Neue Textdatei", "Text file" => "Textdatei", -"New folder" => "Neues Ordner", +"New folder" => "Neuer Ordner", "Folder" => "Ordner", "From link" => "Von einem Link", "Deleted files" => "Gelöschte Dateien", diff --git a/apps/files/l10n/el.php b/apps/files/l10n/el.php index 713072d3e0e24890ac870f4c66dce7becdcc84e3..f103d0621e5293e3e54641501ab8a1284cb86b34 100644 --- a/apps/files/l10n/el.php +++ b/apps/files/l10n/el.php @@ -35,7 +35,6 @@ $TRANSLATIONS = array( "Could not get result from server." => "Αδυναμία λήψης αποτελέσματος από το διακομιστή.", "File upload is in progress. Leaving the page now will cancel the upload." => "Η αποστολή του αρχείου βρίσκεται σε εξέλιξη. Το κλείσιμο της σελίδας θα ακυρώσει την αποστολή.", "URL cannot be empty" => "Η URL δεν πρέπει να είναι κενή", -"In the home folder 'Shared' is a reserved filename" => "Στον αρχικό φάκελο το όνομα 'Shared' διατηρείται από το σύστημα", "{new_name} already exists" => "{new_name} υπάρχει ήδη", "Could not create file" => "Αδυναμία δημιουργίας αρχείου", "Could not create folder" => "Αδυναμία δημιουργίας φακέλου", @@ -62,14 +61,12 @@ $TRANSLATIONS = array( "Name" => "Όνομα", "Size" => "Μέγεθος", "Modified" => "Τροποποιήθηκε", -"Invalid folder name. Usage of 'Shared' is reserved." => "Άκυρο όνομα φακέλου. Η χρήση του 'Shared' διατηρείται από το σύστημα.", "%s could not be renamed" => "Αδυναμία μετονομασίας του %s", -"Upload" => "Μεταφόρτωση", "File handling" => "Διαχείριση αρχείων", "Maximum upload size" => "Μέγιστο μέγεθος αποστολής", "max. possible: " => "μέγιστο δυνατό:", "Needed for multi-file and folder downloads." => "Απαραίτητο για κατέβασμα πολλαπλών αρχείων και φακέλων", -"Enable ZIP-download" => "Ενεργοποίηση κατεβάσματος ZIP", +"Enable ZIP-download" => "Επιτρέπεται η λήψη ZIP", "0 is unlimited" => "0 για απεριόριστο", "Maximum input size for ZIP files" => "Μέγιστο μέγεθος για αρχεία ZIP", "Save" => "Αποθήκευση", diff --git a/apps/files/l10n/en_GB.php b/apps/files/l10n/en_GB.php index 705f6b99b0b25f84357d567fbd1520b45ae33c4b..93e1007e37cb96836c8bff6a851f9b5f2211c0bf 100644 --- a/apps/files/l10n/en_GB.php +++ b/apps/files/l10n/en_GB.php @@ -35,7 +35,6 @@ $TRANSLATIONS = array( "Could not get result from server." => "Could not get result from server.", "File upload is in progress. Leaving the page now will cancel the upload." => "File upload is in progress. Leaving the page now will cancel the upload.", "URL cannot be empty" => "URL cannot be empty", -"In the home folder 'Shared' is a reserved filename" => "In the home folder 'Shared' is a reserved file name", "{new_name} already exists" => "{new_name} already exists", "Could not create file" => "Could not create file", "Could not create folder" => "Could not create folder", @@ -62,9 +61,8 @@ $TRANSLATIONS = array( "Name" => "Name", "Size" => "Size", "Modified" => "Modified", -"Invalid folder name. Usage of 'Shared' is reserved." => "Invalid folder name. Usage of 'Shared' is reserved.", "%s could not be renamed" => "%s could not be renamed", -"Upload" => "Upload", +"Upload (max. %s)" => "Upload (max. %s)", "File handling" => "File handling", "Maximum upload size" => "Maximum upload size", "max. possible: " => "max. possible: ", @@ -88,6 +86,6 @@ $TRANSLATIONS = array( "Upload too large" => "Upload too large", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "The files you are trying to upload exceed the maximum size for file uploads on this server.", "Files are being scanned, please wait." => "Files are being scanned, please wait.", -"Current scanning" => "Current scanning" +"Current scanning" => "Currently scanning" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/apps/files/l10n/eo.php b/apps/files/l10n/eo.php index a6e0d553177e66387a183a373a75bd655297cb46..dfa6f7ec038713ee487a63ebb9972b6035acaa6a 100644 --- a/apps/files/l10n/eo.php +++ b/apps/files/l10n/eo.php @@ -50,7 +50,6 @@ $TRANSLATIONS = array( "Size" => "Grando", "Modified" => "Modifita", "%s could not be renamed" => "%s ne povis alinomiĝi", -"Upload" => "Alŝuti", "File handling" => "Dosieradministro", "Maximum upload size" => "Maksimuma alŝutogrando", "max. possible: " => "maks. ebla: ", diff --git a/apps/files/l10n/es.php b/apps/files/l10n/es.php index 10a378c371bd1cf7bd0f9542421b9908252032c1..fd1d915ea8c90c7c8d9ed71deb88a247f9e7cb93 100644 --- a/apps/files/l10n/es.php +++ b/apps/files/l10n/es.php @@ -35,7 +35,6 @@ $TRANSLATIONS = array( "Could not get result from server." => "No se pudo obtener respuesta del servidor.", "File upload is in progress. Leaving the page now will cancel the upload." => "La subida del archivo está en proceso. Si sale de la página ahora, la subida será cancelada.", "URL cannot be empty" => "La dirección URL no puede estar vacía", -"In the home folder 'Shared' is a reserved filename" => "En la carpeta home, no se puede usar 'Shared'", "{new_name} already exists" => "{new_name} ya existe", "Could not create file" => "No se pudo crear el archivo", "Could not create folder" => "No se pudo crear la carpeta", @@ -62,9 +61,8 @@ $TRANSLATIONS = array( "Name" => "Nombre", "Size" => "Tamaño", "Modified" => "Modificado", -"Invalid folder name. Usage of 'Shared' is reserved." => "Nombre de carpeta inválido. El uso de \"Shared\" esta reservado.", "%s could not be renamed" => "%s no pudo ser renombrado", -"Upload" => "Subir", +"Upload (max. %s)" => "Subida (máx. %s)", "File handling" => "Administración de archivos", "Maximum upload size" => "Tamaño máximo de subida", "max. possible: " => "máx. posible:", diff --git a/apps/files/l10n/es_AR.php b/apps/files/l10n/es_AR.php index f78615fc923d438a9a241ebf05d0b0e66f425390..4d4a349a10da7a029a65b4f1308e6a3f31ddb1ef 100644 --- a/apps/files/l10n/es_AR.php +++ b/apps/files/l10n/es_AR.php @@ -31,7 +31,6 @@ $TRANSLATIONS = array( "Could not get result from server." => "No se pudo obtener resultados del servidor.", "File upload is in progress. Leaving the page now will cancel the upload." => "La subida del archivo está en proceso. Si salís de la página ahora, la subida se cancelará.", "URL cannot be empty" => "La URL no puede estar vacía", -"In the home folder 'Shared' is a reserved filename" => "En el directorio inicial 'Shared' es un nombre de archivo reservado", "{new_name} already exists" => "{new_name} ya existe", "Could not create file" => "No se pudo crear el archivo", "Could not create folder" => "No se pudo crear el directorio", @@ -57,9 +56,7 @@ $TRANSLATIONS = array( "Name" => "Nombre", "Size" => "Tamaño", "Modified" => "Modificado", -"Invalid folder name. Usage of 'Shared' is reserved." => "Nombre de directorio inválido. 'Shared' está reservado.", "%s could not be renamed" => "No se pudo renombrar %s", -"Upload" => "Subir", "File handling" => "Tratamiento de archivos", "Maximum upload size" => "Tamaño máximo de subida", "max. possible: " => "máx. posible:", diff --git a/apps/files/l10n/es_CL.php b/apps/files/l10n/es_CL.php index 8e051d1c389cbeadbe1c689678ab5a86dad73845..fa856ad5292d9d6cec61e5af15db45ae15e33107 100644 --- a/apps/files/l10n/es_CL.php +++ b/apps/files/l10n/es_CL.php @@ -2,11 +2,12 @@ $TRANSLATIONS = array( "Files" => "Archivos", "Share" => "Compartir", +"Rename" => "Renombrar", "Error" => "Error", "_%n folder_::_%n folders_" => array("",""), "_%n file_::_%n files_" => array("",""), "_Uploading %n file_::_Uploading %n files_" => array("",""), -"Upload" => "Subir", +"New folder" => "Nuevo directorio", "Download" => "Descargar" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/apps/files/l10n/es_CR.php b/apps/files/l10n/es_CR.php new file mode 100644 index 0000000000000000000000000000000000000000..0157af093e92200cc7790497227e92e62d80165f --- /dev/null +++ b/apps/files/l10n/es_CR.php @@ -0,0 +1,7 @@ + array("",""), +"_%n file_::_%n files_" => array("",""), +"_Uploading %n file_::_Uploading %n files_" => array("","") +); +$PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/apps/files/l10n/es_MX.php b/apps/files/l10n/es_MX.php index ea7db0d7b9ce1583a634991cadb2aa2e6466b3bf..f5f773c760d9756f3c6c344b56c3322e63041d40 100644 --- a/apps/files/l10n/es_MX.php +++ b/apps/files/l10n/es_MX.php @@ -31,7 +31,6 @@ $TRANSLATIONS = array( "Could not get result from server." => "No se pudo obtener respuesta del servidor.", "File upload is in progress. Leaving the page now will cancel the upload." => "La subida del archivo está en proceso. Si sale de la página ahora, la subida será cancelada.", "URL cannot be empty" => "La dirección URL no puede estar vacía", -"In the home folder 'Shared' is a reserved filename" => "En la carpeta de inicio, 'Shared' es un nombre reservado", "{new_name} already exists" => "{new_name} ya existe", "Could not create file" => "No se pudo crear el archivo", "Could not create folder" => "No se pudo crear la carpeta", @@ -57,9 +56,7 @@ $TRANSLATIONS = array( "Name" => "Nombre", "Size" => "Tamaño", "Modified" => "Modificado", -"Invalid folder name. Usage of 'Shared' is reserved." => "Nombre de carpeta inválido. El uso de \"Shared\" esta reservado.", "%s could not be renamed" => "%s no pudo ser renombrado", -"Upload" => "Subir", "File handling" => "Administración de archivos", "Maximum upload size" => "Tamaño máximo de subida", "max. possible: " => "máx. posible:", diff --git a/apps/files/l10n/et_EE.php b/apps/files/l10n/et_EE.php index 4f0614feb5e9436210abcbbfe6f6b7f64e05296a..4af93fa9ba27800027d8b1c3c8d055b404cfb336 100644 --- a/apps/files/l10n/et_EE.php +++ b/apps/files/l10n/et_EE.php @@ -35,7 +35,6 @@ $TRANSLATIONS = array( "Could not get result from server." => "Serverist ei saadud tulemusi", "File upload is in progress. Leaving the page now will cancel the upload." => "Faili üleslaadimine on töös. Lehelt lahkumine katkestab selle üleslaadimise.", "URL cannot be empty" => "URL ei saa olla tühi", -"In the home folder 'Shared' is a reserved filename" => "Kodukataloogis 'Shared' on reserveeritud failinimi", "{new_name} already exists" => "{new_name} on juba olemas", "Could not create file" => "Ei suuda luua faili", "Could not create folder" => "Ei suuda luua kataloogi", @@ -62,9 +61,8 @@ $TRANSLATIONS = array( "Name" => "Nimi", "Size" => "Suurus", "Modified" => "Muudetud", -"Invalid folder name. Usage of 'Shared' is reserved." => "Vigane kausta nimi. Nime 'Shared' kasutamine on reserveeritud.", "%s could not be renamed" => "%s ümbernimetamine ebaõnnestus", -"Upload" => "Lae üles", +"Upload (max. %s)" => "Üleslaadimine (max. %s)", "File handling" => "Failide käsitlemine", "Maximum upload size" => "Maksimaalne üleslaadimise suurus", "max. possible: " => "maks. võimalik: ", diff --git a/apps/files/l10n/eu.php b/apps/files/l10n/eu.php index d59dd396283ae737cde1de56ea61087c2ab80fdf..e1bb7033ebded45ea884d557dc50b4c0e4927bf1 100644 --- a/apps/files/l10n/eu.php +++ b/apps/files/l10n/eu.php @@ -31,7 +31,6 @@ $TRANSLATIONS = array( "Could not get result from server." => "Ezin da zerbitzaritik emaitzik lortu", "File upload is in progress. Leaving the page now will cancel the upload." => "Fitxategien igoera martxan da. Orria orain uzteak igoera ezeztatutko du.", "URL cannot be empty" => "URLa ezin da hutsik egon", -"In the home folder 'Shared' is a reserved filename" => "Etxeko (home) karpetan 'Shared' erreserbatutako fitxategi izena da", "{new_name} already exists" => "{new_name} dagoeneko existitzen da", "Could not create file" => "Ezin izan da fitxategia sortu", "Could not create folder" => "Ezin izan da karpeta sortu", @@ -57,9 +56,7 @@ $TRANSLATIONS = array( "Name" => "Izena", "Size" => "Tamaina", "Modified" => "Aldatuta", -"Invalid folder name. Usage of 'Shared' is reserved." => "Baliogabeako karpeta izena. 'Shared' izena erreserbatuta dago.", "%s could not be renamed" => "%s ezin da berrizendatu", -"Upload" => "Igo", "File handling" => "Fitxategien kudeaketa", "Maximum upload size" => "Igo daitekeen gehienezko tamaina", "max. possible: " => "max, posiblea:", diff --git a/apps/files/l10n/fa.php b/apps/files/l10n/fa.php index 2e8f6255e2440887a51ab5657c104975aa6e949a..4d2a929195f2cd90b2c22d10a9664e1c5ffaebb8 100644 --- a/apps/files/l10n/fa.php +++ b/apps/files/l10n/fa.php @@ -35,7 +35,6 @@ $TRANSLATIONS = array( "Size" => "اندازه", "Modified" => "تاریخ", "%s could not be renamed" => "%s نمیتواند تغییر نام دهد.", -"Upload" => "بارگزاری", "File handling" => "اداره پرونده ها", "Maximum upload size" => "حداکثر اندازه بارگزاری", "max. possible: " => "حداکثرمقدارممکن:", diff --git a/apps/files/l10n/fi_FI.php b/apps/files/l10n/fi_FI.php index b6383c144deab46a501b4b04c4cd93f0ac931a84..ba3c921dfeef58870b4eb1c78d2ef8b974ebf273 100644 --- a/apps/files/l10n/fi_FI.php +++ b/apps/files/l10n/fi_FI.php @@ -53,14 +53,15 @@ $TRANSLATIONS = array( "\"{name}\" is an invalid file name." => "\"{name}\" on virheellinen tiedostonimi.", "Your storage is full, files can not be updated or synced anymore!" => "Tallennustila on loppu, tiedostoja ei voi enää päivittää tai synkronoida!", "Your storage is almost full ({usedSpacePercent}%)" => "Tallennustila on melkein loppu ({usedSpacePercent}%)", +"Encryption App is enabled but your keys are not initialized, please log-out and log-in again" => "Salaussovellus on käytössä, mutta salausavaimia ei ole alustettu. Ole hyvä ja kirjaudu sisään uudelleen.", +"Invalid private key for Encryption App. Please update your private key password in your personal settings to recover access to your encrypted files." => "Salaussovelluksen salausavain on virheellinen. Ole hyvä ja päivitä salausavain henkilökohtaisissa asetuksissasi jotta voit taas avata salatuskirjoitetut tiedostosi.", "Encryption was disabled but your files are still encrypted. Please go to your personal settings to decrypt your files." => "Salaus poistettiin käytöstä, mutta tiedostosi ovat edelleen salattuina. Siirry henkilökohtaisiin asetuksiin avataksesi tiedostojesi salauksen.", "Your download is being prepared. This might take some time if the files are big." => "Lataustasi valmistellaan. Tämä saattaa kestää hetken, jos tiedostot ovat suuria kooltaan.", "Name" => "Nimi", "Size" => "Koko", "Modified" => "Muokattu", -"Invalid folder name. Usage of 'Shared' is reserved." => "Virheellinen kansion nimi. 'Shared':n käyttö on varattu.", "%s could not be renamed" => "kohteen %s nimeäminen uudelleen epäonnistui", -"Upload" => "Lähetä", +"Upload (max. %s)" => "Lähetys (enintään %s)", "File handling" => "Tiedostonhallinta", "Maximum upload size" => "Lähetettävän tiedoston suurin sallittu koko", "max. possible: " => "suurin mahdollinen:", diff --git a/apps/files/l10n/fr.php b/apps/files/l10n/fr.php index 0eed6a70f91d9d4d0c6cb1cec8de6565b33ff3e7..26ba8445f4f44eaa093f70d456358fa76d19226a 100644 --- a/apps/files/l10n/fr.php +++ b/apps/files/l10n/fr.php @@ -35,7 +35,6 @@ $TRANSLATIONS = array( "Could not get result from server." => "Ne peut recevoir les résultats du serveur.", "File upload is in progress. Leaving the page now will cancel the upload." => "L'envoi du fichier est en cours. Quitter cette page maintenant annulera l'envoi du fichier.", "URL cannot be empty" => "L'URL ne peut pas être vide", -"In the home folder 'Shared' is a reserved filename" => "Dans le dossier home, 'Partagé' est un nom de fichier réservé", "{new_name} already exists" => "{new_name} existe déjà", "Could not create file" => "Impossible de créer le fichier", "Could not create folder" => "Impossible de créer le dossier", @@ -62,9 +61,8 @@ $TRANSLATIONS = array( "Name" => "Nom", "Size" => "Taille", "Modified" => "Modifié", -"Invalid folder name. Usage of 'Shared' is reserved." => "Nom de dossier invalide. L'utilisation du mot 'Shared' est réservée.", "%s could not be renamed" => "%s ne peut être renommé", -"Upload" => "Envoyer", +"Upload (max. %s)" => "Envoi (max. %s)", "File handling" => "Gestion des fichiers", "Maximum upload size" => "Taille max. d'envoi", "max. possible: " => "Max. possible :", diff --git a/apps/files/l10n/gl.php b/apps/files/l10n/gl.php index 9fe6546de510a1663bf76e2ab8676c0d02a1895a..d9c6d0036030cc0e76322821a4c6928e4e8a64aa 100644 --- a/apps/files/l10n/gl.php +++ b/apps/files/l10n/gl.php @@ -35,7 +35,6 @@ $TRANSLATIONS = array( "Could not get result from server." => "Non foi posíbel obter o resultado do servidor.", "File upload is in progress. Leaving the page now will cancel the upload." => "O envío do ficheiro está en proceso. Saír agora da páxina cancelará o envío.", "URL cannot be empty" => "O URL non pode quedar en branco.", -"In the home folder 'Shared' is a reserved filename" => "«Shared» dentro do cartafol persoal é un nome reservado", "{new_name} already exists" => "Xa existe un {new_name}", "Could not create file" => "Non foi posíbel crear o ficheiro", "Could not create folder" => "Non foi posíbel crear o cartafol", @@ -62,9 +61,8 @@ $TRANSLATIONS = array( "Name" => "Nome", "Size" => "Tamaño", "Modified" => "Modificado", -"Invalid folder name. Usage of 'Shared' is reserved." => "Nome de cartafol non válido. O uso de «Shared» está reservado.", "%s could not be renamed" => "%s non pode cambiar de nome", -"Upload" => "Enviar", +"Upload (max. %s)" => "Envío (máx. %s)", "File handling" => "Manexo de ficheiro", "Maximum upload size" => "Tamaño máximo do envío", "max. possible: " => "máx. posíbel: ", diff --git a/apps/files/l10n/he.php b/apps/files/l10n/he.php index ab8640a91d14e805e7f01dbad6ffb0bfd077acd8..6279e675db2b314c7486917ff22ce72c48950fbc 100644 --- a/apps/files/l10n/he.php +++ b/apps/files/l10n/he.php @@ -32,7 +32,6 @@ $TRANSLATIONS = array( "Name" => "שם", "Size" => "גודל", "Modified" => "זמן שינוי", -"Upload" => "העלאה", "File handling" => "טיפול בקבצים", "Maximum upload size" => "גודל העלאה מקסימלי", "max. possible: " => "המרבי האפשרי: ", diff --git a/apps/files/l10n/hi.php b/apps/files/l10n/hi.php index b4234b513765f6fbc79e0da6528efb848d09b1dd..13fded26719d70905b7d378ef137954757e6cd61 100644 --- a/apps/files/l10n/hi.php +++ b/apps/files/l10n/hi.php @@ -5,7 +5,6 @@ $TRANSLATIONS = array( "_%n folder_::_%n folders_" => array("",""), "_%n file_::_%n files_" => array("",""), "_Uploading %n file_::_Uploading %n files_" => array("",""), -"Upload" => "अपलोड ", "Save" => "सहेजें" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/apps/files/l10n/hr.php b/apps/files/l10n/hr.php index ef978e6cfb71d74962cb16a6382e6d6a83b1c701..0876dcdd1ec3e87fabb5eab9178754f55fb62834 100644 --- a/apps/files/l10n/hr.php +++ b/apps/files/l10n/hr.php @@ -19,7 +19,6 @@ $TRANSLATIONS = array( "Name" => "Ime", "Size" => "Veličina", "Modified" => "Zadnja promjena", -"Upload" => "Učitaj", "File handling" => "datoteka za rukovanje", "Maximum upload size" => "Maksimalna veličina prijenosa", "max. possible: " => "maksimalna moguća: ", diff --git a/apps/files/l10n/hu_HU.php b/apps/files/l10n/hu_HU.php index e4ab355c9b1fe4c57261e2c0f98b03f4d2ba6f2d..9ed00326826950738ba82d07768d3c927969842a 100644 --- a/apps/files/l10n/hu_HU.php +++ b/apps/files/l10n/hu_HU.php @@ -31,7 +31,6 @@ $TRANSLATIONS = array( "Could not get result from server." => "A kiszolgálótól nem kapható meg az eredmény.", "File upload is in progress. Leaving the page now will cancel the upload." => "Fájlfeltöltés van folyamatban. Az oldal elhagyása megszakítja a feltöltést.", "URL cannot be empty" => "Az URL-cím nem maradhat kitöltetlenül", -"In the home folder 'Shared' is a reserved filename" => "A kiindulási mappában a 'Shared' egy belső használatra fenntartott név", "{new_name} already exists" => "{new_name} már létezik", "Could not create file" => "Az állomány nem hozható létre", "Could not create folder" => "A mappa nem hozható létre", @@ -57,9 +56,7 @@ $TRANSLATIONS = array( "Name" => "Név", "Size" => "Méret", "Modified" => "Módosítva", -"Invalid folder name. Usage of 'Shared' is reserved." => "Érvénytelen mappanév. A 'Shared' a rendszer számára fenntartott elnevezés.", "%s could not be renamed" => "%s átnevezése nem sikerült", -"Upload" => "Feltöltés", "File handling" => "Fájlkezelés", "Maximum upload size" => "Maximális feltölthető fájlméret", "max. possible: " => "max. lehetséges: ", diff --git a/apps/files/l10n/ia.php b/apps/files/l10n/ia.php index 420e48395c75451ea246e3c660093846ea910000..ff4cb0225b01032cdd381dafaf0ecc343ae3ce58 100644 --- a/apps/files/l10n/ia.php +++ b/apps/files/l10n/ia.php @@ -12,11 +12,11 @@ $TRANSLATIONS = array( "Name" => "Nomine", "Size" => "Dimension", "Modified" => "Modificate", -"Upload" => "Incargar", "Maximum upload size" => "Dimension maxime de incargamento", "Save" => "Salveguardar", "New" => "Nove", "Text file" => "File de texto", +"New folder" => "Nove dossier", "Folder" => "Dossier", "Nothing in here. Upload something!" => "Nihil hic. Incarga alcun cosa!", "Download" => "Discargar", diff --git a/apps/files/l10n/id.php b/apps/files/l10n/id.php index 8356c5465eb377ac08cc5d64f005b649aa0820b9..d8c0a47789c2d6421f3eec69c2e5d23600b4b3a3 100644 --- a/apps/files/l10n/id.php +++ b/apps/files/l10n/id.php @@ -30,7 +30,6 @@ $TRANSLATIONS = array( "Could not get result from server." => "Tidak mendapatkan hasil dari server.", "File upload is in progress. Leaving the page now will cancel the upload." => "Berkas sedang diunggah. Meninggalkan halaman ini akan membatalkan proses.", "URL cannot be empty" => "URL tidak boleh kosong", -"In the home folder 'Shared' is a reserved filename" => "Pada folder home, 'Shared' adalah nama berkas yang sudah digunakan", "{new_name} already exists" => "{new_name} sudah ada", "Could not create file" => "Tidak dapat membuat berkas", "Could not create folder" => "Tidak dapat membuat folder", @@ -55,9 +54,7 @@ $TRANSLATIONS = array( "Name" => "Nama", "Size" => "Ukuran", "Modified" => "Dimodifikasi", -"Invalid folder name. Usage of 'Shared' is reserved." => "Nama folder tidak sah. Menggunakan 'Shared' sudah digunakan.", "%s could not be renamed" => "%s tidak dapat diubah nama", -"Upload" => "Unggah", "File handling" => "Penanganan berkas", "Maximum upload size" => "Ukuran pengunggahan maksimum", "max. possible: " => "Kemungkinan maks.:", diff --git a/apps/files/l10n/is.php b/apps/files/l10n/is.php index 00503028e0e7ebbe3edb82279050ea7c7b9af92f..b8e23b6a3007dc4a044220070ac79a5d76f27f9c 100644 --- a/apps/files/l10n/is.php +++ b/apps/files/l10n/is.php @@ -27,7 +27,6 @@ $TRANSLATIONS = array( "Name" => "Nafn", "Size" => "Stærð", "Modified" => "Breytt", -"Upload" => "Senda inn", "File handling" => "Meðhöndlun skrár", "Maximum upload size" => "Hámarks stærð innsendingar", "max. possible: " => "hámark mögulegt: ", diff --git a/apps/files/l10n/it.php b/apps/files/l10n/it.php index 9539496a3fc31a0a9674e55cd841754824192178..c77fb57c59655736dfb77a08cb4c9584eb55807b 100644 --- a/apps/files/l10n/it.php +++ b/apps/files/l10n/it.php @@ -35,7 +35,6 @@ $TRANSLATIONS = array( "Could not get result from server." => "Impossibile ottenere il risultato dal server.", "File upload is in progress. Leaving the page now will cancel the upload." => "Caricamento del file in corso. La chiusura della pagina annullerà il caricamento.", "URL cannot be empty" => "L'URL non può essere vuoto.", -"In the home folder 'Shared' is a reserved filename" => "Nella cartella home 'Shared' è un nome riservato", "{new_name} already exists" => "{new_name} esiste già", "Could not create file" => "Impossibile creare il file", "Could not create folder" => "Impossibile creare la cartella", @@ -62,9 +61,8 @@ $TRANSLATIONS = array( "Name" => "Nome", "Size" => "Dimensione", "Modified" => "Modificato", -"Invalid folder name. Usage of 'Shared' is reserved." => "Nome della cartella non valido. L'uso di 'Shared' è riservato.", "%s could not be renamed" => "%s non può essere rinominato", -"Upload" => "Carica", +"Upload (max. %s)" => "Carica (massimo %s)", "File handling" => "Gestione file", "Maximum upload size" => "Dimensione massima upload", "max. possible: " => "numero mass.: ", diff --git a/apps/files/l10n/ja.php b/apps/files/l10n/ja.php index dd8d4e4e3f63f8daf79b715aff7fa297b26d59ac..361fd835ce1c3c5790fb207badbfa2bf15e6d107 100644 --- a/apps/files/l10n/ja.php +++ b/apps/files/l10n/ja.php @@ -35,7 +35,6 @@ $TRANSLATIONS = array( "Could not get result from server." => "サーバーから結果を取得できませんでした。", "File upload is in progress. Leaving the page now will cancel the upload." => "ファイル転送を実行中です。今このページから移動するとアップロードが中止されます。", "URL cannot be empty" => "URL は空にできません", -"In the home folder 'Shared' is a reserved filename" => "ホームフォルダーでは、'Shared' はシステムが使用する予約済みのファイル名です", "{new_name} already exists" => "{new_name} はすでに存在します", "Could not create file" => "ファイルを作成できませんでした", "Could not create folder" => "フォルダーを作成できませんでした", @@ -62,9 +61,7 @@ $TRANSLATIONS = array( "Name" => "名前", "Size" => "サイズ", "Modified" => "更新日時", -"Invalid folder name. Usage of 'Shared' is reserved." => "無効なフォルダー名。「Shared」の利用は予約されています。", "%s could not be renamed" => "%sの名前を変更できませんでした", -"Upload" => "アップロード", "File handling" => "ファイル操作", "Maximum upload size" => "最大アップロードサイズ", "max. possible: " => "最大容量: ", diff --git a/apps/files/l10n/ka_GE.php b/apps/files/l10n/ka_GE.php index f9749d72bb4b87bf0d75546daa853b3eef8d4459..ad3a4bff1f183bd8edb81d489e600a76b5573b23 100644 --- a/apps/files/l10n/ka_GE.php +++ b/apps/files/l10n/ka_GE.php @@ -32,7 +32,6 @@ $TRANSLATIONS = array( "Name" => "სახელი", "Size" => "ზომა", "Modified" => "შეცვლილია", -"Upload" => "ატვირთვა", "File handling" => "ფაილის დამუშავება", "Maximum upload size" => "მაქსიმუმ ატვირთის ზომა", "max. possible: " => "მაქს. შესაძლებელი:", diff --git a/apps/files/l10n/km.php b/apps/files/l10n/km.php index a7a01ccab9da7dfdcc58c0a73b53bf501ab573c5..30bb2998afa55628cef51940305957d1d471b5af 100644 --- a/apps/files/l10n/km.php +++ b/apps/files/l10n/km.php @@ -8,7 +8,6 @@ $TRANSLATIONS = array( "_Uploading %n file_::_Uploading %n files_" => array(""), "Name" => "ឈ្មោះ", "Size" => "ទំហំ", -"Upload" => "ផ្ទុក​ឡើង", "Save" => "រក្សាទុក", "New folder" => "ថត​ថ្មី", "Folder" => "ថត", diff --git a/apps/files/l10n/ko.php b/apps/files/l10n/ko.php index c0f0d7d4454d26e8bfcdb35f4a34ecda2ed7159f..21c7ebcdc443cb5b20806af7519014403accdc23 100644 --- a/apps/files/l10n/ko.php +++ b/apps/files/l10n/ko.php @@ -31,7 +31,6 @@ $TRANSLATIONS = array( "Could not get result from server." => "서버에서 결과를 가져올 수 없습니다.", "File upload is in progress. Leaving the page now will cancel the upload." => "파일 업로드가 진행 중입니다. 이 페이지를 벗어나면 업로드가 취소됩니다.", "URL cannot be empty" => "URL이 비어있을 수 없음", -"In the home folder 'Shared' is a reserved filename" => "'공유됨'은 홈 폴더의 예약된 파일 이름임", "{new_name} already exists" => "{new_name}이(가) 이미 존재함", "Could not create file" => "파일을 만들 수 없음", "Could not create folder" => "폴더를 만들 수 없음", @@ -57,9 +56,7 @@ $TRANSLATIONS = array( "Name" => "이름", "Size" => "크기", "Modified" => "수정됨", -"Invalid folder name. Usage of 'Shared' is reserved." => "폴더 이름이 잘못되었습니다. '공유됨'은 예약된 폴더 이름입니다.", "%s could not be renamed" => "%s의 이름을 변경할 수 없습니다", -"Upload" => "업로드", "File handling" => "파일 처리", "Maximum upload size" => "최대 업로드 크기", "max. possible: " => "최대 가능:", diff --git a/apps/files/l10n/ku_IQ.php b/apps/files/l10n/ku_IQ.php index 6ec5819d380ed4a9748ae791b057d54072c82e00..1c9d615ee7e24195c026ebc6bfc1bedcb2e0750e 100644 --- a/apps/files/l10n/ku_IQ.php +++ b/apps/files/l10n/ku_IQ.php @@ -7,7 +7,6 @@ $TRANSLATIONS = array( "_%n file_::_%n files_" => array("",""), "_Uploading %n file_::_Uploading %n files_" => array("",""), "Name" => "ناو", -"Upload" => "بارکردن", "Save" => "پاشکه‌وتکردن", "Folder" => "بوخچه", "Download" => "داگرتن" diff --git a/apps/files/l10n/lb.php b/apps/files/l10n/lb.php index 38b5d672d02e3c54be931fffe8595c0dd9e8d25f..27dc936600b030ffa9090f56af2a7a4716934d89 100644 --- a/apps/files/l10n/lb.php +++ b/apps/files/l10n/lb.php @@ -18,7 +18,6 @@ $TRANSLATIONS = array( "Name" => "Numm", "Size" => "Gréisst", "Modified" => "Geännert", -"Upload" => "Eroplueden", "File handling" => "Fichier handling", "Maximum upload size" => "Maximum Upload Gréisst ", "max. possible: " => "max. méiglech:", diff --git a/apps/files/l10n/lt_LT.php b/apps/files/l10n/lt_LT.php index 50097e5f3624d5f899f1515dbdea3df60551dbf5..a8c30016d3c93e69f9e40ad788597ee641d40b59 100644 --- a/apps/files/l10n/lt_LT.php +++ b/apps/files/l10n/lt_LT.php @@ -31,7 +31,6 @@ $TRANSLATIONS = array( "Could not get result from server." => "Nepavyko gauti rezultato iš serverio.", "File upload is in progress. Leaving the page now will cancel the upload." => "Failo įkėlimas pradėtas. Jei paliksite šį puslapį, įkėlimas nutrūks.", "URL cannot be empty" => "URL negali būti tuščias.", -"In the home folder 'Shared' is a reserved filename" => "Pradiniame aplanke failo pavadinimas „Shared“ yra rezervuotas", "{new_name} already exists" => "{new_name} jau egzistuoja", "Could not create file" => "Neįmanoma sukurti failo", "Could not create folder" => "Neįmanoma sukurti aplanko", @@ -57,9 +56,7 @@ $TRANSLATIONS = array( "Name" => "Pavadinimas", "Size" => "Dydis", "Modified" => "Pakeista", -"Invalid folder name. Usage of 'Shared' is reserved." => "Netinkamas aplanko pavadinimas. „Shared“ pavadinimas yra rezervuotas.", "%s could not be renamed" => "%s negali būti pervadintas", -"Upload" => "Įkelti", "File handling" => "Failų tvarkymas", "Maximum upload size" => "Maksimalus įkeliamo failo dydis", "max. possible: " => "maks. galima:", diff --git a/apps/files/l10n/lv.php b/apps/files/l10n/lv.php index fcb1a59aa38ba5f2f9bd3247c3e45ae85b16e558..71f3976816d3f3697ee56d59a30d3fdae9c9b065 100644 --- a/apps/files/l10n/lv.php +++ b/apps/files/l10n/lv.php @@ -36,7 +36,6 @@ $TRANSLATIONS = array( "Size" => "Izmērs", "Modified" => "Mainīts", "%s could not be renamed" => "%s nevar tikt pārsaukts", -"Upload" => "Augšupielādēt", "File handling" => "Datņu pārvaldība", "Maximum upload size" => "Maksimālais datņu augšupielādes apjoms", "max. possible: " => "maksimālais iespējamais:", diff --git a/apps/files/l10n/mk.php b/apps/files/l10n/mk.php index fa6efd1aff3cdd82ff2f0c4d53d076ac863440ae..e1ff2c0bc27edae9171f60960a97f7497e82bd84 100644 --- a/apps/files/l10n/mk.php +++ b/apps/files/l10n/mk.php @@ -27,7 +27,6 @@ $TRANSLATIONS = array( "Could not get result from server." => "Не можам да добијам резултат од серверот.", "File upload is in progress. Leaving the page now will cancel the upload." => "Подигање на датотека е во тек. Напуштење на страницата ќе го прекине.", "URL cannot be empty" => "URL-то не може да биде празно", -"In the home folder 'Shared' is a reserved filename" => "Во домашната папка, 'Shared' е резервирано има на датотека/папка", "{new_name} already exists" => "{new_name} веќе постои", "Could not create file" => "Не множам да креирам датотека", "Could not create folder" => "Не можам да креирам папка", @@ -49,7 +48,6 @@ $TRANSLATIONS = array( "Size" => "Големина", "Modified" => "Променето", "%s could not be renamed" => "%s не може да биде преименуван", -"Upload" => "Подигни", "File handling" => "Ракување со датотеки", "Maximum upload size" => "Максимална големина за подигање", "max. possible: " => "макс. можно:", diff --git a/apps/files/l10n/ms_MY.php b/apps/files/l10n/ms_MY.php index df0054c3d00b8affc289684e8142253cf76f89df..af42a3838b614c9f3711a1fc94aa0e3a4fc009e5 100644 --- a/apps/files/l10n/ms_MY.php +++ b/apps/files/l10n/ms_MY.php @@ -19,7 +19,6 @@ $TRANSLATIONS = array( "Name" => "Nama", "Size" => "Saiz", "Modified" => "Dimodifikasi", -"Upload" => "Muat naik", "File handling" => "Pengendalian fail", "Maximum upload size" => "Saiz maksimum muat naik", "max. possible: " => "maksimum:", diff --git a/apps/files/l10n/nb_NO.php b/apps/files/l10n/nb_NO.php index f1e2c2edeeb66d93ca08dcc5a38ce2dcb3e6d618..9887087cca7e0ece456e7408c6e904d5ce46cd55 100644 --- a/apps/files/l10n/nb_NO.php +++ b/apps/files/l10n/nb_NO.php @@ -31,7 +31,6 @@ $TRANSLATIONS = array( "Could not get result from server." => "Fikk ikke resultat fra serveren.", "File upload is in progress. Leaving the page now will cancel the upload." => "Filopplasting pågår. Forlater du siden nå avbrytes opplastingen.", "URL cannot be empty" => "URL kan ikke være tom", -"In the home folder 'Shared' is a reserved filename" => "I hjemmemappen er 'Shared' et reservert filnavn", "{new_name} already exists" => "{new_name} finnes allerede", "Could not create file" => "Klarte ikke å opprette fil", "Could not create folder" => "Klarte ikke å opprette mappe", @@ -57,9 +56,7 @@ $TRANSLATIONS = array( "Name" => "Navn", "Size" => "Størrelse", "Modified" => "Endret", -"Invalid folder name. Usage of 'Shared' is reserved." => "Ulovlig mappenavn. Bruken av 'Shared' er reservert.", "%s could not be renamed" => "Kunne ikke gi nytt navn til %s", -"Upload" => "Last opp", "File handling" => "Filhåndtering", "Maximum upload size" => "Maksimum opplastingsstørrelse", "max. possible: " => "max. mulige:", diff --git a/apps/files/l10n/nl.php b/apps/files/l10n/nl.php index 946c7905b23b2bf9026a9686c9d9ea31ae83c9b3..e33d26e38d9106a5291b0b09152a53604f080623 100644 --- a/apps/files/l10n/nl.php +++ b/apps/files/l10n/nl.php @@ -35,7 +35,6 @@ $TRANSLATIONS = array( "Could not get result from server." => "Kon het resultaat van de server niet terugkrijgen.", "File upload is in progress. Leaving the page now will cancel the upload." => "Bestandsupload is bezig. Wanneer de pagina nu verlaten wordt, stopt de upload.", "URL cannot be empty" => "URL mag niet leeg zijn", -"In the home folder 'Shared' is a reserved filename" => "in de home map 'Shared' is een gereserveerde bestandsnaam", "{new_name} already exists" => "{new_name} bestaat al", "Could not create file" => "Kon bestand niet creëren", "Could not create folder" => "Kon niet creëren map", @@ -62,9 +61,8 @@ $TRANSLATIONS = array( "Name" => "Naam", "Size" => "Grootte", "Modified" => "Aangepast", -"Invalid folder name. Usage of 'Shared' is reserved." => "Ongeldige mapnaam. Gebruik van 'Shared' is gereserveerd.", "%s could not be renamed" => "%s kon niet worden hernoemd", -"Upload" => "Uploaden", +"Upload (max. %s)" => "Upload (max. %s)", "File handling" => "Bestand", "Maximum upload size" => "Maximale bestandsgrootte voor uploads", "max. possible: " => "max. mogelijk: ", diff --git a/apps/files/l10n/nn_NO.php b/apps/files/l10n/nn_NO.php index bd17fa3386a5512332fc92f1a4e299429a924f2e..693bfccb09381c4a5e6d08d4e3e70a0b845a652e 100644 --- a/apps/files/l10n/nn_NO.php +++ b/apps/files/l10n/nn_NO.php @@ -42,7 +42,6 @@ $TRANSLATIONS = array( "Size" => "Storleik", "Modified" => "Endra", "%s could not be renamed" => "Klarte ikkje å omdøypa på %s", -"Upload" => "Last opp", "File handling" => "Filhandtering", "Maximum upload size" => "Maksimal opplastingsstorleik", "max. possible: " => "maks. moglege:", diff --git a/apps/files/l10n/oc.php b/apps/files/l10n/oc.php index 7a24c81974ec9f435b11f383f2f454ce091d8df7..f3d790a533fd8fd73677ced8dba6385d3cae498d 100644 --- a/apps/files/l10n/oc.php +++ b/apps/files/l10n/oc.php @@ -19,7 +19,6 @@ $TRANSLATIONS = array( "Name" => "Nom", "Size" => "Talha", "Modified" => "Modificat", -"Upload" => "Amontcarga", "File handling" => "Manejament de fichièr", "Maximum upload size" => "Talha maximum d'amontcargament", "max. possible: " => "max. possible: ", diff --git a/apps/files/l10n/or_IN.php b/apps/files/l10n/or_IN.php new file mode 100644 index 0000000000000000000000000000000000000000..0157af093e92200cc7790497227e92e62d80165f --- /dev/null +++ b/apps/files/l10n/or_IN.php @@ -0,0 +1,7 @@ + array("",""), +"_%n file_::_%n files_" => array("",""), +"_Uploading %n file_::_Uploading %n files_" => array("","") +); +$PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/apps/files/l10n/pa.php b/apps/files/l10n/pa.php index b18d2071e080a9fdd0b9fbf424c5710e7248a923..3cf8724249145e1fc6a6a5178ce8c0f42c5b3e8d 100644 --- a/apps/files/l10n/pa.php +++ b/apps/files/l10n/pa.php @@ -7,7 +7,6 @@ $TRANSLATIONS = array( "_%n folder_::_%n folders_" => array("",""), "_%n file_::_%n files_" => array("",""), "_Uploading %n file_::_Uploading %n files_" => array("",""), -"Upload" => "ਅੱਪਲੋਡ", "Cancel upload" => "ਅੱਪਲੋਡ ਰੱਦ ਕਰੋ", "Download" => "ਡਾਊਨਲੋਡ", "Delete" => "ਹਟਾਓ" diff --git a/apps/files/l10n/pl.php b/apps/files/l10n/pl.php index 83126b3ea035367c8f4868e874b1392971482eeb..30c3496db33e209275bfd94b963db2114a7bd7e3 100644 --- a/apps/files/l10n/pl.php +++ b/apps/files/l10n/pl.php @@ -35,7 +35,6 @@ $TRANSLATIONS = array( "Could not get result from server." => "Nie można uzyskać wyniku z serwera.", "File upload is in progress. Leaving the page now will cancel the upload." => "Wysyłanie pliku jest w toku. Jeśli opuścisz tę stronę, wysyłanie zostanie przerwane.", "URL cannot be empty" => "URL nie może być pusty", -"In the home folder 'Shared' is a reserved filename" => "W katalogu domowym \"Shared\" jest zarezerwowana nazwa pliku", "{new_name} already exists" => "{new_name} już istnieje", "Could not create file" => "Nie można utworzyć pliku", "Could not create folder" => "Nie można utworzyć folderu", @@ -62,9 +61,8 @@ $TRANSLATIONS = array( "Name" => "Nazwa", "Size" => "Rozmiar", "Modified" => "Modyfikacja", -"Invalid folder name. Usage of 'Shared' is reserved." => "Niepoprawna nazwa folderu. Wykorzystanie \"Shared\" jest zarezerwowane.", "%s could not be renamed" => "%s nie można zmienić nazwy", -"Upload" => "Wyślij", +"Upload (max. %s)" => "Wysyłka (max. %s)", "File handling" => "Zarządzanie plikami", "Maximum upload size" => "Maksymalny rozmiar wysyłanego pliku", "max. possible: " => "maks. możliwy:", diff --git a/apps/files/l10n/pt_BR.php b/apps/files/l10n/pt_BR.php index 48c32e8887f6bb8d9b7f32ec98a5eb9d3d72d0cf..10d8892e18faa20d7eba506cde445f4ee388927f 100644 --- a/apps/files/l10n/pt_BR.php +++ b/apps/files/l10n/pt_BR.php @@ -35,7 +35,6 @@ $TRANSLATIONS = array( "Could not get result from server." => "Não foi possível obter o resultado do servidor.", "File upload is in progress. Leaving the page now will cancel the upload." => "Upload em andamento. Sair da página agora resultará no cancelamento do envio.", "URL cannot be empty" => "URL não pode estar vazia", -"In the home folder 'Shared' is a reserved filename" => "Na pasta home 'Shared- Compartilhada' é um nome reservado", "{new_name} already exists" => "{new_name} já existe", "Could not create file" => "Não foi possível criar o arquivo", "Could not create folder" => "Não foi possível criar a pasta", @@ -62,9 +61,8 @@ $TRANSLATIONS = array( "Name" => "Nome", "Size" => "Tamanho", "Modified" => "Modificado", -"Invalid folder name. Usage of 'Shared' is reserved." => "Nome da pasta inválido. Uso de 'Shared' é reservado.", "%s could not be renamed" => "%s não pode ser renomeado", -"Upload" => "Upload", +"Upload (max. %s)" => "Envio (max. %s)", "File handling" => "Tratamento de Arquivo", "Maximum upload size" => "Tamanho máximo para carregar", "max. possible: " => "max. possível:", diff --git a/apps/files/l10n/pt_PT.php b/apps/files/l10n/pt_PT.php index 0afb6b501570cf9d5a6e8875ecc5328ebf81dc3f..c24d2cf8f38e2d1fb6263fa6bee03dd7065af07e 100644 --- a/apps/files/l10n/pt_PT.php +++ b/apps/files/l10n/pt_PT.php @@ -31,7 +31,6 @@ $TRANSLATIONS = array( "Could not get result from server." => "Não foi possível obter o resultado do servidor.", "File upload is in progress. Leaving the page now will cancel the upload." => "Envio de ficheiro em progresso. Irá cancelar o envio se sair da página agora.", "URL cannot be empty" => "URL não pode estar vazio", -"In the home folder 'Shared' is a reserved filename" => "Na pasta pessoal \"Partilhado\" é um nome de ficheiro reservado", "{new_name} already exists" => "O nome {new_name} já existe", "Could not create file" => "Não pôde criar ficheiro", "Could not create folder" => "Não pôde criar pasta", @@ -57,9 +56,7 @@ $TRANSLATIONS = array( "Name" => "Nome", "Size" => "Tamanho", "Modified" => "Modificado", -"Invalid folder name. Usage of 'Shared' is reserved." => "Nome de pasta inválido. Utilização de \"Partilhado\" está reservada.", "%s could not be renamed" => "%s não pode ser renomeada", -"Upload" => "Carregar", "File handling" => "Manuseamento de ficheiros", "Maximum upload size" => "Tamanho máximo de envio", "max. possible: " => "max. possivel: ", diff --git a/apps/files/l10n/ro.php b/apps/files/l10n/ro.php index 6cda724df430f38fccd16b7478edc48867796db3..d3927f5daf0c5a05b29d43d25e0e2f5788cdf1d8 100644 --- a/apps/files/l10n/ro.php +++ b/apps/files/l10n/ro.php @@ -3,7 +3,10 @@ $TRANSLATIONS = array( "Could not move %s - File with this name already exists" => "%s nu se poate muta - Fișierul cu acest nume există deja ", "Could not move %s" => "Nu se poate muta %s", "File name cannot be empty." => "Numele fișierului nu poate rămâne gol.", +"\"%s\" is an invalid file name." => "\"%s\" este un nume de fișier nevalid", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Nume nevalide, '\\', '/', '<', '>', ':', '\"', '|', '?' și '*' nu sunt permise.", +"The target folder has been moved or deleted." => "Dosarul țintă a fost mutat sau șters.", +"Not a valid source" => "Sursă nevalidă", "Error while downloading %s to %s" => "Eroare la descarcarea %s in %s", "Error when creating the file" => "Eroare la crearea fisierului", "Folder name cannot be empty." => "Numele folderului nu poate fi liber.", @@ -24,6 +27,8 @@ $TRANSLATIONS = array( "Invalid directory." => "Dosar nevalid.", "Files" => "Fișiere", "Unable to upload {filename} as it is a directory or has 0 bytes" => "Nu se poate încărca {filename} deoarece este un director sau are mărimea de 0 octeți", +"Total file size {size1} exceeds upload limit {size2}" => "Mărimea fișierului este {size1} ce depășește limita de incarcare de {size2}", +"Not enough free space, you are uploading {size1} but only {size2} is left" => "Spațiu liber insuficient, încărcați {size1} însă doar {size2} disponibil rămas", "Upload cancelled." => "Încărcare anulată.", "Could not get result from server." => "Nu se poate obține rezultatul de la server.", "File upload is in progress. Leaving the page now will cancel the upload." => "Fișierul este în curs de încărcare. Părăsirea paginii va întrerupe încărcarea.", @@ -31,6 +36,7 @@ $TRANSLATIONS = array( "{new_name} already exists" => "{new_name} există deja", "Could not create file" => "Nu s-a putut crea fisierul", "Could not create folder" => "Nu s-a putut crea folderul", +"Error fetching URL" => "Eroare încarcare URL", "Share" => "Partajează", "Delete permanently" => "Șterge permanent", "Rename" => "Redenumește", @@ -38,10 +44,12 @@ $TRANSLATIONS = array( "Error" => "Eroare", "Pending" => "În așteptare", "Could not rename file" => "Nu s-a putut redenumi fisierul", +"Error deleting file." => "Eroare la ștergerea fisierului.", "_%n folder_::_%n folders_" => array("%n director","%n directoare","%n directoare"), "_%n file_::_%n files_" => array("%n fișier","%n fișiere","%n fișiere"), "{dirs} and {files}" => "{dirs} și {files}", "_Uploading %n file_::_Uploading %n files_" => array("Se încarcă %n fișier.","Se încarcă %n fișiere.","Se încarcă %n fișiere."), +"\"{name}\" is an invalid file name." => "\"{name}\" este un nume de fișier nevalid.", "Your storage is full, files can not be updated or synced anymore!" => "Spațiul de stocare este plin, fișierele nu mai pot fi actualizate sau sincronizate!", "Your storage is almost full ({usedSpacePercent}%)" => "Spațiul de stocare este aproape plin ({usedSpacePercent}%)", "Encryption was disabled but your files are still encrypted. Please go to your personal settings to decrypt your files." => "criptarea a fost disactivata dar fisierele sant inca criptate.va rog intrati in setarile personale pentru a decripta fisierele", @@ -50,7 +58,7 @@ $TRANSLATIONS = array( "Size" => "Mărime", "Modified" => "Modificat", "%s could not be renamed" => "%s nu a putut fi redenumit", -"Upload" => "Încărcă", +"Upload (max. %s)" => "Încarcă (max. %s)", "File handling" => "Manipulare fișiere", "Maximum upload size" => "Dimensiune maximă admisă la încărcare", "max. possible: " => "max. posibil:", @@ -60,7 +68,9 @@ $TRANSLATIONS = array( "Maximum input size for ZIP files" => "Dimensiunea maximă de intrare pentru fișierele ZIP", "Save" => "Salvează", "New" => "Nou", +"New text file" => "Un nou fișier text", "Text file" => "Fișier text", +"New folder" => "Un nou dosar", "Folder" => "Dosar", "From link" => "De la adresa", "Deleted files" => "Fișiere șterse", diff --git a/apps/files/l10n/ru.php b/apps/files/l10n/ru.php index 17f06c6a2008aa083e3f40549c8e103c63e36f70..d10a7e2eca5d4745ac70562269f1d2b9d443837a 100644 --- a/apps/files/l10n/ru.php +++ b/apps/files/l10n/ru.php @@ -35,7 +35,6 @@ $TRANSLATIONS = array( "Could not get result from server." => "Не удалось получить ответ от сервера.", "File upload is in progress. Leaving the page now will cancel the upload." => "Идёт загрузка файла. Покинув страницу, вы прервёте загрузку.", "URL cannot be empty" => "Ссылка не может быть пустой.", -"In the home folder 'Shared' is a reserved filename" => "'Shared' - это зарезервированное имя файла в домашнем каталоге", "{new_name} already exists" => "{new_name} уже существует", "Could not create file" => "Не удалось создать файл", "Could not create folder" => "Не удалось создать каталог", @@ -62,9 +61,8 @@ $TRANSLATIONS = array( "Name" => "Имя", "Size" => "Размер", "Modified" => "Дата изменения", -"Invalid folder name. Usage of 'Shared' is reserved." => "Неправильное имя каталога. Имя 'Shared' зарезервировано.", "%s could not be renamed" => "%s не может быть переименован", -"Upload" => "Загрузка", +"Upload (max. %s)" => "Загружено (max. %s)", "File handling" => "Управление файлами", "Maximum upload size" => "Максимальный размер загружаемого файла", "max. possible: " => "макс. возможно: ", diff --git a/apps/files/l10n/si_LK.php b/apps/files/l10n/si_LK.php index a2809ee2f5d834d5540a7101cbfc9e4bafacc488..ff6672f71123f0b7d5a26a156908d5f178921084 100644 --- a/apps/files/l10n/si_LK.php +++ b/apps/files/l10n/si_LK.php @@ -19,7 +19,6 @@ $TRANSLATIONS = array( "Name" => "නම", "Size" => "ප්‍රමාණය", "Modified" => "වෙනස් කළ", -"Upload" => "උඩුගත කරන්න", "File handling" => "ගොනු පරිහරණය", "Maximum upload size" => "උඩුගත කිරීමක උපරිම ප්‍රමාණය", "max. possible: " => "හැකි උපරිමය:", diff --git a/apps/files/l10n/sk_SK.php b/apps/files/l10n/sk_SK.php index cffb89c294d1a71379e711a95092b76e65ed20c6..3d92dc1b5abbbe78cba40d61533d089033e0be05 100644 --- a/apps/files/l10n/sk_SK.php +++ b/apps/files/l10n/sk_SK.php @@ -35,7 +35,6 @@ $TRANSLATIONS = array( "Could not get result from server." => "Nepodarilo sa dostať výsledky zo servera.", "File upload is in progress. Leaving the page now will cancel the upload." => "Opustenie stránky zruší práve prebiehajúce odosielanie súboru.", "URL cannot be empty" => "URL nemôže byť prázdna", -"In the home folder 'Shared' is a reserved filename" => "V domovskom priečinku je názov \"Shared\" vyhradený názov súboru", "{new_name} already exists" => "{new_name} už existuje", "Could not create file" => "Nemožno vytvoriť súbor", "Could not create folder" => "Nemožno vytvoriť priečinok", @@ -62,9 +61,8 @@ $TRANSLATIONS = array( "Name" => "Názov", "Size" => "Veľkosť", "Modified" => "Upravené", -"Invalid folder name. Usage of 'Shared' is reserved." => "Názov priečinka je chybný. Použitie názvu 'Shared' nie je povolené.", "%s could not be renamed" => "%s nemohol byť premenovaný", -"Upload" => "Odoslať", +"Upload (max. %s)" => "Nahrať (max. %s)", "File handling" => "Nastavenie správania sa k súborom", "Maximum upload size" => "Maximálna veľkosť odosielaného súboru", "max. possible: " => "najväčšie možné:", diff --git a/apps/files/l10n/sl.php b/apps/files/l10n/sl.php index fcb358bd7bd71e2856765871f1cbcdae9641d95e..ac304d992ca1b9fa5afb0de02fe99244bd80b420 100644 --- a/apps/files/l10n/sl.php +++ b/apps/files/l10n/sl.php @@ -35,7 +35,6 @@ $TRANSLATIONS = array( "Could not get result from server." => "Ni mogoče pridobiti podatkov s strežnika.", "File upload is in progress. Leaving the page now will cancel the upload." => "V teku je pošiljanje datoteke. Če zapustite to stran zdaj, bo pošiljanje preklicano.", "URL cannot be empty" => "Polje naslova URL ne sme biti prazno", -"In the home folder 'Shared' is a reserved filename" => "V domači mapi ni dovoljeno ustvariti mape z imenom 'Souporabe', saj je ime zadržano za javno mapo.", "{new_name} already exists" => "{new_name} že obstaja", "Could not create file" => "Ni mogoče ustvariti datoteke", "Could not create folder" => "Ni mogoče ustvariti mape", @@ -62,9 +61,8 @@ $TRANSLATIONS = array( "Name" => "Ime", "Size" => "Velikost", "Modified" => "Spremenjeno", -"Invalid folder name. Usage of 'Shared' is reserved." => "Neveljavno ime mape. Ime 'Souporaba' je zadržana za javno mapo.", "%s could not be renamed" => "%s ni mogoče preimenovati", -"Upload" => "Pošlji", +"Upload (max. %s)" => "Pošiljanje (omejitev %s)", "File handling" => "Upravljanje z datotekami", "Maximum upload size" => "Največja velikost za pošiljanja", "max. possible: " => "največ mogoče:", diff --git a/apps/files/l10n/sq.php b/apps/files/l10n/sq.php index ade4f769faa3739009f359e8d465548dcb4895b5..a7f1cb3413349d3bb8d70b29098397b1b20dd3f3 100644 --- a/apps/files/l10n/sq.php +++ b/apps/files/l10n/sq.php @@ -40,7 +40,6 @@ $TRANSLATIONS = array( "Size" => "Madhësia", "Modified" => "Ndryshuar", "%s could not be renamed" => "Nuk është i mundur riemërtimi i %s", -"Upload" => "Ngarko", "File handling" => "Trajtimi i Skedarëve", "Maximum upload size" => "Madhësia maksimale e nagarkimit", "max. possible: " => "maks i mundshëm", diff --git a/apps/files/l10n/sr.php b/apps/files/l10n/sr.php index 44669e8167aaf574c575070ce0e4dca6a3c63ab9..866d8dbdd03cb938bdfc936c6f573f52be061aa8 100644 --- a/apps/files/l10n/sr.php +++ b/apps/files/l10n/sr.php @@ -32,7 +32,6 @@ $TRANSLATIONS = array( "Name" => "Име", "Size" => "Величина", "Modified" => "Измењено", -"Upload" => "Отпреми", "File handling" => "Управљање датотекама", "Maximum upload size" => "Највећа величина датотеке", "max. possible: " => "највећа величина:", diff --git a/apps/files/l10n/sr@latin.php b/apps/files/l10n/sr@latin.php index a5c74860f78fd32d275b283d8cfbb8ea3852f6e6..38039a19fc155d6e3db82484353cdcad835a965d 100644 --- a/apps/files/l10n/sr@latin.php +++ b/apps/files/l10n/sr@latin.php @@ -15,7 +15,6 @@ $TRANSLATIONS = array( "Name" => "Ime", "Size" => "Veličina", "Modified" => "Zadnja izmena", -"Upload" => "Pošalji", "Maximum upload size" => "Maksimalna veličina pošiljke", "Save" => "Snimi", "Nothing in here. Upload something!" => "Ovde nema ničeg. Pošaljite nešto!", diff --git a/apps/files/l10n/sv.php b/apps/files/l10n/sv.php index f420216228cc29e2cb060a1aa087f239c1e11cd4..f3d0170a30324f3d2e7d73be797bc00499740742 100644 --- a/apps/files/l10n/sv.php +++ b/apps/files/l10n/sv.php @@ -35,7 +35,6 @@ $TRANSLATIONS = array( "Could not get result from server." => "Gick inte att hämta resultat från server.", "File upload is in progress. Leaving the page now will cancel the upload." => "Filuppladdning pågår. Lämnar du sidan så avbryts uppladdningen.", "URL cannot be empty" => "URL kan ej vara tomt", -"In the home folder 'Shared' is a reserved filename" => "I hemma katalogen 'Delat' är ett reserverat filnamn", "{new_name} already exists" => "{new_name} finns redan", "Could not create file" => "Kunde ej skapa fil", "Could not create folder" => "Kunde ej skapa katalog", @@ -62,9 +61,8 @@ $TRANSLATIONS = array( "Name" => "Namn", "Size" => "Storlek", "Modified" => "Ändrad", -"Invalid folder name. Usage of 'Shared' is reserved." => "Ogiltigt mappnamn. Användande av 'Shared' är reserverat av ownCloud", "%s could not be renamed" => "%s kunde inte namnändras", -"Upload" => "Ladda upp", +"Upload (max. %s)" => "Ladda upp (max. %s)", "File handling" => "Filhantering", "Maximum upload size" => "Maximal storlek att ladda upp", "max. possible: " => "max. möjligt:", diff --git a/apps/files/l10n/ta_LK.php b/apps/files/l10n/ta_LK.php index 257aacf147170101c3761b9f69a19f44ab5d39cc..0ab17785b93642004f0673e2013551116b9b50c1 100644 --- a/apps/files/l10n/ta_LK.php +++ b/apps/files/l10n/ta_LK.php @@ -22,7 +22,6 @@ $TRANSLATIONS = array( "Name" => "பெயர்", "Size" => "அளவு", "Modified" => "மாற்றப்பட்டது", -"Upload" => "பதிவேற்றுக", "File handling" => "கோப்பு கையாளுதல்", "Maximum upload size" => "பதிவேற்றக்கூடிய ஆகக்கூடிய அளவு ", "max. possible: " => "ஆகக் கூடியது:", diff --git a/apps/files/l10n/th_TH.php b/apps/files/l10n/th_TH.php index 8f5f15f2a34380c59798dad9f70385cd408a417e..f0fd29da7e4cefcc684afe9acbe15099fe83adc0 100644 --- a/apps/files/l10n/th_TH.php +++ b/apps/files/l10n/th_TH.php @@ -31,7 +31,6 @@ $TRANSLATIONS = array( "Name" => "ชื่อ", "Size" => "ขนาด", "Modified" => "แก้ไขแล้ว", -"Upload" => "อัพโหลด", "File handling" => "การจัดกาไฟล์", "Maximum upload size" => "ขนาดไฟล์สูงสุดที่อัพโหลดได้", "max. possible: " => "จำนวนสูงสุดที่สามารถทำได้: ", diff --git a/apps/files/l10n/tr.php b/apps/files/l10n/tr.php index 87c664cc17115590cab7ecfbb936f9ce2c1eebee..77c310fc9ebc691f8da163a106e66f787c5bb3f1 100644 --- a/apps/files/l10n/tr.php +++ b/apps/files/l10n/tr.php @@ -1,6 +1,6 @@ "%s taşınamadı - Bu isimde dosya zaten var", +"Could not move %s - File with this name already exists" => "%s taşınamadı. Bu isimde dosya zaten mevcut", "Could not move %s" => "%s taşınamadı", "File name cannot be empty." => "Dosya adı boş olamaz.", "\"%s\" is an invalid file name." => "'%s' geçersiz bir dosya adı.", @@ -35,7 +35,6 @@ $TRANSLATIONS = array( "Could not get result from server." => "Sunucudan sonuç alınamadı.", "File upload is in progress. Leaving the page now will cancel the upload." => "Dosya yükleme işlemi sürüyor. Şimdi sayfadan ayrılırsanız işleminiz iptal olur.", "URL cannot be empty" => "URL boş olamaz", -"In the home folder 'Shared' is a reserved filename" => "Ev klasöründeki 'Paylaşılan', ayrılmış bir dosya adıdır", "{new_name} already exists" => "{new_name} zaten mevcut", "Could not create file" => "Dosya oluşturulamadı", "Could not create folder" => "Klasör oluşturulamadı", @@ -62,9 +61,8 @@ $TRANSLATIONS = array( "Name" => "İsim", "Size" => "Boyut", "Modified" => "Değiştirilme", -"Invalid folder name. Usage of 'Shared' is reserved." => "Geçersiz klasör adı. 'Shared' ismi ayrılmıştır.", "%s could not be renamed" => "%s yeniden adlandırılamadı", -"Upload" => "Yükle", +"Upload (max. %s)" => "Yükle (azami: %s)", "File handling" => "Dosya işlemleri", "Maximum upload size" => "Maksimum yükleme boyutu", "max. possible: " => "mümkün olan en fazla: ", diff --git a/apps/files/l10n/ug.php b/apps/files/l10n/ug.php index 13354c153b2dfeeca420213507dffa9697fde4b4..58ccba94c1338db34eac094d57291f9846673721 100644 --- a/apps/files/l10n/ug.php +++ b/apps/files/l10n/ug.php @@ -21,7 +21,6 @@ $TRANSLATIONS = array( "Name" => "ئاتى", "Size" => "چوڭلۇقى", "Modified" => "ئۆزگەرتكەن", -"Upload" => "يۈكلە", "Save" => "ساقلا", "New" => "يېڭى", "Text file" => "تېكىست ھۆججەت", diff --git a/apps/files/l10n/uk.php b/apps/files/l10n/uk.php index 905d27c3ee25edde43e58699c1b67e809df0976e..5643dedb9d4c64fe617b42671343dabb0cb020f8 100644 --- a/apps/files/l10n/uk.php +++ b/apps/files/l10n/uk.php @@ -40,7 +40,6 @@ $TRANSLATIONS = array( "Size" => "Розмір", "Modified" => "Змінено", "%s could not be renamed" => "%s не може бути перейменований", -"Upload" => "Вивантажити", "File handling" => "Робота з файлами", "Maximum upload size" => "Максимальний розмір відвантажень", "max. possible: " => "макс.можливе:", diff --git a/apps/files/l10n/vi.php b/apps/files/l10n/vi.php index 55f9bd259423f39c3914c891b428dc03e45e636b..058add4bb73b13b1a5b83be1b60ce67785f1b0ab 100644 --- a/apps/files/l10n/vi.php +++ b/apps/files/l10n/vi.php @@ -55,7 +55,6 @@ $TRANSLATIONS = array( "Size" => "Kích cỡ", "Modified" => "Thay đổi", "%s could not be renamed" => "%s không thể đổi tên", -"Upload" => "Tải lên", "File handling" => "Xử lý tập tin", "Maximum upload size" => "Kích thước tối đa ", "max. possible: " => "tối đa cho phép:", diff --git a/apps/files/l10n/zh_CN.php b/apps/files/l10n/zh_CN.php index 068f97c1ddfb95611ee2bc9f178621059007a373..82cc68a49955fe509aec277f8faffe811ea30385 100644 --- a/apps/files/l10n/zh_CN.php +++ b/apps/files/l10n/zh_CN.php @@ -31,7 +31,6 @@ $TRANSLATIONS = array( "Could not get result from server." => "不能从服务器得到结果", "File upload is in progress. Leaving the page now will cancel the upload." => "文件正在上传中。现在离开此页会导致上传动作被取消。", "URL cannot be empty" => "URL不能为空", -"In the home folder 'Shared' is a reserved filename" => "主目录里 'Shared' 是系统预留目录名", "{new_name} already exists" => "{new_name} 已存在", "Could not create file" => "不能创建文件", "Could not create folder" => "不能创建文件夹", @@ -57,9 +56,7 @@ $TRANSLATIONS = array( "Name" => "名称", "Size" => "大小", "Modified" => "修改日期", -"Invalid folder name. Usage of 'Shared' is reserved." => "无效的文件夹名。”Shared“ 是 Owncloud 预留的文件夹", "%s could not be renamed" => "%s 不能被重命名", -"Upload" => "上传", "File handling" => "文件处理", "Maximum upload size" => "最大上传大小", "max. possible: " => "最大允许: ", diff --git a/apps/files/l10n/zh_HK.php b/apps/files/l10n/zh_HK.php index eaa32cd537a5d00d8a9be7e3125c10fe9d23fe63..13aa6ac3e35cdeebfc9a17f7ea95409d19a25b9b 100644 --- a/apps/files/l10n/zh_HK.php +++ b/apps/files/l10n/zh_HK.php @@ -7,8 +7,9 @@ $TRANSLATIONS = array( "_%n file_::_%n files_" => array(""), "_Uploading %n file_::_Uploading %n files_" => array(""), "Name" => "名稱", -"Upload" => "上傳", +"Size" => "大小", "Save" => "儲存", +"New folder" => "新文件夾", "Download" => "下載", "Delete" => "刪除" ); diff --git a/apps/files/l10n/zh_TW.php b/apps/files/l10n/zh_TW.php index 154efd563fddb83f665dc256c2814671579c2bb5..92bc13189df16e3acb1d40e286d103f1d2f12c79 100644 --- a/apps/files/l10n/zh_TW.php +++ b/apps/files/l10n/zh_TW.php @@ -30,7 +30,6 @@ $TRANSLATIONS = array( "Could not get result from server." => "無法從伺服器取回結果", "File upload is in progress. Leaving the page now will cancel the upload." => "檔案上傳中,離開此頁面將會取消上傳。", "URL cannot be empty" => "URL 不能留空", -"In the home folder 'Shared' is a reserved filename" => "在家目錄中不能使用「共享」作為檔名", "{new_name} already exists" => "{new_name} 已經存在", "Could not create file" => "無法建立檔案", "Could not create folder" => "無法建立資料夾", @@ -55,7 +54,6 @@ $TRANSLATIONS = array( "Size" => "大小", "Modified" => "修改時間", "%s could not be renamed" => "無法重新命名 %s", -"Upload" => "上傳", "File handling" => "檔案處理", "Maximum upload size" => "上傳限制", "max. possible: " => "最大允許:", diff --git a/apps/files/lib/app.php b/apps/files/lib/app.php index adfca669577babcb6c3afba4067a250835453d67..ed4aa32c66246bb0058f1a76d94b6c7c392d6c4a 100644 --- a/apps/files/lib/app.php +++ b/apps/files/lib/app.php @@ -54,13 +54,8 @@ class App { 'data' => NULL ); - // rename to "/Shared" is denied - if( $dir === '/' and $newname === 'Shared' ) { - $result['data'] = array( - 'message' => $this->l10n->t("Invalid folder name. Usage of 'Shared' is reserved.") - ); // rename to non-existing folder is denied - } else if (!$this->view->file_exists($dir)) { + if (!$this->view->file_exists($dir)) { $result['data'] = array('message' => (string)$this->l10n->t( 'The target folder has been moved or deleted.', array($dir)), @@ -68,7 +63,7 @@ class App { ); // rename to existing file is denied } else if ($this->view->file_exists($dir . '/' . $newname)) { - + $result['data'] = array( 'message' => $this->l10n->t( "The name %s is already used in the folder %s. Please choose a different name.", @@ -77,8 +72,6 @@ class App { } else if ( // rename to "." is denied $newname !== '.' and - // rename of "/Shared" is denied - !($dir === '/' and $oldname === 'Shared') and // THEN try to rename $this->view->rename($dir . '/' . $oldname, $dir . '/' . $newname) ) { diff --git a/apps/files/lib/helper.php b/apps/files/lib/helper.php index b765fdaf3e3ac234653b688bb2a6a516412a49ec..0ae87d12fbfb3974a90b47bd388dcc30ef096a5c 100644 --- a/apps/files/lib/helper.php +++ b/apps/files/lib/helper.php @@ -11,7 +11,7 @@ class Helper $l = new \OC_L10N('files'); $maxUploadFilesize = \OCP\Util::maxUploadFilesize($dir, $storageInfo['free']); $maxHumanFilesize = \OCP\Util::humanFileSize($maxUploadFilesize); - $maxHumanFilesize = $l->t('Upload') . ' max. ' . $maxHumanFilesize; + $maxHumanFilesize = $l->t('Upload (max. %s)', array($maxHumanFilesize)); return array('uploadMaxFilesize' => $maxUploadFilesize, 'maxHumanFilesize' => $maxHumanFilesize, @@ -37,8 +37,7 @@ class Helper $sid = explode(':', $sid); if ($sid[0] === 'shared') { $icon = \OC_Helper::mimetypeIcon('dir-shared'); - } - if ($sid[0] !== 'local' and $sid[0] !== 'home') { + } elseif ($sid[0] !== 'local' and $sid[0] !== 'home') { $icon = \OC_Helper::mimetypeIcon('dir-external'); } } @@ -73,13 +72,14 @@ class Helper /** * Formats the file info to be returned as JSON to the client. * - * @param \OCP\Files\FileInfo file info + * @param \OCP\Files\FileInfo $i * @return array formatted file info */ public static function formatFileInfo($i) { $entry = array(); $entry['id'] = $i['fileid']; + $entry['parentId'] = $i['parent']; $entry['date'] = \OCP\Util::formatDate($i['mtime']); $entry['mtime'] = $i['mtime'] * 1000; // only pick out the needed attributes @@ -96,6 +96,9 @@ class Helper if (isset($i['displayname_owner'])) { $entry['shareOwner'] = $i['displayname_owner']; } + if (isset($i['is_share_mount_point'])) { + $entry['isShareMountPoint'] = $i['is_share_mount_point']; + } return $entry; } diff --git a/apps/files/templates/index.php b/apps/files/templates/index.php index 95edd625cb31491552427f55b79b02d3e9680b02..a8437835d959bf8ca662de8492684a6b26fc82d5 100644 --- a/apps/files/templates/index.php +++ b/apps/files/templates/index.php @@ -19,7 +19,7 @@
+ title="t('Upload (max. %s)', array($_['uploadMaxHumanFilesize']))) ?>"> = 0):?> diff --git a/apps/files/tests/ajax_rename.php b/apps/files/tests/ajax_rename.php index cb62d22a7e2bab5cdce938653552f404d93f99d1..74ca1e4495da1409742df4fb142ba2eacc54ad5b 100644 --- a/apps/files/tests/ajax_rename.php +++ b/apps/files/tests/ajax_rename.php @@ -55,88 +55,6 @@ class Test_OC_Files_App_Rename extends \PHPUnit_Framework_TestCase { \OC\Files\Filesystem::tearDown(); } - /** - * @brief test rename of file/folder named "Shared" - */ - function testRenameSharedFolder() { - $dir = '/'; - $oldname = 'Shared'; - $newname = 'new_name'; - - $this->viewMock->expects($this->at(0)) - ->method('file_exists') - ->with('/') - ->will($this->returnValue(true)); - - $result = $this->files->rename($dir, $oldname, $newname); - $expected = array( - 'success' => false, - 'data' => array('message' => '%s could not be renamed') - ); - - $this->assertEquals($expected, $result); - } - - /** - * @brief test rename of file/folder named "Shared" - */ - function testRenameSharedFolderInSubdirectory() { - $dir = '/test'; - $oldname = 'Shared'; - $newname = 'new_name'; - - $this->viewMock->expects($this->at(0)) - ->method('file_exists') - ->with('/test') - ->will($this->returnValue(true)); - - $this->viewMock->expects($this->any()) - ->method('getFileInfo') - ->will($this->returnValue(new \OC\Files\FileInfo( - '/test', - null, - '/test', - array( - 'fileid' => 123, - 'type' => 'dir', - 'mimetype' => 'httpd/unix-directory', - 'mtime' => 0, - 'permissions' => 31, - 'size' => 18, - 'etag' => 'abcdef', - 'directory' => '/', - 'name' => 'new_name', - )))); - - $result = $this->files->rename($dir, $oldname, $newname); - - $this->assertTrue($result['success']); - $this->assertEquals(123, $result['data']['id']); - $this->assertEquals('new_name', $result['data']['name']); - $this->assertEquals(18, $result['data']['size']); - $this->assertEquals('httpd/unix-directory', $result['data']['mimetype']); - $icon = \OC_Helper::mimetypeIcon('dir'); - $icon = substr($icon, 0, -3) . 'svg'; - $this->assertEquals($icon, $result['data']['icon']); - } - - /** - * @brief test rename of file/folder to "Shared" - */ - function testRenameFolderToShared() { - $dir = '/'; - $oldname = 'oldname'; - $newname = 'Shared'; - - $result = $this->files->rename($dir, $oldname, $newname); - $expected = array( - 'success' => false, - 'data' => array('message' => "Invalid folder name. Usage of 'Shared' is reserved.") - ); - - $this->assertEquals($expected, $result); - } - /** * @brief test rename of file/folder */ diff --git a/apps/files/tests/js/filesSpec.js b/apps/files/tests/js/filesSpec.js index 95bf87e03eca176448aa0fc85b229cc43034698b..018c8ef0f3cff215a718a9491e9fbad12a47c1b2 100644 --- a/apps/files/tests/js/filesSpec.js +++ b/apps/files/tests/js/filesSpec.js @@ -48,41 +48,6 @@ describe('Files tests', function() { expect(error).toEqual(false); } }); - it('Validates correct file names do not create Shared folder in root', function() { - // create shared file in subfolder - var error = false; - try { - expect(Files.isFileNameValid('shared', '/foo')).toEqual(true); - expect(Files.isFileNameValid('Shared', '/foo')).toEqual(true); - } - catch (e) { - error = e; - } - expect(error).toEqual(false); - - // create shared file in root - var threwException = false; - try { - Files.isFileNameValid('Shared', '/'); - console.error('Invalid file name not detected'); - } - catch (e) { - threwException = true; - } - expect(threwException).toEqual(true); - - // create shared file in root - var threwException = false; - try { - Files.isFileNameValid('shared', '/'); - console.error('Invalid file name not detected'); - } - catch (e) { - threwException = true; - } - expect(threwException).toEqual(true); - - }); it('Detects invalid file names', function() { var fileNames = [ '', diff --git a/apps/files_encryption/hooks/hooks.php b/apps/files_encryption/hooks/hooks.php index 0b6c5adf3fb613ef9a0a9f7265b69bafd09e57ff..5f0494e62ca38361e2b0b39c42b2218fef5b5536 100644 --- a/apps/files_encryption/hooks/hooks.php +++ b/apps/files_encryption/hooks/hooks.php @@ -302,25 +302,6 @@ class Hooks { */ public static function postShared($params) { - // NOTE: $params has keys: - // [itemType] => file - // itemSource -> int, filecache file ID - // [parent] => - // [itemTarget] => /13 - // shareWith -> string, uid of user being shared to - // fileTarget -> path of file being shared - // uidOwner -> owner of the original file being shared - // [shareType] => 0 - // [shareWith] => test1 - // [uidOwner] => admin - // [permissions] => 17 - // [fileSource] => 13 - // [fileTarget] => /test8 - // [id] => 10 - // [token] => - // [run] => whether emitting script should continue to run - // TODO: Should other kinds of item be encrypted too? - if (\OCP\App::isEnabled('files_encryption') === false) { return true; } @@ -331,71 +312,22 @@ class Hooks { $session = new \OCA\Encryption\Session($view); $userId = \OCP\User::getUser(); $util = new Util($view, $userId); - $path = $util->fileIdToPath($params['itemSource']); - - $share = $util->getParentFromShare($params['id']); - //if parent is set, then this is a re-share action - if ($share['parent'] !== null) { - - // get the parent from current share - $parent = $util->getShareParent($params['parent']); - - // if parent has the same type than the child it is a 1:1 share - if ($parent['item_type'] === $params['itemType']) { - - // prefix path with Shared - $path = '/Shared' . $parent['file_target']; - } else { - - // NOTE: parent is folder but shared was a file! - // we try to rebuild the missing path - // some examples we face here - // user1 share folder1 with user2 folder1 has - // the following structure - // /folder1/subfolder1/subsubfolder1/somefile.txt - // user2 re-share subfolder2 with user3 - // user3 re-share somefile.txt user4 - // so our path should be - // /Shared/subfolder1/subsubfolder1/somefile.txt - // while user3 is sharing - - if ($params['itemType'] === 'file') { - // get target path - $targetPath = $util->fileIdToPath($params['fileSource']); - $targetPathSplit = array_reverse(explode('/', $targetPath)); - - // init values - $path = ''; - $sharedPart = ltrim($parent['file_target'], '/'); - - // rebuild path - foreach ($targetPathSplit as $pathPart) { - if ($pathPart !== $sharedPart) { - $path = '/' . $pathPart . $path; - } else { - break; - } - } - // prefix path with Shared - $path = '/Shared' . $parent['file_target'] . $path; - } else { - // prefix path with Shared - $path = '/Shared' . $parent['file_target'] . $params['fileTarget']; - } - } - } + $path = \OC\Files\Filesystem::getPath($params['fileSource']); $sharingEnabled = \OCP\Share::isEnabled(); // get the path including mount point only if not a shared folder - if (strncmp($path, '/Shared', strlen('/Shared') !== 0)) { - // get path including the the storage mount point - $path = $util->getPathWithMountPoint($params['itemSource']); + list($storage, ) = \OC\Files\Filesystem::resolvePath('/' . $userId . '/files' . $path); + + if (!($storage instanceof \OC\Files\Storage\Local)) { + $mountPoint = 'files' . $storage->getMountPoint(); + } else { + $mountPoint = ''; } // if a folder was shared, get a list of all (sub-)folders if ($params['itemType'] === 'folder') { - $allFiles = $util->getAllFiles($path); + $allFiles = $util->getAllFiles($path, $mountPoint); } else { $allFiles = array($path); } @@ -412,13 +344,6 @@ class Hooks { */ public static function postUnshare($params) { - // NOTE: $params has keys: - // [itemType] => file - // [itemSource] => 13 - // [shareType] => 0 - // [shareWith] => test1 - // [itemParent] => - if (\OCP\App::isEnabled('files_encryption') === false) { return true; } @@ -428,34 +353,7 @@ class Hooks { $view = new \OC_FilesystemView('/'); $userId = \OCP\User::getUser(); $util = new Util($view, $userId); - $path = $util->fileIdToPath($params['itemSource']); - - // check if this is a re-share - if ($params['itemParent']) { - - // get the parent from current share - $parent = $util->getShareParent($params['itemParent']); - - // get target path - $targetPath = $util->fileIdToPath($params['itemSource']); - $targetPathSplit = array_reverse(explode('/', $targetPath)); - - // init values - $path = ''; - $sharedPart = ltrim($parent['file_target'], '/'); - - // rebuild path - foreach ($targetPathSplit as $pathPart) { - if ($pathPart !== $sharedPart) { - $path = '/' . $pathPart . $path; - } else { - break; - } - } - - // prefix path with Shared - $path = '/Shared' . $parent['file_target'] . $path; - } + $path = \OC\Files\Filesystem::getPath($params['fileSource']); // for group shares get a list of the group members if ($params['shareType'] === \OCP\Share::SHARE_TYPE_GROUP) { @@ -469,14 +367,17 @@ class Hooks { } // get the path including mount point only if not a shared folder - if (strncmp($path, '/Shared', strlen('/Shared') !== 0)) { - // get path including the the storage mount point - $path = $util->getPathWithMountPoint($params['itemSource']); + list($storage, ) = \OC\Files\Filesystem::resolvePath('/' . $userId . '/files' . $path); + + if (!($storage instanceof \OC\Files\Storage\Local)) { + $mountPoint = 'files' . $storage->getMountPoint(); + } else { + $mountPoint = ''; } // if we unshare a folder we need a list of all (sub-)files if ($params['itemType'] === 'folder') { - $allFiles = $util->getAllFiles($path); + $allFiles = $util->getAllFiles($path, $mountPoint); } else { $allFiles = array($path); } @@ -510,6 +411,8 @@ class Hooks { // otherwise we perform a stream copy, so we get a new set of keys $mp1 = $view->getMountPoint('/' . $user . '/files/' . $params['oldpath']); $mp2 = $view->getMountPoint('/' . $user . '/files/' . $params['newpath']); + list($storage1, ) = Filesystem::resolvePath($params['oldpath']); + if ($mp1 === $mp2) { self::$renamedFiles[$params['oldpath']] = array( 'uid' => $ownerOld, diff --git a/apps/files_encryption/l10n/es_AR.php b/apps/files_encryption/l10n/es_AR.php index bc1eedbf5ebe82e47de20330255c4b553fbc983d..b885716f89b5b8aa67cd5f75d1b1b1cf315aa061 100644 --- a/apps/files_encryption/l10n/es_AR.php +++ b/apps/files_encryption/l10n/es_AR.php @@ -16,6 +16,7 @@ $TRANSLATIONS = array( "Please make sure that PHP 5.3.3 or newer is installed and that OpenSSL together with the PHP extension is enabled and configured properly. For now, the encryption app has been disabled." => "Por favor, asegúrese de que PHP 5.3.3 o una versión más reciente esté instalado y que OpenSSL junto con la extensión PHP esté habilitado y configurado apropiadamente. Por ahora, la aplicación de encriptación ha sido deshabilitada.", "Following users are not set up for encryption:" => "Los siguientes usuarios no fueron configurados para encriptar:", "Initial encryption started... This can take some time. Please wait." => "Encriptación inicial comenzada... Esto puede durar un tiempo. Por favor espere.", +"Initial encryption running... Please try again later." => "Encriptación inicial corriendo... Por favor intente mas tarde. ", "Go directly to your " => "Ve directamente a tu", "personal settings" => "Configuración personal", "Encryption" => "Encriptación", diff --git a/apps/files_encryption/l10n/km.php b/apps/files_encryption/l10n/km.php new file mode 100644 index 0000000000000000000000000000000000000000..0defe6c5388f0616ffb601e92f208112679bdefc --- /dev/null +++ b/apps/files_encryption/l10n/km.php @@ -0,0 +1,11 @@ + "បាន​ប្ដូរ​ពាក្យ​សម្ងាត់​ដោយ​ជោគជ័យ។", +"Could not change the password. Maybe the old password was not correct." => "មិន​អាច​ប្ដូរ​ពាក្យ​សម្ងាត់​បាន​ទេ។ ប្រហែល​ពាក្យ​សម្ងាត់​ចាស់​មិន​ត្រឹម​ត្រូវ។", +"personal settings" => "ការ​កំណត់​ផ្ទាល់​ខ្លួន", +"Encryption" => "កូដនីយកម្ម", +"Enabled" => "បាន​បើក", +"Disabled" => "បាន​បិទ", +"Change Password" => "ប្ដូរ​ពាក្យ​សម្ងាត់" +); +$PLURAL_FORMS = "nplurals=1; plural=0;"; diff --git a/apps/files_encryption/l10n/pt_PT.php b/apps/files_encryption/l10n/pt_PT.php index d6d3c2603639f7e8e53dc85d556ad56cbaed62a7..9a1963953e3427e2b07dd7c6afb60b19c11c9bc0 100644 --- a/apps/files_encryption/l10n/pt_PT.php +++ b/apps/files_encryption/l10n/pt_PT.php @@ -7,8 +7,11 @@ $TRANSLATIONS = array( "Password successfully changed." => "Password alterada com sucesso.", "Could not change the password. Maybe the old password was not correct." => "Não foi possivel alterar a password. Possivelmente a password antiga não está correcta.", "Could not update the private key password. Maybe the old password was not correct." => "Não foi possível alterar a chave. Possivelmente a password antiga não está correcta.", +"Can not decrypt this file, probably this is a shared file. Please ask the file owner to reshare the file with you." => "Não foi possível desencriptar este ficheiro, possivelmente é um ficheiro partilhado. Peça ao proprietário do ficheiro para voltar a partilhar o ficheiro consigo.", +"Unknown error please check your system settings or contact your administrator" => "Erro desconhecido. Verifique por favor as definições do sistema ou contacte o seu administrador", "Missing requirements." => "Faltam alguns requisitos.", "Following users are not set up for encryption:" => "Os utilizadores seguintes não estão marcados para cifragem:", +"Go directly to your " => "Ir directamente para o seu", "personal settings" => "configurações personalizadas ", "Encryption" => "Encriptação", "Enable recovery key (allow to recover users files in case of password loss):" => "Active a chave de recuperação (permite recuperar os ficheiros no caso de perda da password):", diff --git a/apps/files_encryption/l10n/sk_SK.php b/apps/files_encryption/l10n/sk_SK.php index 5fcd0a9f0604301dae7cc61cc581bb29abd3e1c3..53ee2bc0303dc43c6674b8a843fb7ac595dae0f4 100644 --- a/apps/files_encryption/l10n/sk_SK.php +++ b/apps/files_encryption/l10n/sk_SK.php @@ -16,6 +16,7 @@ $TRANSLATIONS = array( "Please make sure that PHP 5.3.3 or newer is installed and that OpenSSL together with the PHP extension is enabled and configured properly. For now, the encryption app has been disabled." => "Prosím uistite sa, že PHP verzie 5.3.3 alebo novšej je nainštalované a tiež, že OpenSSL knižnica spolu z PHP rozšírením je povolená a konfigurovaná správne. Nateraz bola aplikácia šifrovania zablokovaná.", "Following users are not set up for encryption:" => "Nasledujúci používatelia nie sú nastavení pre šifrovanie:", "Initial encryption started... This can take some time. Please wait." => "Počiatočné šifrovanie započalo ... To môže nejakú dobu trvať. Čakajte prosím.", +"Initial encryption running... Please try again later." => "Počiatočné šifrovanie beží... Skúste to neskôr znovu.", "Go directly to your " => "Choďte priamo do vášho", "personal settings" => "osobné nastavenia", "Encryption" => "Šifrovanie", diff --git a/apps/files_encryption/l10n/zh_TW.php b/apps/files_encryption/l10n/zh_TW.php index 390e6aff8c6acb19083eb1757012a7d686609402..84146437ac0b590b794cc54a9e3cb4f329d79e9c 100644 --- a/apps/files_encryption/l10n/zh_TW.php +++ b/apps/files_encryption/l10n/zh_TW.php @@ -15,6 +15,8 @@ $TRANSLATIONS = array( "Missing requirements." => "遺失必要條件。", "Please make sure that PHP 5.3.3 or newer is installed and that OpenSSL together with the PHP extension is enabled and configured properly. For now, the encryption app has been disabled." => "請確認已安裝 PHP 5.3.3 或是更新的版本以及 OpenSSL 也一併安裝在 PHP extension 裡面並啟用及設置完成。現在,加密功能是停用的。", "Following users are not set up for encryption:" => "以下的使用者無法設定加密:", +"Initial encryption started... This can take some time. Please wait." => "加密初始已啟用...這個需要一些時間。請稍等。", +"Initial encryption running... Please try again later." => "加密初始執行中...請晚點再試。", "Go directly to your " => "直接到您的", "personal settings" => "個人設定", "Encryption" => "加密", diff --git a/apps/files_encryption/lib/crypt.php b/apps/files_encryption/lib/crypt.php index caca13aceceec843928e7a43d2c5329f90909413..a4f7bd35497543c000c43358b0118a6c482ef6e6 100755 --- a/apps/files_encryption/lib/crypt.php +++ b/apps/files_encryption/lib/crypt.php @@ -497,13 +497,13 @@ class Crypt { } /** - * @brief Generate a pseudo random 1024kb ASCII key, used as file key + * @brief Generate a pseudo random 256-bit ASCII key, used as file key * @returns $key Generated key */ public static function generateKey() { // Generate key - if ($key = base64_encode(openssl_random_pseudo_bytes(183, $strong))) { + if ($key = base64_encode(openssl_random_pseudo_bytes(32, $strong))) { if (!$strong) { diff --git a/apps/files_encryption/lib/util.php b/apps/files_encryption/lib/util.php index b86815021a8feb4ed9be997dd936b08124ed28d9..6372ab31b6ec1c2e111825f26dc0926cd2ad4dd3 100644 --- a/apps/files_encryption/lib/util.php +++ b/apps/files_encryption/lib/util.php @@ -969,33 +969,6 @@ class Util { } - /** - * @brief get path of a file. - * @param int $fileId id of the file - * @return string path of the file - */ - public static function fileIdToPath($fileId) { - - $sql = 'SELECT `path` FROM `*PREFIX*filecache` WHERE `fileid` = ?'; - - $query = \OCP\DB::prepare($sql); - - $result = $query->execute(array($fileId)); - - $path = false; - if (\OCP\DB::isError($result)) { - \OCP\Util::writeLog('Encryption library', \OC_DB::getErrorMessage($result), \OCP\Util::ERROR); - } else { - $row = $result->fetchRow(); - if ($row) { - $path = substr($row['path'], strlen('files')); - } - } - - return $path; - - } - /** * @brief Filter an array of UIDs to return only ones ready for sharing * @param array $unfilteredUsers users to be checked for sharing readiness @@ -1398,7 +1371,7 @@ class Util { * @param string $dir relative to the users files folder * @return array with list of files relative to the users files folder */ - public function getAllFiles($dir) { + public function getAllFiles($dir, $mountPoint = '') { $result = array(); $dirList = array($dir); @@ -1408,11 +1381,13 @@ class Util { $this->userFilesDir . '/' . $dir)); foreach ($content as $c) { - $usersPath = isset($c['usersPath']) ? $c['usersPath'] : $c['path']; + // getDirectoryContent() returns the paths relative to the mount points, so we need + // to re-construct the complete path + $path = ($mountPoint !== '') ? $mountPoint . '/' . $c['path'] : $c['path']; if ($c['type'] === 'dir') { - $dirList[] = substr($usersPath, strlen("files")); + $dirList[] = substr($path, strlen("files")); } else { - $result[] = substr($usersPath, strlen("files")); + $result[] = substr($path, strlen("files")); } } @@ -1421,54 +1396,6 @@ class Util { return $result; } - /** - * @brief get shares parent. - * @param int $id of the current share - * @return array of the parent - */ - public static function getShareParent($id) { - - $sql = 'SELECT `file_target`, `item_type` FROM `*PREFIX*share` WHERE `id` = ?'; - - $query = \OCP\DB::prepare($sql); - - $result = $query->execute(array($id)); - - $row = array(); - if (\OCP\DB::isError($result)) { - \OCP\Util::writeLog('Encryption library', \OC_DB::getErrorMessage($result), \OCP\Util::ERROR); - } else { - $row = $result->fetchRow(); - } - - return $row; - - } - - /** - * @brief get shares parent. - * @param int $id of the current share - * @return array of the parent - */ - public static function getParentFromShare($id) { - - $sql = 'SELECT `parent` FROM `*PREFIX*share` WHERE `id` = ?'; - - $query = \OCP\DB::prepare($sql); - - $result = $query->execute(array($id)); - - $row = array(); - if (\OCP\DB::isError($result)) { - \OCP\Util::writeLog('Encryption library', \OC_DB::getErrorMessage($result), \OCP\Util::ERROR); - } else { - $row = $result->fetchRow(); - } - - return $row; - - } - /** * @brief get owner of the shared files. * @param $id @@ -1710,23 +1637,6 @@ class Util { $this->recoverAllFiles('/', $privateKey); } - /** - * Get the path including the storage mount point - * @param int $id - * @return string the path including the mount point like AmazonS3/folder/file.txt - */ - public function getPathWithMountPoint($id) { - list($storage, $internalPath) = \OC\Files\Cache\Cache::getById($id); - $mount = \OC\Files\Filesystem::getMountByStorageId($storage); - $mountPoint = $mount[0]->getMountPoint(); - $path = \OC\Files\Filesystem::normalizePath($mountPoint . '/' . $internalPath); - - // reformat the path to be relative e.g. /user/files/folder becomes /folder/ - $relativePath = \OCA\Encryption\Helper::stripUserFilesPath($path); - - return $relativePath; - } - /** * @brief check if the file is stored on a system wide mount point * @param $path relative to /data/user with leading '/' diff --git a/apps/files_encryption/tests/hooks.php b/apps/files_encryption/tests/hooks.php index d0e4b5f732ee5af06369b4ace3f996fc17a9a205..047084ca2c177713202d75b30fd402c067027e90 100644 --- a/apps/files_encryption/tests/hooks.php +++ b/apps/files_encryption/tests/hooks.php @@ -219,18 +219,20 @@ class Test_Encryption_Hooks extends \PHPUnit_Framework_TestCase { \Test_Encryption_Util::loginHelper(\Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER2); \OC_User::setUserId(\Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER2); - // user2 has a local file with the same name + // user2 update the shared file $this->user2View->file_put_contents($this->filename, $this->data); - // check if all keys are generated - $this->assertTrue($this->rootView->file_exists( + // keys should be stored at user1s dir, not in user2s + $this->assertFalse($this->rootView->file_exists( self::TEST_ENCRYPTION_HOOKS_USER2 . '/files_encryption/share-keys/' . $this->filename . '.' . \Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER2 . '.shareKey')); - $this->assertTrue($this->rootView->file_exists( + $this->assertFalse($this->rootView->file_exists( self::TEST_ENCRYPTION_HOOKS_USER2 . '/files_encryption/keyfiles/' . $this->filename . '.key')); // delete the Shared file from user1 in data/user2/files/Shared - $this->user2View->unlink('/Shared/' . $this->filename); + $result = $this->user2View->unlink($this->filename); + + $this->assertTrue($result); // now keys from user1s home should be gone $this->assertFalse($this->rootView->file_exists( @@ -242,26 +244,12 @@ class Test_Encryption_Hooks extends \PHPUnit_Framework_TestCase { $this->assertFalse($this->rootView->file_exists( self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/keyfiles/' . $this->filename . '.key')); - // but user2 keys should still exist - $this->assertTrue($this->rootView->file_exists( - self::TEST_ENCRYPTION_HOOKS_USER2 . '/files_encryption/share-keys/' - . $this->filename . '.' . \Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER2 . '.shareKey')); - $this->assertTrue($this->rootView->file_exists( - self::TEST_ENCRYPTION_HOOKS_USER2 . '/files_encryption/keyfiles/' . $this->filename . '.key')); - // cleanup - $this->user2View->unlink($this->filename); - \Test_Encryption_Util::logoutHelper(); \Test_Encryption_Util::loginHelper(\Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER1); \OC_User::setUserId(\Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER1); - // unshare the file - \OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, self::TEST_ENCRYPTION_HOOKS_USER2); - - $this->user1View->unlink($this->filename); - if ($stateFilesTrashbin) { OC_App::enable('files_trashbin'); } diff --git a/apps/files_encryption/tests/share.php b/apps/files_encryption/tests/share.php index 1f57d7cb635bca44a0f4f84cdc315c23dd65ec1c..512671c5767b5140c7d93de0727751e4c07080c4 100755 --- a/apps/files_encryption/tests/share.php +++ b/apps/files_encryption/tests/share.php @@ -175,7 +175,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase { // get file contents $retrievedCryptedFile = $this->view->file_get_contents( - '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '/files/Shared/' . $this->filename); + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '/files/' . $this->filename); // check if data is the same as we previously written $this->assertEquals($this->dataShort, $retrievedCryptedFile); @@ -213,14 +213,14 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase { function testReShareFile($withTeardown = true) { $this->testShareFile(false); - // login as user1 + // login as user2 \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2); // get the file info $fileInfo = $this->view->getFileInfo( - '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '/files/Shared/' . $this->filename); + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '/files/' . $this->filename); - // share the file with user2 + // share the file with user3 \OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3, OCP\PERMISSION_ALL); // login as admin @@ -236,7 +236,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase { // get file contents $retrievedCryptedFile = $this->view->file_get_contents( - '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3 . '/files/Shared/' . $this->filename); + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3 . '/files/' . $this->filename); // check if data is the same as previously written $this->assertEquals($this->dataShort, $retrievedCryptedFile); @@ -333,7 +333,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase { // get file contents $retrievedCryptedFile = $this->view->file_get_contents( - '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '/files/Shared' . $this->folder1 + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '/files/' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename); // check if data is the same @@ -376,7 +376,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase { function testReShareFolder($withTeardown = true) { $fileInfoFolder1 = $this->testShareFolder(false); - // login as user1 + // login as user2 \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2); // disable encryption proxy to prevent recursive calls @@ -385,7 +385,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase { // get the file info from previous created folder $fileInfoSubFolder = $this->view->getFileInfo( - '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '/files/Shared' . $this->folder1 + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '/files/' . $this->folder1 . $this->subfolder); // check if we have a valid file info @@ -394,24 +394,24 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase { // re-enable the file proxy \OC_FileProxy::$enabled = $proxyStatus; - // share the file with user2 + // share the file with user3 \OCP\Share::shareItem('folder', $fileInfoSubFolder['fileid'], \OCP\Share::SHARE_TYPE_USER, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3, OCP\PERMISSION_ALL); // login as admin \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1); - // check if share key for user2 exists + // check if share key for user3 exists $this->assertTrue($this->view->file_exists( '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files_encryption/share-keys' . $this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3 . '.shareKey')); - // login as user2 + // login as user3 \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3); // get file contents $retrievedCryptedFile = $this->view->file_get_contents( - '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3 . '/files/Shared' . $this->subfolder + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3 . '/files/' . $this->subfolder . $this->subsubfolder . '/' . $this->filename); // check if data is the same @@ -419,7 +419,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase { // get the file info $fileInfo = $this->view->getFileInfo( - '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3 . '/files/Shared' . $this->subfolder + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3 . '/files/' . $this->subfolder . $this->subsubfolder . '/' . $this->filename); // check if we have fileInfos @@ -442,7 +442,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase { // get file contents $retrievedCryptedFile = $this->view->file_get_contents( - '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER4 . '/files/Shared/' . $this->filename); + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER4 . '/files/' . $this->filename); // check if data is the same $this->assertEquals($this->dataShort, $retrievedCryptedFile); @@ -624,7 +624,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase { // get file contents $retrievedCryptedFile = $this->view->file_get_contents( - '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3 . '/files/Shared/' . $this->filename); + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3 . '/files/' . $this->filename); // check if data is the same as we previously written $this->assertEquals($this->dataShort, $retrievedCryptedFile); @@ -676,6 +676,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase { // enable recovery for admin $this->assertTrue($util->setRecoveryForUser(1)); + $util->addRecoveryKeys(); // create folder structure $this->view->mkdir('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files' . $this->folder1); @@ -981,7 +982,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase { // share the file \OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2, OCP\PERMISSION_ALL); - // check if share key for user2exists + // check if share key for user2 exists $this->assertTrue($this->view->file_exists( '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files_encryption/share-keys/' . $this->filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '.shareKey')); @@ -990,31 +991,29 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase { // login as user2 \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2); - $this->assertTrue($this->view->file_exists('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '/files/Shared/' . $this->filename)); + $this->assertTrue($this->view->file_exists('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '/files/' . $this->filename)); // get file contents $retrievedCryptedFile = $this->view->file_get_contents( - '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '/files/Shared/' . $this->filename); + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '/files/' . $this->filename); // check if data is the same as we previously written $this->assertEquals($this->dataShort, $retrievedCryptedFile); - // move the file out of the shared folder - $this->view->rename('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '/files/Shared/' . $this->filename, - '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '/files/' . $this->filename); + // move the file to a subfolder + $this->view->rename('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '/files/' . $this->filename, + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '/files/' . $this->folder1 . $this->filename); // check if we can read the moved file $retrievedRenamedFile = $this->view->file_get_contents( - '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '/files/' . $this->filename); + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '/files/' . $this->folder1 . $this->filename); // check if data is the same as we previously written $this->assertEquals($this->dataShort, $retrievedRenamedFile); - // the owners file should be deleted - $this->assertFalse($this->view->file_exists('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files/' . $this->filename)); - // cleanup - $this->view->unlink('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '/files/' . $this->filename); + \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1); + $this->view->unlink('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files/' . $this->filename); } } diff --git a/apps/files_encryption/tests/util.php b/apps/files_encryption/tests/util.php index 203ba55dbfd9b6fa73b2c66d20f8255ea78bf624..a29ef831a526c1205c5c1797c87ac181fb5fe470 100755 --- a/apps/files_encryption/tests/util.php +++ b/apps/files_encryption/tests/util.php @@ -510,7 +510,11 @@ class Test_Encryption_Util extends \PHPUnit_Framework_TestCase { */ public static function loginHelper($user, $create = false, $password = false) { if ($create) { - \OC_User::createUser($user, $user); + try { + \OC_User::createUser($user, $user); + } catch(\Exception $e) { // catch username is already being used from previous aborted runs + + } } if ($password === false) { @@ -520,8 +524,8 @@ class Test_Encryption_Util extends \PHPUnit_Framework_TestCase { \OC_Util::tearDownFS(); \OC_User::setUserId(''); \OC\Files\Filesystem::tearDown(); - \OC_Util::setupFS($user); \OC_User::setUserId($user); + \OC_Util::setupFS($user); $params['uid'] = $user; $params['password'] = $password; diff --git a/apps/files_encryption/tests/webdav.php b/apps/files_encryption/tests/webdav.php index 8e8b9c53ceed8382f6554c7ffff5dedafaf0a49b..1fe4c13d59e8bae7622d38848fd4281bfcf9d410 100755 --- a/apps/files_encryption/tests/webdav.php +++ b/apps/files_encryption/tests/webdav.php @@ -33,6 +33,7 @@ use OCA\Encryption; /** * Class Test_Encryption_Webdav + * * @brief this class provide basic webdav tests for PUT,GET and DELETE */ class Test_Encryption_Webdav extends \PHPUnit_Framework_TestCase { @@ -48,6 +49,8 @@ class Test_Encryption_Webdav extends \PHPUnit_Framework_TestCase { public $dataShort; public $stateFilesTrashbin; + private static $storage; + public static function setUpBeforeClass() { // reset backend \OC_User::clearBackends(); @@ -65,6 +68,8 @@ class Test_Encryption_Webdav extends \PHPUnit_Framework_TestCase { // create test user \Test_Encryption_Util::loginHelper(\Test_Encryption_Webdav::TEST_ENCRYPTION_WEBDAV_USER1, true); + + self::$storage = new \OC\Files\Storage\Temporary(array()); } function setUp() { @@ -96,8 +101,7 @@ class Test_Encryption_Webdav extends \PHPUnit_Framework_TestCase { // reset app files_trashbin if ($this->stateFilesTrashbin) { OC_App::enable('files_trashbin'); - } - else { + } else { OC_App::disable('files_trashbin'); } } @@ -153,7 +157,7 @@ class Test_Encryption_Webdav extends \PHPUnit_Framework_TestCase { $this->assertTrue(Encryption\Crypt::isCatfileContent($encryptedContent)); // get decrypted file contents - $decrypt = file_get_contents('crypt:///' . $this->userId . '/files'. $filename); + $decrypt = file_get_contents('crypt:///' . $this->userId . '/files' . $filename); // check if file content match with the written content $this->assertEquals($this->dataShort, $decrypt); @@ -225,7 +229,12 @@ class Test_Encryption_Webdav extends \PHPUnit_Framework_TestCase { $requestBackend = new OC_Connector_Sabre_Request(); // Create ownCloud Dir - $publicDir = new OC_Connector_Sabre_Directory(''); + $root = '/' . $this->userId . '/files'; + \OC\Files\Filesystem::mount(self::$storage, array(), $root); + $view = new \OC\Files\View($root); + $publicDir = new OC_Connector_Sabre_Directory($view, $view->getFileInfo('')); + $objectTree = new \OC\Connector\Sabre\ObjectTree(); + $objectTree->init($publicDir, $view); // Fire up server $server = new Sabre_DAV_Server($publicDir); @@ -236,8 +245,9 @@ class Test_Encryption_Webdav extends \PHPUnit_Framework_TestCase { $server->addPlugin(new Sabre_DAV_Auth_Plugin($authBackend, 'ownCloud')); $server->addPlugin(new Sabre_DAV_Locks_Plugin($lockBackend)); $server->addPlugin(new Sabre_DAV_Browser_Plugin(false)); // Show something in the Browser, but no upload - $server->addPlugin(new OC_Connector_Sabre_QuotaPlugin()); + $server->addPlugin(new OC_Connector_Sabre_QuotaPlugin($view)); $server->addPlugin(new OC_Connector_Sabre_MaintenancePlugin()); + $server->debugExceptions = true; // And off we go! if ($body) { diff --git a/apps/files_external/css/settings.css b/apps/files_external/css/settings.css index 1d3489f7f57a84021704631a94fc36598808bd7d..ee2c0aae64bd2fc5dea1dcdfd7443fdae2742570 100644 --- a/apps/files_external/css/settings.css +++ b/apps/files_external/css/settings.css @@ -6,7 +6,7 @@ td.status > span { } td.mountPoint, td.backend { width:160px; } -td.remove>img { visibility:hidden; padding-top:13px; } +td.remove>img { visibility:hidden; padding-top:7px; } tr:hover>td.remove>img { visibility:visible; cursor:pointer; } #addMountPoint>td { border:none; } #addMountPoint>td.applicable { visibility:hidden; } @@ -15,3 +15,8 @@ tr:hover>td.remove>img { visibility:visible; cursor:pointer; } #externalStorage label > input[type="checkbox"] { margin-right: 3px; } + +#externalStorage td.applicable div.chzn-container { + position: relative; + top: 3px; +} diff --git a/apps/files_external/l10n/ast.php b/apps/files_external/l10n/ast.php index 9d4d891dda317ef79217abb850020c6c0c091016..e2c02408f39849331324d641378c02d6d3e37ab6 100644 --- a/apps/files_external/l10n/ast.php +++ b/apps/files_external/l10n/ast.php @@ -1,7 +1,10 @@ "Nome de la carpeta", +"Configuration" => "Configuración", "Options" => "Opciones", "Groups" => "Grupos", +"Users" => "Usuarios", "Delete" => "Desaniciar" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/apps/files_external/l10n/ca.php b/apps/files_external/l10n/ca.php index 50b6f55231bd6db97e51ee54fe047e4ceb3550c6..8411d79306f098c061e364f96a0137c39a7e4aee 100644 --- a/apps/files_external/l10n/ca.php +++ b/apps/files_external/l10n/ca.php @@ -6,12 +6,19 @@ $TRANSLATIONS = array( "Please provide a valid Dropbox app key and secret." => "Proporcioneu una clau d'aplicació i secret vàlids per a Dropbox", "Error configuring Google Drive storage" => "Error en configurar l'emmagatzemament Google Drive", "Saved" => "Desat", +"Note: " => "Nota: ", +" and " => "i", +"Note: The cURL support in PHP is not enabled or installed. Mounting of %s is not possible. Please ask your system administrator to install it." => "Nota: El suport cURL no està activat o instal·lat a PHP. No es pot muntar %s. Demaneu a l'administrador del sistema que l'instal·li.", +"Note: The FTP support in PHP is not enabled or installed. Mounting of %s is not possible. Please ask your system administrator to install it." => "Nota: El suport FTP per PHP no està activat o no està instal·lat. No es pot muntar %s. Demaneu a l'administrador del sistema que l'instal·li.", +"Note: \"%s\" is not installed. Mounting of %s is not possible. Please ask your system administrator to install it." => "Nota: %s no està instal·lat. No es pot muntar %s. Demaneu a l'administrador del sistema que l'instal·li.", "External Storage" => "Emmagatzemament extern", "Folder name" => "Nom de la carpeta", "External storage" => "Emmagatzemament extern", "Configuration" => "Configuració", "Options" => "Options", +"Available for" => "Disponible per", "Add storage" => "Afegeix emmagatzemament", +"No user or group" => "Sense usuaris o grups", "All Users" => "Tots els usuaris", "Groups" => "Grups", "Users" => "Usuaris", diff --git a/apps/files_external/l10n/cs_CZ.php b/apps/files_external/l10n/cs_CZ.php index 6a4e09aa3ab4bf668cd997a4840966e5b607cd41..90d64529b1c93f81437598b97f2931366332682f 100644 --- a/apps/files_external/l10n/cs_CZ.php +++ b/apps/files_external/l10n/cs_CZ.php @@ -6,12 +6,19 @@ $TRANSLATIONS = array( "Please provide a valid Dropbox app key and secret." => "Zadejte, prosím, platný klíč a bezpečnostní frázi aplikace Dropbox.", "Error configuring Google Drive storage" => "Chyba při nastavení úložiště Google Drive", "Saved" => "Uloženo", +"Note: " => "Poznámka:", +" and " => "a", +"Note: The cURL support in PHP is not enabled or installed. Mounting of %s is not possible. Please ask your system administrator to install it." => "Poznámka: cURL podpora v PHP není povolena nebo nainstalována. Není možné připojení %s. Prosím požádejte svého správce systému ať ji nainstaluje.", +"Note: The FTP support in PHP is not enabled or installed. Mounting of %s is not possible. Please ask your system administrator to install it." => "Poznámka: FTP podpora v PHP není povolena nebo nainstalována. Není možné připojení %s. Prosím požádejte svého správce systému ať ji nainstaluje.", +"Note: \"%s\" is not installed. Mounting of %s is not possible. Please ask your system administrator to install it." => "Poznámka: \"%s\" není instalováno. Není možné připojení %s. Prosím požádejte svého správce systému o instalaci.", "External Storage" => "Externí úložiště", "Folder name" => "Název složky", "External storage" => "Externí úložiště", "Configuration" => "Nastavení", "Options" => "Možnosti", +"Available for" => "Dostupné pro", "Add storage" => "Přidat úložiště", +"No user or group" => "Žádný uživatel nebo skupina.", "All Users" => "Všichni uživatelé", "Groups" => "Skupiny", "Users" => "Uživatelé", diff --git a/apps/files_external/l10n/de.php b/apps/files_external/l10n/de.php index 1d5a4f626b0bc26ccaf3485c4a960e621936afcf..25c7ffb3af185b5b2529ddf4c94837248b6c7bac 100644 --- a/apps/files_external/l10n/de.php +++ b/apps/files_external/l10n/de.php @@ -6,6 +6,11 @@ $TRANSLATIONS = array( "Please provide a valid Dropbox app key and secret." => "Bitte trage einen gültigen Dropbox-App-Key mit Secret ein.", "Error configuring Google Drive storage" => "Fehler beim Einrichten von Google Drive", "Saved" => "Gespeichert", +"Note: " => "Hinweis: ", +" and " => "und", +"Note: The cURL support in PHP is not enabled or installed. Mounting of %s is not possible. Please ask your system administrator to install it." => "Hinweis: Die cURL-Unterstützung von PHP ist nicht aktiviert oder installiert. Das Hinzufügen von %s ist nicht möglich. Bitte wende Dich zur Installation an Deinen Systemadministrator.", +"Note: The FTP support in PHP is not enabled or installed. Mounting of %s is not possible. Please ask your system administrator to install it." => "Hinweis: Die FTP Unterstützung von PHP ist nicht aktiviert oder installiert. Das Hinzufügen von %s ist nicht möglich. Bitte wende Dich sich zur Installation an Deinen Systemadministrator.", +"Note: \"%s\" is not installed. Mounting of %s is not possible. Please ask your system administrator to install it." => "Hinweis: \"%s\" ist nicht installiert. Das Hinzufügen von %s ist nicht möglich. Bitte wende Dich sich zur Installation an Deinen Systemadministrator.", "External Storage" => "Externer Speicher", "Folder name" => "Ordnername", "External storage" => "Externer Speicher", diff --git a/apps/files_external/l10n/de_DE.php b/apps/files_external/l10n/de_DE.php index c2534f3dd8a064745ac23b0af3f4121fb942c7bb..186aa5a299a568397542140d462aeadcb073cf50 100644 --- a/apps/files_external/l10n/de_DE.php +++ b/apps/files_external/l10n/de_DE.php @@ -6,6 +6,11 @@ $TRANSLATIONS = array( "Please provide a valid Dropbox app key and secret." => "Bitte tragen Sie einen gültigen Dropbox-App-Key mit Secret ein.", "Error configuring Google Drive storage" => "Fehler beim Einrichten von Google Drive", "Saved" => "Gespeichert", +"Note: " => "Hinweis: ", +" and " => "und", +"Note: The cURL support in PHP is not enabled or installed. Mounting of %s is not possible. Please ask your system administrator to install it." => "Hinweis: Die cURL-Unterstützung von PHP ist nicht aktiviert oder installiert. Das Hinzufügen von %s ist nicht möglich. Bitte wenden Sie sich zur Installation an Ihren Systemadministrator.", +"Note: The FTP support in PHP is not enabled or installed. Mounting of %s is not possible. Please ask your system administrator to install it." => "Hinweis: Die FTP Unterstützung von PHP ist nicht aktiviert oder installiert. Das Hinzufügen von %s ist nicht möglich. Bitte wenden Sie sich zur Installation an Ihren Systemadministrator.", +"Note: \"%s\" is not installed. Mounting of %s is not possible. Please ask your system administrator to install it." => "Hinweis: \"%s\" ist nicht installiert. Das Hinzufügen von %s ist nicht möglich. Bitte wenden Sie sich zur Installation an Ihren Systemadministrator.", "External Storage" => "Externer Speicher", "Folder name" => "Ordnername", "External storage" => "Externer Speicher", diff --git a/apps/files_external/l10n/en_GB.php b/apps/files_external/l10n/en_GB.php index 3160d5146211ffbe55254afb5ffca8e229e92174..4bd3f9daf2a8fc4fbdc8f53d17c51be60a9ddf90 100644 --- a/apps/files_external/l10n/en_GB.php +++ b/apps/files_external/l10n/en_GB.php @@ -6,6 +6,11 @@ $TRANSLATIONS = array( "Please provide a valid Dropbox app key and secret." => "Please provide a valid Dropbox app key and secret.", "Error configuring Google Drive storage" => "Error configuring Google Drive storage", "Saved" => "Saved", +"Note: " => "Note: ", +" and " => " and ", +"Note: The cURL support in PHP is not enabled or installed. Mounting of %s is not possible. Please ask your system administrator to install it." => "Note: The cURL support in PHP is not enabled or installed. Mounting of %s is not possible. Please ask your system administrator to install it.", +"Note: The FTP support in PHP is not enabled or installed. Mounting of %s is not possible. Please ask your system administrator to install it." => "Note: The FTP support in PHP is not enabled or installed. Mounting of %s is not possible. Please ask your system administrator to install it.", +"Note: \"%s\" is not installed. Mounting of %s is not possible. Please ask your system administrator to install it." => "Note: \"%s\" is not installed. Mounting of %s is not possible. Please ask your system administrator to install it.", "External Storage" => "External Storage", "Folder name" => "Folder name", "External storage" => "External storage", diff --git a/apps/files_external/l10n/es.php b/apps/files_external/l10n/es.php index 0af20d939b2a19f61a2acf8fddc370fba189ba6f..bfe542c6e7123fc4519cc1e15ae297abad07e067 100644 --- a/apps/files_external/l10n/es.php +++ b/apps/files_external/l10n/es.php @@ -6,11 +6,17 @@ $TRANSLATIONS = array( "Please provide a valid Dropbox app key and secret." => "Por favor, proporcione un una clave válida de la app Dropbox y una clave secreta.", "Error configuring Google Drive storage" => "Error configurando el almacenamiento de Google Drive", "Saved" => "Guardado", +"Note: " => "Nota: ", +" and " => "y", +"Note: The cURL support in PHP is not enabled or installed. Mounting of %s is not possible. Please ask your system administrator to install it." => "Nota: El soporte de cURL en PHP no está activado o instalado. No se puede montar %s. Pídale al administrador de sistema que lo instale.", +"Note: The FTP support in PHP is not enabled or installed. Mounting of %s is not possible. Please ask your system administrator to install it." => "Nota: El soporte de FTP en PHP no está activado o instalado. No se puede montar %s. Pídale al administrador de sistema que lo instale.", +"Note: \"%s\" is not installed. Mounting of %s is not possible. Please ask your system administrator to install it." => "Nota: \"%s\" no está instalado. No se puede montar %s. Pídale al administrador de sistema que lo instale.", "External Storage" => "Almacenamiento externo", "Folder name" => "Nombre de la carpeta", "External storage" => "Almacenamiento externo", "Configuration" => "Configuración", "Options" => "Opciones", +"Available for" => "Disponible para", "Add storage" => "Añadir almacenamiento", "No user or group" => "Ningún usuario o grupo", "All Users" => "Todos los usuarios", diff --git a/apps/files_external/l10n/es_AR.php b/apps/files_external/l10n/es_AR.php index f184dbdb7d8aac91df20ff83fdcf0688d198a688..9bcff39f018007b60d5af62b6e99644db20ae9d5 100644 --- a/apps/files_external/l10n/es_AR.php +++ b/apps/files_external/l10n/es_AR.php @@ -5,6 +5,7 @@ $TRANSLATIONS = array( "Grant access" => "Permitir acceso", "Please provide a valid Dropbox app key and secret." => "Por favor, proporcioná un secreto y una contraseña válida para la aplicación Dropbox.", "Error configuring Google Drive storage" => "Error al configurar el almacenamiento de Google Drive", +"Saved" => "Guardado", "External Storage" => "Almacenamiento externo", "Folder name" => "Nombre de la carpeta", "External storage" => "Almacenamiento externo", diff --git a/apps/files_external/l10n/es_CL.php b/apps/files_external/l10n/es_CL.php new file mode 100644 index 0000000000000000000000000000000000000000..f52482cad463852c246e9e025b3b1bd44052f796 --- /dev/null +++ b/apps/files_external/l10n/es_CL.php @@ -0,0 +1,5 @@ + "Nombre del directorio" +); +$PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/apps/files_external/l10n/et_EE.php b/apps/files_external/l10n/et_EE.php index 0589d9fd518c2bbbea6b0db79b97b48671e48bee..4da749b155b2f7f6cdaf21241fd52e67bf519522 100644 --- a/apps/files_external/l10n/et_EE.php +++ b/apps/files_external/l10n/et_EE.php @@ -6,6 +6,11 @@ $TRANSLATIONS = array( "Please provide a valid Dropbox app key and secret." => "Palun sisesta korrektne Dropboxi rakenduse võti ja salasõna.", "Error configuring Google Drive storage" => "Viga Google Drive'i salvestusruumi seadistamisel", "Saved" => "Salvestatud", +"Note: " => "Märkus:", +" and " => "ja", +"Note: The cURL support in PHP is not enabled or installed. Mounting of %s is not possible. Please ask your system administrator to install it." => "Märkus: cURL tugi puudub PHP paigalduses. FTP %s hoidla ühendamine pole võimalik. Palu oma süsteemihalduril paigaldata cURL tugi.", +"Note: The FTP support in PHP is not enabled or installed. Mounting of %s is not possible. Please ask your system administrator to install it." => "Märkus: FTP tugi puudub PHP paigalduses. FTP %s hoidla ühendamine pole võimalik. Palu oma süsteemihalduril paigaldata FTP tugi.", +"Note: \"%s\" is not installed. Mounting of %s is not possible. Please ask your system administrator to install it." => "Märkus: \"%s\" pole paigaldatud. Hoidla %s ühendamine pole võimalik. Palu oma süsteemihalduril paigaldata vajalik tugi.", "External Storage" => "Väline salvestuskoht", "Folder name" => "Kausta nimi", "External storage" => "Väline andmehoidla", diff --git a/apps/files_external/l10n/fi_FI.php b/apps/files_external/l10n/fi_FI.php index 1924478ad235bc6a47472233313c9b3ff7dbde7e..677425d8a6a945cc5a29972718fe4c454ed7ee69 100644 --- a/apps/files_external/l10n/fi_FI.php +++ b/apps/files_external/l10n/fi_FI.php @@ -6,6 +6,11 @@ $TRANSLATIONS = array( "Please provide a valid Dropbox app key and secret." => "Anna kelvollinen Dropbox-sovellusavain ja salainen vastaus.", "Error configuring Google Drive storage" => "Virhe Google Drive levyn asetuksia tehtäessä", "Saved" => "Tallennettu", +"Note: " => "Huomio: ", +" and " => "ja", +"Note: The cURL support in PHP is not enabled or installed. Mounting of %s is not possible. Please ask your system administrator to install it." => "Huomio: PHP:n cURL-tuki ei ole käytössä tai sitä ei ole asennettu. Kohteen %s liittäminen ei ole mahdollista. Pyydä järjestelmän ylläpitäjää ottamaan cURL-tuki käyttöön.", +"Note: The FTP support in PHP is not enabled or installed. Mounting of %s is not possible. Please ask your system administrator to install it." => "Huomio: PHP:n FTP-tuki ei ole käytössä tai sitä ei ole asennettu. Kohteen %s liittäminen ei ole mahdollista. Pyydä järjestelmän ylläpitäjää ottamaan FTP-tuki käyttöön.", +"Note: \"%s\" is not installed. Mounting of %s is not possible. Please ask your system administrator to install it." => "Huomio: \"%s\" ei ole asennettu. Kohteen %s liittäminen ei ole mahdollista. Pyydä järjestelmän ylläpitäjää asentamaan puuttuva kohde.", "External Storage" => "Erillinen tallennusväline", "Folder name" => "Kansion nimi", "External storage" => "Ulkoinen tallennustila", diff --git a/apps/files_external/l10n/fr.php b/apps/files_external/l10n/fr.php index 80c189400963b57186dd15674d5046f0e06afacb..8ff1969718217b2a403d718e88fabe2e4a4b1fdd 100644 --- a/apps/files_external/l10n/fr.php +++ b/apps/files_external/l10n/fr.php @@ -6,6 +6,11 @@ $TRANSLATIONS = array( "Please provide a valid Dropbox app key and secret." => "Veuillez fournir une clé d'application (app key) ainsi qu'un mot de passe valides.", "Error configuring Google Drive storage" => "Erreur lors de la configuration du support de stockage Google Drive", "Saved" => "Sauvegarder", +"Note: " => "Attention :", +" and " => "et", +"Note: The cURL support in PHP is not enabled or installed. Mounting of %s is not possible. Please ask your system administrator to install it." => "Attention : Le support de cURL de PHP n'est pas activé ou installé. Le montage de %s n'est pas possible. Contactez votre administrateur système pour l'installer.", +"Note: The FTP support in PHP is not enabled or installed. Mounting of %s is not possible. Please ask your system administrator to install it." => "Attention : Le support FTP de PHP n'est pas activé ou installé. Le montage de %s n'est pas possible. Contactez votre administrateur système pour l'installer.", +"Note: \"%s\" is not installed. Mounting of %s is not possible. Please ask your system administrator to install it." => "Attention : \"%s\" n'est pas installé. Le montage de %s n'est pas possible. Contactez votre administrateur système pour l'installer.", "External Storage" => "Stockage externe", "Folder name" => "Nom du dossier", "External storage" => "Stockage externe", diff --git a/apps/files_external/l10n/gl.php b/apps/files_external/l10n/gl.php index 1117e8590cfcbc5eb3f0bcaa6affada2f04ee7c5..5efa6ce6028b77766231917e4c4a8d4dd355bae9 100644 --- a/apps/files_external/l10n/gl.php +++ b/apps/files_external/l10n/gl.php @@ -6,6 +6,11 @@ $TRANSLATIONS = array( "Please provide a valid Dropbox app key and secret." => "Forneza unha chave correcta e segreda do Dropbox.", "Error configuring Google Drive storage" => "Produciuse un erro ao configurar o almacenamento en Google Drive", "Saved" => "Gardado", +"Note: " => "Nota: ", +" and " => "e", +"Note: The cURL support in PHP is not enabled or installed. Mounting of %s is not possible. Please ask your system administrator to install it." => "Nota: A compatibilidade de cURL en PHP non está activada, ou non está instalado. Non é posíbel a montaxe de %s. Consulte co administrador do sistema como instalalo.", +"Note: The FTP support in PHP is not enabled or installed. Mounting of %s is not possible. Please ask your system administrator to install it." => "Nota: A compatibilidade de FTP en PHP non está activada, ou non está instalado. Non é posíbel a montaxe de %s. Consulte co administrador do sistema como instalalo.", +"Note: \"%s\" is not installed. Mounting of %s is not possible. Please ask your system administrator to install it." => "Nota: «%s» non está instalado. Non é posíbel a montaxe de %s. Consulte co administrador do sistema como instalalo.", "External Storage" => "Almacenamento externo", "Folder name" => "Nome do cartafol", "External storage" => "Almacenamento externo", diff --git a/apps/files_external/l10n/ia.php b/apps/files_external/l10n/ia.php index 47b0f89b8b5b5c2406a04c311472f3d02c0dbd60..1873f4ad6fe040fa8c64de6a36c03b0a01289a17 100644 --- a/apps/files_external/l10n/ia.php +++ b/apps/files_external/l10n/ia.php @@ -1,5 +1,6 @@ "Nomine de dossier", "Groups" => "Gruppos", "Users" => "Usatores", "Delete" => "Deler" diff --git a/apps/files_external/l10n/it.php b/apps/files_external/l10n/it.php index b68bff2474e4b85901efa03e49f4ee314941308b..f3a6f9af12936dc23fd7d868c4e6eef988e1747b 100644 --- a/apps/files_external/l10n/it.php +++ b/apps/files_external/l10n/it.php @@ -6,6 +6,11 @@ $TRANSLATIONS = array( "Please provide a valid Dropbox app key and secret." => "Fornisci chiave di applicazione e segreto di Dropbox validi.", "Error configuring Google Drive storage" => "Errore durante la configurazione dell'archivio Google Drive", "Saved" => "Salvato", +"Note: " => "Nota:", +" and " => "e", +"Note: The cURL support in PHP is not enabled or installed. Mounting of %s is not possible. Please ask your system administrator to install it." => "Nota: il supporto a cURL di PHP non è abilitato o installato. Impossibile montare %s. Chiedi al tuo amministratore di sistema di installarlo.", +"Note: The FTP support in PHP is not enabled or installed. Mounting of %s is not possible. Please ask your system administrator to install it." => "Nota: il supporto a FTP in PHP non è abilitato o installato. Impossibile montare %s. Chiedi al tuo amministratore di sistema di installarlo.", +"Note: \"%s\" is not installed. Mounting of %s is not possible. Please ask your system administrator to install it." => "Nota: \"%s\" non è installato. Impossibile montare %s. Chiedi al tuo amministratore di sistema di installarlo.", "External Storage" => "Archiviazione esterna", "Folder name" => "Nome della cartella", "External storage" => "Archiviazione esterna", diff --git a/apps/files_external/l10n/km.php b/apps/files_external/l10n/km.php index 4586f866a7453b122bd719ef403e70c3edeb658f..75879f2300e1652263d539dce01556afd4958d56 100644 --- a/apps/files_external/l10n/km.php +++ b/apps/files_external/l10n/km.php @@ -1,6 +1,7 @@ "ឈ្មោះ​ថត", +"Options" => "ជម្រើស", "Groups" => "ក្រុ", "Users" => "អ្នកប្រើ", "Delete" => "លុប" diff --git a/apps/files_external/l10n/nl.php b/apps/files_external/l10n/nl.php index 0226e454f9a2fd124fbf2fdbdc1c4ca78ad263ce..c055be50f8786758713dfe0965787de6231dd8a2 100644 --- a/apps/files_external/l10n/nl.php +++ b/apps/files_external/l10n/nl.php @@ -6,6 +6,11 @@ $TRANSLATIONS = array( "Please provide a valid Dropbox app key and secret." => "Geef een geldige Dropbox key en secret.", "Error configuring Google Drive storage" => "Fout tijdens het configureren van Google Drive opslag", "Saved" => "Bewaard", +"Note: " => "Let op: ", +" and " => "en", +"Note: The cURL support in PHP is not enabled or installed. Mounting of %s is not possible. Please ask your system administrator to install it." => "Let op: Curl ondersteuning in PHP is niet geactiveerd of geïnstalleerd. Mounten van %s is niet mogelijk. Vraag uw systeembeheerder dit te installeren.", +"Note: The FTP support in PHP is not enabled or installed. Mounting of %s is not possible. Please ask your system administrator to install it." => "Let op: FTP ondersteuning in PHP is niet geactiveerd of geïnstalleerd. Mounten van %s is niet mogelijk. Vraag uw beheerder dit te installeren.", +"Note: \"%s\" is not installed. Mounting of %s is not possible. Please ask your system administrator to install it." => "Let op: \"%s\" is niet geïnstalleerd. Mounten van %s is niet mogelijk. Vraag uw beheerder om dit te installeren.", "External Storage" => "Externe opslag", "Folder name" => "Mapnaam", "External storage" => "Externe opslag", diff --git a/apps/files_external/l10n/pl.php b/apps/files_external/l10n/pl.php index 801a72c9bf9bf5d8cccccc28d77f8b23bec2b42d..246e12b6ac8f209ef596d4527e23b128ece6aa45 100644 --- a/apps/files_external/l10n/pl.php +++ b/apps/files_external/l10n/pl.php @@ -6,6 +6,11 @@ $TRANSLATIONS = array( "Please provide a valid Dropbox app key and secret." => "Proszę podać prawidłowy klucz aplikacji Dropbox i klucz sekretny.", "Error configuring Google Drive storage" => "Wystąpił błąd podczas konfigurowania zasobu Google Drive", "Saved" => "Zapisano", +"Note: " => "Uwaga: ", +" and " => "oraz", +"Note: The cURL support in PHP is not enabled or installed. Mounting of %s is not possible. Please ask your system administrator to install it." => "Uwaga: Wsparcie dla cURL w PHP nie zostało włączone lub zainstalowane. Zamontowanie %s nie jest możliwe. Proszę poproś Twojego administratora o zainstalowanie go.", +"Note: The FTP support in PHP is not enabled or installed. Mounting of %s is not possible. Please ask your system administrator to install it." => "Uwaga: Wsparcie dla FTP w PHP nie zostało włączone lub zainstalowane. Zamontowanie %s nie jest możliwe. Proszę poproś Twojego administratora o zainstalowanie go.", +"Note: \"%s\" is not installed. Mounting of %s is not possible. Please ask your system administrator to install it." => "Uwaga: \"%s\" nie jest zainstalowane. Zamontowanie %s nie jest możliwe. Proszę poproś Twojego administratora o zainstalowanie go.", "External Storage" => "Zewnętrzna zasoby dyskowe", "Folder name" => "Nazwa folderu", "External storage" => "Zewnętrzne zasoby dyskowe", diff --git a/apps/files_external/l10n/pt_BR.php b/apps/files_external/l10n/pt_BR.php index 121c45f45d633191d71b06383320f80b69327080..9a5a39f2358595e58fe65703a3df80b49259cf3c 100644 --- a/apps/files_external/l10n/pt_BR.php +++ b/apps/files_external/l10n/pt_BR.php @@ -6,6 +6,11 @@ $TRANSLATIONS = array( "Please provide a valid Dropbox app key and secret." => "Por favor forneça um app key e secret válido do Dropbox", "Error configuring Google Drive storage" => "Erro ao configurar armazenamento do Google Drive", "Saved" => "Salvo", +"Note: " => "Nota:", +" and " => "e", +"Note: The cURL support in PHP is not enabled or installed. Mounting of %s is not possible. Please ask your system administrator to install it." => "Nota: O suporte cURL do PHP não está habilitado ou instalado. Montagem de %s não é possível. Por favor, solicite ao seu administrador do sistema para instalá-lo.", +"Note: The FTP support in PHP is not enabled or installed. Mounting of %s is not possible. Please ask your system administrator to install it." => "Nota: O suporte FTP no PHP não está habilitado ou instalado. Montagem de %s não é possível. Por favor, solicite ao seu administrador do sistema para instalá-lo.", +"Note: \"%s\" is not installed. Mounting of %s is not possible. Please ask your system administrator to install it." => "Nota: \"%s\" não está instalado. Montagem de %s não é possível. Por favor, solicite ao seu administrador do sistema para instalá-lo.", "External Storage" => "Armazenamento Externo", "Folder name" => "Nome da pasta", "External storage" => "Armazenamento Externo", diff --git a/apps/files_external/l10n/sk_SK.php b/apps/files_external/l10n/sk_SK.php index de32fb5ffb16b08c11b6e4f9351a60ce69ae84d9..96f6241e5fb693e4cb878358e7589d6c26c156eb 100644 --- a/apps/files_external/l10n/sk_SK.php +++ b/apps/files_external/l10n/sk_SK.php @@ -6,17 +6,25 @@ $TRANSLATIONS = array( "Please provide a valid Dropbox app key and secret." => "Zadajte platný kľúč aplikácie a heslo Dropbox", "Error configuring Google Drive storage" => "Chyba pri konfigurácii úložiska Google drive", "Saved" => "Uložené", +"Note: " => "Poznámka: ", +" and " => "a", +"Note: The cURL support in PHP is not enabled or installed. Mounting of %s is not possible. Please ask your system administrator to install it." => "Poznámka: cURL podpora v PHP nie je zapnutá alebo nainštalovaná. Pripojenie %s nie je možné. Požiadajte správcu systému, aby ju nainštaloval.", +"Note: The FTP support in PHP is not enabled or installed. Mounting of %s is not possible. Please ask your system administrator to install it." => "Poznámka: FTP podpora v PHP nie je zapnutá alebo nainštalovaná. Pripojenie %s nie je možné. Požiadajte správcu systému, aby ju nainštaloval.", +"Note: \"%s\" is not installed. Mounting of %s is not possible. Please ask your system administrator to install it." => "Poznámka: \"%s\" nie je nainštalovaná. Pripojenie %s nie je možné. Požiadajte správcu systému, aby ju nainštaloval.", "External Storage" => "Externé úložisko", "Folder name" => "Názov priečinka", "External storage" => "Externé úložisko", "Configuration" => "Nastavenia", "Options" => "Možnosti", +"Available for" => "K dispozícii pre", "Add storage" => "Pridať úložisko", +"No user or group" => "Žiadny používateľ alebo skupina", "All Users" => "Všetci používatelia", "Groups" => "Skupiny", "Users" => "Používatelia", "Delete" => "Zmazať", "Enable User External Storage" => "Povoliť externé úložisko", +"Allow users to mount the following external storage" => "Povoliť používateľom pripojiť tieto externé úložiská", "SSL root certificates" => "Koreňové SSL certifikáty", "Import Root Certificate" => "Importovať koreňový certifikát" ); diff --git a/apps/files_external/l10n/sl.php b/apps/files_external/l10n/sl.php index bb34494a5d7e42effbdf15f1c3f33a18a782bba9..baab272d1d2d9888b74cf1981d0b62c300d26b68 100644 --- a/apps/files_external/l10n/sl.php +++ b/apps/files_external/l10n/sl.php @@ -6,6 +6,11 @@ $TRANSLATIONS = array( "Please provide a valid Dropbox app key and secret." => "Vpisati je treba veljaven ključ programa in kodo za Dropbox", "Error configuring Google Drive storage" => "Napaka nastavljanja shrambe Google Drive", "Saved" => "Shranjeno", +"Note: " => "Opomba: ", +" and " => "in", +"Note: The cURL support in PHP is not enabled or installed. Mounting of %s is not possible. Please ask your system administrator to install it." => "Opomba: Podpora za naslove cURL v PHP ni omogočena, ali pa ni ustrezno nameščenih programov. Priklapljanje %s ni mogoče. Za pomoč pri namestitvi se obrnite na sistemskega skrbnika.", +"Note: The FTP support in PHP is not enabled or installed. Mounting of %s is not possible. Please ask your system administrator to install it." => "Opomba: Podpora za protokol FTP v PHP ni omogočena, ali pa ni ustrezno nameščenih programov. Priklapljanje %s ni mogoče. Za pomoč pri namestitvi se obrnite na sistemskega skrbnika.", +"Note: \"%s\" is not installed. Mounting of %s is not possible. Please ask your system administrator to install it." => "Opomba: Program \"%s\" ni nameščen. Priklapljanje %s ni mogoče. Za pomoč pri namestitvi se obrnite na sistemskega skrbnika.", "External Storage" => "Zunanja podatkovna shramba", "Folder name" => "Ime mape", "External storage" => "Zunanja shramba", diff --git a/apps/files_external/l10n/sv.php b/apps/files_external/l10n/sv.php index ad5e6ad050c06b0f09feaca9ca5c880bf01b73d5..761e8f880c1c121dd745ac3f9ae23c3b1f8d3f35 100644 --- a/apps/files_external/l10n/sv.php +++ b/apps/files_external/l10n/sv.php @@ -6,6 +6,11 @@ $TRANSLATIONS = array( "Please provide a valid Dropbox app key and secret." => "Ange en giltig Dropbox nyckel och hemlighet.", "Error configuring Google Drive storage" => "Fel vid konfigurering av Google Drive", "Saved" => "Sparad", +"Note: " => " OBS: ", +" and " => "och", +"Note: The cURL support in PHP is not enabled or installed. Mounting of %s is not possible. Please ask your system administrator to install it." => " OBS: cURL stöd i PHP inte är aktiverat eller installeras. Montering av %s är inte möjlig. Be din systemadministratör att installera det.", +"Note: The FTP support in PHP is not enabled or installed. Mounting of %s is not possible. Please ask your system administrator to install it." => " OBS: Den FTP-stöd i PHP inte är aktiverat eller installeras. Montering av %s är inte möjlig. Be din systemadministratör att installera det.", +"Note: \"%s\" is not installed. Mounting of %s is not possible. Please ask your system administrator to install it." => " OBS: \"%s\" är inte installerat. Montering av %s är inte möjlig. Be din systemadministratör att installera det.", "External Storage" => "Extern lagring", "Folder name" => "Mappnamn", "External storage" => "Extern lagring", diff --git a/apps/files_external/l10n/tr.php b/apps/files_external/l10n/tr.php index 07bf1a2354c55229ff6164208a8e1c459b7e0d18..3646a47605d5ea0c371e30c8ebdc1bcd45d15c13 100644 --- a/apps/files_external/l10n/tr.php +++ b/apps/files_external/l10n/tr.php @@ -6,6 +6,11 @@ $TRANSLATIONS = array( "Please provide a valid Dropbox app key and secret." => "Lütfen Dropbox app key ve secret temin ediniz", "Error configuring Google Drive storage" => "Google Drive depo yapılandırma hatası", "Saved" => "Kaydedildi", +"Note: " => "Not: ", +" and " => "ve", +"Note: The cURL support in PHP is not enabled or installed. Mounting of %s is not possible. Please ask your system administrator to install it." => "Not: PHP'de cURL desteği etkin veya kurulu değil. %s bağlaması mümkün olmayacak. Lütfen kurulumu için sistem yöneticilerinizle iletişime geçin.", +"Note: The FTP support in PHP is not enabled or installed. Mounting of %s is not possible. Please ask your system administrator to install it." => "Not: PHP'de FTP desteği etkin veya kurulu değil. %s bağlaması mümkün olmayacak. Lütfen kurulumu için sistem yöneticilerinizle iletişime geçin.", +"Note: \"%s\" is not installed. Mounting of %s is not possible. Please ask your system administrator to install it." => "Not: \"%s\" kurulu değil. %s bağlaması mümkün olmayacak. Lütfen kurulumu için sistem yöneticilerinizle iletişime geçin.", "External Storage" => "Harici Depolama", "Folder name" => "Klasör ismi", "External storage" => "Harici depolama", diff --git a/apps/files_external/l10n/zh_HK.php b/apps/files_external/l10n/zh_HK.php index 76e0ed69d27fa72ab2b56f77b254e757b29f262a..7a6c4765f96f6eaf655e8676952cec44840dd67f 100644 --- a/apps/files_external/l10n/zh_HK.php +++ b/apps/files_external/l10n/zh_HK.php @@ -1,5 +1,6 @@ "資料夾名稱", "Groups" => "群組", "Users" => "用戶", "Delete" => "刪除" diff --git a/apps/files_external/l10n/zh_TW.php b/apps/files_external/l10n/zh_TW.php index ac480e405fb6c3264d97f2b48a66d78a29b65b2e..9a99a12aaaf67df747e146665a7ec4de0e6eef8e 100644 --- a/apps/files_external/l10n/zh_TW.php +++ b/apps/files_external/l10n/zh_TW.php @@ -5,6 +5,7 @@ $TRANSLATIONS = array( "Grant access" => "允許存取", "Please provide a valid Dropbox app key and secret." => "請提供有效的 Dropbox app key 和 app secret 。", "Error configuring Google Drive storage" => "設定 Google Drive 儲存時發生錯誤", +"Saved" => "已儲存", "External Storage" => "外部儲存", "Folder name" => "資料夾名稱", "External storage" => "外部儲存", diff --git a/apps/files_external/lib/config.php b/apps/files_external/lib/config.php index 613f0b2609ce511f4d3b8cc2a5503f2a4a109197..99eca2f38cfc8a0a8868a6434eadb4c28b8d5b4d 100755 --- a/apps/files_external/lib/config.php +++ b/apps/files_external/lib/config.php @@ -206,6 +206,12 @@ class OC_Mount_Config { */ public static function getPersonalBackends() { + // Check whether the user has permissions to add personal storage backends + // return an empty array if this is not the case + if(OCP\Config::getAppValue('files_external', 'allow_user_mounting', 'yes') !== 'yes') { + return array(); + } + $backEnds = self::getBackends(); // Remove local storage and other disabled storages @@ -367,8 +373,8 @@ class OC_Mount_Config { $isPersonal = false) { $backends = self::getBackends(); $mountPoint = OC\Files\Filesystem::normalizePath($mountPoint); - if ($mountPoint === '' || $mountPoint === '/' || $mountPoint == '/Shared') { - // can't mount at root or "Shared" folder + if ($mountPoint === '' || $mountPoint === '/') { + // can't mount at root folder return false; } diff --git a/apps/files_external/tests/mountconfig.php b/apps/files_external/tests/mountconfig.php index c89874c94d59ce32cd048b330804fae9875ab830..1921ec76af35e53759c57d66b5eb4b965de3c07f 100644 --- a/apps/files_external/tests/mountconfig.php +++ b/apps/files_external/tests/mountconfig.php @@ -128,9 +128,6 @@ class Test_Mount_Config extends \PHPUnit_Framework_TestCase { $isPersonal = false; $this->assertFalse(OC_Mount_Config::addMountPoint('', $storageClass, array(), $mountType, $applicable, $isPersonal)); $this->assertFalse(OC_Mount_Config::addMountPoint('/', $storageClass, array(), $mountType, $applicable, $isPersonal)); - $this->assertFalse(OC_Mount_Config::addMountPoint('Shared', $storageClass, array(), $mountType, $applicable, $isPersonal)); - $this->assertFalse(OC_Mount_Config::addMountPoint('/Shared', $storageClass, array(), $mountType, $applicable, $isPersonal)); - } /** @@ -488,7 +485,7 @@ class Test_Mount_Config extends \PHPUnit_Framework_TestCase { 'root' => 'someroot' ); - // add mount point as "test" user + // add mount point as "test" user $this->assertTrue( OC_Mount_Config::addMountPoint( '/ext', diff --git a/apps/files_sharing/appinfo/app.php b/apps/files_sharing/appinfo/app.php index 217bc005faf3d9fb61caa336e4d7b330084cfd4c..0ef34578117434b00b36cab65ec056fd9478f939 100644 --- a/apps/files_sharing/appinfo/app.php +++ b/apps/files_sharing/appinfo/app.php @@ -17,6 +17,4 @@ OCP\Util::addScript('files_sharing', 'share'); \OC_Hook::connect('OC_Filesystem', 'post_delete', '\OC\Files\Cache\Shared_Updater', 'postDeleteHook'); \OC_Hook::connect('OC_Filesystem', 'delete', '\OC\Files\Cache\Shared_Updater', 'deleteHook'); \OC_Hook::connect('OC_Filesystem', 'post_rename', '\OC\Files\Cache\Shared_Updater', 'renameHook'); -\OC_Hook::connect('OCP\Share', 'post_shared', '\OC\Files\Cache\Shared_Updater', 'shareHook'); -\OC_Hook::connect('OCP\Share', 'pre_unshare', '\OC\Files\Cache\Shared_Updater', 'shareHook'); \OC_Hook::connect('OC_Appconfig', 'post_set_value', '\OCA\Files\Share\Maintainer', 'configChangeHook'); diff --git a/apps/files_sharing/appinfo/update.php b/apps/files_sharing/appinfo/update.php index ab32108ea25f9668ee2ee6ffec704d3a7b5a2ced..bc8cda42313cb929c3eca379a005f3ca91681f97 100644 --- a/apps/files_sharing/appinfo/update.php +++ b/apps/files_sharing/appinfo/update.php @@ -1,76 +1,77 @@ execute(); - $groupShares = array(); - //we need to set up user backends, otherwise creating the shares will fail with "because user does not exist" + $view = new \OC\Files\View('/'); + $users = array(); + $shares = array(); + //we need to set up user backends OC_User::useBackend(new OC_User_Database()); OC_Group::useBackend(new OC_Group_Database()); OC_App::loadApps(array('authentication')); - $rootView = new \OC\Files\View(''); + //we need to set up user backends, otherwise creating the shares will fail with "because user does not exist" while ($row = $result->fetchRow()) { - $meta = $rootView->getFileInfo($$row['source']); - $itemSource = $meta['fileid']; - if ($itemSource != -1) { - $file = $meta; - if ($file['mimetype'] == 'httpd/unix-directory') { - $itemType = 'folder'; - } else { - $itemType = 'file'; - } - if ($row['permissions'] == 0) { - $permissions = OCP\PERMISSION_READ | OCP\PERMISSION_SHARE; - } else { - $permissions = OCP\PERMISSION_READ | OCP\PERMISSION_UPDATE | OCP\PERMISSION_SHARE; - if ($itemType == 'folder') { - $permissions |= OCP\PERMISSION_CREATE; - } - } - $pos = strrpos($row['uid_shared_with'], '@'); - if ($pos !== false && OC_Group::groupExists(substr($row['uid_shared_with'], $pos + 1))) { - $shareType = OCP\Share::SHARE_TYPE_GROUP; - $shareWith = substr($row['uid_shared_with'], 0, $pos); - if (isset($groupShares[$shareWith][$itemSource])) { - continue; - } else { - $groupShares[$shareWith][$itemSource] = true; + //collect all user shares + if ((int)$row['share_type'] === 0 && ($row['item_type'] === 'file' || $row['item_type'] === 'folder')) { + $users[] = $row['share_with']; + $shares[$row['id']] = $row['file_target']; + } else if ((int)$row['share_type'] === 1 && ($row['item_type'] === 'file' || $row['item_type'] === 'folder')) { + //collect all group shares + $users = array_merge($users, \OC_group::usersInGroup($row['share_with'])); + $shares[$row['id']] = $row['file_target']; + } else if ((int)$row['share_type'] === 2) { + $shares[$row['id']] = $row['file_target']; + } + } + + $unique_users = array_unique($users); + + if (!empty($unique_users) && !empty($shares)) { + + // create folder Shared for each user + + if ($mkdirs) { + foreach ($unique_users as $user) { + \OC\Files\Filesystem::initMountPoints($user); + if (!$view->file_exists('/' . $user . '/files/Shared')) { + $view->mkdir('/' . $user . '/files/Shared'); } - } else if ($row['uid_shared_with'] == 'public') { - $shareType = OCP\Share::SHARE_TYPE_LINK; - $shareWith = null; - } else { - $shareType = OCP\Share::SHARE_TYPE_USER; - $shareWith = $row['uid_shared_with']; } - OCP\JSON::checkUserExists($row['uid_owner']); - OC_User::setUserId($row['uid_owner']); - //we need to setup the filesystem for the user, otherwise OC_FileSystem::getRoot will fail and break - OC_Util::setupFS($row['uid_owner']); - try { - OCP\Share::shareItem($itemType, $itemSource, $shareType, $shareWith, $permissions); - } - catch (Exception $e) { - $update_error = true; - OCP\Util::writeLog('files_sharing', - 'Upgrade Routine: Skipping sharing "'.$row['source'].'" to "'.$shareWith - .'" (error is "'.$e->getMessage().'")', - OCP\Util::WARN); + } + + $chunkedShareList = array_chunk($shares, $chunkSize, true); + + foreach ($chunkedShareList as $subList) { + + $statement = "UPDATE `*PREFIX*share` SET `file_target` = CASE `id` "; + //update share table + $ids = implode(',', array_keys($subList)); + foreach ($subList as $id => $target) { + $statement .= "WHEN " . $id . " THEN '/Shared" . $target . "' "; } - OC_Util::tearDownFS(); + $statement .= ' END WHERE `id` IN (' . $ids . ')'; + + $query = OCP\DB::prepare($statement); + + $query->execute(array()); } - } - OC_User::setUserId(null); - if ($update_error) { - OCP\Util::writeLog('files_sharing', 'There were some problems upgrading the sharing of files', OCP\Util::ERROR); - } - // NOTE: Let's drop the table after more testing -// $query = OCP\DB::prepare('DROP TABLE `*PREFIX*sharing`'); -// $query->execute(); -} -// clean up oc_share table from files which are no longer exists -if (version_compare($installedVersion, '0.3.5.6', '<')) { - \OC\Files\Cache\Shared_Updater::fixBrokenSharesOnAppUpdate(); + } } diff --git a/apps/files_sharing/appinfo/version b/apps/files_sharing/appinfo/version index 8f91d33378ed1aa457f6edb4b629f3844829658f..bd73f47072b1fe4b9914ec14a7f6d47fcc8f816a 100644 --- a/apps/files_sharing/appinfo/version +++ b/apps/files_sharing/appinfo/version @@ -1 +1 @@ -0.3.5.6 +0.4 diff --git a/apps/files_sharing/css/public.css b/apps/files_sharing/css/public.css index f0b9b04491fdbb0591f923a64aa79712506313a8..efd8d4950d83492aef439e09f8afbbc999be74ac 100644 --- a/apps/files_sharing/css/public.css +++ b/apps/files_sharing/css/public.css @@ -1,32 +1,3 @@ -body { - height: auto; -} - -#header { - background-color: #1d2d44; - height:32px; - left:0; - line-height:32px; - position:fixed; - right:0; - top:0; - z-index:100; - padding:7px; -} - -.header-right { - padding: 0; - height: 32px; -} - -#details { - color:#fff; - -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"; - filter: alpha(opacity=50); - opacity: .5; - padding-right: 5px; -} - #controls { left: 0; } @@ -99,13 +70,26 @@ thead { margin: 0; } + .directDownload, .directLink { margin-bottom: 20px; } + +/* keep long file names in one line to not overflow download button on mobile */ +.directDownload #download { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + max-width: 90%; + display: inline-block; + margin-left: auto; + margin-right: auto; +} .directDownload .button img { vertical-align: text-bottom; } + .directLink label { font-weight: normal; -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"; @@ -115,4 +99,5 @@ thead { .directLink input { margin-left: 5px; width: 300px; + max-width: 90%; } diff --git a/apps/files_sharing/js/public.js b/apps/files_sharing/js/public.js index 9ce8985f1fd28e381e9a45ae515c0648a714d302..ae2412f6a3b6fd027cfdc840efa8405705a5a717 100644 --- a/apps/files_sharing/js/public.js +++ b/apps/files_sharing/js/public.js @@ -62,11 +62,17 @@ $(document).ready(function() { var file_upload_start = $('#file_upload_start'); file_upload_start.on('fileuploadadd', function(e, data) { + var fileDirectory = ''; + if(typeof data.files[0].relativePath !== 'undefined') { + fileDirectory = data.files[0].relativePath; + } + // Add custom data to the upload handler data.formData = { requesttoken: $('#publicUploadRequestToken').val(), dirToken: $('#dirToken').val(), - subdir: $('input#dir').val() + subdir: $('input#dir').val(), + file_directory: fileDirectory }; }); diff --git a/apps/files_sharing/l10n/bg_BG.php b/apps/files_sharing/l10n/bg_BG.php index 4fe5ba6b16fb683f0eb46e0e0c853508331fac5c..37e2ca33370639c2592f8fd55944050054706bbf 100644 --- a/apps/files_sharing/l10n/bg_BG.php +++ b/apps/files_sharing/l10n/bg_BG.php @@ -1,5 +1,6 @@ "Парола" +"Password" => "Парола", +"shared by %s" => "споделено от %s" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/apps/files_sharing/lib/api.php b/apps/files_sharing/lib/api.php index de3c1cd2630a3d9f0dd932eab28b57e26732b554..438d3cc4ba3702e23cc698b201b2a64275ff2866 100644 --- a/apps/files_sharing/lib/api.php +++ b/apps/files_sharing/lib/api.php @@ -184,7 +184,6 @@ class Api { $receivedFrom = \OCP\Share::getItemSharedWithBySource($itemType, $file['fileid']); reset($share); $key = key($share); - $share[$key]['path'] = self::correctPath($share[$key]['path'], $path); if ($receivedFrom) { $share[$key]['received_from'] = $receivedFrom['uid_owner']; $share[$key]['received_from_displayname'] = \OCP\User::getDisplayName($receivedFrom['uid_owner']); @@ -531,15 +530,4 @@ class Api { } - /** - * @brief make sure that the path has the correct root - * - * @param string $path path returned from the share API - * @param string $folder current root folder - * @return string the correct path - */ - protected static function correctPath($path, $folder) { - return \OC_Filesystem::normalizePath('/' . $folder . '/' . basename($path)); - } - } diff --git a/apps/files_sharing/lib/cache.php b/apps/files_sharing/lib/cache.php index eeb62c3cce2c0c6f4348010cde2130163e1fcd94..8c680e9c9774516202a75f78c5d619923cf4793b 100644 --- a/apps/files_sharing/lib/cache.php +++ b/apps/files_sharing/lib/cache.php @@ -2,8 +2,9 @@ /** * ownCloud * - * @author Michael Gapczynski - * @copyright 2012 Michael Gapczynski mtgap@owncloud.com + * @author Bjoern Schiessle, Michael Gapczynski + * @copyright 2012 Michael Gapczynski + * 2014 Bjoern Schiessle * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -46,12 +47,15 @@ class Shared_Cache extends Cache { * @return \OC\Files\Cache\Cache */ private function getSourceCache($target) { - $source = \OC_Share_Backend_File::getSource($target); + if ($target === false || $target === $this->storage->getMountPoint()) { + $target = ''; + } + $source = \OC_Share_Backend_File::getSource($target, $this->storage->getMountPoint(), $this->storage->getItemType()); if (isset($source['path']) && isset($source['fileOwner'])) { \OC\Files\Filesystem::initMountPoints($source['fileOwner']); - $mount = \OC\Files\Filesystem::getMountByNumericId($source['storage']); - if (is_array($mount)) { - $fullPath = $mount[key($mount)]->getMountPoint() . $source['path']; + $mounts = \OC\Files\Filesystem::getMountByNumericId($source['storage']); + if (is_array($mounts) and count($mounts)) { + $fullPath = $mounts[0]->getMountPoint() . $source['path']; list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($fullPath); if ($storage) { $this->files[$target] = $internalPath; @@ -80,23 +84,26 @@ class Shared_Cache extends Cache { * @return array */ public function get($file) { - if ($file == '') { - $data = \OCP\Share::getItemsSharedWith('file', \OC_Share_Backend_File::FORMAT_FILE_APP_ROOT); - $etag = \OCP\Config::getUserValue(\OCP\User::getUser(), 'files_sharing', 'etag'); - if (!isset($etag)) { - $etag = $this->storage->getETag(''); - \OCP\Config::setUserValue(\OCP\User::getUser(), 'files_sharing', 'etag', $etag); - } - $data['etag'] = $etag; - return $data; - } else if (is_string($file)) { + if (is_string($file)) { if ($cache = $this->getSourceCache($file)) { - return $cache->get($this->files[$file]); + $data = $cache->get($this->files[$file]); + $data['displayname_owner'] = \OC_User::getDisplayName($this->storage->getSharedFrom()); + $data['path'] = $file; + if ($file === '') { + $data['is_share_mount_point'] = true; + } + $data['uid_owner'] = $this->storage->getOwner($file); + return $data; } } else { + // if we are at the root of the mount point we want to return the + // cache information for the source item + if (!is_int($file) || $file === 0) { + $file = $this->storage->getSourceId(); + } $query = \OC_DB::prepare( 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`,' - . ' `size`, `mtime`, `encrypted`, `unencrypted_size`' + . ' `size`, `mtime`, `encrypted`, `unencrypted_size`, `storage_mtime`, `etag`' . ' FROM `*PREFIX*filecache` WHERE `fileid` = ?'); $result = $query->execute(array($file)); $data = $result->fetchRow(); @@ -115,6 +122,10 @@ class Shared_Cache extends Cache { } else { $data['size'] = (int)$data['size']; } + if (!is_int($file) || $file === 0) { + $data['path'] = ''; + $data['is_share_mount_point'] = true; + } return $data; } return false; @@ -127,28 +138,26 @@ class Shared_Cache extends Cache { * @return array */ public function getFolderContents($folder) { - if ($folder == '') { - $files = \OCP\Share::getItemsSharedWith('file', \OC_Share_Backend_File::FORMAT_GET_FOLDER_CONTENTS); - foreach ($files as &$file) { - $file['mimetype'] = $this->getMimetype($file['mimetype']); - $file['mimepart'] = $this->getMimetype($file['mimepart']); - $file['usersPath'] = 'files/Shared/' . ltrim($file['path'], '/'); - } - return $files; - } else { - $cache = $this->getSourceCache($folder); - if ($cache) { - $parent = $this->storage->getFile($folder); - $sourceFolderContent = $cache->getFolderContents($this->files[$folder]); - foreach ($sourceFolderContent as $key => $c) { - $sourceFolderContent[$key]['usersPath'] = 'files/Shared/' . $folder . '/' . $c['name']; - $sourceFolderContent[$key]['uid_owner'] = $parent['uid_owner']; - $sourceFolderContent[$key]['displayname_owner'] = $parent['uid_owner']; - } - return $sourceFolderContent; + if ($folder === false) { + $folder = ''; + } + + $dir = ($folder !== '') ? $folder . '/' : ''; + + $cache = $this->getSourceCache($folder); + if ($cache) { + $parent = $this->storage->getFile($folder); + $sourceFolderContent = $cache->getFolderContents($this->files[$folder]); + foreach ($sourceFolderContent as $key => $c) { + $sourceFolderContent[$key]['path'] = $dir . $c['name']; + $sourceFolderContent[$key]['uid_owner'] = $parent['uid_owner']; + $sourceFolderContent[$key]['displayname_owner'] = $parent['uid_owner']; } + + return $sourceFolderContent; } + return false; } @@ -161,9 +170,8 @@ class Shared_Cache extends Cache { * @return int file id */ public function put($file, array $data) { - if ($file === '' && isset($data['etag'])) { - return \OCP\Config::setUserValue(\OCP\User::getUser(), 'files_sharing', 'etag', $data['etag']); - } else if ($cache = $this->getSourceCache($file)) { + $file = ($file === false) ? '' : $file; + if ($cache = $this->getSourceCache($file)) { return $cache->put($this->files[$file], $data); } return false; @@ -176,7 +184,11 @@ class Shared_Cache extends Cache { * @return int */ public function getId($file) { - if ($cache = $this->getSourceCache($file)) { + if ($file === false) { + return $this->storage->getSourceId(); + } + $cache = $this->getSourceCache($file); + if ($cache) { return $cache->getId($this->files[$file]); } return -1; @@ -201,6 +213,7 @@ class Shared_Cache extends Cache { * @param string $file */ public function remove($file) { + $file = ($file === false) ? '' : $file; if ($cache = $this->getSourceCache($file)) { $cache->remove($this->files[$file]); } @@ -214,7 +227,7 @@ class Shared_Cache extends Cache { */ public function move($source, $target) { if ($cache = $this->getSourceCache($source)) { - $file = \OC_Share_Backend_File::getSource($target); + $file = \OC_Share_Backend_File::getSource($target, $this->storage->getMountPoint(), $this->storage->getItemType()); if ($file && isset($file['path'])) { $cache->move($this->files[$source], $file['path']); } @@ -284,15 +297,17 @@ class Shared_Cache extends Cache { $files = $this->getFolderContents($dir); // no results? if (!$files) { + // maybe it's a single shared file + $file = $this->get(''); + if (($mimepart && $file['mimepart'] === $mimepart) || ($mimetype && $file['mimetype'] === $mimetype)) { + $result[] = $file; + } continue; } foreach ($files as $file) { if ($file['mimetype'] === 'httpd/unix-directory') { $exploreDirs[] = ltrim($dir . '/' . $file['name'], '/'); } else if (($mimepart && $file['mimepart'] === $mimepart) || ($mimetype && $file['mimetype'] === $mimetype)) { - // usersPath not reliable - //$file['path'] = $file['usersPath']; - $file['path'] = ltrim($dir . '/' . $file['name'], '/'); $result[] = $file; } } @@ -359,6 +374,7 @@ class Shared_Cache extends Cache { * @return int */ public function calculateFolderSize($path, $entry = null) { + $path = ($path === false) ? '' : $path; if ($cache = $this->getSourceCache($path)) { return $cache->calculateFolderSize($this->files[$path]); } @@ -401,7 +417,7 @@ class Shared_Cache extends Cache { } /** - * get the path of a file on this storage by it's id + * get the path of a file on this storage relative to the mount point by it's id * * @param int $id * @param string $pathEnd (optional) used internally for recursive calls @@ -409,8 +425,9 @@ class Shared_Cache extends Cache { */ public function getPathById($id, $pathEnd = '') { // direct shares are easy - if ($path = $this->getShareById($id)) { - return $path . $pathEnd; + $path = $this->getShareById($id); + if (is_string($path)) { + return ltrim($pathEnd, '/'); } else { // if the item is a direct share we try and get the path of the parent and append the name of the item to it list($parent, $name) = $this->getParentInfo($id); diff --git a/apps/files_sharing/lib/helper.php b/apps/files_sharing/lib/helper.php index b602fe3599d0888c27ef589f52d774f1c904a15b..cc1f7d9ffdf5bd1d55b48d65ee74f98f337b6270 100644 --- a/apps/files_sharing/lib/helper.php +++ b/apps/files_sharing/lib/helper.php @@ -2,6 +2,9 @@ namespace OCA\Files_Sharing; +use OC_Config; +use PasswordHash; + class Helper { /** @@ -26,9 +29,6 @@ class Helper { exit; } - $type = $linkItem['item_type']; - $fileSource = $linkItem['file_source']; - $shareOwner = $linkItem['uid_owner']; $rootLinkItem = \OCP\Share::resolveReShare($linkItem); $path = null; if (isset($rootLinkItem['uid_owner'])) { @@ -61,7 +61,6 @@ class Helper { } $basePath = $path; - $rootName = basename($path); if ($relativePath !== null && \OC\Files\Filesystem::isReadable($basePath . $relativePath)) { $path .= \OC\Files\Filesystem::normalizePath($relativePath); @@ -111,4 +110,70 @@ class Helper { } return true; } + + public static function getSharesFromItem($target) { + $result = array(); + $owner = \OC\Files\Filesystem::getOwner($target); + \OC\Files\Filesystem::initMountPoints($owner); + $info = \OC\Files\Filesystem::getFileInfo($target); + $ownerView = new \OC\Files\View('/'.$owner.'/files'); + if ( $owner != \OCP\User::getUser() ) { + $path = $ownerView->getPath($info['fileid']); + } else { + $path = $target; + } + + + $ids = array(); + while ($path !== '' && $path !== '.' && $path !== '/') { + $info = $ownerView->getFileInfo($path); + $ids[] = $info['fileid']; + $path = dirname($path); + } + + if (!empty($ids)) { + + $idList = array_chunk($ids, 99, true); + + foreach ($idList as $subList) { + $statement = "SELECT `share_with`, `share_type`, `file_target` FROM `*PREFIX*share` WHERE `file_source` IN (" . implode(',', $subList) . ") AND `share_type` IN (0, 1, 2)"; + $query = \OCP\DB::prepare($statement); + $r = $query->execute(); + $result = array_merge($result, $r->fetchAll()); + } + } + + return $result; + } + + public static function getUidAndFilename($filename) { + $uid = \OC\Files\Filesystem::getOwner($filename); + \OC\Files\Filesystem::initMountPoints($uid); + if ( $uid != \OCP\User::getUser() ) { + $info = \OC\Files\Filesystem::getFileInfo($filename); + $ownerView = new \OC\Files\View('/'.$uid.'/files'); + $filename = $ownerView->getPath($info['fileid']); + } + return array($uid, $filename); + } + + /** + * @brief Format a path to be relative to the /user/files/ directory + * @param string $path the absolute path + * @return string e.g. turns '/admin/files/test.txt' into 'test.txt' + */ + public static function stripUserFilesPath($path) { + $trimmed = ltrim($path, '/'); + $split = explode('/', $trimmed); + + // it is not a file relative to data/user/files + if (count($split) < 3 || $split[1] !== 'files') { + return false; + } + + $sliced = array_slice($split, 2); + $relPath = implode('/', $sliced); + + return $relPath; + } } diff --git a/apps/files_sharing/lib/permissions.php b/apps/files_sharing/lib/permissions.php index 31b7ac361ae8f5bfe6643040cf806a6f77185b10..c3ad63e2fd26b6ab8725d3b9619151ea85beea87 100644 --- a/apps/files_sharing/lib/permissions.php +++ b/apps/files_sharing/lib/permissions.php @@ -31,7 +31,9 @@ class Shared_Permissions extends Permissions { */ public function get($fileId, $user) { if ($fileId == -1) { - return \OCP\PERMISSION_READ; + // if we ask for the mount point return -1 so that we can get the correct + // permissions by the path, with the root fileId we have no idea which share is meant + return -1; } $source = \OCP\Share::getItemSharedWithBySource('file', $fileId, \OC_Share_Backend_File::FORMAT_SHARED_STORAGE, null, true); diff --git a/apps/files_sharing/lib/share/file.php b/apps/files_sharing/lib/share/file.php index 5e00050fe1e9f5aa79584317c3806f8ef3ccb887..c0c9e0c107eda3fd543af9cf3a1ba41a65c042ee 100644 --- a/apps/files_sharing/lib/share/file.php +++ b/apps/files_sharing/lib/share/file.php @@ -2,8 +2,9 @@ /** * ownCloud * -* @author Michael Gapczynski -* @copyright 2012 Michael Gapczynski mtgap@owncloud.com +* @author Bjoern Schiessle, Michael Gapczynski +* @copyright 2012 Michael Gapczynski + * 2014 Bjoern Schiessle * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -27,6 +28,7 @@ class OC_Share_Backend_File implements OCP\Share_Backend_File_Dependent { const FORMAT_OPENDIR = 3; const FORMAT_GET_ALL = 4; const FORMAT_PERMISSIONS = 5; + const FORMAT_TARGET_NAMES = 6; private $path; @@ -49,24 +51,37 @@ class OC_Share_Backend_File implements OCP\Share_Backend_File_Dependent { return false; } + /** + * @brief create unique target + * @param string $filePath + * @param string $shareWith + * @param string $exclude + * @return string + */ public function generateTarget($filePath, $shareWith, $exclude = null) { $target = '/'.basename($filePath); - if (isset($exclude)) { - if ($pos = strrpos($target, '.')) { - $name = substr($target, 0, $pos); - $ext = substr($target, $pos); - } else { - $name = $target; - $ext = ''; - } - $i = 2; - $append = ''; - while (in_array($name.$append.$ext, $exclude)) { - $append = ' ('.$i.')'; - $i++; - } - $target = $name.$append.$ext; + + // for group shares we return the target right away + if ($shareWith === false) { + return $target; + } + + \OC\Files\Filesystem::initMountPoints($shareWith); + $view = new \OC\Files\View('/' . $shareWith . '/files'); + $excludeList = \OCP\Share::getItemsSharedWithUser('file', $shareWith, self::FORMAT_TARGET_NAMES); + if (is_array($exclude)) { + $excludeList = array_merge($excludeList, $exclude); + } + + $pathinfo = pathinfo($target); + $ext = (isset($pathinfo['extension'])) ? '.'.$pathinfo['extension'] : ''; + $name = $pathinfo['filename']; + $i = 2; + while ($view->file_exists($target) || in_array($target, $excludeList)) { + $target = '/' . $name . ' ('.$i.')' . $ext; + $i++; } + return $target; } @@ -78,7 +93,7 @@ class OC_Share_Backend_File implements OCP\Share_Backend_File_Dependent { 'path' => $items[key($items)]['path'], 'storage' => $items[key($items)]['storage'], 'permissions' => $items[key($items)]['permissions'], - 'uid_owner' => $items[key($items)]['uid_owner'] + 'uid_owner' => $items[key($items)]['uid_owner'], ); } else if ($format == self::FORMAT_GET_FOLDER_CONTENTS) { $files = array(); @@ -108,22 +123,6 @@ class OC_Share_Backend_File implements OCP\Share_Backend_File_Dependent { $files[] = $file; } return $files; - } else if ($format == self::FORMAT_FILE_APP_ROOT) { - $mtime = 0; - $size = 0; - foreach ($items as $item) { - if ($item['mtime'] > $mtime) { - $mtime = $item['mtime']; - } - $size += (int)$item['size']; - } - return array( - 'fileid' => -1, - 'name' => 'Shared', - 'mtime' => $mtime, - 'mimetype' => 'httpd/unix-directory', - 'size' => $size - ); } else if ($format == self::FORMAT_OPENDIR) { $files = array(); foreach ($items as $item) { @@ -142,46 +141,60 @@ class OC_Share_Backend_File implements OCP\Share_Backend_File_Dependent { $filePermissions[$item['file_source']] = $item['permissions']; } return $filePermissions; + } else if ($format === self::FORMAT_TARGET_NAMES) { + $targets = array(); + foreach ($items as $item) { + $targets[] = $item['file_target']; + } + return $targets; } return array(); } - public static function getSource($target) { - if ($target == '') { - return false; + /** + * @brief resolve reshares to return the correct source item + * @param array $source + * @return array source item + */ + protected static function resolveReshares($source) { + if (isset($source['parent'])) { + $parent = $source['parent']; + while (isset($parent)) { + $query = \OC_DB::prepare('SELECT `parent`, `uid_owner` FROM `*PREFIX*share` WHERE `id` = ?', 1); + $item = $query->execute(array($parent))->fetchRow(); + if (isset($item['parent'])) { + $parent = $item['parent']; + } else { + $fileOwner = $item['uid_owner']; + break; + } + } + } else { + $fileOwner = $source['uid_owner']; } - $target = '/'.$target; - $target = rtrim($target, '/'); - $pos = strpos($target, '/', 1); - // Get shared folder name - if ($pos !== false) { - $folder = substr($target, 0, $pos); - $source = \OCP\Share::getItemSharedWith('folder', $folder, \OC_Share_Backend_File::FORMAT_SHARED_STORAGE); - if ($source) { - $source['path'] = $source['path'].substr($target, strlen($folder)); + if (isset($fileOwner)) { + $source['fileOwner'] = $fileOwner; + } else { + \OCP\Util::writeLog('files_sharing', "No owner found for reshare", \OCP\Util::ERROR); + } + + return $source; + } + + public static function getSource($target, $mountPoint, $itemType) { + + if ($itemType === 'folder') { + $source = \OCP\Share::getItemSharedWith('folder', $mountPoint, \OC_Share_Backend_File::FORMAT_SHARED_STORAGE); + if ($source && $target !== '') { + $source['path'] = $source['path'].'/'.$target; } } else { - $source = \OCP\Share::getItemSharedWith('file', $target, \OC_Share_Backend_File::FORMAT_SHARED_STORAGE); + $source = \OCP\Share::getItemSharedWith('file', $mountPoint, \OC_Share_Backend_File::FORMAT_SHARED_STORAGE); } if ($source) { - if (isset($source['parent'])) { - $parent = $source['parent']; - while (isset($parent)) { - $query = \OC_DB::prepare('SELECT `parent`, `uid_owner` FROM `*PREFIX*share` WHERE `id` = ?', 1); - $item = $query->execute(array($parent))->fetchRow(); - if (isset($item['parent'])) { - $parent = $item['parent']; - } else { - $fileOwner = $item['uid_owner']; - break; - } - } - } else { - $fileOwner = $source['uid_owner']; - } - $source['fileOwner'] = $fileOwner; - return $source; + return self::resolveReshares($source); } + \OCP\Util::writeLog('files_sharing', 'File source not found for: '.$target, \OCP\Util::DEBUG); return false; } diff --git a/apps/files_sharing/lib/sharedstorage.php b/apps/files_sharing/lib/sharedstorage.php index b922654e5ec2db2e2e8f711eed54d68e843e622d..5e478d5ead8557435df085bcb7632c887a3d4df4 100644 --- a/apps/files_sharing/lib/sharedstorage.php +++ b/apps/files_sharing/lib/sharedstorage.php @@ -2,8 +2,9 @@ /** * ownCloud * - * @author Michael Gapczynski - * @copyright 2011 Michael Gapczynski mtgap@owncloud.com + * @author Bjoern Schiessle, Michael Gapczynski + * @copyright 2011 Michael Gapczynski + * 2014 Bjoern Schiessle * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -27,15 +28,27 @@ namespace OC\Files\Storage; */ class Shared extends \OC\Files\Storage\Common { - private $sharedFolder; + private $share; // the shared resource private $files = array(); public function __construct($arguments) { - $this->sharedFolder = $arguments['sharedFolder']; + $this->share = $arguments['share']; } + /** + * @breif get id of the mount point + * @return string + */ public function getId() { - return 'shared::' . $this->sharedFolder; + return 'shared::' . $this->getMountPoint(); + } + + /** + * @breif get file cache of the shared item source + * @return string + */ + public function getSourceId() { + return $this->share['file_source']; } /** @@ -48,14 +61,14 @@ class Shared extends \OC\Files\Storage\Common { if (!isset($this->files[$target])) { // Check for partial files if (pathinfo($target, PATHINFO_EXTENSION) === 'part') { - $source = \OC_Share_Backend_File::getSource(substr($target, 0, -5)); + $source = \OC_Share_Backend_File::getSource(substr($target, 0, -5), $this->getMountPoint(), $this->getItemType()); if ($source) { $source['path'] .= '.part'; // All partial files have delete permission $source['permissions'] |= \OCP\PERMISSION_DELETE; } } else { - $source = \OC_Share_Backend_File::getSource($target); + $source = \OC_Share_Backend_File::getSource($target, $this->getMountPoint(), $this->getItemType()); } $this->files[$target] = $source; } @@ -117,25 +130,15 @@ class Shared extends \OC\Files\Storage\Common { } public function opendir($path) { - if ($path == '' || $path == '/') { - $files = \OCP\Share::getItemsSharedWith('file', \OC_Share_Backend_Folder::FORMAT_OPENDIR); - \OC\Files\Stream\Dir::register('shared', $files); - return opendir('fakedir://shared'); - } else if ($source = $this->getSourcePath($path)) { - list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); - return $storage->opendir($internalPath); - } - return false; + $source = $this->getSourcePath($path); + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); + return $storage->opendir($internalPath); } public function is_dir($path) { - if ($path == '' || $path == '/') { - return true; - } else if ($source = $this->getSourcePath($path)) { - list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); - return $storage->is_dir($internalPath); - } - return false; + $source = $this->getSourcePath($path); + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); + return $storage->is_dir($internalPath); } public function is_file($path) { @@ -180,7 +183,7 @@ class Shared extends \OC\Files\Storage\Common { public function isCreatable($path) { if ($path == '') { - return false; + $path = $this->getMountPoint(); } return ($this->getPermissions($path) & \OCP\PERMISSION_CREATE); } @@ -191,21 +194,21 @@ class Shared extends \OC\Files\Storage\Common { public function isUpdatable($path) { if ($path == '') { - return false; + $path = $this->getMountPoint(); } return ($this->getPermissions($path) & \OCP\PERMISSION_UPDATE); } public function isDeletable($path) { if ($path == '') { - return true; + $path = $this->getMountPoint(); } return ($this->getPermissions($path) & \OCP\PERMISSION_DELETE); } public function isSharable($path) { if ($path == '') { - return false; + $path = $this->getMountPoint(); } return ($this->getPermissions($path) & \OCP\PERMISSION_SHARE); } @@ -221,32 +224,16 @@ class Shared extends \OC\Files\Storage\Common { } public function filemtime($path) { - if ($path == '' || $path == '/') { - $mtime = 0; - $dh = $this->opendir($path); - if (is_resource($dh)) { - while (($filename = readdir($dh)) !== false) { - $tempmtime = $this->filemtime($filename); - if ($tempmtime > $mtime) { - $mtime = $tempmtime; - } - } - } - return $mtime; - } else { - $source = $this->getSourcePath($path); - if ($source) { - list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); - return $storage->filemtime($internalPath); - } - } + $source = $this->getSourcePath($path); + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); + return $storage->filemtime($internalPath); } public function file_get_contents($path) { $source = $this->getSourcePath($path); if ($source) { $info = array( - 'target' => $this->sharedFolder . $path, + 'target' => $this->getMountPoint() . $path, 'source' => $source, ); \OCP\Util::emitHook('\OC\Files\Storage\Shared', 'file_get_contents', $info); @@ -264,7 +251,7 @@ class Shared extends \OC\Files\Storage\Common { return false; } $info = array( - 'target' => $this->sharedFolder . $path, + 'target' => $this->getMountPoint() . '/' . $path, 'source' => $source, ); \OCP\Util::emitHook('\OC\Files\Storage\Shared', 'file_put_contents', $info); @@ -277,6 +264,7 @@ class Shared extends \OC\Files\Storage\Common { public function unlink($path) { // Delete the file if DELETE permission is granted + $path = ($path === false) ? '' : $path; if ($source = $this->getSourcePath($path)) { if ($this->isDeletable($path)) { list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); @@ -286,23 +274,117 @@ class Shared extends \OC\Files\Storage\Common { return false; } + /** + * @brief Format a path to be relative to the /user/files/ directory + * @param string $path the absolute path + * @return string e.g. turns '/admin/files/test.txt' into '/test.txt' + */ + private static function stripUserFilesPath($path) { + $trimmed = ltrim($path, '/'); + $split = explode('/', $trimmed); + + // it is not a file relative to data/user/files + if (count($split) < 3 || $split[1] !== 'files') { + \OCP\Util::writeLog('file sharing', + 'Can not strip userid and "files/" from path: ' . $path, + \OCP\Util::DEBUG); + return false; + } + + // skip 'user' and 'files' + $sliced = array_slice($split, 2); + $relPath = implode('/', $sliced); + + return '/' . $relPath; + } + + /** + * @brief rename a shared folder/file + * @param string $sourcePath + * @param string $targetPath + * @return bool + */ + private function renameMountPoint($sourcePath, $targetPath) { + + // it shouldn't be possible to move a Shared storage into another one + list($targetStorage, ) = \OC\Files\Filesystem::resolvePath($targetPath); + if ($targetStorage instanceof \OC\Files\Storage\Shared) { + \OCP\Util::writeLog('file sharing', + 'It is not allowed to move one mount point into another one', + \OCP\Util::DEBUG); + return false; + } + + $relTargetPath = $this->stripUserFilesPath($targetPath); + + // if the user renames a mount point from a group share we need to create a new db entry + // for the unique name + if ($this->getShareType() === \OCP\Share::SHARE_TYPE_GROUP && $this->uniqueNameSet() === false) { + $query = \OC_DB::prepare('INSERT INTO `*PREFIX*share` (`item_type`, `item_source`, `item_target`,' + .' `share_type`, `share_with`, `uid_owner`, `permissions`, `stime`, `file_source`,' + .' `file_target`, `token`, `parent`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)'); + $arguments = array($this->share['item_type'], $this->share['item_source'], $this->share['item_target'], + 2, \OCP\User::getUser(), $this->share['uid_owner'], $this->share['permissions'], $this->share['stime'], $this->share['file_source'], + $relTargetPath, $this->share['token'], $this->share['id']); + + } else { + // rename mount point + $query = \OC_DB::prepare( + 'Update `*PREFIX*share` + SET `file_target` = ? + WHERE `id` = ?' + ); + $arguments = array($relTargetPath, $this->getShareId()); + } + + $result = $query->execute($arguments); + + if ($result) { + // update the mount manager with the new paths + $mountManager = \OC\Files\Filesystem::getMountManager(); + $mount = $mountManager->find($sourcePath); + $mount->setMountPoint($targetPath . '/'); + $mountManager->addMount($mount); + $mountManager->removeMount($sourcePath . '/'); + $this->setUniqueName(); + $this->setMountPoint($relTargetPath); + + } else { + \OCP\Util::writeLog('file sharing', + 'Could not rename mount point for shared folder "' . $sourcePath . '" to "' . $targetPath . '"', + \OCP\Util::ERROR); + } + + return $result; + } + + public function rename($path1, $path2) { - // Renaming/moving is only allowed within shared folders - $pos1 = strpos($path1, '/', 1); - $pos2 = strpos($path2, '/', 1); - if ($pos1 !== false && $pos2 !== false && ($oldSource = $this->getSourcePath($path1))) { - $newSource = $this->getSourcePath(dirname($path2)) . '/' . basename($path2); - // Within the same folder, we only need UPDATE permissions - if (dirname($path1) == dirname($path2) and $this->isUpdatable($path1)) { - list($storage, $oldInternalPath) = \OC\Files\Filesystem::resolvePath($oldSource); - list(, $newInternalPath) = \OC\Files\Filesystem::resolvePath($newSource); - return $storage->rename($oldInternalPath, $newInternalPath); + + $sourceMountPoint = \OC\Files\Filesystem::getMountPoint($path1); + $targetMountPoint = \OC\Files\Filesystem::getMountPoint($path2); + $relPath1 = \OCA\Files_Sharing\Helper::stripUserFilesPath($path1); + $relPath2 = \OCA\Files_Sharing\Helper::stripUserFilesPath($path2); + + // if we renamed the mount point we need to adjust the file_target in the + // database + if (\OC\Files\Filesystem::normalizePath($sourceMountPoint) === \OC\Files\Filesystem::normalizePath($path1)) { + return $this->renameMountPoint($path1, $path2); + } + + + if ( // Within the same mount point, we only need UPDATE permissions + ($sourceMountPoint === $targetMountPoint && $this->isUpdatable($sourceMountPoint)) || // otherwise DELETE and CREATE permissions required - } elseif ($this->isDeletable($path1) && $this->isCreatable(dirname($path2))) { - $rootView = new \OC\Files\View(''); - return $rootView->rename($oldSource, $newSource); - } + ($this->isDeletable($path1) && $this->isCreatable(dirname($path2)))) { + + list($user1, $path1) = \OCA\Files_Sharing\Helper::getUidAndFilename($relPath1); + $targetFilename = basename($relPath2); + list($user2, $path2) = \OCA\Files_Sharing\Helper::getUidAndFilename(dirname($relPath2)); + $rootView = new \OC\Files\View(''); + return $rootView->rename('/' . $user1 . '/files/' . $path1, '/' . $user2 . '/files/' . $path2 . '/' . $targetFilename); } + return false; } @@ -343,7 +425,7 @@ class Shared extends \OC\Files\Storage\Common { } } $info = array( - 'target' => $this->sharedFolder . $path, + 'target' => $this->getMountPoint() . $path, 'source' => $source, 'mode' => $mode, ); @@ -355,9 +437,6 @@ class Shared extends \OC\Files\Storage\Common { } public function getMimeType($path) { - if ($path == '' || $path == '/') { - return 'httpd/unix-directory'; - } if ($source = $this->getSourcePath($path)) { list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); return $storage->getMimeType($internalPath); @@ -367,13 +446,14 @@ class Shared extends \OC\Files\Storage\Common { public function free_space($path) { if ($path == '') { - return \OC\Files\SPACE_UNKNOWN; + $path = $this->getMountPoint(); } $source = $this->getSourcePath($path); if ($source) { list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); return $storage->free_space($internalPath); } + return \OC\Files\SPACE_UNKNOWN; } public function getLocalFile($path) { @@ -393,20 +473,80 @@ class Shared extends \OC\Files\Storage\Common { } public static function setup($options) { + $shares = \OCP\Share::getItemsSharedWith('file'); if (!\OCP\User::isLoggedIn() || \OCP\User::getUser() != $options['user'] - || \OCP\Share::getItemsSharedWith('file') + || $shares ) { - $user_dir = $options['user_dir']; - \OC\Files\Filesystem::mount('\OC\Files\Storage\Shared', - array('sharedFolder' => '/Shared'), - $user_dir . '/Shared/'); + foreach ($shares as $share) { + \OC\Files\Filesystem::mount('\OC\Files\Storage\Shared', + array( + 'share' => $share, + ), + $options['user_dir'] . '/' . $share['file_target']); + } } } + /** + * @brief return mount point of share, relative to data/user/files + * @return string + */ + public function getMountPoint() { + return $this->share['file_target']; + } + + /** + * @brief get share type + * @return integer can be single user share (0) group share (1), unique group share name (2) + */ + private function getShareType() { + return $this->share['share_type']; + } + + private function setMountPoint($path) { + $this->share['file_target'] = $path; + } + + /** + * @brief does the group share already has a user specific unique name + * @return bool + */ + private function uniqueNameSet() { + return (isset($this->share['unique_name']) && $this->share['unique_name']); + } + + /** + * @brief the share now uses a unique name of this user + */ + private function setUniqueName() { + $this->share['unique_name'] = true; + } + + /** + * @brief get share ID + * @return integer unique share ID + */ + private function getShareId() { + return $this->share['id']; + } + + /** + * @brief get the user who shared the file + * @return string + */ + public function getSharedFrom() { + return $this->share['uid_owner']; + } + + /** + * @brief return share type, can be "file" or "folder" + * @return string + */ + public function getItemType() { + return $this->share['item_type']; + } + public function hasUpdated($path, $time) { - if ($path == '') { - return false; - } return $this->filemtime($path) > $time; } @@ -428,7 +568,7 @@ class Shared extends \OC\Files\Storage\Common { public function getOwner($path) { if ($path == '') { - return false; + $path = $this->getMountPoint(); } $source = $this->getFile($path); if ($source) { @@ -439,7 +579,7 @@ class Shared extends \OC\Files\Storage\Common { public function getETag($path) { if ($path == '') { - return parent::getETag($path); + $path = $this->getMountPoint(); } if ($source = $this->getSourcePath($path)) { list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); diff --git a/apps/files_sharing/lib/updater.php b/apps/files_sharing/lib/updater.php index e3a7679292d0e5aecf0e00287b70e1e4918a582a..f7c0a75aeeb200e5c1ce17218958fa1699c989bd 100644 --- a/apps/files_sharing/lib/updater.php +++ b/apps/files_sharing/lib/updater.php @@ -26,31 +26,48 @@ class Shared_Updater { // shares which can be removed from oc_share after the delete operation was successful static private $toRemove = array(); + /** + * @brief walk up the users file tree and update the etags + * @param string $user + * @param string $path + */ + static private function correctUsersFolder($user, $path) { + // $path points to the mount point which is a virtual folder, so we start with + // the parent + $path = '/files' . dirname($path); + \OC\Files\Filesystem::initMountPoints($user); + $view = new \OC\Files\View('/' . $user); + if ($view->file_exists($path)) { + while ($path !== '/') { + $etag = $view->getETag($path); + $view->putFileInfo($path, array('etag' => $etag)); + $path = dirname($path); + } + } else { + error_log("error!" . 'can not update etags on ' . $path . ' for user ' . $user); + \OCP\Util::writeLog('files_sharing', 'can not update etags on ' . $path . ' for user ' . $user, \OCP\Util::ERROR); + } + } + /** * Correct the parent folders' ETags for all users shared the file at $target * * @param string $target */ static public function correctFolders($target) { - $uid = \OCP\User::getUser(); - $uidOwner = \OC\Files\Filesystem::getOwner($target); - $info = \OC\Files\Filesystem::getFileInfo($target); - $checkedUser = array($uidOwner); // Correct Shared folders of other users shared with - $users = \OCP\Share::getUsersItemShared('file', $info['fileid'], $uidOwner, true); - if (!empty($users)) { - while (!empty($users)) { - $reshareUsers = array(); + $shares = \OCA\Files_Sharing\Helper::getSharesFromItem($target); + + foreach ($shares as $share) { + if ((int)$share['share_type'] === \OCP\Share::SHARE_TYPE_USER) { + self::correctUsersFolder($share['share_with'], $share['file_target']); + } elseif ((int)$share['share_type'] === \OCP\Share::SHARE_TYPE_GROUP) { + $users = \OC_Group::usersInGroup($share['share_with']); foreach ($users as $user) { - if ( !in_array($user, $checkedUser) ) { - $etag = \OC\Files\Filesystem::getETag(''); - \OCP\Config::setUserValue($user, 'files_sharing', 'etag', $etag); - // Look for reshares - $reshareUsers = array_merge($reshareUsers, \OCP\Share::getUsersItemShared('file', $info['fileid'], $user, true)); - $checkedUser[] = $user; - } + self::correctUsersFolder($user, $share['file_target']); } - $users = $reshareUsers; + } else { //unique name for group share + self::correctUsersFolder($share['share_with'], $share['file_target']); } } } @@ -107,34 +124,6 @@ class Shared_Updater { self::removeShare($params['path']); } - /** - * @param array $params - */ - static public function shareHook($params) { - if ($params['itemType'] === 'file' || $params['itemType'] === 'folder') { - if (isset($params['uidOwner'])) { - $uidOwner = $params['uidOwner']; - } else { - $uidOwner = \OCP\User::getUser(); - } - $users = \OCP\Share::getUsersItemShared($params['itemType'], $params['fileSource'], $uidOwner, true, false); - if (!empty($users)) { - while (!empty($users)) { - $reshareUsers = array(); - foreach ($users as $user) { - if ($user !== $uidOwner) { - $etag = \OC\Files\Filesystem::getETag(''); - \OCP\Config::setUserValue($user, 'files_sharing', 'etag', $etag); - // Look for reshares - $reshareUsers = array_merge($reshareUsers, \OCP\Share::getUsersItemShared('file', $params['fileSource'], $user, true)); - } - } - $users = $reshareUsers; - } - } - } - } - /** * clean up oc_share table from files which are no longer exists * diff --git a/apps/files_sharing/lib/watcher.php b/apps/files_sharing/lib/watcher.php index 285b1a58c6eb468feb0ef6c34aa8368289f38242..11d3ce1cabd9c840dd4287ea6a3cd2a7c1450b2a 100644 --- a/apps/files_sharing/lib/watcher.php +++ b/apps/files_sharing/lib/watcher.php @@ -32,7 +32,7 @@ class Shared_Watcher extends Watcher { * @param string $path */ public function checkUpdate($path) { - if ($path != '' && parent::checkUpdate($path) === true) { + if (parent::checkUpdate($path) === true) { // since checkUpdate() has already updated the size of the subdirs, // only apply the update to the owner's parent dirs diff --git a/apps/files_sharing/tests/api.php b/apps/files_sharing/tests/api.php index e3c5b6e4315ab9c706faa778c46fe28a13cd49bd..b2f05d10ac6e5e22cbe548b73fb1e10bb7ab1d27 100644 --- a/apps/files_sharing/tests/api.php +++ b/apps/files_sharing/tests/api.php @@ -324,10 +324,10 @@ class Test_Files_Sharing_Api extends Test_Files_Sharing_Base { $this->assertTrue(is_string($result)); $testValues=array( - array('query' => 'Shared/' . $this->folder, - 'expectedResult' => '/Shared' . $this->folder . $this->filename), - array('query' => 'Shared/' . $this->folder . $this->subfolder, - 'expectedResult' => '/Shared' . $this->folder . $this->subfolder . $this->filename), + array('query' => $this->folder, + 'expectedResult' => $this->folder . $this->filename), + array('query' => $this->folder . $this->subfolder, + 'expectedResult' => $this->folder . $this->subfolder . $this->filename), ); foreach ($testValues as $value) { @@ -382,7 +382,7 @@ class Test_Files_Sharing_Api extends Test_Files_Sharing_Base { // share was successful? $this->assertTrue(is_string($result)); - $_GET['path'] = '/Shared'; + $_GET['path'] = '/'; $_GET['subfiles'] = 'true'; $result = Share\Api::getAllShares(array()); @@ -395,7 +395,7 @@ class Test_Files_Sharing_Api extends Test_Files_Sharing_Base { // we should get exactly one result $this->assertEquals(1, count($data)); - $expectedPath = '/Shared' . $this->subfolder; + $expectedPath = $this->subfolder; $this->assertEquals($expectedPath, $data[0]['path']); // cleanup @@ -444,7 +444,7 @@ class Test_Files_Sharing_Api extends Test_Files_Sharing_Base { $this->assertTrue(is_string($result)); - $_GET['path'] = '/Shared'; + $_GET['path'] = '/'; $_GET['subfiles'] = 'true'; $result = Share\Api::getAllShares(array()); @@ -457,7 +457,7 @@ class Test_Files_Sharing_Api extends Test_Files_Sharing_Base { // we should get exactly one result $this->assertEquals(1, count($data)); - $expectedPath = '/Shared' . $this->subsubfolder; + $expectedPath = $this->subsubfolder; $this->assertEquals($expectedPath, $data[0]['path']); @@ -512,8 +512,8 @@ class Test_Files_Sharing_Api extends Test_Files_Sharing_Base { $this->assertTrue(is_string($result)); - // ask for shared/subfolder - $expectedPath1 = '/Shared' . $this->subfolder; + // ask for subfolder + $expectedPath1 = $this->subfolder; $_GET['path'] = $expectedPath1; $result1 = Share\Api::getAllShares(array()); @@ -524,8 +524,8 @@ class Test_Files_Sharing_Api extends Test_Files_Sharing_Base { $data1 = $result1->getData(); $share1 = reset($data1); - // ask for shared/folder/subfolder - $expectedPath2 = '/Shared' . $this->folder . $this->subfolder; + // ask for folder/subfolder + $expectedPath2 = $this->folder . $this->subfolder; $_GET['path'] = $expectedPath2; $result2 = Share\Api::getAllShares(array()); @@ -595,7 +595,7 @@ class Test_Files_Sharing_Api extends Test_Files_Sharing_Base { $this->assertTrue(is_string($result)); - $_GET['path'] = '/Shared'; + $_GET['path'] = '/'; $_GET['subfiles'] = 'true'; $result = Share\Api::getAllShares(array()); @@ -608,7 +608,7 @@ class Test_Files_Sharing_Api extends Test_Files_Sharing_Base { // we should get exactly one result $this->assertEquals(1, count($data)); - $expectedPath = '/Shared' . $this->filename; + $expectedPath = $this->filename; $this->assertEquals($expectedPath, $data[0]['path']); @@ -866,25 +866,89 @@ class Test_Files_Sharing_Api extends Test_Files_Sharing_Base { $this->assertTrue($result3->succeeded()); + // cleanup + \Test_Files_Sharing_Api::loginHelper(\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER1); + + $result = \OCP\Share::unshare('folder', $fileInfo1['fileid'], \OCP\Share::SHARE_TYPE_USER, + \Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2); + + $this->assertTrue($result); + + + } - function testCorrectPath() { - $path = "/foo/bar/test.txt"; - $folder = "/correct/path"; - $expectedResult = "/correct/path/test.txt"; + /** + * @brief share a folder which contains a share mount point, should be forbidden + */ + public function testShareFolderWithAMountPoint() { + // user 1 shares a folder with user2 + \Test_Files_Sharing_Api::loginHelper(\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER1); + + $fileInfo = $this->view->getFileInfo($this->folder); + + $result = \OCP\Share::shareItem('folder', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, + \Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2, 31); + + $this->assertTrue($result); + + // user2 shares a file from the folder as link + \Test_Files_Sharing_Api::loginHelper(\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2); + + $view = new \OC\Files\View('/' . \Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2 . '/files'); + $view->mkdir("localDir"); - $shareApiDummy = new TestShareApi(); + // move mount point to the folder "localDir" + $result = $view->rename($this->folder, 'localDir/'.$this->folder); + $this->assertTrue($result !== false); - $this->assertSame($expectedResult, $shareApiDummy->correctPathTest($path, $folder)); + // try to share "localDir" + $fileInfo2 = $view->getFileInfo('localDir'); + + $this->assertTrue($fileInfo2 instanceof \OC\Files\FileInfo); + + try { + $result2 = \OCP\Share::shareItem('folder', $fileInfo2['fileid'], \OCP\Share::SHARE_TYPE_USER, + \Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER3, 31); + } catch (\Exception $e) { + $result2 = false; + } + + $this->assertFalse($result2); + + //cleanup + + $result = $view->rename('localDir/' . $this->folder, $this->folder); + $this->assertTrue($result !== false); + $view->unlink('localDir'); + + \Test_Files_Sharing_Api::loginHelper(\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER1); + + \OCP\Share::unshare('folder', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, + \Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2); } -} + /** + * @expectedException \Exception + */ + public function testShareNonExisting() { + \Test_Files_Sharing_Api::loginHelper(\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER1); -/** - * @brief dumnmy class to test protected methods - */ -class TestShareApi extends \OCA\Files\Share\Api { - public function correctPathTest($path, $folder) { - return self::correctPath($path, $folder); + $id = PHP_INT_MAX - 1; + \OCP\Share::shareItem('file', $id, \OCP\Share::SHARE_TYPE_LINK, \Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2, 31); + } + + /** + * @expectedException \Exception + */ + public function testShareNotOwner() { + \Test_Files_Sharing_Api::loginHelper(\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2); + \OC\Files\Filesystem::file_put_contents('foo.txt', 'bar'); + $info = \OC\Files\Filesystem::getFileInfo('foo.txt'); + + \Test_Files_Sharing_Api::loginHelper(\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER1); + + \OCP\Share::shareItem('file', $info->getId(), \OCP\Share::SHARE_TYPE_LINK, \Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2, 31); } + } diff --git a/apps/files_sharing/tests/base.php b/apps/files_sharing/tests/base.php index d44972d01f147bdc35a0538f33b31e4e1ebf856f..495dca072c72759af0ce50a5a5630065031e4366 100644 --- a/apps/files_sharing/tests/base.php +++ b/apps/files_sharing/tests/base.php @@ -102,22 +102,20 @@ abstract class Test_Files_Sharing_Base extends \PHPUnit_Framework_TestCase { * @param bool $password */ protected static function loginHelper($user, $create = false, $password = false) { - if ($create) { - \OC_User::createUser($user, $user); - } if ($password === false) { $password = $user; } + if ($create) { + \OC_User::createUser($user, $password); + } + \OC_Util::tearDownFS(); \OC_User::setUserId(''); \OC\Files\Filesystem::tearDown(); - \OC_Util::setupFS($user); \OC_User::setUserId($user); - - $params['uid'] = $user; - $params['password'] = $password; + \OC_Util::setupFS($user); } /** diff --git a/apps/files_sharing/tests/cache.php b/apps/files_sharing/tests/cache.php index 47969833ab515bb01af818f5d7101ff4f10740b6..1af73c558d553c476f0386a536c5b18ee244141b 100644 --- a/apps/files_sharing/tests/cache.php +++ b/apps/files_sharing/tests/cache.php @@ -68,7 +68,7 @@ class Test_Files_Sharing_Cache extends Test_Files_Sharing_Base { // retrieve the shared storage $secondView = new \OC\Files\View('/' . self::TEST_FILES_SHARING_API_USER2); - list($this->sharedStorage, $internalPath) = $secondView->resolvePath('files/Shared/shareddir'); + list($this->sharedStorage, $internalPath) = $secondView->resolvePath('files/shareddir'); $this->sharedCache = $this->sharedStorage->getCache(); } @@ -98,46 +98,46 @@ class Test_Files_Sharing_Cache extends Test_Files_Sharing_Base { function testSearchByMime() { $results = $this->sharedStorage->getCache()->searchByMime('text'); $check = array( - array( - 'name' => 'shared single file.txt', - 'path' => 'shared single file.txt' - ), array( 'name' => 'bar.txt', - 'path' => 'shareddir/bar.txt' + 'path' => 'bar.txt' ), array( 'name' => 'another too.txt', - 'path' => 'shareddir/subdir/another too.txt' + 'path' => 'subdir/another too.txt' ), array( 'name' => 'another.txt', - 'path' => 'shareddir/subdir/another.txt' + 'path' => 'subdir/another.txt' ), ); $this->verifyFiles($check, $results); - $results2 = $this->sharedStorage->getCache()->searchByMime('text/plain'); - $this->verifyFiles($check, $results); } function testGetFolderContentsInRoot() { - $results = $this->user2View->getDirectoryContent('/Shared/'); + $results = $this->user2View->getDirectoryContent('/'); + // we should get the shared items "shareddir" and "shared single file.txt" + // additional root will always contain the example file "welcome.txt", + // so this will be part of the result $this->verifyFiles( array( + array( + 'name' => 'welcome.txt', + 'path' => 'files/welcome.txt', + 'mimetype' => 'text/plain', + ), array( 'name' => 'shareddir', - 'path' => '/shareddir', + 'path' => 'files/shareddir', 'mimetype' => 'httpd/unix-directory', - 'usersPath' => 'files/Shared/shareddir' ), array( 'name' => 'shared single file.txt', - 'path' => '/shared single file.txt', + 'path' => 'files/shared single file.txt', 'mimetype' => 'text/plain', - 'usersPath' => 'files/Shared/shared single file.txt' ), ), $results @@ -145,27 +145,24 @@ class Test_Files_Sharing_Cache extends Test_Files_Sharing_Base { } function testGetFolderContentsInSubdir() { - $results = $this->user2View->getDirectoryContent('/Shared/shareddir'); + $results = $this->user2View->getDirectoryContent('/shareddir'); $this->verifyFiles( array( array( 'name' => 'bar.txt', - 'path' => 'files/container/shareddir/bar.txt', + 'path' => 'bar.txt', 'mimetype' => 'text/plain', - 'usersPath' => 'files/Shared/shareddir/bar.txt' ), array( 'name' => 'emptydir', - 'path' => 'files/container/shareddir/emptydir', + 'path' => 'emptydir', 'mimetype' => 'httpd/unix-directory', - 'usersPath' => 'files/Shared/shareddir/emptydir' ), array( 'name' => 'subdir', - 'path' => 'files/container/shareddir/subdir', + 'path' => 'subdir', 'mimetype' => 'httpd/unix-directory', - 'usersPath' => 'files/Shared/shareddir/subdir' ), ), $results @@ -182,27 +179,24 @@ class Test_Files_Sharing_Cache extends Test_Files_Sharing_Base { self::loginHelper(self::TEST_FILES_SHARING_API_USER3); $thirdView = new \OC\Files\View('/' . self::TEST_FILES_SHARING_API_USER3 . '/files'); - $results = $thirdView->getDirectoryContent('/Shared/subdir'); + $results = $thirdView->getDirectoryContent('/subdir'); $this->verifyFiles( array( array( 'name' => 'another too.txt', - 'path' => 'files/container/shareddir/subdir/another too.txt', + 'path' => 'another too.txt', 'mimetype' => 'text/plain', - 'usersPath' => 'files/Shared/subdir/another too.txt' ), array( 'name' => 'another.txt', - 'path' => 'files/container/shareddir/subdir/another.txt', + 'path' => 'another.txt', 'mimetype' => 'text/plain', - 'usersPath' => 'files/Shared/subdir/another.txt' ), array( 'name' => 'not a text file.xml', - 'path' => 'files/container/shareddir/subdir/not a text file.xml', + 'path' => 'not a text file.xml', 'mimetype' => 'application/xml', - 'usersPath' => 'files/Shared/subdir/not a text file.xml' ), ), $results @@ -254,14 +248,14 @@ class Test_Files_Sharing_Cache extends Test_Files_Sharing_Base { \OC_Util::tearDownFS(); self::loginHelper(self::TEST_FILES_SHARING_API_USER2); - $this->assertTrue(\OC\Files\Filesystem::file_exists('/Shared/test.txt')); - list($sharedStorage) = \OC\Files\Filesystem::resolvePath('/' . self::TEST_FILES_SHARING_API_USER2 . '/files/Shared/test.txt'); + $this->assertTrue(\OC\Files\Filesystem::file_exists('/test.txt')); + list($sharedStorage) = \OC\Files\Filesystem::resolvePath('/' . self::TEST_FILES_SHARING_API_USER2 . '/files/test.txt'); /** * @var \OC\Files\Storage\Shared $sharedStorage */ $sharedCache = $sharedStorage->getCache(); - $this->assertEquals('test.txt', $sharedCache->getPathById($info->getId())); + $this->assertEquals('', $sharedCache->getPathById($info->getId())); } public function testGetPathByIdShareSubFolder() { @@ -275,14 +269,14 @@ class Test_Files_Sharing_Cache extends Test_Files_Sharing_Base { \OC_Util::tearDownFS(); self::loginHelper(self::TEST_FILES_SHARING_API_USER2); - $this->assertTrue(\OC\Files\Filesystem::file_exists('/Shared/foo')); - list($sharedStorage) = \OC\Files\Filesystem::resolvePath('/' . self::TEST_FILES_SHARING_API_USER2 . '/files/Shared/foo'); + $this->assertTrue(\OC\Files\Filesystem::file_exists('/foo')); + list($sharedStorage) = \OC\Files\Filesystem::resolvePath('/' . self::TEST_FILES_SHARING_API_USER2 . '/files/foo'); /** * @var \OC\Files\Storage\Shared $sharedStorage */ $sharedCache = $sharedStorage->getCache(); - $this->assertEquals('foo', $sharedCache->getPathById($folderInfo->getId())); - $this->assertEquals('foo/bar/test.txt', $sharedCache->getPathById($fileInfo->getId())); + $this->assertEquals('', $sharedCache->getPathById($folderInfo->getId())); + $this->assertEquals('bar/test.txt', $sharedCache->getPathById($fileInfo->getId())); } } diff --git a/apps/files_sharing/tests/permissions.php b/apps/files_sharing/tests/permissions.php index e301d384a495bdc4f76c0261a175145003a91583..5ac251b0527cf59b321cceb473796dc03d4ebee1 100644 --- a/apps/files_sharing/tests/permissions.php +++ b/apps/files_sharing/tests/permissions.php @@ -23,6 +23,9 @@ require_once __DIR__ . '/base.php'; class Test_Files_Sharing_Permissions extends Test_Files_Sharing_Base { + private $sharedStorageRestrictedShare; + private $sharedCacheRestrictedShare; + function setUp() { parent::setUp(); @@ -55,8 +58,10 @@ class Test_Files_Sharing_Permissions extends Test_Files_Sharing_Base { // retrieve the shared storage $this->secondView = new \OC\Files\View('/' . self::TEST_FILES_SHARING_API_USER2); - list($this->sharedStorage, $internalPath) = $this->secondView->resolvePath('files/Shared/shareddir'); + list($this->sharedStorage, $internalPath) = $this->secondView->resolvePath('files/shareddir'); + list($this->sharedStorageRestrictedShare, $internalPath) = $this->secondView->resolvePath('files/shareddirrestricted'); $this->sharedCache = $this->sharedStorage->getCache(); + $this->sharedCacheRestrictedShare = $this->sharedStorageRestrictedShare->getCache(); } function tearDown() { @@ -86,9 +91,9 @@ class Test_Files_Sharing_Permissions extends Test_Files_Sharing_Base { $this->assertEquals(31, $sharedDirPerms); $sharedDirPerms = $this->sharedStorage->getPermissions('shareddir/textfile.txt'); $this->assertEquals(31, $sharedDirPerms); - $sharedDirRestrictedPerms = $this->sharedStorage->getPermissions('shareddirrestricted'); + $sharedDirRestrictedPerms = $this->sharedStorageRestrictedShare->getPermissions('shareddirrestricted'); $this->assertEquals(7, $sharedDirRestrictedPerms); - $sharedDirRestrictedPerms = $this->sharedStorage->getPermissions('shareddirrestricted/textfile.txt'); + $sharedDirRestrictedPerms = $this->sharedStorageRestrictedShare->getPermissions('shareddirrestricted/textfile.txt'); $this->assertEquals(7, $sharedDirRestrictedPerms); } @@ -96,12 +101,12 @@ class Test_Files_Sharing_Permissions extends Test_Files_Sharing_Base { * Test that the permissions of shared directory are returned correctly */ function testGetDirectoryPermissions() { - $contents = $this->secondView->getDirectoryContent('files/Shared/shareddir'); + $contents = $this->secondView->getDirectoryContent('files/shareddir'); $this->assertEquals('subdir', $contents[0]['name']); $this->assertEquals(31, $contents[0]['permissions']); $this->assertEquals('textfile.txt', $contents[1]['name']); $this->assertEquals(31, $contents[1]['permissions']); - $contents = $this->secondView->getDirectoryContent('files/Shared/shareddirrestricted'); + $contents = $this->secondView->getDirectoryContent('files/shareddirrestricted'); $this->assertEquals('subdir', $contents[0]['name']); $this->assertEquals(7, $contents[0]['permissions']); $this->assertEquals('textfile1.txt', $contents[1]['name']); diff --git a/apps/files_sharing/tests/updater.php b/apps/files_sharing/tests/updater.php index 79ae4879b645d1aa55612ae5b2fdfe1abf4a7fe7..3427cfe388c68cafe1b6d118cb16d606bc9c85db 100644 --- a/apps/files_sharing/tests/updater.php +++ b/apps/files_sharing/tests/updater.php @@ -20,6 +20,8 @@ * */ +require_once __DIR__ . '/../appinfo/update.php'; + /** * Class Test_Files_Sharing_Updater */ @@ -88,4 +90,57 @@ class Test_Files_Sharing_Updater extends \PHPUnit_Framework_TestCase { $result = $countItems->execute()->fetchOne(); $this->assertEquals(2, $result); } + + /** + * test update for the removal of the logical "Shared" folder. It should update + * the file_target for every share and create a physical "Shared" folder for each user + */ + function testRemoveSharedFolder() { + self::prepareDB(); + // run the update routine to remove the shared folder and replace it with a real folder + removeSharedFolder(false, 2); + + // verify results + $query = \OC_DB::prepare('SELECT * FROM `*PREFIX*share`'); + $result = $query->execute(array()); + + $newDBContent = $result->fetchAll(); + + foreach ($newDBContent as $row) { + if ((int)$row['share_type'] === \OCP\Share::SHARE_TYPE_USER) { + $this->assertSame('/Shared', substr($row['file_target'], 0, strlen('/Shared'))); + } else { + $this->assertSame('/ShouldNotChange', $row['file_target']); + } + } + + $this->cleanupSharedTable(); + + } + + private function cleanupSharedTable() { + $query = \OC_DB::prepare('DELETE FROM `*PREFIX*share`'); + $query->execute(); + } + + private function prepareDB() { + $this->cleanupSharedTable(); + // add items except one - because this is the test case for the broken share table + $addItems = \OC_DB::prepare('INSERT INTO `*PREFIX*share` (`share_type`, `item_type`, ' . + '`share_with`, `uid_owner` , `file_target`) ' . + 'VALUES (?, ?, ?, ?, ?)'); + $items = array( + array(\OCP\Share::SHARE_TYPE_USER, 'file', 'user1', 'admin' , '/foo'), + array(\OCP\Share::SHARE_TYPE_USER, 'folder', 'user2', 'admin', '/foo2'), + array(\OCP\Share::SHARE_TYPE_USER, 'file', 'user3', 'admin', '/foo3'), + array(\OCP\Share::SHARE_TYPE_USER, 'folder', 'user4', 'admin', '/foo4'), + array(\OCP\Share::SHARE_TYPE_LINK, 'file', 'user1', 'admin', '/ShouldNotChange'), + array(\OCP\Share::SHARE_TYPE_CONTACT, 'contact', 'admin', 'user1', '/ShouldNotChange'), + + ); + foreach($items as $item) { + // the number is used as path_hash + $addItems->execute($item); + } + } } diff --git a/apps/files_sharing/tests/watcher.php b/apps/files_sharing/tests/watcher.php index 5ab716e829f82ee687bd384c77c8ee2f75c58bbf..bce93c80a6c3076dda38156a99e351102bd84f34 100644 --- a/apps/files_sharing/tests/watcher.php +++ b/apps/files_sharing/tests/watcher.php @@ -48,7 +48,7 @@ class Test_Files_Sharing_Watcher extends Test_Files_Sharing_Base { // retrieve the shared storage $secondView = new \OC\Files\View('/' . self::TEST_FILES_SHARING_API_USER2); - list($this->sharedStorage, $internalPath) = $secondView->resolvePath('files/Shared/shareddir'); + list($this->sharedStorage, $internalPath) = $secondView->resolvePath('files/shareddir'); $this->sharedCache = $this->sharedStorage->getCache(); } @@ -77,12 +77,12 @@ class Test_Files_Sharing_Watcher extends Test_Files_Sharing_Base { $textData = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; $dataLen = strlen($textData); - $this->sharedCache->put('shareddir/bar.txt', array('storage_mtime' => 10)); - $this->sharedStorage->file_put_contents('shareddir/bar.txt', $textData); - $this->sharedCache->put('shareddir', array('storage_mtime' => 10)); + $this->sharedCache->put('bar.txt', array('mtime' => 10, 'storage_mtime' => 10, 'size' => $dataLen, 'mimetype' => 'text/plain')); + $this->sharedStorage->file_put_contents('bar.txt', $textData); + $this->sharedCache->put('', array('mtime' => 10, 'storage_mtime' => 10, 'size' => '-1', 'mimetype' => 'httpd/unix-directory')); // run the propagation code - $result = $this->sharedStorage->getWatcher()->checkUpdate('shareddir'); + $result = $this->sharedStorage->getWatcher()->checkUpdate(''); $this->assertTrue($result); @@ -94,7 +94,7 @@ class Test_Files_Sharing_Watcher extends Test_Files_Sharing_Base { $this->assertEquals($initialSizes['files/container/shareddir'] + $dataLen, $newSizes['files/container/shareddir']); // no more updates - $result = $this->sharedStorage->getWatcher()->checkUpdate('shareddir'); + $result = $this->sharedStorage->getWatcher()->checkUpdate(''); $this->assertFalse($result); } @@ -108,12 +108,12 @@ class Test_Files_Sharing_Watcher extends Test_Files_Sharing_Base { $textData = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; $dataLen = strlen($textData); - $this->sharedCache->put('shareddir/subdir/bar.txt', array('storage_mtime' => 10)); - $this->sharedStorage->file_put_contents('shareddir/subdir/bar.txt', $textData); - $this->sharedCache->put('shareddir/subdir', array('storage_mtime' => 10)); + $this->sharedCache->put('subdir/bar.txt', array('mtime' => 10, 'storage_mtime' => 10, 'size' => $dataLen, 'mimetype' => 'text/plain')); + $this->sharedStorage->file_put_contents('subdir/bar.txt', $textData); + $this->sharedCache->put('subdir', array('mtime' => 10, 'storage_mtime' => 10, 'size' => $dataLen, 'mimetype' => 'text/plain')); // run the propagation code - $result = $this->sharedStorage->getWatcher()->checkUpdate('shareddir/subdir'); + $result = $this->sharedStorage->getWatcher()->checkUpdate('subdir'); $this->assertTrue($result); @@ -126,20 +126,9 @@ class Test_Files_Sharing_Watcher extends Test_Files_Sharing_Base { $this->assertEquals($initialSizes['files/container/shareddir/subdir'] + $dataLen, $newSizes['files/container/shareddir/subdir']); // no more updates - $result = $this->sharedStorage->getWatcher()->checkUpdate('shareddir/subdir'); - - $this->assertFalse($result); - } - - function testNoUpdateOnRoot() { - // no updates when called for root path - $result = $this->sharedStorage->getWatcher()->checkUpdate(''); + $result = $this->sharedStorage->getWatcher()->checkUpdate('subdir'); $this->assertFalse($result); - - // FIXME: for some reason when running this "naked" test, - // there will be remaining nonsensical entries in the - // database with a path "test-share-user1/container/..." } /** diff --git a/apps/files_trashbin/js/trash.js b/apps/files_trashbin/js/trash.js index 5cd49e19aaacab86488be40b35ccfe60e1bf2ba5..f7724d07d2b86ee20c119216ebef49abfb4d5191 100644 --- a/apps/files_trashbin/js/trash.js +++ b/apps/files_trashbin/js/trash.js @@ -30,7 +30,7 @@ $(document).ready(function() { function removeCallback(result) { if (result.status !== 'success') { - OC.dialogs.alert(result.data.message, t('core', 'Error')); + OC.dialogs.alert(result.data.message, t('files_trashbin', 'Error')); } var files = result.data.success; @@ -59,7 +59,7 @@ $(document).ready(function() { }, removeCallback ); - }); + }, t('files_trashbin', 'Restore')); }; FileActions.register('all', 'Delete', OC.PERMISSION_READ, function() { @@ -121,7 +121,7 @@ $(document).ready(function() { function(result) { if (allFiles) { if (result.status !== 'success') { - OC.dialogs.alert(result.data.message, t('core', 'Error')); + OC.dialogs.alert(result.data.message, t('files_trashbin', 'Error')); } FileList.hideMask(); // simply remove all files @@ -170,7 +170,7 @@ $(document).ready(function() { function(result) { if (allFiles) { if (result.status !== 'success') { - OC.dialogs.alert(result.data.message, t('core', 'Error')); + OC.dialogs.alert(result.data.message, t('files_trashbin', 'Error')); } FileList.hideMask(); // simply remove all files diff --git a/apps/files_trashbin/l10n/ar.php b/apps/files_trashbin/l10n/ar.php index 4084daa127a3c8675ff298d82e93ece09a20988e..b3abc7df86ef2aff85f4edd6f3f56d7e76701daf 100644 --- a/apps/files_trashbin/l10n/ar.php +++ b/apps/files_trashbin/l10n/ar.php @@ -4,12 +4,12 @@ $TRANSLATIONS = array( "Couldn't restore %s" => "تعذّر استرجاع %s ", "Deleted files" => "حذف الملفات", "Error" => "خطأ", +"Deleted Files" => "الملفات المحذوفه", "restored" => "تمت الاستعادة", "Nothing in here. Your trash bin is empty!" => "لا يوجد شيء هنا. سلة المهملات خاليه.", "Name" => "اسم", "Restore" => "استعيد", "Deleted" => "تم الحذف", -"Delete" => "إلغاء", -"Deleted Files" => "الملفات المحذوفه" +"Delete" => "إلغاء" ); $PLURAL_FORMS = "nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;"; diff --git a/apps/files_trashbin/l10n/ast.php b/apps/files_trashbin/l10n/ast.php index d2cfafd679c836dcbeff64968132386ea370c18a..688e1ce3d8fa6735dfd93009d2bea9c1d2aeebd0 100644 --- a/apps/files_trashbin/l10n/ast.php +++ b/apps/files_trashbin/l10n/ast.php @@ -1,7 +1,13 @@ "Nun pudo desaniciase %s dafechu", +"Couldn't restore %s" => "Nun pudo restaurase %s", +"Error" => "Fallu", +"Deleted Files" => "Ficheros desaniciaos", +"Nothing in here. Your trash bin is empty!" => "Nun hai un res equí. La papelera ta balera!", "Name" => "Nome", "Restore" => "Restaurar", +"Deleted" => "Desaniciáu", "Delete" => "Desaniciar" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/apps/files_trashbin/l10n/bg_BG.php b/apps/files_trashbin/l10n/bg_BG.php index 2f1521feaa51f7e413ecb2d62881aa749712bb8b..8c9e658068c440a7e14880551e5d789dddb4c17a 100644 --- a/apps/files_trashbin/l10n/bg_BG.php +++ b/apps/files_trashbin/l10n/bg_BG.php @@ -3,11 +3,11 @@ $TRANSLATIONS = array( "Couldn't delete %s permanently" => "Невъзможно перманентното изтриване на %s", "Couldn't restore %s" => "Невъзможно възтановяване на %s", "Error" => "Грешка", +"Deleted Files" => "Изтрити файлове", "Nothing in here. Your trash bin is empty!" => "Няма нищо. Кофата е празна!", "Name" => "Име", "Restore" => "Възтановяване", "Deleted" => "Изтрито", -"Delete" => "Изтриване", -"Deleted Files" => "Изтрити файлове" +"Delete" => "Изтриване" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/apps/files_trashbin/l10n/ca.php b/apps/files_trashbin/l10n/ca.php index c99b414c7a8db8d221ac7fa022a9ea53a09c6d55..196d6ac00a4a3a7ad7823846096e44f51f776fd8 100644 --- a/apps/files_trashbin/l10n/ca.php +++ b/apps/files_trashbin/l10n/ca.php @@ -4,12 +4,12 @@ $TRANSLATIONS = array( "Couldn't restore %s" => "No s'ha pogut restaurar %s", "Deleted files" => "Fitxers esborrats", "Error" => "Error", +"Deleted Files" => "Fitxers eliminats", "restored" => "restaurat", "Nothing in here. Your trash bin is empty!" => "La paperera està buida!", "Name" => "Nom", "Restore" => "Recupera", "Deleted" => "Eliminat", -"Delete" => "Esborra", -"Deleted Files" => "Fitxers eliminats" +"Delete" => "Esborra" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/apps/files_trashbin/l10n/cs_CZ.php b/apps/files_trashbin/l10n/cs_CZ.php index e0c46c5137da0e856141f8a9469b48dc658f8aaf..ed795582e43012534173a7d411866bc8d57bde83 100644 --- a/apps/files_trashbin/l10n/cs_CZ.php +++ b/apps/files_trashbin/l10n/cs_CZ.php @@ -4,12 +4,12 @@ $TRANSLATIONS = array( "Couldn't restore %s" => "Nelze obnovit %s", "Deleted files" => "Odstraněné soubory", "Error" => "Chyba", +"Deleted Files" => "Smazané soubory", "restored" => "obnoveno", "Nothing in here. Your trash bin is empty!" => "Žádný obsah. Váš koš je prázdný.", "Name" => "Název", "Restore" => "Obnovit", "Deleted" => "Smazáno", -"Delete" => "Smazat", -"Deleted Files" => "Smazané soubory" +"Delete" => "Smazat" ); $PLURAL_FORMS = "nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;"; diff --git a/apps/files_trashbin/l10n/cy_GB.php b/apps/files_trashbin/l10n/cy_GB.php index bc8a318733b6af4b44b39848a4b8959ab80d1d6c..7b1405777dda4bc6e266d41e6e8166b07156894f 100644 --- a/apps/files_trashbin/l10n/cy_GB.php +++ b/apps/files_trashbin/l10n/cy_GB.php @@ -4,11 +4,11 @@ $TRANSLATIONS = array( "Couldn't restore %s" => "Methwyd adfer %s", "Deleted files" => "Ffeiliau ddilewyd", "Error" => "Gwall", +"Deleted Files" => "Ffeiliau Ddilewyd", "Nothing in here. Your trash bin is empty!" => "Does dim byd yma. Mae eich bin sbwriel yn wag!", "Name" => "Enw", "Restore" => "Adfer", "Deleted" => "Wedi dileu", -"Delete" => "Dileu", -"Deleted Files" => "Ffeiliau Ddilewyd" +"Delete" => "Dileu" ); $PLURAL_FORMS = "nplurals=4; plural=(n==1) ? 0 : (n==2) ? 1 : (n != 8 && n != 11) ? 2 : 3;"; diff --git a/apps/files_trashbin/l10n/da.php b/apps/files_trashbin/l10n/da.php index 831ba6067da425a957de4009baa8069902b768a3..7f7b65bca2ce764837bb547fe0886765b4645c05 100644 --- a/apps/files_trashbin/l10n/da.php +++ b/apps/files_trashbin/l10n/da.php @@ -4,12 +4,12 @@ $TRANSLATIONS = array( "Couldn't restore %s" => "Kunne ikke gendanne %s", "Deleted files" => "Slettede filer", "Error" => "Fejl", +"Deleted Files" => "Slettede filer", "restored" => "Gendannet", "Nothing in here. Your trash bin is empty!" => "Intet at se her. Din papirkurv er tom!", "Name" => "Navn", "Restore" => "Gendan", "Deleted" => "Slettet", -"Delete" => "Slet", -"Deleted Files" => "Slettede filer" +"Delete" => "Slet" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/apps/files_trashbin/l10n/de.php b/apps/files_trashbin/l10n/de.php index fd00b4c433fd149d4cc07ebe6199dfbcaf3cc34c..4778e159e1d9cae73a9143d7fdc41efb9bf7538b 100644 --- a/apps/files_trashbin/l10n/de.php +++ b/apps/files_trashbin/l10n/de.php @@ -4,12 +4,12 @@ $TRANSLATIONS = array( "Couldn't restore %s" => "Konnte %s nicht wiederherstellen", "Deleted files" => "Gelöschte Dateien", "Error" => "Fehler", +"Deleted Files" => "Gelöschte Dateien", "restored" => "Wiederhergestellt", "Nothing in here. Your trash bin is empty!" => "Nichts zu löschen, der Papierkorb ist leer!", "Name" => "Name", "Restore" => "Wiederherstellen", "Deleted" => "gelöscht", -"Delete" => "Löschen", -"Deleted Files" => "Gelöschte Dateien" +"Delete" => "Löschen" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/apps/files_trashbin/l10n/de_CH.php b/apps/files_trashbin/l10n/de_CH.php index 665be5d5ee7305128adb8ad5de00d5e9a4063ae5..603d82f5c7afbeb4de27624dd6d3456f876dbd23 100644 --- a/apps/files_trashbin/l10n/de_CH.php +++ b/apps/files_trashbin/l10n/de_CH.php @@ -4,12 +4,12 @@ $TRANSLATIONS = array( "Couldn't restore %s" => "Konnte %s nicht wiederherstellen", "Deleted files" => "Gelöschte Dateien", "Error" => "Fehler", +"Deleted Files" => "Gelöschte Dateien", "restored" => "Wiederhergestellt", "Nothing in here. Your trash bin is empty!" => "Nichts zu löschen, Ihr Papierkorb ist leer!", "Name" => "Name", "Restore" => "Wiederherstellen", "Deleted" => "Gelöscht", -"Delete" => "Löschen", -"Deleted Files" => "Gelöschte Dateien" +"Delete" => "Löschen" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/apps/files_trashbin/l10n/de_DE.php b/apps/files_trashbin/l10n/de_DE.php index 665be5d5ee7305128adb8ad5de00d5e9a4063ae5..603d82f5c7afbeb4de27624dd6d3456f876dbd23 100644 --- a/apps/files_trashbin/l10n/de_DE.php +++ b/apps/files_trashbin/l10n/de_DE.php @@ -4,12 +4,12 @@ $TRANSLATIONS = array( "Couldn't restore %s" => "Konnte %s nicht wiederherstellen", "Deleted files" => "Gelöschte Dateien", "Error" => "Fehler", +"Deleted Files" => "Gelöschte Dateien", "restored" => "Wiederhergestellt", "Nothing in here. Your trash bin is empty!" => "Nichts zu löschen, Ihr Papierkorb ist leer!", "Name" => "Name", "Restore" => "Wiederherstellen", "Deleted" => "Gelöscht", -"Delete" => "Löschen", -"Deleted Files" => "Gelöschte Dateien" +"Delete" => "Löschen" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/apps/files_trashbin/l10n/el.php b/apps/files_trashbin/l10n/el.php index 567958de9007d1e49883bf399f4a42d897d1ff1c..23b23ed93a68e4d34e1c832a3981504bcfedb5be 100644 --- a/apps/files_trashbin/l10n/el.php +++ b/apps/files_trashbin/l10n/el.php @@ -4,12 +4,12 @@ $TRANSLATIONS = array( "Couldn't restore %s" => "Αδυναμία επαναφοράς %s", "Deleted files" => "Διαγραμμένα αρχεία", "Error" => "Σφάλμα", +"Deleted Files" => "Διαγραμμένα Αρχεία", "restored" => "επαναφέρθηκαν", "Nothing in here. Your trash bin is empty!" => "Δεν υπάρχει τίποτα εδώ. Ο κάδος σας είναι άδειος!", "Name" => "Όνομα", "Restore" => "Επαναφορά", "Deleted" => "Διαγραμμένα", -"Delete" => "Διαγραφή", -"Deleted Files" => "Διαγραμμένα Αρχεία" +"Delete" => "Διαγραφή" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/apps/files_trashbin/l10n/en_GB.php b/apps/files_trashbin/l10n/en_GB.php index 04abf2d6f2359993c8c756ced06ba9ecf4b5edfd..a660b4b1ca1776a26ed70c1937ae486aaf1f3ff8 100644 --- a/apps/files_trashbin/l10n/en_GB.php +++ b/apps/files_trashbin/l10n/en_GB.php @@ -4,12 +4,12 @@ $TRANSLATIONS = array( "Couldn't restore %s" => "Couldn't restore %s", "Deleted files" => "Deleted files", "Error" => "Error", +"Deleted Files" => "Deleted Files", "restored" => "restored", "Nothing in here. Your trash bin is empty!" => "Nothing in here. Your recycle bin is empty!", "Name" => "Name", "Restore" => "Restore", "Deleted" => "Deleted", -"Delete" => "Delete", -"Deleted Files" => "Deleted Files" +"Delete" => "Delete" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/apps/files_trashbin/l10n/eo.php b/apps/files_trashbin/l10n/eo.php index b3248406f52541b1939cc0d75d2db447ae9a6edb..d644f0f6420b768e25e1387176220c9c6cc734fe 100644 --- a/apps/files_trashbin/l10n/eo.php +++ b/apps/files_trashbin/l10n/eo.php @@ -4,12 +4,12 @@ $TRANSLATIONS = array( "Couldn't restore %s" => "Ne povis restaŭriĝi %s", "Deleted files" => "Forigitaj dosieroj", "Error" => "Eraro", +"Deleted Files" => "Forigitaj dosieroj", "restored" => "restaŭrita", "Nothing in here. Your trash bin is empty!" => "Nenio estas ĉi tie. Via rubujo malplenas!", "Name" => "Nomo", "Restore" => "Restaŭri", "Deleted" => "Forigita", -"Delete" => "Forigi", -"Deleted Files" => "Forigitaj dosieroj" +"Delete" => "Forigi" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/apps/files_trashbin/l10n/es.php b/apps/files_trashbin/l10n/es.php index 79f122ad35b90982e43206fd86aba15fdf5cb4b0..c0dc6bb45c29e375dacf88b377b7efc611ba9a21 100644 --- a/apps/files_trashbin/l10n/es.php +++ b/apps/files_trashbin/l10n/es.php @@ -4,12 +4,12 @@ $TRANSLATIONS = array( "Couldn't restore %s" => "No se puede restaurar %s", "Deleted files" => "Archivos eliminados", "Error" => "Error", +"Deleted Files" => "Archivos Eliminados", "restored" => "recuperado", "Nothing in here. Your trash bin is empty!" => "No hay nada aquí. ¡Tu papelera esta vacía!", "Name" => "Nombre", "Restore" => "Recuperar", "Deleted" => "Eliminado", -"Delete" => "Eliminar", -"Deleted Files" => "Archivos Eliminados" +"Delete" => "Eliminar" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/apps/files_trashbin/l10n/es_AR.php b/apps/files_trashbin/l10n/es_AR.php index 50b337b81442908e9fd00f499f1555d985571427..b354dd656b1c287ac1a6fed572bcf8607a5fcf5e 100644 --- a/apps/files_trashbin/l10n/es_AR.php +++ b/apps/files_trashbin/l10n/es_AR.php @@ -4,12 +4,12 @@ $TRANSLATIONS = array( "Couldn't restore %s" => "No se pudo restaurar %s", "Deleted files" => "Archivos borrados", "Error" => "Error", +"Deleted Files" => "Archivos eliminados", "restored" => "recuperado", "Nothing in here. Your trash bin is empty!" => "No hay nada acá. ¡La papelera está vacía!", "Name" => "Nombre", "Restore" => "Recuperar", "Deleted" => "Borrado", -"Delete" => "Borrar", -"Deleted Files" => "Archivos eliminados" +"Delete" => "Borrar" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/apps/files_trashbin/l10n/es_MX.php b/apps/files_trashbin/l10n/es_MX.php index 79f122ad35b90982e43206fd86aba15fdf5cb4b0..c0dc6bb45c29e375dacf88b377b7efc611ba9a21 100644 --- a/apps/files_trashbin/l10n/es_MX.php +++ b/apps/files_trashbin/l10n/es_MX.php @@ -4,12 +4,12 @@ $TRANSLATIONS = array( "Couldn't restore %s" => "No se puede restaurar %s", "Deleted files" => "Archivos eliminados", "Error" => "Error", +"Deleted Files" => "Archivos Eliminados", "restored" => "recuperado", "Nothing in here. Your trash bin is empty!" => "No hay nada aquí. ¡Tu papelera esta vacía!", "Name" => "Nombre", "Restore" => "Recuperar", "Deleted" => "Eliminado", -"Delete" => "Eliminar", -"Deleted Files" => "Archivos Eliminados" +"Delete" => "Eliminar" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/apps/files_trashbin/l10n/et_EE.php b/apps/files_trashbin/l10n/et_EE.php index 69ed5cee76731a0ff3fa6032d05653a25eb78eb7..69d20cb195ff7a35b1e40bf3d283288ff3d018ba 100644 --- a/apps/files_trashbin/l10n/et_EE.php +++ b/apps/files_trashbin/l10n/et_EE.php @@ -4,12 +4,12 @@ $TRANSLATIONS = array( "Couldn't restore %s" => "%s ei saa taastada", "Deleted files" => "Kustutatud failid", "Error" => "Viga", +"Deleted Files" => "Kustutatud failid", "restored" => "taastatud", "Nothing in here. Your trash bin is empty!" => "Siin pole midagi. Sinu prügikast on tühi!", "Name" => "Nimi", "Restore" => "Taasta", "Deleted" => "Kustutatud", -"Delete" => "Kustuta", -"Deleted Files" => "Kustutatud failid" +"Delete" => "Kustuta" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/apps/files_trashbin/l10n/eu.php b/apps/files_trashbin/l10n/eu.php index 648aaaf738307b4b248261fbe74f6b4eb9fa1ff7..42476bccfe4d05388999124e2e4e6c63f3a25b90 100644 --- a/apps/files_trashbin/l10n/eu.php +++ b/apps/files_trashbin/l10n/eu.php @@ -4,12 +4,12 @@ $TRANSLATIONS = array( "Couldn't restore %s" => "Ezin izan da %s berreskuratu", "Deleted files" => "Ezabatutako fitxategiak", "Error" => "Errorea", +"Deleted Files" => "Ezabatutako Fitxategiak", "restored" => "Berrezarrita", "Nothing in here. Your trash bin is empty!" => "Ez dago ezer ez. Zure zakarrontzia hutsik dago!", "Name" => "Izena", "Restore" => "Berrezarri", "Deleted" => "Ezabatuta", -"Delete" => "Ezabatu", -"Deleted Files" => "Ezabatutako Fitxategiak" +"Delete" => "Ezabatu" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/apps/files_trashbin/l10n/fa.php b/apps/files_trashbin/l10n/fa.php index 40cdec8a4846fde78b775eb13fe896ce216f82a0..407524eb62050960823df51fd14e65f5985e274f 100644 --- a/apps/files_trashbin/l10n/fa.php +++ b/apps/files_trashbin/l10n/fa.php @@ -4,11 +4,11 @@ $TRANSLATIONS = array( "Couldn't restore %s" => "%s را نمی توان بازگرداند", "Deleted files" => "فایل های حذف شده", "Error" => "خطا", +"Deleted Files" => "فایلهای حذف شده", "Nothing in here. Your trash bin is empty!" => "هیچ چیزی اینجا نیست. سطل زباله ی شما خالی است.", "Name" => "نام", "Restore" => "بازیابی", "Deleted" => "حذف شده", -"Delete" => "حذف", -"Deleted Files" => "فایلهای حذف شده" +"Delete" => "حذف" ); $PLURAL_FORMS = "nplurals=1; plural=0;"; diff --git a/apps/files_trashbin/l10n/fi_FI.php b/apps/files_trashbin/l10n/fi_FI.php index e7b62dbb437c222012222df77ab594ef8be30953..da56baf0bd7fa08b48f19bb2e37ddb830d456615 100644 --- a/apps/files_trashbin/l10n/fi_FI.php +++ b/apps/files_trashbin/l10n/fi_FI.php @@ -4,12 +4,12 @@ $TRANSLATIONS = array( "Couldn't restore %s" => "Kohteen %s palautus epäonnistui", "Deleted files" => "Poistetut tiedostot", "Error" => "Virhe", +"Deleted Files" => "Poistetut tiedostot", "restored" => "palautettu", "Nothing in here. Your trash bin is empty!" => "Tyhjää täynnä! Roskakorissa ei ole mitään.", "Name" => "Nimi", "Restore" => "Palauta", "Deleted" => "Poistettu", -"Delete" => "Poista", -"Deleted Files" => "Poistetut tiedostot" +"Delete" => "Poista" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/apps/files_trashbin/l10n/fr.php b/apps/files_trashbin/l10n/fr.php index 9e74657fc10be88811f7885f070c4795624cec99..b71fbea96ad09bc657905a869ee1a9d5b773d31f 100644 --- a/apps/files_trashbin/l10n/fr.php +++ b/apps/files_trashbin/l10n/fr.php @@ -4,12 +4,12 @@ $TRANSLATIONS = array( "Couldn't restore %s" => "Impossible de restaurer %s", "Deleted files" => "Fichiers supprimés", "Error" => "Erreur", +"Deleted Files" => "Fichiers effacés", "restored" => "restauré", "Nothing in here. Your trash bin is empty!" => "Il n'y a rien ici. Votre corbeille est vide !", "Name" => "Nom", "Restore" => "Restaurer", "Deleted" => "Effacé", -"Delete" => "Supprimer", -"Deleted Files" => "Fichiers effacés" +"Delete" => "Supprimer" ); $PLURAL_FORMS = "nplurals=2; plural=(n > 1);"; diff --git a/apps/files_trashbin/l10n/gl.php b/apps/files_trashbin/l10n/gl.php index bf26936be0bbf7911e62a3216a2fb4b59718a624..fe74ab34a02d5655ae03155958a787797dc4df5b 100644 --- a/apps/files_trashbin/l10n/gl.php +++ b/apps/files_trashbin/l10n/gl.php @@ -4,12 +4,12 @@ $TRANSLATIONS = array( "Couldn't restore %s" => "Non foi posíbel restaurar %s", "Deleted files" => "Ficheiros eliminados", "Error" => "Erro", +"Deleted Files" => "Ficheiros eliminados", "restored" => "restaurado", "Nothing in here. Your trash bin is empty!" => "Aquí non hai nada. O cesto do lixo está baleiro!", "Name" => "Nome", "Restore" => "Restablecer", "Deleted" => "Eliminado", -"Delete" => "Eliminar", -"Deleted Files" => "Ficheiros eliminados" +"Delete" => "Eliminar" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/apps/files_trashbin/l10n/he.php b/apps/files_trashbin/l10n/he.php index 6d2d184bec26f7c0c70acc3e27cef0b47e3037e0..6cdc5c05c9d0985fd52a13c0dcef7914c4cdaf90 100644 --- a/apps/files_trashbin/l10n/he.php +++ b/apps/files_trashbin/l10n/he.php @@ -4,12 +4,12 @@ $TRANSLATIONS = array( "Couldn't restore %s" => "לא ניתן לשחזר את %s", "Deleted files" => "קבצים שנמחקו", "Error" => "שגיאה", +"Deleted Files" => "קבצים שנמחקו", "restored" => "שוחזר", "Nothing in here. Your trash bin is empty!" => "אין כאן שום דבר. סל המיחזור שלך ריק!", "Name" => "שם", "Restore" => "שחזור", "Deleted" => "נמחק", -"Delete" => "מחיקה", -"Deleted Files" => "קבצים שנמחקו" +"Delete" => "מחיקה" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/apps/files_trashbin/l10n/hu_HU.php b/apps/files_trashbin/l10n/hu_HU.php index 69a2f5d5d4532f70883436f108ff5168bb2f811f..2912821d96f97c32c4f7d3af58589b0f7d92592f 100644 --- a/apps/files_trashbin/l10n/hu_HU.php +++ b/apps/files_trashbin/l10n/hu_HU.php @@ -4,12 +4,12 @@ $TRANSLATIONS = array( "Couldn't restore %s" => "Nem sikerült %s visszaállítása", "Deleted files" => "Törölt fájlok", "Error" => "Hiba", +"Deleted Files" => "Törölt fájlok", "restored" => "visszaállítva", "Nothing in here. Your trash bin is empty!" => "Itt nincs semmi. Az Ön szemetes mappája üres!", "Name" => "Név", "Restore" => "Visszaállítás", "Deleted" => "Törölve", -"Delete" => "Törlés", -"Deleted Files" => "Törölt fájlok" +"Delete" => "Törlés" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/apps/files_trashbin/l10n/id.php b/apps/files_trashbin/l10n/id.php index 11e1675074c89513c2e76bff05a2c8b0cdc79c16..166b9aa811a2441f4099c091c1fbee3a4c6336dd 100644 --- a/apps/files_trashbin/l10n/id.php +++ b/apps/files_trashbin/l10n/id.php @@ -4,11 +4,11 @@ $TRANSLATIONS = array( "Couldn't restore %s" => "Tidak dapat memulihkan %s", "Deleted files" => "Berkas yang dihapus", "Error" => "Galat", +"Deleted Files" => "Berkas yang Dihapus", "Nothing in here. Your trash bin is empty!" => "Tempat sampah anda kosong!", "Name" => "Nama", "Restore" => "Pulihkan", "Deleted" => "Dihapus", -"Delete" => "Hapus", -"Deleted Files" => "Berkas yang Dihapus" +"Delete" => "Hapus" ); $PLURAL_FORMS = "nplurals=1; plural=0;"; diff --git a/apps/files_trashbin/l10n/it.php b/apps/files_trashbin/l10n/it.php index 739dda24564f721ddfe7f01ef9a9874be8a1d980..057305ac517e6b09be9904d7890a0976d271ea54 100644 --- a/apps/files_trashbin/l10n/it.php +++ b/apps/files_trashbin/l10n/it.php @@ -4,12 +4,12 @@ $TRANSLATIONS = array( "Couldn't restore %s" => "Impossibile ripristinare %s", "Deleted files" => "File eliminati", "Error" => "Errore", +"Deleted Files" => "File eliminati", "restored" => "ripristinati", "Nothing in here. Your trash bin is empty!" => "Qui non c'è niente. Il tuo cestino è vuoto.", "Name" => "Nome", "Restore" => "Ripristina", "Deleted" => "Eliminati", -"Delete" => "Elimina", -"Deleted Files" => "File eliminati" +"Delete" => "Elimina" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/apps/files_trashbin/l10n/ja.php b/apps/files_trashbin/l10n/ja.php index 0aab7d05754c55a55cee2c3fd09baed99d157165..13ca95e6fb4e38fa70d66d1259c59e2b02825faf 100644 --- a/apps/files_trashbin/l10n/ja.php +++ b/apps/files_trashbin/l10n/ja.php @@ -4,12 +4,12 @@ $TRANSLATIONS = array( "Couldn't restore %s" => "%s を復元できませんでした", "Deleted files" => "ゴミ箱", "Error" => "エラー", +"Deleted Files" => "ゴミ箱", "restored" => "復元済", "Nothing in here. Your trash bin is empty!" => "ここには何もありません。ゴミ箱は空です!", "Name" => "名前", "Restore" => "復元", "Deleted" => "削除済み", -"Delete" => "削除", -"Deleted Files" => "ゴミ箱" +"Delete" => "削除" ); $PLURAL_FORMS = "nplurals=1; plural=0;"; diff --git a/apps/files_trashbin/l10n/ka_GE.php b/apps/files_trashbin/l10n/ka_GE.php index f36bbea59bc1fcadd41d6ab3033e7dad1b55d21a..4e3ad4260e3ab9e15e520342dc960b7cd1275f11 100644 --- a/apps/files_trashbin/l10n/ka_GE.php +++ b/apps/files_trashbin/l10n/ka_GE.php @@ -4,11 +4,11 @@ $TRANSLATIONS = array( "Couldn't restore %s" => "%s–ის აღდგენა ვერ მოხერხდა", "Deleted files" => "წაშლილი ფაილები", "Error" => "შეცდომა", +"Deleted Files" => "წაშლილი ფაილები", "Nothing in here. Your trash bin is empty!" => "აქ არაფერი არ არის. სანაგვე ყუთი ცარიელია!", "Name" => "სახელი", "Restore" => "აღდგენა", "Deleted" => "წაშლილი", -"Delete" => "წაშლა", -"Deleted Files" => "წაშლილი ფაილები" +"Delete" => "წაშლა" ); $PLURAL_FORMS = "nplurals=1; plural=0;"; diff --git a/apps/files_trashbin/l10n/ko.php b/apps/files_trashbin/l10n/ko.php index f95d14324519343f1bdbf019f762d6d54a07797e..d9d870708047b562deee62c9e193e70f71d0e048 100644 --- a/apps/files_trashbin/l10n/ko.php +++ b/apps/files_trashbin/l10n/ko.php @@ -4,12 +4,12 @@ $TRANSLATIONS = array( "Couldn't restore %s" => "%s을(를) 복원할 수 없습니다", "Deleted files" => "삭제된 파일", "Error" => "오류", +"Deleted Files" => "삭제된 파일", "restored" => "복원됨", "Nothing in here. Your trash bin is empty!" => "휴지통이 비어 있습니다!", "Name" => "이름", "Restore" => "복원", "Deleted" => "삭제됨", -"Delete" => "삭제", -"Deleted Files" => "삭제된 파일" +"Delete" => "삭제" ); $PLURAL_FORMS = "nplurals=1; plural=0;"; diff --git a/apps/files_trashbin/l10n/lt_LT.php b/apps/files_trashbin/l10n/lt_LT.php index 07325665d78b91b3f78c80342adb0a78e036c293..2bf545483f6ec1e30efdb95908b8288084cb31d3 100644 --- a/apps/files_trashbin/l10n/lt_LT.php +++ b/apps/files_trashbin/l10n/lt_LT.php @@ -4,12 +4,12 @@ $TRANSLATIONS = array( "Couldn't restore %s" => "Nepavyko atkurti %s", "Deleted files" => "Ištrinti failai", "Error" => "Klaida", +"Deleted Files" => "Ištrinti failai", "restored" => "atstatyta", "Nothing in here. Your trash bin is empty!" => "Nieko nėra. Jūsų šiukšliadėžė tuščia!", "Name" => "Pavadinimas", "Restore" => "Atstatyti", "Deleted" => "Ištrinti", -"Delete" => "Ištrinti", -"Deleted Files" => "Ištrinti failai" +"Delete" => "Ištrinti" ); $PLURAL_FORMS = "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n%100<10 || n%100>=20) ? 1 : 2);"; diff --git a/apps/files_trashbin/l10n/lv.php b/apps/files_trashbin/l10n/lv.php index d356b344377dbea4c8e38afc53033ddf17e2e3a9..c173d0501477774111301adfd2f5d28dc21acc18 100644 --- a/apps/files_trashbin/l10n/lv.php +++ b/apps/files_trashbin/l10n/lv.php @@ -4,12 +4,12 @@ $TRANSLATIONS = array( "Couldn't restore %s" => "Nevarēja atjaunot %s", "Deleted files" => "Dzēstās datnes", "Error" => "Kļūda", +"Deleted Files" => "Dzēstās datnes", "restored" => "atjaunots", "Nothing in here. Your trash bin is empty!" => "Šeit nekā nav. Jūsu miskaste ir tukša!", "Name" => "Nosaukums", "Restore" => "Atjaunot", "Deleted" => "Dzēsts", -"Delete" => "Dzēst", -"Deleted Files" => "Dzēstās datnes" +"Delete" => "Dzēst" ); $PLURAL_FORMS = "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2);"; diff --git a/apps/files_trashbin/l10n/mk.php b/apps/files_trashbin/l10n/mk.php index f025a13db1a471801fa55de5b3417aebbc5e9f15..910b11e21e603de847d78ea8742285a8e687940f 100644 --- a/apps/files_trashbin/l10n/mk.php +++ b/apps/files_trashbin/l10n/mk.php @@ -4,12 +4,12 @@ $TRANSLATIONS = array( "Couldn't restore %s" => "Не можеше да се поврати %s", "Deleted files" => "Избришани датотеки", "Error" => "Грешка", +"Deleted Files" => "Избришани датотеки", "restored" => "повратени", "Nothing in here. Your trash bin is empty!" => "Тука нема ништо. Вашата корпа за отпадоци е празна!", "Name" => "Име", "Restore" => "Поврати", "Deleted" => "Избришан", -"Delete" => "Избриши", -"Deleted Files" => "Избришани датотеки" +"Delete" => "Избриши" ); $PLURAL_FORMS = "nplurals=2; plural=(n % 10 == 1 && n % 100 != 11) ? 0 : 1;"; diff --git a/apps/files_trashbin/l10n/ms_MY.php b/apps/files_trashbin/l10n/ms_MY.php index e772131391820353ed43f45aaca8b612be17a2de..f084f58465dca8e4afa59f44c3dca3e0a92db11f 100644 --- a/apps/files_trashbin/l10n/ms_MY.php +++ b/apps/files_trashbin/l10n/ms_MY.php @@ -3,12 +3,12 @@ $TRANSLATIONS = array( "Couldn't delete %s permanently" => "Tidak dapat menghapuskan %s secara kekal", "Couldn't restore %s" => "Tidak dapat memulihkan %s", "Error" => "Ralat", +"Deleted Files" => "Fail Dihapus", "restored" => "dipulihkan", "Nothing in here. Your trash bin is empty!" => "Tiada apa disini. Tong sampah anda kosong!", "Name" => "Nama", "Restore" => "Pulihkan", "Deleted" => "Dihapuskan", -"Delete" => "Padam", -"Deleted Files" => "Fail Dihapus" +"Delete" => "Padam" ); $PLURAL_FORMS = "nplurals=1; plural=0;"; diff --git a/apps/files_trashbin/l10n/nb_NO.php b/apps/files_trashbin/l10n/nb_NO.php index 66e61ee72a17a7b7c23b41ff3ba99999dd7b2070..2293e5a4e7b3265459fc282f34957fe06523a193 100644 --- a/apps/files_trashbin/l10n/nb_NO.php +++ b/apps/files_trashbin/l10n/nb_NO.php @@ -4,12 +4,12 @@ $TRANSLATIONS = array( "Couldn't restore %s" => "Kunne ikke gjenopprette %s", "Deleted files" => "Slettede filer", "Error" => "Feil", +"Deleted Files" => "Slettede filer", "restored" => "gjenopprettet", "Nothing in here. Your trash bin is empty!" => "Ingenting her. Søppelkassen din er tom!", "Name" => "Navn", "Restore" => "Gjenopprett", "Deleted" => "Slettet", -"Delete" => "Slett", -"Deleted Files" => "Slettede filer" +"Delete" => "Slett" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/apps/files_trashbin/l10n/nl.php b/apps/files_trashbin/l10n/nl.php index 533571fd9e15de67c9a4cddc2cbdae8ac8b0a95c..c8fb12885387f371d23cbf296e7cdc6f313bb99a 100644 --- a/apps/files_trashbin/l10n/nl.php +++ b/apps/files_trashbin/l10n/nl.php @@ -4,12 +4,12 @@ $TRANSLATIONS = array( "Couldn't restore %s" => "Kon %s niet herstellen", "Deleted files" => "Verwijderde bestanden", "Error" => "Fout", +"Deleted Files" => "Verwijderde bestanden", "restored" => "hersteld", "Nothing in here. Your trash bin is empty!" => "Niets te vinden. Uw prullenbak is leeg!", "Name" => "Naam", "Restore" => "Herstellen", "Deleted" => "Verwijderd", -"Delete" => "Verwijder", -"Deleted Files" => "Verwijderde bestanden" +"Delete" => "Verwijder" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/apps/files_trashbin/l10n/nn_NO.php b/apps/files_trashbin/l10n/nn_NO.php index 9282d9d49d4a4a43841151e213ac788d47c71dfe..38bc64e6ce505bcca607a141660fb2c0869ffd94 100644 --- a/apps/files_trashbin/l10n/nn_NO.php +++ b/apps/files_trashbin/l10n/nn_NO.php @@ -4,12 +4,12 @@ $TRANSLATIONS = array( "Couldn't restore %s" => "Klarte ikkje gjenoppretta %s", "Deleted files" => "Sletta filer", "Error" => "Feil", +"Deleted Files" => "Sletta filer", "restored" => "gjenoppretta", "Nothing in here. Your trash bin is empty!" => "Ingenting her. Papirkorga di er tom!", "Name" => "Namn", "Restore" => "Gjenopprett", "Deleted" => "Sletta", -"Delete" => "Slett", -"Deleted Files" => "Sletta filer" +"Delete" => "Slett" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/apps/files_trashbin/l10n/pl.php b/apps/files_trashbin/l10n/pl.php index 5075dad271a0483da639179a4014699bf51ccc24..b961efd7daeee7dcbefb2d6d0ab3dfe0e032f5be 100644 --- a/apps/files_trashbin/l10n/pl.php +++ b/apps/files_trashbin/l10n/pl.php @@ -4,12 +4,12 @@ $TRANSLATIONS = array( "Couldn't restore %s" => "Nie można przywrócić %s", "Deleted files" => "Pliki usunięte", "Error" => "Błąd", +"Deleted Files" => "Usunięte pliki", "restored" => "przywrócony", "Nothing in here. Your trash bin is empty!" => "Nic tu nie ma. Twój kosz jest pusty!", "Name" => "Nazwa", "Restore" => "Przywróć", "Deleted" => "Usunięte", -"Delete" => "Usuń", -"Deleted Files" => "Usunięte pliki" +"Delete" => "Usuń" ); $PLURAL_FORMS = "nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);"; diff --git a/apps/files_trashbin/l10n/pt_BR.php b/apps/files_trashbin/l10n/pt_BR.php index 300235452b4c9f2601840893f17651b3a8cd9a72..d524d8879e23f3040215e6a1fc58dbf941fbe16c 100644 --- a/apps/files_trashbin/l10n/pt_BR.php +++ b/apps/files_trashbin/l10n/pt_BR.php @@ -4,12 +4,12 @@ $TRANSLATIONS = array( "Couldn't restore %s" => "Não foi possível restaurar %s", "Deleted files" => "Arquivos apagados", "Error" => "Erro", +"Deleted Files" => "Arquivos Apagados", "restored" => "restaurado", "Nothing in here. Your trash bin is empty!" => "Nada aqui. Sua lixeira está vazia!", "Name" => "Nome", "Restore" => "Restaurar", "Deleted" => "Excluído", -"Delete" => "Excluir", -"Deleted Files" => "Arquivos Apagados" +"Delete" => "Excluir" ); $PLURAL_FORMS = "nplurals=2; plural=(n > 1);"; diff --git a/apps/files_trashbin/l10n/pt_PT.php b/apps/files_trashbin/l10n/pt_PT.php index 69f83e8a0e4a0a670bb98de7bc12648a987d32cb..94dd0eb707a99af065bdd2557b876b94a2a17a1e 100644 --- a/apps/files_trashbin/l10n/pt_PT.php +++ b/apps/files_trashbin/l10n/pt_PT.php @@ -4,12 +4,12 @@ $TRANSLATIONS = array( "Couldn't restore %s" => "Não foi possível restaurar %s", "Deleted files" => "Ficheiros eliminados", "Error" => "Erro", +"Deleted Files" => "Ficheiros Apagados", "restored" => "Restaurado", "Nothing in here. Your trash bin is empty!" => "Não hà ficheiros. O lixo está vazio!", "Name" => "Nome", "Restore" => "Restaurar", "Deleted" => "Apagado", -"Delete" => "Eliminar", -"Deleted Files" => "Ficheiros Apagados" +"Delete" => "Eliminar" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/apps/files_trashbin/l10n/ru.php b/apps/files_trashbin/l10n/ru.php index 3210ad7290b25e03e3238ad545bcc549b484eb57..d10369b9ca1eee47448d45af38857729d8aa73ea 100644 --- a/apps/files_trashbin/l10n/ru.php +++ b/apps/files_trashbin/l10n/ru.php @@ -4,12 +4,12 @@ $TRANSLATIONS = array( "Couldn't restore %s" => "%s не может быть восстановлен", "Deleted files" => "Удалённые файлы", "Error" => "Ошибка", +"Deleted Files" => "Удаленные файлы", "restored" => "восстановлен", "Nothing in here. Your trash bin is empty!" => "Здесь ничего нет. Ваша корзина пуста!", "Name" => "Имя", "Restore" => "Восстановить", "Deleted" => "Удалён", -"Delete" => "Удалить", -"Deleted Files" => "Удаленные файлы" +"Delete" => "Удалить" ); $PLURAL_FORMS = "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);"; diff --git a/apps/files_trashbin/l10n/sk_SK.php b/apps/files_trashbin/l10n/sk_SK.php index 6bb39bb89dd971ae987ed8bd34cdea8d7ac6718c..3badd3a423bd1798ff3f024ad424a8644265bdc8 100644 --- a/apps/files_trashbin/l10n/sk_SK.php +++ b/apps/files_trashbin/l10n/sk_SK.php @@ -4,12 +4,12 @@ $TRANSLATIONS = array( "Couldn't restore %s" => "Nemožno obnoviť %s", "Deleted files" => "Zmazané súbory", "Error" => "Chyba", +"Deleted Files" => "Zmazané súbory", "restored" => "obnovené", "Nothing in here. Your trash bin is empty!" => "Žiadny obsah. Kôš je prázdny!", "Name" => "Názov", "Restore" => "Obnoviť", "Deleted" => "Zmazané", -"Delete" => "Zmazať", -"Deleted Files" => "Zmazané súbory" +"Delete" => "Zmazať" ); $PLURAL_FORMS = "nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;"; diff --git a/apps/files_trashbin/l10n/sl.php b/apps/files_trashbin/l10n/sl.php index ca69c8d2784fb751e34291c72189179ee941140c..08da9b1c6e90c8c4b78ca6ef2c16838559a1a66f 100644 --- a/apps/files_trashbin/l10n/sl.php +++ b/apps/files_trashbin/l10n/sl.php @@ -4,12 +4,12 @@ $TRANSLATIONS = array( "Couldn't restore %s" => "Ni mogoče obnoviti %s", "Deleted files" => "Izbrisane datoteke", "Error" => "Napaka", +"Deleted Files" => "Izbrisane datoteke", "restored" => "obnovljeno", "Nothing in here. Your trash bin is empty!" => "Mapa smeti je prazna.", "Name" => "Ime", "Restore" => "Obnovi", "Deleted" => "Izbrisano", -"Delete" => "Izbriši", -"Deleted Files" => "Izbrisane datoteke" +"Delete" => "Izbriši" ); $PLURAL_FORMS = "nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3);"; diff --git a/apps/files_trashbin/l10n/sq.php b/apps/files_trashbin/l10n/sq.php index 10354e45408132d8398df2723b2f3d1ec6ad8105..60d16f9b913a397525b35c81a6c8071ff7b8ade0 100644 --- a/apps/files_trashbin/l10n/sq.php +++ b/apps/files_trashbin/l10n/sq.php @@ -4,12 +4,12 @@ $TRANSLATIONS = array( "Couldn't restore %s" => "Nuk munda ta rivendos %s", "Deleted files" => "Skedarë të fshirë ", "Error" => "Veprim i gabuar", +"Deleted Files" => "Skedarë të eliminuar", "restored" => "rivendosur", "Nothing in here. Your trash bin is empty!" => "Këtu nuk ka asgjë. Koshi juaj është bosh!", "Name" => "Emri", "Restore" => "Rivendos", "Deleted" => "Eliminuar", -"Delete" => "Elimino", -"Deleted Files" => "Skedarë të eliminuar" +"Delete" => "Elimino" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/apps/files_trashbin/l10n/sv.php b/apps/files_trashbin/l10n/sv.php index ec9de3acaa2159f64df0b87ce582ac35ba78144e..fd9ca8653f3e58856f0058b315846756d7d2118f 100644 --- a/apps/files_trashbin/l10n/sv.php +++ b/apps/files_trashbin/l10n/sv.php @@ -4,12 +4,12 @@ $TRANSLATIONS = array( "Couldn't restore %s" => "Kunde inte återställa %s", "Deleted files" => "Raderade filer", "Error" => "Fel", +"Deleted Files" => "Raderade filer", "restored" => "återställd", "Nothing in here. Your trash bin is empty!" => "Ingenting här. Din papperskorg är tom!", "Name" => "Namn", "Restore" => "Återskapa", "Deleted" => "Raderad", -"Delete" => "Radera", -"Deleted Files" => "Raderade filer" +"Delete" => "Radera" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/apps/files_trashbin/l10n/th_TH.php b/apps/files_trashbin/l10n/th_TH.php index 65fd081a952e2226dc8d72966acd1f47cb1b8a11..857737c59e86a873b3a88493b7e93887194f1848 100644 --- a/apps/files_trashbin/l10n/th_TH.php +++ b/apps/files_trashbin/l10n/th_TH.php @@ -1,11 +1,11 @@ "ข้อผิดพลาด", +"Deleted Files" => "ไฟล์ที่ลบทิ้ง", "Nothing in here. Your trash bin is empty!" => "ไม่มีอะไรอยู่ในนี้ ถังขยะของคุณยังว่างอยู่", "Name" => "ชื่อ", "Restore" => "คืนค่า", "Deleted" => "ลบแล้ว", -"Delete" => "ลบ", -"Deleted Files" => "ไฟล์ที่ลบทิ้ง" +"Delete" => "ลบ" ); $PLURAL_FORMS = "nplurals=1; plural=0;"; diff --git a/apps/files_trashbin/l10n/tr.php b/apps/files_trashbin/l10n/tr.php index c1234cdfb0ca79bc0db98753fc64cd02fe67311b..ff4227e38c74a8d2c99b423b7e63e760a0237209 100644 --- a/apps/files_trashbin/l10n/tr.php +++ b/apps/files_trashbin/l10n/tr.php @@ -1,15 +1,15 @@ "%s alıcı olarak silinemedi", -"Couldn't restore %s" => "%s eri yüklenemedi", +"Couldn't restore %s" => "%s geri yüklenemedi", "Deleted files" => "Silinmiş dosyalar", "Error" => "Hata", +"Deleted Files" => "Silinen Dosyalar", "restored" => "geri yüklendi", "Nothing in here. Your trash bin is empty!" => "Burada hiçbir şey yok. Çöp kutunuz tamamen boş!", "Name" => "İsim", "Restore" => "Geri yükle", "Deleted" => "Silindi", -"Delete" => "Sil", -"Deleted Files" => "Silinen Dosyalar" +"Delete" => "Sil" ); $PLURAL_FORMS = "nplurals=2; plural=(n > 1);"; diff --git a/apps/files_trashbin/l10n/uk.php b/apps/files_trashbin/l10n/uk.php index 19d4bda97c732d9d10819c5d932a1a40b3db8fce..fa523fa3218685fbff12f8678c441d5699f12a89 100644 --- a/apps/files_trashbin/l10n/uk.php +++ b/apps/files_trashbin/l10n/uk.php @@ -4,12 +4,12 @@ $TRANSLATIONS = array( "Couldn't restore %s" => "Неможливо відновити %s", "Deleted files" => "Видалено файлів", "Error" => "Помилка", +"Deleted Files" => "Видалено Файлів", "restored" => "відновлено", "Nothing in here. Your trash bin is empty!" => "Нічого немає. Ваший кошик для сміття пустий!", "Name" => "Ім'я", "Restore" => "Відновити", "Deleted" => "Видалено", -"Delete" => "Видалити", -"Deleted Files" => "Видалено Файлів" +"Delete" => "Видалити" ); $PLURAL_FORMS = "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);"; diff --git a/apps/files_trashbin/l10n/vi.php b/apps/files_trashbin/l10n/vi.php index 81a0cf14bcc48d4eb9abb7426b71b7827363d5e5..57c82cea5f75d99f206278464948e2c40d745b46 100644 --- a/apps/files_trashbin/l10n/vi.php +++ b/apps/files_trashbin/l10n/vi.php @@ -4,12 +4,12 @@ $TRANSLATIONS = array( "Couldn't restore %s" => "Không thể khôi phục %s", "Deleted files" => "File đã bị xóa", "Error" => "Lỗi", +"Deleted Files" => "File đã xóa", "restored" => "khôi phục", "Nothing in here. Your trash bin is empty!" => "Không có gì ở đây. Thùng rác của bạn rỗng!", "Name" => "Tên", "Restore" => "Khôi phục", "Deleted" => "Đã xóa", -"Delete" => "Xóa", -"Deleted Files" => "File đã xóa" +"Delete" => "Xóa" ); $PLURAL_FORMS = "nplurals=1; plural=0;"; diff --git a/apps/files_trashbin/l10n/zh_CN.php b/apps/files_trashbin/l10n/zh_CN.php index 4c61aef33b16124a406fa0f02e199690502343fa..ef6a63b29593e72ddd96e05c06cdc842a607de08 100644 --- a/apps/files_trashbin/l10n/zh_CN.php +++ b/apps/files_trashbin/l10n/zh_CN.php @@ -4,12 +4,12 @@ $TRANSLATIONS = array( "Couldn't restore %s" => "无法恢复%s", "Deleted files" => "已删除文件", "Error" => "错误", +"Deleted Files" => "已删除文件", "restored" => "已恢复", "Nothing in here. Your trash bin is empty!" => "这里没有东西. 你的回收站是空的!", "Name" => "名称", "Restore" => "恢复", "Deleted" => "已删除", -"Delete" => "删除", -"Deleted Files" => "已删除文件" +"Delete" => "删除" ); $PLURAL_FORMS = "nplurals=1; plural=0;"; diff --git a/apps/files_trashbin/l10n/zh_TW.php b/apps/files_trashbin/l10n/zh_TW.php index 65b954e2c631d9f9564520777ad114b2a2b5e642..c42d70790e92e3f2e5cf5a060288a668f00f0d7b 100644 --- a/apps/files_trashbin/l10n/zh_TW.php +++ b/apps/files_trashbin/l10n/zh_TW.php @@ -4,12 +4,12 @@ $TRANSLATIONS = array( "Couldn't restore %s" => "無法還原 %s", "Deleted files" => "回收桶", "Error" => "錯誤", +"Deleted Files" => "已刪除的檔案", "restored" => "已還原", "Nothing in here. Your trash bin is empty!" => "您的回收桶是空的!", "Name" => "名稱", "Restore" => "還原", "Deleted" => "已刪除", -"Delete" => "刪除", -"Deleted Files" => "已刪除的檔案" +"Delete" => "刪除" ); $PLURAL_FORMS = "nplurals=1; plural=0;"; diff --git a/apps/files_trashbin/lib/trashbin.php b/apps/files_trashbin/lib/trashbin.php index 7b14a4ec0816ddae30f00be3bdb4c5c0101c8239..9b931333b7f729542c2c87bd539ea45a410d6588 100644 --- a/apps/files_trashbin/lib/trashbin.php +++ b/apps/files_trashbin/lib/trashbin.php @@ -824,13 +824,15 @@ class Trashbin { $matches = glob($escapedVersionsName . '*'); } - foreach ($matches as $ma) { - if ($timestamp) { - $parts = explode('.v', substr($ma, 0, $offset)); - $versions[] = (end($parts)); - } else { - $parts = explode('.v', $ma); - $versions[] = (end($parts)); + if (is_array($matches)) { + foreach ($matches as $ma) { + if ($timestamp) { + $parts = explode('.v', substr($ma, 0, $offset)); + $versions[] = (end($parts)); + } else { + $parts = explode('.v', $ma); + $versions[] = (end($parts)); + } } } return $versions; @@ -921,13 +923,11 @@ class Trashbin { public static function isEmpty($user) { $view = new \OC\Files\View('/' . $user . '/files_trashbin'); - $dh = $view->opendir('/files'); - if (!$dh) { - return false; - } - while ($file = readdir($dh)) { - if ($file !== '.' and $file !== '..') { - return false; + if ($view->is_dir('/files') && $dh = $view->opendir('/files')) { + while ($file = readdir($dh)) { + if ($file !== '.' and $file !== '..') { + return false; + } } } return true; diff --git a/apps/files_versions/l10n/ast.php b/apps/files_versions/l10n/ast.php index 4869d70b54d5d09cb4958d2fe5b78da4cef1d6e4..31e6e72b2d7e6aa7e1eb1d936550e8de8b45a1a8 100644 --- a/apps/files_versions/l10n/ast.php +++ b/apps/files_versions/l10n/ast.php @@ -1,6 +1,8 @@ "Nun pudo revertise: %s", "Versions" => "Versiones", +"Failed to revert {file} to revision {timestamp}." => "Fallu al revertir {file} a la revisión {timestamp}.", "More versions..." => "Más versiones...", "No other versions available" => "Nun hai otres versiones disponibles", "Restore" => "Restaurar" diff --git a/apps/user_ldap/group_ldap.php b/apps/user_ldap/group_ldap.php index 4f2424d9531671d358ac84f6976bf3717b2757bd..3b5f377e1085f2431b9e9ab42d2996c62c335b3f 100644 --- a/apps/user_ldap/group_ldap.php +++ b/apps/user_ldap/group_ldap.php @@ -88,6 +88,10 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface { return $isInGroup; } + /** + * @param string $dnGroup + * @param array|null &$seen + */ private function _groupMembers($dnGroup, &$seen = null) { if ($seen === null) { $seen = array(); @@ -163,6 +167,10 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface { return $groups; } + /** + * @param string $dn + * @param array|null &$seen + */ private function getGroupsByMember($dn, &$seen = null) { if ($seen === null) { $seen = array(); @@ -276,6 +284,84 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface { return $groupUsers; } + /** + * @brief returns the number of users in a group, who match the search term + * @param string the internal group name + * @param string optional, a search string + * @returns int | bool + */ + public function countUsersInGroup($gid, $search = '') { + $cachekey = 'countUsersInGroup-'.$gid.'-'.$search; + if(!$this->enabled || !$this->groupExists($gid)) { + return false; + } + $groupUsers = $this->access->connection->getFromCache($cachekey); + if(!is_null($groupUsers)) { + return $groupUsers; + } + + $groupDN = $this->access->groupname2dn($gid); + if(!$groupDN) { + // group couldn't be found, return empty resultset + $this->access->connection->writeToCache($cachekey, false); + return false; + } + + $members = array_keys($this->_groupMembers($groupDN)); + if(!$members) { + //in case users could not be retrieved, return empty resultset + $this->access->connection->writeToCache($cachekey, false); + return false; + } + + if(empty($search)) { + $groupUsers = count($members); + $this->access->connection->writeToCache($cachekey, $groupUsers); + return $groupUsers; + } + $isMemberUid = + (strtolower($this->access->connection->ldapGroupMemberAssocAttr) + === 'memberuid'); + + //we need to apply the search filter + //alternatives that need to be checked: + //a) get all users by search filter and array_intersect them + //b) a, but only when less than 1k 10k ?k users like it is + //c) put all DNs|uids in a LDAP filter, combine with the search string + // and let it count. + //For now this is not important, because the only use of this method + //does not supply a search string + $groupUsers = array(); + foreach($members as $member) { + if($isMemberUid) { + //we got uids, need to get their DNs to 'tranlsate' them to usernames + $filter = $this->access->combineFilterWithAnd(array( + \OCP\Util::mb_str_replace('%uid', $member, + $this->access->connection->ldapLoginFilter, 'UTF-8'), + $this->access->getFilterPartForUserSearch($search) + )); + $ldap_users = $this->access->fetchListOfUsers($filter, 'dn'); + if(count($ldap_users) < 1) { + continue; + } + $groupUsers[] = $this->access->dn2username($ldap_users[0]); + } else { + //we need to apply the search filter now + if(!$this->access->readAttribute($member, + $this->access->connection->ldapUserDisplayName, + $this->access->getFilterPartForUserSearch($search))) { + continue; + } + // dn2username will also check if the users belong to the allowed base + if($ocname = $this->access->dn2username($member)) { + $groupUsers[] = $ocname; + } + } + } + + return count($groupUsers); + } + /** * @brief get a list of all display names in a group * @returns array with display names (value) and user ids(key) @@ -376,6 +462,9 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface { return $allGroups; } + /** + * @param string $group + */ public function groupMatchesFilter($group) { return (strripos($group, $this->groupSearch) !== false); } @@ -418,6 +507,9 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface { * compared with OC_USER_BACKEND_CREATE_USER etc. */ public function implementsActions($actions) { - return (bool)(OC_GROUP_BACKEND_GET_DISPLAYNAME & $actions); + return (bool)(( + OC_GROUP_BACKEND_GET_DISPLAYNAME + | OC_GROUP_BACKEND_COUNT_USERS + ) & $actions); } } diff --git a/apps/user_ldap/group_proxy.php b/apps/user_ldap/group_proxy.php index 4404bd7fe3a0567f4deba34e1d1591ea7f090ed0..c00097362399bec634a33d4bb2dd4e723ba0d1ce 100644 --- a/apps/user_ldap/group_proxy.php +++ b/apps/user_ldap/group_proxy.php @@ -144,6 +144,17 @@ class Group_Proxy extends lib\Proxy implements \OCP\GroupInterface { return $users; } + /** + * @brief returns the number of users in a group, who match the search term + * @param string the internal group name + * @param string optional, a search string + * @returns int | bool + */ + public function countUsersInGroup($gid, $search = '') { + return $this->handleRequest( + $gid, 'countUsersInGroup', array($gid, $search)); + } + /** * @brief get a list of all display names in a group * @returns array with display names (value) and user ids(key) diff --git a/apps/user_ldap/js/ldapFilter.js b/apps/user_ldap/js/ldapFilter.js new file mode 100644 index 0000000000000000000000000000000000000000..df3bd67aec2cc4d6e1d0a8b1b3387ec0603c2af5 --- /dev/null +++ b/apps/user_ldap/js/ldapFilter.js @@ -0,0 +1,100 @@ +/* global LdapWizard */ + +function LdapFilter(target) { + this.locked = true; + this.target = false; + this.mode = LdapWizard.filterModeAssisted; + this.lazyRunCompose = false; + + if( target === 'User' || + target === 'Login' || + target === 'Group') { + this.target = target; + this.determineMode(); + } +} + +LdapFilter.prototype.compose = function() { + var action; + + if(this.locked) { + this.lazyRunCompose = true; + return false; + } + + if(this.target === 'User') { + action = 'getUserListFilter'; + } else if(this.target === 'Login') { + action = 'getUserLoginFilter'; + } else if(this.target === 'Group') { + action = 'getGroupFilter'; + } + + if(!$('#raw'+this.target+'FilterContainer').hasClass('invisible')) { + //Raw filter editing, i.e. user defined filter, don't compose + return; + } + + var param = 'action='+action+ + '&ldap_serverconfig_chooser='+ + encodeURIComponent($('#ldap_serverconfig_chooser').val()); + + var filter = this; + + LdapWizard.ajax(param, + function(result) { + LdapWizard.applyChanges(result); + if(filter.target === 'User') { + LdapWizard.countUsers(); + } else if(filter.target === 'Group') { + LdapWizard.countGroups(); + LdapWizard.detectGroupMemberAssoc(); + } + }, + function () { + console.log('LDAP Wizard: could not compose filter. '+ + 'Please check owncloud.log'); + } + ); +}; + +LdapFilter.prototype.determineMode = function() { + var param = 'action=get'+encodeURIComponent(this.target)+'FilterMode'+ + '&ldap_serverconfig_chooser='+ + encodeURIComponent($('#ldap_serverconfig_chooser').val()); + + var filter = this; + LdapWizard.ajax(param, + function(result) { + var property = 'ldap' + filter.target + 'FilterMode'; + filter.mode = parseInt(result.changes[property], 10); + if(filter.mode === LdapWizard.filterModeRaw && + $('#raw'+filter.target+'FilterContainer').hasClass('invisible')) { + LdapWizard['toggleRaw'+filter.target+'Filter'](); + } else if(filter.mode === LdapWizard.filterModeAssisted && + !$('#raw'+filter.target+'FilterContainer').hasClass('invisible')) { + LdapWizard['toggleRaw'+filter.target+'Filter'](); + } else { + console.log('LDAP Wizard determineMode: returned mode was »' + + filter.mode + '« of type ' + typeof filter.mode); + } + filter.unlock(); + }, + function () { + //on error case get back to default i.e. Assisted + if(!$('#raw'+filter.target+'FilterContainer').hasClass('invisible')) { + LdapWizard['toggleRaw'+filter.target+'Filter'](); + filter.mode = LdapWizard.filterModeAssisted; + } + filter.unlock(); + } + ); +}; + +LdapFilter.prototype.unlock = function() { + this.locked = false; + if(this.lazyRunCompose) { + this.lazyRunCompose = false; + this.compose(); + } +}; diff --git a/apps/user_ldap/js/settings.js b/apps/user_ldap/js/settings.js index 792638f2b58cab8a65c89c8afff66c988e432363..fca2dc13d1518e7a5a30b92cb1b398f1fc625b41 100644 --- a/apps/user_ldap/js/settings.js +++ b/apps/user_ldap/js/settings.js @@ -14,7 +14,7 @@ var LdapConfiguration = { //deal with Checkboxes if($(elementID).is('input[type=checkbox]')) { - if(parseInt(configvalue) === 1) { + if(parseInt(configvalue, 10) === 1) { $(elementID).attr('checked', 'checked'); } else { $(elementID).removeAttr('checked'); @@ -145,6 +145,9 @@ var LdapWizard = { spinner: '', filterModeAssisted: 0, filterModeRaw: 1, + userFilter: false, + loginFilter: false, + groupFilter: false, ajax: function(param, fnOnSuccess, fnOnError) { $.post( @@ -276,41 +279,6 @@ var LdapWizard = { } }, - composeFilter: function(type) { - subject = type.charAt(0).toUpperCase() + type.substr(1); - if(!$('#raw'+subject+'FilterContainer').hasClass('invisible')) { - //Raw filter editing, i.e. user defined filter, don't compose - return; - } - - if(type == 'user') { - action = 'getUserListFilter'; - } else if(type == 'login') { - action = 'getUserLoginFilter'; - } else if(type == 'group') { - action = 'getGroupFilter'; - } - - param = 'action='+action+ - '&ldap_serverconfig_chooser='+ - encodeURIComponent($('#ldap_serverconfig_chooser').val()); - - LdapWizard.ajax(param, - function(result) { - LdapWizard.applyChanges(result); - if(type == 'user') { - LdapWizard.countUsers(); - } else if(type == 'group') { - LdapWizard.countGroups(); - LdapWizard.detectGroupMemberAssoc(); - } - }, - function (result) { - // error handling - } - ); - }, - controlBack: function() { curTabIndex = $('#ldapSettings').tabs('option', 'active'); if(curTabIndex == 0) { @@ -510,7 +478,7 @@ var LdapWizard = { }, functionalityCheck: function() { - //criterias to enable the connection: + //criteria to enable the connection: // - host, port, basedn, user filter, login filter host = $('#ldap_host').val(); port = $('#ldap_port').val(); @@ -560,7 +528,7 @@ var LdapWizard = { }, initGroupFilter: function() { - LdapWizard.regardFilterMode('Group'); + LdapWizard.groupFilter = new LdapFilter('Group'); LdapWizard.findObjectClasses('ldap_groupfilter_objectclass', 'Group'); LdapWizard.findAvailableGroups('ldap_groupfilter_groups', 'Groups'); LdapWizard.countGroups(); @@ -569,13 +537,13 @@ var LdapWizard = { /** init login filter tab section **/ initLoginFilter: function() { - LdapWizard.regardFilterMode('Login'); + LdapWizard.loginFilter = new LdapFilter('Login'); LdapWizard.findAttributes(); }, postInitLoginFilter: function() { if($('#rawLoginFilterContainer').hasClass('invisible')) { - LdapWizard.composeFilter('login'); + LdapWizard.loginFilter.compose(); } }, @@ -588,7 +556,7 @@ var LdapWizard = { noneSelectedText: caption, click: function(event, ui) { LdapWizard.saveMultiSelect(id, - $('#'+id).multiselect("getChecked")); + $('#'+id).multiselect("getChecked")); } }); }, @@ -601,15 +569,15 @@ var LdapWizard = { initUserFilter: function() { LdapWizard.userFilterObjectClassesHasRun = false; LdapWizard.userFilterAvailableGroupsHasRun = false; - LdapWizard.regardFilterMode('User'); + LdapWizard.userFilter = new LdapFilter('User'); LdapWizard.findObjectClasses('ldap_userfilter_objectclass', 'User'); LdapWizard.findAvailableGroups('ldap_userfilter_groups', 'Users'); }, postInitUserFilter: function() { - if(LdapWizard.userFilterObjectClassesHasRun - && LdapWizard.userFilterAvailableGroupsHasRun) { - LdapWizard.composeFilter('user'); + if(LdapWizard.userFilterObjectClassesHasRun && + LdapWizard.userFilterAvailableGroupsHasRun) { + LdapWizard.userFilter.compose(); LdapWizard.countUsers(); } }, @@ -658,7 +626,7 @@ var LdapWizard = { if(triggerObj.id == 'ldap_loginfilter_username' || triggerObj.id == 'ldap_loginfilter_email') { - LdapWizard.composeFilter('login'); + LdapWizard.loginFilter.compose(); } if($('#ldapSettings').tabs('option', 'active') == 0) { @@ -667,32 +635,6 @@ var LdapWizard = { } }, - regardFilterMode: function(subject) { - param = 'action=get'+encodeURIComponent(subject)+'FilterMode'+ - '&ldap_serverconfig_chooser='+ - encodeURIComponent($('#ldap_serverconfig_chooser').val()); - - LdapWizard.ajax(param, - function(result) { - property = 'ldap' + subject + 'FilterMode'; - mode = result.changes[property]; - if(mode == LdapWizard.filterModeRaw - && $('#raw'+subject+'FilterContainer').hasClass('invisible')) { - LdapWizard['toggleRaw'+subject+'Filter'](); - } else if(mode == LdapWizard.filterModeAssisted - && !$('#raw'+subject+'FilterContainer').hasClass('invisible')) { - LdapWizard['toggleRaw'+subject+'Filter'](); - } - }, - function (result) { - //on error case get back to default i.e. Assisted - if(!$('#raw'+subject+'FilterContainer').hasClass('invisible')) { - LdapWizard['toggleRaw'+subject+'Filter'](); - } - } - ); - }, - save: function(inputObj) { if(LdapWizard.blacklistRemove(inputObj.id)) { return; @@ -714,15 +656,15 @@ var LdapWizard = { LdapWizard._save($('#'+originalObj)[0], $.trim(values)); if(originalObj == 'ldap_userfilter_objectclass' || originalObj == 'ldap_userfilter_groups') { - LdapWizard.composeFilter('user'); + LdapWizard.userFilter.compose(); //when user filter is changed afterwards, login filter needs to //be adjusted, too - LdapWizard.composeFilter('login'); + LdapWizard.loginFilter.compose(); } else if(originalObj == 'ldap_loginfilter_attributes') { - LdapWizard.composeFilter('login'); + LdapWizard.loginFilter.compose(); } else if(originalObj == 'ldap_groupfilter_objectclass' || originalObj == 'ldap_groupfilter_groups') { - LdapWizard.composeFilter('group'); + LdapWizard.groupFilter.compose(); } }, @@ -778,10 +720,10 @@ var LdapWizard = { LdapWizard._save({ id: modeKey }, LdapWizard.filterModeAssisted); if(moc.indexOf('user') >= 0) { LdapWizard.blacklistRemove('ldap_userlist_filter'); - LdapWizard.composeFilter('user'); + LdapWizard.userFilter.compose(); } else { LdapWizard.blacklistRemove('ldap_group_filter'); - LdapWizard.composeFilter('group'); + LdapWizard.groupFilter.compose(); } } }, @@ -815,7 +757,7 @@ var LdapWizard = { $('#ldap_loginfilter_username').prop('disabled', property); LdapWizard._save({ id: 'ldapLoginFilterMode' }, mode); if(action == 'enable') { - LdapWizard.composeFilter('login'); + LdapWizard.loginFilter.compose(); } }, diff --git a/apps/user_ldap/l10n/af_ZA.php b/apps/user_ldap/l10n/af_ZA.php index 130e471e0e41d3baa6dae04d7d72af48406dbb8c..483a30b8e482b317516a9f61f768816b01872fe0 100644 --- a/apps/user_ldap/l10n/af_ZA.php +++ b/apps/user_ldap/l10n/af_ZA.php @@ -3,6 +3,7 @@ $TRANSLATIONS = array( "_%s group found_::_%s groups found_" => array("",""), "_%s user found_::_%s users found_" => array("",""), "Help" => "Hulp", -"Password" => "Wagwoord" +"Password" => "Wagwoord", +"Continue" => "Gaan voort" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/apps/user_ldap/l10n/ast.php b/apps/user_ldap/l10n/ast.php index e8e2d779d19f582082da2b56c77cbfb07b64bbed..032de8aedbcd77be3b4572351c0fdfa5ee697a5e 100644 --- a/apps/user_ldap/l10n/ast.php +++ b/apps/user_ldap/l10n/ast.php @@ -1,10 +1,41 @@ "La configuración nun ye válida. Por favor, écha-y un güeyu a los rexistros pa más detalles.", +"No action specified" => "Nun s'especificó l'aición", +"No configuration specified" => "Nun s'especificó la configuración", +"No data specified" => "Nun s'especificaron los datos", +" Could not set configuration %s" => "Nun pudo afitase la configuración %s", "Deletion failed" => "Falló'l borráu", -"_%s group found_::_%s groups found_" => array("",""), -"_%s user found_::_%s users found_" => array("",""), +"Keep settings?" => "¿Caltener los axustes?", +"Cannot add server configuration" => "Nun pue amestase la configuración del sirvidor", +"Success" => "Con ésitu", +"Error" => "Fallu", +"Configuration incorrect" => "Configuración incorreuta", +"Configuration incomplete" => "Configuración incompleta", +"Select groups" => "Esbillar grupos", +"Select attributes" => "Esbillar atributos", +"_%s group found_::_%s groups found_" => array("%s grupu alcontráu","%s grupos alcontraos"), +"_%s user found_::_%s users found_" => array("%s usuariu alcontráu","%s usuarios alcontraos"), +"Could not find the desired feature" => "Nun pudo alcontrase la carauterística deseyada", "Save" => "Guardar", +"groups found" => "grupos alcontraos", +"Users login with this attribute:" => "Aniciu de sesión d'usuarios con esti atributu:", +"LDAP Username:" => "Nome d'usuariu LDAP", +"Other Attributes:" => "Otros atributos:", +"Add Server Configuration" => "Amestar configuración del sirvidor", +"Host" => "Equipu", +"Port" => "Puertu", "Password" => "Contraseña", -"Continue" => "Continuar" +"For anonymous access, leave DN and Password empty." => "Pa un accesu anónimu, dexar el DN y la contraseña baleros.", +"users found" => "usuarios alcontraos", +"Continue" => "Continuar", +"Connection Settings" => "Axustes de conexón", +"Configuration Active" => "Configuración activa", +"When unchecked, this configuration will be skipped." => "Cuando nun tea conseñáu, saltaráse esta configuración.", +"Disable Main Server" => "Deshabilitar sirvidor principal", +"Turn off SSL certificate validation." => "Apagar la validación del certificáu SSL.", +"Directory Settings" => "Axustes del direutoriu", +"in bytes" => "en bytes", +"Internal Username" => "Nome d'usuariu internu" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/apps/user_ldap/l10n/bn_BD.php b/apps/user_ldap/l10n/bn_BD.php index 0b43a27df940d9ecd1f4efee7bcb5758b2e3984c..90f7b572a952c71e5c0476d57e6a9b14ae23d623 100644 --- a/apps/user_ldap/l10n/bn_BD.php +++ b/apps/user_ldap/l10n/bn_BD.php @@ -13,7 +13,6 @@ $TRANSLATIONS = array( "Password" => "কূটশব্দ", "For anonymous access, leave DN and Password empty." => "অজ্ঞাতকুলশীল অধিগমনের জন্য DN এবং কূটশব্দটি ফাঁকা রাখুন।", "You can specify Base DN for users and groups in the Advanced tab" => "সুচারু ট্যঅবে গিয়ে আপনি ব্যবহারকারি এবং গোষ্ঠীসমূহের জন্য ভিত্তি DN নির্ধারণ করতে পারেন।", -"Case insensitve LDAP server (Windows)" => "বর্ণ অসংবেদী LDAP সার্ভার (উইন্ডোজ)", "Turn off SSL certificate validation." => "SSL সনদপত্র যাচাইকরণ বন্ধ রাক।", "in seconds. A change empties the cache." => "সেকেন্ডে। কোন পরিবর্তন ক্যাসে খালি করবে।", "User Display Name Field" => "ব্যবহারকারীর প্রদর্শিতব্য নামের ক্ষেত্র", diff --git a/apps/user_ldap/l10n/ca.php b/apps/user_ldap/l10n/ca.php index 940c6ea8b6608735044f16b0acffab1749e9ace7..c8542586f82bc091ad82cd1f9e364c9b86054c2b 100644 --- a/apps/user_ldap/l10n/ca.php +++ b/apps/user_ldap/l10n/ca.php @@ -70,7 +70,7 @@ $TRANSLATIONS = array( "Backup (Replica) Port" => "Port de la còpia de seguretat (rèplica)", "Disable Main Server" => "Desactiva el servidor principal", "Only connect to the replica server." => "Connecta només al servidor rèplica.", -"Case insensitve LDAP server (Windows)" => "Servidor LDAP sense distinció entre majúscules i minúscules (Windows)", +"Case insensitive LDAP server (Windows)" => "Servidor LDAP sense distinció entre majúscules i minúscules (Windows)", "Turn off SSL certificate validation." => "Desactiva la validació de certificat SSL.", "Not recommended, use it for testing only! If connection only works with this option, import the LDAP server's SSL certificate in your %s server." => "No es recomana, useu-ho només com a prova! Importeu el certificat SSL del servidor LDAP al servidor %s només si la connexió funciona amb aquesta opció.", "Cache Time-To-Live" => "Memòria de cau Time-To-Live", @@ -90,6 +90,8 @@ $TRANSLATIONS = array( "Group-Member association" => "Associació membres-grup", "Nested Groups" => "Grups imbricats", "When switched on, groups that contain groups are supported. (Only works if the group member attribute contains DNs.)" => "Quan està activat, els grups que contenen grups estan permesos. (Només funciona si l'atribut del grup membre conté DNs.)", +"Paging chunksize" => "Mida de la pàgina", +"Chunksize used for paged LDAP searches that may return bulky results like user or group enumeration. (Setting it 0 disables paged LDAP searches in those situations.)" => "Mida usada per cerques LDAP paginades que podrien retornar respostes de volcat com enumeració d'usuari o grup. (Establint-ho a 0 desactiva les cerques LDAP paginades en aquestes situacions.)", "Special Attributes" => "Atributs especials", "Quota Field" => "Camp de quota", "Quota Default" => "Quota per defecte", diff --git a/apps/user_ldap/l10n/cs_CZ.php b/apps/user_ldap/l10n/cs_CZ.php index 536834ee8b54053187589299576725a040b10cfc..b7dfa5a4c414865e1376026cb52eb7bc0784ab4e 100644 --- a/apps/user_ldap/l10n/cs_CZ.php +++ b/apps/user_ldap/l10n/cs_CZ.php @@ -70,7 +70,7 @@ $TRANSLATIONS = array( "Backup (Replica) Port" => "Záložní (kopie) port", "Disable Main Server" => "Zakázat hlavní server", "Only connect to the replica server." => "Připojit jen k záložnímu serveru.", -"Case insensitve LDAP server (Windows)" => "LDAP server nerozlišující velikost znaků (Windows)", +"Case insensitive LDAP server (Windows)" => "LDAP server nerozlišující velikost znaků (Windows)", "Turn off SSL certificate validation." => "Vypnout ověřování SSL certifikátu.", "Not recommended, use it for testing only! If connection only works with this option, import the LDAP server's SSL certificate in your %s server." => "Nedoporučuje se, určeno pouze k testovacímu použití. Pokud spojení funguje jen s touto volbou, importujte SSL certifikát vašeho LDAP serveru na server %s.", "Cache Time-To-Live" => "TTL vyrovnávací paměti", diff --git a/apps/user_ldap/l10n/da.php b/apps/user_ldap/l10n/da.php index e375598c9bd1cd2e8b73dca48a72912d60b7f98d..8264a18116c9292427e8189f9e8653379ab2998b 100644 --- a/apps/user_ldap/l10n/da.php +++ b/apps/user_ldap/l10n/da.php @@ -35,7 +35,6 @@ $TRANSLATIONS = array( "Backup (Replica) Port" => "Backup (Replika) Port", "Disable Main Server" => "Deaktiver Hovedserver", "Only connect to the replica server." => "Forbind kun til replika serveren.", -"Case insensitve LDAP server (Windows)" => "Ikke versalfølsom LDAP server (Windows)", "Turn off SSL certificate validation." => "Deaktiver SSL certifikat validering", "Cache Time-To-Live" => "Cache Time-To-Live", "User Display Name Field" => "User Display Name Field", diff --git a/apps/user_ldap/l10n/de.php b/apps/user_ldap/l10n/de.php index 8dad9b34539f48c2e573b1403d008093e2a63993..43336520739fd3d6db89f89bfbd459902a1799e4 100644 --- a/apps/user_ldap/l10n/de.php +++ b/apps/user_ldap/l10n/de.php @@ -70,7 +70,7 @@ $TRANSLATIONS = array( "Backup (Replica) Port" => "Backup Port", "Disable Main Server" => "Hauptserver deaktivieren", "Only connect to the replica server." => "Nur zum Replikat-Server verbinden.", -"Case insensitve LDAP server (Windows)" => "LDAP-Server (Windows: Groß- und Kleinschreibung bleibt unbeachtet)", +"Case insensitive LDAP server (Windows)" => "LDAP-Server (Windows - Groß- und Kleinschreibung bleibt unbeachtet)", "Turn off SSL certificate validation." => "Schalte die SSL-Zertifikatsprüfung aus.", "Not recommended, use it for testing only! If connection only works with this option, import the LDAP server's SSL certificate in your %s server." => "Nur für Testzwecke geeignet, sollte Standardmäßig nicht verwendet werden. Falls die Verbindung nur mit dieser Option funktioniert, importiere das SSL-Zertifikat des LDAP-Servers in deinen %s Server.", "Cache Time-To-Live" => "Speichere Time-To-Live zwischen", @@ -90,6 +90,8 @@ $TRANSLATIONS = array( "Group-Member association" => "Assoziation zwischen Gruppe und Benutzer", "Nested Groups" => "Eingebundene Gruppen", "When switched on, groups that contain groups are supported. (Only works if the group member attribute contains DNs.)" => "Wenn aktiviert, werden Gruppen, die Gruppen enthalten, unterstützt. (Funktioniert nur, wenn das Merkmal des Gruppenmitgliedes den Domain-Namen enthält.)", +"Paging chunksize" => "Seitenstücke (Paging chunksize)", +"Chunksize used for paged LDAP searches that may return bulky results like user or group enumeration. (Setting it 0 disables paged LDAP searches in those situations.)" => "Die Größe der Seitenstücke (Chunksize) wird für seitenbezogene LDAP-Suchen verwendet die sehr viele Ergebnisse z.B. Nutzer- und Gruppenaufzählungen liefern. (Die Einstellung 0 deaktiviert das seitenbezogene LDAP-Suchen in diesen Situationen)", "Special Attributes" => "Spezielle Eigenschaften", "Quota Field" => "Kontingent Feld", "Quota Default" => "Standard Kontingent", diff --git a/apps/user_ldap/l10n/de_CH.php b/apps/user_ldap/l10n/de_CH.php index 5f8e2907f0747fcd9f914d384a64f00e57ce6137..f60d425a2e6adb04dbd3af4a96913ff116b2a8fb 100644 --- a/apps/user_ldap/l10n/de_CH.php +++ b/apps/user_ldap/l10n/de_CH.php @@ -42,7 +42,6 @@ $TRANSLATIONS = array( "Backup (Replica) Port" => "Backup Port", "Disable Main Server" => "Hauptserver deaktivieren", "Only connect to the replica server." => "Nur zum Replikat-Server verbinden.", -"Case insensitve LDAP server (Windows)" => "LDAP-Server (Windows: Gross- und Kleinschreibung bleibt unbeachtet)", "Turn off SSL certificate validation." => "Schalten Sie die SSL-Zertifikatsprüfung aus.", "Not recommended, use it for testing only! If connection only works with this option, import the LDAP server's SSL certificate in your %s server." => "Nur für Testzwecke geeignet, sollte Standardmäßig nicht verwendet werden. Falls die Verbindung nur mit dieser Option funktioniert, importieren Sie das SSL-Zertifikat des LDAP-Servers in Ihren %s Server.", "Cache Time-To-Live" => "Speichere Time-To-Live zwischen", diff --git a/apps/user_ldap/l10n/de_DE.php b/apps/user_ldap/l10n/de_DE.php index 6e6e29ea3db0cb18c1383199886410982622400c..21c149d1a1d243ca47bc88ce6a50507abc293cf8 100644 --- a/apps/user_ldap/l10n/de_DE.php +++ b/apps/user_ldap/l10n/de_DE.php @@ -70,7 +70,7 @@ $TRANSLATIONS = array( "Backup (Replica) Port" => "Backup Port", "Disable Main Server" => "Hauptserver deaktivieren", "Only connect to the replica server." => "Nur zum Replikat-Server verbinden.", -"Case insensitve LDAP server (Windows)" => "LDAP-Server (Windows: Groß- und Kleinschreibung bleibt unbeachtet)", +"Case insensitive LDAP server (Windows)" => "LDAP-Server (Windows: Groß- und Kleinschreibung bleibt unbeachtet)", "Turn off SSL certificate validation." => "Schalten Sie die SSL-Zertifikatsprüfung aus.", "Not recommended, use it for testing only! If connection only works with this option, import the LDAP server's SSL certificate in your %s server." => "Nur für Testzwecke geeignet, sollte Standardmäßig nicht verwendet werden. Falls die Verbindung nur mit dieser Option funktioniert, importieren Sie das SSL-Zertifikat des LDAP-Servers in Ihren %s Server.", "Cache Time-To-Live" => "Speichere Time-To-Live zwischen", @@ -90,6 +90,8 @@ $TRANSLATIONS = array( "Group-Member association" => "Assoziation zwischen Gruppe und Benutzer", "Nested Groups" => "Eingebundene Gruppen", "When switched on, groups that contain groups are supported. (Only works if the group member attribute contains DNs.)" => "Wenn aktiviert, werden Gruppen, die Gruppen enthalten, unterstützt. (Funktioniert nur, wenn das Merkmal des Gruppenmitgliedes den Domain-Namen enthält.)", +"Paging chunksize" => "Seitenstücke (Paging chunksize)", +"Chunksize used for paged LDAP searches that may return bulky results like user or group enumeration. (Setting it 0 disables paged LDAP searches in those situations.)" => "Die Größe der Seitenstücke (Chunksize) wird für seitenbezogene LDAP-Suchen verwendet die sehr viele Ergebnisse z.B. Nutzer- und Gruppenaufzählungen liefern. (Die Einstellung 0 deaktiviert das seitenbezogene LDAP-Suchen in diesen Situationen)", "Special Attributes" => "Spezielle Eigenschaften", "Quota Field" => "Kontingent-Feld", "Quota Default" => "Standard-Kontingent", diff --git a/apps/user_ldap/l10n/el.php b/apps/user_ldap/l10n/el.php index 926419d86b2f48046322b106d8b1144145a6ef67..cfc5286a4084bbe498f91376d62aa2a0db1ba06b 100644 --- a/apps/user_ldap/l10n/el.php +++ b/apps/user_ldap/l10n/el.php @@ -70,7 +70,6 @@ $TRANSLATIONS = array( "Backup (Replica) Port" => "Δημιουργία αντιγράφων ασφαλείας (Replica) Υποδοχη", "Disable Main Server" => "Απενεργοποιηση του κεντρικου διακομιστη", "Only connect to the replica server." => "Σύνδεση μόνο με το διακομιστή-αντίγραφο.", -"Case insensitve LDAP server (Windows)" => "LDAP server (Windows) με διάκριση πεζών-ΚΕΦΑΛΑΙΩΝ", "Turn off SSL certificate validation." => "Απενεργοποίηση επικύρωσης πιστοποιητικού SSL.", "Not recommended, use it for testing only! If connection only works with this option, import the LDAP server's SSL certificate in your %s server." => "Δεν προτείνεται, χρησιμοποιείστε το μόνο για δοκιμές! Εάν η σύνδεση λειτουργεί μόνο με αυτή την επιλογή, εισάγετε το πιστοποιητικό SSL του διακομιστή LDAP στο %s διακομιστή σας.", "Cache Time-To-Live" => "Cache Time-To-Live", diff --git a/apps/user_ldap/l10n/en_GB.php b/apps/user_ldap/l10n/en_GB.php index b87c99c135c30f927df0190d04765fec982ccf07..cb0ac1a5497c8c47905b1b4bee2474fd43d2627d 100644 --- a/apps/user_ldap/l10n/en_GB.php +++ b/apps/user_ldap/l10n/en_GB.php @@ -70,7 +70,7 @@ $TRANSLATIONS = array( "Backup (Replica) Port" => "Backup (Replica) Port", "Disable Main Server" => "Disable Main Server", "Only connect to the replica server." => "Only connect to the replica server.", -"Case insensitve LDAP server (Windows)" => "Case insensitve LDAP server (Windows)", +"Case insensitive LDAP server (Windows)" => "Case insensitive LDAP server (Windows)", "Turn off SSL certificate validation." => "Turn off SSL certificate validation.", "Not recommended, use it for testing only! If connection only works with this option, import the LDAP server's SSL certificate in your %s server." => "Not recommended, use it for testing only! If connection only works with this option, import the LDAP server's SSL certificate in your %s server.", "Cache Time-To-Live" => "Cache Time-To-Live", diff --git a/apps/user_ldap/l10n/eo.php b/apps/user_ldap/l10n/eo.php index 0cae524bcc1649c4fb8cb0123857d412cd216154..74928899c8a39623cab319eec90ac84f67d0ed92 100644 --- a/apps/user_ldap/l10n/eo.php +++ b/apps/user_ldap/l10n/eo.php @@ -35,7 +35,6 @@ $TRANSLATIONS = array( "Back" => "Antaŭen", "Connection Settings" => "Agordo de konekto", "Disable Main Server" => "Malkapabligi la ĉefan servilon", -"Case insensitve LDAP server (Windows)" => "LDAP-servilo blinda je litergrandeco (Vindozo)", "Turn off SSL certificate validation." => "Malkapabligi validkontrolon de SSL-atestiloj.", "Cache Time-To-Live" => "Vivotempo de la kaŝmemoro", "in seconds. A change empties the cache." => "sekunde. Ajna ŝanĝo malplenigas la kaŝmemoron.", diff --git a/apps/user_ldap/l10n/es.php b/apps/user_ldap/l10n/es.php index 1821aa8d446a84b2f7f9ad17a490a51ae73f31bc..bb1c9acb2abf5a6cb412ce27631beeeaab56fe70 100644 --- a/apps/user_ldap/l10n/es.php +++ b/apps/user_ldap/l10n/es.php @@ -70,7 +70,7 @@ $TRANSLATIONS = array( "Backup (Replica) Port" => "Puerto para copias de seguridad (Replica)", "Disable Main Server" => "Deshabilitar servidor principal", "Only connect to the replica server." => "Conectar sólo con el servidor de réplica.", -"Case insensitve LDAP server (Windows)" => "Servidor de LDAP no sensible a mayúsculas/minúsculas (Windows)", +"Case insensitive LDAP server (Windows)" => "Servidor de LDAP insensible a mayúsculas/minúsculas (Windows)", "Turn off SSL certificate validation." => "Apagar la validación por certificado SSL.", "Not recommended, use it for testing only! If connection only works with this option, import the LDAP server's SSL certificate in your %s server." => "No se recomienda, ¡utilízalo únicamente para pruebas! Si la conexión únicamente funciona con esta opción, importa el certificado SSL del servidor LDAP en tu servidor %s.", "Cache Time-To-Live" => "Cache TTL", @@ -90,6 +90,8 @@ $TRANSLATIONS = array( "Group-Member association" => "Asociación Grupo-Miembro", "Nested Groups" => "Grupos anidados", "When switched on, groups that contain groups are supported. (Only works if the group member attribute contains DNs.)" => "Cuando se active, se permitirán grupos que contenga otros grupos (solo funciona si el atributo de miembro de grupo contiene DNs).", +"Paging chunksize" => "Tamaño de los fragmentos de paginación", +"Chunksize used for paged LDAP searches that may return bulky results like user or group enumeration. (Setting it 0 disables paged LDAP searches in those situations.)" => "Tamaño de los fragmentos usado para búsquedas LDAP paginadas que pueden devolver resultados voluminosos, como enumeración de usuarios o de grupos. (Si se establece en 0, se deshabilitan las búsquedas LDAP paginadas en esas situaciones.)", "Special Attributes" => "Atributos especiales", "Quota Field" => "Cuota", "Quota Default" => "Cuota por defecto", diff --git a/apps/user_ldap/l10n/es_AR.php b/apps/user_ldap/l10n/es_AR.php index 6323fe0c4adefc3b2f3b7e08e34e1ded523f85f0..4a8047c6d91b52920b45cad39a3ade51af8b150d 100644 --- a/apps/user_ldap/l10n/es_AR.php +++ b/apps/user_ldap/l10n/es_AR.php @@ -33,12 +33,14 @@ $TRANSLATIONS = array( "Save" => "Guardar", "Test Configuration" => "Probar configuración", "Help" => "Ayuda", +"Groups meeting these criteria are available in %s:" => "Los grupos que cumplen con estos criterios están disponibles en %s:", "only those object classes:" => "solo estos objetos de clases:", "only from those groups:" => "solo provenientes de estos grupos:", "Edit raw filter instead" => "Editar filtro en bruto", "Raw LDAP filter" => "Filtro LDAP en bruto", "The filter specifies which LDAP groups shall have access to the %s instance." => "El filtro especifica qué grupos LDAP deben tener acceso a la instancia %s.", "groups found" => "grupos encontrados", +"Users login with this attribute:" => "Los usuarios inician sesión con este atributo:", "LDAP Username:" => "Nombre de usuario LDAP:", "LDAP Email Address:" => "Correo electrónico LDAP:", "Other Attributes:" => "Otros atributos:", @@ -53,6 +55,7 @@ $TRANSLATIONS = array( "For anonymous access, leave DN and Password empty." => "Para acceso anónimo, dejá DN y contraseña vacíos.", "One Base DN per line" => "Una DN base por línea", "You can specify Base DN for users and groups in the Advanced tab" => "Podés especificar el DN base para usuarios y grupos en la pestaña \"Avanzado\"", +"Limit %s access to users meeting these criteria:" => "Limitar acceso %s a los usuarios que cumplen con este criterio:", "The filter specifies which LDAP users shall have access to the %s instance." => "El filtro especifica cuáles usuarios LDAP deben tener acceso a la instancia %s.", "users found" => "usuarios encontrados", "Back" => "Volver", @@ -67,7 +70,7 @@ $TRANSLATIONS = array( "Backup (Replica) Port" => "Puerto para copia de seguridad (réplica)", "Disable Main Server" => "Deshabilitar el Servidor Principal", "Only connect to the replica server." => "Conectarse únicamente al servidor de réplica.", -"Case insensitve LDAP server (Windows)" => "Servidor de LDAP sensible a mayúsculas/minúsculas (Windows)", +"Case insensitive LDAP server (Windows)" => "Servidor de LDAP insensible a mayúsculas/minúsculas (Windows)", "Turn off SSL certificate validation." => "Desactivar la validación por certificado SSL.", "Not recommended, use it for testing only! If connection only works with this option, import the LDAP server's SSL certificate in your %s server." => "No es recomendado, ¡Usalo solamente para pruebas! Si la conexión únicamente funciona con esta opción, importá el certificado SSL del servidor LDAP en tu servidor %s.", "Cache Time-To-Live" => "Tiempo de vida del caché", @@ -85,6 +88,9 @@ $TRANSLATIONS = array( "One Group Base DN per line" => "Una DN base de grupo por línea", "Group Search Attributes" => "Atributos de búsqueda de grupo", "Group-Member association" => "Asociación Grupo-Miembro", +"Nested Groups" => "Grupos Anidados", +"When switched on, groups that contain groups are supported. (Only works if the group member attribute contains DNs.)" => "Cuando se activa, grupos que contienen grupos son soportados. (Solo funciona si el atributo de miembro del grupo contiene DNs)", +"Paging chunksize" => "Tamaño del fragmento de paginación", "Special Attributes" => "Atributos Especiales", "Quota Field" => "Campo de cuota", "Quota Default" => "Cuota por defecto", diff --git a/apps/user_ldap/l10n/es_CR.php b/apps/user_ldap/l10n/es_CR.php new file mode 100644 index 0000000000000000000000000000000000000000..3a1e002311cd9c1851bda1b96f71bc4d3e3e047b --- /dev/null +++ b/apps/user_ldap/l10n/es_CR.php @@ -0,0 +1,6 @@ + array("",""), +"_%s user found_::_%s users found_" => array("","") +); +$PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/apps/user_ldap/l10n/es_MX.php b/apps/user_ldap/l10n/es_MX.php index 09c99209a5578f359210578b477fd9a190d00dd9..bb4c3700ec56d9a374dc11e5e391740cddef7fd7 100644 --- a/apps/user_ldap/l10n/es_MX.php +++ b/apps/user_ldap/l10n/es_MX.php @@ -67,7 +67,6 @@ $TRANSLATIONS = array( "Backup (Replica) Port" => "Puerto para copias de seguridad (Replica)", "Disable Main Server" => "Deshabilitar servidor principal", "Only connect to the replica server." => "Conectar sólo con el servidor de réplica.", -"Case insensitve LDAP server (Windows)" => "Servidor de LDAP no sensible a mayúsculas/minúsculas (Windows)", "Turn off SSL certificate validation." => "Apagar la validación por certificado SSL.", "Not recommended, use it for testing only! If connection only works with this option, import the LDAP server's SSL certificate in your %s server." => "No se recomienda, ¡utilízalo únicamente para pruebas! Si la conexión únicamente funciona con esta opción, importa el certificado SSL del servidor LDAP en tu servidor %s.", "Cache Time-To-Live" => "Cache TTL", diff --git a/apps/user_ldap/l10n/et_EE.php b/apps/user_ldap/l10n/et_EE.php index 9bf3a189efd8700b5def8951ff6dd5f7cd319cf4..11941bf4ac95cbb1ac40f93d3c3c4f91145f837c 100644 --- a/apps/user_ldap/l10n/et_EE.php +++ b/apps/user_ldap/l10n/et_EE.php @@ -70,7 +70,7 @@ $TRANSLATIONS = array( "Backup (Replica) Port" => "Varuserveri (replika) port", "Disable Main Server" => "Ära kasuta peaserverit", "Only connect to the replica server." => "Ühendu ainult replitseeriva serveriga.", -"Case insensitve LDAP server (Windows)" => "Mittetõstutundlik LDAP server (Windows)", +"Case insensitive LDAP server (Windows)" => "Tõusutundetu LDAP server (Windows)", "Turn off SSL certificate validation." => "Lülita SSL sertifikaadi kontrollimine välja.", "Not recommended, use it for testing only! If connection only works with this option, import the LDAP server's SSL certificate in your %s server." => "Pole soovitatav, kasuta seda ainult testimiseks! Kui ühendus toimib ainult selle valikuga, siis impordi LDAP serveri SSL sertifikaat oma %s serverisse.", "Cache Time-To-Live" => "Puhvri iga", diff --git a/apps/user_ldap/l10n/eu.php b/apps/user_ldap/l10n/eu.php index a7bd52e50acadf36101cabd8a29557353be40dc8..c19cb034ad0c3ab0986eca900309dedd8dfd7681 100644 --- a/apps/user_ldap/l10n/eu.php +++ b/apps/user_ldap/l10n/eu.php @@ -61,7 +61,6 @@ $TRANSLATIONS = array( "Backup (Replica) Port" => "Babeskopia (Replica) Ataka", "Disable Main Server" => "Desgaitu Zerbitzari Nagusia", "Only connect to the replica server." => "Konektatu bakarrik erreplika zerbitzarira", -"Case insensitve LDAP server (Windows)" => "Maiuskulak eta minuskulak ezberditzen ez dituen LDAP zerbitzaria (windows)", "Turn off SSL certificate validation." => "Ezgaitu SSL ziurtagirien egiaztapena.", "Cache Time-To-Live" => "Katxearen Bizi-Iraupena", "in seconds. A change empties the cache." => "segundutan. Aldaketak katxea husten du.", diff --git a/apps/user_ldap/l10n/fa.php b/apps/user_ldap/l10n/fa.php index 688a6ee0d42b2958a1dfe3fc81499c810be931ae..62a949f88bd58211b6e82b3a6146bf1b3eb3747a 100644 --- a/apps/user_ldap/l10n/fa.php +++ b/apps/user_ldap/l10n/fa.php @@ -34,7 +34,6 @@ $TRANSLATIONS = array( "Backup (Replica) Host" => "پشتیبان گیری (بدل) میزبان", "Backup (Replica) Port" => "پشتیبان گیری (بدل) پورت", "Disable Main Server" => "غیر فعال کردن سرور اصلی", -"Case insensitve LDAP server (Windows)" => "غیر حساس به بزرگی و کوچکی حروف LDAP سرور (ویندوز)", "Turn off SSL certificate validation." => "غیرفعال کردن اعتبار گواهی نامه SSL .", "Directory Settings" => "تنظیمات پوشه", "User Display Name Field" => "فیلد نام کاربر", diff --git a/apps/user_ldap/l10n/fi_FI.php b/apps/user_ldap/l10n/fi_FI.php index ac1dfcc5ab8ca9d3f6d342a0368cbd0212d17abb..552fd9f923bbf63906083533d0bcf7bd40167651 100644 --- a/apps/user_ldap/l10n/fi_FI.php +++ b/apps/user_ldap/l10n/fi_FI.php @@ -25,7 +25,6 @@ $TRANSLATIONS = array( "Continue" => "Jatka", "Connection Settings" => "Yhteysasetukset", "Disable Main Server" => "Poista pääpalvelin käytöstä", -"Case insensitve LDAP server (Windows)" => "Kirjainkoosta piittamaton LDAP-palvelin (Windows)", "Turn off SSL certificate validation." => "Poista käytöstä SSL-varmenteen vahvistus", "in seconds. A change empties the cache." => "sekunneissa. Muutos tyhjentää välimuistin.", "Directory Settings" => "Hakemistoasetukset", diff --git a/apps/user_ldap/l10n/fr.php b/apps/user_ldap/l10n/fr.php index dbcaadfc2a7ec51d56d9f7a4407291a117f05c3f..44b92077b92de30d7920fbc70bb924b6731304fa 100644 --- a/apps/user_ldap/l10n/fr.php +++ b/apps/user_ldap/l10n/fr.php @@ -70,7 +70,7 @@ $TRANSLATIONS = array( "Backup (Replica) Port" => "Port du serveur de backup (réplique)", "Disable Main Server" => "Désactiver le serveur principal", "Only connect to the replica server." => "Se connecter uniquement au serveur de replica.", -"Case insensitve LDAP server (Windows)" => "Serveur LDAP insensible à la casse (Windows)", +"Case insensitive LDAP server (Windows)" => "Serveur LDAP insensible à la casse (Windows)", "Turn off SSL certificate validation." => "Désactiver la validation du certificat SSL.", "Not recommended, use it for testing only! If connection only works with this option, import the LDAP server's SSL certificate in your %s server." => "Non recommandé, à utiliser à des fins de tests uniquement. Si la connexion ne fonctionne qu'avec cette option, importez le certificat SSL du serveur LDAP dans le serveur %s.", "Cache Time-To-Live" => "Durée de vie du cache", @@ -90,6 +90,8 @@ $TRANSLATIONS = array( "Group-Member association" => "Association groupe-membre", "Nested Groups" => "Groupes imbriqués", "When switched on, groups that contain groups are supported. (Only works if the group member attribute contains DNs.)" => "Si activé, les groupes contenant d'autres groupes sont supportés (fonctionne uniquement si l'attribut membre du groupe contient des DNs).", +"Paging chunksize" => "Dimensionnement des paginations", +"Chunksize used for paged LDAP searches that may return bulky results like user or group enumeration. (Setting it 0 disables paged LDAP searches in those situations.)" => "La taille d'une part (chunksize) est utilisée pour les recherches paginées de LDAP qui peuvent retourner des résultats par lots comme une énumération d'utilisateurs ou groupes. (Configurer à 0 pour désactiver les recherches paginées de LDAP.)", "Special Attributes" => "Attributs spéciaux", "Quota Field" => "Champ du quota", "Quota Default" => "Quota par défaut", diff --git a/apps/user_ldap/l10n/gl.php b/apps/user_ldap/l10n/gl.php index c35244538996ab070706c2cb81974536ea4d9a85..82c484bb95d44eb77786a87a8bc4d98e56fea337 100644 --- a/apps/user_ldap/l10n/gl.php +++ b/apps/user_ldap/l10n/gl.php @@ -70,7 +70,7 @@ $TRANSLATIONS = array( "Backup (Replica) Port" => "Porto da copia de seguranza (Réplica)", "Disable Main Server" => "Desactivar o servidor principal", "Only connect to the replica server." => "Conectar só co servidor de réplica.", -"Case insensitve LDAP server (Windows)" => "Servidor LDAP que non distingue entre maiúsculas e minúsculas (Windows)", +"Case insensitive LDAP server (Windows)" => "Servidor LDAP non sensíbel a maiúsculas (Windows)", "Turn off SSL certificate validation." => "Desactiva a validación do certificado SSL.", "Not recommended, use it for testing only! If connection only works with this option, import the LDAP server's SSL certificate in your %s server." => "Non recomendado, utilizar só para probas! Se a conexión só funciona con esta opción importa o certificado SSL do servidor LDAP no seu servidor %s.", "Cache Time-To-Live" => "Tempo de persistencia da caché", diff --git a/apps/user_ldap/l10n/hu_HU.php b/apps/user_ldap/l10n/hu_HU.php index 277ae96fcd48269b1c4a21ce1e960522840c089e..34daa25ce6867f462c7f129c89c6465787b6609a 100644 --- a/apps/user_ldap/l10n/hu_HU.php +++ b/apps/user_ldap/l10n/hu_HU.php @@ -67,7 +67,6 @@ $TRANSLATIONS = array( "Backup (Replica) Port" => "A másodkiszolgáló (replika) portszáma", "Disable Main Server" => "A fő szerver kihagyása", "Only connect to the replica server." => "Csak a másodlagos (másolati) kiszolgálóhoz kapcsolódjunk.", -"Case insensitve LDAP server (Windows)" => "Az LDAP-kiszolgáló nem tesz különbséget a kis- és nagybetűk között (Windows)", "Turn off SSL certificate validation." => "Ne ellenőrizzük az SSL-tanúsítvány érvényességét", "Not recommended, use it for testing only! If connection only works with this option, import the LDAP server's SSL certificate in your %s server." => "Használata nem javasolt (kivéve tesztelési céllal). Ha a kapcsolat csak ezzel a beállítással működik, akkor importálja az LDAP-kiszolgáló SSL tanúsítványát a(z) %s kiszolgálóra!", "Cache Time-To-Live" => "A gyorsítótár tárolási időtartama", diff --git a/apps/user_ldap/l10n/ia.php b/apps/user_ldap/l10n/ia.php index e138fd835f151995a566fab18b544891e791e5fc..b29ecbb1d243814acbeb910fec9c23eb3fed7292 100644 --- a/apps/user_ldap/l10n/ia.php +++ b/apps/user_ldap/l10n/ia.php @@ -7,6 +7,7 @@ $TRANSLATIONS = array( "Save" => "Salveguardar", "Help" => "Adjuta", "Password" => "Contrasigno", -"Back" => "Retro" +"Back" => "Retro", +"Continue" => "Continuar" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/apps/user_ldap/l10n/id.php b/apps/user_ldap/l10n/id.php index 03071bb1d0fa302a98c85160ce6e9b72d0334996..1d42031a4982f7efdf91d9600789b2688ce2e8b8 100644 --- a/apps/user_ldap/l10n/id.php +++ b/apps/user_ldap/l10n/id.php @@ -39,7 +39,6 @@ $TRANSLATIONS = array( "Give an optional backup host. It must be a replica of the main LDAP/AD server." => "Berikan pilihan host cadangan. Harus merupakan replika dari server LDAP/AD utama.", "Backup (Replica) Port" => "Port Cadangan (Replika)", "Disable Main Server" => "Nonaktifkan Server Utama", -"Case insensitve LDAP server (Windows)" => "Server LDAP dengan kapitalisasi tidak sensitif (Windows)", "Turn off SSL certificate validation." => "matikan validasi sertivikat SSL", "Cache Time-To-Live" => "Gunakan Tembolok untuk Time-To-Live", "in seconds. A change empties the cache." => "dalam detik. perubahan mengosongkan cache", diff --git a/apps/user_ldap/l10n/it.php b/apps/user_ldap/l10n/it.php index 4436127747f68162981caa9e54533a40e69a3eb6..095a0619ee1fc0e2d48d40f3e1208358ec4c8bd6 100644 --- a/apps/user_ldap/l10n/it.php +++ b/apps/user_ldap/l10n/it.php @@ -70,7 +70,7 @@ $TRANSLATIONS = array( "Backup (Replica) Port" => "Porta di backup (Replica)", "Disable Main Server" => "Disabilita server principale", "Only connect to the replica server." => "Collegati solo al server di replica.", -"Case insensitve LDAP server (Windows)" => "Case insensitve LDAP server (Windows)", +"Case insensitive LDAP server (Windows)" => "Server LDAP non sensibile alle maiuscole (Windows)", "Turn off SSL certificate validation." => "Disattiva il controllo del certificato SSL.", "Not recommended, use it for testing only! If connection only works with this option, import the LDAP server's SSL certificate in your %s server." => "Non consigliata, da utilizzare solo per test! Se la connessione funziona solo con questa opzione, importa il certificate SSL del server LDAP sul tuo server %s.", "Cache Time-To-Live" => "Tempo di vita della cache", @@ -90,6 +90,8 @@ $TRANSLATIONS = array( "Group-Member association" => "Associazione gruppo-utente ", "Nested Groups" => "Gruppi nidificati", "When switched on, groups that contain groups are supported. (Only works if the group member attribute contains DNs.)" => "Quando è attivato, i gruppi che contengono altri gruppi sono supportati. (Funziona solo se l'attributo del gruppo membro contiene DN.)", +"Paging chunksize" => "Dimensione del blocco di paginazione", +"Chunksize used for paged LDAP searches that may return bulky results like user or group enumeration. (Setting it 0 disables paged LDAP searches in those situations.)" => "Dimensione del blocco per le ricerche LDAP paginate che potrebbero restituire risultati pesanti come l'enumerazione di utenti o gruppi.(L'impostazione a 0 disabilita le ricerche LDAP paginate in questi casi.)", "Special Attributes" => "Attributi speciali", "Quota Field" => "Campo Quota", "Quota Default" => "Quota predefinita", diff --git a/apps/user_ldap/l10n/ja.php b/apps/user_ldap/l10n/ja.php index d13deafe6c3d4a909e95adcf8f99d2683a1eccb2..e7e4706c46a4d43e959f030f8ed02e08e9ba14d1 100644 --- a/apps/user_ldap/l10n/ja.php +++ b/apps/user_ldap/l10n/ja.php @@ -70,7 +70,6 @@ $TRANSLATIONS = array( "Backup (Replica) Port" => "バックアップ(レプリカ)ポート", "Disable Main Server" => "メインサーバーを無効にする", "Only connect to the replica server." => "レプリカサーバーにのみ接続します。", -"Case insensitve LDAP server (Windows)" => "大文字/小文字を区別しないLDAPサーバー(Windows)", "Turn off SSL certificate validation." => "SSL証明書の確認を無効にする。", "Not recommended, use it for testing only! If connection only works with this option, import the LDAP server's SSL certificate in your %s server." => "推奨されません、テストにおいてのみ使用してください!このオプションでのみ接続が動作する場合は、LDAP サーバーのSSL証明書を %s サーバーにインポートしてください。", "Cache Time-To-Live" => "キャッシュのTTL", diff --git a/apps/user_ldap/l10n/ka_GE.php b/apps/user_ldap/l10n/ka_GE.php index ffdf76555178e628ce8da673808ac1581a1e1fa1..3ee307b55ccb88e9986444c5e9a7f163be37b54d 100644 --- a/apps/user_ldap/l10n/ka_GE.php +++ b/apps/user_ldap/l10n/ka_GE.php @@ -37,7 +37,6 @@ $TRANSLATIONS = array( "Give an optional backup host. It must be a replica of the main LDAP/AD server." => "მიუთითეთ რაიმე ბექაფ ჰოსტი. ის უნდა იყოს ძირითადი LDAP/AD სერვერის რეპლიკა.", "Backup (Replica) Port" => "ბექაფ (რეპლიკა) პორტი", "Disable Main Server" => "გამორთეთ ძირითადი სერვერი", -"Case insensitve LDAP server (Windows)" => "LDAP server (Windows)", "Turn off SSL certificate validation." => "გამორთეთ SSL სერთიფიკატის ვალიდაცია.", "Cache Time-To-Live" => "ქეშის სიცოცხლის ხანგრძლივობა", "in seconds. A change empties the cache." => "წამებში. ცვლილება ასუფთავებს ქეშს.", diff --git a/apps/user_ldap/l10n/ko.php b/apps/user_ldap/l10n/ko.php index 5dd2151a411484c69d7461207050e601bd006768..1ae63bd4d2146228e4e8dfac309cc759da011707 100644 --- a/apps/user_ldap/l10n/ko.php +++ b/apps/user_ldap/l10n/ko.php @@ -67,7 +67,6 @@ $TRANSLATIONS = array( "Backup (Replica) Port" => "백업 (복제) 포트", "Disable Main Server" => "주 서버 비활성화", "Only connect to the replica server." => "복제 서버에만 연결합니다.", -"Case insensitve LDAP server (Windows)" => "서버에서 대소문자를 구분하지 않음 (Windows)", "Turn off SSL certificate validation." => "SSL 인증서 유효성 검사를 해제합니다.", "Not recommended, use it for testing only! If connection only works with this option, import the LDAP server's SSL certificate in your %s server." => "테스트 목적으로만 사용하십시오! 이 옵션을 사용해야만 연결할 수 있으면 %s 서버에 LDAP 서버의 SSL 인증서를 설치하십시오.", "Cache Time-To-Live" => "캐시 유지 시간", diff --git a/apps/user_ldap/l10n/lv.php b/apps/user_ldap/l10n/lv.php index 769f17e633884b4e05075487914bd29e5104c022..e3cb206e2d6db7b9b2830005135887e70fdf9051 100644 --- a/apps/user_ldap/l10n/lv.php +++ b/apps/user_ldap/l10n/lv.php @@ -36,7 +36,6 @@ $TRANSLATIONS = array( "Give an optional backup host. It must be a replica of the main LDAP/AD server." => "Norādi rezerves serveri (nav obligāti). Tam ir jābūt galvenā LDAP/AD servera kopijai.", "Backup (Replica) Port" => "Rezerves (kopijas) ports", "Disable Main Server" => "Deaktivēt galveno serveri", -"Case insensitve LDAP server (Windows)" => "Reģistrnejutīgs LDAP serveris (Windows)", "Turn off SSL certificate validation." => "Izslēgt SSL sertifikātu validēšanu.", "Cache Time-To-Live" => "Kešatmiņas dzīvlaiks", "in seconds. A change empties the cache." => "sekundēs. Izmaiņas iztukšos kešatmiņu.", diff --git a/apps/user_ldap/l10n/nb_NO.php b/apps/user_ldap/l10n/nb_NO.php index 0b60752e9d9504796f12fb179a9dc1e24598e44b..dd4af3e759e93f24085b13f3760d7925636175cc 100644 --- a/apps/user_ldap/l10n/nb_NO.php +++ b/apps/user_ldap/l10n/nb_NO.php @@ -67,7 +67,6 @@ $TRANSLATIONS = array( "Backup (Replica) Port" => "Reserve (Replika) Port", "Disable Main Server" => "Deaktiver hovedtjeneren", "Only connect to the replica server." => "Koble til bare replika-tjeneren.", -"Case insensitve LDAP server (Windows)" => "Case-insensitiv LDAP tjener (Windows)", "Turn off SSL certificate validation." => "Slå av SSL-sertifikat validering", "Not recommended, use it for testing only! If connection only works with this option, import the LDAP server's SSL certificate in your %s server." => "Ikke anbefalt, bruk kun for testing! Hvis tilkobling bare virker med dette valget, importer LDAP-tjenerens SSL-sertifikat i %s-serveren din.", "Cache Time-To-Live" => "Levetid i mellomlager", diff --git a/apps/user_ldap/l10n/nl.php b/apps/user_ldap/l10n/nl.php index d1ffd4f00d4b0a1b1ed5a1252b6efbfc6fed44b7..196e5a52d120f480d26b8eb365373f0c50b77543 100644 --- a/apps/user_ldap/l10n/nl.php +++ b/apps/user_ldap/l10n/nl.php @@ -70,7 +70,7 @@ $TRANSLATIONS = array( "Backup (Replica) Port" => "Backup (Replica) Poort", "Disable Main Server" => "Deactiveren hoofdserver", "Only connect to the replica server." => "Maak alleen een verbinding met de replica server.", -"Case insensitve LDAP server (Windows)" => "Niet-hoofdlettergevoelige LDAP server (Windows)", +"Case insensitive LDAP server (Windows)" => "Niet-hoofdlettergevoelige LDAP server (Windows)", "Turn off SSL certificate validation." => "Schakel SSL certificaat validatie uit.", "Not recommended, use it for testing only! If connection only works with this option, import the LDAP server's SSL certificate in your %s server." => "Niet aanbevolen, gebruik alleen om te testen! Als de connectie alleen werkt met deze optie, importeer dan het SSL-certificaat van de LDAP-server naar uw %s server.", "Cache Time-To-Live" => "Cache time-to-live", diff --git a/apps/user_ldap/l10n/or_IN.php b/apps/user_ldap/l10n/or_IN.php new file mode 100644 index 0000000000000000000000000000000000000000..3a1e002311cd9c1851bda1b96f71bc4d3e3e047b --- /dev/null +++ b/apps/user_ldap/l10n/or_IN.php @@ -0,0 +1,6 @@ + array("",""), +"_%s user found_::_%s users found_" => array("","") +); +$PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/apps/user_ldap/l10n/pl.php b/apps/user_ldap/l10n/pl.php index 69a94ec693c4313aa79a18aa6765399b39406415..988e44dbe387fbd6b195e7d487d54226aa6b0971 100644 --- a/apps/user_ldap/l10n/pl.php +++ b/apps/user_ldap/l10n/pl.php @@ -70,7 +70,7 @@ $TRANSLATIONS = array( "Backup (Replica) Port" => "Kopia zapasowa (repliki) Port", "Disable Main Server" => "Wyłącz serwer główny", "Only connect to the replica server." => "Połącz tylko do repliki serwera.", -"Case insensitve LDAP server (Windows)" => "Wielkość liter serwera LDAP (Windows)", +"Case insensitive LDAP server (Windows)" => "Serwer LDAP nie rozróżniający wielkości liter (Windows)", "Turn off SSL certificate validation." => "Wyłączyć sprawdzanie poprawności certyfikatu SSL.", "Not recommended, use it for testing only! If connection only works with this option, import the LDAP server's SSL certificate in your %s server." => "Nie polecane, używać tylko w celu testowania! Jeśli połączenie działa tylko z tą opcją, zaimportuj certyfikat SSL serwera LDAP na swój %s.", "Cache Time-To-Live" => "Przechowuj czas życia", diff --git a/apps/user_ldap/l10n/pt_BR.php b/apps/user_ldap/l10n/pt_BR.php index 844024a953d28b81b81e2bec897f95a959140b34..0c15694e3ff502566afa662626b5fc8b514e2a00 100644 --- a/apps/user_ldap/l10n/pt_BR.php +++ b/apps/user_ldap/l10n/pt_BR.php @@ -70,7 +70,7 @@ $TRANSLATIONS = array( "Backup (Replica) Port" => "Porta do Backup (Réplica)", "Disable Main Server" => "Desativar Servidor Principal", "Only connect to the replica server." => "Conectar-se somente ao servidor de réplica.", -"Case insensitve LDAP server (Windows)" => "Servidor LDAP sensível à caixa alta (Windows)", +"Case insensitive LDAP server (Windows)" => "Servidor LDAP(Windows) não distigue maiúscula de minúscula", "Turn off SSL certificate validation." => "Desligar validação de certificado SSL.", "Not recommended, use it for testing only! If connection only works with this option, import the LDAP server's SSL certificate in your %s server." => "Não recomendado, use-o somente para teste! Se a conexão só funciona com esta opção, importar o certificado SSL do servidor LDAP em seu servidor %s.", "Cache Time-To-Live" => "Cache Time-To-Live", diff --git a/apps/user_ldap/l10n/pt_PT.php b/apps/user_ldap/l10n/pt_PT.php index 37142c8175aa1e2d0587cfbf49cb1bb366a43412..e2754785c650518c8ff240abedf77012e69e640e 100644 --- a/apps/user_ldap/l10n/pt_PT.php +++ b/apps/user_ldap/l10n/pt_PT.php @@ -65,7 +65,6 @@ $TRANSLATIONS = array( "Backup (Replica) Port" => "Porta do servidor de backup (Replica)", "Disable Main Server" => "Desactivar servidor principal", "Only connect to the replica server." => "Ligar apenas ao servidor de réplicas.", -"Case insensitve LDAP server (Windows)" => "Servidor LDAP (Windows) não sensível a maiúsculas.", "Turn off SSL certificate validation." => "Desligar a validação de certificado SSL.", "Cache Time-To-Live" => "Cache do tempo de vida dos objetos no servidor", "in seconds. A change empties the cache." => "em segundos. Uma alteração esvazia a cache.", diff --git a/apps/user_ldap/l10n/ro.php b/apps/user_ldap/l10n/ro.php index 0214be3f787fb7d32637e8633d860fef5bb71688..26394db0f85d320b08780ce8df5055770047a5f3 100644 --- a/apps/user_ldap/l10n/ro.php +++ b/apps/user_ldap/l10n/ro.php @@ -19,7 +19,6 @@ $TRANSLATIONS = array( "Back" => "Înapoi", "Continue" => "Continuă", "Warning: The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "Atenție Modulul PHP LDAP nu este instalat, infrastructura nu va funcționa. Contactează administratorul sistemului pentru al instala.", -"Case insensitve LDAP server (Windows)" => "Server LDAP insensibil la majuscule (Windows)", "Turn off SSL certificate validation." => "Oprește validarea certificatelor SSL ", "in seconds. A change empties the cache." => "în secunde. O schimbare curăță memoria tampon.", "User Display Name Field" => "Câmpul cu numele vizibil al utilizatorului", diff --git a/apps/user_ldap/l10n/ru.php b/apps/user_ldap/l10n/ru.php index d9685368cbcf8064a990f7aedb91b43a3fcc4f18..e719f01c370789067d16e8bc09ec5c5c8df92d37 100644 --- a/apps/user_ldap/l10n/ru.php +++ b/apps/user_ldap/l10n/ru.php @@ -53,6 +53,7 @@ $TRANSLATIONS = array( "For anonymous access, leave DN and Password empty." => "Для анонимного доступа оставьте DN и пароль пустыми.", "One Base DN per line" => "По одной базе поиска (Base DN) в строке.", "You can specify Base DN for users and groups in the Advanced tab" => "Вы можете задать Base DN для пользователей и групп на вкладке \"Расширенное\"", +"Limit %s access to users meeting these criteria:" => "Ограничить доступ к %s пользователям, удовлетворяющим этому критерию:", "The filter specifies which LDAP users shall have access to the %s instance." => "Этот фильтр указывает, какие пользователи LDAP должны иметь доступ к %s.", "users found" => "пользователей найдено", "Back" => "Назад", @@ -67,7 +68,7 @@ $TRANSLATIONS = array( "Backup (Replica) Port" => "Порт резервного сервера", "Disable Main Server" => "Отключить главный сервер", "Only connect to the replica server." => "Подключаться только к серверу-реплике.", -"Case insensitve LDAP server (Windows)" => "Нечувствительный к регистру сервер LDAP (Windows)", +"Case insensitive LDAP server (Windows)" => "Нечувствительный к регистру сервер LDAP (Windows)", "Turn off SSL certificate validation." => "Отключить проверку сертификата SSL.", "Not recommended, use it for testing only! If connection only works with this option, import the LDAP server's SSL certificate in your %s server." => "Не рекомендуется, используйте только в режиме тестирования! Если соединение работает только с этой опцией, импортируйте на ваш %s сервер SSL-сертификат сервера LDAP.", "Cache Time-To-Live" => "Кэш времени жизни", diff --git a/apps/user_ldap/l10n/sk_SK.php b/apps/user_ldap/l10n/sk_SK.php index 460064593ee72566bbd13714fb63a84a82ffaf28..3c6b51824f4511437218da9fb7443e69f3c414a5 100644 --- a/apps/user_ldap/l10n/sk_SK.php +++ b/apps/user_ldap/l10n/sk_SK.php @@ -33,12 +33,14 @@ $TRANSLATIONS = array( "Save" => "Uložiť", "Test Configuration" => "Test nastavenia", "Help" => "Pomoc", +"Groups meeting these criteria are available in %s:" => "Skupiny spĺňajúce tieto kritériá sú k dispozícii v %s:", "only those object classes:" => "len tieto triedy objektov:", "only from those groups:" => "len z týchto skupín:", "Edit raw filter instead" => "Miesto pre úpravu raw filtra", "Raw LDAP filter" => "Raw LDAP filter", "The filter specifies which LDAP groups shall have access to the %s instance." => "Tento filter LDAP určuje, ktoré skupiny budú mať prístup k %s inštancii.", "groups found" => "nájdené skupiny", +"Users login with this attribute:" => "Používateľov prihlásiť pomocou tohto atribútu:", "LDAP Username:" => "LDAP používateľské meno:", "LDAP Email Address:" => "LDAP emailová adresa:", "Other Attributes:" => "Iné atribúty:", @@ -53,6 +55,7 @@ $TRANSLATIONS = array( "For anonymous access, leave DN and Password empty." => "Pre anonymný prístup ponechajte údaje DN a Heslo prázdne.", "One Base DN per line" => "Jedno základné DN na riadok", "You can specify Base DN for users and groups in the Advanced tab" => "V rozšírenom nastavení môžete zadať základné DN pre používateľov a skupiny", +"Limit %s access to users meeting these criteria:" => "Obmedziť %s prístup na používateľov spĺňajúcich tieto kritériá:", "The filter specifies which LDAP users shall have access to the %s instance." => "Tento filter LDAP určuje, ktorí používatelia majú prístup k %s inštancii.", "users found" => "nájdení používatelia", "Back" => "Späť", @@ -67,7 +70,7 @@ $TRANSLATIONS = array( "Backup (Replica) Port" => "Záložný server (kópia) port", "Disable Main Server" => "Zakázať hlavný server", "Only connect to the replica server." => "Pripojiť sa len k záložnému serveru.", -"Case insensitve LDAP server (Windows)" => "LDAP server nerozlišuje veľkosť znakov (Windows)", +"Case insensitive LDAP server (Windows)" => "LDAP server je citlivý na veľkosť písmen (Windows)", "Turn off SSL certificate validation." => "Vypnúť overovanie SSL certifikátu.", "Not recommended, use it for testing only! If connection only works with this option, import the LDAP server's SSL certificate in your %s server." => "Neodporúčané, použite iba pri testovaní! Pokiaľ spojenie funguje iba z daným nastavením, importujte SSL certifikát LDAP servera do vášho %s servera.", "Cache Time-To-Live" => "Životnosť objektov vo vyrovnávacej pamäti", @@ -85,6 +88,7 @@ $TRANSLATIONS = array( "One Group Base DN per line" => "Jedna skupinová základná DN na riadok", "Group Search Attributes" => "Atribúty vyhľadávania skupín", "Group-Member association" => "Priradenie člena skupiny", +"Nested Groups" => "Vnorené skupiny", "Special Attributes" => "Špeciálne atribúty", "Quota Field" => "Pole kvóty", "Quota Default" => "Predvolená kvóta", diff --git a/apps/user_ldap/l10n/sl.php b/apps/user_ldap/l10n/sl.php index b6df62ffb79f3eb0cba0964e33d7dd37c8657532..54c91103a3e368b1c982d1d5eb4725aebe85af60 100644 --- a/apps/user_ldap/l10n/sl.php +++ b/apps/user_ldap/l10n/sl.php @@ -67,7 +67,6 @@ $TRANSLATIONS = array( "Backup (Replica) Port" => "Vrata varnostne kopije (replike)", "Disable Main Server" => "Onemogoči glavni strežnik", "Only connect to the replica server." => "Poveži le s podvojenim strežnikom.", -"Case insensitve LDAP server (Windows)" => "Strežnik LDAP ne upošteva velikosti črk (Windows)", "Turn off SSL certificate validation." => "Onemogoči določanje veljavnosti potrdila SSL.", "Not recommended, use it for testing only! If connection only works with this option, import the LDAP server's SSL certificate in your %s server." => "Možnosti ni priporočljivo uporabiti; namenjena je zgolj preizkušanju! Če deluje povezava le s to možnostjo, je treba uvoziti potrdilo SSL strežnika LDAP na strežnik %s.", "Cache Time-To-Live" => "Predpomni podatke TTL", diff --git a/apps/user_ldap/l10n/sq.php b/apps/user_ldap/l10n/sq.php index 0f18ac02351ea26515bebccdc58945a97716aad2..03ff204bbca74623fc36fac4b41a59b847738ccf 100644 --- a/apps/user_ldap/l10n/sq.php +++ b/apps/user_ldap/l10n/sq.php @@ -38,7 +38,6 @@ $TRANSLATIONS = array( "Give an optional backup host. It must be a replica of the main LDAP/AD server." => "Jepni një pritës rezervë. Duhet të jetë replikimi i serverit AD/LDAP kryesor.", "Backup (Replica) Port" => "Porta rezervë (Replika)", "Disable Main Server" => "Ç'aktivizoni serverin kryesor", -"Case insensitve LDAP server (Windows)" => " Server LDAP i pavëmëndshëm ndaj gërmëzimit të madh apo jo (Windows)", "Turn off SSL certificate validation." => "Ç'aktivizoni kontrollin e certifikatës SSL.", "Cache Time-To-Live" => "Cache Time-To-Live", "in seconds. A change empties the cache." => "në sekonda Ndryshimi boshatis 'cache'-n.", diff --git a/apps/user_ldap/l10n/sr.php b/apps/user_ldap/l10n/sr.php index d2ce2cf08b61052c4effff62ecc81863885fbc5a..fb016fdd17e895e46f1bc7d295b2622d422aafb2 100644 --- a/apps/user_ldap/l10n/sr.php +++ b/apps/user_ldap/l10n/sr.php @@ -14,7 +14,6 @@ $TRANSLATIONS = array( "Password" => "Лозинка", "For anonymous access, leave DN and Password empty." => "За анониман приступ, оставите поља DN и лозинка празним.", "Back" => "Назад", -"Case insensitve LDAP server (Windows)" => "LDAP сервер осетљив на велика и мала слова (Windows)", "Turn off SSL certificate validation." => "Искључите потврду SSL сертификата.", "in seconds. A change empties the cache." => "у секундама. Промена испражњава кеш меморију.", "User Display Name Field" => "Име приказа корисника", diff --git a/apps/user_ldap/l10n/sv.php b/apps/user_ldap/l10n/sv.php index 6a4ed0c720407bee13f8cac8c5600934cde457e2..2a4f8b19655ff98837a6f6025d5255b74278b4db 100644 --- a/apps/user_ldap/l10n/sv.php +++ b/apps/user_ldap/l10n/sv.php @@ -70,7 +70,7 @@ $TRANSLATIONS = array( "Backup (Replica) Port" => "Säkerhetskopierins-port (Replika)", "Disable Main Server" => "Inaktivera huvudserver", "Only connect to the replica server." => "Anslut endast till replikaservern.", -"Case insensitve LDAP server (Windows)" => "LDAP-servern är okänslig för gemener och versaler (Windows)", +"Case insensitive LDAP server (Windows)" => "om okänslig LDAP-server (Windows)", "Turn off SSL certificate validation." => "Stäng av verifiering av SSL-certifikat.", "Not recommended, use it for testing only! If connection only works with this option, import the LDAP server's SSL certificate in your %s server." => "Rekommenderas inte, använd endast för test! Om anslutningen bara fungerar med denna inställning behöver du importera LDAP-serverns SSL-certifikat till din %s server.", "Cache Time-To-Live" => "Cache Time-To-Live", diff --git a/apps/user_ldap/l10n/ta_LK.php b/apps/user_ldap/l10n/ta_LK.php index c7efcf05894e65d6a7ed4f87373a1bd760140afb..27a3068f963b7bdea64aec8b1f06026a522fd5a3 100644 --- a/apps/user_ldap/l10n/ta_LK.php +++ b/apps/user_ldap/l10n/ta_LK.php @@ -13,7 +13,6 @@ $TRANSLATIONS = array( "Password" => "கடவுச்சொல்", "You can specify Base DN for users and groups in the Advanced tab" => "நீங்கள் பயனாளர்களுக்கும் மேன்மை தத்தலில் உள்ள குழுவிற்கும் தள DN ஐ குறிப்பிடலாம் ", "Back" => "பின்னுக்கு", -"Case insensitve LDAP server (Windows)" => "உணர்ச்சியான LDAP சேவையகம் (சாளரங்கள்)", "Turn off SSL certificate validation." => "SSL சான்றிதழின் செல்லுபடியை நிறுத்திவிடவும்", "in seconds. A change empties the cache." => "செக்கன்களில். ஒரு மாற்றம் இடைமாற்றுநினைவகத்தை வெற்றிடமாக்கும்.", "User Display Name Field" => "பயனாளர் காட்சிப்பெயர் புலம்", diff --git a/apps/user_ldap/l10n/th_TH.php b/apps/user_ldap/l10n/th_TH.php index 2202a2f0a837178991396803c6bb8026f0fc7594..1c6dbe0473f785ab6949794f728a05748e3b6552 100644 --- a/apps/user_ldap/l10n/th_TH.php +++ b/apps/user_ldap/l10n/th_TH.php @@ -31,7 +31,6 @@ $TRANSLATIONS = array( "Warning: The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "คำเตือน: โมดูล PHP LDAP ยังไม่ได้ถูกติดตั้ง, ระบบด้านหลังจะไม่สามารถทำงานได้ กรุณาติดต่อผู้ดูแลระบบของคุณเพื่อทำการติดตั้งโมดูลดังกล่าว", "Connection Settings" => "ตั้งค่าการเชื่อมต่อ", "Disable Main Server" => "ปิดใช้งานเซิร์ฟเวอร์หลัก", -"Case insensitve LDAP server (Windows)" => "เซิร์ฟเวอร์ LDAP ประเภท Case insensitive (วินโดวส์)", "Turn off SSL certificate validation." => "ปิดใช้งานการตรวจสอบความถูกต้องของใบรับรองความปลอดภัย SSL", "in seconds. A change empties the cache." => "ในอีกไม่กี่วินาที ระบบจะเปลี่ยนแปลงข้อมูลในแคชให้ว่างเปล่า", "Directory Settings" => "ตั้งค่าไดเร็กทอรี่", diff --git a/apps/user_ldap/l10n/tr.php b/apps/user_ldap/l10n/tr.php index d654b4e241689e4a721a32879e0d41885d73c509..48e39c8523cce56e9b0e6ab5bbf9f56940b7bfb5 100644 --- a/apps/user_ldap/l10n/tr.php +++ b/apps/user_ldap/l10n/tr.php @@ -45,48 +45,48 @@ $TRANSLATIONS = array( "LDAP Email Address:" => "LDAP E-posta Adresi:", "Other Attributes:" => "Diğer Nitelikler", "Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action. Example: \"uid=%%uid\"" => "Oturum açma girişimi olduğunda uygulanacak filtreyi tanımlar. %%uid, oturum işleminde kullanıcı adı ile değiştirilir. Örneğin: \"uid=%%uid\"", -"Add Server Configuration" => "Sunucu Uyunlama birlemek ", +"Add Server Configuration" => "Sunucu Yapılandırması Ekle", "Host" => "Sunucu", -"You can omit the protocol, except you require SSL. Then start with ldaps://" => "Protokol atlamak edesin, sadece SSL istiyorsaniz. O zaman, idapsile baslamak. ", +"You can omit the protocol, except you require SSL. Then start with ldaps://" => "SSL gerektirmediğiniz takdirde protokol belirtmeyebilirsiniz. Ardından ldaps:// ile başlayın", "Port" => "Port", "User DN" => "Kullanıcı DN", -"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "DN musterinin, kimle baglamaya yapacagiz,meselâ uid=agent.dc mesela, dc=com Gecinme adisiz ici, DN ve Parola bos birakmak. ", +"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "İstemci kullanıcısının yapılacağı atamanın DN'i örn. uid=agent,dc=örnek,dc=com. Anonim erişim için DN ve Parolayı boş bırakın.", "Password" => "Parola", "For anonymous access, leave DN and Password empty." => "Anonim erişim için DN ve Parola alanlarını boş bırakın.", -"One Base DN per line" => "Bir Tabani DN herbir dizi. ", -"You can specify Base DN for users and groups in the Advanced tab" => "Base DN kullanicileri ve kaynaklari icin tablosu Advanced tayin etmek ederiz. ", +"One Base DN per line" => "Her satırda tek bir Base DN", +"You can specify Base DN for users and groups in the Advanced tab" => "Gelişmiş sekmesinde kullanıcılar ve gruplar için Base DN belirtebilirsiniz.", "Limit %s access to users meeting these criteria:" => "%s erişimini, şu kriterle eşleşen kullanıcılara sınırla:", "The filter specifies which LDAP users shall have access to the %s instance." => "Filtre, %s örneğine erişmesi gereken LDAP kullanıcılarını belirtir.", "users found" => "kullanıcı bulundu", "Back" => "Geri", "Continue" => "Devam et", "Warning: Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behavior. Please ask your system administrator to disable one of them." => "Uyarı: user_ldap ve user_webdavauth uygulamaları uyumlu değil. Beklenmedik bir davranışla karşılaşabilirsiniz. Lütfen ikisinden birini devre dışı bırakmak için sistem yöneticinizle iletişime geçin.", -"Warning: The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "Ihbar Modulu PHP LDAP yuklemdi degil, backend calismacak. Lutfen sistem yonetici sormak yuklemek icin.", +"Warning: The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "Uyarı: PHP LDAP modülü kurulu değil, arka uç çalışmayacak. Lütfen kurulumu için sistem yöneticinizle iletişime geçin.", "Connection Settings" => "Bağlantı ayarları", "Configuration Active" => "Yapılandırma Etkin", -"When unchecked, this configuration will be skipped." => "Ne zaman iptal, bu uynnlama isletici ", -"Backup (Replica) Host" => "Sigorta Kopya Cephe ", -"Give an optional backup host. It must be a replica of the main LDAP/AD server." => "Bir kopya cevre vermek, kopya sunucu onemli olmali. ", -"Backup (Replica) Port" => "Kopya Port ", +"When unchecked, this configuration will be skipped." => "İşaretli değilse, bu yapılandırma atlanacaktır.", +"Backup (Replica) Host" => "Yedek (Replica) Sunucu", +"Give an optional backup host. It must be a replica of the main LDAP/AD server." => "İsteğe bağlı bir yedek sunucusu belirtin. Ana LDAP/AD sunucusunun bir kopyası olmalıdır.", +"Backup (Replica) Port" => "Yedek (Replica) Bağlantı Noktası", "Disable Main Server" => "Ana sunucuyu devredışı birak", "Only connect to the replica server." => "Sadece kopya sunucuya bağlan.", -"Case insensitve LDAP server (Windows)" => "Dusme sunucu LDAP zor degil. (Windows)", +"Case insensitive LDAP server (Windows)" => "Büyük küçük harf duyarsız LDAP sunucusu (Windows)", "Turn off SSL certificate validation." => "SSL sertifika doğrulamasını kapat.", "Not recommended, use it for testing only! If connection only works with this option, import the LDAP server's SSL certificate in your %s server." => "Önerilmez, sadece test için kullanın! Eğer bağlantı sadece bu seçenekle çalışıyorsa %s sunucunuza LDAP sunucusunun SSL sertifikasını ekleyin.", -"Cache Time-To-Live" => "Cache Time-To-Live ", +"Cache Time-To-Live" => "Önbellek Time-To-Live Değeri", "in seconds. A change empties the cache." => "saniye cinsinden. Bir değişiklik önbelleği temizleyecektir.", -"Directory Settings" => "Parametrar Listesin Adresinin ", -"User Display Name Field" => "Ekran Adi Kullanici, (Alan Adi Kullanici Ekrane)", +"Directory Settings" => "Dizin Ayarları", +"User Display Name Field" => "Kullanıcı Görünen Ad Alanı", "The LDAP attribute to use to generate the user's display name." => "Kullanıcının görünen adını oluşturmak için kullanılacak LDAP niteliği.", "Base User Tree" => "Temel Kullanıcı Ağacı", -"One User Base DN per line" => "Bir Temel Kullanici DN her dizgi ", -"User Search Attributes" => "Kategorii Arama Kullanici ", +"One User Base DN per line" => "Her satırda Tek Kullanıcı Base DN'i", +"User Search Attributes" => "Kullanıcı Arama Nitelikleri", "Optional; one attribute per line" => "Tercihe bağlı; her bir satırda bir öznitelik", -"Group Display Name Field" => "Grub Ekrane Alani Adi", +"Group Display Name Field" => "Grup Görünen Ad Alanı", "The LDAP attribute to use to generate the groups's display name." => "Grubun görünen adını oluşturmak için kullanılacak LDAP niteliği.", "Base Group Tree" => "Temel Grup Ağacı", -"One Group Base DN per line" => "Bir Grubu Tabani DN her dizgi. ", -"Group Search Attributes" => "Kategorii Arama Grubu", +"One Group Base DN per line" => "Her satırda Tek Grup Base DN'i", +"Group Search Attributes" => "Grup Arama Nitelikleri", "Group-Member association" => "Grup-Üye işbirliği", "Nested Groups" => "İç içe Gruplar", "When switched on, groups that contain groups are supported. (Only works if the group member attribute contains DNs.)" => "Etkinleştirildiğinde, grup içeren gruplar desteklenir (Sadece grup üyesi DN niteliği içeriyorsa çalışır).", diff --git a/apps/user_ldap/l10n/uk.php b/apps/user_ldap/l10n/uk.php index e87348ec44d365fd3e0f778e15c875a14acec310..5f4db6d5a03b7b855547e4b92f306b8a8b4e8a38 100644 --- a/apps/user_ldap/l10n/uk.php +++ b/apps/user_ldap/l10n/uk.php @@ -39,7 +39,6 @@ $TRANSLATIONS = array( "Give an optional backup host. It must be a replica of the main LDAP/AD server." => "Вкажіть додатковий резервний сервер. Він повинен бути копією головного LDAP/AD сервера.", "Backup (Replica) Port" => "Порт сервера для резервних копій", "Disable Main Server" => "Вимкнути Головний Сервер", -"Case insensitve LDAP server (Windows)" => "Нечутливий до регістру LDAP сервер (Windows)", "Turn off SSL certificate validation." => "Вимкнути перевірку SSL сертифіката.", "Cache Time-To-Live" => "Час актуальності Кеша", "in seconds. A change empties the cache." => "в секундах. Зміна очищує кеш.", diff --git a/apps/user_ldap/l10n/vi.php b/apps/user_ldap/l10n/vi.php index 84d001cb5966fea072203000979d260d53d02065..21b9783897ea66a044c7ca08f60e049fb05ab8b2 100644 --- a/apps/user_ldap/l10n/vi.php +++ b/apps/user_ldap/l10n/vi.php @@ -21,7 +21,6 @@ $TRANSLATIONS = array( "Connection Settings" => "Connection Settings", "Backup (Replica) Port" => "Cổng sao lưu (Replica)", "Disable Main Server" => "Tắt máy chủ chính", -"Case insensitve LDAP server (Windows)" => "Trường hợp insensitve LDAP máy chủ (Windows)", "Turn off SSL certificate validation." => "Tắt xác thực chứng nhận SSL", "in seconds. A change empties the cache." => "trong vài giây. Một sự thay đổi bộ nhớ cache.", "Directory Settings" => "Directory Settings", diff --git a/apps/user_ldap/l10n/zh_CN.php b/apps/user_ldap/l10n/zh_CN.php index aae100ef9adf8548059a0866bb595b6649073e0c..d56f394f644021f7a85f68f5f3e8806c822b66da 100644 --- a/apps/user_ldap/l10n/zh_CN.php +++ b/apps/user_ldap/l10n/zh_CN.php @@ -42,7 +42,6 @@ $TRANSLATIONS = array( "Backup (Replica) Port" => "备份 (镜像) 端口", "Disable Main Server" => "禁用主服务器", "Only connect to the replica server." => "只能连接到复制服务器", -"Case insensitve LDAP server (Windows)" => "大小写敏感LDAP服务器(Windows)", "Turn off SSL certificate validation." => "关闭SSL证书验证", "Cache Time-To-Live" => "缓存存活时间", "in seconds. A change empties the cache." => "以秒计。修改将清空缓存。", diff --git a/apps/user_ldap/l10n/zh_TW.php b/apps/user_ldap/l10n/zh_TW.php index a7ae04cd167850a26f39fe7326e98c690b1465fb..a0975b92238d286b33a223e660e0797600280f57 100644 --- a/apps/user_ldap/l10n/zh_TW.php +++ b/apps/user_ldap/l10n/zh_TW.php @@ -41,7 +41,6 @@ $TRANSLATIONS = array( "Give an optional backup host. It must be a replica of the main LDAP/AD server." => "可以選擇性設定備用主機,必須是 LDAP/AD 中央伺服器的複本。", "Backup (Replica) Port" => "備用(複本)連接埠", "Disable Main Server" => "停用主伺服器", -"Case insensitve LDAP server (Windows)" => "不區分大小寫的 LDAP 伺服器 (Windows)", "Turn off SSL certificate validation." => "關閉 SSL 憑證檢查", "Cache Time-To-Live" => "快取的存活時間", "in seconds. A change empties the cache." => "以秒為單位。變更後會清空快取。", diff --git a/apps/user_ldap/lib/access.php b/apps/user_ldap/lib/access.php index 4d187bab8d56214bbaf35f4bf67182fd467853be..712407505fb9bcac85e102e118ca22493c484ff7 100644 --- a/apps/user_ldap/lib/access.php +++ b/apps/user_ldap/lib/access.php @@ -63,7 +63,6 @@ class Access extends LDAPUtility { return false; } //all or nothing! otherwise we get in trouble with. - $this->initPagedSearch($filter, array($dn), $attr, 99999, 0); $dn = $this->DNasBaseParameter($dn); $rr = @$this->ldap->read($cr, $dn, $filter, array($attr)); if(!$this->ldap->isResource($rr)) { @@ -434,12 +433,27 @@ class Access extends LDAPUtility { $ocname = $this->dn2ocname($ldapObject['dn'], $nameByLDAP, $isUsers); if($ocname) { $ownCloudNames[] = $ocname; + if($isUsers) { + //cache the user names so it does not need to be retrieved + //again later (e.g. sharing dialogue). + $this->cacheUserDisplayName($ocname, $nameByLDAP); + } } continue; } return $ownCloudNames; } + /** + * @brief caches the user display name + * @param string the internal owncloud username + * @param string the display name + */ + public function cacheUserDisplayName($ocname, $displayName) { + $cacheKeyTrunk = 'getDisplayName'; + $this->connection->writeToCache($cacheKeyTrunk.$ocname, $displayName); + } + /** * @brief creates a unique name for internal ownCloud use for users. Don't call it directly. * @param $name the display name of the object @@ -659,7 +673,7 @@ class Access extends LDAPUtility { * @param string $filter */ public function countUsers($filter, $attr = array('dn'), $limit = null, $offset = null) { - return $this->count($filter, $this->connection->ldapBaseGroups, $attr, $limit, $offset); + return $this->count($filter, $this->connection->ldapBaseUsers, $attr, $limit, $offset); } /** @@ -707,6 +721,9 @@ class Access extends LDAPUtility { $linkResources = array_pad(array(), count($base), $cr); $sr = $this->ldap->search($linkResources, $base, $filter, $attr); $error = $this->ldap->errno($cr); + if ($pagedSearchOK) { + $this->ldap->controlPagedResult($cr, 999999, false, ""); + } if(!is_array($sr) || $error !== 0) { \OCP\Util::writeLog('user_ldap', 'Error when searching: '.$this->ldap->error($cr). @@ -775,22 +792,47 @@ class Access extends LDAPUtility { */ private function count($filter, $base, $attr = null, $limit = null, $offset = null, $skipHandling = false) { \OCP\Util::writeLog('user_ldap', 'Count filter: '.print_r($filter, true), \OCP\Util::DEBUG); - $search = $this->executeSearch($filter, $base, $attr, $limit, $offset); - if($search === false) { - return false; + + if(is_null($limit)) { + $limit = $this->connection->ldapPagingSize; } - list($sr, $pagedSearchOK) = $search; - $cr = $this->connection->getConnectionResource(); + $counter = 0; - foreach($sr as $key => $res) { - $count = $this->ldap->countEntries($cr, $res); - if($count !== false) { - $counter += $count; + $count = null; + $cr = $this->connection->getConnectionResource(); + + do { + $continue = false; + $search = $this->executeSearch($filter, $base, $attr, + $limit, $offset); + if($search === false) { + return $counter > 0 ? $counter : false; } - } + list($sr, $pagedSearchOK) = $search; - $this->processPagedSearchStatus($sr, $filter, $base, $counter, $limit, + $count = $this->countEntriesInSearchResults($sr, $limit, $continue); + $counter += $count; + + $this->processPagedSearchStatus($sr, $filter, $base, $count, $limit, $offset, $pagedSearchOK, $skipHandling); + $offset += $limit; + } while($continue); + + return $counter; + } + + private function countEntriesInSearchResults($searchResults, $limit, + &$hasHitLimit) { + $cr = $this->connection->getConnectionResource(); + $count = 0; + + foreach($searchResults as $res) { + $count = intval($this->ldap->countEntries($cr, $res)); + $counter += $count; + if($count === $limit) { + $hasHitLimit = true; + } + } return $counter; } @@ -891,7 +933,7 @@ class Access extends LDAPUtility { //we slice the findings, when //a) paged search insuccessful, though attempted //b) no paged search, but limit set - if((!$this->pagedSearchedSuccessful + if((!$this->getPagedSearchResultState() && $pagedSearchOK) || ( !$pagedSearchOK @@ -923,8 +965,8 @@ class Access extends LDAPUtility { /** * @brief escapes (user provided) parts for LDAP filter - * @param String $input, the provided value - * @returns the escaped string + * @param string $input, the provided value + * @return the escaped string */ public function escapeFilterPart($input) { $search = array('*', '\\', '(', ')'); @@ -1184,7 +1226,7 @@ class Access extends LDAPUtility { } $offset -= $limit; //we work with cache here - $cachekey = 'lc' . crc32($base) . '-' . crc32($filter) . '-' . $limit . '-' . $offset; + $cachekey = 'lc' . crc32($base) . '-' . crc32($filter) . '-' . intval($limit) . '-' . intval($offset); $cookie = ''; if(isset($this->cookies[$cachekey])) { $cookie = $this->cookies[$cachekey]; @@ -1206,7 +1248,7 @@ class Access extends LDAPUtility { */ private function setPagedResultCookie($base, $filter, $limit, $offset, $cookie) { if(!empty($cookie)) { - $cachekey = 'lc' . crc32($base) . '-' . crc32($filter) . '-' .$limit . '-' . $offset; + $cachekey = 'lc' . crc32($base) . '-' . crc32($filter) . '-' .intval($limit) . '-' . intval($offset); $this->cookies[$cachekey] = $cookie; } } @@ -1236,9 +1278,9 @@ class Access extends LDAPUtility { if($this->connection->hasPagedResultSupport && !is_null($limit)) { $offset = intval($offset); //can be null \OCP\Util::writeLog('user_ldap', - 'initializing paged search for Filter'.$filter.' base '.print_r($bases, true) + 'initializing paged search for Filter '.$filter.' base '.print_r($bases, true) .' attr '.print_r($attr, true). ' limit ' .$limit.' offset '.$offset, - \OCP\Util::INFO); + \OCP\Util::DEBUG); //get the cookie from the search for the previous search, required by LDAP foreach($bases as $base) { @@ -1260,7 +1302,7 @@ class Access extends LDAPUtility { } if(!is_null($cookie)) { if($offset > 0) { - \OCP\Util::writeLog('user_ldap', 'Cookie '.$cookie, \OCP\Util::INFO); + \OCP\Util::writeLog('user_ldap', 'Cookie '.CRC32($cookie), \OCP\Util::INFO); } $pagedSearchOK = $this->ldap->controlPagedResult( $this->connection->getConnectionResource(), $limit, diff --git a/apps/user_ldap/lib/connection.php b/apps/user_ldap/lib/connection.php index 08ac4ac626b6a3718eb49fa88c5ee8ef04e366af..173c4ebcc2347008227b8a3ae6835da060304425 100644 --- a/apps/user_ldap/lib/connection.php +++ b/apps/user_ldap/lib/connection.php @@ -134,6 +134,9 @@ class Connection extends LDAPUtility { return $this->ldapConnectionRes; } + /** + * @param string|null $key + */ private function getCacheKey($key) { $prefix = 'LDAP-'.$this->configID.'-'.$this->configPrefix.'-'; if(is_null($key)) { diff --git a/apps/user_ldap/lib/ldap.php b/apps/user_ldap/lib/ldap.php index de9b7481c19d8d1d8e8051159ff0d7e0d5d4670b..d1ca91045b2de06a7185d2b2296203f3d7f8ebf5 100644 --- a/apps/user_ldap/lib/ldap.php +++ b/apps/user_ldap/lib/ldap.php @@ -139,7 +139,9 @@ class LDAP implements ILDAPWrapper { if(function_exists($func)) { $this->preFunctionCall($func, $arguments); $result = call_user_func_array($func, $arguments); - $this->postFunctionCall(); + if ($result === FALSE) { + $this->postFunctionCall(); + } return $result; } } diff --git a/apps/user_ldap/lib/proxy.php b/apps/user_ldap/lib/proxy.php index b27233bcd192d455df1c895a509a1e2c4ee21f6b..0eb294eb7a0d36350c8956490d4b16712d276638 100644 --- a/apps/user_ldap/lib/proxy.php +++ b/apps/user_ldap/lib/proxy.php @@ -80,6 +80,9 @@ abstract class Proxy { return $result; } + /** + * @param string|null $key + */ private function getCacheKey($key) { $prefix = 'LDAP-Proxy-'; if(is_null($key)) { diff --git a/apps/user_ldap/lib/wizard.php b/apps/user_ldap/lib/wizard.php index 3854af617c11afaa21548d1f178e019dfb8d2077..8406b2d42a594e72e8e4bf6e43f496491c585e27 100644 --- a/apps/user_ldap/lib/wizard.php +++ b/apps/user_ldap/lib/wizard.php @@ -485,7 +485,7 @@ class Wizard extends LDAPUtility { /** * @brief sets the found value for the configuration key in the WizardResult * as well as in the Configuration instance - * @param $key the configuration key + * @param string $key the configuration key * @param $value the (detected) value * @return null * @@ -799,6 +799,7 @@ class Wizard extends LDAPUtility { \OCP\Util::writeLog('user_ldap', 'Wiz: Setting LDAP Options ', \OCP\Util::DEBUG); //set LDAP options $this->ldap->setOption($cr, LDAP_OPT_PROTOCOL_VERSION, 3); + $this->ldap->setOption($cr, LDAP_OPT_REFERRALS, 0); $this->ldap->setOption($cr, LDAP_OPT_NETWORK_TIMEOUT, self::LDAP_NW_TIMEOUT); if($tls) { $isTlsWorking = @$this->ldap->startTls($cr); @@ -1000,7 +1001,7 @@ class Wizard extends LDAPUtility { /** * @brief appends a list of values fr * @param $result resource, the return value from ldap_get_attributes - * @param $attribute string, the attribute values to look for + * @param string $attribute the attribute values to look for * @param &$known array, new values will be appended here * @return int, state on of the class constants LRESULT_PROCESSED_OK, * LRESULT_PROCESSED_INVALID or LRESULT_PROCESSED_SKIP diff --git a/apps/user_ldap/lib/wizardresult.php b/apps/user_ldap/lib/wizardresult.php index 542f106cad8dc2b88a80aeac3bdce6ec400045fe..9e0936faa69d182e2d88d99119084c5b6ac88af7 100644 --- a/apps/user_ldap/lib/wizardresult.php +++ b/apps/user_ldap/lib/wizardresult.php @@ -36,6 +36,10 @@ class WizardResult { $this->markedChange = true; } + /** + * @param string $key + * @param array|string $values + */ public function addOptions($key, $values) { if(!is_array($values)) { $values = array($values); diff --git a/apps/user_ldap/settings.php b/apps/user_ldap/settings.php index d077eafdde9c0d09507afedf82578b50b74ee759..6b7d8e6f53e5e4409225a8c155849980011a054f 100644 --- a/apps/user_ldap/settings.php +++ b/apps/user_ldap/settings.php @@ -25,6 +25,7 @@ OC_Util::checkAdminUser(); +OCP\Util::addScript('user_ldap', 'ldapFilter'); OCP\Util::addScript('user_ldap', 'settings'); OCP\Util::addScript('core', 'jquery.multiselect'); OCP\Util::addStyle('user_ldap', 'settings'); diff --git a/apps/user_ldap/templates/settings.php b/apps/user_ldap/templates/settings.php index 32cf44a56b9e0dda1a6f07463271c71b32facc42..03f2b8db090ae98d7e6d81a72c74b03be98b44ff 100644 --- a/apps/user_ldap/templates/settings.php +++ b/apps/user_ldap/templates/settings.php @@ -23,7 +23,7 @@

-

>

+

>


@@ -37,7 +37,7 @@

-

+

t('Special Attributes'));?>

diff --git a/apps/user_ldap/tests/group_ldap.php b/apps/user_ldap/tests/group_ldap.php new file mode 100644 index 0000000000000000000000000000000000000000..ecbd42319e319706f791fde97bcca59c4d0b7ddf --- /dev/null +++ b/apps/user_ldap/tests/group_ldap.php @@ -0,0 +1,115 @@ + +* +* 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 . +* +*/ + +namespace OCA\user_ldap\tests; + +namespace OCA\user_ldap\tests; + +use \OCA\user_ldap\GROUP_LDAP as GroupLDAP; +use \OCA\user_ldap\lib\Access; +use \OCA\user_ldap\lib\Connection; +use \OCA\user_ldap\lib\ILDAPWrapper; + +class Test_Group_Ldap extends \PHPUnit_Framework_TestCase { + private function getAccessMock() { + static $conMethods; + static $accMethods; + + if(is_null($conMethods) || is_null($accMethods)) { + $conMethods = get_class_methods('\OCA\user_ldap\lib\Connection'); + $accMethods = get_class_methods('\OCA\user_ldap\lib\Access'); + } + $lw = $this->getMock('\OCA\user_ldap\lib\ILDAPWrapper'); + $connector = $this->getMock('\OCA\user_ldap\lib\Connection', + $conMethods, + array($lw, null, null)); + $access = $this->getMock('\OCA\user_ldap\lib\Access', + $accMethods, + array($connector, $lw)); + + return $access; + } + + private function enableGroups($access) { + $access->connection->expects($this->any()) + ->method('__get') + ->will($this->returnCallback(function($name) { +// if($name === 'ldapLoginFilter') { +// return '%uid'; +// } + return 1; + })); + } + + public function testCountEmptySearchString() { + $access = $this->getAccessMock(); + + $this->enableGroups($access); + + $access->expects($this->any()) + ->method('groupname2dn') + ->will($this->returnValue('cn=group,dc=foo,dc=bar')); + + $access->expects($this->any()) + ->method('readAttribute') + ->will($this->returnValue(array('u11', 'u22', 'u33', 'u34'))); + + $groupBackend = new GroupLDAP($access); + $users = $groupBackend->countUsersInGroup('group'); + + $this->assertSame(4, $users); + } + + public function testCountWithSearchString() { + $access = $this->getAccessMock(); + + $this->enableGroups($access); + + $access->expects($this->any()) + ->method('groupname2dn') + ->will($this->returnValue('cn=group,dc=foo,dc=bar')); + + $access->expects($this->any()) + ->method('readAttribute') + ->will($this->returnCallback(function($name) { + //the search operation will call readAttribute, thus we need + //to anaylze the "dn". All other times we just need to return + //something that is neither null or false, but once an array + //with the users in the group – so we do so all other times for + //simplicicity. + if(strpos($name, 'u') === 0) { + return strpos($name, '3'); + } + return array('u11', 'u22', 'u33', 'u34'); + })); + + $access->expects($this->any()) + ->method('dn2username') + ->will($this->returnValue('foobar')); + + $groupBackend = new GroupLDAP($access); + $users = $groupBackend->countUsersInGroup('group', '3'); + + $this->assertSame(2, $users); + } + +} \ No newline at end of file diff --git a/config/.htaccess b/config/.htaccess new file mode 100644 index 0000000000000000000000000000000000000000..2421e9a16311e41d0f5f48d931a25e6af360fda6 --- /dev/null +++ b/config/.htaccess @@ -0,0 +1,12 @@ +# line below if for Apache 2.4 + +Require all denied + + +# line below if for Apache 2.2 + +deny from all + + +# section for Apache 2.2 and 2.4 +IndexIgnore * diff --git a/core/ajax/share.php b/core/ajax/share.php index e667d9b5faae6a282845ff361976d4aabd31daca..2b41bd8a5da17fb8693f50c658d3e6dd77db546b 100644 --- a/core/ajax/share.php +++ b/core/ajax/share.php @@ -205,6 +205,34 @@ if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSo OC_JSON::success(array('data' => array('reshare' => $reshare, 'shares' => $shares))); } break; + case 'getShareWithEmail': + $result = array(); + if (isset($_GET['search'])) { + $cm = OC::$server->getContactsManager(); + if (!is_null($cm) && $cm->isEnabled()) { + $contacts = $cm->search($_GET['search'], array('FN', 'EMAIL')); + foreach ($contacts as $contact) { + if (!isset($contact['EMAIL'])) { + continue; + } + + $emails = $contact['EMAIL']; + if (!is_array($emails)) { + $emails = array($emails); + } + + foreach($emails as $email) { + $result[] = array( + 'id' => $contact['id'], + 'email' => $email, + 'displayname' => $contact['FN'], + ); + } + } + } + } + OC_JSON::success(array('data' => $result)); + break; case 'getShareWith': if (isset($_GET['search'])) { $sharePolicy = OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global'); diff --git a/core/css/mobile.css b/core/css/mobile.css index c67ac3e5ecf82d3a3563c9ad2e7fade5593721f2..fd0628d7e28150169d8b6211cb9dd8b84a088739 100644 --- a/core/css/mobile.css +++ b/core/css/mobile.css @@ -7,6 +7,11 @@ background-position: right 26px; padding-right: 16px !important; } +/* do not show menu toggle on public share links as there is no menu */ +#body-public #owncloud.menutoggle { + background-image: none; + padding-right: 0 !important; +} /* compress search box on mobile, expand when focused */ .searchbox input[type="search"] { @@ -19,6 +24,7 @@ .searchbox input[type="search"]:focus, .searchbox input[type="search"]:active { width: 155px; + max-width: 50%; } /* do not show display name on mobile when profile picture is present */ @@ -102,4 +108,12 @@ } +/* fix error display on smaller screens */ +.error-wide { + width: 100%; + margin-left: 0 !important; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + } diff --git a/core/css/styles.css b/core/css/styles.css index 57e2c4479a1c561e816ad1c1a60ca19309d78e10..c26c556825ad36b3a9669492ed1effc38e20b2ec 100644 --- a/core/css/styles.css +++ b/core/css/styles.css @@ -12,13 +12,30 @@ table, td, th { vertical-align:middle; } a { border:0; color:#000; text-decoration:none;} a, a *, input, input *, select, .button span, label { cursor:pointer; } ul { list-style:none; } -body { background:#fefefe; font:normal .8em/1.6em "Helvetica Neue",Helvetica,Arial,FreeSans,sans-serif; color:#000; } + + +body { + background: #fefefe; + font: normal .8em/1.6em "Helvetica Neue",Helvetica,Arial,FreeSans,sans-serif; + color: #000; + height: auto; +} /* HEADERS */ -#body-user #header, #body-settings #header { - position:fixed; top:0; left:0; right:0; z-index:100; height:45px; line-height:2.5em; - background:#1d2d44 url('../img/noise.png') repeat; +#body-user #header, +#body-settings #header, +#body-public #header { + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: 100; + height: 45px; + line-height: 2.5em; + background: #1d2d44 url('../img/noise.png') repeat; + -moz-box-sizing: border-box; + box-sizing: border-box; } #body-login { @@ -33,9 +50,34 @@ body { background:#fefefe; font:normal .8em/1.6em "Helvetica Neue",Helvetica,Ari filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#35537a', endColorstr='#1d2d44',GradientType=0 ); /* IE6-9 */ } -#owncloud { position:absolute; top:0; left:0; padding:6px; padding-bottom:0; } -.header-right { float:right; vertical-align:middle; padding:0.5em; } -.header-right > * { vertical-align:middle; } +#owncloud { + position: absolute; + top: 0; + left: 0; + padding: 6px; + padding-bottom: 0; +} + +/* info part on the right, used e.g. for info on who shared something */ +.header-right { + position: absolute; + right: 0; + padding-right: 10px; + color: #fff; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"; + filter: alpha(opacity=50); + opacity: .5; + height: 100%; + max-width: 40%; + white-space: nowrap; +} +.header-right #details { + display: inline-block; + margin-top: 6px; + width: 100%; + text-overflow: ellipsis; + overflow: hidden; +} /* Profile picture in header */ #header .avatardiv { @@ -406,14 +448,29 @@ input[type="submit"].enabled { } /* Icons for username and password fields to better recognize them */ -#adminlogin, #adminpass, #user, #password { width:11.7em!important; padding-left:1.8em; } -#adminlogin+label+img, #adminpass-icon, #user+label+img, #password-icon { - position:absolute; left:1.25em; top:1.65em; - -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=30)"; filter:alpha(opacity=30); opacity:.3; +#adminlogin, +#adminpass, +input[name='adminpass-clone'], +#user, +#password, +input[name='password-clone'] { + width: 223px !important; + padding-left: 36px !important; +} +#adminlogin+label+img, +#adminpass-icon, +#user+label+img, +#password-icon { + position: absolute; + left: 1.25em; + top: 1.65em; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=30)"; + filter: alpha(opacity=30); + opacity: .3; +} +#adminpass-icon, #password-icon { + top: 1.1em; } -#adminpass-icon, #password-icon { top:1.1em; } -input[name="password-clone"] { padding-left:1.8em; width:11.7em !important; } -input[name="adminpass-clone"] { padding-left:1.8em; width:11.7em !important; } /* General new input field look */ #body-login input[type="text"], @@ -452,15 +509,22 @@ input[name="adminpass-clone"] { padding-left:1.8em; width:11.7em !important; } p.infield { position:relative; } label.infield { cursor:text !important; top:1.05em; left:.85em; } #body-login form label.infield { /* labels are ellipsized when too long, keep them short */ - position:absolute; width:90%; padding-left:1.4em; - font-size:19px; color:#aaa; - white-space:nowrap; overflow:hidden; text-overflow:ellipsis; + position: absolute; + width: 82%; + margin-left: 26px; + font-size: 19px; + color: #aaa; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +#body-login #databaseField .infield { + margin-left: 0; } -#body-login #databaseField .infield { padding-left:0; } #body-login form input[type="checkbox"]+label { position: relative; margin: 0; - font-size: 1em; + font-size: 13px; padding: 14px; padding-left: 28px; margin-left: -28px; @@ -586,8 +650,8 @@ label.infield { cursor:text !important; top:1.05em; left:.85em; } } .error-wide { - width: 800px; - margin-left: -250px; + width: 700px; + margin-left: -200px !important; } /* Fixes for log in page, TODO should be removed some time */ @@ -614,11 +678,13 @@ label.infield { cursor:text !important; top:1.05em; left:.85em; } /* Log in and install button */ #body-login input { - font-size: 1.5em; + font-size: 20px; + margin: 5px; + padding: 12px 10px 8px; } #body-login input[type="text"], #body-login input[type="password"] { - width: 13em; + width: 249px; } #body-login input.login { width: auto; @@ -628,7 +694,7 @@ label.infield { cursor:text !important; top:1.05em; left:.85em; } padding: 10px 20px; /* larger log in and installation buttons */ } #remember_login { - margin: 18px 5px 0 18px; + margin: 24px 5px 0 16px !important; vertical-align: text-bottom; } diff --git a/core/img/actions/add.png b/core/img/actions/add.png index 3c051e4d73e64a7e17df89d6e1344643b515d6ea..8ae17cfe11b4365254e1acbc1c0ade0793f25bef 100644 Binary files a/core/img/actions/add.png and b/core/img/actions/add.png differ diff --git a/core/img/actions/add.svg b/core/img/actions/add.svg index 250746e1660b64662fe0654a67fd5de75b4f8136..ecbab6f13acef9508370929a8f9f9ac23e161bd0 100644 --- a/core/img/actions/add.svg +++ b/core/img/actions/add.svg @@ -1,6 +1,5 @@ - - - + + diff --git a/core/img/actions/close.png b/core/img/actions/close.png index 3389c66e03b92db38d2ae6ffb41f1971d83c86bb..ece33258e5676951d9aa54ae634aa6948b0c0e4d 100644 Binary files a/core/img/actions/close.png and b/core/img/actions/close.png differ diff --git a/core/img/actions/close.svg b/core/img/actions/close.svg index ef564bfd482292f589ad750827209a6b1580fd99..4471dbc6301ebe7962f2b9f47be0ad5de0bcf9e7 100644 --- a/core/img/actions/close.svg +++ b/core/img/actions/close.svg @@ -1,6 +1,3 @@ - - - - + diff --git a/core/img/actions/delete-hover.png b/core/img/actions/delete-hover.png index 48e6c089c9de1dfb777d052e0f7a54a7c049b3d3..3f8cb6eff92db2d16b041818ada8b4b5f343bec0 100644 Binary files a/core/img/actions/delete-hover.png and b/core/img/actions/delete-hover.png differ diff --git a/core/img/actions/delete-hover.svg b/core/img/actions/delete-hover.svg index 9e5150359dedfeed04cf092dce968f7681daf833..9583ec15b32e00d0a0622d33c348cd8d986c9086 100644 --- a/core/img/actions/delete-hover.svg +++ b/core/img/actions/delete-hover.svg @@ -1,4 +1,4 @@ - + diff --git a/core/img/actions/delete.png b/core/img/actions/delete.png index 3389c66e03b92db38d2ae6ffb41f1971d83c86bb..e891b370cca7b3d83e937863dbbc9578c5b8b1bd 100644 Binary files a/core/img/actions/delete.png and b/core/img/actions/delete.png differ diff --git a/core/img/actions/delete.svg b/core/img/actions/delete.svg index ef564bfd482292f589ad750827209a6b1580fd99..f0a3cd4db889acc95eda1df31d164ca4e0f69b21 100644 --- a/core/img/actions/delete.svg +++ b/core/img/actions/delete.svg @@ -1,6 +1,4 @@ - - - + diff --git a/core/img/actions/download.png b/core/img/actions/download.png index 0f71a5a776f1194b6a0a1adf01df4490e0cb2104..1f8e1a4f7e167efc019708a635eb763445dc255b 100644 Binary files a/core/img/actions/download.png and b/core/img/actions/download.png differ diff --git a/core/img/actions/download.svg b/core/img/actions/download.svg index a469c3b8a00e9620fc5439830ea581a425281efb..0d698bca8dc6376db5b2da8b8cb6bc035033372f 100644 --- a/core/img/actions/download.svg +++ b/core/img/actions/download.svg @@ -1,6 +1,5 @@ - - + diff --git a/core/img/actions/upload.png b/core/img/actions/upload.png index f6a0c4cfa835dec886379ef770aff694e9d3a1b3..a6969c23fa6640726d3300a233ff15264c0b32c6 100644 Binary files a/core/img/actions/upload.png and b/core/img/actions/upload.png differ diff --git a/core/img/actions/upload.svg b/core/img/actions/upload.svg index eae4515c7223bb00ef1642f15ebb4b25019d3b74..80231797c9693458443ab6c75be31cfa1918f7a8 100644 --- a/core/img/actions/upload.svg +++ b/core/img/actions/upload.svg @@ -1,6 +1,4 @@ - - - - + + diff --git a/core/img/actions/view-close.png b/core/img/actions/view-close.png index 8422b733466565267ab8be1627ebc1c988f3c12f..c21f6ee30e780f09554e76839c212e6cf3e34425 100644 Binary files a/core/img/actions/view-close.png and b/core/img/actions/view-close.png differ diff --git a/core/img/actions/view-close.svg b/core/img/actions/view-close.svg index 1d5b1a9f49fe2bb76700f6e08f8cfe7f3b7d12a6..89d1fab88dd7979c9abff43c32fef488c7d64ad7 100644 --- a/core/img/actions/view-close.svg +++ b/core/img/actions/view-close.svg @@ -1,6 +1,3 @@ - - - - + diff --git a/core/js/eventsource.js b/core/js/eventsource.js index 536b180bc8f9d5ca989a864d21cc2304ca72e462..70f4a2a9aa84f5dc5b7b2dbbd971812c5e1e8bfe 100644 --- a/core/js/eventsource.js +++ b/core/js/eventsource.js @@ -20,16 +20,16 @@ */ /** - * wrapper for server side events (http://en.wikipedia.org/wiki/Server-sent_events) + * Wrapper for server side events (http://en.wikipedia.org/wiki/Server-sent_events) * includes a fallback for older browsers and IE * - * use server side events with causion, to many open requests can hang the server + * Use server side events with caution, too many open requests can hang the server */ /** - * create a new event source - * @param string src - * @param object data to be send as GET + * Create a new event source + * @param {string} src + * @param {object} [data] to be send as GET */ OC.EventSource=function(src,data){ var dataStr=''; @@ -74,12 +74,12 @@ OC.EventSource=function(src,data){ this.close(); } }.bind(this)); -} +}; OC.EventSource.fallBackSources=[]; OC.EventSource.iframeCount=0;//number of fallback iframes OC.EventSource.fallBackCallBack=function(id,type,data){ OC.EventSource.fallBackSources[id].fallBackCallBack(type,data); -} +}; OC.EventSource.prototype={ typelessListeners:[], iframe:null, @@ -127,4 +127,4 @@ OC.EventSource.prototype={ this.source.close(); } } -} +}; diff --git a/core/js/jquery-1.10.0.js b/core/js/jquery-1.10.0.js new file mode 100644 index 0000000000000000000000000000000000000000..f38148cf1258b921ea04f2f6fbf9637ce953b1ff --- /dev/null +++ b/core/js/jquery-1.10.0.js @@ -0,0 +1,9800 @@ +/*! + * jQuery JavaScript Library v1.10.0 + * http://jquery.com/ + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * + * Copyright 2005, 2013 jQuery Foundation, Inc. and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2013-05-24T18:39Z + */ +(function( window, undefined ) { + +// Can't do this because several apps including ASP.NET trace +// the stack via arguments.caller.callee and Firefox dies if +// you try to trace through "use strict" call chains. (#13335) +// Support: Firefox 18+ +//"use strict"; +var + // The deferred used on DOM ready + readyList, + + // A central reference to the root jQuery(document) + rootjQuery, + + // Support: IE<10 + // For `typeof xmlNode.method` instead of `xmlNode.method !== undefined` + core_strundefined = typeof undefined, + + // Use the correct document accordingly with window argument (sandbox) + location = window.location, + document = window.document, + docElem = document.documentElement, + + // Map over jQuery in case of overwrite + _jQuery = window.jQuery, + + // Map over the $ in case of overwrite + _$ = window.$, + + // [[Class]] -> type pairs + class2type = {}, + + // List of deleted data cache ids, so we can reuse them + core_deletedIds = [], + + core_version = "1.10.0", + + // Save a reference to some core methods + core_concat = core_deletedIds.concat, + core_push = core_deletedIds.push, + core_slice = core_deletedIds.slice, + core_indexOf = core_deletedIds.indexOf, + core_toString = class2type.toString, + core_hasOwn = class2type.hasOwnProperty, + core_trim = core_version.trim, + + // Define a local copy of jQuery + jQuery = function( selector, context ) { + // The jQuery object is actually just the init constructor 'enhanced' + return new jQuery.fn.init( selector, context, rootjQuery ); + }, + + // Used for matching numbers + core_pnum = /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source, + + // Used for splitting on whitespace + core_rnotwhite = /\S+/g, + + // Make sure we trim BOM and NBSP (here's looking at you, Safari 5.0 and IE) + rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, + + // A simple way to check for HTML strings + // Prioritize #id over to avoid XSS via location.hash (#9521) + // Strict HTML recognition (#11290: must start with <) + rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/, + + // Match a standalone tag + rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>|)$/, + + // JSON RegExp + rvalidchars = /^[\],:{}\s]*$/, + rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g, + rvalidescape = /\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g, + rvalidtokens = /"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g, + + // Matches dashed string for camelizing + rmsPrefix = /^-ms-/, + rdashAlpha = /-([\da-z])/gi, + + // Used by jQuery.camelCase as callback to replace() + fcamelCase = function( all, letter ) { + return letter.toUpperCase(); + }, + + // The ready event handler + completed = function( event ) { + + // readyState === "complete" is good enough for us to call the dom ready in oldIE + if ( document.addEventListener || event.type === "load" || document.readyState === "complete" ) { + detach(); + jQuery.ready(); + } + }, + // Clean-up method for dom ready events + detach = function() { + if ( document.addEventListener ) { + document.removeEventListener( "DOMContentLoaded", completed, false ); + window.removeEventListener( "load", completed, false ); + + } else { + document.detachEvent( "onreadystatechange", completed ); + window.detachEvent( "onload", completed ); + } + }; + +jQuery.fn = jQuery.prototype = { + // The current version of jQuery being used + jquery: core_version, + + constructor: jQuery, + init: function( selector, context, rootjQuery ) { + var match, elem; + + // HANDLE: $(""), $(null), $(undefined), $(false) + if ( !selector ) { + return this; + } + + // Handle HTML strings + if ( typeof selector === "string" ) { + if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = rquickExpr.exec( selector ); + } + + // Match html or make sure no context is specified for #id + if ( match && (match[1] || !context) ) { + + // HANDLE: $(html) -> $(array) + if ( match[1] ) { + context = context instanceof jQuery ? context[0] : context; + + // scripts is true for back-compat + jQuery.merge( this, jQuery.parseHTML( + match[1], + context && context.nodeType ? context.ownerDocument || context : document, + true + ) ); + + // HANDLE: $(html, props) + if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) { + for ( match in context ) { + // Properties of context are called as methods if possible + if ( jQuery.isFunction( this[ match ] ) ) { + this[ match ]( context[ match ] ); + + // ...and otherwise set as attributes + } else { + this.attr( match, context[ match ] ); + } + } + } + + return this; + + // HANDLE: $(#id) + } else { + elem = document.getElementById( match[2] ); + + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem.id !== match[2] ) { + return rootjQuery.find( selector ); + } + + // Otherwise, we inject the element directly into the jQuery object + this.length = 1; + this[0] = elem; + } + + this.context = document; + this.selector = selector; + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || rootjQuery ).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(DOMElement) + } else if ( selector.nodeType ) { + this.context = this[0] = selector; + this.length = 1; + return this; + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( jQuery.isFunction( selector ) ) { + return rootjQuery.ready( selector ); + } + + if ( selector.selector !== undefined ) { + this.selector = selector.selector; + this.context = selector.context; + } + + return jQuery.makeArray( selector, this ); + }, + + // Start with an empty selector + selector: "", + + // The default length of a jQuery object is 0 + length: 0, + + toArray: function() { + return core_slice.call( this ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + return num == null ? + + // Return a 'clean' array + this.toArray() : + + // Return just the object + ( num < 0 ? this[ this.length + num ] : this[ num ] ); + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems ) { + + // Build a new jQuery matched element set + var ret = jQuery.merge( this.constructor(), elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + ret.context = this.context; + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + // (You can seed the arguments with an array of args, but this is + // only used internally.) + each: function( callback, args ) { + return jQuery.each( this, callback, args ); + }, + + ready: function( fn ) { + // Add the callback + jQuery.ready.promise().done( fn ); + + return this; + }, + + slice: function() { + return this.pushStack( core_slice.apply( this, arguments ) ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + eq: function( i ) { + var len = this.length, + j = +i + ( i < 0 ? len : 0 ); + return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map(this, function( elem, i ) { + return callback.call( elem, i, elem ); + })); + }, + + end: function() { + return this.prevObject || this.constructor(null); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: core_push, + sort: [].sort, + splice: [].splice +}; + +// Give the init function the jQuery prototype for later instantiation +jQuery.fn.init.prototype = jQuery.fn; + +jQuery.extend = jQuery.fn.extend = function() { + var src, copyIsArray, copy, name, options, clone, + target = arguments[0] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + target = arguments[1] || {}; + // skip the boolean and the target + i = 2; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !jQuery.isFunction(target) ) { + target = {}; + } + + // extend jQuery itself if only one argument is passed + if ( length === i ) { + target = this; + --i; + } + + for ( ; i < length; i++ ) { + // Only deal with non-null/undefined values + if ( (options = arguments[ i ]) != null ) { + // Extend the base object + for ( name in options ) { + src = target[ name ]; + copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { + if ( copyIsArray ) { + copyIsArray = false; + clone = src && jQuery.isArray(src) ? src : []; + + } else { + clone = src && jQuery.isPlainObject(src) ? src : {}; + } + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend({ + // Unique for each copy of jQuery on the page + // Non-digits removed to match rinlinejQuery + expando: "jQuery" + ( core_version + Math.random() ).replace( /\D/g, "" ), + + noConflict: function( deep ) { + if ( window.$ === jQuery ) { + window.$ = _$; + } + + if ( deep && window.jQuery === jQuery ) { + window.jQuery = _jQuery; + } + + return jQuery; + }, + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Hold (or release) the ready event + holdReady: function( hold ) { + if ( hold ) { + jQuery.readyWait++; + } else { + jQuery.ready( true ); + } + }, + + // Handle when the DOM is ready + ready: function( wait ) { + + // Abort if there are pending holds or we're already ready + if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { + return; + } + + // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). + if ( !document.body ) { + return setTimeout( jQuery.ready ); + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.resolveWith( document, [ jQuery ] ); + + // Trigger any bound ready events + if ( jQuery.fn.trigger ) { + jQuery( document ).trigger("ready").off("ready"); + } + }, + + // See test/unit/core.js for details concerning isFunction. + // Since version 1.3, DOM methods and functions like alert + // aren't supported. They return false on IE (#2968). + isFunction: function( obj ) { + return jQuery.type(obj) === "function"; + }, + + isArray: Array.isArray || function( obj ) { + return jQuery.type(obj) === "array"; + }, + + isWindow: function( obj ) { + /* jshint eqeqeq: false */ + return obj != null && obj == obj.window; + }, + + isNumeric: function( obj ) { + return !isNaN( parseFloat(obj) ) && isFinite( obj ); + }, + + type: function( obj ) { + if ( obj == null ) { + return String( obj ); + } + return typeof obj === "object" || typeof obj === "function" ? + class2type[ core_toString.call(obj) ] || "object" : + typeof obj; + }, + + isPlainObject: function( obj ) { + var key; + + // Must be an Object. + // Because of IE, we also have to check the presence of the constructor property. + // Make sure that DOM nodes and window objects don't pass through, as well + if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { + return false; + } + + try { + // Not own constructor property must be Object + if ( obj.constructor && + !core_hasOwn.call(obj, "constructor") && + !core_hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { + return false; + } + } catch ( e ) { + // IE8,9 Will throw exceptions on certain host objects #9897 + return false; + } + + // Support: IE<9 + // Handle iteration over inherited properties before own properties. + if ( jQuery.support.ownLast ) { + for ( key in obj ) { + return core_hasOwn.call( obj, key ); + } + } + + // Own properties are enumerated firstly, so to speed up, + // if last one is own, then all properties are own. + for ( key in obj ) {} + + return key === undefined || core_hasOwn.call( obj, key ); + }, + + isEmptyObject: function( obj ) { + var name; + for ( name in obj ) { + return false; + } + return true; + }, + + error: function( msg ) { + throw new Error( msg ); + }, + + // data: string of html + // context (optional): If specified, the fragment will be created in this context, defaults to document + // keepScripts (optional): If true, will include scripts passed in the html string + parseHTML: function( data, context, keepScripts ) { + if ( !data || typeof data !== "string" ) { + return null; + } + if ( typeof context === "boolean" ) { + keepScripts = context; + context = false; + } + context = context || document; + + var parsed = rsingleTag.exec( data ), + scripts = !keepScripts && []; + + // Single tag + if ( parsed ) { + return [ context.createElement( parsed[1] ) ]; + } + + parsed = jQuery.buildFragment( [ data ], context, scripts ); + if ( scripts ) { + jQuery( scripts ).remove(); + } + return jQuery.merge( [], parsed.childNodes ); + }, + + parseJSON: function( data ) { + // Attempt to parse using the native JSON parser first + if ( window.JSON && window.JSON.parse ) { + return window.JSON.parse( data ); + } + + if ( data === null ) { + return data; + } + + if ( typeof data === "string" ) { + + // Make sure leading/trailing whitespace is removed (IE can't handle it) + data = jQuery.trim( data ); + + if ( data ) { + // Make sure the incoming data is actual JSON + // Logic borrowed from http://json.org/json2.js + if ( rvalidchars.test( data.replace( rvalidescape, "@" ) + .replace( rvalidtokens, "]" ) + .replace( rvalidbraces, "")) ) { + + return ( new Function( "return " + data ) )(); + } + } + } + + jQuery.error( "Invalid JSON: " + data ); + }, + + // Cross-browser xml parsing + parseXML: function( data ) { + var xml, tmp; + if ( !data || typeof data !== "string" ) { + return null; + } + try { + if ( window.DOMParser ) { // Standard + tmp = new DOMParser(); + xml = tmp.parseFromString( data , "text/xml" ); + } else { // IE + xml = new ActiveXObject( "Microsoft.XMLDOM" ); + xml.async = "false"; + xml.loadXML( data ); + } + } catch( e ) { + xml = undefined; + } + if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) { + jQuery.error( "Invalid XML: " + data ); + } + return xml; + }, + + noop: function() {}, + + // Evaluates a script in a global context + // Workarounds based on findings by Jim Driscoll + // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context + globalEval: function( data ) { + if ( data && jQuery.trim( data ) ) { + // We use execScript on Internet Explorer + // We use an anonymous function so that context is window + // rather than jQuery in Firefox + ( window.execScript || function( data ) { + window[ "eval" ].call( window, data ); + } )( data ); + } + }, + + // Convert dashed to camelCase; used by the css and data modules + // Microsoft forgot to hump their vendor prefix (#9572) + camelCase: function( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); + }, + + nodeName: function( elem, name ) { + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + }, + + // args is for internal usage only + each: function( obj, callback, args ) { + var value, + i = 0, + length = obj.length, + isArray = isArraylike( obj ); + + if ( args ) { + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback.apply( obj[ i ], args ); + + if ( value === false ) { + break; + } + } + } else { + for ( i in obj ) { + value = callback.apply( obj[ i ], args ); + + if ( value === false ) { + break; + } + } + } + + // A special, fast, case for the most common use of each + } else { + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback.call( obj[ i ], i, obj[ i ] ); + + if ( value === false ) { + break; + } + } + } else { + for ( i in obj ) { + value = callback.call( obj[ i ], i, obj[ i ] ); + + if ( value === false ) { + break; + } + } + } + } + + return obj; + }, + + // Use native String.trim function wherever possible + trim: core_trim && !core_trim.call("\uFEFF\xA0") ? + function( text ) { + return text == null ? + "" : + core_trim.call( text ); + } : + + // Otherwise use our own trimming functionality + function( text ) { + return text == null ? + "" : + ( text + "" ).replace( rtrim, "" ); + }, + + // results is for internal usage only + makeArray: function( arr, results ) { + var ret = results || []; + + if ( arr != null ) { + if ( isArraylike( Object(arr) ) ) { + jQuery.merge( ret, + typeof arr === "string" ? + [ arr ] : arr + ); + } else { + core_push.call( ret, arr ); + } + } + + return ret; + }, + + inArray: function( elem, arr, i ) { + var len; + + if ( arr ) { + if ( core_indexOf ) { + return core_indexOf.call( arr, elem, i ); + } + + len = arr.length; + i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0; + + for ( ; i < len; i++ ) { + // Skip accessing in sparse arrays + if ( i in arr && arr[ i ] === elem ) { + return i; + } + } + } + + return -1; + }, + + merge: function( first, second ) { + var l = second.length, + i = first.length, + j = 0; + + if ( typeof l === "number" ) { + for ( ; j < l; j++ ) { + first[ i++ ] = second[ j ]; + } + } else { + while ( second[j] !== undefined ) { + first[ i++ ] = second[ j++ ]; + } + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, inv ) { + var retVal, + ret = [], + i = 0, + length = elems.length; + inv = !!inv; + + // Go through the array, only saving the items + // that pass the validator function + for ( ; i < length; i++ ) { + retVal = !!callback( elems[ i ], i ); + if ( inv !== retVal ) { + ret.push( elems[ i ] ); + } + } + + return ret; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var value, + i = 0, + length = elems.length, + isArray = isArraylike( elems ), + ret = []; + + // Go through the array, translating each of the items to their + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret[ ret.length ] = value; + } + } + + // Go through every key on the object, + } else { + for ( i in elems ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret[ ret.length ] = value; + } + } + } + + // Flatten any nested arrays + return core_concat.apply( [], ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // Bind a function to a context, optionally partially applying any + // arguments. + proxy: function( fn, context ) { + var args, proxy, tmp; + + if ( typeof context === "string" ) { + tmp = fn[ context ]; + context = fn; + fn = tmp; + } + + // Quick check to determine if target is callable, in the spec + // this throws a TypeError, but we will just return undefined. + if ( !jQuery.isFunction( fn ) ) { + return undefined; + } + + // Simulated bind + args = core_slice.call( arguments, 2 ); + proxy = function() { + return fn.apply( context || this, args.concat( core_slice.call( arguments ) ) ); + }; + + // Set the guid of unique handler to the same of original handler, so it can be removed + proxy.guid = fn.guid = fn.guid || jQuery.guid++; + + return proxy; + }, + + // Multifunctional method to get and set values of a collection + // The value/s can optionally be executed if it's a function + access: function( elems, fn, key, value, chainable, emptyGet, raw ) { + var i = 0, + length = elems.length, + bulk = key == null; + + // Sets many values + if ( jQuery.type( key ) === "object" ) { + chainable = true; + for ( i in key ) { + jQuery.access( elems, fn, i, key[i], true, emptyGet, raw ); + } + + // Sets one value + } else if ( value !== undefined ) { + chainable = true; + + if ( !jQuery.isFunction( value ) ) { + raw = true; + } + + if ( bulk ) { + // Bulk operations run against the entire set + if ( raw ) { + fn.call( elems, value ); + fn = null; + + // ...except when executing function values + } else { + bulk = fn; + fn = function( elem, key, value ) { + return bulk.call( jQuery( elem ), value ); + }; + } + } + + if ( fn ) { + for ( ; i < length; i++ ) { + fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) ); + } + } + } + + return chainable ? + elems : + + // Gets + bulk ? + fn.call( elems ) : + length ? fn( elems[0], key ) : emptyGet; + }, + + now: function() { + return ( new Date() ).getTime(); + }, + + // A method for quickly swapping in/out CSS properties to get correct calculations. + // Note: this method belongs to the css module but it's needed here for the support module. + // If support gets modularized, this method should be moved back to the css module. + swap: function( elem, options, callback, args ) { + var ret, name, + old = {}; + + // Remember the old values, and insert the new ones + for ( name in options ) { + old[ name ] = elem.style[ name ]; + elem.style[ name ] = options[ name ]; + } + + ret = callback.apply( elem, args || [] ); + + // Revert the old values + for ( name in options ) { + elem.style[ name ] = old[ name ]; + } + + return ret; + } +}); + +jQuery.ready.promise = function( obj ) { + if ( !readyList ) { + + readyList = jQuery.Deferred(); + + // Catch cases where $(document).ready() is called after the browser event has already occurred. + // we once tried to use readyState "interactive" here, but it caused issues like the one + // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15 + if ( document.readyState === "complete" ) { + // Handle it asynchronously to allow scripts the opportunity to delay ready + setTimeout( jQuery.ready ); + + // Standards-based browsers support DOMContentLoaded + } else if ( document.addEventListener ) { + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", completed, false ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", completed, false ); + + // If IE event model is used + } else { + // Ensure firing before onload, maybe late but safe also for iframes + document.attachEvent( "onreadystatechange", completed ); + + // A fallback to window.onload, that will always work + window.attachEvent( "onload", completed ); + + // If IE and not a frame + // continually check to see if the document is ready + var top = false; + + try { + top = window.frameElement == null && document.documentElement; + } catch(e) {} + + if ( top && top.doScroll ) { + (function doScrollCheck() { + if ( !jQuery.isReady ) { + + try { + // Use the trick by Diego Perini + // http://javascript.nwbox.com/IEContentLoaded/ + top.doScroll("left"); + } catch(e) { + return setTimeout( doScrollCheck, 50 ); + } + + // detach all dom ready events + detach(); + + // and execute any waiting functions + jQuery.ready(); + } + })(); + } + } + } + return readyList.promise( obj ); +}; + +// Populate the class2type map +jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +}); + +function isArraylike( obj ) { + var length = obj.length, + type = jQuery.type( obj ); + + if ( jQuery.isWindow( obj ) ) { + return false; + } + + if ( obj.nodeType === 1 && length ) { + return true; + } + + return type === "array" || type !== "function" && + ( length === 0 || + typeof length === "number" && length > 0 && ( length - 1 ) in obj ); +} + +// All jQuery objects should point back to these +rootjQuery = jQuery(document); +/*! + * Sizzle CSS Selector Engine v1.9.4-pre + * http://sizzlejs.com/ + * + * Copyright 2013 jQuery Foundation, Inc. and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2013-05-15 + */ +(function( window, undefined ) { + +var i, + support, + cachedruns, + Expr, + getText, + isXML, + compile, + outermostContext, + sortInput, + + // Local document vars + setDocument, + document, + docElem, + documentIsHTML, + rbuggyQSA, + rbuggyMatches, + matches, + contains, + + // Instance-specific data + expando = "sizzle" + -(new Date()), + preferredDoc = window.document, + dirruns = 0, + done = 0, + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + hasDuplicate = false, + sortOrder = function() { return 0; }, + + // General-purpose constants + strundefined = typeof undefined, + MAX_NEGATIVE = 1 << 31, + + // Instance methods + hasOwn = ({}).hasOwnProperty, + arr = [], + pop = arr.pop, + push_native = arr.push, + push = arr.push, + slice = arr.slice, + // Use a stripped-down indexOf if we can't use a native one + indexOf = arr.indexOf || function( elem ) { + var i = 0, + len = this.length; + for ( ; i < len; i++ ) { + if ( this[i] === elem ) { + return i; + } + } + return -1; + }, + + booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", + + // Regular expressions + + // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]", + // http://www.w3.org/TR/css3-syntax/#characters + characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+", + + // Loosely modeled on CSS identifier characters + // An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors + // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier + identifier = characterEncoding.replace( "w", "w#" ), + + // Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors + attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace + + "*(?:([*^$|!~]?=)" + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]", + + // Prefer arguments quoted, + // then not containing pseudos/brackets, + // then attribute selectors/non-parenthetical expressions, + // then anything else + // These preferences are here to reduce the number of selectors + // needing tokenize in the PSEUDO preFilter + pseudos = ":(" + characterEncoding + ")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|" + attributes.replace( 3, 8 ) + ")*)|.*)\\)|)", + + // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), + + rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), + rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), + + rsibling = new RegExp( whitespace + "*[+~]" ), + rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*)" + whitespace + "*\\]", "g" ), + + rpseudo = new RegExp( pseudos ), + ridentifier = new RegExp( "^" + identifier + "$" ), + + matchExpr = { + "ID": new RegExp( "^#(" + characterEncoding + ")" ), + "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ), + "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ), + "ATTR": new RegExp( "^" + attributes ), + "PSEUDO": new RegExp( "^" + pseudos ), + "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), + "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), + // For use in libraries implementing .is() + // We use this for POS matching in `select` + "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + + whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) + }, + + rnative = /^[^{]+\{\s*\[native \w/, + + // Easily-parseable/retrievable ID or TAG or CLASS selectors + rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, + + rinputs = /^(?:input|select|textarea|button)$/i, + rheader = /^h\d$/i, + + rescape = /'|\\/g, + + // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters + runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ), + funescape = function( _, escaped, escapedWhitespace ) { + var high = "0x" + escaped - 0x10000; + // NaN means non-codepoint + // Support: Firefox + // Workaround erroneous numeric interpretation of +"0x" + return high !== high || escapedWhitespace ? + escaped : + // BMP codepoint + high < 0 ? + String.fromCharCode( high + 0x10000 ) : + // Supplemental Plane codepoint (surrogate pair) + String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); + }; + +// Optimize for push.apply( _, NodeList ) +try { + push.apply( + (arr = slice.call( preferredDoc.childNodes )), + preferredDoc.childNodes + ); + // Support: Android<4.0 + // Detect silently failing push.apply + arr[ preferredDoc.childNodes.length ].nodeType; +} catch ( e ) { + push = { apply: arr.length ? + + // Leverage slice if possible + function( target, els ) { + push_native.apply( target, slice.call(els) ); + } : + + // Support: IE<9 + // Otherwise append directly + function( target, els ) { + var j = target.length, + i = 0; + // Can't trust NodeList.length + while ( (target[j++] = els[i++]) ) {} + target.length = j - 1; + } + }; +} + +function Sizzle( selector, context, results, seed ) { + var match, elem, m, nodeType, + // QSA vars + i, groups, old, nid, newContext, newSelector; + + if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { + setDocument( context ); + } + + context = context || document; + results = results || []; + + if ( !selector || typeof selector !== "string" ) { + return results; + } + + if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) { + return []; + } + + if ( documentIsHTML && !seed ) { + + // Shortcuts + if ( (match = rquickExpr.exec( selector )) ) { + // Speed-up: Sizzle("#ID") + if ( (m = match[1]) ) { + if ( nodeType === 9 ) { + elem = context.getElementById( m ); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE, Opera, and Webkit return items + // by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + } else { + // Context is not a document + if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) && + contains( context, elem ) && elem.id === m ) { + results.push( elem ); + return results; + } + } + + // Speed-up: Sizzle("TAG") + } else if ( match[2] ) { + push.apply( results, context.getElementsByTagName( selector ) ); + return results; + + // Speed-up: Sizzle(".CLASS") + } else if ( (m = match[3]) && support.getElementsByClassName && context.getElementsByClassName ) { + push.apply( results, context.getElementsByClassName( m ) ); + return results; + } + } + + // QSA path + if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { + nid = old = expando; + newContext = context; + newSelector = nodeType === 9 && selector; + + // qSA works strangely on Element-rooted queries + // We can work around this by specifying an extra ID on the root + // and working up from there (Thanks to Andrew Dupont for the technique) + // IE 8 doesn't work on object elements + if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { + groups = tokenize( selector ); + + if ( (old = context.getAttribute("id")) ) { + nid = old.replace( rescape, "\\$&" ); + } else { + context.setAttribute( "id", nid ); + } + nid = "[id='" + nid + "'] "; + + i = groups.length; + while ( i-- ) { + groups[i] = nid + toSelector( groups[i] ); + } + newContext = rsibling.test( selector ) && context.parentNode || context; + newSelector = groups.join(","); + } + + if ( newSelector ) { + try { + push.apply( results, + newContext.querySelectorAll( newSelector ) + ); + return results; + } catch(qsaError) { + } finally { + if ( !old ) { + context.removeAttribute("id"); + } + } + } + } + } + + // All others + return select( selector.replace( rtrim, "$1" ), context, results, seed ); +} + +/** + * For feature detection + * @param {Function} fn The function to test for native support + */ +function isNative( fn ) { + return rnative.test( fn + "" ); +} + +/** + * Create key-value caches of limited size + * @returns {Function(string, Object)} Returns the Object data after storing it on itself with + * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) + * deleting the oldest entry + */ +function createCache() { + var keys = []; + + function cache( key, value ) { + // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) + if ( keys.push( key += " " ) > Expr.cacheLength ) { + // Only keep the most recent entries + delete cache[ keys.shift() ]; + } + return (cache[ key ] = value); + } + return cache; +} + +/** + * Mark a function for special use by Sizzle + * @param {Function} fn The function to mark + */ +function markFunction( fn ) { + fn[ expando ] = true; + return fn; +} + +/** + * Support testing using an element + * @param {Function} fn Passed the created div and expects a boolean result + */ +function assert( fn ) { + var div = document.createElement("div"); + + try { + return !!fn( div ); + } catch (e) { + return false; + } finally { + // Remove from its parent by default + if ( div.parentNode ) { + div.parentNode.removeChild( div ); + } + // release memory in IE + div = null; + } +} + +/** + * Adds the same handler for all of the specified attrs + * @param {String} attrs Pipe-separated list of attributes + * @param {Function} handler The method that will be applied if the test fails + * @param {Boolean} test The result of a test. If true, null will be set as the handler in leiu of the specified handler + */ +function addHandle( attrs, handler, test ) { + attrs = attrs.split("|"); + var current, + i = attrs.length, + setHandle = test ? null : handler; + + while ( i-- ) { + // Don't override a user's handler + if ( !(current = Expr.attrHandle[ attrs[i] ]) || current === handler ) { + Expr.attrHandle[ attrs[i] ] = setHandle; + } + } +} + +/** + * Fetches boolean attributes by node + * @param {Element} elem + * @param {String} name + */ +function boolHandler( elem, name ) { + // XML does not need to be checked as this will not be assigned for XML documents + var val = elem.getAttributeNode( name ); + return val && val.specified ? + val.value : + elem[ name ] === true ? name.toLowerCase() : null; +} + +/** + * Fetches attributes without interpolation + * http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx + * @param {Element} elem + * @param {String} name + */ +function interpolationHandler( elem, name ) { + // XML does not need to be checked as this will not be assigned for XML documents + return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); +} + +/** + * Uses defaultValue to retrieve value in IE6/7 + * @param {Element} elem + * @param {String} name + */ +function valueHandler( elem ) { + // Ignore the value *property* on inputs by using defaultValue + // Fallback to Sizzle.attr by returning undefined where appropriate + // XML does not need to be checked as this will not be assigned for XML documents + if ( elem.nodeName.toLowerCase() === "input" ) { + return elem.defaultValue; + } +} + +/** + * Checks document order of two siblings + * @param {Element} a + * @param {Element} b + * @returns Returns -1 if a precedes b, 1 if a follows b + */ +function siblingCheck( a, b ) { + var cur = b && a, + diff = cur && a.nodeType === 1 && b.nodeType === 1 && + ( ~b.sourceIndex || MAX_NEGATIVE ) - + ( ~a.sourceIndex || MAX_NEGATIVE ); + + // Use IE sourceIndex if available on both nodes + if ( diff ) { + return diff; + } + + // Check if b follows a + if ( cur ) { + while ( (cur = cur.nextSibling) ) { + if ( cur === b ) { + return -1; + } + } + } + + return a ? 1 : -1; +} + +/** + * Returns a function to use in pseudos for input types + * @param {String} type + */ +function createInputPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for buttons + * @param {String} type + */ +function createButtonPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for positionals + * @param {Function} fn + */ +function createPositionalPseudo( fn ) { + return markFunction(function( argument ) { + argument = +argument; + return markFunction(function( seed, matches ) { + var j, + matchIndexes = fn( [], seed.length, argument ), + i = matchIndexes.length; + + // Match elements found at the specified indexes + while ( i-- ) { + if ( seed[ (j = matchIndexes[i]) ] ) { + seed[j] = !(matches[j] = seed[j]); + } + } + }); + }); +} + +/** + * Detect xml + * @param {Element|Object} elem An element or a document + */ +isXML = Sizzle.isXML = function( elem ) { + // documentElement is verified for cases where it doesn't yet exist + // (such as loading iframes in IE - #4833) + var documentElement = elem && (elem.ownerDocument || elem).documentElement; + return documentElement ? documentElement.nodeName !== "HTML" : false; +}; + +// Expose support vars for convenience +support = Sizzle.support = {}; + +/** + * Sets document-related variables once based on the current document + * @param {Element|Object} [doc] An element or document object to use to set the document + * @returns {Object} Returns the current document + */ +setDocument = Sizzle.setDocument = function( node ) { + var doc = node ? node.ownerDocument || node : preferredDoc; + + // If no document and documentElement is available, return + if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { + return document; + } + + // Set our document + document = doc; + docElem = doc.documentElement; + + // Support tests + documentIsHTML = !isXML( doc ); + + /* Attributes + ---------------------------------------------------------------------- */ + + // Support: IE<8 + // Verify that getAttribute really returns attributes and not properties (excepting IE8 booleans) + support.attributes = assert(function( div ) { + + // Support: IE<8 + // Prevent attribute/property "interpolation" + div.innerHTML = ""; + addHandle( "type|href|height|width", interpolationHandler, div.firstChild.getAttribute("href") === "#" ); + + // Support: IE<9 + // Use getAttributeNode to fetch booleans when getAttribute lies + addHandle( booleans, boolHandler, div.getAttribute("disabled") == null ); + + div.className = "i"; + return !div.getAttribute("className"); + }); + + // Support: IE<9 + // Retrieving value should defer to defaultValue + support.input = assert(function( div ) { + div.innerHTML = ""; + div.firstChild.setAttribute( "value", "" ); + return div.firstChild.getAttribute( "value" ) === ""; + }); + + // IE6/7 still return empty string for value, + // but are actually retrieving the property + addHandle( "value", valueHandler, support.attributes && support.input ); + + /* getElement(s)By* + ---------------------------------------------------------------------- */ + + // Check if getElementsByTagName("*") returns only elements + support.getElementsByTagName = assert(function( div ) { + div.appendChild( doc.createComment("") ); + return !div.getElementsByTagName("*").length; + }); + + // Check if getElementsByClassName can be trusted + support.getElementsByClassName = assert(function( div ) { + div.innerHTML = "
"; + + // Support: Safari<4 + // Catch class over-caching + div.firstChild.className = "i"; + // Support: Opera<10 + // Catch gEBCN failure to find non-leading classes + return div.getElementsByClassName("i").length === 2; + }); + + // Support: IE<10 + // Check if getElementById returns elements by name + // The broken getElementById methods don't pick up programatically-set names, + // so use a roundabout getElementsByName test + support.getById = assert(function( div ) { + docElem.appendChild( div ).id = expando; + return !doc.getElementsByName || !doc.getElementsByName( expando ).length; + }); + + // ID find and filter + if ( support.getById ) { + Expr.find["ID"] = function( id, context ) { + if ( typeof context.getElementById !== strundefined && documentIsHTML ) { + var m = context.getElementById( id ); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + return m && m.parentNode ? [m] : []; + } + }; + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + return elem.getAttribute("id") === attrId; + }; + }; + } else { + // Support: IE6/7 + // getElementById is not reliable as a find shortcut + delete Expr.find["ID"]; + + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id"); + return node && node.value === attrId; + }; + }; + } + + // Tag + Expr.find["TAG"] = support.getElementsByTagName ? + function( tag, context ) { + if ( typeof context.getElementsByTagName !== strundefined ) { + return context.getElementsByTagName( tag ); + } + } : + function( tag, context ) { + var elem, + tmp = [], + i = 0, + results = context.getElementsByTagName( tag ); + + // Filter out possible comments + if ( tag === "*" ) { + while ( (elem = results[i++]) ) { + if ( elem.nodeType === 1 ) { + tmp.push( elem ); + } + } + + return tmp; + } + return results; + }; + + // Class + Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) { + if ( typeof context.getElementsByClassName !== strundefined && documentIsHTML ) { + return context.getElementsByClassName( className ); + } + }; + + /* QSA/matchesSelector + ---------------------------------------------------------------------- */ + + // QSA and matchesSelector support + + // matchesSelector(:active) reports false when true (IE9/Opera 11.5) + rbuggyMatches = []; + + // qSa(:focus) reports false when true (Chrome 21) + // We allow this because of a bug in IE8/9 that throws an error + // whenever `document.activeElement` is accessed on an iframe + // So, we allow :focus to pass through QSA all the time to avoid the IE error + // See http://bugs.jquery.com/ticket/13378 + rbuggyQSA = []; + + if ( (support.qsa = isNative(doc.querySelectorAll)) ) { + // Build QSA regex + // Regex strategy adopted from Diego Perini + assert(function( div ) { + // Select is set to empty string on purpose + // This is to test IE's treatment of not explicitly + // setting a boolean content attribute, + // since its presence should be enough + // http://bugs.jquery.com/ticket/12359 + div.innerHTML = ""; + + // Support: IE8 + // Boolean attributes and "value" are not treated correctly + if ( !div.querySelectorAll("[selected]").length ) { + rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); + } + + // Webkit/Opera - :checked should return selected option elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + // IE8 throws error here and will not see later tests + if ( !div.querySelectorAll(":checked").length ) { + rbuggyQSA.push(":checked"); + } + }); + + assert(function( div ) { + + // Support: Opera 10-12/IE8 + // ^= $= *= and empty values + // Should not select anything + // Support: Windows 8 Native Apps + // The type attribute is restricted during .innerHTML assignment + var input = doc.createElement("input"); + input.setAttribute( "type", "hidden" ); + div.appendChild( input ).setAttribute( "t", "" ); + + if ( div.querySelectorAll("[t^='']").length ) { + rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); + } + + // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) + // IE8 throws error here and will not see later tests + if ( !div.querySelectorAll(":enabled").length ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Opera 10-11 does not throw on post-comma invalid pseudos + div.querySelectorAll("*,:x"); + rbuggyQSA.push(",.*:"); + }); + } + + if ( (support.matchesSelector = isNative( (matches = docElem.webkitMatchesSelector || + docElem.mozMatchesSelector || + docElem.oMatchesSelector || + docElem.msMatchesSelector) )) ) { + + assert(function( div ) { + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9) + support.disconnectedMatch = matches.call( div, "div" ); + + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( div, "[s!='']:x" ); + rbuggyMatches.push( "!=", pseudos ); + }); + } + + rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") ); + rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") ); + + /* Contains + ---------------------------------------------------------------------- */ + + // Element contains another + // Purposefully does not implement inclusive descendent + // As in, an element does not contain itself + contains = isNative(docElem.contains) || docElem.compareDocumentPosition ? + function( a, b ) { + var adown = a.nodeType === 9 ? a.documentElement : a, + bup = b && b.parentNode; + return a === bup || !!( bup && bup.nodeType === 1 && ( + adown.contains ? + adown.contains( bup ) : + a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 + )); + } : + function( a, b ) { + if ( b ) { + while ( (b = b.parentNode) ) { + if ( b === a ) { + return true; + } + } + } + return false; + }; + + /* Sorting + ---------------------------------------------------------------------- */ + + // Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) + // Detached nodes confoundingly follow *each other* + support.sortDetached = assert(function( div1 ) { + // Should return 1, but returns 4 (following) + return div1.compareDocumentPosition( doc.createElement("div") ) & 1; + }); + + // Document order sorting + sortOrder = docElem.compareDocumentPosition ? + function( a, b ) { + + // Flag for duplicate removal + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + var compare = b.compareDocumentPosition && a.compareDocumentPosition && a.compareDocumentPosition( b ); + + if ( compare ) { + // Disconnected nodes + if ( compare & 1 || + (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) { + + // Choose the first element that is related to our preferred document + if ( a === doc || contains(preferredDoc, a) ) { + return -1; + } + if ( b === doc || contains(preferredDoc, b) ) { + return 1; + } + + // Maintain original order + return sortInput ? + ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : + 0; + } + + return compare & 4 ? -1 : 1; + } + + // Not directly comparable, sort on existence of method + return a.compareDocumentPosition ? -1 : 1; + } : + function( a, b ) { + var cur, + i = 0, + aup = a.parentNode, + bup = b.parentNode, + ap = [ a ], + bp = [ b ]; + + // Exit early if the nodes are identical + if ( a === b ) { + hasDuplicate = true; + return 0; + + // Parentless nodes are either documents or disconnected + } else if ( !aup || !bup ) { + return a === doc ? -1 : + b === doc ? 1 : + aup ? -1 : + bup ? 1 : + sortInput ? + ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : + 0; + + // If the nodes are siblings, we can do a quick check + } else if ( aup === bup ) { + return siblingCheck( a, b ); + } + + // Otherwise we need full lists of their ancestors for comparison + cur = a; + while ( (cur = cur.parentNode) ) { + ap.unshift( cur ); + } + cur = b; + while ( (cur = cur.parentNode) ) { + bp.unshift( cur ); + } + + // Walk down the tree looking for a discrepancy + while ( ap[i] === bp[i] ) { + i++; + } + + return i ? + // Do a sibling check if the nodes have a common ancestor + siblingCheck( ap[i], bp[i] ) : + + // Otherwise nodes in our document sort first + ap[i] === preferredDoc ? -1 : + bp[i] === preferredDoc ? 1 : + 0; + }; + + return doc; +}; + +Sizzle.matches = function( expr, elements ) { + return Sizzle( expr, null, null, elements ); +}; + +Sizzle.matchesSelector = function( elem, expr ) { + // Set document vars if needed + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } + + // Make sure that attribute selectors are quoted + expr = expr.replace( rattributeQuotes, "='$1']" ); + + if ( support.matchesSelector && documentIsHTML && + ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && + ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { + + try { + var ret = matches.call( elem, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || support.disconnectedMatch || + // As well, disconnected nodes are said to be in a document + // fragment in IE 9 + elem.document && elem.document.nodeType !== 11 ) { + return ret; + } + } catch(e) {} + } + + return Sizzle( expr, document, null, [elem] ).length > 0; +}; + +Sizzle.contains = function( context, elem ) { + // Set document vars if needed + if ( ( context.ownerDocument || context ) !== document ) { + setDocument( context ); + } + return contains( context, elem ); +}; + +Sizzle.attr = function( elem, name ) { + // Set document vars if needed + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } + + var fn = Expr.attrHandle[ name.toLowerCase() ], + // Don't get fooled by Object.prototype properties (jQuery #13807) + val = ( fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? + fn( elem, name, !documentIsHTML ) : + undefined ); + + return val === undefined ? + support.attributes || !documentIsHTML ? + elem.getAttribute( name ) : + (val = elem.getAttributeNode(name)) && val.specified ? + val.value : + null : + val; +}; + +Sizzle.error = function( msg ) { + throw new Error( "Syntax error, unrecognized expression: " + msg ); +}; + +/** + * Document sorting and removing duplicates + * @param {ArrayLike} results + */ +Sizzle.uniqueSort = function( results ) { + var elem, + duplicates = [], + j = 0, + i = 0; + + // Unless we *know* we can detect duplicates, assume their presence + hasDuplicate = !support.detectDuplicates; + sortInput = !support.sortStable && results.slice( 0 ); + results.sort( sortOrder ); + + if ( hasDuplicate ) { + while ( (elem = results[i++]) ) { + if ( elem === results[ i ] ) { + j = duplicates.push( i ); + } + } + while ( j-- ) { + results.splice( duplicates[ j ], 1 ); + } + } + + return results; +}; + +/** + * Utility function for retrieving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ +getText = Sizzle.getText = function( elem ) { + var node, + ret = "", + i = 0, + nodeType = elem.nodeType; + + if ( !nodeType ) { + // If no nodeType, this is expected to be an array + for ( ; (node = elem[i]); i++ ) { + // Do not traverse comment nodes + ret += getText( node ); + } + } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { + // Use textContent for elements + // innerText usage removed for consistency of new lines (see #11153) + if ( typeof elem.textContent === "string" ) { + return elem.textContent; + } else { + // Traverse its children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + // Do not include comment or processing instruction nodes + + return ret; +}; + +Expr = Sizzle.selectors = { + + // Can be adjusted by the user + cacheLength: 50, + + createPseudo: markFunction, + + match: matchExpr, + + attrHandle: {}, + + find: {}, + + relative: { + ">": { dir: "parentNode", first: true }, + " ": { dir: "parentNode" }, + "+": { dir: "previousSibling", first: true }, + "~": { dir: "previousSibling" } + }, + + preFilter: { + "ATTR": function( match ) { + match[1] = match[1].replace( runescape, funescape ); + + // Move the given value to match[3] whether quoted or unquoted + match[3] = ( match[4] || match[5] || "" ).replace( runescape, funescape ); + + if ( match[2] === "~=" ) { + match[3] = " " + match[3] + " "; + } + + return match.slice( 0, 4 ); + }, + + "CHILD": function( match ) { + /* matches from matchExpr["CHILD"] + 1 type (only|nth|...) + 2 what (child|of-type) + 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) + 4 xn-component of xn+y argument ([+-]?\d*n|) + 5 sign of xn-component + 6 x of xn-component + 7 sign of y-component + 8 y of y-component + */ + match[1] = match[1].toLowerCase(); + + if ( match[1].slice( 0, 3 ) === "nth" ) { + // nth-* requires argument + if ( !match[3] ) { + Sizzle.error( match[0] ); + } + + // numeric x and y parameters for Expr.filter.CHILD + // remember that false/true cast respectively to 0/1 + match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) ); + match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" ); + + // other types prohibit arguments + } else if ( match[3] ) { + Sizzle.error( match[0] ); + } + + return match; + }, + + "PSEUDO": function( match ) { + var excess, + unquoted = !match[5] && match[2]; + + if ( matchExpr["CHILD"].test( match[0] ) ) { + return null; + } + + // Accept quoted arguments as-is + if ( match[3] && match[4] !== undefined ) { + match[2] = match[4]; + + // Strip excess characters from unquoted arguments + } else if ( unquoted && rpseudo.test( unquoted ) && + // Get excess from tokenize (recursively) + (excess = tokenize( unquoted, true )) && + // advance to the next closing parenthesis + (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { + + // excess is a negative index + match[0] = match[0].slice( 0, excess ); + match[2] = unquoted.slice( 0, excess ); + } + + // Return only captures needed by the pseudo filter method (type and argument) + return match.slice( 0, 3 ); + } + }, + + filter: { + + "TAG": function( nodeNameSelector ) { + var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); + return nodeNameSelector === "*" ? + function() { return true; } : + function( elem ) { + return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; + }; + }, + + "CLASS": function( className ) { + var pattern = classCache[ className + " " ]; + + return pattern || + (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && + classCache( className, function( elem ) { + return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== strundefined && elem.getAttribute("class") || "" ); + }); + }, + + "ATTR": function( name, operator, check ) { + return function( elem ) { + var result = Sizzle.attr( elem, name ); + + if ( result == null ) { + return operator === "!="; + } + if ( !operator ) { + return true; + } + + result += ""; + + return operator === "=" ? result === check : + operator === "!=" ? result !== check : + operator === "^=" ? check && result.indexOf( check ) === 0 : + operator === "*=" ? check && result.indexOf( check ) > -1 : + operator === "$=" ? check && result.slice( -check.length ) === check : + operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 : + operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : + false; + }; + }, + + "CHILD": function( type, what, argument, first, last ) { + var simple = type.slice( 0, 3 ) !== "nth", + forward = type.slice( -4 ) !== "last", + ofType = what === "of-type"; + + return first === 1 && last === 0 ? + + // Shortcut for :nth-*(n) + function( elem ) { + return !!elem.parentNode; + } : + + function( elem, context, xml ) { + var cache, outerCache, node, diff, nodeIndex, start, + dir = simple !== forward ? "nextSibling" : "previousSibling", + parent = elem.parentNode, + name = ofType && elem.nodeName.toLowerCase(), + useCache = !xml && !ofType; + + if ( parent ) { + + // :(first|last|only)-(child|of-type) + if ( simple ) { + while ( dir ) { + node = elem; + while ( (node = node[ dir ]) ) { + if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) { + return false; + } + } + // Reverse direction for :only-* (if we haven't yet done so) + start = dir = type === "only" && !start && "nextSibling"; + } + return true; + } + + start = [ forward ? parent.firstChild : parent.lastChild ]; + + // non-xml :nth-child(...) stores cache data on `parent` + if ( forward && useCache ) { + // Seek `elem` from a previously-cached index + outerCache = parent[ expando ] || (parent[ expando ] = {}); + cache = outerCache[ type ] || []; + nodeIndex = cache[0] === dirruns && cache[1]; + diff = cache[0] === dirruns && cache[2]; + node = nodeIndex && parent.childNodes[ nodeIndex ]; + + while ( (node = ++nodeIndex && node && node[ dir ] || + + // Fallback to seeking `elem` from the start + (diff = nodeIndex = 0) || start.pop()) ) { + + // When found, cache indexes on `parent` and break + if ( node.nodeType === 1 && ++diff && node === elem ) { + outerCache[ type ] = [ dirruns, nodeIndex, diff ]; + break; + } + } + + // Use previously-cached element index if available + } else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) { + diff = cache[1]; + + // xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...) + } else { + // Use the same loop as above to seek `elem` from the start + while ( (node = ++nodeIndex && node && node[ dir ] || + (diff = nodeIndex = 0) || start.pop()) ) { + + if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) { + // Cache the index of each encountered element + if ( useCache ) { + (node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ]; + } + + if ( node === elem ) { + break; + } + } + } + } + + // Incorporate the offset, then check against cycle size + diff -= last; + return diff === first || ( diff % first === 0 && diff / first >= 0 ); + } + }; + }, + + "PSEUDO": function( pseudo, argument ) { + // pseudo-class names are case-insensitive + // http://www.w3.org/TR/selectors/#pseudo-classes + // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters + // Remember that setFilters inherits from pseudos + var args, + fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || + Sizzle.error( "unsupported pseudo: " + pseudo ); + + // The user may use createPseudo to indicate that + // arguments are needed to create the filter function + // just as Sizzle does + if ( fn[ expando ] ) { + return fn( argument ); + } + + // But maintain support for old signatures + if ( fn.length > 1 ) { + args = [ pseudo, pseudo, "", argument ]; + return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? + markFunction(function( seed, matches ) { + var idx, + matched = fn( seed, argument ), + i = matched.length; + while ( i-- ) { + idx = indexOf.call( seed, matched[i] ); + seed[ idx ] = !( matches[ idx ] = matched[i] ); + } + }) : + function( elem ) { + return fn( elem, 0, args ); + }; + } + + return fn; + } + }, + + pseudos: { + // Potentially complex pseudos + "not": markFunction(function( selector ) { + // Trim the selector passed to compile + // to avoid treating leading and trailing + // spaces as combinators + var input = [], + results = [], + matcher = compile( selector.replace( rtrim, "$1" ) ); + + return matcher[ expando ] ? + markFunction(function( seed, matches, context, xml ) { + var elem, + unmatched = matcher( seed, null, xml, [] ), + i = seed.length; + + // Match elements unmatched by `matcher` + while ( i-- ) { + if ( (elem = unmatched[i]) ) { + seed[i] = !(matches[i] = elem); + } + } + }) : + function( elem, context, xml ) { + input[0] = elem; + matcher( input, null, xml, results ); + return !results.pop(); + }; + }), + + "has": markFunction(function( selector ) { + return function( elem ) { + return Sizzle( selector, elem ).length > 0; + }; + }), + + "contains": markFunction(function( text ) { + return function( elem ) { + return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; + }; + }), + + // "Whether an element is represented by a :lang() selector + // is based solely on the element's language value + // being equal to the identifier C, + // or beginning with the identifier C immediately followed by "-". + // The matching of C against the element's language value is performed case-insensitively. + // The identifier C does not have to be a valid language name." + // http://www.w3.org/TR/selectors/#lang-pseudo + "lang": markFunction( function( lang ) { + // lang value must be a valid identifier + if ( !ridentifier.test(lang || "") ) { + Sizzle.error( "unsupported lang: " + lang ); + } + lang = lang.replace( runescape, funescape ).toLowerCase(); + return function( elem ) { + var elemLang; + do { + if ( (elemLang = documentIsHTML ? + elem.lang : + elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) { + + elemLang = elemLang.toLowerCase(); + return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; + } + } while ( (elem = elem.parentNode) && elem.nodeType === 1 ); + return false; + }; + }), + + // Miscellaneous + "target": function( elem ) { + var hash = window.location && window.location.hash; + return hash && hash.slice( 1 ) === elem.id; + }, + + "root": function( elem ) { + return elem === docElem; + }, + + "focus": function( elem ) { + return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); + }, + + // Boolean properties + "enabled": function( elem ) { + return elem.disabled === false; + }, + + "disabled": function( elem ) { + return elem.disabled === true; + }, + + "checked": function( elem ) { + // In CSS3, :checked should return both checked and selected elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + var nodeName = elem.nodeName.toLowerCase(); + return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); + }, + + "selected": function( elem ) { + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + // Contents + "empty": function( elem ) { + // http://www.w3.org/TR/selectors/#empty-pseudo + // :empty is only affected by element nodes and content nodes(including text(3), cdata(4)), + // not comment, processing instructions, or others + // Thanks to Diego Perini for the nodeName shortcut + // Greater than "@" means alpha characters (specifically not starting with "#" or "?") + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + if ( elem.nodeName > "@" || elem.nodeType === 3 || elem.nodeType === 4 ) { + return false; + } + } + return true; + }, + + "parent": function( elem ) { + return !Expr.pseudos["empty"]( elem ); + }, + + // Element/input types + "header": function( elem ) { + return rheader.test( elem.nodeName ); + }, + + "input": function( elem ) { + return rinputs.test( elem.nodeName ); + }, + + "button": function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === "button" || name === "button"; + }, + + "text": function( elem ) { + var attr; + // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) + // use getAttribute instead to test this case + return elem.nodeName.toLowerCase() === "input" && + elem.type === "text" && + ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === elem.type ); + }, + + // Position-in-collection + "first": createPositionalPseudo(function() { + return [ 0 ]; + }), + + "last": createPositionalPseudo(function( matchIndexes, length ) { + return [ length - 1 ]; + }), + + "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { + return [ argument < 0 ? argument + length : argument ]; + }), + + "even": createPositionalPseudo(function( matchIndexes, length ) { + var i = 0; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "odd": createPositionalPseudo(function( matchIndexes, length ) { + var i = 1; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; --i >= 0; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; ++i < length; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }) + } +}; + +// Add button/input type pseudos +for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { + Expr.pseudos[ i ] = createInputPseudo( i ); +} +for ( i in { submit: true, reset: true } ) { + Expr.pseudos[ i ] = createButtonPseudo( i ); +} + +function tokenize( selector, parseOnly ) { + var matched, match, tokens, type, + soFar, groups, preFilters, + cached = tokenCache[ selector + " " ]; + + if ( cached ) { + return parseOnly ? 0 : cached.slice( 0 ); + } + + soFar = selector; + groups = []; + preFilters = Expr.preFilter; + + while ( soFar ) { + + // Comma and first run + if ( !matched || (match = rcomma.exec( soFar )) ) { + if ( match ) { + // Don't consume trailing commas as valid + soFar = soFar.slice( match[0].length ) || soFar; + } + groups.push( tokens = [] ); + } + + matched = false; + + // Combinators + if ( (match = rcombinators.exec( soFar )) ) { + matched = match.shift(); + tokens.push({ + value: matched, + // Cast descendant combinators to space + type: match[0].replace( rtrim, " " ) + }); + soFar = soFar.slice( matched.length ); + } + + // Filters + for ( type in Expr.filter ) { + if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || + (match = preFilters[ type ]( match ))) ) { + matched = match.shift(); + tokens.push({ + value: matched, + type: type, + matches: match + }); + soFar = soFar.slice( matched.length ); + } + } + + if ( !matched ) { + break; + } + } + + // Return the length of the invalid excess + // if we're just parsing + // Otherwise, throw an error or return tokens + return parseOnly ? + soFar.length : + soFar ? + Sizzle.error( selector ) : + // Cache the tokens + tokenCache( selector, groups ).slice( 0 ); +} + +function toSelector( tokens ) { + var i = 0, + len = tokens.length, + selector = ""; + for ( ; i < len; i++ ) { + selector += tokens[i].value; + } + return selector; +} + +function addCombinator( matcher, combinator, base ) { + var dir = combinator.dir, + checkNonElements = base && dir === "parentNode", + doneName = done++; + + return combinator.first ? + // Check against closest ancestor/preceding element + function( elem, context, xml ) { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + return matcher( elem, context, xml ); + } + } + } : + + // Check against all ancestor/preceding elements + function( elem, context, xml ) { + var data, cache, outerCache, + dirkey = dirruns + " " + doneName; + + // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching + if ( xml ) { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + if ( matcher( elem, context, xml ) ) { + return true; + } + } + } + } else { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + outerCache = elem[ expando ] || (elem[ expando ] = {}); + if ( (cache = outerCache[ dir ]) && cache[0] === dirkey ) { + if ( (data = cache[1]) === true || data === cachedruns ) { + return data === true; + } + } else { + cache = outerCache[ dir ] = [ dirkey ]; + cache[1] = matcher( elem, context, xml ) || cachedruns; + if ( cache[1] === true ) { + return true; + } + } + } + } + } + }; +} + +function elementMatcher( matchers ) { + return matchers.length > 1 ? + function( elem, context, xml ) { + var i = matchers.length; + while ( i-- ) { + if ( !matchers[i]( elem, context, xml ) ) { + return false; + } + } + return true; + } : + matchers[0]; +} + +function condense( unmatched, map, filter, context, xml ) { + var elem, + newUnmatched = [], + i = 0, + len = unmatched.length, + mapped = map != null; + + for ( ; i < len; i++ ) { + if ( (elem = unmatched[i]) ) { + if ( !filter || filter( elem, context, xml ) ) { + newUnmatched.push( elem ); + if ( mapped ) { + map.push( i ); + } + } + } + } + + return newUnmatched; +} + +function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { + if ( postFilter && !postFilter[ expando ] ) { + postFilter = setMatcher( postFilter ); + } + if ( postFinder && !postFinder[ expando ] ) { + postFinder = setMatcher( postFinder, postSelector ); + } + return markFunction(function( seed, results, context, xml ) { + var temp, i, elem, + preMap = [], + postMap = [], + preexisting = results.length, + + // Get initial elements from seed or context + elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), + + // Prefilter to get matcher input, preserving a map for seed-results synchronization + matcherIn = preFilter && ( seed || !selector ) ? + condense( elems, preMap, preFilter, context, xml ) : + elems, + + matcherOut = matcher ? + // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, + postFinder || ( seed ? preFilter : preexisting || postFilter ) ? + + // ...intermediate processing is necessary + [] : + + // ...otherwise use results directly + results : + matcherIn; + + // Find primary matches + if ( matcher ) { + matcher( matcherIn, matcherOut, context, xml ); + } + + // Apply postFilter + if ( postFilter ) { + temp = condense( matcherOut, postMap ); + postFilter( temp, [], context, xml ); + + // Un-match failing elements by moving them back to matcherIn + i = temp.length; + while ( i-- ) { + if ( (elem = temp[i]) ) { + matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); + } + } + } + + if ( seed ) { + if ( postFinder || preFilter ) { + if ( postFinder ) { + // Get the final matcherOut by condensing this intermediate into postFinder contexts + temp = []; + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) ) { + // Restore matcherIn since elem is not yet a final match + temp.push( (matcherIn[i] = elem) ); + } + } + postFinder( null, (matcherOut = []), temp, xml ); + } + + // Move matched elements from seed to results to keep them synchronized + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) && + (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) { + + seed[temp] = !(results[temp] = elem); + } + } + } + + // Add elements to results, through postFinder if defined + } else { + matcherOut = condense( + matcherOut === results ? + matcherOut.splice( preexisting, matcherOut.length ) : + matcherOut + ); + if ( postFinder ) { + postFinder( null, results, matcherOut, xml ); + } else { + push.apply( results, matcherOut ); + } + } + }); +} + +function matcherFromTokens( tokens ) { + var checkContext, matcher, j, + len = tokens.length, + leadingRelative = Expr.relative[ tokens[0].type ], + implicitRelative = leadingRelative || Expr.relative[" "], + i = leadingRelative ? 1 : 0, + + // The foundational matcher ensures that elements are reachable from top-level context(s) + matchContext = addCombinator( function( elem ) { + return elem === checkContext; + }, implicitRelative, true ), + matchAnyContext = addCombinator( function( elem ) { + return indexOf.call( checkContext, elem ) > -1; + }, implicitRelative, true ), + matchers = [ function( elem, context, xml ) { + return ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( + (checkContext = context).nodeType ? + matchContext( elem, context, xml ) : + matchAnyContext( elem, context, xml ) ); + } ]; + + for ( ; i < len; i++ ) { + if ( (matcher = Expr.relative[ tokens[i].type ]) ) { + matchers = [ addCombinator(elementMatcher( matchers ), matcher) ]; + } else { + matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); + + // Return special upon seeing a positional matcher + if ( matcher[ expando ] ) { + // Find the next relative operator (if any) for proper handling + j = ++i; + for ( ; j < len; j++ ) { + if ( Expr.relative[ tokens[j].type ] ) { + break; + } + } + return setMatcher( + i > 1 && elementMatcher( matchers ), + i > 1 && toSelector( + // If the preceding token was a descendant combinator, insert an implicit any-element `*` + tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" }) + ).replace( rtrim, "$1" ), + matcher, + i < j && matcherFromTokens( tokens.slice( i, j ) ), + j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), + j < len && toSelector( tokens ) + ); + } + matchers.push( matcher ); + } + } + + return elementMatcher( matchers ); +} + +function matcherFromGroupMatchers( elementMatchers, setMatchers ) { + // A counter to specify which element is currently being matched + var matcherCachedRuns = 0, + bySet = setMatchers.length > 0, + byElement = elementMatchers.length > 0, + superMatcher = function( seed, context, xml, results, expandContext ) { + var elem, j, matcher, + setMatched = [], + matchedCount = 0, + i = "0", + unmatched = seed && [], + outermost = expandContext != null, + contextBackup = outermostContext, + // We must always have either seed elements or context + elems = seed || byElement && Expr.find["TAG"]( "*", expandContext && context.parentNode || context ), + // Use integer dirruns iff this is the outermost matcher + dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1); + + if ( outermost ) { + outermostContext = context !== document && context; + cachedruns = matcherCachedRuns; + } + + // Add elements passing elementMatchers directly to results + // Keep `i` a string if there are no elements so `matchedCount` will be "00" below + for ( ; (elem = elems[i]) != null; i++ ) { + if ( byElement && elem ) { + j = 0; + while ( (matcher = elementMatchers[j++]) ) { + if ( matcher( elem, context, xml ) ) { + results.push( elem ); + break; + } + } + if ( outermost ) { + dirruns = dirrunsUnique; + cachedruns = ++matcherCachedRuns; + } + } + + // Track unmatched elements for set filters + if ( bySet ) { + // They will have gone through all possible matchers + if ( (elem = !matcher && elem) ) { + matchedCount--; + } + + // Lengthen the array for every element, matched or not + if ( seed ) { + unmatched.push( elem ); + } + } + } + + // Apply set filters to unmatched elements + matchedCount += i; + if ( bySet && i !== matchedCount ) { + j = 0; + while ( (matcher = setMatchers[j++]) ) { + matcher( unmatched, setMatched, context, xml ); + } + + if ( seed ) { + // Reintegrate element matches to eliminate the need for sorting + if ( matchedCount > 0 ) { + while ( i-- ) { + if ( !(unmatched[i] || setMatched[i]) ) { + setMatched[i] = pop.call( results ); + } + } + } + + // Discard index placeholder values to get only actual matches + setMatched = condense( setMatched ); + } + + // Add matches to results + push.apply( results, setMatched ); + + // Seedless set matches succeeding multiple successful matchers stipulate sorting + if ( outermost && !seed && setMatched.length > 0 && + ( matchedCount + setMatchers.length ) > 1 ) { + + Sizzle.uniqueSort( results ); + } + } + + // Override manipulation of globals by nested matchers + if ( outermost ) { + dirruns = dirrunsUnique; + outermostContext = contextBackup; + } + + return unmatched; + }; + + return bySet ? + markFunction( superMatcher ) : + superMatcher; +} + +compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) { + var i, + setMatchers = [], + elementMatchers = [], + cached = compilerCache[ selector + " " ]; + + if ( !cached ) { + // Generate a function of recursive functions that can be used to check each element + if ( !group ) { + group = tokenize( selector ); + } + i = group.length; + while ( i-- ) { + cached = matcherFromTokens( group[i] ); + if ( cached[ expando ] ) { + setMatchers.push( cached ); + } else { + elementMatchers.push( cached ); + } + } + + // Cache the compiled function + cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); + } + return cached; +}; + +function multipleContexts( selector, contexts, results ) { + var i = 0, + len = contexts.length; + for ( ; i < len; i++ ) { + Sizzle( selector, contexts[i], results ); + } + return results; +} + +function select( selector, context, results, seed ) { + var i, tokens, token, type, find, + match = tokenize( selector ); + + if ( !seed ) { + // Try to minimize operations if there is only one group + if ( match.length === 1 ) { + + // Take a shortcut and set the context if the root selector is an ID + tokens = match[0] = match[0].slice( 0 ); + if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && + support.getById && context.nodeType === 9 && documentIsHTML && + Expr.relative[ tokens[1].type ] ) { + + context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0]; + if ( !context ) { + return results; + } + selector = selector.slice( tokens.shift().value.length ); + } + + // Fetch a seed set for right-to-left matching + i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length; + while ( i-- ) { + token = tokens[i]; + + // Abort if we hit a combinator + if ( Expr.relative[ (type = token.type) ] ) { + break; + } + if ( (find = Expr.find[ type ]) ) { + // Search, expanding context for leading sibling combinators + if ( (seed = find( + token.matches[0].replace( runescape, funescape ), + rsibling.test( tokens[0].type ) && context.parentNode || context + )) ) { + + // If seed is empty or no tokens remain, we can return early + tokens.splice( i, 1 ); + selector = seed.length && toSelector( tokens ); + if ( !selector ) { + push.apply( results, seed ); + return results; + } + + break; + } + } + } + } + } + + // Compile and execute a filtering function + // Provide `match` to avoid retokenization if we modified the selector above + compile( selector, match )( + seed, + context, + !documentIsHTML, + results, + rsibling.test( selector ) + ); + return results; +} + +// Deprecated +Expr.pseudos["nth"] = Expr.pseudos["eq"]; + +// Easy API for creating new setFilters +function setFilters() {} +setFilters.prototype = Expr.filters = Expr.pseudos; +Expr.setFilters = new setFilters(); + +// One-time assignments + +// Sort stability +support.sortStable = expando.split("").sort( sortOrder ).join("") === expando; + +// Initialize against the default document +setDocument(); + +// Support: Chrome<<14 +// Always assume duplicates if they aren't passed to the comparison function +[0, 0].sort( sortOrder ); +support.detectDuplicates = hasDuplicate; + +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; +jQuery.expr[":"] = jQuery.expr.pseudos; +jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; + + +})( window ); +// String to Object options format cache +var optionsCache = {}; + +// Convert String-formatted options into Object-formatted ones and store in cache +function createOptions( options ) { + var object = optionsCache[ options ] = {}; + jQuery.each( options.match( core_rnotwhite ) || [], function( _, flag ) { + object[ flag ] = true; + }); + return object; +} + +/* + * Create a callback list using the following parameters: + * + * options: an optional list of space-separated options that will change how + * the callback list behaves or a more traditional option object + * + * By default a callback list will act like an event callback list and can be + * "fired" multiple times. + * + * Possible options: + * + * once: will ensure the callback list can only be fired once (like a Deferred) + * + * memory: will keep track of previous values and will call any callback added + * after the list has been fired right away with the latest "memorized" + * values (like a Deferred) + * + * unique: will ensure a callback can only be added once (no duplicate in the list) + * + * stopOnFalse: interrupt callings when a callback returns false + * + */ +jQuery.Callbacks = function( options ) { + + // Convert options from String-formatted to Object-formatted if needed + // (we check in cache first) + options = typeof options === "string" ? + ( optionsCache[ options ] || createOptions( options ) ) : + jQuery.extend( {}, options ); + + var // Flag to know if list is currently firing + firing, + // Last fire value (for non-forgettable lists) + memory, + // Flag to know if list was already fired + fired, + // End of the loop when firing + firingLength, + // Index of currently firing callback (modified by remove if needed) + firingIndex, + // First callback to fire (used internally by add and fireWith) + firingStart, + // Actual callback list + list = [], + // Stack of fire calls for repeatable lists + stack = !options.once && [], + // Fire callbacks + fire = function( data ) { + memory = options.memory && data; + fired = true; + firingIndex = firingStart || 0; + firingStart = 0; + firingLength = list.length; + firing = true; + for ( ; list && firingIndex < firingLength; firingIndex++ ) { + if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) { + memory = false; // To prevent further calls using add + break; + } + } + firing = false; + if ( list ) { + if ( stack ) { + if ( stack.length ) { + fire( stack.shift() ); + } + } else if ( memory ) { + list = []; + } else { + self.disable(); + } + } + }, + // Actual Callbacks object + self = { + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + // First, we save the current length + var start = list.length; + (function add( args ) { + jQuery.each( args, function( _, arg ) { + var type = jQuery.type( arg ); + if ( type === "function" ) { + if ( !options.unique || !self.has( arg ) ) { + list.push( arg ); + } + } else if ( arg && arg.length && type !== "string" ) { + // Inspect recursively + add( arg ); + } + }); + })( arguments ); + // Do we need to add the callbacks to the + // current firing batch? + if ( firing ) { + firingLength = list.length; + // With memory, if we're not firing then + // we should call right away + } else if ( memory ) { + firingStart = start; + fire( memory ); + } + } + return this; + }, + // Remove a callback from the list + remove: function() { + if ( list ) { + jQuery.each( arguments, function( _, arg ) { + var index; + while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { + list.splice( index, 1 ); + // Handle firing indexes + if ( firing ) { + if ( index <= firingLength ) { + firingLength--; + } + if ( index <= firingIndex ) { + firingIndex--; + } + } + } + }); + } + return this; + }, + // Check if a given callback is in the list. + // If no argument is given, return whether or not list has callbacks attached. + has: function( fn ) { + return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length ); + }, + // Remove all callbacks from the list + empty: function() { + list = []; + firingLength = 0; + return this; + }, + // Have the list do nothing anymore + disable: function() { + list = stack = memory = undefined; + return this; + }, + // Is it disabled? + disabled: function() { + return !list; + }, + // Lock the list in its current state + lock: function() { + stack = undefined; + if ( !memory ) { + self.disable(); + } + return this; + }, + // Is it locked? + locked: function() { + return !stack; + }, + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + args = args || []; + args = [ context, args.slice ? args.slice() : args ]; + if ( list && ( !fired || stack ) ) { + if ( firing ) { + stack.push( args ); + } else { + fire( args ); + } + } + return this; + }, + // Call all the callbacks with the given arguments + fire: function() { + self.fireWith( this, arguments ); + return this; + }, + // To know if the callbacks have already been called at least once + fired: function() { + return !!fired; + } + }; + + return self; +}; +jQuery.extend({ + + Deferred: function( func ) { + var tuples = [ + // action, add listener, listener list, final state + [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], + [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], + [ "notify", "progress", jQuery.Callbacks("memory") ] + ], + state = "pending", + promise = { + state: function() { + return state; + }, + always: function() { + deferred.done( arguments ).fail( arguments ); + return this; + }, + then: function( /* fnDone, fnFail, fnProgress */ ) { + var fns = arguments; + return jQuery.Deferred(function( newDefer ) { + jQuery.each( tuples, function( i, tuple ) { + var action = tuple[ 0 ], + fn = jQuery.isFunction( fns[ i ] ) && fns[ i ]; + // deferred[ done | fail | progress ] for forwarding actions to newDefer + deferred[ tuple[1] ](function() { + var returned = fn && fn.apply( this, arguments ); + if ( returned && jQuery.isFunction( returned.promise ) ) { + returned.promise() + .done( newDefer.resolve ) + .fail( newDefer.reject ) + .progress( newDefer.notify ); + } else { + newDefer[ action + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments ); + } + }); + }); + fns = null; + }).promise(); + }, + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + return obj != null ? jQuery.extend( obj, promise ) : promise; + } + }, + deferred = {}; + + // Keep pipe for back-compat + promise.pipe = promise.then; + + // Add list-specific methods + jQuery.each( tuples, function( i, tuple ) { + var list = tuple[ 2 ], + stateString = tuple[ 3 ]; + + // promise[ done | fail | progress ] = list.add + promise[ tuple[1] ] = list.add; + + // Handle state + if ( stateString ) { + list.add(function() { + // state = [ resolved | rejected ] + state = stateString; + + // [ reject_list | resolve_list ].disable; progress_list.lock + }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock ); + } + + // deferred[ resolve | reject | notify ] + deferred[ tuple[0] ] = function() { + deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments ); + return this; + }; + deferred[ tuple[0] + "With" ] = list.fireWith; + }); + + // Make the deferred a promise + promise.promise( deferred ); + + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + + // All done! + return deferred; + }, + + // Deferred helper + when: function( subordinate /* , ..., subordinateN */ ) { + var i = 0, + resolveValues = core_slice.call( arguments ), + length = resolveValues.length, + + // the count of uncompleted subordinates + remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, + + // the master Deferred. If resolveValues consist of only a single Deferred, just use that. + deferred = remaining === 1 ? subordinate : jQuery.Deferred(), + + // Update function for both resolve and progress values + updateFunc = function( i, contexts, values ) { + return function( value ) { + contexts[ i ] = this; + values[ i ] = arguments.length > 1 ? core_slice.call( arguments ) : value; + if( values === progressValues ) { + deferred.notifyWith( contexts, values ); + } else if ( !( --remaining ) ) { + deferred.resolveWith( contexts, values ); + } + }; + }, + + progressValues, progressContexts, resolveContexts; + + // add listeners to Deferred subordinates; treat others as resolved + if ( length > 1 ) { + progressValues = new Array( length ); + progressContexts = new Array( length ); + resolveContexts = new Array( length ); + for ( ; i < length; i++ ) { + if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) { + resolveValues[ i ].promise() + .done( updateFunc( i, resolveContexts, resolveValues ) ) + .fail( deferred.reject ) + .progress( updateFunc( i, progressContexts, progressValues ) ); + } else { + --remaining; + } + } + } + + // if we're not waiting on anything, resolve the master + if ( !remaining ) { + deferred.resolveWith( resolveContexts, resolveValues ); + } + + return deferred.promise(); + } +}); +jQuery.support = (function( support ) { + + var all, a, input, select, fragment, opt, eventName, isSupported, i, + div = document.createElement("div"); + + // Setup + div.setAttribute( "className", "t" ); + div.innerHTML = "
a"; + + // Finish early in limited (non-browser) environments + all = div.getElementsByTagName("*") || []; + a = div.getElementsByTagName("a")[ 0 ]; + if ( !a || !a.style || !all.length ) { + return support; + } + + // First batch of tests + select = document.createElement("select"); + opt = select.appendChild( document.createElement("option") ); + input = div.getElementsByTagName("input")[ 0 ]; + + a.style.cssText = "top:1px;float:left;opacity:.5"; + + // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7) + support.getSetAttribute = div.className !== "t"; + + // IE strips leading whitespace when .innerHTML is used + support.leadingWhitespace = div.firstChild.nodeType === 3; + + // Make sure that tbody elements aren't automatically inserted + // IE will insert them into empty tables + support.tbody = !div.getElementsByTagName("tbody").length; + + // Make sure that link elements get serialized correctly by innerHTML + // This requires a wrapper element in IE + support.htmlSerialize = !!div.getElementsByTagName("link").length; + + // Get the style information from getAttribute + // (IE uses .cssText instead) + support.style = /top/.test( a.getAttribute("style") ); + + // Make sure that URLs aren't manipulated + // (IE normalizes it by default) + support.hrefNormalized = a.getAttribute("href") === "/a"; + + // Make sure that element opacity exists + // (IE uses filter instead) + // Use a regex to work around a WebKit issue. See #5145 + support.opacity = /^0.5/.test( a.style.opacity ); + + // Verify style float existence + // (IE uses styleFloat instead of cssFloat) + support.cssFloat = !!a.style.cssFloat; + + // Check the default checkbox/radio value ("" on WebKit; "on" elsewhere) + support.checkOn = !!input.value; + + // Make sure that a selected-by-default option has a working selected property. + // (WebKit defaults to false instead of true, IE too, if it's in an optgroup) + support.optSelected = opt.selected; + + // Tests for enctype support on a form (#6743) + support.enctype = !!document.createElement("form").enctype; + + // Makes sure cloning an html5 element does not cause problems + // Where outerHTML is undefined, this still works + support.html5Clone = document.createElement("nav").cloneNode( true ).outerHTML !== "<:nav>"; + + // Will be defined later + support.inlineBlockNeedsLayout = false; + support.shrinkWrapBlocks = false; + support.pixelPosition = false; + support.deleteExpando = true; + support.noCloneEvent = true; + support.reliableMarginRight = true; + support.boxSizingReliable = true; + + // Make sure checked status is properly cloned + input.checked = true; + support.noCloneChecked = input.cloneNode( true ).checked; + + // Make sure that the options inside disabled selects aren't marked as disabled + // (WebKit marks them as disabled) + select.disabled = true; + support.optDisabled = !opt.disabled; + + // Support: IE<9 + try { + delete div.test; + } catch( e ) { + support.deleteExpando = false; + } + + // Check if we can trust getAttribute("value") + input = document.createElement("input"); + input.setAttribute( "value", "" ); + support.input = input.getAttribute( "value" ) === ""; + + // Check if an input maintains its value after becoming a radio + input.value = "t"; + input.setAttribute( "type", "radio" ); + support.radioValue = input.value === "t"; + + // #11217 - WebKit loses check when the name is after the checked attribute + input.setAttribute( "checked", "t" ); + input.setAttribute( "name", "t" ); + + fragment = document.createDocumentFragment(); + fragment.appendChild( input ); + + // Check if a disconnected checkbox will retain its checked + // value of true after appended to the DOM (IE6/7) + support.appendChecked = input.checked; + + // WebKit doesn't clone checked state correctly in fragments + support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Support: IE<9 + // Opera does not clone events (and typeof div.attachEvent === undefined). + // IE9-10 clones events bound via attachEvent, but they don't trigger with .click() + if ( div.attachEvent ) { + div.attachEvent( "onclick", function() { + support.noCloneEvent = false; + }); + + div.cloneNode( true ).click(); + } + + // Support: IE<9 (lack submit/change bubble), Firefox 17+ (lack focusin event) + // Beware of CSP restrictions (https://developer.mozilla.org/en/Security/CSP) + for ( i in { submit: true, change: true, focusin: true }) { + div.setAttribute( eventName = "on" + i, "t" ); + + support[ i + "Bubbles" ] = eventName in window || div.attributes[ eventName ].expando === false; + } + + div.style.backgroundClip = "content-box"; + div.cloneNode( true ).style.backgroundClip = ""; + support.clearCloneStyle = div.style.backgroundClip === "content-box"; + + // Support: IE<9 + // Iteration over object's inherited properties before its own. + for ( i in jQuery( support ) ) { + break; + } + support.ownLast = i !== "0"; + + // Run tests that need a body at doc ready + jQuery(function() { + var container, marginDiv, tds, + divReset = "padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;", + body = document.getElementsByTagName("body")[0]; + + if ( !body ) { + // Return for frameset docs that don't have a body + return; + } + + container = document.createElement("div"); + container.style.cssText = "border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px"; + + body.appendChild( container ).appendChild( div ); + + // Support: IE8 + // Check if table cells still have offsetWidth/Height when they are set + // to display:none and there are still other visible table cells in a + // table row; if so, offsetWidth/Height are not reliable for use when + // determining if an element has been hidden directly using + // display:none (it is still safe to use offsets if a parent element is + // hidden; don safety goggles and see bug #4512 for more information). + div.innerHTML = "
t
"; + tds = div.getElementsByTagName("td"); + tds[ 0 ].style.cssText = "padding:0;margin:0;border:0;display:none"; + isSupported = ( tds[ 0 ].offsetHeight === 0 ); + + tds[ 0 ].style.display = ""; + tds[ 1 ].style.display = "none"; + + // Support: IE8 + // Check if empty table cells still have offsetWidth/Height + support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 ); + + // Check box-sizing and margin behavior. + div.innerHTML = ""; + div.style.cssText = "box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;"; + + // Workaround failing boxSizing test due to offsetWidth returning wrong value + // with some non-1 values of body zoom, ticket #13543 + jQuery.swap( body, body.style.zoom != null ? { zoom: 1 } : {}, function() { + support.boxSizing = div.offsetWidth === 4; + }); + + // Use window.getComputedStyle because jsdom on node.js will break without it. + if ( window.getComputedStyle ) { + support.pixelPosition = ( window.getComputedStyle( div, null ) || {} ).top !== "1%"; + support.boxSizingReliable = ( window.getComputedStyle( div, null ) || { width: "4px" } ).width === "4px"; + + // Check if div with explicit width and no margin-right incorrectly + // gets computed margin-right based on width of container. (#3333) + // Fails in WebKit before Feb 2011 nightlies + // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right + marginDiv = div.appendChild( document.createElement("div") ); + marginDiv.style.cssText = div.style.cssText = divReset; + marginDiv.style.marginRight = marginDiv.style.width = "0"; + div.style.width = "1px"; + + support.reliableMarginRight = + !parseFloat( ( window.getComputedStyle( marginDiv, null ) || {} ).marginRight ); + } + + if ( typeof div.style.zoom !== core_strundefined ) { + // Support: IE<8 + // Check if natively block-level elements act like inline-block + // elements when setting their display to 'inline' and giving + // them layout + div.innerHTML = ""; + div.style.cssText = divReset + "width:1px;padding:1px;display:inline;zoom:1"; + support.inlineBlockNeedsLayout = ( div.offsetWidth === 3 ); + + // Support: IE6 + // Check if elements with layout shrink-wrap their children + div.style.display = "block"; + div.innerHTML = "
"; + div.firstChild.style.width = "5px"; + support.shrinkWrapBlocks = ( div.offsetWidth !== 3 ); + + if ( support.inlineBlockNeedsLayout ) { + // Prevent IE 6 from affecting layout for positioned elements #11048 + // Prevent IE from shrinking the body in IE 7 mode #12869 + // Support: IE<8 + body.style.zoom = 1; + } + } + + body.removeChild( container ); + + // Null elements to avoid leaks in IE + container = div = tds = marginDiv = null; + }); + + // Null elements to avoid leaks in IE + all = select = fragment = opt = a = input = null; + + return support; +})({}); + +var rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/, + rmultiDash = /([A-Z])/g; + +function internalData( elem, name, data, pvt /* Internal Use Only */ ){ + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var ret, thisCache, + internalKey = jQuery.expando, + + // We have to handle DOM nodes and JS objects differently because IE6-7 + // can't GC object references properly across the DOM-JS boundary + isNode = elem.nodeType, + + // Only DOM nodes need the global jQuery cache; JS object data is + // attached directly to the object so GC can occur automatically + cache = isNode ? jQuery.cache : elem, + + // Only defining an ID for JS objects if its cache already exists allows + // the code to shortcut on the same path as a DOM node with no cache + id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey; + + // Avoid doing any more work than we need to when trying to get data on an + // object that has no data at all + if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && data === undefined && typeof name === "string" ) { + return; + } + + if ( !id ) { + // Only DOM nodes need a new unique ID for each element since their data + // ends up in the global cache + if ( isNode ) { + id = elem[ internalKey ] = core_deletedIds.pop() || jQuery.guid++; + } else { + id = internalKey; + } + } + + if ( !cache[ id ] ) { + // Avoid exposing jQuery metadata on plain JS objects when the object + // is serialized using JSON.stringify + cache[ id ] = isNode ? {} : { toJSON: jQuery.noop }; + } + + // An object can be passed to jQuery.data instead of a key/value pair; this gets + // shallow copied over onto the existing cache + if ( typeof name === "object" || typeof name === "function" ) { + if ( pvt ) { + cache[ id ] = jQuery.extend( cache[ id ], name ); + } else { + cache[ id ].data = jQuery.extend( cache[ id ].data, name ); + } + } + + thisCache = cache[ id ]; + + // jQuery data() is stored in a separate object inside the object's internal data + // cache in order to avoid key collisions between internal data and user-defined + // data. + if ( !pvt ) { + if ( !thisCache.data ) { + thisCache.data = {}; + } + + thisCache = thisCache.data; + } + + if ( data !== undefined ) { + thisCache[ jQuery.camelCase( name ) ] = data; + } + + // Check for both converted-to-camel and non-converted data property names + // If a data property was specified + if ( typeof name === "string" ) { + + // First Try to find as-is property data + ret = thisCache[ name ]; + + // Test for null|undefined property data + if ( ret == null ) { + + // Try to find the camelCased property + ret = thisCache[ jQuery.camelCase( name ) ]; + } + } else { + ret = thisCache; + } + + return ret; +} + +function internalRemoveData( elem, name, pvt ) { + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var thisCache, i, + isNode = elem.nodeType, + + // See jQuery.data for more information + cache = isNode ? jQuery.cache : elem, + id = isNode ? elem[ jQuery.expando ] : jQuery.expando; + + // If there is already no cache entry for this object, there is no + // purpose in continuing + if ( !cache[ id ] ) { + return; + } + + if ( name ) { + + thisCache = pvt ? cache[ id ] : cache[ id ].data; + + if ( thisCache ) { + + // Support array or space separated string names for data keys + if ( !jQuery.isArray( name ) ) { + + // try the string as a key before any manipulation + if ( name in thisCache ) { + name = [ name ]; + } else { + + // split the camel cased version by spaces unless a key with the spaces exists + name = jQuery.camelCase( name ); + if ( name in thisCache ) { + name = [ name ]; + } else { + name = name.split(" "); + } + } + } else { + // If "name" is an array of keys... + // When data is initially created, via ("key", "val") signature, + // keys will be converted to camelCase. + // Since there is no way to tell _how_ a key was added, remove + // both plain key and camelCase key. #12786 + // This will only penalize the array argument path. + name = name.concat( jQuery.map( name, jQuery.camelCase ) ); + } + + i = name.length; + while ( i-- ) { + delete thisCache[ name[i] ]; + } + + // If there is no data left in the cache, we want to continue + // and let the cache object itself get destroyed + if ( pvt ? !isEmptyDataObject(thisCache) : !jQuery.isEmptyObject(thisCache) ) { + return; + } + } + } + + // See jQuery.data for more information + if ( !pvt ) { + delete cache[ id ].data; + + // Don't destroy the parent cache unless the internal data object + // had been the only thing left in it + if ( !isEmptyDataObject( cache[ id ] ) ) { + return; + } + } + + // Destroy the cache + if ( isNode ) { + jQuery.cleanData( [ elem ], true ); + + // Use delete when supported for expandos or `cache` is not a window per isWindow (#10080) + /* jshint eqeqeq: false */ + } else if ( jQuery.support.deleteExpando || cache != cache.window ) { + /* jshint eqeqeq: true */ + delete cache[ id ]; + + // When all else fails, null + } else { + cache[ id ] = null; + } +} + +jQuery.extend({ + cache: {}, + + // The following elements throw uncatchable exceptions if you + // attempt to add expando properties to them. + noData: { + "applet": true, + "embed": true, + // Ban all objects except for Flash (which handle expandos) + "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" + }, + + hasData: function( elem ) { + elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ]; + return !!elem && !isEmptyDataObject( elem ); + }, + + data: function( elem, name, data ) { + return internalData( elem, name, data ); + }, + + removeData: function( elem, name ) { + return internalRemoveData( elem, name ); + }, + + // For internal use only. + _data: function( elem, name, data ) { + return internalData( elem, name, data, true ); + }, + + _removeData: function( elem, name ) { + return internalRemoveData( elem, name, true ); + }, + + // A method for determining if a DOM node can handle the data expando + acceptData: function( elem ) { + // Do not set data on non-element because it will not be cleared (#8335). + if ( elem.nodeType && elem.nodeType !== 1 && elem.nodeType !== 9 ) { + return false; + } + + var noData = elem.nodeName && jQuery.noData[ elem.nodeName.toLowerCase() ]; + + // nodes accept data unless otherwise specified; rejection can be conditional + return !noData || noData !== true && elem.getAttribute("classid") === noData; + } +}); + +jQuery.fn.extend({ + data: function( key, value ) { + var attrs, name, + data = null, + i = 0, + elem = this[0]; + + // Special expections of .data basically thwart jQuery.access, + // so implement the relevant behavior ourselves + + // Gets all values + if ( key === undefined ) { + if ( this.length ) { + data = jQuery.data( elem ); + + if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) { + attrs = elem.attributes; + for ( ; i < attrs.length; i++ ) { + name = attrs[i].name; + + if ( name.indexOf("data-") === 0 ) { + name = jQuery.camelCase( name.slice(5) ); + + dataAttr( elem, name, data[ name ] ); + } + } + jQuery._data( elem, "parsedAttrs", true ); + } + } + + return data; + } + + // Sets multiple values + if ( typeof key === "object" ) { + return this.each(function() { + jQuery.data( this, key ); + }); + } + + return arguments.length > 1 ? + + // Sets one value + this.each(function() { + jQuery.data( this, key, value ); + }) : + + // Gets one value + // Try to fetch any internally stored data first + elem ? dataAttr( elem, key, jQuery.data( elem, key ) ) : null; + }, + + removeData: function( key ) { + return this.each(function() { + jQuery.removeData( this, key ); + }); + } +}); + +function dataAttr( elem, key, data ) { + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + + var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); + + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = data === "true" ? true : + data === "false" ? false : + data === "null" ? null : + // Only convert to a number if it doesn't change the string + +data + "" === data ? +data : + rbrace.test( data ) ? jQuery.parseJSON( data ) : + data; + } catch( e ) {} + + // Make sure we set the data so it isn't changed later + jQuery.data( elem, key, data ); + + } else { + data = undefined; + } + } + + return data; +} + +// checks a cache object for emptiness +function isEmptyDataObject( obj ) { + var name; + for ( name in obj ) { + + // if the public data object is empty, the private is still empty + if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) { + continue; + } + if ( name !== "toJSON" ) { + return false; + } + } + + return true; +} +jQuery.extend({ + queue: function( elem, type, data ) { + var queue; + + if ( elem ) { + type = ( type || "fx" ) + "queue"; + queue = jQuery._data( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !queue || jQuery.isArray(data) ) { + queue = jQuery._data( elem, type, jQuery.makeArray(data) ); + } else { + queue.push( data ); + } + } + return queue || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + startLength = queue.length, + fn = queue.shift(), + hooks = jQuery._queueHooks( elem, type ), + next = function() { + jQuery.dequeue( elem, type ); + }; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + startLength--; + } + + hooks.cur = fn; + if ( fn ) { + + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift( "inprogress" ); + } + + // clear up the last queue stop function + delete hooks.stop; + fn.call( elem, next, hooks ); + } + + if ( !startLength && hooks ) { + hooks.empty.fire(); + } + }, + + // not intended for public consumption - generates a queueHooks object, or returns the current one + _queueHooks: function( elem, type ) { + var key = type + "queueHooks"; + return jQuery._data( elem, key ) || jQuery._data( elem, key, { + empty: jQuery.Callbacks("once memory").add(function() { + jQuery._removeData( elem, type + "queue" ); + jQuery._removeData( elem, key ); + }) + }); + } +}); + +jQuery.fn.extend({ + queue: function( type, data ) { + var setter = 2; + + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + setter--; + } + + if ( arguments.length < setter ) { + return jQuery.queue( this[0], type ); + } + + return data === undefined ? + this : + this.each(function() { + var queue = jQuery.queue( this, type, data ); + + // ensure a hooks for this queue + jQuery._queueHooks( this, type ); + + if ( type === "fx" && queue[0] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + }); + }, + dequeue: function( type ) { + return this.each(function() { + jQuery.dequeue( this, type ); + }); + }, + // Based off of the plugin by Clint Helfers, with permission. + // http://blindsignals.com/index.php/2009/07/jquery-delay/ + delay: function( time, type ) { + time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; + type = type || "fx"; + + return this.queue( type, function( next, hooks ) { + var timeout = setTimeout( next, time ); + hooks.stop = function() { + clearTimeout( timeout ); + }; + }); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, obj ) { + var tmp, + count = 1, + defer = jQuery.Deferred(), + elements = this, + i = this.length, + resolve = function() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + }; + + if ( typeof type !== "string" ) { + obj = type; + type = undefined; + } + type = type || "fx"; + + while( i-- ) { + tmp = jQuery._data( elements[ i ], type + "queueHooks" ); + if ( tmp && tmp.empty ) { + count++; + tmp.empty.add( resolve ); + } + } + resolve(); + return defer.promise( obj ); + } +}); +var nodeHook, boolHook, + rclass = /[\t\r\n\f]/g, + rreturn = /\r/g, + rfocusable = /^(?:input|select|textarea|button|object)$/i, + rclickable = /^(?:a|area)$/i, + ruseDefault = /^(?:checked|selected)$/i, + getSetAttribute = jQuery.support.getSetAttribute, + getSetInput = jQuery.support.input; + +jQuery.fn.extend({ + attr: function( name, value ) { + return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 ); + }, + + removeAttr: function( name ) { + return this.each(function() { + jQuery.removeAttr( this, name ); + }); + }, + + prop: function( name, value ) { + return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 ); + }, + + removeProp: function( name ) { + name = jQuery.propFix[ name ] || name; + return this.each(function() { + // try/catch handles cases where IE balks (such as removing a property on window) + try { + this[ name ] = undefined; + delete this[ name ]; + } catch( e ) {} + }); + }, + + addClass: function( value ) { + var classes, elem, cur, clazz, j, + i = 0, + len = this.length, + proceed = typeof value === "string" && value; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( j ) { + jQuery( this ).addClass( value.call( this, j, this.className ) ); + }); + } + + if ( proceed ) { + // The disjunction here is for better compressibility (see removeClass) + classes = ( value || "" ).match( core_rnotwhite ) || []; + + for ( ; i < len; i++ ) { + elem = this[ i ]; + cur = elem.nodeType === 1 && ( elem.className ? + ( " " + elem.className + " " ).replace( rclass, " " ) : + " " + ); + + if ( cur ) { + j = 0; + while ( (clazz = classes[j++]) ) { + if ( cur.indexOf( " " + clazz + " " ) < 0 ) { + cur += clazz + " "; + } + } + elem.className = jQuery.trim( cur ); + + } + } + } + + return this; + }, + + removeClass: function( value ) { + var classes, elem, cur, clazz, j, + i = 0, + len = this.length, + proceed = arguments.length === 0 || typeof value === "string" && value; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( j ) { + jQuery( this ).removeClass( value.call( this, j, this.className ) ); + }); + } + if ( proceed ) { + classes = ( value || "" ).match( core_rnotwhite ) || []; + + for ( ; i < len; i++ ) { + elem = this[ i ]; + // This expression is here for better compressibility (see addClass) + cur = elem.nodeType === 1 && ( elem.className ? + ( " " + elem.className + " " ).replace( rclass, " " ) : + "" + ); + + if ( cur ) { + j = 0; + while ( (clazz = classes[j++]) ) { + // Remove *all* instances + while ( cur.indexOf( " " + clazz + " " ) >= 0 ) { + cur = cur.replace( " " + clazz + " ", " " ); + } + } + elem.className = value ? jQuery.trim( cur ) : ""; + } + } + } + + return this; + }, + + toggleClass: function( value, stateVal ) { + var type = typeof value, + isBool = typeof stateVal === "boolean"; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( i ) { + jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal ); + }); + } + + return this.each(function() { + if ( type === "string" ) { + // toggle individual class names + var className, + i = 0, + self = jQuery( this ), + state = stateVal, + classNames = value.match( core_rnotwhite ) || []; + + while ( (className = classNames[ i++ ]) ) { + // check each className given, space separated list + state = isBool ? state : !self.hasClass( className ); + self[ state ? "addClass" : "removeClass" ]( className ); + } + + // Toggle whole class name + } else if ( type === core_strundefined || type === "boolean" ) { + if ( this.className ) { + // store className if set + jQuery._data( this, "__className__", this.className ); + } + + // If the element has a class name or if we're passed "false", + // then remove the whole classname (if there was one, the above saved it). + // Otherwise bring back whatever was previously saved (if anything), + // falling back to the empty string if nothing was stored. + this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || ""; + } + }); + }, + + hasClass: function( selector ) { + var className = " " + selector + " ", + i = 0, + l = this.length; + for ( ; i < l; i++ ) { + if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) >= 0 ) { + return true; + } + } + + return false; + }, + + val: function( value ) { + var ret, hooks, isFunction, + elem = this[0]; + + if ( !arguments.length ) { + if ( elem ) { + hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ]; + + if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) { + return ret; + } + + ret = elem.value; + + return typeof ret === "string" ? + // handle most common string cases + ret.replace(rreturn, "") : + // handle cases where value is null/undef or number + ret == null ? "" : ret; + } + + return; + } + + isFunction = jQuery.isFunction( value ); + + return this.each(function( i ) { + var val; + + if ( this.nodeType !== 1 ) { + return; + } + + if ( isFunction ) { + val = value.call( this, i, jQuery( this ).val() ); + } else { + val = value; + } + + // Treat null/undefined as ""; convert numbers to string + if ( val == null ) { + val = ""; + } else if ( typeof val === "number" ) { + val += ""; + } else if ( jQuery.isArray( val ) ) { + val = jQuery.map(val, function ( value ) { + return value == null ? "" : value + ""; + }); + } + + hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; + + // If set returns undefined, fall back to normal setting + if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) { + this.value = val; + } + }); + } +}); + +jQuery.extend({ + valHooks: { + option: { + get: function( elem ) { + // Use proper attribute retrieval(#6932, #12072) + var val = jQuery.find.attr( elem, "value" ); + return val != null ? + val : + elem.text; + } + }, + select: { + get: function( elem ) { + var value, option, + options = elem.options, + index = elem.selectedIndex, + one = elem.type === "select-one" || index < 0, + values = one ? null : [], + max = one ? index + 1 : options.length, + i = index < 0 ? + max : + one ? index : 0; + + // Loop through all the selected options + for ( ; i < max; i++ ) { + option = options[ i ]; + + // oldIE doesn't update selected after form reset (#2551) + if ( ( option.selected || i === index ) && + // Don't return options that are disabled or in a disabled optgroup + ( jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null ) && + ( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) { + + // Get the specific value for the option + value = jQuery( option ).val(); + + // We don't need an array for one selects + if ( one ) { + return value; + } + + // Multi-Selects return an array + values.push( value ); + } + } + + return values; + }, + + set: function( elem, value ) { + var optionSet, option, + options = elem.options, + values = jQuery.makeArray( value ), + i = options.length; + + while ( i-- ) { + option = options[ i ]; + if ( (option.selected = jQuery.inArray( jQuery(option).val(), values ) >= 0) ) { + optionSet = true; + } + } + + // force browsers to behave consistently when non-matching value is set + if ( !optionSet ) { + elem.selectedIndex = -1; + } + return values; + } + } + }, + + attr: function( elem, name, value ) { + var hooks, ret, + nType = elem.nodeType; + + // don't get/set attributes on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + // Fallback to prop when attributes are not supported + if ( typeof elem.getAttribute === core_strundefined ) { + return jQuery.prop( elem, name, value ); + } + + // All attributes are lowercase + // Grab necessary hook if one is defined + if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { + name = name.toLowerCase(); + hooks = jQuery.attrHooks[ name ] || + ( jQuery.expr.match.bool.test( name ) ? boolHook : nodeHook ); + } + + if ( value !== undefined ) { + + if ( value === null ) { + jQuery.removeAttr( elem, name ); + + } else if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) { + return ret; + + } else { + elem.setAttribute( name, value + "" ); + return value; + } + + } else if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) { + return ret; + + } else { + ret = jQuery.find.attr( elem, name ); + + // Non-existent attributes return null, we normalize to undefined + return ret == null ? + undefined : + ret; + } + }, + + removeAttr: function( elem, value ) { + var name, propName, + i = 0, + attrNames = value && value.match( core_rnotwhite ); + + if ( attrNames && elem.nodeType === 1 ) { + while ( (name = attrNames[i++]) ) { + propName = jQuery.propFix[ name ] || name; + + // Boolean attributes get special treatment (#10870) + if ( jQuery.expr.match.bool.test( name ) ) { + // Set corresponding property to false + if ( getSetInput && getSetAttribute || !ruseDefault.test( name ) ) { + elem[ propName ] = false; + // Support: IE<9 + // Also clear defaultChecked/defaultSelected (if appropriate) + } else { + elem[ jQuery.camelCase( "default-" + name ) ] = + elem[ propName ] = false; + } + + // See #9699 for explanation of this approach (setting first, then removal) + } else { + jQuery.attr( elem, name, "" ); + } + + elem.removeAttribute( getSetAttribute ? name : propName ); + } + } + }, + + attrHooks: { + type: { + set: function( elem, value ) { + if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) { + // Setting the type on a radio button after the value resets the value in IE6-9 + // Reset value to default in case type is set after value during creation + var val = elem.value; + elem.setAttribute( "type", value ); + if ( val ) { + elem.value = val; + } + return value; + } + } + } + }, + + propFix: { + "for": "htmlFor", + "class": "className" + }, + + prop: function( elem, name, value ) { + var ret, hooks, notxml, + nType = elem.nodeType; + + // don't get/set properties on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); + + if ( notxml ) { + // Fix name and attach hooks + name = jQuery.propFix[ name ] || name; + hooks = jQuery.propHooks[ name ]; + } + + if ( value !== undefined ) { + return hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ? + ret : + ( elem[ name ] = value ); + + } else { + return hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ? + ret : + elem[ name ]; + } + }, + + propHooks: { + tabIndex: { + get: function( elem ) { + // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set + // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ + // Use proper attribute retrieval(#12072) + var tabindex = jQuery.find.attr( elem, "tabindex" ); + + return tabindex ? + parseInt( tabindex, 10 ) : + rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ? + 0 : + -1; + } + } + } +}); + +// Hooks for boolean attributes +boolHook = { + set: function( elem, value, name ) { + if ( value === false ) { + // Remove boolean attributes when set to false + jQuery.removeAttr( elem, name ); + } else if ( getSetInput && getSetAttribute || !ruseDefault.test( name ) ) { + // IE<8 needs the *property* name + elem.setAttribute( !getSetAttribute && jQuery.propFix[ name ] || name, name ); + + // Use defaultChecked and defaultSelected for oldIE + } else { + elem[ jQuery.camelCase( "default-" + name ) ] = elem[ name ] = true; + } + + return name; + } +}; +jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( i, name ) { + var getter = jQuery.expr.attrHandle[ name ] || jQuery.find.attr; + + jQuery.expr.attrHandle[ name ] = getSetInput && getSetAttribute || !ruseDefault.test( name ) ? + function( elem, name, isXML ) { + var fn = jQuery.expr.attrHandle[ name ], + ret = isXML ? + undefined : + /* jshint eqeqeq: false */ + (jQuery.expr.attrHandle[ name ] = undefined) != + getter( elem, name, isXML ) ? + + name.toLowerCase() : + null; + jQuery.expr.attrHandle[ name ] = fn; + return ret; + } : + function( elem, name, isXML ) { + return isXML ? + undefined : + elem[ jQuery.camelCase( "default-" + name ) ] ? + name.toLowerCase() : + null; + }; +}); + +// fix oldIE attroperties +if ( !getSetInput || !getSetAttribute ) { + jQuery.attrHooks.value = { + set: function( elem, value, name ) { + if ( jQuery.nodeName( elem, "input" ) ) { + // Does not return so that setAttribute is also used + elem.defaultValue = value; + } else { + // Use nodeHook if defined (#1954); otherwise setAttribute is fine + return nodeHook && nodeHook.set( elem, value, name ); + } + } + }; +} + +// IE6/7 do not support getting/setting some attributes with get/setAttribute +if ( !getSetAttribute ) { + + // Use this for any attribute in IE6/7 + // This fixes almost every IE6/7 issue + nodeHook = { + set: function( elem, value, name ) { + // Set the existing or create a new attribute node + var ret = elem.getAttributeNode( name ); + if ( !ret ) { + elem.setAttributeNode( + (ret = elem.ownerDocument.createAttribute( name )) + ); + } + + ret.value = value += ""; + + // Break association with cloned elements by also using setAttribute (#9646) + return name === "value" || value === elem.getAttribute( name ) ? + value : + undefined; + } + }; + jQuery.expr.attrHandle.id = jQuery.expr.attrHandle.name = jQuery.expr.attrHandle.coords = + // Some attributes are constructed with empty-string values when not defined + function( elem, name, isXML ) { + var ret; + return isXML ? + undefined : + (ret = elem.getAttributeNode( name )) && ret.value !== "" ? + ret.value : + null; + }; + jQuery.valHooks.button = { + get: function( elem, name ) { + var ret = elem.getAttributeNode( name ); + return ret && ret.specified ? + ret.value : + undefined; + }, + set: nodeHook.set + }; + + // Set contenteditable to false on removals(#10429) + // Setting to empty string throws an error as an invalid value + jQuery.attrHooks.contenteditable = { + set: function( elem, value, name ) { + nodeHook.set( elem, value === "" ? false : value, name ); + } + }; + + // Set width and height to auto instead of 0 on empty string( Bug #8150 ) + // This is for removals + jQuery.each([ "width", "height" ], function( i, name ) { + jQuery.attrHooks[ name ] = { + set: function( elem, value ) { + if ( value === "" ) { + elem.setAttribute( name, "auto" ); + return value; + } + } + }; + }); +} + + +// Some attributes require a special call on IE +// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx +if ( !jQuery.support.hrefNormalized ) { + // href/src property should get the full normalized URL (#10299/#12915) + jQuery.each([ "href", "src" ], function( i, name ) { + jQuery.propHooks[ name ] = { + get: function( elem ) { + return elem.getAttribute( name, 4 ); + } + }; + }); +} + +if ( !jQuery.support.style ) { + jQuery.attrHooks.style = { + get: function( elem ) { + // Return undefined in the case of empty string + // Note: IE uppercases css property names, but if we were to .toLowerCase() + // .cssText, that would destroy case senstitivity in URL's, like in "background" + return elem.style.cssText || undefined; + }, + set: function( elem, value ) { + return ( elem.style.cssText = value + "" ); + } + }; +} + +// Safari mis-reports the default selected property of an option +// Accessing the parent's selectedIndex property fixes it +if ( !jQuery.support.optSelected ) { + jQuery.propHooks.selected = { + get: function( elem ) { + var parent = elem.parentNode; + + if ( parent ) { + parent.selectedIndex; + + // Make sure that it also works with optgroups, see #5701 + if ( parent.parentNode ) { + parent.parentNode.selectedIndex; + } + } + return null; + } + }; +} + +jQuery.each([ + "tabIndex", + "readOnly", + "maxLength", + "cellSpacing", + "cellPadding", + "rowSpan", + "colSpan", + "useMap", + "frameBorder", + "contentEditable" +], function() { + jQuery.propFix[ this.toLowerCase() ] = this; +}); + +// IE6/7 call enctype encoding +if ( !jQuery.support.enctype ) { + jQuery.propFix.enctype = "encoding"; +} + +// Radios and checkboxes getter/setter +jQuery.each([ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = { + set: function( elem, value ) { + if ( jQuery.isArray( value ) ) { + return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 ); + } + } + }; + if ( !jQuery.support.checkOn ) { + jQuery.valHooks[ this ].get = function( elem ) { + // Support: Webkit + // "" is returned instead of "on" if a value isn't specified + return elem.getAttribute("value") === null ? "on" : elem.value; + }; + } +}); +var rformElems = /^(?:input|select|textarea)$/i, + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|contextmenu)|click/, + rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, + rtypenamespace = /^([^.]*)(?:\.(.+)|)$/; + +function returnTrue() { + return true; +} + +function returnFalse() { + return false; +} + +function safeActiveElement() { + try { + return document.activeElement; + } catch ( err ) { } +} + +/* + * Helper functions for managing events -- not part of the public interface. + * Props to Dean Edwards' addEvent library for many of the ideas. + */ +jQuery.event = { + + global: {}, + + add: function( elem, types, handler, data, selector ) { + var tmp, events, t, handleObjIn, + special, eventHandle, handleObj, + handlers, type, namespaces, origType, + elemData = jQuery._data( elem ); + + // Don't attach events to noData or text/comment nodes (but allow plain objects) + if ( !elemData ) { + return; + } + + // Caller can pass in an object of custom data in lieu of the handler + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + selector = handleObjIn.selector; + } + + // Make sure that the handler has a unique ID, used to find/remove it later + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure and main handler, if this is the first + if ( !(events = elemData.events) ) { + events = elemData.events = {}; + } + if ( !(eventHandle = elemData.handle) ) { + eventHandle = elemData.handle = function( e ) { + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== core_strundefined && (!e || jQuery.event.triggered !== e.type) ? + jQuery.event.dispatch.apply( eventHandle.elem, arguments ) : + undefined; + }; + // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events + eventHandle.elem = elem; + } + + // Handle multiple events separated by a space + types = ( types || "" ).match( core_rnotwhite ) || [""]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[t] ) || []; + type = origType = tmp[1]; + namespaces = ( tmp[2] || "" ).split( "." ).sort(); + + // There *must* be a type, no attaching namespace-only handlers + if ( !type ) { + continue; + } + + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; + + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; + + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; + + // handleObj is passed to all event handlers + handleObj = jQuery.extend({ + type: type, + origType: origType, + data: data, + handler: handler, + guid: handler.guid, + selector: selector, + needsContext: selector && jQuery.expr.match.needsContext.test( selector ), + namespace: namespaces.join(".") + }, handleObjIn ); + + // Init the event handler queue if we're the first + if ( !(handlers = events[ type ]) ) { + handlers = events[ type ] = []; + handlers.delegateCount = 0; + + // Only use addEventListener/attachEvent if the special events handler returns false + if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + // Bind the global event handler to the element + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle, false ); + + } else if ( elem.attachEvent ) { + elem.attachEvent( "on" + type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } + + // Keep track of which events have ever been used, for event optimization + jQuery.event.global[ type ] = true; + } + + // Nullify elem to prevent memory leaks in IE + elem = null; + }, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { + var j, handleObj, tmp, + origCount, t, events, + special, handlers, type, + namespaces, origType, + elemData = jQuery.hasData( elem ) && jQuery._data( elem ); + + if ( !elemData || !(events = elemData.events) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = ( types || "" ).match( core_rnotwhite ) || [""]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[t] ) || []; + type = origType = tmp[1]; + namespaces = ( tmp[2] || "" ).split( "." ).sort(); + + // Unbind all events (on this namespace, if provided) for the element + if ( !type ) { + for ( type in events ) { + jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); + } + continue; + } + + special = jQuery.event.special[ type ] || {}; + type = ( selector ? special.delegateType : special.bindType ) || type; + handlers = events[ type ] || []; + tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ); + + // Remove matching events + origCount = j = handlers.length; + while ( j-- ) { + handleObj = handlers[ j ]; + + if ( ( mappedTypes || origType === handleObj.origType ) && + ( !handler || handler.guid === handleObj.guid ) && + ( !tmp || tmp.test( handleObj.namespace ) ) && + ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) { + handlers.splice( j, 1 ); + + if ( handleObj.selector ) { + handlers.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + } + + // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + if ( origCount && !handlers.length ) { + if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) { + jQuery.removeEvent( elem, type, elemData.handle ); + } + + delete events[ type ]; + } + } + + // Remove the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + delete elemData.handle; + + // removeData also checks for emptiness and clears the expando if empty + // so use it instead of delete + jQuery._removeData( elem, "events" ); + } + }, + + trigger: function( event, data, elem, onlyHandlers ) { + var handle, ontype, cur, + bubbleType, special, tmp, i, + eventPath = [ elem || document ], + type = core_hasOwn.call( event, "type" ) ? event.type : event, + namespaces = core_hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : []; + + cur = tmp = elem = elem || document; + + // Don't do events on text and comment nodes + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + // focus/blur morphs to focusin/out; ensure we're not firing them right now + if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { + return; + } + + if ( type.indexOf(".") >= 0 ) { + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split("."); + type = namespaces.shift(); + namespaces.sort(); + } + ontype = type.indexOf(":") < 0 && "on" + type; + + // Caller can pass in a jQuery.Event object, Object, or just an event type string + event = event[ jQuery.expando ] ? + event : + new jQuery.Event( type, typeof event === "object" && event ); + + // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) + event.isTrigger = onlyHandlers ? 2 : 3; + event.namespace = namespaces.join("."); + event.namespace_re = event.namespace ? + new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) : + null; + + // Clean up the event in case it is being reused + event.result = undefined; + if ( !event.target ) { + event.target = elem; + } + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data == null ? + [ event ] : + jQuery.makeArray( data, [ event ] ); + + // Allow special events to draw outside the lines + special = jQuery.event.special[ type ] || {}; + if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { + return; + } + + // Determine event propagation path in advance, per W3C events spec (#9951) + // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) + if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { + + bubbleType = special.delegateType || type; + if ( !rfocusMorph.test( bubbleType + type ) ) { + cur = cur.parentNode; + } + for ( ; cur; cur = cur.parentNode ) { + eventPath.push( cur ); + tmp = cur; + } + + // Only add window if we got to document (e.g., not plain obj or detached DOM) + if ( tmp === (elem.ownerDocument || document) ) { + eventPath.push( tmp.defaultView || tmp.parentWindow || window ); + } + } + + // Fire handlers on the event path + i = 0; + while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) { + + event.type = i > 1 ? + bubbleType : + special.bindType || type; + + // jQuery handler + handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" ); + if ( handle ) { + handle.apply( cur, data ); + } + + // Native handler + handle = ontype && cur[ ontype ]; + if ( handle && jQuery.acceptData( cur ) && handle.apply && handle.apply( cur, data ) === false ) { + event.preventDefault(); + } + } + event.type = type; + + // If nobody prevented the default action, do it now + if ( !onlyHandlers && !event.isDefaultPrevented() ) { + + if ( (!special._default || special._default.apply( eventPath.pop(), data ) === false) && + jQuery.acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name name as the event. + // Can't use an .isFunction() check here because IE6/7 fails that test. + // Don't do default actions on window, that's where global variables be (#6170) + if ( ontype && elem[ type ] && !jQuery.isWindow( elem ) ) { + + // Don't re-trigger an onFOO event when we call its FOO() method + tmp = elem[ ontype ]; + + if ( tmp ) { + elem[ ontype ] = null; + } + + // Prevent re-triggering of the same event, since we already bubbled it above + jQuery.event.triggered = type; + try { + elem[ type ](); + } catch ( e ) { + // IE<9 dies on focus/blur to hidden element (#1486,#12518) + // only reproducible on winXP IE8 native, not IE9 in IE8 mode + } + jQuery.event.triggered = undefined; + + if ( tmp ) { + elem[ ontype ] = tmp; + } + } + } + } + + return event.result; + }, + + dispatch: function( event ) { + + // Make a writable jQuery.Event from the native event object + event = jQuery.event.fix( event ); + + var i, ret, handleObj, matched, j, + handlerQueue = [], + args = core_slice.call( arguments ), + handlers = ( jQuery._data( this, "events" ) || {} )[ event.type ] || [], + special = jQuery.event.special[ event.type ] || {}; + + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[0] = event; + event.delegateTarget = this; + + // Call the preDispatch hook for the mapped type, and let it bail if desired + if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { + return; + } + + // Determine handlers + handlerQueue = jQuery.event.handlers.call( this, event, handlers ); + + // Run delegates first; they may want to stop propagation beneath us + i = 0; + while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) { + event.currentTarget = matched.elem; + + j = 0; + while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) { + + // Triggered event must either 1) have no namespace, or + // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace). + if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) { + + event.handleObj = handleObj; + event.data = handleObj.data; + + ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler ) + .apply( matched.elem, args ); + + if ( ret !== undefined ) { + if ( (event.result = ret) === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } + + // Call the postDispatch hook for the mapped type + if ( special.postDispatch ) { + special.postDispatch.call( this, event ); + } + + return event.result; + }, + + handlers: function( event, handlers ) { + var sel, handleObj, matches, i, + handlerQueue = [], + delegateCount = handlers.delegateCount, + cur = event.target; + + // Find delegate handlers + // Black-hole SVG instance trees (#13180) + // Avoid non-left-click bubbling in Firefox (#3861) + if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) { + + /* jshint eqeqeq: false */ + for ( ; cur != this; cur = cur.parentNode || this ) { + /* jshint eqeqeq: true */ + + // Don't check non-elements (#13208) + // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) + if ( cur.nodeType === 1 && (cur.disabled !== true || event.type !== "click") ) { + matches = []; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + + // Don't conflict with Object.prototype properties (#13203) + sel = handleObj.selector + " "; + + if ( matches[ sel ] === undefined ) { + matches[ sel ] = handleObj.needsContext ? + jQuery( sel, this ).index( cur ) >= 0 : + jQuery.find( sel, this, null, [ cur ] ).length; + } + if ( matches[ sel ] ) { + matches.push( handleObj ); + } + } + if ( matches.length ) { + handlerQueue.push({ elem: cur, handlers: matches }); + } + } + } + } + + // Add the remaining (directly-bound) handlers + if ( delegateCount < handlers.length ) { + handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) }); + } + + return handlerQueue; + }, + + fix: function( event ) { + if ( event[ jQuery.expando ] ) { + return event; + } + + // Create a writable copy of the event object and normalize some properties + var i, prop, copy, + type = event.type, + originalEvent = event, + fixHook = this.fixHooks[ type ]; + + if ( !fixHook ) { + this.fixHooks[ type ] = fixHook = + rmouseEvent.test( type ) ? this.mouseHooks : + rkeyEvent.test( type ) ? this.keyHooks : + {}; + } + copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props; + + event = new jQuery.Event( originalEvent ); + + i = copy.length; + while ( i-- ) { + prop = copy[ i ]; + event[ prop ] = originalEvent[ prop ]; + } + + // Support: IE<9 + // Fix target property (#1925) + if ( !event.target ) { + event.target = originalEvent.srcElement || document; + } + + // Support: Chrome 23+, Safari? + // Target should not be a text node (#504, #13143) + if ( event.target.nodeType === 3 ) { + event.target = event.target.parentNode; + } + + // Support: IE<9 + // For mouse/key events, metaKey==false if it's undefined (#3368, #11328) + event.metaKey = !!event.metaKey; + + return fixHook.filter ? fixHook.filter( event, originalEvent ) : event; + }, + + // Includes some event props shared by KeyEvent and MouseEvent + props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "), + + fixHooks: {}, + + keyHooks: { + props: "char charCode key keyCode".split(" "), + filter: function( event, original ) { + + // Add which for key events + if ( event.which == null ) { + event.which = original.charCode != null ? original.charCode : original.keyCode; + } + + return event; + } + }, + + mouseHooks: { + props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "), + filter: function( event, original ) { + var body, eventDoc, doc, + button = original.button, + fromElement = original.fromElement; + + // Calculate pageX/Y if missing and clientX/Y available + if ( event.pageX == null && original.clientX != null ) { + eventDoc = event.target.ownerDocument || document; + doc = eventDoc.documentElement; + body = eventDoc.body; + + event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 ); + event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 ); + } + + // Add relatedTarget, if necessary + if ( !event.relatedTarget && fromElement ) { + event.relatedTarget = fromElement === event.target ? original.toElement : fromElement; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + // Note: button is not normalized, so don't use it + if ( !event.which && button !== undefined ) { + event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); + } + + return event; + } + }, + + special: { + load: { + // Prevent triggered image.load events from bubbling to window.load + noBubble: true + }, + focus: { + // Fire native event if possible so blur/focus sequence is correct + trigger: function() { + if ( this !== safeActiveElement() && this.focus ) { + try { + this.focus(); + return false; + } catch ( e ) { + // Support: IE<9 + // If we error on focus to hidden element (#1486, #12518), + // let .trigger() run the handlers + } + } + }, + delegateType: "focusin" + }, + blur: { + trigger: function() { + if ( this === safeActiveElement() && this.blur ) { + this.blur(); + return false; + } + }, + delegateType: "focusout" + }, + click: { + // For checkbox, fire native event so checked state will be right + trigger: function() { + if ( jQuery.nodeName( this, "input" ) && this.type === "checkbox" && this.click ) { + this.click(); + return false; + } + }, + + // For cross-browser consistency, don't fire native .click() on links + _default: function( event ) { + return jQuery.nodeName( event.target, "a" ); + } + }, + + beforeunload: { + postDispatch: function( event ) { + + // Even when returnValue equals to undefined Firefox will still show alert + if ( event.result !== undefined ) { + event.originalEvent.returnValue = event.result; + } + } + } + }, + + simulate: function( type, elem, event, bubble ) { + // Piggyback on a donor event to simulate a different one. + // Fake originalEvent to avoid donor's stopPropagation, but if the + // simulated event prevents default then we do the same on the donor. + var e = jQuery.extend( + new jQuery.Event(), + event, + { + type: type, + isSimulated: true, + originalEvent: {} + } + ); + if ( bubble ) { + jQuery.event.trigger( e, null, elem ); + } else { + jQuery.event.dispatch.call( elem, e ); + } + if ( e.isDefaultPrevented() ) { + event.preventDefault(); + } + } +}; + +jQuery.removeEvent = document.removeEventListener ? + function( elem, type, handle ) { + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle, false ); + } + } : + function( elem, type, handle ) { + var name = "on" + type; + + if ( elem.detachEvent ) { + + // #8545, #7054, preventing memory leaks for custom events in IE6-8 + // detachEvent needed property on element, by name of that event, to properly expose it to GC + if ( typeof elem[ name ] === core_strundefined ) { + elem[ name ] = null; + } + + elem.detachEvent( name, handle ); + } + }; + +jQuery.Event = function( src, props ) { + // Allow instantiation without the 'new' keyword + if ( !(this instanceof jQuery.Event) ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false || + src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || jQuery.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse, + + preventDefault: function() { + var e = this.originalEvent; + + this.isDefaultPrevented = returnTrue; + if ( !e ) { + return; + } + + // If preventDefault exists, run it on the original event + if ( e.preventDefault ) { + e.preventDefault(); + + // Support: IE + // Otherwise set the returnValue property of the original event to false + } else { + e.returnValue = false; + } + }, + stopPropagation: function() { + var e = this.originalEvent; + + this.isPropagationStopped = returnTrue; + if ( !e ) { + return; + } + // If stopPropagation exists, run it on the original event + if ( e.stopPropagation ) { + e.stopPropagation(); + } + + // Support: IE + // Set the cancelBubble property of the original event to true + e.cancelBubble = true; + }, + stopImmediatePropagation: function() { + this.isImmediatePropagationStopped = returnTrue; + this.stopPropagation(); + } +}; + +// Create mouseenter/leave events using mouseover/out and event-time checks +jQuery.each({ + mouseenter: "mouseover", + mouseleave: "mouseout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + delegateType: fix, + bindType: fix, + + handle: function( event ) { + var ret, + target = this, + related = event.relatedTarget, + handleObj = event.handleObj; + + // For mousenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || (related !== target && !jQuery.contains( target, related )) ) { + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = fix; + } + return ret; + } + }; +}); + +// IE submit delegation +if ( !jQuery.support.submitBubbles ) { + + jQuery.event.special.submit = { + setup: function() { + // Only need this for delegated form submit events + if ( jQuery.nodeName( this, "form" ) ) { + return false; + } + + // Lazy-add a submit handler when a descendant form may potentially be submitted + jQuery.event.add( this, "click._submit keypress._submit", function( e ) { + // Node name check avoids a VML-related crash in IE (#9807) + var elem = e.target, + form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined; + if ( form && !jQuery._data( form, "submitBubbles" ) ) { + jQuery.event.add( form, "submit._submit", function( event ) { + event._submit_bubble = true; + }); + jQuery._data( form, "submitBubbles", true ); + } + }); + // return undefined since we don't need an event listener + }, + + postDispatch: function( event ) { + // If form was submitted by the user, bubble the event up the tree + if ( event._submit_bubble ) { + delete event._submit_bubble; + if ( this.parentNode && !event.isTrigger ) { + jQuery.event.simulate( "submit", this.parentNode, event, true ); + } + } + }, + + teardown: function() { + // Only need this for delegated form submit events + if ( jQuery.nodeName( this, "form" ) ) { + return false; + } + + // Remove delegated handlers; cleanData eventually reaps submit handlers attached above + jQuery.event.remove( this, "._submit" ); + } + }; +} + +// IE change delegation and checkbox/radio fix +if ( !jQuery.support.changeBubbles ) { + + jQuery.event.special.change = { + + setup: function() { + + if ( rformElems.test( this.nodeName ) ) { + // IE doesn't fire change on a check/radio until blur; trigger it on click + // after a propertychange. Eat the blur-change in special.change.handle. + // This still fires onchange a second time for check/radio after blur. + if ( this.type === "checkbox" || this.type === "radio" ) { + jQuery.event.add( this, "propertychange._change", function( event ) { + if ( event.originalEvent.propertyName === "checked" ) { + this._just_changed = true; + } + }); + jQuery.event.add( this, "click._change", function( event ) { + if ( this._just_changed && !event.isTrigger ) { + this._just_changed = false; + } + // Allow triggered, simulated change events (#11500) + jQuery.event.simulate( "change", this, event, true ); + }); + } + return false; + } + // Delegated event; lazy-add a change handler on descendant inputs + jQuery.event.add( this, "beforeactivate._change", function( e ) { + var elem = e.target; + + if ( rformElems.test( elem.nodeName ) && !jQuery._data( elem, "changeBubbles" ) ) { + jQuery.event.add( elem, "change._change", function( event ) { + if ( this.parentNode && !event.isSimulated && !event.isTrigger ) { + jQuery.event.simulate( "change", this.parentNode, event, true ); + } + }); + jQuery._data( elem, "changeBubbles", true ); + } + }); + }, + + handle: function( event ) { + var elem = event.target; + + // Swallow native change events from checkbox/radio, we already triggered them above + if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) { + return event.handleObj.handler.apply( this, arguments ); + } + }, + + teardown: function() { + jQuery.event.remove( this, "._change" ); + + return !rformElems.test( this.nodeName ); + } + }; +} + +// Create "bubbling" focus and blur events +if ( !jQuery.support.focusinBubbles ) { + jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler while someone wants focusin/focusout + var attaches = 0, + handler = function( event ) { + jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true ); + }; + + jQuery.event.special[ fix ] = { + setup: function() { + if ( attaches++ === 0 ) { + document.addEventListener( orig, handler, true ); + } + }, + teardown: function() { + if ( --attaches === 0 ) { + document.removeEventListener( orig, handler, true ); + } + } + }; + }); +} + +jQuery.fn.extend({ + + on: function( types, selector, data, fn, /*INTERNAL*/ one ) { + var type, origFn; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { + // ( types-Object, data ) + data = data || selector; + selector = undefined; + } + for ( type in types ) { + this.on( type, selector, data, types[ type ], one ); + } + return this; + } + + if ( data == null && fn == null ) { + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return this; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return this.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + }); + }, + one: function( types, selector, data, fn ) { + return this.on( types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + var handleObj, type; + if ( types && types.preventDefault && types.handleObj ) { + // ( event ) dispatched jQuery.Event + handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { + // ( types-object [, selector] ) + for ( type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; + } + if ( selector === false || typeof selector === "function" ) { + // ( types [, fn] ) + fn = selector; + selector = undefined; + } + if ( fn === false ) { + fn = returnFalse; + } + return this.each(function() { + jQuery.event.remove( this, types, fn, selector ); + }); + }, + + trigger: function( type, data ) { + return this.each(function() { + jQuery.event.trigger( type, data, this ); + }); + }, + triggerHandler: function( type, data ) { + var elem = this[0]; + if ( elem ) { + return jQuery.event.trigger( type, data, elem, true ); + } + } +}); +var isSimple = /^.[^:#\[\.,]*$/, + rparentsprev = /^(?:parents|prev(?:Until|All))/, + rneedsContext = jQuery.expr.match.needsContext, + // methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.fn.extend({ + find: function( selector ) { + var i, + ret = [], + self = this, + len = self.length; + + if ( typeof selector !== "string" ) { + return this.pushStack( jQuery( selector ).filter(function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + }) ); + } + + for ( i = 0; i < len; i++ ) { + jQuery.find( selector, self[ i ], ret ); + } + + // Needed because $( selector, context ) becomes $( context ).find( selector ) + ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret ); + ret.selector = this.selector ? this.selector + " " + selector : selector; + return ret; + }, + + has: function( target ) { + var i, + targets = jQuery( target, this ), + len = targets.length; + + return this.filter(function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( this, targets[i] ) ) { + return true; + } + } + }); + }, + + not: function( selector ) { + return this.pushStack( winnow(this, selector || [], true) ); + }, + + filter: function( selector ) { + return this.pushStack( winnow(this, selector || [], false) ); + }, + + is: function( selector ) { + return !!winnow( + this, + + // If this is a positional/relative selector, check membership in the returned set + // so $("p:first").is("p:last") won't return true for a doc with two "p". + typeof selector === "string" && rneedsContext.test( selector ) ? + jQuery( selector ) : + selector || [], + false + ).length; + }, + + closest: function( selectors, context ) { + var cur, + i = 0, + l = this.length, + ret = [], + pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ? + jQuery( selectors, context || this.context ) : + 0; + + for ( ; i < l; i++ ) { + for ( cur = this[i]; cur && cur !== context; cur = cur.parentNode ) { + // Always skip document fragments + if ( cur.nodeType < 11 && (pos ? + pos.index(cur) > -1 : + + // Don't pass non-elements to Sizzle + cur.nodeType === 1 && + jQuery.find.matchesSelector(cur, selectors)) ) { + + cur = ret.push( cur ); + break; + } + } + } + + return this.pushStack( ret.length > 1 ? jQuery.unique( ret ) : ret ); + }, + + // Determine the position of an element within + // the matched set of elements + index: function( elem ) { + + // No argument, return index in parent + if ( !elem ) { + return ( this[0] && this[0].parentNode ) ? this.first().prevAll().length : -1; + } + + // index in selector + if ( typeof elem === "string" ) { + return jQuery.inArray( this[0], jQuery( elem ) ); + } + + // Locate the position of the desired element + return jQuery.inArray( + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[0] : elem, this ); + }, + + add: function( selector, context ) { + var set = typeof selector === "string" ? + jQuery( selector, context ) : + jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ), + all = jQuery.merge( this.get(), set ); + + return this.pushStack( jQuery.unique(all) ); + }, + + addBack: function( selector ) { + return this.add( selector == null ? + this.prevObject : this.prevObject.filter(selector) + ); + } +}); + +function sibling( cur, dir ) { + do { + cur = cur[ dir ]; + } while ( cur && cur.nodeType !== 1 ); + + return cur; +} + +jQuery.each({ + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return jQuery.dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, i, until ) { + return jQuery.dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return sibling( elem, "nextSibling" ); + }, + prev: function( elem ) { + return sibling( elem, "previousSibling" ); + }, + nextAll: function( elem ) { + return jQuery.dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return jQuery.dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, i, until ) { + return jQuery.dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, i, until ) { + return jQuery.dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem ); + }, + children: function( elem ) { + return jQuery.sibling( elem.firstChild ); + }, + contents: function( elem ) { + return jQuery.nodeName( elem, "iframe" ) ? + elem.contentDocument || elem.contentWindow.document : + jQuery.merge( [], elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var ret = jQuery.map( this, fn, until ); + + if ( name.slice( -5 ) !== "Until" ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + ret = jQuery.filter( selector, ret ); + } + + if ( this.length > 1 ) { + // Remove duplicates + if ( !guaranteedUnique[ name ] ) { + ret = jQuery.unique( ret ); + } + + // Reverse order for parents* and prev-derivatives + if ( rparentsprev.test( name ) ) { + ret = ret.reverse(); + } + } + + return this.pushStack( ret ); + }; +}); + +jQuery.extend({ + filter: function( expr, elems, not ) { + var elem = elems[ 0 ]; + + if ( not ) { + expr = ":not(" + expr + ")"; + } + + return elems.length === 1 && elem.nodeType === 1 ? + jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] : + jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { + return elem.nodeType === 1; + })); + }, + + dir: function( elem, dir, until ) { + var matched = [], + cur = elem[ dir ]; + + while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) { + if ( cur.nodeType === 1 ) { + matched.push( cur ); + } + cur = cur[dir]; + } + return matched; + }, + + sibling: function( n, elem ) { + var r = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + r.push( n ); + } + } + + return r; + } +}); + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, not ) { + if ( jQuery.isFunction( qualifier ) ) { + return jQuery.grep( elements, function( elem, i ) { + /* jshint -W018 */ + return !!qualifier.call( elem, i, elem ) !== not; + }); + + } + + if ( qualifier.nodeType ) { + return jQuery.grep( elements, function( elem ) { + return ( elem === qualifier ) !== not; + }); + + } + + if ( typeof qualifier === "string" ) { + if ( isSimple.test( qualifier ) ) { + return jQuery.filter( qualifier, elements, not ); + } + + qualifier = jQuery.filter( qualifier, elements ); + } + + return jQuery.grep( elements, function( elem ) { + return ( jQuery.inArray( elem, qualifier ) >= 0 ) !== not; + }); +} +function createSafeFragment( document ) { + var list = nodeNames.split( "|" ), + safeFrag = document.createDocumentFragment(); + + if ( safeFrag.createElement ) { + while ( list.length ) { + safeFrag.createElement( + list.pop() + ); + } + } + return safeFrag; +} + +var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" + + "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video", + rinlinejQuery = / jQuery\d+="(?:null|\d+)"/g, + rnoshimcache = new RegExp("<(?:" + nodeNames + ")[\\s/>]", "i"), + rleadingWhitespace = /^\s+/, + rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi, + rtagName = /<([\w:]+)/, + rtbody = /\s*$/g, + + // We have to close these tags to support XHTML (#13200) + wrapMap = { + option: [ 1, "" ], + legend: [ 1, "
", "
" ], + area: [ 1, "", "" ], + param: [ 1, "", "" ], + thead: [ 1, "", "
" ], + tr: [ 2, "", "
" ], + col: [ 2, "", "
" ], + td: [ 3, "", "
" ], + + // IE6-8 can't serialize link, script, style, or any html5 (NoScope) tags, + // unless wrapped in a div with non-breaking characters in front of it. + _default: jQuery.support.htmlSerialize ? [ 0, "", "" ] : [ 1, "X
", "
" ] + }, + safeFragment = createSafeFragment( document ), + fragmentDiv = safeFragment.appendChild( document.createElement("div") ); + +wrapMap.optgroup = wrapMap.option; +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + +jQuery.fn.extend({ + text: function( value ) { + return jQuery.access( this, function( value ) { + return value === undefined ? + jQuery.text( this ) : + this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( value ) ); + }, null, value, arguments.length ); + }, + + append: function() { + return this.domManip( arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.appendChild( elem ); + } + }); + }, + + prepend: function() { + return this.domManip( arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.insertBefore( elem, target.firstChild ); + } + }); + }, + + before: function() { + return this.domManip( arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this ); + } + }); + }, + + after: function() { + return this.domManip( arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this.nextSibling ); + } + }); + }, + + // keepData is for internal use only--do not document + remove: function( selector, keepData ) { + var elem, + elems = selector ? jQuery.filter( selector, this ) : this, + i = 0; + + for ( ; (elem = elems[i]) != null; i++ ) { + + if ( !keepData && elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem ) ); + } + + if ( elem.parentNode ) { + if ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) { + setGlobalEval( getAll( elem, "script" ) ); + } + elem.parentNode.removeChild( elem ); + } + } + + return this; + }, + + empty: function() { + var elem, + i = 0; + + for ( ; (elem = this[i]) != null; i++ ) { + // Remove element nodes and prevent memory leaks + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + } + + // Remove any remaining nodes + while ( elem.firstChild ) { + elem.removeChild( elem.firstChild ); + } + + // If this is a select, ensure that it displays empty (#12336) + // Support: IE<9 + if ( elem.options && jQuery.nodeName( elem, "select" ) ) { + elem.options.length = 0; + } + } + + return this; + }, + + clone: function( dataAndEvents, deepDataAndEvents ) { + dataAndEvents = dataAndEvents == null ? false : dataAndEvents; + deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; + + return this.map( function () { + return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); + }); + }, + + html: function( value ) { + return jQuery.access( this, function( value ) { + var elem = this[0] || {}, + i = 0, + l = this.length; + + if ( value === undefined ) { + return elem.nodeType === 1 ? + elem.innerHTML.replace( rinlinejQuery, "" ) : + undefined; + } + + // See if we can take a shortcut and just use innerHTML + if ( typeof value === "string" && !rnoInnerhtml.test( value ) && + ( jQuery.support.htmlSerialize || !rnoshimcache.test( value ) ) && + ( jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value ) ) && + !wrapMap[ ( rtagName.exec( value ) || ["", ""] )[1].toLowerCase() ] ) { + + value = value.replace( rxhtmlTag, "<$1>" ); + + try { + for (; i < l; i++ ) { + // Remove element nodes and prevent memory leaks + elem = this[i] || {}; + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + elem.innerHTML = value; + } + } + + elem = 0; + + // If using innerHTML throws an exception, use the fallback method + } catch(e) {} + } + + if ( elem ) { + this.empty().append( value ); + } + }, null, value, arguments.length ); + }, + + replaceWith: function() { + var + // Snapshot the DOM in case .domManip sweeps something relevant into its fragment + args = jQuery.map( this, function( elem ) { + return [ elem.nextSibling, elem.parentNode ]; + }), + i = 0; + + // Make the changes, replacing each context element with the new content + this.domManip( arguments, function( elem ) { + var next = args[ i++ ], + parent = args[ i++ ]; + + if ( parent ) { + // Don't use the snapshot next if it has moved (#13810) + if ( next && next.parentNode !== parent ) { + next = this.nextSibling; + } + jQuery( this ).remove(); + parent.insertBefore( elem, next ); + } + // Allow new content to include elements from the context set + }, true ); + + // Force removal if there was no new content (e.g., from empty arguments) + return i ? this : this.remove(); + }, + + detach: function( selector ) { + return this.remove( selector, true ); + }, + + domManip: function( args, callback, allowIntersection ) { + + // Flatten any nested arrays + args = core_concat.apply( [], args ); + + var first, node, hasScripts, + scripts, doc, fragment, + i = 0, + l = this.length, + set = this, + iNoClone = l - 1, + value = args[0], + isFunction = jQuery.isFunction( value ); + + // We can't cloneNode fragments that contain checked, in WebKit + if ( isFunction || !( l <= 1 || typeof value !== "string" || jQuery.support.checkClone || !rchecked.test( value ) ) ) { + return this.each(function( index ) { + var self = set.eq( index ); + if ( isFunction ) { + args[0] = value.call( this, index, self.html() ); + } + self.domManip( args, callback, allowIntersection ); + }); + } + + if ( l ) { + fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, !allowIntersection && this ); + first = fragment.firstChild; + + if ( fragment.childNodes.length === 1 ) { + fragment = first; + } + + if ( first ) { + scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); + hasScripts = scripts.length; + + // Use the original fragment for the last item instead of the first because it can end up + // being emptied incorrectly in certain situations (#8070). + for ( ; i < l; i++ ) { + node = fragment; + + if ( i !== iNoClone ) { + node = jQuery.clone( node, true, true ); + + // Keep references to cloned scripts for later restoration + if ( hasScripts ) { + jQuery.merge( scripts, getAll( node, "script" ) ); + } + } + + callback.call( this[i], node, i ); + } + + if ( hasScripts ) { + doc = scripts[ scripts.length - 1 ].ownerDocument; + + // Reenable scripts + jQuery.map( scripts, restoreScript ); + + // Evaluate executable scripts on first document insertion + for ( i = 0; i < hasScripts; i++ ) { + node = scripts[ i ]; + if ( rscriptType.test( node.type || "" ) && + !jQuery._data( node, "globalEval" ) && jQuery.contains( doc, node ) ) { + + if ( node.src ) { + // Hope ajax is available... + jQuery._evalUrl( node.src ); + } else { + jQuery.globalEval( ( node.text || node.textContent || node.innerHTML || "" ).replace( rcleanScript, "" ) ); + } + } + } + } + + // Fix #11809: Avoid leaking memory + fragment = first = null; + } + } + + return this; + } +}); + +// Support: IE<8 +// Manipulating tables requires a tbody +function manipulationTarget( elem, content ) { + return jQuery.nodeName( elem, "table" ) && + jQuery.nodeName( content.nodeType === 1 ? content : content.firstChild, "tr" ) ? + + elem.getElementsByTagName("tbody")[0] || + elem.appendChild( elem.ownerDocument.createElement("tbody") ) : + elem; +} + +// Replace/restore the type attribute of script elements for safe DOM manipulation +function disableScript( elem ) { + elem.type = (jQuery.find.attr( elem, "type" ) !== null) + "/" + elem.type; + return elem; +} +function restoreScript( elem ) { + var match = rscriptTypeMasked.exec( elem.type ); + if ( match ) { + elem.type = match[1]; + } else { + elem.removeAttribute("type"); + } + return elem; +} + +// Mark scripts as having already been evaluated +function setGlobalEval( elems, refElements ) { + var elem, + i = 0; + for ( ; (elem = elems[i]) != null; i++ ) { + jQuery._data( elem, "globalEval", !refElements || jQuery._data( refElements[i], "globalEval" ) ); + } +} + +function cloneCopyEvent( src, dest ) { + + if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) { + return; + } + + var type, i, l, + oldData = jQuery._data( src ), + curData = jQuery._data( dest, oldData ), + events = oldData.events; + + if ( events ) { + delete curData.handle; + curData.events = {}; + + for ( type in events ) { + for ( i = 0, l = events[ type ].length; i < l; i++ ) { + jQuery.event.add( dest, type, events[ type ][ i ] ); + } + } + } + + // make the cloned public data object a copy from the original + if ( curData.data ) { + curData.data = jQuery.extend( {}, curData.data ); + } +} + +function fixCloneNodeIssues( src, dest ) { + var nodeName, e, data; + + // We do not need to do anything for non-Elements + if ( dest.nodeType !== 1 ) { + return; + } + + nodeName = dest.nodeName.toLowerCase(); + + // IE6-8 copies events bound via attachEvent when using cloneNode. + if ( !jQuery.support.noCloneEvent && dest[ jQuery.expando ] ) { + data = jQuery._data( dest ); + + for ( e in data.events ) { + jQuery.removeEvent( dest, e, data.handle ); + } + + // Event data gets referenced instead of copied if the expando gets copied too + dest.removeAttribute( jQuery.expando ); + } + + // IE blanks contents when cloning scripts, and tries to evaluate newly-set text + if ( nodeName === "script" && dest.text !== src.text ) { + disableScript( dest ).text = src.text; + restoreScript( dest ); + + // IE6-10 improperly clones children of object elements using classid. + // IE10 throws NoModificationAllowedError if parent is null, #12132. + } else if ( nodeName === "object" ) { + if ( dest.parentNode ) { + dest.outerHTML = src.outerHTML; + } + + // This path appears unavoidable for IE9. When cloning an object + // element in IE9, the outerHTML strategy above is not sufficient. + // If the src has innerHTML and the destination does not, + // copy the src.innerHTML into the dest.innerHTML. #10324 + if ( jQuery.support.html5Clone && ( src.innerHTML && !jQuery.trim(dest.innerHTML) ) ) { + dest.innerHTML = src.innerHTML; + } + + } else if ( nodeName === "input" && manipulation_rcheckableType.test( src.type ) ) { + // IE6-8 fails to persist the checked state of a cloned checkbox + // or radio button. Worse, IE6-7 fail to give the cloned element + // a checked appearance if the defaultChecked value isn't also set + + dest.defaultChecked = dest.checked = src.checked; + + // IE6-7 get confused and end up setting the value of a cloned + // checkbox/radio button to an empty string instead of "on" + if ( dest.value !== src.value ) { + dest.value = src.value; + } + + // IE6-8 fails to return the selected option to the default selected + // state when cloning options + } else if ( nodeName === "option" ) { + dest.defaultSelected = dest.selected = src.defaultSelected; + + // IE6-8 fails to set the defaultValue to the correct value when + // cloning other types of input fields + } else if ( nodeName === "input" || nodeName === "textarea" ) { + dest.defaultValue = src.defaultValue; + } +} + +jQuery.each({ + appendTo: "append", + prependTo: "prepend", + insertBefore: "before", + insertAfter: "after", + replaceAll: "replaceWith" +}, function( name, original ) { + jQuery.fn[ name ] = function( selector ) { + var elems, + i = 0, + ret = [], + insert = jQuery( selector ), + last = insert.length - 1; + + for ( ; i <= last; i++ ) { + elems = i === last ? this : this.clone(true); + jQuery( insert[i] )[ original ]( elems ); + + // Modern browsers can apply jQuery collections as arrays, but oldIE needs a .get() + core_push.apply( ret, elems.get() ); + } + + return this.pushStack( ret ); + }; +}); + +function getAll( context, tag ) { + var elems, elem, + i = 0, + found = typeof context.getElementsByTagName !== core_strundefined ? context.getElementsByTagName( tag || "*" ) : + typeof context.querySelectorAll !== core_strundefined ? context.querySelectorAll( tag || "*" ) : + undefined; + + if ( !found ) { + for ( found = [], elems = context.childNodes || context; (elem = elems[i]) != null; i++ ) { + if ( !tag || jQuery.nodeName( elem, tag ) ) { + found.push( elem ); + } else { + jQuery.merge( found, getAll( elem, tag ) ); + } + } + } + + return tag === undefined || tag && jQuery.nodeName( context, tag ) ? + jQuery.merge( [ context ], found ) : + found; +} + +// Used in buildFragment, fixes the defaultChecked property +function fixDefaultChecked( elem ) { + if ( manipulation_rcheckableType.test( elem.type ) ) { + elem.defaultChecked = elem.checked; + } +} + +jQuery.extend({ + clone: function( elem, dataAndEvents, deepDataAndEvents ) { + var destElements, node, clone, i, srcElements, + inPage = jQuery.contains( elem.ownerDocument, elem ); + + if ( jQuery.support.html5Clone || jQuery.isXMLDoc(elem) || !rnoshimcache.test( "<" + elem.nodeName + ">" ) ) { + clone = elem.cloneNode( true ); + + // IE<=8 does not properly clone detached, unknown element nodes + } else { + fragmentDiv.innerHTML = elem.outerHTML; + fragmentDiv.removeChild( clone = fragmentDiv.firstChild ); + } + + if ( (!jQuery.support.noCloneEvent || !jQuery.support.noCloneChecked) && + (elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) { + + // We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2 + destElements = getAll( clone ); + srcElements = getAll( elem ); + + // Fix all IE cloning issues + for ( i = 0; (node = srcElements[i]) != null; ++i ) { + // Ensure that the destination node is not null; Fixes #9587 + if ( destElements[i] ) { + fixCloneNodeIssues( node, destElements[i] ); + } + } + } + + // Copy the events from the original to the clone + if ( dataAndEvents ) { + if ( deepDataAndEvents ) { + srcElements = srcElements || getAll( elem ); + destElements = destElements || getAll( clone ); + + for ( i = 0; (node = srcElements[i]) != null; i++ ) { + cloneCopyEvent( node, destElements[i] ); + } + } else { + cloneCopyEvent( elem, clone ); + } + } + + // Preserve script evaluation history + destElements = getAll( clone, "script" ); + if ( destElements.length > 0 ) { + setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); + } + + destElements = srcElements = node = null; + + // Return the cloned set + return clone; + }, + + buildFragment: function( elems, context, scripts, selection ) { + var j, elem, contains, + tmp, tag, tbody, wrap, + l = elems.length, + + // Ensure a safe fragment + safe = createSafeFragment( context ), + + nodes = [], + i = 0; + + for ( ; i < l; i++ ) { + elem = elems[ i ]; + + if ( elem || elem === 0 ) { + + // Add nodes directly + if ( jQuery.type( elem ) === "object" ) { + jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); + + // Convert non-html into a text node + } else if ( !rhtml.test( elem ) ) { + nodes.push( context.createTextNode( elem ) ); + + // Convert html into DOM nodes + } else { + tmp = tmp || safe.appendChild( context.createElement("div") ); + + // Deserialize a standard representation + tag = ( rtagName.exec( elem ) || ["", ""] )[1].toLowerCase(); + wrap = wrapMap[ tag ] || wrapMap._default; + + tmp.innerHTML = wrap[1] + elem.replace( rxhtmlTag, "<$1>" ) + wrap[2]; + + // Descend through wrappers to the right content + j = wrap[0]; + while ( j-- ) { + tmp = tmp.lastChild; + } + + // Manually add leading whitespace removed by IE + if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) { + nodes.push( context.createTextNode( rleadingWhitespace.exec( elem )[0] ) ); + } + + // Remove IE's autoinserted from table fragments + if ( !jQuery.support.tbody ) { + + // String was a , *may* have spurious + elem = tag === "table" && !rtbody.test( elem ) ? + tmp.firstChild : + + // String was a bare or + wrap[1] === "
" && !rtbody.test( elem ) ? + tmp : + 0; + + j = elem && elem.childNodes.length; + while ( j-- ) { + if ( jQuery.nodeName( (tbody = elem.childNodes[j]), "tbody" ) && !tbody.childNodes.length ) { + elem.removeChild( tbody ); + } + } + } + + jQuery.merge( nodes, tmp.childNodes ); + + // Fix #12392 for WebKit and IE > 9 + tmp.textContent = ""; + + // Fix #12392 for oldIE + while ( tmp.firstChild ) { + tmp.removeChild( tmp.firstChild ); + } + + // Remember the top-level container for proper cleanup + tmp = safe.lastChild; + } + } + } + + // Fix #11356: Clear elements from fragment + if ( tmp ) { + safe.removeChild( tmp ); + } + + // Reset defaultChecked for any radios and checkboxes + // about to be appended to the DOM in IE 6/7 (#8060) + if ( !jQuery.support.appendChecked ) { + jQuery.grep( getAll( nodes, "input" ), fixDefaultChecked ); + } + + i = 0; + while ( (elem = nodes[ i++ ]) ) { + + // #4087 - If origin and destination elements are the same, and this is + // that element, do not do anything + if ( selection && jQuery.inArray( elem, selection ) !== -1 ) { + continue; + } + + contains = jQuery.contains( elem.ownerDocument, elem ); + + // Append to fragment + tmp = getAll( safe.appendChild( elem ), "script" ); + + // Preserve script evaluation history + if ( contains ) { + setGlobalEval( tmp ); + } + + // Capture executables + if ( scripts ) { + j = 0; + while ( (elem = tmp[ j++ ]) ) { + if ( rscriptType.test( elem.type || "" ) ) { + scripts.push( elem ); + } + } + } + } + + tmp = null; + + return safe; + }, + + cleanData: function( elems, /* internal */ acceptData ) { + var elem, type, id, data, + i = 0, + internalKey = jQuery.expando, + cache = jQuery.cache, + deleteExpando = jQuery.support.deleteExpando, + special = jQuery.event.special; + + for ( ; (elem = elems[i]) != null; i++ ) { + + if ( acceptData || jQuery.acceptData( elem ) ) { + + id = elem[ internalKey ]; + data = id && cache[ id ]; + + if ( data ) { + if ( data.events ) { + for ( type in data.events ) { + if ( special[ type ] ) { + jQuery.event.remove( elem, type ); + + // This is a shortcut to avoid jQuery.event.remove's overhead + } else { + jQuery.removeEvent( elem, type, data.handle ); + } + } + } + + // Remove cache only if it was not already removed by jQuery.event.remove + if ( cache[ id ] ) { + + delete cache[ id ]; + + // IE does not allow us to delete expando properties from nodes, + // nor does it have a removeAttribute function on Document nodes; + // we must handle all of these cases + if ( deleteExpando ) { + delete elem[ internalKey ]; + + } else if ( typeof elem.removeAttribute !== core_strundefined ) { + elem.removeAttribute( internalKey ); + + } else { + elem[ internalKey ] = null; + } + + core_deletedIds.push( id ); + } + } + } + } + }, + + _evalUrl: function( url ) { + return jQuery.ajax({ + url: url, + type: "GET", + dataType: "script", + async: false, + global: false, + "throws": true + }); + } +}); +jQuery.fn.extend({ + wrapAll: function( html ) { + if ( jQuery.isFunction( html ) ) { + return this.each(function(i) { + jQuery(this).wrapAll( html.call(this, i) ); + }); + } + + if ( this[0] ) { + // The elements to wrap the target around + var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true); + + if ( this[0].parentNode ) { + wrap.insertBefore( this[0] ); + } + + wrap.map(function() { + var elem = this; + + while ( elem.firstChild && elem.firstChild.nodeType === 1 ) { + elem = elem.firstChild; + } + + return elem; + }).append( this ); + } + + return this; + }, + + wrapInner: function( html ) { + if ( jQuery.isFunction( html ) ) { + return this.each(function(i) { + jQuery(this).wrapInner( html.call(this, i) ); + }); + } + + return this.each(function() { + var self = jQuery( this ), + contents = self.contents(); + + if ( contents.length ) { + contents.wrapAll( html ); + + } else { + self.append( html ); + } + }); + }, + + wrap: function( html ) { + var isFunction = jQuery.isFunction( html ); + + return this.each(function(i) { + jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html ); + }); + }, + + unwrap: function() { + return this.parent().each(function() { + if ( !jQuery.nodeName( this, "body" ) ) { + jQuery( this ).replaceWith( this.childNodes ); + } + }).end(); + } +}); +var iframe, getStyles, curCSS, + ralpha = /alpha\([^)]*\)/i, + ropacity = /opacity\s*=\s*([^)]*)/, + rposition = /^(top|right|bottom|left)$/, + // swappable if display is none or starts with table except "table", "table-cell", or "table-caption" + // see here for display values: https://developer.mozilla.org/en-US/docs/CSS/display + rdisplayswap = /^(none|table(?!-c[ea]).+)/, + rmargin = /^margin/, + rnumsplit = new RegExp( "^(" + core_pnum + ")(.*)$", "i" ), + rnumnonpx = new RegExp( "^(" + core_pnum + ")(?!px)[a-z%]+$", "i" ), + rrelNum = new RegExp( "^([+-])=(" + core_pnum + ")", "i" ), + elemdisplay = { BODY: "block" }, + + cssShow = { position: "absolute", visibility: "hidden", display: "block" }, + cssNormalTransform = { + letterSpacing: 0, + fontWeight: 400 + }, + + cssExpand = [ "Top", "Right", "Bottom", "Left" ], + cssPrefixes = [ "Webkit", "O", "Moz", "ms" ]; + +// return a css property mapped to a potentially vendor prefixed property +function vendorPropName( style, name ) { + + // shortcut for names that are not vendor prefixed + if ( name in style ) { + return name; + } + + // check for vendor prefixed names + var capName = name.charAt(0).toUpperCase() + name.slice(1), + origName = name, + i = cssPrefixes.length; + + while ( i-- ) { + name = cssPrefixes[ i ] + capName; + if ( name in style ) { + return name; + } + } + + return origName; +} + +function isHidden( elem, el ) { + // isHidden might be called from jQuery#filter function; + // in that case, element will be second argument + elem = el || elem; + return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem ); +} + +function showHide( elements, show ) { + var display, elem, hidden, + values = [], + index = 0, + length = elements.length; + + for ( ; index < length; index++ ) { + elem = elements[ index ]; + if ( !elem.style ) { + continue; + } + + values[ index ] = jQuery._data( elem, "olddisplay" ); + display = elem.style.display; + if ( show ) { + // Reset the inline display of this element to learn if it is + // being hidden by cascaded rules or not + if ( !values[ index ] && display === "none" ) { + elem.style.display = ""; + } + + // Set elements which have been overridden with display: none + // in a stylesheet to whatever the default browser style is + // for such an element + if ( elem.style.display === "" && isHidden( elem ) ) { + values[ index ] = jQuery._data( elem, "olddisplay", css_defaultDisplay(elem.nodeName) ); + } + } else { + + if ( !values[ index ] ) { + hidden = isHidden( elem ); + + if ( display && display !== "none" || !hidden ) { + jQuery._data( elem, "olddisplay", hidden ? display : jQuery.css( elem, "display" ) ); + } + } + } + } + + // Set the display of most of the elements in a second loop + // to avoid the constant reflow + for ( index = 0; index < length; index++ ) { + elem = elements[ index ]; + if ( !elem.style ) { + continue; + } + if ( !show || elem.style.display === "none" || elem.style.display === "" ) { + elem.style.display = show ? values[ index ] || "" : "none"; + } + } + + return elements; +} + +jQuery.fn.extend({ + css: function( name, value ) { + return jQuery.access( this, function( elem, name, value ) { + var len, styles, + map = {}, + i = 0; + + if ( jQuery.isArray( name ) ) { + styles = getStyles( elem ); + len = name.length; + + for ( ; i < len; i++ ) { + map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles ); + } + + return map; + } + + return value !== undefined ? + jQuery.style( elem, name, value ) : + jQuery.css( elem, name ); + }, name, value, arguments.length > 1 ); + }, + show: function() { + return showHide( this, true ); + }, + hide: function() { + return showHide( this ); + }, + toggle: function( state ) { + var bool = typeof state === "boolean"; + + return this.each(function() { + if ( bool ? state : isHidden( this ) ) { + jQuery( this ).show(); + } else { + jQuery( this ).hide(); + } + }); + } +}); + +jQuery.extend({ + // Add in style property hooks for overriding the default + // behavior of getting and setting a style property + cssHooks: { + opacity: { + get: function( elem, computed ) { + if ( computed ) { + // We should always get a number back from opacity + var ret = curCSS( elem, "opacity" ); + return ret === "" ? "1" : ret; + } + } + } + }, + + // Don't automatically add "px" to these possibly-unitless properties + cssNumber: { + "columnCount": true, + "fillOpacity": true, + "fontWeight": true, + "lineHeight": true, + "opacity": true, + "orphans": true, + "widows": true, + "zIndex": true, + "zoom": true + }, + + // Add in properties whose names you wish to fix before + // setting or getting the value + cssProps: { + // normalize float css property + "float": jQuery.support.cssFloat ? "cssFloat" : "styleFloat" + }, + + // Get and set the style property on a DOM Node + style: function( elem, name, value, extra ) { + // Don't set styles on text and comment nodes + if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { + return; + } + + // Make sure that we're working with the right name + var ret, type, hooks, + origName = jQuery.camelCase( name ), + style = elem.style; + + name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( style, origName ) ); + + // gets hook for the prefixed version + // followed by the unprefixed version + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // Check if we're setting a value + if ( value !== undefined ) { + type = typeof value; + + // convert relative number strings (+= or -=) to relative numbers. #7345 + if ( type === "string" && (ret = rrelNum.exec( value )) ) { + value = ( ret[1] + 1 ) * ret[2] + parseFloat( jQuery.css( elem, name ) ); + // Fixes bug #9237 + type = "number"; + } + + // Make sure that NaN and null values aren't set. See: #7116 + if ( value == null || type === "number" && isNaN( value ) ) { + return; + } + + // If a number was passed in, add 'px' to the (except for certain CSS properties) + if ( type === "number" && !jQuery.cssNumber[ origName ] ) { + value += "px"; + } + + // Fixes #8908, it can be done more correctly by specifing setters in cssHooks, + // but it would mean to define eight (for every problematic property) identical functions + if ( !jQuery.support.clearCloneStyle && value === "" && name.indexOf("background") === 0 ) { + style[ name ] = "inherit"; + } + + // If a hook was provided, use that value, otherwise just set the specified value + if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value, extra )) !== undefined ) { + + // Wrapped to prevent IE from throwing errors when 'invalid' values are provided + // Fixes bug #5509 + try { + style[ name ] = value; + } catch(e) {} + } + + } else { + // If a hook was provided get the non-computed value from there + if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) { + return ret; + } + + // Otherwise just get the value from the style object + return style[ name ]; + } + }, + + css: function( elem, name, extra, styles ) { + var num, val, hooks, + origName = jQuery.camelCase( name ); + + // Make sure that we're working with the right name + name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, origName ) ); + + // gets hook for the prefixed version + // followed by the unprefixed version + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // If a hook was provided get the computed value from there + if ( hooks && "get" in hooks ) { + val = hooks.get( elem, true, extra ); + } + + // Otherwise, if a way to get the computed value exists, use that + if ( val === undefined ) { + val = curCSS( elem, name, styles ); + } + + //convert "normal" to computed value + if ( val === "normal" && name in cssNormalTransform ) { + val = cssNormalTransform[ name ]; + } + + // Return, converting to number if forced or a qualifier was provided and val looks numeric + if ( extra === "" || extra ) { + num = parseFloat( val ); + return extra === true || jQuery.isNumeric( num ) ? num || 0 : val; + } + return val; + } +}); + +// NOTE: we've included the "window" in window.getComputedStyle +// because jsdom on node.js will break without it. +if ( window.getComputedStyle ) { + getStyles = function( elem ) { + return window.getComputedStyle( elem, null ); + }; + + curCSS = function( elem, name, _computed ) { + var width, minWidth, maxWidth, + computed = _computed || getStyles( elem ), + + // getPropertyValue is only needed for .css('filter') in IE9, see #12537 + ret = computed ? computed.getPropertyValue( name ) || computed[ name ] : undefined, + style = elem.style; + + if ( computed ) { + + if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) { + ret = jQuery.style( elem, name ); + } + + // A tribute to the "awesome hack by Dean Edwards" + // Chrome < 17 and Safari 5.0 uses "computed value" instead of "used value" for margin-right + // Safari 5.1.7 (at least) returns percentage for a larger set of values, but width seems to be reliably pixels + // this is against the CSSOM draft spec: http://dev.w3.org/csswg/cssom/#resolved-values + if ( rnumnonpx.test( ret ) && rmargin.test( name ) ) { + + // Remember the original values + width = style.width; + minWidth = style.minWidth; + maxWidth = style.maxWidth; + + // Put in the new values to get a computed value out + style.minWidth = style.maxWidth = style.width = ret; + ret = computed.width; + + // Revert the changed values + style.width = width; + style.minWidth = minWidth; + style.maxWidth = maxWidth; + } + } + + return ret; + }; +} else if ( document.documentElement.currentStyle ) { + getStyles = function( elem ) { + return elem.currentStyle; + }; + + curCSS = function( elem, name, _computed ) { + var left, rs, rsLeft, + computed = _computed || getStyles( elem ), + ret = computed ? computed[ name ] : undefined, + style = elem.style; + + // Avoid setting ret to empty string here + // so we don't default to auto + if ( ret == null && style && style[ name ] ) { + ret = style[ name ]; + } + + // From the awesome hack by Dean Edwards + // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291 + + // If we're not dealing with a regular pixel number + // but a number that has a weird ending, we need to convert it to pixels + // but not position css attributes, as those are proportional to the parent element instead + // and we can't measure the parent instead because it might trigger a "stacking dolls" problem + if ( rnumnonpx.test( ret ) && !rposition.test( name ) ) { + + // Remember the original values + left = style.left; + rs = elem.runtimeStyle; + rsLeft = rs && rs.left; + + // Put in the new values to get a computed value out + if ( rsLeft ) { + rs.left = elem.currentStyle.left; + } + style.left = name === "fontSize" ? "1em" : ret; + ret = style.pixelLeft + "px"; + + // Revert the changed values + style.left = left; + if ( rsLeft ) { + rs.left = rsLeft; + } + } + + return ret === "" ? "auto" : ret; + }; +} + +function setPositiveNumber( elem, value, subtract ) { + var matches = rnumsplit.exec( value ); + return matches ? + // Guard against undefined "subtract", e.g., when used as in cssHooks + Math.max( 0, matches[ 1 ] - ( subtract || 0 ) ) + ( matches[ 2 ] || "px" ) : + value; +} + +function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) { + var i = extra === ( isBorderBox ? "border" : "content" ) ? + // If we already have the right measurement, avoid augmentation + 4 : + // Otherwise initialize for horizontal or vertical properties + name === "width" ? 1 : 0, + + val = 0; + + for ( ; i < 4; i += 2 ) { + // both box models exclude margin, so add it if we want it + if ( extra === "margin" ) { + val += jQuery.css( elem, extra + cssExpand[ i ], true, styles ); + } + + if ( isBorderBox ) { + // border-box includes padding, so remove it if we want content + if ( extra === "content" ) { + val -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + } + + // at this point, extra isn't border nor margin, so remove border + if ( extra !== "margin" ) { + val -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + } else { + // at this point, extra isn't content, so add padding + val += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + + // at this point, extra isn't content nor padding, so add border + if ( extra !== "padding" ) { + val += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + } + } + + return val; +} + +function getWidthOrHeight( elem, name, extra ) { + + // Start with offset property, which is equivalent to the border-box value + var valueIsBorderBox = true, + val = name === "width" ? elem.offsetWidth : elem.offsetHeight, + styles = getStyles( elem ), + isBorderBox = jQuery.support.boxSizing && jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; + + // some non-html elements return undefined for offsetWidth, so check for null/undefined + // svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285 + // MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668 + if ( val <= 0 || val == null ) { + // Fall back to computed then uncomputed css if necessary + val = curCSS( elem, name, styles ); + if ( val < 0 || val == null ) { + val = elem.style[ name ]; + } + + // Computed unit is not pixels. Stop here and return. + if ( rnumnonpx.test(val) ) { + return val; + } + + // we need the check for style in case a browser which returns unreliable values + // for getComputedStyle silently falls back to the reliable elem.style + valueIsBorderBox = isBorderBox && ( jQuery.support.boxSizingReliable || val === elem.style[ name ] ); + + // Normalize "", auto, and prepare for extra + val = parseFloat( val ) || 0; + } + + // use the active box-sizing model to add/subtract irrelevant styles + return ( val + + augmentWidthOrHeight( + elem, + name, + extra || ( isBorderBox ? "border" : "content" ), + valueIsBorderBox, + styles + ) + ) + "px"; +} + +// Try to determine the default display value of an element +function css_defaultDisplay( nodeName ) { + var doc = document, + display = elemdisplay[ nodeName ]; + + if ( !display ) { + display = actualDisplay( nodeName, doc ); + + // If the simple way fails, read from inside an iframe + if ( display === "none" || !display ) { + // Use the already-created iframe if possible + iframe = ( iframe || + jQuery("