aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.htaccess48
-rw-r--r--apps/files/admin.php3
-rw-r--r--apps/files/index.php3
-rw-r--r--apps/files/js/detailsview.js176
-rw-r--r--apps/files/js/detailtabview.js6
-rw-r--r--apps/files/js/tagsplugin.js3
-rw-r--r--apps/files/templates/admin.php2
-rw-r--r--apps/files/templates/test.pngbin0 -> 2302 bytes
-rw-r--r--apps/files/tests/js/detailsviewSpec.js80
-rw-r--r--apps/files_sharing/js/share.js8
-rw-r--r--apps/files_sharing/js/sharetabview.js9
-rw-r--r--apps/files_sharing/lib/capabilities.php1
-rw-r--r--apps/files_sharing/tests/capabilities.php20
-rw-r--r--config/config.sample.php33
-rw-r--r--lib/private/files.php91
-rw-r--r--lib/private/ocsclient.php2
-rw-r--r--lib/private/server.php17
-rw-r--r--lib/public/iservercontainer.php9
-rw-r--r--tests/data/setUploadLimit/htaccess60
-rw-r--r--tests/data/setUploadLimit/user.ini7
-rw-r--r--tests/lib/files.php135
-rw-r--r--tests/settings/controller/userscontrollertest.php18
22 files changed, 565 insertions, 166 deletions
diff --git a/.htaccess b/.htaccess
index 65957a29838..35c478860d5 100644
--- a/.htaccess
+++ b/.htaccess
@@ -22,36 +22,36 @@
</FilesMatch>
</IfModule>
<IfModule mod_php5.c>
-php_value upload_max_filesize 513M
-php_value post_max_size 513M
-php_value memory_limit 512M
-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
-<IfModule mod_env.c>
- SetEnv htaccessWorking true
-</IfModule>
+ php_value upload_max_filesize 513M
+ php_value post_max_size 513M
+ php_value memory_limit 512M
+ 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
+ <IfModule mod_env.c>
+ SetEnv htaccessWorking true
+ </IfModule>
</IfModule>
<IfModule mod_rewrite.c>
-RewriteEngine on
-RewriteRule .* - [env=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
-RewriteRule ^\.well-known/host-meta /public.php?service=host-meta [QSA,L]
-RewriteRule ^\.well-known/host-meta\.json /public.php?service=host-meta-json [QSA,L]
-RewriteRule ^\.well-known/carddav /remote.php/carddav/ [R=301,L]
-RewriteRule ^\.well-known/caldav /remote.php/caldav/ [R=301,L]
-RewriteRule ^apps/calendar/caldav\.php remote.php/caldav/ [QSA,L]
-RewriteRule ^apps/contacts/carddav\.php remote.php/carddav/ [QSA,L]
-RewriteRule ^remote/(.*) remote.php [QSA,L]
-RewriteRule ^(build|tests|config|lib|3rdparty|templates)/.* - [R=404,L]
-RewriteRule ^(\.|autotest|occ|issue|indie|db_|console).* - [R=404,L]
+ RewriteEngine on
+ RewriteRule .* - [env=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
+ RewriteRule ^\.well-known/host-meta /public.php?service=host-meta [QSA,L]
+ RewriteRule ^\.well-known/host-meta\.json /public.php?service=host-meta-json [QSA,L]
+ RewriteRule ^\.well-known/carddav /remote.php/carddav/ [R=301,L]
+ RewriteRule ^\.well-known/caldav /remote.php/caldav/ [R=301,L]
+ RewriteRule ^apps/calendar/caldav\.php remote.php/caldav/ [QSA,L]
+ RewriteRule ^apps/contacts/carddav\.php remote.php/carddav/ [QSA,L]
+ RewriteRule ^remote/(.*) remote.php [QSA,L]
+ RewriteRule ^(build|tests|config|lib|3rdparty|templates)/.* - [R=404,L]
+ RewriteRule ^(\.|autotest|occ|issue|indie|db_|console).* - [R=404,L]
</IfModule>
<IfModule mod_mime.c>
-AddType image/svg+xml svg svgz
-AddEncoding gzip svgz
+ AddType image/svg+xml svg svgz
+ AddEncoding gzip svgz
</IfModule>
<IfModule mod_dir.c>
-DirectoryIndex index.php index.html
+ DirectoryIndex index.php index.html
</IfModule>
AddDefaultCharset utf-8
Options -Indexes
diff --git a/apps/files/admin.php b/apps/files/admin.php
index 70f537d0db9..349c27ff742 100644
--- a/apps/files/admin.php
+++ b/apps/files/admin.php
@@ -42,9 +42,10 @@ if($_POST && OC_Util::isCallRegistered()) {
}
$htaccessWritable=is_writable(OC::$SERVERROOT.'/.htaccess');
+$userIniWritable=is_writable(OC::$SERVERROOT.'/.user.ini');
$tmpl = new OCP\Template( 'files', 'admin' );
-$tmpl->assign( 'uploadChangable', $htaccessWorking and $htaccessWritable );
+$tmpl->assign( 'uploadChangable', ($htaccessWorking and $htaccessWritable) or $userIniWritable );
$tmpl->assign( 'uploadMaxFilesize', $maxUploadFilesize);
// max possible makes only sense on a 32 bit system
$tmpl->assign( 'displayMaxPossibleUploadSize', PHP_INT_SIZE===4);
diff --git a/apps/files/index.php b/apps/files/index.php
index beae585cea4..a73caa50fbe 100644
--- a/apps/files/index.php
+++ b/apps/files/index.php
@@ -143,6 +143,9 @@ OCP\Util::addscript('files', 'fileactionsmenu');
OCP\Util::addscript('files', 'files');
OCP\Util::addscript('files', 'navigation');
OCP\Util::addscript('files', 'keyboardshortcuts');
+
+\OC::$server->getEventDispatcher()->dispatch('OCA\Files::loadAdditionalScripts');
+
$tmpl = new OCP\Template('files', 'index', 'user');
$tmpl->assign('usedSpacePercent', (int)$storageInfo['relative']);
$tmpl->assign('owner', $storageInfo['owner']);
diff --git a/apps/files/js/detailsview.js b/apps/files/js/detailsview.js
index 4df359e4523..a4ebe90cd64 100644
--- a/apps/files/js/detailsview.js
+++ b/apps/files/js/detailsview.js
@@ -9,23 +9,26 @@
*/
(function() {
-
var TEMPLATE =
'<div>' +
- ' <div class="detailFileInfoContainer">' +
- ' </div>' +
- ' <div>' +
- ' <ul class="tabHeaders">' +
- ' </ul>' +
- ' <div class="tabsContainer">' +
- ' </div>' +
- ' </div>' +
- ' <a class="close icon-close" href="#" alt="{{closeLabel}}"></a>' +
+ ' <div class="detailFileInfoContainer">' +
+ ' </div>' +
+ ' <div>' +
+ ' {{#if tabHeaders}}' +
+ ' <ul class="tabHeaders">' +
+ ' {{#each tabHeaders}}' +
+ ' <li class="tabHeader" data-tabid="{{tabId}}" data-tabindex="{{tabIndex}}">' +
+ ' <a href="#">{{label}}</a>' +
+ ' </li>' +
+ ' {{/each}}' +
+ ' </ul>' +
+ ' {{/if}}' +
+ ' <div class="tabsContainer">' +
+ ' </div>' +
+ ' </div>' +
+ ' <a class="close icon-close" href="#" alt="{{closeLabel}}"></a>' +
'</div>';
- var TEMPLATE_TAB_HEADER =
- '<li class="tabHeader {{#if selected}}selected{{/if}}" data-tabid="{{tabId}}" data-tabindex="{{tabIndex}}"><a href="#">{{label}}</a></li>';
-
/**
* @class OCA.Files.DetailsView
* @classdesc
@@ -39,7 +42,6 @@
className: 'detailsView',
_template: null,
- _templateTabHeader: null,
/**
* List of detail tab views
@@ -62,6 +64,11 @@
*/
_currentTabId: null,
+ /**
+ * Dirty flag, whether the view needs to be rerendered
+ */
+ _dirty: false,
+
events: {
'click a.close': '_onClose',
'click .tabHeaders .tabHeader': '_onClickTab'
@@ -74,8 +81,10 @@
this._tabViews = [];
this._detailFileInfoViews = [];
+ this._dirty = true;
+
// uncomment to add some dummy tabs for testing
- // this._addTestTabs();
+ //this._addTestTabs();
},
_onClose: function(event) {
@@ -85,28 +94,21 @@
_onClickTab: function(e) {
var $target = $(e.target);
+ e.preventDefault();
if (!$target.hasClass('tabHeader')) {
$target = $target.closest('.tabHeader');
}
- var tabIndex = $target.attr('data-tabindex');
- var targetTab;
- if (_.isUndefined(tabIndex)) {
+ var tabId = $target.attr('data-tabid');
+ if (_.isUndefined(tabId)) {
return;
}
- this.$el.find('.tabsContainer .tab').addClass('hidden');
- targetTab = this._tabViews[tabIndex];
- targetTab.$el.removeClass('hidden');
-
- this.$el.find('.tabHeaders li').removeClass('selected');
- $target.addClass('selected');
-
- e.preventDefault();
+ this.selectTab(tabId);
},
_addTestTabs: function() {
for (var j = 0; j < 2; j++) {
- var testView = new OCA.Files.DetailTabView('testtab' + j);
+ var testView = new OCA.Files.DetailTabView({id: 'testtab' + j});
testView.index = j;
testView.getLabel = function() { return 'Test tab ' + this.index; };
testView.render = function() {
@@ -119,26 +121,34 @@
}
},
+ template: function(vars) {
+ if (!this._template) {
+ this._template = Handlebars.compile(TEMPLATE);
+ }
+ return this._template(vars);
+ },
+
/**
* Renders this details view
*/
render: function() {
- var self = this;
-
- if (!this._template) {
- this._template = Handlebars.compile(TEMPLATE);
- }
+ var templateVars = {
+ closeLabel: t('files', 'Close')
+ };
- if (!this._templateTabHeader) {
- this._templateTabHeader = Handlebars.compile(TEMPLATE_TAB_HEADER);
+ 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()
+ };
+ });
}
- this.$el.html(this._template({
- closeLabel: t('files', 'Close')
- }));
+ this.$el.html(this.template(templateVars));
- var $tabsContainer = this.$el.find('.tabsContainer');
- var $tabHeadsContainer = this.$el.find('.tabHeaders');
var $detailsContainer = this.$el.find('.detailFileInfoContainer');
// render details
@@ -146,29 +156,58 @@
$detailsContainer.append(detailView.get$());
});
- if (this._tabViews.length > 0) {
- if (!this._currentTab) {
- this._currentTab = this._tabViews[0].id;
- }
-
- // render tabs
- _.each(this._tabViews, function(tabView, i) {
- // hidden by default
- var $el = tabView.get$();
- var isCurrent = (tabView.id === self._currentTab);
- if (!isCurrent) {
- $el.addClass('hidden');
- }
- $tabsContainer.append($el);
+ if (!this._currentTabId && this._tabViews.length > 0) {
+ this._currentTabId = this._tabViews[0].id;
+ }
- $tabHeadsContainer.append(self._templateTabHeader({
- tabId: tabView.id,
- tabIndex: i,
- label: tabView.getLabel(),
- selected: isCurrent
- }));
- });
+ this.selectTab(this._currentTabId);
+
+ this._dirty = false;
+ },
+
+ /**
+ * Selects the given tab by id
+ *
+ * @param {string} tabId tab id
+ */
+ selectTab: function(tabId) {
+ if (!tabId) {
+ return;
+ }
+
+ var tabView = _.find(this._tabViews, function(tab) {
+ return tab.id === tabId;
+ });
+
+ if (!tabView) {
+ console.warn('Details view tab with id "' + tabId + '" not found');
+ return;
}
+
+ this._currentTabId = tabId;
+
+ var $tabsContainer = this.$el.find('.tabsContainer');
+ var $tabEl = $tabsContainer.find('#' + tabId);
+
+ // hide other tabs
+ $tabsContainer.find('.tab').addClass('hidden');
+
+ // tab already rendered ?
+ if (!$tabEl.length) {
+ // render tab
+ $tabsContainer.append(tabView.$el);
+ $tabEl = tabView.$el;
+ }
+
+ // this should trigger tab rendering
+ tabView.setFileInfo(this.model);
+
+ $tabEl.removeClass('hidden');
+
+ // update tab headers
+ var $tabHeaders = this.$el.find('.tabHeaders li');
+ $tabHeaders.removeClass('selected');
+ $tabHeaders.filterAttr('data-tabid', tabView.id).addClass('selected');
},
/**
@@ -179,12 +218,19 @@
setFileInfo: function(fileInfo) {
this.model = fileInfo;
- this.render();
+ if (this._dirty) {
+ this.render();
+ }
- // notify all panels
- _.each(this._tabViews, function(tabView) {
+ if (this._currentTabId) {
+ // only update current tab, others will be updated on-demand
+ var tabId = this._currentTabId;
+ var tabView = _.find(this._tabViews, function(tab) {
+ return tab.id === tabId;
+ });
tabView.setFileInfo(fileInfo);
- });
+ }
+
_.each(this._detailFileInfoViews, function(detailView) {
detailView.setFileInfo(fileInfo);
});
@@ -206,6 +252,7 @@
*/
addTabView: function(tabView) {
this._tabViews.push(tabView);
+ this._dirty = true;
},
/**
@@ -215,6 +262,7 @@
*/
addDetailView: function(detailView) {
this._detailFileInfoViews.push(detailView);
+ this._dirty = true;
}
});
diff --git a/apps/files/js/detailtabview.js b/apps/files/js/detailtabview.js
index b2e02971fb4..67f8b535abd 100644
--- a/apps/files/js/detailtabview.js
+++ b/apps/files/js/detailtabview.js
@@ -71,8 +71,10 @@
* @param {OCA.Files.FileInfoModel} fileInfo file info to set
*/
setFileInfo: function(fileInfo) {
- this.model = fileInfo;
- this.render();
+ if (this.model !== fileInfo) {
+ this.model = fileInfo;
+ this.render();
+ }
},
/**
diff --git a/apps/files/js/tagsplugin.js b/apps/files/js/tagsplugin.js
index d8552c71e45..ed1105a1706 100644
--- a/apps/files/js/tagsplugin.js
+++ b/apps/files/js/tagsplugin.js
@@ -108,6 +108,8 @@
}
toggleStar($actionEl, !isFavorite);
+ context.fileInfoModel.set('tags', tags);
+
self.applyFileTags(
dir + '/' + fileName,
tags,
@@ -124,7 +126,6 @@
toggleStar($actionEl, (newTags.indexOf(OC.TAG_FAVORITE) >= 0));
$file.attr('data-tags', newTags.join('|'));
$file.attr('data-favorite', !isFavorite);
- context.fileInfoModel.set('tags', newTags);
fileInfo.tags = newTags;
});
}
diff --git a/apps/files/templates/admin.php b/apps/files/templates/admin.php
index adf756a12be..822fc779bd3 100644
--- a/apps/files/templates/admin.php
+++ b/apps/files/templates/admin.php
@@ -10,6 +10,8 @@
<br/>
<input type="hidden" value="<?php p($_['requesttoken']); ?>" name="requesttoken" />
<?php if($_['uploadChangable']): ?>
+ <?php p($l->t('With PHP-FPM this value may take up to 5 minutes to take effect after saving.')); ?>
+ <br/>
<input type="submit" name="submitFilesAdminSettings" id="submitFilesAdminSettings"
value="<?php p($l->t( 'Save' )); ?>"/>
<?php else: ?>
diff --git a/apps/files/templates/test.png b/apps/files/templates/test.png
new file mode 100644
index 00000000000..2b90216f797
--- /dev/null
+++ b/apps/files/templates/test.png
Binary files differ
diff --git a/apps/files/tests/js/detailsviewSpec.js b/apps/files/tests/js/detailsviewSpec.js
index 4261aa53c94..852f8b04293 100644
--- a/apps/files/tests/js/detailsviewSpec.js
+++ b/apps/files/tests/js/detailsviewSpec.js
@@ -67,32 +67,75 @@ describe('OCA.Files.DetailsView tests', function() {
var testView, testView2;
beforeEach(function() {
- testView = new OCA.Files.DetailTabView('test1');
- testView2 = new OCA.Files.DetailTabView('test2');
+ testView = new OCA.Files.DetailTabView({id: 'test1'});
+ testView2 = new OCA.Files.DetailTabView({id: 'test2'});
detailsView.addTabView(testView);
detailsView.addTabView(testView2);
detailsView.render();
});
- it('renders registered tabs', function() {
- expect(detailsView.$el.find('.tab').length).toEqual(2);
+ it('initially renders only the selected tab', function() {
+ expect(detailsView.$el.find('.tab').length).toEqual(1);
+ expect(detailsView.$el.find('.tab').attr('id')).toEqual('test1');
});
- it('updates registered tabs when fileinfo is updated', function() {
- var tabRenderStub = sinon.stub(OCA.Files.DetailTabView.prototype, 'render');
- var fileInfo = {id: 5, name: 'test.txt'};
- tabRenderStub.reset();
- detailsView.setFileInfo(fileInfo);
+ it('updates tab model and rerenders on-demand as soon as it gets selected', function() {
+ var tab1RenderStub = sinon.stub(testView, 'render');
+ var tab2RenderStub = sinon.stub(testView2, 'render');
+ var fileInfo1 = new OCA.Files.FileInfoModel({id: 5, name: 'test.txt'});
+ var fileInfo2 = new OCA.Files.FileInfoModel({id: 8, name: 'test2.txt'});
- expect(testView.getFileInfo()).toEqual(fileInfo);
- expect(testView2.getFileInfo()).toEqual(fileInfo);
+ detailsView.setFileInfo(fileInfo1);
+
+ // first tab renders, not the second one
+ expect(tab1RenderStub.calledOnce).toEqual(true);
+ expect(tab2RenderStub.notCalled).toEqual(true);
+
+ // info got set only to the first visible tab
+ expect(testView.getFileInfo()).toEqual(fileInfo1);
+ expect(testView2.getFileInfo()).toBeUndefined();
+
+ // select second tab for first render
+ detailsView.$el.find('.tabHeader').eq(1).click();
+
+ // second tab got rendered
+ expect(tab2RenderStub.calledOnce).toEqual(true);
+ expect(testView2.getFileInfo()).toEqual(fileInfo1);
+
+ // select the first tab again
+ detailsView.$el.find('.tabHeader').eq(0).click();
+
+ // no re-render
+ expect(tab1RenderStub.calledOnce).toEqual(true);
+ expect(tab2RenderStub.calledOnce).toEqual(true);
- expect(tabRenderStub.callCount).toEqual(2);
- tabRenderStub.restore();
+ tab1RenderStub.reset();
+ tab2RenderStub.reset();
+
+ // switch to another file
+ detailsView.setFileInfo(fileInfo2);
+
+ // only the visible tab was updated and rerendered
+ expect(tab1RenderStub.calledOnce).toEqual(true);
+ expect(testView.getFileInfo()).toEqual(fileInfo2);
+
+ // second/invisible tab still has old info, not rerendered
+ expect(tab2RenderStub.notCalled).toEqual(true);
+ expect(testView2.getFileInfo()).toEqual(fileInfo1);
+
+ // reselect the second one
+ detailsView.$el.find('.tabHeader').eq(1).click();
+
+ // second tab becomes visible, updated and rendered
+ expect(testView2.getFileInfo()).toEqual(fileInfo2);
+ expect(tab2RenderStub.calledOnce).toEqual(true);
+
+ tab1RenderStub.restore();
+ tab2RenderStub.restore();
});
it('selects the first tab by default', function() {
expect(detailsView.$el.find('.tabHeader').eq(0).hasClass('selected')).toEqual(true);
expect(detailsView.$el.find('.tabHeader').eq(1).hasClass('selected')).toEqual(false);
expect(detailsView.$el.find('.tab').eq(0).hasClass('hidden')).toEqual(false);
- expect(detailsView.$el.find('.tab').eq(1).hasClass('hidden')).toEqual(true);
+ expect(detailsView.$el.find('.tab').eq(1).length).toEqual(0);
});
it('switches the current tab when clicking on tab header', function() {
detailsView.$el.find('.tabHeader').eq(1).click();
@@ -101,5 +144,14 @@ 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);
+ });
});
});
diff --git a/apps/files_sharing/js/share.js b/apps/files_sharing/js/share.js
index 04700b84011..c124d390d04 100644
--- a/apps/files_sharing/js/share.js
+++ b/apps/files_sharing/js/share.js
@@ -60,6 +60,14 @@
return tr;
};
+ var oldElementToFile = fileList.elementToFile;
+ fileList.elementToFile = function($el) {
+ var fileInfo = oldElementToFile.apply(this, arguments);
+ fileInfo.sharePermissions = $el.attr('data-share-permissions') || undefined;
+ fileInfo.shareOwner = $el.attr('data-share-owner') || undefined;
+ return fileInfo;
+ };
+
// use delegate to catch the case with multiple file lists
fileList.$el.on('fileActionsReady', function(ev){
var fileList = ev.fileList;
diff --git a/apps/files_sharing/js/sharetabview.js b/apps/files_sharing/js/sharetabview.js
index 5f4a21a4a57..ee572b747ea 100644
--- a/apps/files_sharing/js/sharetabview.js
+++ b/apps/files_sharing/js/sharetabview.js
@@ -10,7 +10,7 @@
(function() {
var TEMPLATE =
- '<div>Owner: {{owner}}';
+ '<div><ul>{{#if owner}}<li>Owner: {{owner}}</li>{{/if}}</ul></div>';
/**
* @memberof OCA.Sharing
@@ -37,8 +37,13 @@
}
if (this.model) {
+ console.log(this.model);
+ var owner = this.model.get('shareOwner');
+ if (owner === OC.currentUser) {
+ owner = null;
+ }
this.$el.append(this._template({
- owner: this.model.get('shareOwner') || OC.currentUser
+ owner: owner
}));
} else {
diff --git a/apps/files_sharing/lib/capabilities.php b/apps/files_sharing/lib/capabilities.php
index 0338dfe96e2..ea71c47a05c 100644
--- a/apps/files_sharing/lib/capabilities.php
+++ b/apps/files_sharing/lib/capabilities.php
@@ -59,6 +59,7 @@ class Capabilities implements ICapability {
}
$public['send_mail'] = $this->config->getAppValue('core', 'shareapi_allow_public_notification', 'no') === 'yes';
+ $public['upload'] = $this->config->getAppValue('core', 'shareapi_allow_public_upload', 'yes') === 'yes';
}
$res["public"] = $public;
diff --git a/apps/files_sharing/tests/capabilities.php b/apps/files_sharing/tests/capabilities.php
index 7656849120b..5b9789ce324 100644
--- a/apps/files_sharing/tests/capabilities.php
+++ b/apps/files_sharing/tests/capabilities.php
@@ -183,4 +183,24 @@ class FilesSharingCapabilitiesTest extends \Test\TestCase {
$result = $this->getResults($map);
$this->assertFalse($result['resharing']);
}
+
+ public function testLinkPublicUpload() {
+ $map = [
+ ['core', 'shareapi_allow_links', 'yes', 'yes'],
+ ['core', 'shareapi_allow_public_upload', 'yes', 'yes'],
+ ];
+ $result = $this->getResults($map);
+ $this->assertTrue($result['public']['upload']);
+ }
+
+ public function testLinkNoPublicUpload() {
+ $map = [
+ ['core', 'shareapi_allow_links', 'yes', 'yes'],
+ ['core', 'shareapi_allow_public_upload', 'yes', 'no'],
+ ];
+ $result = $this->getResults($map);
+ $this->assertFalse($result['public']['upload']);
+ }
+
+
}
diff --git a/config/config.sample.php b/config/config.sample.php
index 047e7ccdd12..2b4873d5310 100644
--- a/config/config.sample.php
+++ b/config/config.sample.php
@@ -284,7 +284,7 @@ $CONFIG = array(
'mail_smtpmode' => 'sendmail',
/**
- * This depends on ``mail_smtpmode``. Specified the IP address of your mail
+ * This depends on ``mail_smtpmode``. Specify the IP address of your mail
* server host. This may contain multiple hosts separated by a semi-colon. If
* you need to specify the port number append it to the IP address separated by
* a colon, like this: ``127.0.0.1:24``.
@@ -297,7 +297,7 @@ $CONFIG = array(
'mail_smtpport' => 25,
/**
- * This depends on ``mail_smtpmode``. This set an SMTP server timeout, in
+ * This depends on ``mail_smtpmode``. This sets the SMTP server timeout, in
* seconds. You may need to increase this if you are running an anti-malware or
* spam scanner.
*/
@@ -373,7 +373,7 @@ $CONFIG = array(
'overwritecondaddr' => '',
/**
- * Use this configuration parameter to specify the base url for any urls which
+ * Use this configuration parameter to specify the base URL for any URLs which
* are generated within ownCloud using any kind of command line tools (cron or
* occ). The value should contain the full base URL:
* ``https://www.example.com/owncloud``
@@ -412,18 +412,19 @@ $CONFIG = array(
* ownCloud 8.1 and before.
*
* Available values:
- * ``auto`` default setting. keeps files and folders in the trash bin
+ *
+ * * ``auto`` default setting. keeps files and folders in the trash bin
* for 30 days and automatically deletes anytime after that
* if space is needed (note: files may not be deleted if space
* is not needed).
- * ``D, auto`` keeps files and folders in the trash bin for D+ days,
+ * * ``D, auto`` keeps files and folders in the trash bin for D+ days,
* delete anytime if space needed (note: files may not be deleted
* if space is not needed)
* * ``auto, D`` delete all files in the trash bin that are older than D days
* automatically, delete other files anytime if space needed
* * ``D1, D2`` keep files and folders the in trash bin for at least D1 days
* and delete when exceeds D2 days
- * ``disabled`` trash bin auto clean disabled, files and folders will be
+ * * ``disabled`` trash bin auto clean disabled, files and folders will be
* kept forever
*/
'trashbin_retention_obligation' => 'auto',
@@ -550,8 +551,8 @@ $CONFIG = array(
* Location of the lock file for cron executions can be specified here.
* Default is within the tmp directory. The file is named in the following way:
* owncloud-server-$INSTANCEID-cron.lock
- * where $INSTANCEID is the string specified in the instanceid field.
- * Because the cron lock file is accessed in regular intervals, it may prevent
+ * where $INSTANCEID is the string specified in the ``instanceid`` field.
+ * Because the cron lock file is accessed at regular intervals, it may prevent
* enabled disk drives from spinning down. A different location for this file
* can solve such issues.
*/
@@ -654,7 +655,7 @@ $CONFIG = array(
/**
* By default, ownCloud can generate previews for the following filetypes:
*
- * - Images files
+ * - Image files
* - Covers of MP3 files
* - Text documents
*
@@ -765,7 +766,7 @@ $CONFIG = array(
/**
* defines the interval in minutes for the background job that checks user
- * existance and marks them as ready to be cleaned up. The number is always
+ * existence and marks them as ready to be cleaned up. The number is always
* minutes. Setting it to 0 disables the feature.
* See command line (occ) methods ldap:show-remnants and user:delete
*/
@@ -782,10 +783,10 @@ $CONFIG = array(
/**
* Enable maintenance mode to disable ownCloud
*
- * If you want to prevent users to login to ownCloud before you start doing some
- * maintenance work, you need to set the value of the maintenance parameter to
- * true. Please keep in mind that users who are already logged-in are kicked out
- * of ownCloud instantly.
+ * If you want to prevent users from logging in to ownCloud before you start
+ * doing some maintenance work, you need to set the value of the maintenance
+ * parameter to true. Please keep in mind that users who are already logged-in
+ * are kicked out of ownCloud instantly.
*/
'maintenance' => false,
@@ -1003,7 +1004,7 @@ $CONFIG = array(
/**
* The parent of the directory where css and js assets will be stored if
- * piplelining is enabled; this defaults to the ownCloud directory. The assets
+ * pipelining is enabled; this defaults to the ownCloud directory. The assets
* will be stored in a subdirectory of this directory named 'assets'. The
* server *must* be configured to serve that directory as $WEBROOT/assets.
* You will only likely need to change this if the main ownCloud directory
@@ -1059,7 +1060,7 @@ $CONFIG = array(
* Enables transactional file locking.
* This is disabled by default as it is still beta.
*
- * Prevents concurrent processes to access the same files
+ * Prevents concurrent processes from accessing the same files
* at the same time. Can help prevent side effects that would
* be caused by concurrent operations. Mainly relevant for
* very large installations with many users working with
diff --git a/lib/private/files.php b/lib/private/files.php
index b61d09d8a0c..6268bf8a129 100644
--- a/lib/private/files.php
+++ b/lib/private/files.php
@@ -21,6 +21,7 @@
* @author Thomas Müller <thomas.mueller@tmit.eu>
* @author Valerio Ponte <valerio.ponte@gmail.com>
* @author Vincent Petry <pvince81@owncloud.com>
+ * @author Robin McCorkell <rmccorkell@karoshi.org.uk>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
@@ -268,58 +269,80 @@ class OC_Files {
* set the maximum upload size limit for apache hosts using .htaccess
*
* @param int $size file size in bytes
+ * @param array $files override '.htaccess' and '.user.ini' locations
* @return bool false on failure, size on success
*/
- static function setUploadLimit($size) {
+ public static function setUploadLimit($size, $files = []) {
//don't allow user to break his config
- if ($size > PHP_INT_MAX) {
- //max size is always 1 byte lower than computerFileSize returns
- if ($size > PHP_INT_MAX + 1)
- return false;
- $size -= 1;
- }
+ $size = intval($size);
if ($size < self::UPLOAD_MIN_LIMIT_BYTES) {
return false;
}
$size = OC_Helper::phpFileSize($size);
- //don't allow user to break his config -- broken or malicious size input
- if (intval($size) === 0) {
- return false;
- }
-
- //suppress errors in case we don't have permissions for
- $htaccess = @file_get_contents(OC::$SERVERROOT . '/.htaccess');
- if (!$htaccess) {
- return false;
- }
-
$phpValueKeys = array(
'upload_max_filesize',
'post_max_size'
);
- foreach ($phpValueKeys as $key) {
- $pattern = '/php_value ' . $key . ' (\S)*/';
- $setting = 'php_value ' . $key . ' ' . $size;
- $hasReplaced = 0;
- $content = preg_replace($pattern, $setting, $htaccess, 1, $hasReplaced);
- if ($content !== null) {
- $htaccess = $content;
+ // default locations if not overridden by $files
+ $files = array_merge([
+ '.htaccess' => OC::$SERVERROOT . '/.htaccess',
+ '.user.ini' => OC::$SERVERROOT . '/.user.ini'
+ ], $files);
+
+ $updateFiles = [
+ $files['.htaccess'] => [
+ 'pattern' => '/php_value %1$s (\S)*/',
+ 'setting' => 'php_value %1$s %2$s'
+ ],
+ $files['.user.ini'] => [
+ 'pattern' => '/%1$s=(\S)*/',
+ 'setting' => '%1$s=%2$s'
+ ]
+ ];
+
+ $success = true;
+
+ foreach ($updateFiles as $filename => $patternMap) {
+ // suppress warnings from fopen()
+ $handle = @fopen($filename, 'r+');
+ if (!$handle) {
+ \OCP\Util::writeLog('files',
+ 'Can\'t write upload limit to ' . $filename . '. Please check the file permissions',
+ \OCP\Util::WARN);
+ $success = false;
+ continue; // try to update as many files as possible
}
- if ($hasReplaced === 0) {
- $htaccess .= "\n" . $setting;
+
+ $content = '';
+ while (!feof($handle)) {
+ $content .= fread($handle, 1000);
}
+
+ foreach ($phpValueKeys as $key) {
+ $pattern = vsprintf($patternMap['pattern'], [$key]);
+ $setting = vsprintf($patternMap['setting'], [$key, $size]);
+ $hasReplaced = 0;
+ $newContent = preg_replace($pattern, $setting, $content, 1, $hasReplaced);
+ if ($newContent !== null) {
+ $content = $newContent;
+ }
+ if ($hasReplaced === 0) {
+ $content .= "\n" . $setting;
+ }
+ }
+
+ // write file back
+ ftruncate($handle, 0);
+ rewind($handle);
+ fwrite($handle, $content);
+
+ fclose($handle);
}
- //check for write permissions
- if (is_writable(OC::$SERVERROOT . '/.htaccess')) {
- file_put_contents(OC::$SERVERROOT . '/.htaccess', $htaccess);
+ if ($success) {
return OC_Helper::computerFileSize($size);
- } else {
- \OCP\Util::writeLog('files',
- 'Can\'t write upload limit to ' . OC::$SERVERROOT . '/.htaccess. Please check the file permissions',
- \OCP\Util::WARN);
}
return false;
}
diff --git a/lib/private/ocsclient.php b/lib/private/ocsclient.php
index d6593e5089a..78df3b79bb6 100644
--- a/lib/private/ocsclient.php
+++ b/lib/private/ocsclient.php
@@ -272,7 +272,7 @@ class OCSClient {
$tmp = $data->data->content;
if (is_null($tmp)) {
- \OCP\Util::writeLog('core', 'No update found at the ownCloud appstore for app ' . $id, \OCP\Util::INFO);
+ \OCP\Util::writeLog('core', 'No update found at the ownCloud appstore for app ' . $id, \OCP\Util::DEBUG);
return null;
}
diff --git a/lib/private/server.php b/lib/private/server.php
index 618431ff2d4..89001567219 100644
--- a/lib/private/server.php
+++ b/lib/private/server.php
@@ -51,7 +51,6 @@ use OC\Http\Client\ClientService;
use OC\Lock\MemcacheLockingProvider;
use OC\Lock\NoopLockingProvider;
use OC\Mail\Mailer;
-use OC\Memcache\NullCache;
use OC\Security\CertificateManager;
use OC\Security\Crypto;
use OC\Security\Hasher;
@@ -59,6 +58,8 @@ use OC\Security\SecureRandom;
use OC\Security\TrustedDomainHelper;
use OC\Tagging\TagMapper;
use OCP\IServerContainer;
+use Symfony\Component\EventDispatcher\EventDispatcher;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
/**
* Class Server
@@ -457,7 +458,9 @@ class Server extends SimpleContainer implements IServerContainer {
});
return $manager;
});
-
+ $this->registerService('EventDispatcher', function() {
+ return new EventDispatcher();
+ });
}
/**
@@ -963,4 +966,14 @@ class Server extends SimpleContainer implements IServerContainer {
public function getCapabilitiesManager() {
return $this->query('CapabilitiesManager');
}
+
+ /**
+ * Get the EventDispatcher
+ *
+ * @return EventDispatcherInterface
+ * @since 8.2.0
+ */
+ public function getEventDispatcher() {
+ return $this->query('EventDispatcher');
+ }
}
diff --git a/lib/public/iservercontainer.php b/lib/public/iservercontainer.php
index ab1729da255..a6d83156de3 100644
--- a/lib/public/iservercontainer.php
+++ b/lib/public/iservercontainer.php
@@ -39,6 +39,7 @@
// use OCP namespace for all classes that are considered public.
// This means that they should be used by apps instead of the internal ownCloud classes
namespace OCP;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
/**
@@ -439,4 +440,12 @@ interface IServerContainer {
*/
public function getMimeTypeDetector();
+
+ /**
+ * Get the EventDispatcher
+ *
+ * @return EventDispatcherInterface
+ * @since 8.2.0
+ */
+ public function getEventDispatcher();
}
diff --git a/tests/data/setUploadLimit/htaccess b/tests/data/setUploadLimit/htaccess
new file mode 100644
index 00000000000..65957a29838
--- /dev/null
+++ b/tests/data/setUploadLimit/htaccess
@@ -0,0 +1,60 @@
+# Version: 8.2.0
+<IfModule mod_headers.c>
+ <IfModule mod_fcgid.c>
+ <IfModule mod_setenvif.c>
+ SetEnvIfNoCase ^Authorization$ "(.+)" XAUTHORIZATION=$1
+ RequestHeader set XAuthorization %{XAUTHORIZATION}e env=XAUTHORIZATION
+ </IfModule>
+ </IfModule>
+
+ <IfModule mod_env.c>
+ # Add security and privacy related headers
+ Header set X-Content-Type-Options "nosniff"
+ Header set X-XSS-Protection "1; mode=block"
+ Header set X-Robots-Tag "none"
+ Header set X-Frame-Options "SAMEORIGIN"
+ SetEnv modHeadersAvailable true
+ </IfModule>
+
+ # Add cache control for CSS and JS files
+ <FilesMatch "\.(css|js)$">
+ Header set Cache-Control "max-age=7200, public"
+ </FilesMatch>
+</IfModule>
+<IfModule mod_php5.c>
+php_value upload_max_filesize 513M
+php_value post_max_size 513M
+php_value memory_limit 512M
+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
+<IfModule mod_env.c>
+ SetEnv htaccessWorking true
+</IfModule>
+</IfModule>
+<IfModule mod_rewrite.c>
+RewriteEngine on
+RewriteRule .* - [env=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
+RewriteRule ^\.well-known/host-meta /public.php?service=host-meta [QSA,L]
+RewriteRule ^\.well-known/host-meta\.json /public.php?service=host-meta-json [QSA,L]
+RewriteRule ^\.well-known/carddav /remote.php/carddav/ [R=301,L]
+RewriteRule ^\.well-known/caldav /remote.php/caldav/ [R=301,L]
+RewriteRule ^apps/calendar/caldav\.php remote.php/caldav/ [QSA,L]
+RewriteRule ^apps/contacts/carddav\.php remote.php/carddav/ [QSA,L]
+RewriteRule ^remote/(.*) remote.php [QSA,L]
+RewriteRule ^(build|tests|config|lib|3rdparty|templates)/.* - [R=404,L]
+RewriteRule ^(\.|autotest|occ|issue|indie|db_|console).* - [R=404,L]
+</IfModule>
+<IfModule mod_mime.c>
+AddType image/svg+xml svg svgz
+AddEncoding gzip svgz
+</IfModule>
+<IfModule mod_dir.c>
+DirectoryIndex index.php index.html
+</IfModule>
+AddDefaultCharset utf-8
+Options -Indexes
+<IfModule pagespeed_module>
+ ModPagespeed Off
+</IfModule>
diff --git a/tests/data/setUploadLimit/user.ini b/tests/data/setUploadLimit/user.ini
new file mode 100644
index 00000000000..c5996e8d47e
--- /dev/null
+++ b/tests/data/setUploadLimit/user.ini
@@ -0,0 +1,7 @@
+upload_max_filesize=513M
+post_max_size=513M
+memory_limit=512M
+mbstring.func_overload=0
+always_populate_raw_post_data=-1
+default_charset='UTF-8'
+output_buffering=off
diff --git a/tests/lib/files.php b/tests/lib/files.php
new file mode 100644
index 00000000000..6808b3e9f64
--- /dev/null
+++ b/tests/lib/files.php
@@ -0,0 +1,135 @@
+<?php
+/**
+ * @author Robin McCorkell <rmccorkell@karoshi.org.uk>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace Test;
+
+class Files extends \Test\TestCase {
+
+ const UPLOAD_LIMIT_DEFAULT_STR = '513M';
+ const UPLOAD_LIMIT_SETTING_STR = '2M';
+ const UPLOAD_LIMIT_SETTING_BYTES = 2097152;
+
+ /** @var array $tmpDirs */
+ private $tmpDirs = [];
+
+ /**
+ * @return array
+ */
+ private function getUploadLimitTestFiles() {
+ $dir = \OC::$server->getTempManager()->getTemporaryFolder();
+ $this->tmpDirs[] = $dir;
+ $result = [
+ '.htaccess' => $dir . '/htaccess',
+ '.user.ini' => $dir . '/user.ini'
+ ];
+ copy(\OC::$SERVERROOT . '/tests/data/setUploadLimit/htaccess', $result['.htaccess']);
+ copy(\OC::$SERVERROOT . '/tests/data/setUploadLimit/user.ini', $result['.user.ini']);
+ return $result;
+ }
+
+ protected function tearDown() {
+ foreach ($this->tmpDirs as $dir) {
+ \OC_Helper::rmdirr($dir);
+ }
+ parent::tearDown();
+ }
+
+ public function testSetUploadLimitSizeSanity() {
+ $this->assertFalse(\OC_Files::setUploadLimit(PHP_INT_MAX + 10));
+ $this->assertFalse(\OC_Files::setUploadLimit(\OC_Files::UPLOAD_MIN_LIMIT_BYTES - 10));
+ $this->assertFalse(\OC_Files::setUploadLimit('foobar'));
+ }
+
+ public function setUploadLimitWriteProvider() {
+ return [
+ [
+ // both files writable
+ true, true,
+ self::UPLOAD_LIMIT_SETTING_BYTES, self::UPLOAD_LIMIT_SETTING_BYTES,
+ self::UPLOAD_LIMIT_SETTING_STR, self::UPLOAD_LIMIT_SETTING_STR
+ ],
+ [
+ // neither file writable
+ false, false,
+ self::UPLOAD_LIMIT_SETTING_BYTES, false,
+ self::UPLOAD_LIMIT_DEFAULT_STR, self::UPLOAD_LIMIT_DEFAULT_STR
+ ],
+ [
+ // only .htaccess writable
+ true, false,
+ self::UPLOAD_LIMIT_SETTING_BYTES, false,
+ self::UPLOAD_LIMIT_SETTING_STR, self::UPLOAD_LIMIT_DEFAULT_STR
+ ],
+ [
+ // only .user.ini writable
+ false, true,
+ self::UPLOAD_LIMIT_SETTING_BYTES, false,
+ self::UPLOAD_LIMIT_DEFAULT_STR, self::UPLOAD_LIMIT_SETTING_STR
+ ],
+ [
+ // test rounding of values
+ true, true,
+ self::UPLOAD_LIMIT_SETTING_BYTES + 20, self::UPLOAD_LIMIT_SETTING_BYTES,
+ self::UPLOAD_LIMIT_SETTING_STR, self::UPLOAD_LIMIT_SETTING_STR
+ ]
+ ];
+ }
+
+ /**
+ * @dataProvider setUploadLimitWriteProvider
+ */
+ public function testSetUploadLimitWrite(
+ $htaccessWritable, $userIniWritable,
+ $setSize, $expectedSize,
+ $htaccessStr, $userIniStr
+ ) {
+ $files = $this->getUploadLimitTestFiles();
+ chmod($files['.htaccess'], ($htaccessWritable ? 0644 : 0444));
+ chmod($files['.user.ini'], ($userIniWritable ? 0644 : 0444));
+
+ $htaccessSize = filesize($files['.htaccess']);
+ $userIniSize = filesize($files['.user.ini']);
+ $htaccessSizeMod = 2*(strlen($htaccessStr) - strlen(self::UPLOAD_LIMIT_DEFAULT_STR));
+ $userIniSizeMod = 2*(strlen($userIniStr) - strlen(self::UPLOAD_LIMIT_DEFAULT_STR));
+
+ $this->assertEquals($expectedSize, \OC_Files::setUploadLimit($setSize, $files));
+
+ // check file contents
+ $htaccess = file_get_contents($files['.htaccess']);
+ $this->assertEquals(1,
+ preg_match('/php_value upload_max_filesize '.$htaccessStr.'/', $htaccess)
+ );
+ $this->assertEquals(1,
+ preg_match('/php_value post_max_size '.$htaccessStr.'/', $htaccess)
+ );
+ $this->assertEquals($htaccessSize + $htaccessSizeMod, filesize($files['.htaccess']));
+
+ $userIni = file_get_contents($files['.user.ini']);
+ $this->assertEquals(1,
+ preg_match('/upload_max_filesize='.$userIniStr.'/', $userIni)
+ );
+ $this->assertEquals(1,
+ preg_match('/post_max_size='.$userIniStr.'/', $userIni)
+ );
+ $this->assertEquals($userIniSize + $userIniSizeMod, filesize($files['.user.ini']));
+ }
+}
diff --git a/tests/settings/controller/userscontrollertest.php b/tests/settings/controller/userscontrollertest.php
index 5f98cf21c04..06065a8454e 100644
--- a/tests/settings/controller/userscontrollertest.php
+++ b/tests/settings/controller/userscontrollertest.php
@@ -1390,9 +1390,11 @@ class UsersControllerTest extends \Test\TestCase {
public function setEmailAddressData() {
return [
- ['', true, false, true],
- ['foobar@localhost', true, true, false],
- ['foo@bar@localhost', false, false, false],
+ /* mailAddress, isValid, expectsUpdate, expectsDelete, canChangeDisplayName, responseCode */
+ [ '', true, false, true, true, Http::STATUS_OK ],
+ [ 'foo@local', true, true, false, true, Http::STATUS_OK],
+ [ 'foo@bar@local', false, false, false, true, Http::STATUS_UNPROCESSABLE_ENTITY],
+ [ 'foo@local', true, false, false, false, Http::STATUS_FORBIDDEN],
];
}
@@ -1404,7 +1406,7 @@ class UsersControllerTest extends \Test\TestCase {
* @param bool $expectsUpdate
* @param bool $expectsDelete
*/
- public function testSetEmailAddress($mailAddress, $isValid, $expectsUpdate, $expectsDelete) {
+ public function testSetEmailAddress($mailAddress, $isValid, $expectsUpdate, $expectsDelete, $canChangeDisplayName, $responseCode) {
$this->container['IsAdmin'] = true;
$user = $this->getMockBuilder('\OC\User\User')
@@ -1413,6 +1415,10 @@ class UsersControllerTest extends \Test\TestCase {
->expects($this->any())
->method('getUID')
->will($this->returnValue('foo'));
+ $user
+ ->expects($this->any())
+ ->method('canChangeDisplayName')
+ ->will($this->returnValue($canChangeDisplayName));
$this->container['UserSession']
->expects($this->atLeastOnce())
->method('getUser')
@@ -1455,7 +1461,9 @@ class UsersControllerTest extends \Test\TestCase {
);
- $this->container['UsersController']->setMailAddress($user->getUID(), $mailAddress);
+ $response = $this->container['UsersController']->setMailAddress($user->getUID(), $mailAddress);
+
+ $this->assertSame($responseCode, $response->getStatus());
}
}