diff options
42 files changed, 735 insertions, 224 deletions
diff --git a/.htaccess b/.htaccess index 35c478860d5..392b9b41559 100644 --- a/.htaccess +++ b/.htaccess @@ -28,7 +28,7 @@ php_value mbstring.func_overload 0 php_value always_populate_raw_post_data -1 php_value default_charset 'UTF-8' - php_value output_buffering off + php_value output_buffering 0 <IfModule mod_env.c> SetEnv htaccessWorking true </IfModule> diff --git a/.user.ini b/.user.ini index 66bf6484fe2..68ef3e8672c 100644 --- a/.user.ini +++ b/.user.ini @@ -4,4 +4,4 @@ memory_limit=512M mbstring.func_overload=0 always_populate_raw_post_data=-1 default_charset='UTF-8' -output_buffering=off
\ No newline at end of file +output_buffering=0 diff --git a/3rdparty b/3rdparty -Subproject 5ce8de19b67296cc03957d4483ae8ddacc1d4f8 +Subproject 1914e923a4589e619b930e1ca587041d6fd3a1f diff --git a/apps/files/js/detailsview.js b/apps/files/js/detailsview.js index bad4be4ceef..f04adcf1292 100644 --- a/apps/files/js/detailsview.js +++ b/apps/files/js/detailsview.js @@ -140,16 +140,14 @@ } return orderA - orderB; }); - if (this._tabViews.length > 1) { - // only render headers if there is more than one available - templateVars.tabHeaders = _.map(this._tabViews, function(tabView, i) { - return { - tabId: tabView.id, - tabIndex: i, - label: tabView.getLabel() - }; - }); - } + + templateVars.tabHeaders = _.map(this._tabViews, function(tabView, i) { + return { + tabId: tabView.id, + tabIndex: i, + label: tabView.getLabel() + }; + }); this.$el.html(this.template(templateVars)); @@ -166,6 +164,8 @@ this.selectTab(this._currentTabId); + this._updateTabVisibilities(); + this._dirty = false; }, @@ -224,6 +224,8 @@ if (this._dirty) { this.render(); + } else { + this._updateTabVisibilities(); } if (this._currentTabId) { @@ -241,6 +243,37 @@ }, /** + * Update tab headers based on the current model + */ + _updateTabVisibilities: function() { + // update tab header visibilities + var self = this; + var deselect = false; + var countVisible = 0; + var $tabHeaders = this.$el.find('.tabHeaders li'); + _.each(this._tabViews, function(tabView) { + var isVisible = tabView.canDisplay(self.model); + if (isVisible) { + countVisible += 1; + } + if (!isVisible && self._currentTabId === tabView.id) { + deselect = true; + } + $tabHeaders.filterAttr('data-tabid', tabView.id).toggleClass('hidden', !isVisible); + }); + + // hide the whole container if there is only one tab + this.$el.find('.tabHeaders').toggleClass('hidden', countVisible <= 1); + + if (deselect) { + // select the first visible tab instead + var visibleTabId = this.$el.find('.tabHeader:not(.hidden):first').attr('data-tabid'); + this.selectTab(visibleTabId); + } + + }, + + /** * Returns the file info. * * @return {OCA.Files.FileInfoModel} file info diff --git a/apps/files/js/detailtabview.js b/apps/files/js/detailtabview.js index d885e47b15e..0bd34a88188 100644 --- a/apps/files/js/detailtabview.js +++ b/apps/files/js/detailtabview.js @@ -95,6 +95,17 @@ */ nextPage: function() { // load the next page, if applicable + }, + + /** + * Returns whether the current tab is able to display + * the given file info, for example based on mime type. + * + * @param {OCA.Files.FileInfoModel} fileInfo file info model + * @return {bool} whether to display this tab + */ + canDisplay: function(fileInfo) { + return true; } }); DetailTabView._TAB_COUNT = 0; diff --git a/apps/files/js/file-upload.js b/apps/files/js/file-upload.js index 17f0f777169..03330ad7c5d 100644 --- a/apps/files/js/file-upload.js +++ b/apps/files/js/file-upload.js @@ -18,7 +18,7 @@ * - TODO music upload button */ -/* global Files, FileList, jQuery, oc_requesttoken, humanFileSize, getUniqueName */ +/* global jQuery, oc_requesttoken, humanFileSize */ /** * Function that will allow us to know if Ajax uploads are supported @@ -139,6 +139,9 @@ OC.Upload = { if (data.data) { data.data.append('resolution', 'replace'); } else { + if (!data.formData) { + data.formData = []; + } data.formData.push({name:'resolution', value:'replace'}); //hack for ie8 } data.submit(); @@ -152,6 +155,9 @@ OC.Upload = { if (data.data) { data.data.append('resolution', 'autorename'); } else { + if (!data.formData) { + data.formData = []; + } data.formData.push({name:'resolution', value:'autorename'}); //hack for ie8 } data.submit(); @@ -164,8 +170,9 @@ OC.Upload = { } }, /** - * TODO checks the list of existing files prior to uploading and shows a simple dialog to choose + * checks the list of existing files prior to uploading and shows a simple dialog to choose * skip all, replace all or choose which files to keep + * * @param {array} selection of files to upload * @param {object} callbacks - object with several callback methods * @param {function} callbacks.onNoConflicts @@ -175,14 +182,34 @@ OC.Upload = { * @param {function} callbacks.onCancel */ checkExistingFiles: function (selection, callbacks) { - /* - $.each(selection.uploads, function(i, upload) { - var $row = OCA.Files.App.fileList.findFileEl(upload.files[0].name); - if ($row) { - // TODO check filelist before uploading and show dialog on conflicts, use callbacks + var fileList = OCA.Files.App.fileList; + var conflicts = []; + // only keep non-conflicting uploads + selection.uploads = _.filter(selection.uploads, function(upload) { + var fileInfo = fileList.findFile(upload.files[0].name); + if (fileInfo) { + conflicts.push([ + // original + _.extend(fileInfo, { + directory: fileInfo.directory || fileInfo.path || fileList.getCurrentDirectory() + }), + // replacement (File object) + upload + ]); + return false; } + return true; }); - */ + if (conflicts.length) { + _.each(conflicts, function(conflictData) { + OC.dialogs.fileexists(conflictData[1], conflictData[0], conflictData[1].files[0], OC.Upload); + }); + } + + // upload non-conflicting files + // note: when reaching the server they might still meet conflicts + // if the folder was concurrently modified, these will get added + // to the already visible dialog, if applicable callbacks.onNoConflicts(selection); }, @@ -368,18 +395,18 @@ 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: data.targetDir || FileList.getCurrentDirectory(), - file_directory: fileDirectory - }; + if (!data.formData) { + data.formData = []; + } + + var fileDirectory = ''; + if(typeof data.files[0].relativePath !== 'undefined') { + fileDirectory = data.files[0].relativePath; } + // FIXME: prevent re-adding the same + data.formData.push({name: 'requesttoken', value: oc_requesttoken}); + data.formData.push({name: 'dir', value: data.targetDir || FileList.getCurrentDirectory()}); + data.formData.push({name: 'file_directory', value: fileDirectory}); }, fail: function(e, data) { OC.Upload.log('fail', e, data); diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index e4a7aadd600..b4cc71e2d3b 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -239,13 +239,6 @@ this.updateSearch(); - this.$el.on('click', function(event) { - var $target = $(event.target); - // click outside file row ? - if (!$target.closest('tbody').length && !$target.closest('#app-sidebar').length) { - self._updateDetailsView(null); - } - }); this.$fileList.on('click','td.filename>a.name', _.bind(this._onClickFile, this)); this.$fileList.on('change', 'td.filename>.selectCheckBox', _.bind(this._onClickFileCheckbox, this)); this.$el.on('urlChanged', _.bind(this._onUrlChanged, this)); @@ -278,6 +271,9 @@ if (this._newButton) { this._newButton.remove(); } + if (this._detailsView) { + this._detailsView.remove(); + } // TODO: also unregister other event handlers this.fileActions.off('registerAction', this._onFileActionsUpdated); this.fileActions.off('setDefault', this._onFileActionsUpdated); @@ -307,7 +303,6 @@ permissions: OC.PERMISSION_READ, actionHandler: function(fileName, context) { self._updateDetailsView(fileName); - OC.Apps.showAppSidebar(self._detailsView.$el); } }); } @@ -408,9 +403,14 @@ this._currentFileModel.off(); } this._currentFileModel = null; + OC.Apps.hideAppSidebar(this._detailsView.$el); return; } + if (this._detailsView.$el.hasClass('disappear')) { + OC.Apps.showAppSidebar(this._detailsView.$el); + } + var $tr = this.findFileEl(fileName); var model = this.getModelForFile($tr); @@ -453,10 +453,10 @@ * Selected/deselects the given file element and updated * the internal selection cache. * - * @param $tr single file row element - * @param state true to select, false to deselect + * @param {Object} $tr single file row element + * @param {bool} state true to select, false to deselect */ - _selectFileEl: function($tr, state) { + _selectFileEl: function($tr, state, showDetailsView) { var $checkbox = $tr.find('td.filename>.selectCheckBox'); var oldData = !!this._selectedFiles[$tr.data('id')]; var data; @@ -475,11 +475,8 @@ delete this._selectedFiles[$tr.data('id')]; this._selectionSummary.remove(data); } - if (this._selectionSummary.getTotal() === 1) { + if (this._detailsView && this._selectionSummary.getTotal() === 1 && !this._detailsView.$el.hasClass('disappear')) { this._updateDetailsView(_.values(this._selectedFiles)[0].name); - } else { - // show nothing when multiple files are selected - this._updateDetailsView(null); } this.$el.find('.select-all').prop('checked', this._selectionSummary.getTotal() === this.files.length); }, @@ -489,6 +486,9 @@ */ _onClickFile: function(event) { var $tr = $(event.target).closest('tr'); + if ($tr.hasClass('dragging')) { + return; + } if (this._allowSelection && (event.ctrlKey || event.shiftKey)) { event.preventDefault(); if (event.shiftKey) { @@ -552,9 +552,13 @@ */ _onClickFileCheckbox: function(e) { var $tr = $(e.target).closest('tr'); - this._selectFileEl($tr, !$tr.hasClass('selected')); + var state = !$tr.hasClass('selected'); + this._selectFileEl($tr, state); this._lastChecked = $tr; this.updateSelectionSummary(); + if (state) { + this._updateDetailsView($tr.attr('data-file')); + } }, /** @@ -719,8 +723,23 @@ return true; }, /** - * Returns the tr element for a given file name - * @param fileName file name + * Returns the file info for the given file name from the internal collection. + * + * @param {string} fileName file name + * @return {OCA.Files.FileInfo} file info or null if it was not found + * + * @since 8.2 + */ + findFile: function(fileName) { + return _.find(this.files, function(aFile) { + return (aFile.name === fileName); + }) || null; + }, + /** + * Returns the tr element for a given file name, but only if it was already rendered. + * + * @param {string} fileName file name + * @return {Object} jQuery object of the matching row */ findFileEl: function(fileName){ // use filterAttr to avoid escaping issues @@ -1305,8 +1324,10 @@ sortdirection: this._sortDirection } }); - // close sidebar - this._updateDetailsView(null); + if (this._detailsView) { + // close sidebar + this._updateDetailsView(null); + } var callBack = this.reloadCallback.bind(this); return this._reloadCall.then(callBack, callBack); }, @@ -1513,11 +1534,12 @@ remove: function(name, options){ options = options || {}; var fileEl = this.findFileEl(name); + var fileId = fileEl.data('id'); var index = fileEl.index(); if (!fileEl.length) { return null; } - if (this._selectedFiles[fileEl.data('id')]) { + if (this._selectedFiles[fileId]) { // remove from selection first this._selectFileEl(fileEl, false); this.updateSelectionSummary(); @@ -1527,6 +1549,14 @@ fileEl.find('td.filename').draggable('destroy'); } this.files.splice(index, 1); + if (this._currentFileModel && this._currentFileModel.get('id') === fileId) { + // Note: in the future we should call destroy() directly on the model + // and the model will take care of the deletion. + // Here we only trigger the event to notify listeners that + // the file was removed. + this._currentFileModel.trigger('destroy'); + this._updateDetailsView(null); + } fileEl.remove(); // TODO: improve performance on batch update this.isEmpty = !this.files.length; @@ -1877,7 +1907,7 @@ * @return {bool} true if the file exists in the list, false otherwise */ inList:function(file) { - return this.findFileEl(file).length; + return this.findFile(file); }, /** diff --git a/apps/files/js/files.js b/apps/files/js/files.js index 4fdc9eb2110..9ab7609cc40 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -356,7 +356,7 @@ var createDragShadow = function(event) { var isDragSelected = $(event.target).parents('tr').find('td input:first').prop('checked'); if (!isDragSelected) { //select dragged file - FileList._selectFileEl($(event.target).parents('tr:first'), true); + FileList._selectFileEl($(event.target).parents('tr:first'), true, false); } // do not show drag shadow for too many files @@ -365,7 +365,7 @@ var createDragShadow = function(event) { if (!isDragSelected && selectedFiles.length === 1) { //revert the selection - FileList._selectFileEl($(event.target).parents('tr:first'), false); + FileList._selectFileEl($(event.target).parents('tr:first'), false, false); } // build dragshadow @@ -413,22 +413,17 @@ var dragOptions={ cursor: 'move', start: function(event, ui){ var $selectedFiles = $('td.filename input:checkbox:checked'); - if($selectedFiles.length > 1){ - $selectedFiles.parents('tr').fadeTo(250, 0.2); - } - else{ - $(this).fadeTo(250, 0.2); + if (!$selectedFiles.length) { + $selectedFiles = $(this); } + $selectedFiles.closest('tr').fadeTo(250, 0.2).addClass('dragging'); }, stop: function(event, ui) { var $selectedFiles = $('td.filename input:checkbox:checked'); - if($selectedFiles.length > 1){ - $selectedFiles.parents('tr').fadeTo(250, 1); - } - else{ - $(this).fadeTo(250, 1); + if (!$selectedFiles.length) { + $selectedFiles = $(this); } - $('#fileList tr td.filename').addClass('ui-draggable'); + $selectedFiles.closest('tr').fadeTo(250, 1).removeClass('dragging'); } }; // sane browsers support using the distance option diff --git a/apps/files/tests/js/detailsviewSpec.js b/apps/files/tests/js/detailsviewSpec.js index f02e419434f..26a16b31530 100644 --- a/apps/files/tests/js/detailsviewSpec.js +++ b/apps/files/tests/js/detailsviewSpec.js @@ -144,14 +144,76 @@ describe('OCA.Files.DetailsView tests', function() { expect(detailsView.$el.find('.tab').eq(0).hasClass('hidden')).toEqual(true); expect(detailsView.$el.find('.tab').eq(1).hasClass('hidden')).toEqual(false); }); - it('does not render tab headers when only one tab exists', function() { - detailsView.remove(); - detailsView = new OCA.Files.DetailsView(); - testView = new OCA.Files.DetailTabView({id: 'test1'}); - detailsView.addTabView(testView); - detailsView.render(); - - expect(detailsView.$el.find('.tabHeader').length).toEqual(0); + describe('tab visibility', function() { + beforeEach(function() { + detailsView.remove(); + detailsView = new OCA.Files.DetailsView(); + }); + it('does not display tab headers when only one tab exists', function() { + testView = new OCA.Files.DetailTabView({id: 'test1'}); + detailsView.addTabView(testView); + detailsView.render(); + + expect(detailsView.$el.find('.tabHeaders').hasClass('hidden')).toEqual(true); + expect(detailsView.$el.find('.tabHeader').length).toEqual(1); + }); + it('does not display tab that do not pass visibility check', function() { + testView = new OCA.Files.DetailTabView({id: 'test1'}); + testView.canDisplay = sinon.stub().returns(false); + testView2 = new OCA.Files.DetailTabView({id: 'test2'}); + var testView3 = new OCA.Files.DetailTabView({id: 'test3'}); + detailsView.addTabView(testView); + detailsView.addTabView(testView2); + detailsView.addTabView(testView3); + + var fileInfo = {id: 5, name: 'test.txt'}; + detailsView.setFileInfo(fileInfo); + + expect(testView.canDisplay.calledOnce).toEqual(true); + expect(testView.canDisplay.calledWith(fileInfo)).toEqual(true); + + expect(detailsView.$el.find('.tabHeaders').hasClass('hidden')).toEqual(false); + expect(detailsView.$el.find('.tabHeader[data-tabid=test1]').hasClass('hidden')).toEqual(true); + expect(detailsView.$el.find('.tabHeader[data-tabid=test2]').hasClass('hidden')).toEqual(false); + expect(detailsView.$el.find('.tabHeader[data-tabid=test3]').hasClass('hidden')).toEqual(false); + }); + it('does not show tab headers if only one header is visible due to visibility check', function() { + testView = new OCA.Files.DetailTabView({id: 'test1'}); + testView.canDisplay = sinon.stub().returns(false); + testView2 = new OCA.Files.DetailTabView({id: 'test2'}); + detailsView.addTabView(testView); + detailsView.addTabView(testView2); + + var fileInfo = {id: 5, name: 'test.txt'}; + detailsView.setFileInfo(fileInfo); + + expect(testView.canDisplay.calledOnce).toEqual(true); + expect(testView.canDisplay.calledWith(fileInfo)).toEqual(true); + + expect(detailsView.$el.find('.tabHeaders').hasClass('hidden')).toEqual(true); + expect(detailsView.$el.find('.tabHeader[data-tabid=test1]').hasClass('hidden')).toEqual(true); + expect(detailsView.$el.find('.tabHeader[data-tabid=test2]').hasClass('hidden')).toEqual(false); + }); + it('deselects the current tab if a model update does not pass the visibility check', function() { + testView = new OCA.Files.DetailTabView({id: 'test1'}); + testView.canDisplay = function(fileInfo) { + return fileInfo.mimetype !== 'httpd/unix-directory'; + }; + testView2 = new OCA.Files.DetailTabView({id: 'test2'}); + detailsView.addTabView(testView); + detailsView.addTabView(testView2); + + var fileInfo = {id: 5, name: 'test.txt', mimetype: 'text/plain'}; + detailsView.setFileInfo(fileInfo); + + expect(detailsView.$el.find('.tabHeader[data-tabid=test1]').hasClass('selected')).toEqual(true); + expect(detailsView.$el.find('.tabHeader[data-tabid=test2]').hasClass('selected')).toEqual(false); + + detailsView.setFileInfo({id: 10, name: 'folder', mimetype: 'httpd/unix-directory'}); + + expect(detailsView.$el.find('.tabHeader[data-tabid=test1]').hasClass('selected')).toEqual(false); + expect(detailsView.$el.find('.tabHeader[data-tabid=test2]').hasClass('selected')).toEqual(true); + }); }); it('sorts by order and then label', function() { detailsView.remove(); diff --git a/apps/files/tests/js/filelistSpec.js b/apps/files/tests/js/filelistSpec.js index c05e7c37214..b3d85cf08fa 100644 --- a/apps/files/tests/js/filelistSpec.js +++ b/apps/files/tests/js/filelistSpec.js @@ -135,6 +135,9 @@ describe('OCA.Files.FileList tests', function() { }); afterEach(function() { testFiles = undefined; + if (fileList) { + fileList.destroy(); + } fileList = undefined; notificationStub.restore(); @@ -1881,8 +1884,9 @@ describe('OCA.Files.FileList tests', function() { describe('Details sidebar', function() { beforeEach(function() { fileList.setFiles(testFiles); + fileList.showDetailsView('Two.jpg'); }); - it('Clicking on a file row will trigger file action if no details view configured', function() { + it('triggers file action when clicking on row if no details view configured', function() { fileList._detailsView = null; var updateDetailsViewStub = sinon.stub(fileList, '_updateDetailsView'); var actionStub = sinon.stub(); @@ -1904,7 +1908,7 @@ describe('OCA.Files.FileList tests', function() { expect(updateDetailsViewStub.notCalled).toEqual(true); updateDetailsViewStub.restore(); }); - it('Clicking on a file row will trigger details sidebar', function() { + it('highlights current file when clicked and updates sidebar', function() { fileList.fileActions.setDefault('text/plain', 'Test'); var $tr = fileList.findFileEl('One.txt'); $tr.find('td.filename>a.name').click(); @@ -1912,14 +1916,34 @@ describe('OCA.Files.FileList tests', function() { expect(fileList._detailsView.getFileInfo().id).toEqual(1); }); - it('Clicking outside to deselect a file row will trigger details sidebar', function() { + it('keeps the last highlighted file when clicking outside', function() { var $tr = fileList.findFileEl('One.txt'); $tr.find('td.filename>a.name').click(); fileList.$el.find('tfoot').click(); - expect($tr.hasClass('highlighted')).toEqual(false); - expect(fileList._detailsView.getFileInfo()).toEqual(null); + expect($tr.hasClass('highlighted')).toEqual(true); + expect(fileList._detailsView.getFileInfo().id).toEqual(1); + }); + it('keeps the last highlighted file when unselecting file using checkbox', function() { + var $tr = fileList.findFileEl('One.txt'); + $tr.find('input:checkbox').click(); + expect($tr.hasClass('highlighted')).toEqual(true); + $tr.find('input:checkbox').click(); + + expect($tr.hasClass('highlighted')).toEqual(true); + expect(fileList._detailsView.getFileInfo().id).toEqual(1); + }); + it('closes sidebar whenever the currently highlighted file was removed from the list', function() { + var $tr = fileList.findFileEl('One.txt'); + $tr.find('td.filename>a.name').click(); + expect($tr.hasClass('highlighted')).toEqual(true); + + expect(fileList._detailsView.getFileInfo().id).toEqual(1); + + expect($('#app-sidebar').hasClass('disappear')).toEqual(false); + fileList.remove('One.txt'); + expect($('#app-sidebar').hasClass('disappear')).toEqual(true); }); it('returns the currently selected model instance when calling getModelForFile', function() { var $tr = fileList.findFileEl('One.txt'); @@ -1935,6 +1959,14 @@ describe('OCA.Files.FileList tests', function() { var model3 = fileList.getModelForFile($tr); expect(model3).toEqual(model1); }); + it('closes the sidebar when switching folders', function() { + var $tr = fileList.findFileEl('One.txt'); + $tr.find('td.filename>a.name').click(); + + expect($('#app-sidebar').hasClass('disappear')).toEqual(false); + fileList.changeDirectory('/another'); + expect($('#app-sidebar').hasClass('disappear')).toEqual(true); + }); }); describe('File actions', function() { it('Clicking on a file name will trigger default action', function() { diff --git a/apps/files_sharing/publicwebdav.php b/apps/files_sharing/publicwebdav.php index 773a15c888e..fbf9d22cf76 100644 --- a/apps/files_sharing/publicwebdav.php +++ b/apps/files_sharing/publicwebdav.php @@ -46,7 +46,8 @@ $serverFactory = new \OC\Connector\Sabre\ServerFactory( $requestUri = \OC::$server->getRequest()->getRequestUri(); $server = $serverFactory->createServer($baseuri, $requestUri, $authBackend, function () use ($authBackend) { - if (OCA\Files_Sharing\Helper::isOutgoingServer2serverShareEnabled() === false) { + $isAjax = (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest'); + if (OCA\Files_Sharing\Helper::isOutgoingServer2serverShareEnabled() === false && !$isAjax) { // this is what is thrown when trying to access a non-existing share throw new \Sabre\DAV\Exception\NotAuthenticated(); } diff --git a/apps/files_versions/js/versionstabview.js b/apps/files_versions/js/versionstabview.js index 6dab8014229..55f24868196 100644 --- a/apps/files_versions/js/versionstabview.js +++ b/apps/files_versions/js/versionstabview.js @@ -189,6 +189,18 @@ this.$el.find('.has-tooltip').tooltip(); this.$versionsContainer = this.$el.find('ul.versions'); this.delegateEvents(); + }, + + /** + * Returns true for files, false for folders. + * + * @return {bool} true for files, false for folders + */ + canDisplay: function(fileInfo) { + if (!fileInfo) { + return false; + } + return !fileInfo.isDirectory(); } }); diff --git a/build/integration/.gitignore b/build/integration/.gitignore new file mode 100644 index 00000000000..18b981bf7ed --- /dev/null +++ b/build/integration/.gitignore @@ -0,0 +1,3 @@ +vendor +output +composer.lock diff --git a/build/integration/composer.json b/build/integration/composer.json new file mode 100644 index 00000000000..98b2f294c7a --- /dev/null +++ b/build/integration/composer.json @@ -0,0 +1,7 @@ +{ + "require-dev": { + "phpunit/phpunit": "~4.6", + "guzzlehttp/guzzle": "~5.0", + "behat/behat": "2.4.*@stable" + } +} diff --git a/build/integration/config/behat.yml b/build/integration/config/behat.yml new file mode 100644 index 00000000000..01ca0d18790 --- /dev/null +++ b/build/integration/config/behat.yml @@ -0,0 +1,17 @@ +default: + paths: + features: ../features + bootstrap: %behat.paths.features%/bootstrap + + context: + parameters: + baseUrl: http://localhost:8080/ocs/ + admin: + - admin + - admin + +ci: + formatter: + name: pretty,junit + parameters: + output_path: null,./output diff --git a/build/integration/features/bootstrap/FeatureContext.php b/build/integration/features/bootstrap/FeatureContext.php new file mode 100644 index 00000000000..b7a04e1ca76 --- /dev/null +++ b/build/integration/features/bootstrap/FeatureContext.php @@ -0,0 +1,142 @@ +<?php + +use Behat\Behat\Context\BehatContext; +use GuzzleHttp\Client; +use GuzzleHttp\Message\ResponseInterface; + +require __DIR__ . '/../../vendor/autoload.php'; + +/** + * Features context. + */ +class FeatureContext extends BehatContext { + + /** @var string */ + private $baseUrl = ''; + + /** @var ResponseInterface */ + private $response = null; + + /** @var string */ + private $currentUser = ''; + + /** @var int */ + private $apiVersion = 1; + + /** + * Initializes context. + * Every scenario gets it's own context object. + * + * @param array $parameters context parameters (set them up through behat.yml) + */ + public function __construct(array $parameters) { + + // Initialize your context here + $this->baseUrl = $parameters['baseUrl']; + $this->adminUser = $parameters['admin']; + + // in case of ci deployment we take the server url from the environment + $testServerUrl = getenv('TEST_SERVER_URL'); + if ($testServerUrl !== false) { + $this->baseUrl = $testServerUrl; + } + } + + /** + * @When /^sending "([^"]*)" to "([^"]*)"$/ + */ + public function sendingTo($verb, $url) { + $this->sendingToWith($verb, $url, null); + } + + /** + * @Then /^the status code should be "([^"]*)"$/ + */ + public function theStatusCodeShouldBe($statusCode) { + PHPUnit_Framework_Assert::assertEquals($statusCode, $this->response->getStatusCode()); + } + + /** + * @Given /^As an "([^"]*)"$/ + */ + public function asAn($user) { + $this->currentUser = $user; + } + + /** + * @Given /^using api version "([^"]*)"$/ + */ + public function usingApiVersion($version) { + $this->apiVersion = $version; + } + + /** + * @Given /^user "([^"]*)" exists$/ + */ + public function userExists($user) { + $fullUrl = $this->baseUrl . "v2.php/cloud/users/$user"; + $client = new Client(); + $options = []; + if ($this->currentUser === 'admin') { + $options['auth'] = $this->adminUser; + } + + $this->response = $client->get($fullUrl, $options); + PHPUnit_Framework_Assert::assertEquals(200, $this->response->getStatusCode()); + } + + /** + * @Given /^user "([^"]*)" does not exist$/ + */ + public function userDoesNotExist($user) { + try { + $this->userExists($user); + } catch (\GuzzleHttp\Exception\ClientException $ex) { + PHPUnit_Framework_Assert::assertEquals(404, $ex->getResponse()->getStatusCode()); + } + } + + /** + * @When /^creating the user "([^"]*)r"$/ + */ + public function creatingTheUser($user) { + $fullUrl = $this->baseUrl . "v2.php/cloud/users/$user"; + $client = new Client(); + $options = []; + if ($this->currentUser === 'admin') { + $options['auth'] = $this->adminUser; + } + + $this->response = $client->post($fullUrl, [ + 'form_params' => [ + 'userid' => $user, + 'password' => '123456' + ] + ]); + PHPUnit_Framework_Assert::assertEquals(200, $this->response->getStatusCode()); + } + + /** + * @When /^sending "([^"]*)" to "([^"]*)" with$/ + * @param \Behat\Gherkin\Node\TableNode|null $formData + */ + public function sendingToWith($verb, $url, $body) { + $fullUrl = $this->baseUrl . "v{$this->apiVersion}.php" . $url; + $client = new Client(); + $options = []; + if ($this->currentUser === 'admin') { + $options['auth'] = $this->adminUser; + } + if ($body instanceof \Behat\Gherkin\Node\TableNode) { + $fd = $body->getRowsHash(); + $options['body'] = $fd; + } + + try { + $this->response = $client->send($client->createRequest($verb, $fullUrl, $options)); + } catch (\GuzzleHttp\Exception\ClientException $ex) { + $this->response = $ex->getResponse(); + } + } + +} diff --git a/build/integration/features/provisioning-v1.feature b/build/integration/features/provisioning-v1.feature new file mode 100644 index 00000000000..9e3d2df50bb --- /dev/null +++ b/build/integration/features/provisioning-v1.feature @@ -0,0 +1,32 @@ +Feature: provisioning + Background: + Given using api version "1" + + Scenario: Getting an not existing user + Given As an "admin" + When sending "GET" to "/cloud/users/test" + Then the status code should be "200" + + Scenario: Listing all users + Given As an "admin" + When sending "GET" to "/cloud/users" + Then the status code should be "200" + + Scenario: Create a user + Given As an "admin" + And user "brand-new-user" does not exist + When sending "POST" to "/cloud/users" with + | userid | brand-new-user | + | password | 123456 | + + Then the status code should be "200" + And user "brand-new-user" exists + + + Scenario: Delete a user + Given As an "admin" + And user "brand-new-user" exists + When sending "POST" to "/cloud/users" with + | userid | brand-new-user | + Then the status code should be "200" + And user "brand-new-user" does not exist diff --git a/build/integration/features/provisioning-v2.feature b/build/integration/features/provisioning-v2.feature new file mode 100644 index 00000000000..72ceed5d6a5 --- /dev/null +++ b/build/integration/features/provisioning-v2.feature @@ -0,0 +1,9 @@ +Feature: provisioning + Background: + Given using api version "2" + + Scenario: Getting an not existing user + Given As an "admin" + When sending "GET" to "/cloud/users/test" + Then the status code should be "404" + diff --git a/build/integration/run.sh b/build/integration/run.sh new file mode 100755 index 00000000000..08f10b86c5f --- /dev/null +++ b/build/integration/run.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +composer install + +# TODO: avoid port collision on jenkins - use $EXECUTOR_NUMBER +if [ -z "$EXECUTOR_NUMBER" ]; then + EXECUTOR_NUMBER=0 +fi +PORT=$((8080 + $EXECUTOR_NUMBER)) +#PORT=8080 +echo $PORT +php -S localhost:$PORT -t ../.. & +PHPPID=$! +echo $PHPPID + +export TEST_SERVER_URL="http://localhost:$PORT/ocs/" +vendor/bin/behat --profile ci + +kill $PHPPID diff --git a/core/ajax/update.php b/core/ajax/update.php index a693deeb9cf..ff18d2bc04b 100644 --- a/core/ajax/update.php +++ b/core/ajax/update.php @@ -88,6 +88,12 @@ if (OC::checkUpgrade(false)) { $eventSource->close(); OC_Config::setValue('maintenance', false); }); + $updater->listen('\OC\Updater', 'setDebugLogLevel', function ($logLevel, $logLevelName) use($eventSource, $l) { + $eventSource->send('success', (string)$l->t('Set log level to debug - current level: "%s"', [ $logLevelName ])); + }); + $updater->listen('\OC\Updater', 'resetLogLevel', function ($logLevel, $logLevelName) use($eventSource, $l) { + $eventSource->send('success', (string)$l->t('Reset log level to "%s"', [ $logLevelName ])); + }); try { $updater->upgrade(); diff --git a/core/command/upgrade.php b/core/command/upgrade.php index 44e0b66c17c..fa160d9a1c0 100644 --- a/core/command/upgrade.php +++ b/core/command/upgrade.php @@ -177,6 +177,12 @@ class Upgrade extends Command { $updater->listen('\OC\Updater', 'failure', function ($message) use($output, $self) { $output->writeln("<error>$message</error>"); }); + $updater->listen('\OC\Updater', 'setDebugLogLevel', function ($logLevel, $logLevelName) use($output) { + $output->writeln("<info>Set log level to debug - current level: '$logLevelName'</info>"); + }); + $updater->listen('\OC\Updater', 'resetLogLevel', function ($logLevel, $logLevelName) use($output) { + $output->writeln("<info>Reset log level to '$logLevelName'</info>"); + }); if(OutputInterface::VERBOSITY_NORMAL < $output->getVerbosity()) { $updater->listen('\OC\Updater', 'repairInfo', function ($message) use($output) { diff --git a/core/js/js.js b/core/js/js.js index b5971bfc579..397fea8e3c5 100644 --- a/core/js/js.js +++ b/core/js/js.js @@ -1773,9 +1773,7 @@ OC.Util.History = { params = OC.parseQueryString(this._decodeQuery(query)); } // else read from query attributes - if (!params) { - params = OC.parseQueryString(this._decodeQuery(location.search)); - } + params = _.extend(params || {}, OC.parseQueryString(this._decodeQuery(location.search))); return params || {}; }, diff --git a/core/js/oc-dialogs.js b/core/js/oc-dialogs.js index 38b91be9d2e..c38250c79c6 100644 --- a/core/js/oc-dialogs.js +++ b/core/js/oc-dialogs.js @@ -342,11 +342,12 @@ var OCdialogs = { var crop = function(img) { var canvas = document.createElement('canvas'), - width = img.width, - height = img.height, - x, y, size; + targetSize = 96, + width = img.width, + height = img.height, + x, y, size; - // calculate the width and height, constraining the proportions + // Calculate the width and height, constraining the proportions if (width > height) { y = 0; x = (width - height) / 2; @@ -356,14 +357,90 @@ var OCdialogs = { } size = Math.min(width, height); - // resize the canvas and draw the image data into it - canvas.width = 64; - canvas.height = 64; + // Set canvas size to the cropped area + canvas.width = size; + canvas.height = size; var ctx = canvas.getContext("2d"); - ctx.drawImage(img, x, y, size, size, 0, 0, 64, 64); + ctx.drawImage(img, x, y, size, size, 0, 0, size, size); + + // Resize the canvas to match the destination (right size uses 96px) + resampleHermite(canvas, size, size, targetSize, targetSize); + return canvas.toDataURL("image/png", 0.7); }; + /** + * Fast image resize/resample using Hermite filter with JavaScript. + * + * @author: ViliusL + * + * @param {*} canvas + * @param {number} W + * @param {number} H + * @param {number} W2 + * @param {number} H2 + */ + var resampleHermite = function (canvas, W, H, W2, H2) { + W2 = Math.round(W2); + H2 = Math.round(H2); + var img = canvas.getContext("2d").getImageData(0, 0, W, H); + var img2 = canvas.getContext("2d").getImageData(0, 0, W2, H2); + var data = img.data; + var data2 = img2.data; + var ratio_w = W / W2; + var ratio_h = H / H2; + var ratio_w_half = Math.ceil(ratio_w / 2); + var ratio_h_half = Math.ceil(ratio_h / 2); + + for (var j = 0; j < H2; j++) { + for (var i = 0; i < W2; i++) { + var x2 = (i + j * W2) * 4; + var weight = 0; + var weights = 0; + var weights_alpha = 0; + var gx_r = 0; + var gx_g = 0; + var gx_b = 0; + var gx_a = 0; + var center_y = (j + 0.5) * ratio_h; + for (var yy = Math.floor(j * ratio_h); yy < (j + 1) * ratio_h; yy++) { + var dy = Math.abs(center_y - (yy + 0.5)) / ratio_h_half; + var center_x = (i + 0.5) * ratio_w; + var w0 = dy * dy; //pre-calc part of w + for (var xx = Math.floor(i * ratio_w); xx < (i + 1) * ratio_w; xx++) { + var dx = Math.abs(center_x - (xx + 0.5)) / ratio_w_half; + var w = Math.sqrt(w0 + dx * dx); + if (w >= -1 && w <= 1) { + //hermite filter + weight = 2 * w * w * w - 3 * w * w + 1; + if (weight > 0) { + dx = 4 * (xx + yy * W); + //alpha + gx_a += weight * data[dx + 3]; + weights_alpha += weight; + //colors + if (data[dx + 3] < 255) + weight = weight * data[dx + 3] / 250; + gx_r += weight * data[dx]; + gx_g += weight * data[dx + 1]; + gx_b += weight * data[dx + 2]; + weights += weight; + } + } + } + } + data2[x2] = gx_r / weights; + data2[x2 + 1] = gx_g / weights; + data2[x2 + 2] = gx_b / weights; + data2[x2 + 3] = gx_a / weights_alpha; + } + } + canvas.getContext("2d").clearRect(0, 0, Math.max(W, W2), Math.max(H, H2)); + canvas.width = W2; + canvas.height = H2; + canvas.getContext("2d").putImageData(img2, 0, 0); + }; + var addConflict = function($conflicts, original, replacement) { var $conflict = $conflicts.find('.template').clone().removeClass('template').addClass('conflict'); diff --git a/core/js/sharedialogview.js b/core/js/sharedialogview.js index 57743118f28..ee31ed33132 100644 --- a/core/js/sharedialogview.js +++ b/core/js/sharedialogview.js @@ -14,7 +14,7 @@ } var TEMPLATE_BASE = - '<div class="resharerInfoView"></div>' + + '<div class="resharerInfoView subView"></div>' + '{{#if isSharingAllowed}}' + '<label for="shareWith" class="hidden-visually">{{shareLabel}}</label>' + '<div class="oneline">' + @@ -23,10 +23,10 @@ '{{{remoteShareInfo}}}' + '</div>' + '{{/if}}' + - '<div class="shareeListView"></div>' + - '<div class="linkShareView"></div>' + - '<div class="expirationView"></div>' - ; + '<div class="shareeListView subView"></div>' + + '<div class="linkShareView subView"></div>' + + '<div class="expirationView subView"></div>' + + '<div class="loading hidden" style="height: 50px"></div>'; var TEMPLATE_REMOTE_SHARE_INFO = '<a target="_blank" class="icon-info svg shareWithRemoteInfo hasTooltip" href="{{docLink}}" ' + @@ -87,6 +87,9 @@ view.render(); }); + this.model.on('request', this._onRequest, this); + this.model.on('sync', this._onEndRequest, this); + var subViewOptions = { model: this.model, configModel: this.configModel @@ -161,6 +164,24 @@ this.model.addShare(s.item.value); }, + _toggleLoading: function(state) { + this._loading = state; + this.$el.find('.subView').toggleClass('hidden', state); + this.$el.find('.loading').toggleClass('hidden', !state); + }, + + _onRequest: function() { + // only show the loading spinner for the first request (for now) + if (!this._loadingOnce) { + this._toggleLoading(true); + this._loadingOnce = true; + } + }, + + _onEndRequest: function() { + this._toggleLoading(false); + }, + render: function() { var baseTemplate = this._getTemplate('base', TEMPLATE_BASE); diff --git a/core/js/shareitemmodel.js b/core/js/shareitemmodel.js index 13396670aae..328d291b778 100644 --- a/core/js/shareitemmodel.js +++ b/core/js/shareitemmodel.js @@ -624,7 +624,9 @@ fetch: function() { var model = this; + this.trigger('request', this); OC.Share.loadItem(this.get('itemType'), this.get('itemSource'), function(data) { + model.trigger('sync', 'GET', this); model.set(model.parse(data)); }); }, diff --git a/core/l10n/es.js b/core/l10n/es.js index 1b026297565..a35f8464325 100644 --- a/core/l10n/es.js +++ b/core/l10n/es.js @@ -182,6 +182,7 @@ OC.L10N.register( "New Password" : "Contraseña nueva", "Reset password" : "Restablecer contraseña", "Searching other places" : "Buscando en otros lugares", + "No search results in other folders" : "Ningún resultado de búsqueda en otras carpetas", "Personal" : "Personal", "Users" : "Usuarios", "Apps" : "Aplicaciones", diff --git a/core/l10n/es.json b/core/l10n/es.json index a4f5ba0cf5c..2a1d8b79a28 100644 --- a/core/l10n/es.json +++ b/core/l10n/es.json @@ -180,6 +180,7 @@ "New Password" : "Contraseña nueva", "Reset password" : "Restablecer contraseña", "Searching other places" : "Buscando en otros lugares", + "No search results in other folders" : "Ningún resultado de búsqueda en otras carpetas", "Personal" : "Personal", "Users" : "Usuarios", "Apps" : "Aplicaciones", diff --git a/lib/base.php b/lib/base.php index 32b90cad1fa..889801f9653 100644 --- a/lib/base.php +++ b/lib/base.php @@ -237,7 +237,7 @@ class OC { // Check if config is writable $configFileWritable = is_writable($configFilePath); if (!$configFileWritable && !OC_Helper::isReadOnlyConfigEnabled() - || !$configFileWritable && \OCP\Util::needUpgrade()) { + || !$configFileWritable && self::checkUpgrade(false)) { if (self::$CLI) { echo $l->t('Cannot write into "config" directory!')."\n"; echo $l->t('This can usually be fixed by giving the webserver write access to the config directory')."\n"; @@ -678,7 +678,7 @@ class OC { */ public static function registerCacheHooks() { //don't try to do this before we are properly setup - if (\OC::$server->getSystemConfig()->getValue('installed', false) && !\OCP\Util::needUpgrade()) { + if (\OC::$server->getSystemConfig()->getValue('installed', false) && !self::checkUpgrade(false)) { // NOTE: This will be replaced to use OCP $userSession = self::$server->getUserSession(); @@ -714,7 +714,7 @@ class OC { */ public static function registerLogRotate() { $systemConfig = \OC::$server->getSystemConfig(); - if ($systemConfig->getValue('installed', false) && $systemConfig->getValue('log_rotate_size', false) && !\OCP\Util::needUpgrade()) { + if ($systemConfig->getValue('installed', false) && $systemConfig->getValue('log_rotate_size', false) && !self::checkUpgrade(false)) { //don't try to do this before we are properly setup //use custom logfile path if defined, otherwise use default of owncloud.log in data directory \OCP\BackgroundJob::registerJob('OC\Log\Rotate', $systemConfig->getValue('logfile', $systemConfig->getValue('datadirectory', OC::$SERVERROOT . '/data') . '/owncloud.log')); @@ -807,8 +807,7 @@ class OC { // Load minimum set of apps if (!self::checkUpgrade(false) - && !$systemConfig->getValue('maintenance', false) - && !\OCP\Util::needUpgrade()) { + && !$systemConfig->getValue('maintenance', false)) { // For logged-in users: Load everything if(OC_User::isLoggedIn()) { OC_App::loadApps(); @@ -821,7 +820,7 @@ class OC { if (!self::$CLI and (!isset($_GET["logout"]) or ($_GET["logout"] !== 'true'))) { try { - if (!$systemConfig->getValue('maintenance', false) && !\OCP\Util::needUpgrade()) { + if (!$systemConfig->getValue('maintenance', false) && !self::checkUpgrade(false)) { OC_App::loadApps(array('filesystem', 'logging')); OC_App::loadApps(); } diff --git a/lib/private/assetic/separatorfilter.php b/lib/private/assetic/separatorfilter.php deleted file mode 100644 index ee6d5c11c20..00000000000 --- a/lib/private/assetic/separatorfilter.php +++ /dev/null @@ -1,57 +0,0 @@ -<?php -/** - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin McCorkell <rmccorkell@karoshi.org.uk> - * - * @copyright Copyright (c) 2015, ownCloud, Inc. - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * - */ - -namespace OC\Assetic; - -use Assetic\Filter\FilterInterface; -use Assetic\Asset\AssetInterface; - -/** - * Inserts a separator between assets to prevent merge failures - * e.g. missing semicolon at the end of a JS file - */ -class SeparatorFilter implements FilterInterface -{ - /** - * @var string - */ - private $separator; - - /** - * Constructor. - * - * @param string $separator Separator to use between assets - */ - public function __construct($separator = ';') - { - $this->separator = $separator; - } - - public function filterLoad(AssetInterface $asset) - { - } - - public function filterDump(AssetInterface $asset) - { - $asset->setContent($asset->getContent() . $this->separator); - } -} diff --git a/lib/private/lock/dblockingprovider.php b/lib/private/lock/dblockingprovider.php index a08357a6024..8f017a713c1 100644 --- a/lib/private/lock/dblockingprovider.php +++ b/lib/private/lock/dblockingprovider.php @@ -102,10 +102,6 @@ class DBLockingProvider extends AbstractLockingProvider { * @throws \OCP\Lock\LockedException */ public function acquireLock($path, $type) { - if ($this->connection->inTransaction()) { - $this->logger->warning("Trying to acquire a lock for '$path' while inside a transition"); - } - $expire = $this->getExpireTime(); if ($type === self::LOCK_SHARED) { $result = $this->initLockField($path,1); diff --git a/lib/private/response.php b/lib/private/response.php index f1a429463f2..2cd1d990e51 100644 --- a/lib/private/response.php +++ b/lib/private/response.php @@ -247,7 +247,7 @@ class OC_Response { . 'script-src \'self\' \'unsafe-eval\'; ' . 'style-src \'self\' \'unsafe-inline\'; ' . 'frame-src *; ' - . 'img-src *; ' + . 'img-src * data: blob:; ' . 'font-src \'self\' data:; ' . 'media-src *; ' . 'connect-src *'; diff --git a/lib/private/templatelayout.php b/lib/private/templatelayout.php index ae3c1093b20..43c83dea815 100644 --- a/lib/private/templatelayout.php +++ b/lib/private/templatelayout.php @@ -38,8 +38,8 @@ use Assetic\AssetWriter; use Assetic\Filter\CssImportFilter; use Assetic\Filter\CssMinFilter; use Assetic\Filter\CssRewriteFilter; -use Assetic\Filter\JSMinFilter; -use OC\Assetic\SeparatorFilter; // waiting on upstream +use Assetic\Filter\JSqueezeFilter; +use Assetic\Filter\SeparatorFilter; /** * Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl> @@ -225,7 +225,7 @@ class OC_TemplateLayout extends OC_Template { ), $root, $file); } return new FileAsset($root . '/' . $file, array( - new JSMinFilter(), + new JSqueezeFilter(), new SeparatorFilter(';') ), $root, $file); }, $jsFiles); diff --git a/lib/private/updater.php b/lib/private/updater.php index b33180c3425..8aa8b0703d7 100644 --- a/lib/private/updater.php +++ b/lib/private/updater.php @@ -32,6 +32,7 @@ namespace OC; +use OC\Core\Command\Log\Manage; use OC\Hooks\BasicEmitter; use OC_App; use OC_Installer; @@ -69,6 +70,14 @@ class Updater extends BasicEmitter { /** @var bool */ private $skip3rdPartyAppsDisable; + private $logLevelNames = [ + 0 => 'Debug', + 1 => 'Info', + 2 => 'Warning', + 3 => 'Error', + 4 => 'Fatal', + ]; + /** * @param HTTPHelper $httpHelper * @param IConfig $config @@ -177,6 +186,10 @@ class Updater extends BasicEmitter { * @return bool true if the operation succeeded, false otherwise */ public function upgrade() { + $logLevel = $this->config->getSystemValue('loglevel', \OCP\Util::WARN); + $this->emit('\OC\Updater', 'setDebugLogLevel', [ $logLevel, $this->logLevelNames[$logLevel] ]); + $this->config->setSystemValue('loglevel', \OCP\Util::DEBUG); + $wasMaintenanceModeEnabled = $this->config->getSystemValue('maintenance', false); if(!$wasMaintenanceModeEnabled) { @@ -208,6 +221,9 @@ class Updater extends BasicEmitter { $this->emit('\OC\Updater', 'maintenanceActive'); } + $this->emit('\OC\Updater', 'resetLogLevel', [ $logLevel, $this->logLevelNames[$logLevel] ]); + $this->config->setSystemValue('loglevel', $logLevel); + return $success; } diff --git a/lib/public/appframework/http/contentsecuritypolicy.php b/lib/public/appframework/http/contentsecuritypolicy.php index ee36f7aac17..07c76f2969c 100644 --- a/lib/public/appframework/http/contentsecuritypolicy.php +++ b/lib/public/appframework/http/contentsecuritypolicy.php @@ -64,6 +64,7 @@ class ContentSecurityPolicy { private $allowedImageDomains = [ '\'self\'', 'data:', + 'blob:', ]; /** @var array Domains to which connections can be done */ private $allowedConnectDomains = [ diff --git a/lib/public/util.php b/lib/public/util.php index 652df5192cf..1ba091d1952 100644 --- a/lib/public/util.php +++ b/lib/public/util.php @@ -654,6 +654,7 @@ class Util { return \OC_Util::isDefaultExpireDateEnforced(); } + protected static $needUpgradeCache = null; /** * Checks whether the current version needs upgrade. @@ -662,6 +663,9 @@ class Util { * @since 7.0.0 */ public static function needUpgrade() { - return \OC_Util::needUpgrade(\OC::$server->getConfig()); + if (!isset(self::$needUpgradeCache)) { + self::$needUpgradeCache=\OC_Util::needUpgrade(\OC::$server->getConfig()); + } + return self::$needUpgradeCache; } } diff --git a/settings/l10n/es_AR.js b/settings/l10n/es_AR.js index fa4a506fab0..122422aac97 100644 --- a/settings/l10n/es_AR.js +++ b/settings/l10n/es_AR.js @@ -94,6 +94,9 @@ OC.L10N.register( "Cheers!" : "¡Saludos!", "Forum" : "Foro", "Get the apps to sync your files" : "Obtené Apps para sincronizar tus archivos", + "Desktop client" : "Cliente de escritorio", + "Android app" : "App para Android", + "iOS app" : "App para iOS", "Show First Run Wizard again" : "Mostrar de nuevo el asistente de primera ejecución", "You have used <strong>%s</strong> of the available <strong>%s</strong>" : "Usás <strong>%s</strong> de los <strong>%s</strong> disponibles", "Password" : "Contraseña", diff --git a/settings/l10n/es_AR.json b/settings/l10n/es_AR.json index e8098e575c5..72e593031b8 100644 --- a/settings/l10n/es_AR.json +++ b/settings/l10n/es_AR.json @@ -92,6 +92,9 @@ "Cheers!" : "¡Saludos!", "Forum" : "Foro", "Get the apps to sync your files" : "Obtené Apps para sincronizar tus archivos", + "Desktop client" : "Cliente de escritorio", + "Android app" : "App para Android", + "iOS app" : "App para iOS", "Show First Run Wizard again" : "Mostrar de nuevo el asistente de primera ejecución", "You have used <strong>%s</strong> of the available <strong>%s</strong>" : "Usás <strong>%s</strong> de los <strong>%s</strong> disponibles", "Password" : "Contraseña", diff --git a/tests/lib/appframework/controller/ControllerTest.php b/tests/lib/appframework/controller/ControllerTest.php index 243014a91a7..c847525c263 100644 --- a/tests/lib/appframework/controller/ControllerTest.php +++ b/tests/lib/appframework/controller/ControllerTest.php @@ -178,7 +178,7 @@ class ControllerTest extends \Test\TestCase { 'test' => 'something', 'Cache-Control' => 'no-cache, must-revalidate', 'Content-Type' => 'application/json; charset=utf-8', - 'Content-Security-Policy' => "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'", + 'Content-Security-Policy' => "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self';connect-src 'self';media-src 'self'", ]; $response = $this->controller->customDataResponse(array('hi')); diff --git a/tests/lib/appframework/http/ContentSecurityPolicyTest.php b/tests/lib/appframework/http/ContentSecurityPolicyTest.php index 082c032a420..6d9c6d7b8d9 100644 --- a/tests/lib/appframework/http/ContentSecurityPolicyTest.php +++ b/tests/lib/appframework/http/ContentSecurityPolicyTest.php @@ -28,19 +28,19 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyDefault() { - $defaultPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; + $defaultPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self';connect-src 'self';media-src 'self'"; $this->assertSame($defaultPolicy, $this->contentSecurityPolicy->buildPolicy()); } public function testGetPolicyScriptDomainValid() { - $expectedPolicy = "default-src 'none';script-src 'self' www.owncloud.com 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' www.owncloud.com 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedScriptDomain('www.owncloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } public function testGetPolicyScriptDomainValidMultiple() { - $expectedPolicy = "default-src 'none';script-src 'self' www.owncloud.com www.owncloud.org 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' www.owncloud.com www.owncloud.org 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedScriptDomain('www.owncloud.com'); $this->contentSecurityPolicy->addAllowedScriptDomain('www.owncloud.org'); @@ -48,7 +48,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyDisallowScriptDomain() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedScriptDomain('www.owncloud.com'); $this->contentSecurityPolicy->disallowScriptDomain('www.owncloud.com'); @@ -56,7 +56,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyDisallowScriptDomainMultiple() { - $expectedPolicy = "default-src 'none';script-src 'self' www.owncloud.com 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' www.owncloud.com 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedScriptDomain('www.owncloud.com'); $this->contentSecurityPolicy->disallowScriptDomain('www.owncloud.org'); @@ -64,7 +64,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyDisallowScriptDomainMultipleStacked() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedScriptDomain('www.owncloud.com'); $this->contentSecurityPolicy->disallowScriptDomain('www.owncloud.org')->disallowScriptDomain('www.owncloud.com'); @@ -72,14 +72,14 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyScriptAllowInline() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-inline' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-inline' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->allowInlineScript(true); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } public function testGetPolicyScriptAllowInlineWithDomain() { - $expectedPolicy = "default-src 'none';script-src 'self' www.owncloud.com 'unsafe-inline' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' www.owncloud.com 'unsafe-inline' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedScriptDomain('www.owncloud.com'); $this->contentSecurityPolicy->allowInlineScript(true); @@ -87,7 +87,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyScriptDisallowInlineAndEval() { - $expectedPolicy = "default-src 'none';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->allowInlineScript(false); $this->contentSecurityPolicy->allowEvalScript(false); @@ -95,14 +95,14 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyStyleDomainValid() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' www.owncloud.com 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' www.owncloud.com 'unsafe-inline';img-src 'self' data: blob:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedStyleDomain('www.owncloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } public function testGetPolicyStyleDomainValidMultiple() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' www.owncloud.com www.owncloud.org 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' www.owncloud.com www.owncloud.org 'unsafe-inline';img-src 'self' data: blob:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedStyleDomain('www.owncloud.com'); $this->contentSecurityPolicy->addAllowedStyleDomain('www.owncloud.org'); @@ -110,7 +110,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyDisallowStyleDomain() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedStyleDomain('www.owncloud.com'); $this->contentSecurityPolicy->disallowStyleDomain('www.owncloud.com'); @@ -118,7 +118,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyDisallowStyleDomainMultiple() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' www.owncloud.com 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' www.owncloud.com 'unsafe-inline';img-src 'self' data: blob:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedStyleDomain('www.owncloud.com'); $this->contentSecurityPolicy->disallowStyleDomain('www.owncloud.org'); @@ -126,7 +126,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyDisallowStyleDomainMultipleStacked() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedStyleDomain('www.owncloud.com'); $this->contentSecurityPolicy->disallowStyleDomain('www.owncloud.org')->disallowStyleDomain('www.owncloud.com'); @@ -134,35 +134,35 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyStyleAllowInline() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->allowInlineStyle(true); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } public function testGetPolicyStyleAllowInlineWithDomain() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' www.owncloud.com 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' www.owncloud.com 'unsafe-inline';img-src 'self' data: blob:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedStyleDomain('www.owncloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } public function testGetPolicyStyleDisallowInline() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self';img-src 'self' data: blob:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->allowInlineStyle(false); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } public function testGetPolicyImageDomainValid() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: www.owncloud.com;font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob: www.owncloud.com;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedImageDomain('www.owncloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } public function testGetPolicyImageDomainValidMultiple() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: www.owncloud.com www.owncloud.org;font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob: www.owncloud.com www.owncloud.org;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedImageDomain('www.owncloud.com'); $this->contentSecurityPolicy->addAllowedImageDomain('www.owncloud.org'); @@ -170,7 +170,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyDisallowImageDomain() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedImageDomain('www.owncloud.com'); $this->contentSecurityPolicy->disallowImageDomain('www.owncloud.com'); @@ -178,7 +178,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyDisallowImageDomainMultiple() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: www.owncloud.com;font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob: www.owncloud.com;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedImageDomain('www.owncloud.com'); $this->contentSecurityPolicy->disallowImageDomain('www.owncloud.org'); @@ -186,7 +186,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyDisallowImageDomainMultipleStakes() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedImageDomain('www.owncloud.com'); $this->contentSecurityPolicy->disallowImageDomain('www.owncloud.org')->disallowImageDomain('www.owncloud.com'); @@ -194,14 +194,14 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyFontDomainValid() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self' www.owncloud.com;connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' www.owncloud.com;connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedFontDomain('www.owncloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } public function testGetPolicyFontDomainValidMultiple() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self' www.owncloud.com www.owncloud.org;connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' www.owncloud.com www.owncloud.org;connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedFontDomain('www.owncloud.com'); $this->contentSecurityPolicy->addAllowedFontDomain('www.owncloud.org'); @@ -209,7 +209,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyDisallowFontDomain() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedFontDomain('www.owncloud.com'); $this->contentSecurityPolicy->disallowFontDomain('www.owncloud.com'); @@ -217,7 +217,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyDisallowFontDomainMultiple() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self' www.owncloud.com;connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' www.owncloud.com;connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedFontDomain('www.owncloud.com'); $this->contentSecurityPolicy->disallowFontDomain('www.owncloud.org'); @@ -225,7 +225,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyDisallowFontDomainMultipleStakes() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedFontDomain('www.owncloud.com'); $this->contentSecurityPolicy->disallowFontDomain('www.owncloud.org')->disallowFontDomain('www.owncloud.com'); @@ -233,14 +233,14 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyConnectDomainValid() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self' www.owncloud.com;media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self';connect-src 'self' www.owncloud.com;media-src 'self'"; $this->contentSecurityPolicy->addAllowedConnectDomain('www.owncloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } public function testGetPolicyConnectDomainValidMultiple() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self' www.owncloud.com www.owncloud.org;media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self';connect-src 'self' www.owncloud.com www.owncloud.org;media-src 'self'"; $this->contentSecurityPolicy->addAllowedConnectDomain('www.owncloud.com'); $this->contentSecurityPolicy->addAllowedConnectDomain('www.owncloud.org'); @@ -248,7 +248,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyDisallowConnectDomain() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedConnectDomain('www.owncloud.com'); $this->contentSecurityPolicy->disallowConnectDomain('www.owncloud.com'); @@ -256,7 +256,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyDisallowConnectDomainMultiple() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self' www.owncloud.com;media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self';connect-src 'self' www.owncloud.com;media-src 'self'"; $this->contentSecurityPolicy->addAllowedConnectDomain('www.owncloud.com'); $this->contentSecurityPolicy->disallowConnectDomain('www.owncloud.org'); @@ -264,7 +264,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyDisallowConnectDomainMultipleStakes() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedConnectDomain('www.owncloud.com'); $this->contentSecurityPolicy->disallowConnectDomain('www.owncloud.org')->disallowConnectDomain('www.owncloud.com'); @@ -272,14 +272,14 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyMediaDomainValid() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self' www.owncloud.com"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self';connect-src 'self';media-src 'self' www.owncloud.com"; $this->contentSecurityPolicy->addAllowedMediaDomain('www.owncloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } public function testGetPolicyMediaDomainValidMultiple() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self' www.owncloud.com www.owncloud.org"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self';connect-src 'self';media-src 'self' www.owncloud.com www.owncloud.org"; $this->contentSecurityPolicy->addAllowedMediaDomain('www.owncloud.com'); $this->contentSecurityPolicy->addAllowedMediaDomain('www.owncloud.org'); @@ -287,7 +287,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyDisallowMediaDomain() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedMediaDomain('www.owncloud.com'); $this->contentSecurityPolicy->disallowMediaDomain('www.owncloud.com'); @@ -295,7 +295,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyDisallowMediaDomainMultiple() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self' www.owncloud.com"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self';connect-src 'self';media-src 'self' www.owncloud.com"; $this->contentSecurityPolicy->addAllowedMediaDomain('www.owncloud.com'); $this->contentSecurityPolicy->disallowMediaDomain('www.owncloud.org'); @@ -303,7 +303,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyDisallowMediaDomainMultipleStakes() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedMediaDomain('www.owncloud.com'); $this->contentSecurityPolicy->disallowMediaDomain('www.owncloud.org')->disallowMediaDomain('www.owncloud.com'); @@ -311,14 +311,14 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyObjectDomainValid() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self';object-src www.owncloud.com"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self';connect-src 'self';media-src 'self';object-src www.owncloud.com"; $this->contentSecurityPolicy->addAllowedObjectDomain('www.owncloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } public function testGetPolicyObjectDomainValidMultiple() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self';object-src www.owncloud.com www.owncloud.org"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self';connect-src 'self';media-src 'self';object-src www.owncloud.com www.owncloud.org"; $this->contentSecurityPolicy->addAllowedObjectDomain('www.owncloud.com'); $this->contentSecurityPolicy->addAllowedObjectDomain('www.owncloud.org'); @@ -326,7 +326,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyDisallowObjectDomain() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedObjectDomain('www.owncloud.com'); $this->contentSecurityPolicy->disallowObjectDomain('www.owncloud.com'); @@ -334,7 +334,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyDisallowObjectDomainMultiple() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self';object-src www.owncloud.com"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self';connect-src 'self';media-src 'self';object-src www.owncloud.com"; $this->contentSecurityPolicy->addAllowedObjectDomain('www.owncloud.com'); $this->contentSecurityPolicy->disallowObjectDomain('www.owncloud.org'); @@ -342,7 +342,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyDisallowObjectDomainMultipleStakes() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedObjectDomain('www.owncloud.com'); $this->contentSecurityPolicy->disallowObjectDomain('www.owncloud.org')->disallowObjectDomain('www.owncloud.com'); @@ -350,14 +350,14 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetAllowedFrameDomain() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self';frame-src www.owncloud.com"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self';connect-src 'self';media-src 'self';frame-src www.owncloud.com"; $this->contentSecurityPolicy->addAllowedFrameDomain('www.owncloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } public function testGetPolicyFrameDomainValidMultiple() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self';frame-src www.owncloud.com www.owncloud.org"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self';connect-src 'self';media-src 'self';frame-src www.owncloud.com www.owncloud.org"; $this->contentSecurityPolicy->addAllowedFrameDomain('www.owncloud.com'); $this->contentSecurityPolicy->addAllowedFrameDomain('www.owncloud.org'); @@ -365,7 +365,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyDisallowFrameDomain() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedFrameDomain('www.owncloud.com'); $this->contentSecurityPolicy->disallowFrameDomain('www.owncloud.com'); @@ -373,7 +373,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyDisallowFrameDomainMultiple() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self';frame-src www.owncloud.com"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self';connect-src 'self';media-src 'self';frame-src www.owncloud.com"; $this->contentSecurityPolicy->addAllowedFrameDomain('www.owncloud.com'); $this->contentSecurityPolicy->disallowFrameDomain('www.owncloud.org'); @@ -381,7 +381,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyDisallowFrameDomainMultipleStakes() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedFrameDomain('www.owncloud.com'); $this->contentSecurityPolicy->disallowFrameDomain('www.owncloud.org')->disallowFrameDomain('www.owncloud.com'); @@ -389,14 +389,14 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetAllowedChildSrcDomain() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self';child-src child.owncloud.com"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self';connect-src 'self';media-src 'self';child-src child.owncloud.com"; $this->contentSecurityPolicy->addAllowedChildSrcDomain('child.owncloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } public function testGetPolicyChildSrcValidMultiple() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self';child-src child.owncloud.com child.owncloud.org"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self';connect-src 'self';media-src 'self';child-src child.owncloud.com child.owncloud.org"; $this->contentSecurityPolicy->addAllowedChildSrcDomain('child.owncloud.com'); $this->contentSecurityPolicy->addAllowedChildSrcDomain('child.owncloud.org'); @@ -404,7 +404,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyDisallowChildSrcDomain() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedChildSrcDomain('www.owncloud.com'); $this->contentSecurityPolicy->disallowChildSrcDomain('www.owncloud.com'); @@ -412,7 +412,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyDisallowChildSrcDomainMultiple() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self';child-src www.owncloud.com"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self';connect-src 'self';media-src 'self';child-src www.owncloud.com"; $this->contentSecurityPolicy->addAllowedChildSrcDomain('www.owncloud.com'); $this->contentSecurityPolicy->disallowChildSrcDomain('www.owncloud.org'); @@ -420,7 +420,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testGetPolicyDisallowChildSrcDomainMultipleStakes() { - $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; + $expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self';connect-src 'self';media-src 'self'"; $this->contentSecurityPolicy->addAllowedChildSrcDomain('www.owncloud.com'); $this->contentSecurityPolicy->disallowChildSrcDomain('www.owncloud.org')->disallowChildSrcDomain('www.owncloud.com'); @@ -428,7 +428,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { } public function testConfigureStacked() { - $expectedPolicy = "default-src 'none';script-src 'self' script.owncloud.org;style-src 'self' style.owncloud.org;img-src 'self' data: img.owncloud.org;font-src 'self' font.owncloud.org;connect-src 'self' connect.owncloud.org;media-src 'self' media.owncloud.org;object-src objects.owncloud.org;frame-src frame.owncloud.org;child-src child.owncloud.org"; + $expectedPolicy = "default-src 'none';script-src 'self' script.owncloud.org;style-src 'self' style.owncloud.org;img-src 'self' data: blob: img.owncloud.org;font-src 'self' font.owncloud.org;connect-src 'self' connect.owncloud.org;media-src 'self' media.owncloud.org;object-src objects.owncloud.org;frame-src frame.owncloud.org;child-src child.owncloud.org"; $this->contentSecurityPolicy->allowInlineStyle(false) ->allowEvalScript(false) diff --git a/tests/lib/appframework/http/DataResponseTest.php b/tests/lib/appframework/http/DataResponseTest.php index 2b7817c28e9..e3d5689d54c 100644 --- a/tests/lib/appframework/http/DataResponseTest.php +++ b/tests/lib/appframework/http/DataResponseTest.php @@ -68,7 +68,7 @@ class DataResponseTest extends \Test\TestCase { $expectedHeaders = [ 'Cache-Control' => 'no-cache, must-revalidate', - 'Content-Security-Policy' => "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'", + 'Content-Security-Policy' => "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self';connect-src 'self';media-src 'self'", ]; $expectedHeaders = array_merge($expectedHeaders, $headers); diff --git a/tests/lib/appframework/http/ResponseTest.php b/tests/lib/appframework/http/ResponseTest.php index 61dd95e5948..f845f02d984 100644 --- a/tests/lib/appframework/http/ResponseTest.php +++ b/tests/lib/appframework/http/ResponseTest.php @@ -58,7 +58,7 @@ class ResponseTest extends \Test\TestCase { $this->childResponse->setHeaders($expected); $headers = $this->childResponse->getHeaders(); - $expected['Content-Security-Policy'] = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data:;font-src 'self';connect-src 'self';media-src 'self'"; + $expected['Content-Security-Policy'] = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self';connect-src 'self';media-src 'self'"; $this->assertEquals($expected, $headers); } diff --git a/tests/lib/util.php b/tests/lib/util.php index b9b8062653e..49579b3b6bd 100644 --- a/tests/lib/util.php +++ b/tests/lib/util.php @@ -406,11 +406,13 @@ class Test_Util extends \Test\TestCase { OC_Config::setValue('version', '7.0.0.0'); \OC::$server->getSession()->set('OC_Version', array(7, 0, 0, 1)); + self::invokePrivate(new \OCP\Util, 'needUpgradeCache', array(null)); $this->assertTrue(\OCP\Util::needUpgrade()); OC_Config::setValue('version', $oldConfigVersion); $oldSessionVersion = \OC::$server->getSession()->set('OC_Version', $oldSessionVersion); + self::invokePrivate(new \OCP\Util, 'needUpgradeCache', array(null)); $this->assertFalse(\OCP\Util::needUpgrade()); } |