Compare commits

...

66 Commits

Author SHA1 Message Date
Frank Karlitschek e3d50804f7 7RC2 2014-07-13 22:45:11 -04:00
Vincent Petry 14ea0ea3bc Propagate file action changes to the file lists
Whenever an app needs to register an event late, it does that on the
original file actions object.

Since the file actions that the file list work on is a merged list, not
the original one, the registration event needs to be propagated there as
well.
2014-07-10 16:15:46 +02:00
Thomas Müller 66130ad336 fixing JS syntax errors
remove the group in case the last user has removed from that group

cleanup

use .filterAttr()
2014-07-10 16:13:01 +02:00
Arthur Schiwon a310415b09 increment group counters when a user is created
decrease user count in affected groups after user delete

increase/decrease everyone count on user creation/deletion

avoid global selector
2014-07-10 16:12:53 +02:00
Thomas Müller 10bac56551 adding 'groups' entry to remote apps 2014-07-10 15:47:29 +02:00
Thomas Müller 32c6afba90 fixing typos 2014-07-10 15:47:29 +02:00
Robin Appelman 31d8bce383 debounce the search function 2014-07-10 15:32:17 +02:00
Morris Jobke 2fb022fbc3 fix translations 2014-07-09 16:19:32 +02:00
Volkan Gezer 5911188211 fix apostrophe fixes #9486 2014-07-09 16:19:32 +02:00
libasys 9ccadcfe80 BugFix missing $item on 'file_target' Line 1911
I think this should be right!

Backport of e70a7af6da from master
2014-07-09 16:05:47 +02:00
Arthur Schiwon 9ee46bbe91 test class is already in preferences.php
Conflicts:
	tests/lib/preferences-singleton.php
2014-07-09 13:14:34 +02:00
Arthur Schiwon bc1ff4744b support for AD primary groups
support for primary groups

actually the problem is only known on AD, it is only needed to take care of their attributes

adjust to ADs special behaviour

this change was not intended

cache the SID value so it is not requested over and over again

theres only one, use singular

we are access

add tests for new Access methods

add tests for new Group methods

address scrutinizer findings, mostly doc

call ldap_explode_dn from ldap wrapper, enables tests without php5-ldap

PHP Doc

yo dawg, i heard you like backslashes … php doc fix

PHPDoc updated and typos fixed while reviewing
2014-07-08 21:34:38 +02:00
Vincent Petry 6043a90a71 Fix update cleanup to only affect file and folders
Fix bug in the SQL query that cleans up stray shares for removed
files/folders, which is now correctly limited to that item type instead
of also removing all other share types.

Backport of f4f52cf from master
2014-07-08 18:10:03 +02:00
Thomas Müller 0e62a7dc59 Upload abortion is now detected within the OC_Connector_Sabre_File::put()
OC_Connector_Sabre_AbortedUploadDetectionPlugin is pointless

Adding unit test testUploadAbort()
2014-07-08 17:40:08 +02:00
Thomas Müller 7a6b76f96e Adding new interface \OCP\Activity\IExtentsion
Adding method getNotificationTypes()
Adding method filterNotificationTypes()
Adding method getDefaultTypes()
Adding method translate() and getTypeIcon()
Adding method getGroupParameter()
Adding method getNavigation()
Adding method getNavigation()
Adding method isFilterValid() and getQueryForFilter()
Adding unit tests for \OC\ActivityManager
2014-07-08 16:38:18 +02:00
blizzz a78293dd3f Merge pull request #9500 from owncloud/fix9475
Avoid unnecessary writing to the DB when prefrences are not changed in fact
2014-07-08 16:30:09 +02:00
Robin Appelman 1ce9ba1ebc use case insensitive LIKE when searching for files in mysql 2014-07-08 15:49:05 +02:00
Robin Appelman 2f15ae988f Add repair step to set MySQL collation to utf8_bin
Set default collation of mysql connection to utf8_bin
Set utf_bin as default collation for new tables
2014-07-08 15:14:29 +02:00
Christopher T. Johnson c02e95ff40 Fix Signiture Does Not Match when mounting Amazon S3 external storage
For some reason the aws-sdk-php package does not caclulate the
signiture correctly when accessing an object in a bucket with a name of
'.'.

When we are at the top of a S3 bucket there is a need(?) to have a directory
name.  Per standard Unix the name picked was '.' (dot or period).  This
choice exercises the aws-sdk bug.

This fix is to add a field to the method to store the name to use instead of
'.' which at this point is hard coded to '<root>'.  We also add a private
function 'cleanKey()' which will test for the '.' name and replace it with
the variable.  Finally all calls to manipulate objects where the path is
not obviously not '.' are processed through cleanKey().

An example where we don't process through clean key would be
	'Key' => $path.'/',

Use correct relationship operator

Per feed back use === instead of ==

use '/' instead of '<root>'
2014-07-08 14:26:34 +02:00
Arthur Schiwon 03c13021db also appconfig shall not write to database if the value is unchanged 2014-07-08 12:30:38 +02:00
Morris Jobke 01c0601d39 specify CSS rule for warning & update fieldsets - fixes #9491 2014-07-08 09:45:57 +02:00
Stephane V 8bd5c6e04d Fixes #9497 2014-07-08 01:19:24 +02:00
Arthur Schiwon 76b310de9d add test cases. also split test classes in a file each, looks like only the first class is being executed 2014-07-08 00:20:46 +02:00
Arthur Schiwon ee2886331e do not write to database when the value is the same 2014-07-08 00:19:58 +02:00
Arthur Schiwon ed1c918d9e don't trigger update from checkPassword, it is already called by userExists, this is enough. 2014-07-08 00:19:17 +02:00
Robin Appelman 18f5f85160 When changing the mountpoint of an external storage, ensure the old one is removed 2014-07-07 23:25:26 +02:00
Vincent Petry 19dedf3d61 Do not show recipient for link shares in file list
In the "Shared with link" section, the share_with field can contain a
value when a password was set.

This fix prevents this value to be shown as it is not intended for the
end user.
2014-07-07 20:04:03 +02:00
Georg Ehrke efadfedbaa add ocsids to info.xml 2014-07-07 20:00:08 +02:00
Vincent Petry 60e3195600 Improved external share dialog
Changed title, label and buttons.
Removed icon.
2014-07-07 19:53:19 +02:00
Vincent Petry db72e90504 Fixed dialogs styling, reversed buttons
Default dialog button is now on the right, other one on the left.
2014-07-07 19:52:53 +02:00
Joas Schilling cf982e9818 Add comment to overwrite* configs about CLI/cron problems 2014-07-07 19:40:28 +02:00
Joas Schilling dccab5d20f Only calculate the WEBROOT from scriptName if it contains $SUBURI
If not we are most likely in CLI mode. However to be able to still
generate valid URLs, we need to use the overwrite webroot instead.

Fix #9490
2014-07-07 19:39:58 +02:00
Joas Schilling f5e4ebf2ba Only overwrite serverHost when in unit tests
The intention of the original commit 12f7cb8767
was to fix tests. But cron should always return a valid host not localhost.
2014-07-07 19:39:20 +02:00
Vincent Petry 2bfdd02c2a Fixed shared list sorting
Use Array.sort instead of underscore's sortBy() as they don't use the
same method/function signature.
2014-07-07 19:33:22 +02:00
Thomas Müller 8b97073e13 MySQL: adding repair step to convert MyIsam tables to InnoDB 2014-07-07 15:51:52 +02:00
Georg Ehrke d2fd78a0c9 fix phpDocBlock for OC_App::getAppInfo 2014-07-07 15:03:38 +02:00
Georg Ehrke 59fd9d7517 better validation: cadd extra check if appinfo/info.xml exists 2014-07-07 15:03:33 +02:00
Stephane V 5344d9beab Bug 9147 owncloud/core
Added class="date" in the latest column of the log table to get everything on one line (in the ajax request).
2014-07-07 13:58:01 +02:00
Morris Jobke 127ce3c5d9 fix loading spinner on ctrl click a app entry- fixes #9063 2014-07-07 12:19:07 +02:00
Georg Ehrke 04604dae0d improvements for uninstall button 2014-07-06 23:57:27 +02:00
Volkan Gezer 0fe1f25a9e Merge pull request #9466 from owncloud/design-fix-fieldset-legend
fix fieldset look, fix #8158
2014-07-06 16:06:25 +02:00
Morris Jobke a5d34b435f also make strengthify transparent on setup submit - fixes #9436 2014-07-05 10:31:27 +02:00
Morris Jobke 67cf1d61e1 fix size of breadcrumb separator 2014-07-04 21:37:54 +02:00
Bjoern Schiessle 5549148039 add owner as parameter for delShareKey 2014-07-04 19:00:28 +02:00
Bjoern Schiessle 0319ee3894 make sure that the umount hook always contains the path relative to data/user/files 2014-07-04 19:00:20 +02:00
Bjoern Schiessle 2bd3aa6b21 if a folder gets deleted we unshare all shared files/folders below 2014-07-04 19:00:05 +02:00
Jörn Friedrich Dreyer 36d2aab945 deprecate OC_Search_Provider in favor of \OCP\Search\Provider 2014-07-04 18:29:26 +02:00
Jörn Friedrich Dreyer 0a4e95cc07 fix '' to '/' when determining parent for search result 2014-07-04 18:29:16 +02:00
Robin Appelman b429a71660 Add machine readable error messages to OC\JSON
Reload the files app in case of authentication errors, expired tokens or disabled app

Reloading will triger the full server side handeling of those errors

formatting

fix missing semicolon + some jshint warnings
2014-07-04 16:39:45 +02:00
Jan-Christoph Borchardt b33c61798c show loading feedback also when clicking 'Apps' entry in app list 2014-07-04 16:37:51 +02:00
Thomas Müller 11a2c0249d update snap.js to v2.0.0-rc1 2014-07-04 16:34:19 +02:00
Vincent Petry 86545a90d0 Fix reload call for all subclasses
All subclasses must also properly return the ajax call object.
2014-07-04 16:13:52 +02:00
Jörn Friedrich Dreyer 71261decf1 make search case insensitive on postgres and oracle 2014-07-04 15:53:10 +02:00
Jörn Friedrich Dreyer f390ae53ba introduce and use getCurrentConnection() 2014-07-04 15:26:30 +02:00
Jörn Friedrich Dreyer 195cf273f8 reset collection to 'root' after adding a route to the api 2014-07-04 15:26:21 +02:00
Jörn Friedrich Dreyer 3a1c7182e6 change order of registering api and capabilities to fix file version previews 2014-07-04 15:26:11 +02:00
Robin Appelman c272338897 set localhost as default database host for installation 2014-07-04 15:21:26 +02:00
Morris Jobke 74edbd5df0 fix cursor for other elements in top right corner 2014-07-04 14:58:19 +02:00
Jan-Christoph Borchardt c28fb2de4e fix 'remember' label being slightly off in log in 2014-07-04 14:58:14 +02:00
Jan-Christoph Borchardt b343b18cd9 fix icons being slightly off in log in 2014-07-04 14:58:09 +02:00
Jan-Christoph Borchardt 2224d1c0d3 fix user menu name and image not showing clicky mouse pointer 2014-07-04 14:58:05 +02:00
Vincent Petry b42215a3c9 Fix FileActions merging override
When merging FileActions, the register() functio needs to correctly
override the previously merged action handler without affecting the
original one.
2014-07-04 14:49:15 +02:00
Morris Jobke 2115a9bf1a IE8 fixes 2014-07-04 14:14:12 +02:00
Jan-Christoph Borchardt 843ad18bf3 use icon-confirm instead of text for accepting remote share, works better with translations 2014-07-04 14:14:07 +02:00
Vincent Petry ed4ba00b77 Return and use isPreviewAvailable for share previews
Since the mime type is known, now isPreviewAvailable is returned as well
and used by the JS side to properly render mime icon and previews.
2014-07-04 14:04:53 +02:00
Frank Karlitschek 4a7aaa8914 7RC1 2014-07-03 20:30:45 -04:00
261 changed files with 3634 additions and 1493 deletions
-1
View File
@@ -53,7 +53,6 @@ $server->subscribeEvent('beforeMethod', function () use ($server, $objectTree) {
$rootDir = new OC_Connector_Sabre_Directory($view, $rootInfo);
$objectTree->init($rootDir, $view, $mountManager);
$server->addPlugin(new OC_Connector_Sabre_AbortedUploadDetectionPlugin($view));
$server->addPlugin(new OC_Connector_Sabre_QuotaPlugin($view));
}, 30); // priority 30: after auth (10) and acl(20), before lock(50) and handling the request
+31 -3
View File
@@ -32,9 +32,11 @@
// regular actions
fileActions.merge(OCA.Files.fileActions);
// in case apps would decide to register file actions later,
// replace the global object with this one
OCA.Files.fileActions = fileActions;
this._onActionsUpdated = _.bind(this._onActionsUpdated, this);
OCA.Files.fileActions.on('setDefault.app-files', this._onActionsUpdated);
OCA.Files.fileActions.on('registerAction.app-files', this._onActionsUpdated);
window.FileActions.on('setDefault.app-files', this._onActionsUpdated);
window.FileActions.on('registerAction.app-files', this._onActionsUpdated);
this.files = OCA.Files.Files;
@@ -59,6 +61,32 @@
this._onPopState(OC.Util.History.parseUrlQuery());
},
/**
* Destroy the app
*/
destroy: function() {
this.navigation = null;
this.fileList.destroy();
this.fileList = null;
this.files = null;
OCA.Files.fileActions.off('setDefault.app-files', this._onActionsUpdated);
OCA.Files.fileActions.off('registerAction.app-files', this._onActionsUpdated);
window.FileActions.off('setDefault.app-files', this._onActionsUpdated);
window.FileActions.off('registerAction.app-files', this._onActionsUpdated);
},
_onActionsUpdated: function(ev, newAction) {
// forward new action to the file list
if (ev.action) {
this.fileList.fileActions.registerAction(ev.action);
} else if (ev.defaultAction) {
this.fileList.fileActions.setDefault(
ev.defaultAction.mime,
ev.defaultAction.name
);
}
},
/**
* Returns the container of the currently visible app.
*
+60 -33
View File
@@ -23,48 +23,52 @@
icons: {},
currentFile: null,
/**
* Dummy jquery element, for events
*/
$el: null,
/**
* List of handlers to be notified whenever a register() or
* setDefault() was called.
*/
_updateListeners: [],
_updateListeners: {},
initialize: function() {
this.clear();
// abusing jquery for events until we get a real event lib
this.$el = $('<div class="dummy-fileactions hidden"></div>');
$('body').append(this.$el);
},
/**
* Adds an update listener to be notified whenever register()
* or setDefault() has been called.
* Adds an event handler
*
* @param {String} eventName event name
* @param Function callback
*/
addUpdateListener: function(callback) {
if (!_.isFunction(callback)) {
throw 'Argument passed to FileActions.addUpdateListener must be a function';
}
this._updateListeners.push(callback);
on: function(eventName, callback) {
this.$el.on(eventName, callback);
},
/**
* Removes an update listener.
* Removes an event handler
*
* @param {String} eventName event name
* @param Function callback
*/
removeUpdateListener: function(callback) {
if (!_.isFunction(callback)) {
throw 'Argument passed to FileActions.removeUpdateListener must be a function';
}
this._updateListeners = _.without(this._updateListeners, callback);
off: function(eventName, callback) {
this.$el.off(eventName, callback);
},
/**
* Notifies the registered update listeners
* Notifies the event handlers
*
* @param {String} eventName event name
* @param {Object} data data
*/
_notifyUpdateListeners: function() {
for (var i = 0; i < this._updateListeners.length; i++) {
this._updateListeners[i](this);
}
_notifyUpdateListeners: function(eventName, data) {
this.$el.trigger(new $.Event(eventName, data));
},
/**
@@ -87,21 +91,44 @@
this.defaults = _.extend(this.defaults, fileActions.defaults);
this.icons = _.extend(this.icons, fileActions.icons);
},
register: function (mime, name, permissions, icon, action, displayName) {
/**
* @deprecated use #registerAction() instead
*/
register: function(mime, name, permissions, icon, action, displayName) {
return this.registerAction({
name: name,
mime: mime,
permissions: permissions,
icon: icon,
actionHandler: action,
displayName: displayName
});
},
/**
* Register action
*
* @param {Object} action action object
* @param {String} action.name identifier of the action
* @param {String} action.displayName display name of the action, defaults
* to the name given in action.name
* @param {String} action.mime mime type
* @param {int} action.permissions permissions
* @param {(Function|String)} action.icon icon
* @param {Function} action.actionHandler function that performs the action
*/
registerAction: function (action) {
var mime = action.mime;
var name = action.name;
if (!this.actions[mime]) {
this.actions[mime] = {};
}
if (!this.actions[mime][name]) {
this.actions[mime][name] = {};
}
if (!displayName) {
displayName = t('files', name);
}
this.actions[mime][name]['action'] = action;
this.actions[mime][name]['permissions'] = permissions;
this.actions[mime][name]['displayName'] = displayName;
this.icons[name] = icon;
this._notifyUpdateListeners();
this.actions[mime][name] = {
action: action.actionHandler,
permissions: action.permissions,
displayName: action.displayName || t('files', name)
};
this.icons[name] = action.icon;
this._notifyUpdateListeners('registerAction', {action: action});
},
clear: function() {
this.actions = {};
@@ -112,7 +139,7 @@
},
setDefault: function (mime, name) {
this.defaults[mime] = name;
this._notifyUpdateListeners();
this._notifyUpdateListeners('setDefault', {defaultAction: {mime: mime, name: name}});
},
get: function (mime, type, permissions) {
var actions = this.getActions(mime, type, permissions);
@@ -314,7 +341,7 @@
});
this.register('dir', 'Open', OC.PERMISSION_READ, '', function (filename, context) {
var dir = context.fileList.getCurrentDirectory();
var dir = context.$file.attr('data-path') || context.fileList.getCurrentDirectory();
if (dir !== '/') {
dir = dir + '/';
}
+16 -5
View File
@@ -172,7 +172,8 @@
*/
destroy: function() {
// TODO: also unregister other event handlers
this.fileActions.removeUpdateListener(this._onFileActionsUpdated);
this.fileActions.off('registerAction', this._onFileActionsUpdated);
this.fileActions.off('setDefault', this._onFileActionsUpdated);
},
_initFileActions: function(fileActions) {
@@ -182,7 +183,8 @@
this.fileActions.registerDefaultActions();
}
this._onFileActionsUpdated = _.debounce(_.bind(this._onFileActionsUpdated, this), 100);
this.fileActions.addUpdateListener(this._onFileActionsUpdated);
this.fileActions.on('registerAction', this._onFileActionsUpdated);
this.fileActions.on('setDefault', this._onFileActionsUpdated);
},
/**
@@ -919,7 +921,9 @@
.addClass(direction === 'desc' ? this.SORT_INDICATOR_DESC_CLASS : this.SORT_INDICATOR_ASC_CLASS);
},
/**
* @brief Reloads the file list using ajax call
* Reloads the file list using ajax call
*
* @return ajax call object
*/
reload: function() {
this._selectedFiles = {};
@@ -945,6 +949,13 @@
this.hideMask();
if (!result || result.status === 'error') {
// if the error is not related to folder we're trying to load, reload the page to handle logout etc
if (result.data.error === 'authentication_error' ||
result.data.error === 'token_expired' ||
result.data.error === 'application_not_enabled'
) {
OC.redirect(OC.generateUrl('apps/files'));
}
OC.Notification.show(result.data.message);
return false;
}
@@ -968,7 +979,7 @@
}
this.setFiles(result.data.files);
return true
return true;
},
updateStorageStatistics: function(force) {
@@ -1566,7 +1577,7 @@
numMatch=base.match(/\((\d+)\)/);
var num=2;
if (numMatch && numMatch.length>0) {
num=parseInt(numMatch[numMatch.length-1])+1;
num=parseInt(numMatch[numMatch.length-1], 10)+1;
base=base.split('(');
base.pop();
base=$.trim(base.join('('));
+1 -3
View File
@@ -52,9 +52,7 @@ describe('OCA.Files.App tests', function() {
App.initialize();
});
afterEach(function() {
App.navigation = null;
App.fileList = null;
App.files = null;
App.destroy();
pushStateStub.restore();
parseUrlQueryStub.restore();
+153 -3
View File
@@ -193,6 +193,156 @@ describe('OCA.Files.FileActions tests', function() {
context = actionStub.getCall(0).args[1];
expect(context.dir).toEqual('/somepath');
});
describe('merging', function() {
var $tr;
beforeEach(function() {
var fileData = {
id: 18,
type: 'file',
name: 'testName.txt',
path: '/anotherpath/there',
mimetype: 'text/plain',
size: '1234',
etag: 'a01234c',
mtime: '123456'
};
$tr = fileList.add(fileData);
});
afterEach(function() {
$tr = null;
});
it('copies all actions to target file actions', function() {
var actions1 = new OCA.Files.FileActions();
var actions2 = new OCA.Files.FileActions();
var actionStub1 = sinon.stub();
var actionStub2 = sinon.stub();
actions1.register(
'all',
'Test',
OC.PERMISSION_READ,
OC.imagePath('core', 'actions/test'),
actionStub1
);
actions2.register(
'all',
'Test2',
OC.PERMISSION_READ,
OC.imagePath('core', 'actions/test'),
actionStub2
);
actions2.merge(actions1);
actions2.display($tr.find('td.filename'), true, fileList);
expect($tr.find('.action-test').length).toEqual(1);
expect($tr.find('.action-test2').length).toEqual(1);
$tr.find('.action-test').click();
expect(actionStub1.calledOnce).toEqual(true);
expect(actionStub2.notCalled).toEqual(true);
actionStub1.reset();
$tr.find('.action-test2').click();
expect(actionStub1.notCalled).toEqual(true);
expect(actionStub2.calledOnce).toEqual(true);
});
it('overrides existing actions on merge', function() {
var actions1 = new OCA.Files.FileActions();
var actions2 = new OCA.Files.FileActions();
var actionStub1 = sinon.stub();
var actionStub2 = sinon.stub();
actions1.register(
'all',
'Test',
OC.PERMISSION_READ,
OC.imagePath('core', 'actions/test'),
actionStub1
);
actions2.register(
'all',
'Test', // override
OC.PERMISSION_READ,
OC.imagePath('core', 'actions/test'),
actionStub2
);
actions1.merge(actions2);
actions1.display($tr.find('td.filename'), true, fileList);
expect($tr.find('.action-test').length).toEqual(1);
$tr.find('.action-test').click();
expect(actionStub1.notCalled).toEqual(true);
expect(actionStub2.calledOnce).toEqual(true);
});
it('overrides existing action when calling register after merge', function() {
var actions1 = new OCA.Files.FileActions();
var actions2 = new OCA.Files.FileActions();
var actionStub1 = sinon.stub();
var actionStub2 = sinon.stub();
actions1.register(
'all',
'Test',
OC.PERMISSION_READ,
OC.imagePath('core', 'actions/test'),
actionStub1
);
actions1.merge(actions2);
// late override
actions1.register(
'all',
'Test', // override
OC.PERMISSION_READ,
OC.imagePath('core', 'actions/test'),
actionStub2
);
actions1.display($tr.find('td.filename'), true, fileList);
expect($tr.find('.action-test').length).toEqual(1);
$tr.find('.action-test').click();
expect(actionStub1.notCalled).toEqual(true);
expect(actionStub2.calledOnce).toEqual(true);
});
it('leaves original file actions untouched (clean copy)', function() {
var actions1 = new OCA.Files.FileActions();
var actions2 = new OCA.Files.FileActions();
var actionStub1 = sinon.stub();
var actionStub2 = sinon.stub();
actions1.register(
'all',
'Test',
OC.PERMISSION_READ,
OC.imagePath('core', 'actions/test'),
actionStub1
);
// copy the Test action to actions2
actions2.merge(actions1);
// late override
actions2.register(
'all',
'Test', // override
OC.PERMISSION_READ,
OC.imagePath('core', 'actions/test'),
actionStub2
);
// check if original actions still call the correct handler
actions1.display($tr.find('td.filename'), true, fileList);
expect($tr.find('.action-test').length).toEqual(1);
$tr.find('.action-test').click();
expect(actionStub1.calledOnce).toEqual(true);
expect(actionStub2.notCalled).toEqual(true);
});
});
describe('events', function() {
var clock;
beforeEach(function() {
@@ -204,7 +354,7 @@ describe('OCA.Files.FileActions tests', function() {
it('notifies update event handlers once after multiple changes', function() {
var actionStub = sinon.stub();
var handler = sinon.stub();
FileActions.addUpdateListener(handler);
FileActions.on('registerAction', handler);
FileActions.register(
'all',
'Test',
@@ -224,8 +374,8 @@ describe('OCA.Files.FileActions tests', function() {
it('does not notifies update event handlers after unregistering', function() {
var actionStub = sinon.stub();
var handler = sinon.stub();
FileActions.addUpdateListener(handler);
FileActions.removeUpdateListener(handler);
FileActions.on('registerAction', handler);
FileActions.off('registerAction', handler);
FileActions.register(
'all',
'Test',
+26
View File
@@ -1933,4 +1933,30 @@ describe('OCA.Files.FileList tests', function() {
});
});
});
describe('Handeling errors', function () {
beforeEach(function () {
redirectStub = sinon.stub(OC, 'redirect');
fileList = new OCA.Files.FileList($('#app-content-files'));
});
afterEach(function () {
fileList = undefined;
redirectStub.restore();
});
it('reloads the page on authentication errors', function () {
fileList.reload();
fakeServer.requests[0].respond(
200,
{ 'Content-Type': 'application/json' },
JSON.stringify({
status: 'error',
data: {
'error': 'authentication_error'
}
})
);
expect(redirectStub.calledWith(OC.generateUrl('apps/files'))).toEqual(true);
});
});
});
+1
View File
@@ -15,4 +15,5 @@
<types>
<filesystem/>
</types>
<ocsid>166047</ocsid>
</info>
+1 -1
View File
@@ -1 +1 @@
0.6
0.6.1
+10 -4
View File
@@ -340,7 +340,7 @@ class Hooks {
}
/**
* @brief
* unshare file/folder from a user with whom you shared the file before
*/
public static function postUnshare($params) {
@@ -385,8 +385,10 @@ class Hooks {
// Unshare every user who no longer has access to the file
$delUsers = array_diff($userIds, $sharingUsers);
list($owner, $ownerPath) = $util->getUidAndFilename($path);
// delete share key
Keymanager::delShareKey($view, $delUsers, $path);
Keymanager::delShareKey($view, $delUsers, $ownerPath, $owner);
}
}
@@ -441,7 +443,7 @@ class Hooks {
$ownerOld = self::$renamedFiles[$params['oldpath']]['uid'];
$pathOld = self::$renamedFiles[$params['oldpath']]['path'];
} else {
\OCP\Util::writeLog('Encryption library', "can't get path and owner from the file before it was renamed", \OCP\Util::ERROR);
\OCP\Util::writeLog('Encryption library', "can't get path and owner from the file before it was renamed", \OCP\Util::DEBUG);
return false;
}
@@ -595,6 +597,7 @@ class Hooks {
}
/**
* unmount file from yourself
* remember files/folders which get unmounted
*/
public static function preUmount($params) {
@@ -613,6 +616,9 @@ class Hooks {
'itemType' => $itemType);
}
/**
* unmount file from yourself
*/
public static function postUmount($params) {
if (!isset(self::$umountedFiles[$params[\OC\Files\Filesystem::signal_param_path]])) {
@@ -642,7 +648,7 @@ class Hooks {
// check if the user still has access to the file, otherwise delete share key
$sharingUsers = \OCP\Share::getUsersSharingFile($path, $user);
if (!in_array(\OCP\User::getUser(), $sharingUsers['users'])) {
Keymanager::delShareKey($view, array(\OCP\User::getUser()), $path);
Keymanager::delShareKey($view, array(\OCP\User::getUser()), $path, $user);
}
}
}
+7 -6
View File
@@ -444,17 +444,18 @@ class Keymanager {
/**
* Delete a single user's shareKey for a single file
*
* @param \OC\Files\View $view relative to data/
* @param array $userIds list of users we want to remove
* @param string $filename the owners name of the file for which we want to remove the users relative to data/user/files
* @param string $owner owner of the file
*/
public static function delShareKey(\OC\Files\View $view, $userIds, $filePath) {
public static function delShareKey($view, $userIds, $filename, $owner) {
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
$userId = Helper::getUser($filePath);
$util = new Util($view, $userId);
list($owner, $filename) = $util->getUidAndFilename($filePath);
$util = new Util($view, $owner);
if ($util->isSystemWideMountPoint($filename)) {
$shareKeyPath = \OC\Files\Filesystem::normalizePath('/files_encryption/share-keys/' . $filename);
+2 -2
View File
@@ -225,7 +225,7 @@ class Test_Encryption_Keymanager extends \PHPUnit_Framework_TestCase {
$this->view->file_put_contents('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/subfolder/subsubfolder/file2.user3.shareKey', 'data');
// recursive delete share keys from user1 and user2
Encryption\Keymanager::delShareKey($this->view, array('user1', 'user2', Test_Encryption_Keymanager::TEST_USER), '/folder1/');
Encryption\Keymanager::delShareKey($this->view, array('user1', 'user2', Test_Encryption_Keymanager::TEST_USER), '/folder1/', Test_Encryption_Keymanager::TEST_USER);
// check if share keys from user1 and user2 are deleted
$this->assertFalse($this->view->file_exists(
@@ -274,7 +274,7 @@ class Test_Encryption_Keymanager extends \PHPUnit_Framework_TestCase {
$this->view->file_put_contents('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/existingFile.txt.' . Test_Encryption_Keymanager::TEST_USER . '.shareKey', 'data');
// recursive delete share keys from user1 and user2
Encryption\Keymanager::delShareKey($this->view, array('user1', 'user2', Test_Encryption_Keymanager::TEST_USER), '/folder1/existingFile.txt');
Encryption\Keymanager::delShareKey($this->view, array('user1', 'user2', Test_Encryption_Keymanager::TEST_USER), '/folder1/existingFile.txt', Test_Encryption_Keymanager::TEST_USER);
// check if share keys from user1 and user2 are deleted
$this->assertFalse($this->view->file_exists(
+13 -6
View File
@@ -10,10 +10,17 @@ if ($_POST['isPersonal'] == 'true') {
OCP\JSON::checkAdminUser();
$isPersonal = false;
}
$status = OC_Mount_Config::addMountPoint($_POST['mountPoint'],
$_POST['class'],
$_POST['classOptions'],
$_POST['mountType'],
$_POST['applicable'],
$isPersonal);
$mountPoint = $_POST['mountPoint'];
$oldMountPoint = $_POST['oldMountPoint'];
$class = $_POST['class'];
$options = $_POST['classOptions'];
$type = $_POST['mountType'];
$applicable = $_POST['applicable'];
if ($oldMountPoint and $oldMountPoint !== $mountPoint) {
OC_Mount_Config::removeMountPoint($oldMountPoint, $type, $applicable, $isPersonal);
}
$status = OC_Mount_Config::addMountPoint($mountPoint, $class, $options, $type, $applicable, $isPersonal);
OCP\JSON::success(array('data' => array('message' => $status)));
+1
View File
@@ -10,4 +10,5 @@
<types>
<filesystem/>
</types>
<ocsid>166048</ocsid>
</info>
+1 -1
View File
@@ -1 +1 @@
0.2
0.2.1
+2 -7
View File
@@ -65,7 +65,6 @@
},
reload: function() {
var self = this;
this.showMask();
if (this._reloadCall) {
this._reloadCall.abort();
@@ -78,14 +77,10 @@
type: 'GET',
beforeSend: function(xhr) {
xhr.setRequestHeader('OCS-APIREQUEST', 'true');
},
error: function(result) {
self.reloadCallback(result);
},
success: function(result) {
self.reloadCallback(result);
}
});
var callBack = this.reloadCallback.bind(this);
return this._reloadCall.then(callBack, callBack);
},
reloadCallback: function(result) {
+7 -2
View File
@@ -14,6 +14,7 @@ function updateStatus(statusEl, result){
OC.MountConfig={
saveStorage:function(tr, callback) {
var mountPoint = $(tr).find('.mountPoint input').val();
var oldMountPoint = $(tr).find('.mountPoint input').data('mountpoint');
if (mountPoint == '') {
return false;
}
@@ -80,9 +81,11 @@ OC.MountConfig={
classOptions: classOptions,
mountType: mountType,
applicable: applicable,
isPersonal: isPersonal
isPersonal: isPersonal,
oldMountPoint: oldMountPoint
},
success: function(result) {
$(tr).find('.mountPoint input').data('mountpoint', mountPoint);
status = updateStatus(statusSpan, result);
if (callback) {
callback(status);
@@ -139,9 +142,11 @@ OC.MountConfig={
classOptions: classOptions,
mountType: mountType,
applicable: applicable,
isPersonal: isPersonal
isPersonal: isPersonal,
oldMountPoint: oldMountPoint
},
success: function(result) {
$(tr).find('.mountPoint input').data('mountpoint', mountPoint);
status = updateStatus(statusSpan, result);
if (callback) {
callback(status);
+17 -13
View File
@@ -72,6 +72,12 @@ class AmazonS3 extends \OC\Files\Storage\Common {
sleep($this->timeout);
}
}
private function cleanKey($path) {
if ($path === '.') {
return '/';
}
return $path;
}
public function __construct($params) {
if (!isset($params['key']) || !isset($params['secret']) || !isset($params['bucket'])) {
@@ -118,11 +124,10 @@ class AmazonS3 extends \OC\Files\Storage\Common {
throw new \Exception("Creation of bucket failed.");
}
}
if (!$this->file_exists('.')) {
$result = $this->connection->putObject(array(
'Bucket' => $this->bucket,
'Key' => '.',
'Key' => $this->cleanKey('.'),
'Body' => '',
'ContentType' => 'httpd/unix-directory',
'ContentLength' => 0
@@ -167,7 +172,7 @@ class AmazonS3 extends \OC\Files\Storage\Common {
try {
$result = $this->connection->doesObjectExist(
$this->bucket,
$path
$this->cleanKey($path)
);
} catch (S3Exception $e) {
\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
@@ -261,7 +266,7 @@ class AmazonS3 extends \OC\Files\Storage\Common {
$result = $this->connection->headObject(array(
'Bucket' => $this->bucket,
'Key' => $path
'Key' => $this->cleanKey($path)
));
$stat = array();
@@ -291,8 +296,7 @@ class AmazonS3 extends \OC\Files\Storage\Common {
if ($path != '.') {
$path .= '/';
}
if ($this->connection->doesObjectExist($this->bucket, $path)) {
if ($this->connection->doesObjectExist($this->bucket, $this->cleanKey($path))) {
return 'dir';
}
} catch (S3Exception $e) {
@@ -309,7 +313,7 @@ class AmazonS3 extends \OC\Files\Storage\Common {
try {
$result = $this->connection->deleteObject(array(
'Bucket' => $this->bucket,
'Key' => $path
'Key' => $this->cleanKey($path)
));
$this->testTimeout();
} catch (S3Exception $e) {
@@ -332,7 +336,7 @@ class AmazonS3 extends \OC\Files\Storage\Common {
try {
$result = $this->connection->getObject(array(
'Bucket' => $this->bucket,
'Key' => $path,
'Key' => $this->cleanKey($path),
'SaveAs' => $tmpFile
));
} catch (S3Exception $e) {
@@ -380,7 +384,7 @@ class AmazonS3 extends \OC\Files\Storage\Common {
try {
$result = $this->connection->headObject(array(
'Bucket' => $this->bucket,
'Key' => $path
'Key' => $this->cleanKey($path)
));
} catch (S3Exception $e) {
\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
@@ -407,7 +411,7 @@ class AmazonS3 extends \OC\Files\Storage\Common {
}
$result = $this->connection->copyObject(array(
'Bucket' => $this->bucket,
'Key' => $path,
'Key' => $this->cleanKey($path),
'Metadata' => $metadata,
'CopySource' => $this->bucket . '/' . $path
));
@@ -415,7 +419,7 @@ class AmazonS3 extends \OC\Files\Storage\Common {
} else {
$result = $this->connection->putObject(array(
'Bucket' => $this->bucket,
'Key' => $path,
'Key' => $this->cleanKey($path),
'Metadata' => $metadata
));
$this->testTimeout();
@@ -436,7 +440,7 @@ class AmazonS3 extends \OC\Files\Storage\Common {
try {
$result = $this->connection->copyObject(array(
'Bucket' => $this->bucket,
'Key' => $path2,
'Key' => $this->cleanKey($path2),
'CopySource' => $this->bucket . '/' . $path1
));
$this->testTimeout();
@@ -535,7 +539,7 @@ class AmazonS3 extends \OC\Files\Storage\Common {
try {
$result= $this->connection->putObject(array(
'Bucket' => $this->bucket,
'Key' => self::$tmpFiles[$tmpFile],
'Key' => $this->cleanKey(self::$tmpFiles[$tmpFile]),
'SourceFile' => $tmpFile,
'ContentType' => \OC_Helper::getMimeType($tmpFile),
'ContentLength' => filesize($tmpFile)
@@ -24,6 +24,7 @@
</td>
<td class="mountPoint"><input type="text" name="mountPoint"
value="<?php p(isset($mount['mountpoint']) ? $mount['mountpoint'] : ''); ?>"
data-mountpoint="<?php p(isset($mount['mountpoint']) ? $mount['mountpoint'] : ''); ?>"
placeholder="<?php p($l->t('Folder name')); ?>" /></td>
<?php if (!isset($mount['mountpoint'])): ?>
<td class="backend">
+1
View File
@@ -15,4 +15,5 @@
<files>public.php</files>
<webdav>publicwebdav.php</webdav>
</public>
<ocsid>166050</ocsid>
</info>
+1 -1
View File
@@ -1 +1 @@
0.5.2
0.5.3
+36 -9
View File
@@ -89,21 +89,48 @@ thead {
}
/* within #save */
#remote_address {
margin: 0;
height: 14px;
padding: 6px;
#save .save-form {
position: relative;
}
#save button {
#remote_address {
margin: 0;
width: 130px;
height: 14px;
padding: 6px;
padding-right: 24px;
}
.ie8 #remote_address {
padding-right: 30px;
}
#save #save-button,
#save #save-button-confirm {
margin: 0 5px;
height: 28px;
padding-bottom: 4px;
line-height: 14px;
}
#save .save-form [type="submit"] {
margin: 0 5px;
height: 28px;
padding-bottom: 4px;
#save-button-confirm {
position: absolute;
background-color: transparent;
border: none;
margin: 2px 4px !important;
right: 0;
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)";
filter: alpha(opacity=50);
opacity: .5;
}
.ie8 #save-button-confirm {
margin: 2px 0 !important;
}
#save-button-confirm:hover,
#save-button-confirm:focus {
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";
filter: alpha(opacity=100);
opacity: 1;
}
+40
View File
@@ -92,6 +92,21 @@ OCA.Sharing.App = {
}
},
/**
* Destroy the app
*/
destroy: function() {
OCA.Files.fileActions.off('setDefault.app-sharing', this._onActionsUpdated);
OCA.Files.fileActions.off('registerAction.app-sharing', this._onActionsUpdated);
this.removeSharingIn();
this.removeSharingOut();
this.removeSharingLinks();
this._inFileList = null;
this._outFileList = null;
this._linkFileList = null;
delete this._globalActionsInitialized;
},
_createFileActions: function() {
// inherit file actions from the files app
var fileActions = new OCA.Files.FileActions();
@@ -100,6 +115,14 @@ OCA.Sharing.App = {
fileActions.registerDefaultActions();
fileActions.merge(OCA.Files.fileActions);
if (!this._globalActionsInitialized) {
// in case actions are registered later
this._onActionsUpdated = _.bind(this._onActionsUpdated, this);
OCA.Files.fileActions.on('setDefault.app-sharing', this._onActionsUpdated);
OCA.Files.fileActions.on('registerAction.app-sharing', this._onActionsUpdated);
this._globalActionsInitialized = true;
}
// when the user clicks on a folder, redirect to the corresponding
// folder in the files app instead of opening it directly
fileActions.register('dir', 'Open', OC.PERMISSION_READ, '', function (filename, context) {
@@ -110,6 +133,23 @@ OCA.Sharing.App = {
return fileActions;
},
_onActionsUpdated: function(ev) {
_.each([this._inFileList, this._outFileList, this._linkFileList], function(list) {
if (!list) {
return;
}
if (ev.action) {
list.fileActions.registerAction(ev.action);
} else if (ev.defaultAction) {
list.fileActions.setDefault(
ev.defaultAction.mime,
ev.defaultAction.name
);
}
});
},
_extendFileList: function(fileList) {
// remove size column from summary
fileList.fileSummary.$el.find('.filesize').remove();
+31 -4
View File
@@ -42,13 +42,40 @@
}
};
if (!passwordProtected) {
OC.dialogs.confirm(t('files_sharing', 'Add {name} from {owner}@{remote}', {name: name, owner: owner, remote: remoteClean})
, t('files_sharing','Add Share'), callback, true);
OC.dialogs.confirm(
t(
'files_sharing',
'Do you want to add the remote share {name} from {owner}@{remote}?',
{name: name, owner: owner, remote: remoteClean}
),
t('files_sharing','Remote share'),
callback,
true
).then(this._adjustDialog);
} else {
OC.dialogs.prompt(t('files_sharing', 'Add {name} from {owner}@{remote}', {name: name, owner: owner, remote: remoteClean})
, t('files_sharing','Add Share'), callback, true, t('files_sharing','Password'), true);
OC.dialogs.prompt(
t(
'files_sharing',
'Do you want to add the remote share {name} from {owner}@{remote}?',
{name: name, owner: owner, remote: remoteClean}
),
t('files_sharing','Remote share'),
callback,
true,
t('files_sharing','Remote share password'),
true
).then(this._adjustDialog);
}
};
OCA.Sharing._adjustDialog = function() {
var $dialog = $('.oc-dialog:visible');
var $buttons = $dialog.find('button');
// hack the buttons
$dialog.find('.ui-icon').remove();
$buttons.eq(0).text(t('core', 'Cancel'));
$buttons.eq(1).text(t('core', 'Add remote share'));
};
})();
$(document).ready(function () {
+1 -1
View File
@@ -163,7 +163,7 @@ OCA.Sharing.PublicApp = {
OCA.Sharing.PublicApp._saveToOwnCloud(remote, token, owner, name, isProtected);
});
$('#save > button').click(function () {
$('#save #save-button').click(function () {
$(this).hide();
$('.save-form').css('display', 'inline');
$('#remote_address').focus();
+11 -16
View File
@@ -95,7 +95,6 @@
},
reload: function() {
var self = this;
this.showMask();
if (this._reloadCall) {
this._reloadCall.abort();
@@ -110,14 +109,10 @@
type: 'GET',
beforeSend: function(xhr) {
xhr.setRequestHeader('OCS-APIREQUEST', 'true');
},
error: function(result) {
self.reloadCallback(result);
},
success: function(result) {
self.reloadCallback(result);
}
});
var callBack = this.reloadCallback.bind(this);
return this._reloadCall.then(callBack, callBack);
},
reloadCallback: function(result) {
@@ -166,11 +161,10 @@
}
else {
file.type = 'file';
// force preview retrieval as we don't have mime types,
// the preview endpoint will fall back to the mime type
// icon if no preview exists
file.isPreviewAvailable = true;
file.icon = true;
if (share.isPreviewAvailable) {
file.icon = true;
file.isPreviewAvailable = true;
}
}
file.share = {
id: share.id,
@@ -185,7 +179,9 @@
file.permissions = share.permissions;
}
else {
file.share.targetDisplayName = share.share_with_displayname;
if (share.share_type !== OC.Share.SHARE_TYPE_LINK) {
file.share.targetDisplayName = share.share_with_displayname;
}
file.name = OC.basename(share.path);
file.path = OC.dirname(share.path);
file.permissions = OC.PERMISSION_ALL;
@@ -244,12 +240,11 @@
);
delete data.recipientsCount;
})
// Sort by expected sort comparator
.sortBy(this._sortComparator)
// Finish the chain by getting the result
.value();
return files;
// Sort by expected sort comparator
return files.sort(this._sortComparator);
}
});
+6
View File
@@ -60,6 +60,9 @@ class Api {
foreach ($shares as &$share) {
if ($share['item_type'] === 'file' && isset($share['path'])) {
$share['mimetype'] = \OC_Helper::getFileNameMimeType($share['path']);
if (\OC::$server->getPreviewManager()->isMimeSupported($share['mimetype'])) {
$share['isPreviewAvailable'] = true;
}
}
$newShares[] = $share;
}
@@ -214,6 +217,9 @@ class Api {
foreach ($shares as &$share) {
if ($share['item_type'] === 'file') {
$share['mimetype'] = \OC_Helper::getFileNameMimeType($share['file_target']);
if (\OC::$server->getPreviewManager()->isMimeSupported($share['mimetype'])) {
$share['isPreviewAvailable'] = true;
}
}
}
$result = new \OC_OCS_Result($shares);
+10 -14
View File
@@ -21,47 +21,43 @@
*/
namespace OCA\Files\Share;
use OCA\Files_Sharing\Helper;
class Proxy extends \OC_FileProxy {
/**
* check if the deleted folder contains share mount points and move them
* up to the parent
* check if the deleted folder contains share mount points and unshare them
*
* @param string $path
*/
public function preUnlink($path) {
$this->moveMountPointsUp($path);
$this->unshareChildren($path);
}
/**
* check if the deleted folder contains share mount points and move them
* up to the parent
* check if the deleted folder contains share mount points and unshare them
*
* @param string $path
*/
public function preRmdir($path) {
$this->moveMountPointsUp($path);
$this->unshareChildren($path);
}
/**
* move share mount points up to the parent
* unshare shared items below the deleted folder
*
* @param string $path
*/
private function moveMountPointsUp($path) {
private function unshareChildren($path) {
$view = new \OC\Files\View('/');
// find share mount points within $path and move them up to the parent folder
// before we delete $path
// find share mount points within $path and unmount them
$mountManager = \OC\Files\Filesystem::getMountManager();
$mountedShares = $mountManager->findIn($path);
foreach ($mountedShares as $mount) {
if ($mount->getStorage()->instanceOfStorage('\OC\Files\Storage\Shared')) {
if ($mount->getStorage()->instanceOfStorage('OCA\Files_Sharing\ISharedStorage')) {
$mountPoint = $mount->getMountPoint();
$mountPointName = $mount->getMountPointName();
$target = \OCA\Files_Sharing\Helper::generateUniqueTarget(dirname($path) . '/' . $mountPointName, array(), $view);
$view->rename($mountPoint, $target);
$view->unlink($mountPoint);
}
}
}
+2
View File
@@ -143,8 +143,10 @@ class SharedMount extends Mount implements MoveableMount {
* @return bool
*/
public function removeMount() {
$mountManager = \OC\Files\Filesystem::getMountManager();
$storage = $this->getStorage();
$result = \OCP\Share::unshareFromSelf($storage->getItemType(), $storage->getMountPoint());
$mountManager->removeMount($this->mountPoint);
return $result;
}
+2 -2
View File
@@ -204,8 +204,8 @@ class Shared_Updater {
static public function fixBrokenSharesOnAppUpdate() {
// delete all shares where the original file no longer exists
$findAndRemoveShares = \OC_DB::prepare('DELETE FROM `*PREFIX*share` ' .
'WHERE `file_source` NOT IN ( ' .
'SELECT `fileid` FROM `*PREFIX*filecache` WHERE `item_type` IN (\'file\', \'folder\'))'
'WHERE `item_type` IN (\'file\', \'folder\') ' .
'AND `file_source` NOT IN (SELECT `fileid` FROM `*PREFIX*filecache`)'
);
$findAndRemoveShares->execute(array());
}
-1
View File
@@ -67,7 +67,6 @@ $server->subscribeEvent('beforeMethod', function () use ($server, $objectTree, $
$mountManager = \OC\Files\Filesystem::getMountManager();
$objectTree->init($root, $view, $mountManager);
$server->addPlugin(new OC_Connector_Sabre_AbortedUploadDetectionPlugin($view));
$server->addPlugin(new OC_Connector_Sabre_QuotaPlugin($view));
}, 30); // priority 30: after auth (10) and acl(20), before lock(50) and handling the request
+2 -2
View File
@@ -19,10 +19,10 @@
<div class="header-right">
<span id="details">
<span id="save" data-protected="<?php p($_['protected'])?>" data-owner="<?php p($_['displayName'])?>" data-name="<?php p($_['filename'])?>">
<button><?php p($l->t('Add to your ownCloud')) ?></button>
<button id="save-button"><?php p($l->t('Add to your ownCloud')) ?></button>
<form class="save-form hidden" action="#">
<input type="text" id="remote_address" placeholder="example.com/owncloud"/>
<input type="submit" value="<?php p($l->t('Save')) ?>"/>
<button id="save-button-confirm" class="icon-confirm svg"></button>
</form>
</span>
<a href="<?php p($_['downloadURL']); ?>" id="download" class="button">
+1 -6
View File
@@ -45,12 +45,7 @@ describe('OCA.Sharing.App tests', function() {
fileListOut = App.initSharingOut($('#app-content-sharingout'));
});
afterEach(function() {
App._inFileList = null;
App._outFileList = null;
fileListIn.destroy();
fileListOut.destroy();
fileListIn = null;
fileListOut = null;
App.destroy();
});
describe('initialization', function() {
@@ -499,6 +499,48 @@ describe('OCA.Sharing.FileList tests', function() {
expect($tr.attr('data-permissions')).toEqual('31'); // read and delete
expect($tr.attr('data-mime')).toEqual('text/plain');
expect($tr.attr('data-mtime')).toEqual('11111000');
expect($tr.attr('data-share-recipients')).not.toBeDefined();
expect($tr.attr('data-share-owner')).not.toBeDefined();
expect($tr.attr('data-share-id')).toEqual('7');
expect($tr.find('a.name').attr('href')).toEqual(
OC.webroot +
'/index.php/apps/files/ajax/download.php' +
'?dir=%2Flocal%20path&files=local%20name.txt');
expect($tr.find('.nametext').text().trim()).toEqual('local name.txt');
});
it('does not show virtual token recipient as recipient when password was set', function() {
/* jshint camelcase: false */
var request;
// when a password is set, share_with contains an auth token
ocsResponse.ocs.data[0].share_with = 'abc01234/01234abc';
ocsResponse.ocs.data[0].share_with_displayname = 'abc01234/01234abc';
expect(fakeServer.requests.length).toEqual(1);
request = fakeServer.requests[0];
expect(request.url).toEqual(
OC.linkToOCS('apps/files_sharing/api/v1') +
'shares?format=json&shared_with_me=false'
);
fakeServer.requests[0].respond(
200,
{ 'Content-Type': 'application/json' },
JSON.stringify(ocsResponse)
);
// only renders the link share entry
var $rows = fileList.$el.find('tbody tr');
var $tr = $rows.eq(0);
expect($rows.length).toEqual(1);
expect($tr.attr('data-id')).toEqual('49');
expect($tr.attr('data-type')).toEqual('file');
expect($tr.attr('data-file')).toEqual('local name.txt');
expect($tr.attr('data-path')).toEqual('/local path');
expect($tr.attr('data-size')).not.toBeDefined();
expect($tr.attr('data-permissions')).toEqual('31'); // read and delete
expect($tr.attr('data-mime')).toEqual('text/plain');
expect($tr.attr('data-mtime')).toEqual('11111000');
expect($tr.attr('data-share-recipients')).not.toBeDefined();
expect($tr.attr('data-share-owner')).not.toBeDefined();
expect($tr.attr('data-share-id')).toEqual('7');
expect($tr.find('a.name').attr('href')).toEqual(
+14 -13
View File
@@ -47,7 +47,6 @@ class Test_Files_Sharing_Proxy extends Test_Files_Sharing_Base {
$this->filename = '/share-api-test';
// save file with content
$this->view->file_put_contents($this->filename, $this->data);
$this->view->mkdir($this->folder);
$this->view->mkdir($this->folder . $this->subfolder);
$this->view->mkdir($this->folder . $this->subfolder . $this->subsubfolder);
@@ -56,7 +55,6 @@ class Test_Files_Sharing_Proxy extends Test_Files_Sharing_Base {
}
function tearDown() {
$this->view->unlink($this->filename);
$this->view->deleteAll($this->folder);
self::$tempStorage = null;
@@ -69,30 +67,33 @@ class Test_Files_Sharing_Proxy extends Test_Files_Sharing_Base {
*/
function testpreUnlink() {
$fileInfo1 = \OC\Files\Filesystem::getFileInfo($this->filename);
$fileInfo2 = \OC\Files\Filesystem::getFileInfo($this->folder);
$result = \OCP\Share::shareItem('file', $fileInfo1->getId(), \OCP\Share::SHARE_TYPE_USER, self::TEST_FILES_SHARING_API_USER2, 31);
$this->assertTrue($result);
$result = \OCP\Share::shareItem('folder', $fileInfo2->getId(), \OCP\Share::SHARE_TYPE_USER, self::TEST_FILES_SHARING_API_USER2, 31);
$this->assertTrue($result);
self::loginHelper(self::TEST_FILES_SHARING_API_USER2);
// move shared folder to 'localDir' and rename it, so that it uses the same
// name as the shared file
// one folder should be shared with the user
$sharedFolders = \OCP\Share::getItemsSharedWith('folder');
$this->assertSame(1, count($sharedFolders));
// move shared folder to 'localDir'
\OC\Files\Filesystem::mkdir('localDir');
$result = \OC\Files\Filesystem::rename($this->folder, '/localDir/' . $this->filename);
$result = \OC\Files\Filesystem::rename($this->folder, '/localDir/' . $this->folder);
$this->assertTrue($result);
\OC\Files\Filesystem::unlink('localDir');
self::loginHelper(self::TEST_FILES_SHARING_API_USER2);
// after we deleted 'localDir' the share should be moved up to the root and be
// renamed to "filename (2)"
$this->assertTrue(\OC\Files\Filesystem::file_exists($this->filename));
$this->assertTrue(\OC\Files\Filesystem::file_exists($this->filename . ' (2)' ));
// after the parent directory was deleted the share should be unshared
$sharedFolders = \OCP\Share::getItemsSharedWith('folder');
$this->assertTrue(empty($sharedFolders));
self::loginHelper(self::TEST_FILES_SHARING_API_USER1);
// the folder for the owner should still exists
$this->assertTrue(\OC\Files\Filesystem::file_exists($this->folder));
}
}
+22 -8
View File
@@ -87,13 +87,18 @@ class Test_Files_Sharing_Update_Routine extends Test_Files_Sharing_Base {
/**
* @medium
*/
function testRemoveBrokenShares() {
function testRemoveBrokenFileShares() {
$this->prepareFileCache();
// check if there are just 3 shares (see setUp - precondition: empty table)
$countShares = \OC_DB::prepare('SELECT COUNT(`id`) FROM `*PREFIX*share`');
$result = $countShares->execute()->fetchOne();
// check if there are just 4 shares (see setUp - precondition: empty table)
$countAllShares = \OC_DB::prepare('SELECT COUNT(`id`) FROM `*PREFIX*share`');
$result = $countAllShares->execute()->fetchOne();
$this->assertEquals(4, $result);
// check if there are just 3 file shares (see setUp - precondition: empty table)
$countFileShares = \OC_DB::prepare('SELECT COUNT(`id`) FROM `*PREFIX*share` WHERE `item_type` IN (\'file\', \'folder\')');
$result = $countFileShares->execute()->fetchOne();
$this->assertEquals(3, $result);
// check if there are just 2 items (see setUp - precondition: empty table)
@@ -105,19 +110,23 @@ class Test_Files_Sharing_Update_Routine extends Test_Files_Sharing_Base {
\OC\Files\Cache\Shared_Updater::fixBrokenSharesOnAppUpdate();
// check if there are just 2 shares (one gets killed by the code as there is no filecache entry for this)
$countShares = \OC_DB::prepare('SELECT COUNT(`id`) FROM `*PREFIX*share`');
$result = $countShares->execute()->fetchOne();
$result = $countFileShares->execute()->fetchOne();
$this->assertEquals(2, $result);
// check if the share of file '200' is removed as there is no entry for this in filecache table
$countShares = \OC_DB::prepare('SELECT COUNT(`id`) FROM `*PREFIX*share` WHERE `file_source` = 200');
$result = $countShares->execute()->fetchOne();
$countFileShares = \OC_DB::prepare('SELECT COUNT(`id`) FROM `*PREFIX*share` WHERE `file_source` = 200');
$result = $countFileShares->execute()->fetchOne();
$this->assertEquals(0, $result);
// check if there are just 2 items
$countItems = \OC_DB::prepare('SELECT COUNT(`fileid`) FROM `*PREFIX*filecache`');
$result = $countItems->execute()->fetchOne();
$this->assertEquals(2, $result);
// the calendar share survived
$countOtherShares = \OC_DB::prepare('SELECT COUNT(`id`) FROM `*PREFIX*share` WHERE `item_source` = \'999\'');
$result = $countOtherShares->execute()->fetchOne();
$this->assertEquals(1, $result);
}
/**
@@ -228,6 +237,11 @@ class Test_Files_Sharing_Update_Routine extends Test_Files_Sharing_Base {
$addShares->execute(array($fileIds[0]));
$addShares->execute(array(200)); // id of "deleted" file
$addShares->execute(array($fileIds[1]));
// add a few unrelated shares, calendar share that must be left untouched
$addShares = \OC_DB::prepare('INSERT INTO `*PREFIX*share` (`item_source`, `item_type`, `uid_owner`) VALUES (?, \'calendar\', 1)');
// the number is used as item_source
$addShares->execute(array(999));
}
}
+7 -7
View File
@@ -57,7 +57,7 @@ class Test_Files_Sharing_Updater extends Test_Files_Sharing_Base {
/**
* test deletion of a folder which contains share mount points. Share mount
* points should move up to the parent before the folder gets deleted so
* points should be unshared before the folder gets deleted so
* that the mount point doesn't end up at the trash bin
*/
function testDeleteParentFolder() {
@@ -78,6 +78,9 @@ class Test_Files_Sharing_Updater extends Test_Files_Sharing_Base {
// check if user2 can see the shared folder
$this->assertTrue($view->file_exists($this->folder));
$foldersShared = \OCP\Share::getItemsSharedWith('folder');
$this->assertSame(1, count($foldersShared));
$view->mkdir("localFolder");
$view->file_put_contents("localFolder/localFile.txt", "local file");
@@ -91,8 +94,9 @@ class Test_Files_Sharing_Updater extends Test_Files_Sharing_Base {
$this->loginHelper(self::TEST_FILES_SHARING_API_USER2);
// mount point should move up again
$this->assertTrue($view->file_exists($this->folder));
// shared folder should be unshared
$foldersShared = \OCP\Share::getItemsSharedWith('folder');
$this->assertTrue(empty($foldersShared));
// trashbin should contain the local file but not the mount point
$rootView = new \OC\Files\View('/' . self::TEST_FILES_SHARING_API_USER2);
@@ -109,10 +113,6 @@ class Test_Files_Sharing_Updater extends Test_Files_Sharing_Base {
if ($status === false) {
\OC_App::disable('files_trashbin');
}
// cleanup
$this->loginHelper(self::TEST_FILES_SHARING_API_USER1);
$result = \OCP\Share::unshare('folder', $fileinfo->getId(), \OCP\Share::SHARE_TYPE_USER, self::TEST_FILES_SHARING_API_USER2);
$this->assertTrue($result);
}
/**
+1
View File
@@ -24,4 +24,5 @@
<types>
<filesystem/>
</types>
<ocsid>166052</ocsid>
</info>
+1 -1
View File
@@ -1 +1 @@
0.6.1
0.6.2
+1
View File
@@ -29,4 +29,5 @@
<filesystem/>
</types>
<default_enable/>
<ocsid>166053</ocsid>
</info>
+3 -3
View File
@@ -5,11 +5,11 @@
* See the COPYING-README file.
*/
// Register with the capabilities API
OC_API::register('get', '/cloud/capabilities', array('OCA\Files_Versions\Capabilities', 'getCapabilities'), 'files_versions', OC_API::USER_AUTH);
/** @var $this \OCP\Route\IRouter */
$this->create('core_ajax_versions_preview', '/preview')->action(
function() {
require_once __DIR__ . '/../ajax/preview.php';
});
// Register with the capabilities API
OC_API::register('get', '/cloud/capabilities', array('OCA\Files_Versions\Capabilities', 'getCapabilities'), 'files_versions', OC_API::USER_AUTH);
+1 -1
View File
@@ -1 +1 @@
1.0.4
1.0.5
+1
View File
@@ -17,4 +17,5 @@
<documentation>
<admin>http://doc.owncloud.org/server/6.0/go.php?to=admin-ldap</admin>
</documentation>
<ocsid>166061</ocsid>
</info>
+1 -1
View File
@@ -1 +1 @@
0.4.2
0.4.3
+198 -37
View File
@@ -50,20 +50,29 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface {
if(!$this->enabled) {
return false;
}
if($this->access->connection->isCached('inGroup'.$uid.':'.$gid)) {
return $this->access->connection->getFromCache('inGroup'.$uid.':'.$gid);
$cacheKey = 'inGroup'.$uid.':'.$gid;
if($this->access->connection->isCached($cacheKey)) {
return $this->access->connection->getFromCache($cacheKey);
}
$dn_user = $this->access->username2dn($uid);
$dn_group = $this->access->groupname2dn($gid);
$userDN = $this->access->username2dn($uid);
$groupDN = $this->access->groupname2dn($gid);
// just in case
if(!$dn_group || !$dn_user) {
$this->access->connection->writeToCache('inGroup'.$uid.':'.$gid, false);
if(!$groupDN || !$userDN) {
$this->access->connection->writeToCache($cacheKey, false);
return false;
}
//check primary group first
if($gid === $this->getUserPrimaryGroup($userDN)) {
$this->access->connection->writeToCache($cacheKey, true);
return true;
}
//usually, LDAP attributes are said to be case insensitive. But there are exceptions of course.
$members = array_keys($this->_groupMembers($dn_group));
$members = array_keys($this->_groupMembers($groupDN));
if(!$members) {
$this->access->connection->writeToCache('inGroup'.$uid.':'.$gid, false);
$this->access->connection->writeToCache($cacheKey, false);
return false;
}
@@ -82,8 +91,8 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface {
$members = $dns;
}
$isInGroup = in_array($dn_user, $members);
$this->access->connection->writeToCache('inGroup'.$uid.':'.$gid, $isInGroup);
$isInGroup = in_array($userDN, $members);
$this->access->connection->writeToCache($cacheKey, $isInGroup);
return $isInGroup;
}
@@ -91,6 +100,7 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface {
/**
* @param string $dnGroup
* @param array|null &$seen
* @return array|mixed|null
*/
private function _groupMembers($dnGroup, &$seen = null) {
if ($seen === null) {
@@ -125,6 +135,125 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface {
return $allMembers;
}
/**
* translates a primary group ID into an ownCloud internal name
* @param string $gid as given by primaryGroupID on AD
* @param string $dn a DN that belongs to the same domain as the group
* @return string|bool
*/
public function primaryGroupID2Name($gid, $dn) {
$cacheKey = 'primaryGroupIDtoName';
if($this->access->connection->isCached($cacheKey)) {
$groupNames = $this->access->connection->getFromCache($cacheKey);
if(isset($groupNames[$gid])) {
return $groupNames[$gid];
}
}
$domainObjectSid = $this->access->getSID($dn);
if($domainObjectSid === false) {
return false;
}
//we need to get the DN from LDAP
$filter = $this->access->combineFilterWithAnd(array(
$this->access->connection->ldapGroupFilter,
'objectsid=' . $domainObjectSid . '-' . $gid
));
$result = $this->access->searchGroups($filter, array('dn'), 1);
if(empty($result)) {
return false;
}
$dn = $result[0];
//and now the group name
//NOTE once we have separate ownCloud group IDs and group names we can
//directly read the display name attribute instead of the DN
$name = $this->access->dn2groupname($dn);
$this->access->connection->writeToCache($cacheKey, $name);
return $name;
}
/**
* returns the entry's primary group ID
* @param string $dn
* @param string $attribute
* @return string|bool
*/
private function getEntryGroupID($dn, $attribute) {
$value = $this->access->readAttribute($dn, $attribute);
if(is_array($value) && !empty($value)) {
return $value[0];
}
return false;
}
/**
* returns the group's primary ID
* @param string $dn
* @return string|bool
*/
public function getGroupPrimaryGroupID($dn) {
return $this->getEntryGroupID($dn, 'primaryGroupToken');
}
/**
* returns the user's primary group ID
* @param string $dn
* @return string|bool
*/
public function getUserPrimaryGroupIDs($dn) {
return $this->getEntryGroupID($dn, 'primaryGroupID');
}
/**
* returns a list of users that have the given group as primary group
*
* @param string $groupDN
* @param $limit
* @param int $offset
* @return string[]
*/
public function getUsersInPrimaryGroup($groupDN, $limit = -1, $offset = 0) {
$groupID = $this->getGroupPrimaryGroupID($groupDN);
if($groupID === false) {
return array();
}
$filter = $this->access->combineFilterWithAnd(array(
$this->access->connection->ldapUserFilter,
'primaryGroupID=' . $groupID
));
$users = $this->access->fetchListOfUsers(
$filter,
array($this->access->connection->ldapUserDisplayName, 'dn'),
$limit,
$offset
);
return $users;
}
/**
* gets the primary group of a user
* @param string $dn
* @return string
*/
public function getUserPrimaryGroup($dn) {
$groupID = $this->getUserPrimaryGroupIDs($dn);
if($groupID !== false) {
$groupName = $this->primaryGroupID2Name($groupID, $dn);
if($groupName !== false) {
return $groupName;
}
}
return false;
}
/**
* Get all groups a user belongs to
* @param string $uid Name of the user
@@ -161,7 +290,14 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface {
}
$groups = array_values($this->getGroupsByMember($uid));
$groups = array_unique($this->access->ownCloudGroupNames($groups), SORT_LOCALE_STRING);
$groups = $this->access->ownCloudGroupNames($groups);
$primaryGroup = $this->getUserPrimaryGroup($userDN);
if($primaryGroup !== false) {
$groups[] = $primaryGroup;
}
$groups = array_unique($groups, SORT_LOCALE_STRING);
$this->access->connection->writeToCache($cacheKey, $groups);
return $groups;
@@ -170,6 +306,7 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface {
/**
* @param string $dn
* @param array|null &$seen
* @return array
*/
private function getGroupsByMember($dn, &$seen = null) {
if ($seen === null) {
@@ -205,6 +342,11 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface {
/**
* get a list of all users in a group
*
* @param string $gid
* @param string $search
* @param int $limit
* @param int $offset
* @return array with user ids
*/
public function usersInGroup($gid, $search = '', $limit = -1, $offset = 0) {
@@ -214,9 +356,9 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface {
if(!$this->groupExists($gid)) {
return array();
}
$cachekey = 'usersInGroup-'.$gid.'-'.$search.'-'.$limit.'-'.$offset;
$cacheKey = 'usersInGroup-'.$gid.'-'.$search.'-'.$limit.'-'.$offset;
// check for cache of the exact query
$groupUsers = $this->access->connection->getFromCache($cachekey);
$groupUsers = $this->access->connection->getFromCache($cacheKey);
if(!is_null($groupUsers)) {
return $groupUsers;
}
@@ -225,7 +367,7 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface {
$groupUsers = $this->access->connection->getFromCache('usersInGroup-'.$gid.'-'.$search);
if(!is_null($groupUsers)) {
$groupUsers = array_slice($groupUsers, $offset, $limit);
$this->access->connection->writeToCache($cachekey, $groupUsers);
$this->access->connection->writeToCache($cacheKey, $groupUsers);
return $groupUsers;
}
@@ -235,14 +377,14 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface {
$groupDN = $this->access->groupname2dn($gid);
if(!$groupDN) {
// group couldn't be found, return empty resultset
$this->access->connection->writeToCache($cachekey, array());
$this->access->connection->writeToCache($cacheKey, array());
return array();
}
$members = array_keys($this->_groupMembers($groupDN));
if(!$members) {
//in case users could not be retrieved, return empty resultset
$this->access->connection->writeToCache($cachekey, array());
//in case users could not be retrieved, return empty result set
$this->access->connection->writeToCache($cacheKey, array());
return array();
}
@@ -250,7 +392,7 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface {
$isMemberUid = (strtolower($this->access->connection->ldapGroupMemberAssocAttr) === 'memberuid');
foreach($members as $member) {
if($isMemberUid) {
//we got uids, need to get their DNs to 'tranlsate' them to usernames
//we got uids, need to get their DNs to 'translate' them to user names
$filter = $this->access->combineFilterWithAnd(array(
\OCP\Util::mb_str_replace('%uid', $member,
$this->access->connection->ldapLoginFilter, 'UTF-8'),
@@ -276,10 +418,16 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface {
}
}
}
natsort($groupUsers);
$this->access->connection->writeToCache('usersInGroup-'.$gid.'-'.$search, $groupUsers);
$groupUsers = array_slice($groupUsers, $offset, $limit);
$this->access->connection->writeToCache($cachekey, $groupUsers);
//and get users that have the group as primary
$primaryUsers = $this->getUsersInPrimaryGroup($groupDN, $limit, $offset);
$groupUsers = array_unique(array_merge($groupUsers, $primaryUsers));
$this->access->connection->writeToCache($cacheKey, $groupUsers);
return $groupUsers;
}
@@ -291,32 +439,32 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface {
* @return int|bool
*/
public function countUsersInGroup($gid, $search = '') {
$cachekey = 'countUsersInGroup-'.$gid.'-'.$search;
$cacheKey = 'countUsersInGroup-'.$gid.'-'.$search;
if(!$this->enabled || !$this->groupExists($gid)) {
return false;
}
$groupUsers = $this->access->connection->getFromCache($cachekey);
$groupUsers = $this->access->connection->getFromCache($cacheKey);
if(!is_null($groupUsers)) {
return $groupUsers;
}
$groupDN = $this->access->groupname2dn($gid);
if(!$groupDN) {
// group couldn't be found, return empty resultset
$this->access->connection->writeToCache($cachekey, false);
// group couldn't be found, return empty result set
$this->access->connection->writeToCache($cacheKey, false);
return false;
}
$members = array_keys($this->_groupMembers($groupDN));
if(!$members) {
//in case users could not be retrieved, return empty resultset
$this->access->connection->writeToCache($cachekey, false);
//in case users could not be retrieved, return empty result set
$this->access->connection->writeToCache($cacheKey, false);
return false;
}
if(empty($search)) {
$groupUsers = count($members);
$this->access->connection->writeToCache($cachekey, $groupUsers);
$this->access->connection->writeToCache($cacheKey, $groupUsers);
return $groupUsers;
}
$isMemberUid =
@@ -334,7 +482,7 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface {
$groupUsers = array();
foreach($members as $member) {
if($isMemberUid) {
//we got uids, need to get their DNs to 'tranlsate' them to usernames
//we got uids, need to get their DNs to 'translate' them to user names
$filter = $this->access->combineFilterWithAnd(array(
\OCP\Util::mb_str_replace('%uid', $member,
$this->access->connection->ldapLoginFilter, 'UTF-8'),
@@ -359,11 +507,19 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface {
}
}
//and get users that have the group as primary
$primaryUsers = $this->getUsersInPrimaryGroup($groupDN);
$groupUsers = array_unique(array_merge($groupUsers, $primaryUsers));
return count($groupUsers);
}
/**
* get a list of all groups
*
* @param string $search
* @param $limit
* @param int $offset
* @return array with group names
*
* Returns a list with all groups (used by getGroups)
@@ -372,11 +528,11 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface {
if(!$this->enabled) {
return array();
}
$cachekey = 'getGroups-'.$search.'-'.$limit.'-'.$offset;
$cacheKey = 'getGroups-'.$search.'-'.$limit.'-'.$offset;
//Check cache before driving unnecessary searches
\OCP\Util::writeLog('user_ldap', 'getGroups '.$cachekey, \OCP\Util::DEBUG);
$ldap_groups = $this->access->connection->getFromCache($cachekey);
\OCP\Util::writeLog('user_ldap', 'getGroups '.$cacheKey, \OCP\Util::DEBUG);
$ldap_groups = $this->access->connection->getFromCache($cacheKey);
if(!is_null($ldap_groups)) {
return $ldap_groups;
}
@@ -397,26 +553,30 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface {
$offset);
$ldap_groups = $this->access->ownCloudGroupNames($ldap_groups);
$this->access->connection->writeToCache($cachekey, $ldap_groups);
$this->access->connection->writeToCache($cacheKey, $ldap_groups);
return $ldap_groups;
}
/**
* get a list of all groups using a paged search
*
* @param string $search
* @param int $limit
* @param int $offset
* @return array with group names
*
* Returns a list with all groups
* Uses a paged search if available to override a
* server side search limit.
* (active directory has a limit of 1000 by default)
* Uses a paged search if available to override a
* server side search limit.
* (active directory has a limit of 1000 by default)
*/
public function getGroups($search = '', $limit = -1, $offset = 0) {
if(!$this->enabled) {
return array();
}
$pagingsize = $this->access->connection->ldapPagingSize;
$pagingSize = $this->access->connection->ldapPagingSize;
if ((! $this->access->connection->hasPagedResultSupport)
|| empty($pagingsize)) {
|| empty($pagingSize)) {
return $this->getGroupsChunk($search, $limit, $offset);
}
$maxGroups = 100000; // limit max results (just for safety reasons)
@@ -428,7 +588,7 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface {
$chunkOffset = $offset;
$allGroups = array();
while ($chunkOffset < $overallLimit) {
$chunkLimit = min($pagingsize, $overallLimit - $chunkOffset);
$chunkLimit = min($pagingSize, $overallLimit - $chunkOffset);
$ldapGroups = $this->getGroupsChunk($search, $chunkLimit, $chunkOffset);
$nread = count($ldapGroups);
\OCP\Util::writeLog('user_ldap', 'getGroups('.$search.'): read '.$nread.' at offset '.$chunkOffset.' (limit: '.$chunkLimit.')', \OCP\Util::DEBUG);
@@ -445,6 +605,7 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface {
/**
* @param string $group
* @return bool
*/
public function groupMatchesFilter($group) {
return (strripos($group, $this->groupSearch) !== false);
+89 -5
View File
@@ -28,6 +28,9 @@ namespace OCA\user_ldap\lib;
* @package OCA\user_ldap\lib
*/
class Access extends LDAPUtility implements user\IUserTools {
/**
* @var \OCA\user_ldap\lib\Connection
*/
public $connection;
public $userManager;
//never ever check this var directly, always use getPagedSearchResultState
@@ -61,8 +64,8 @@ class Access extends LDAPUtility implements user\IUserTools {
/**
* reads a given attribute for an LDAP record identified by a DN
* @param $dn the record in question
* @param $attr the attribute that shall be retrieved
* @param string $dn the record in question
* @param string $attr the attribute that shall be retrieved
* if empty, just check the record's existence
* @param string $filter
* @return array|false an array of values on success or an empty
@@ -180,6 +183,33 @@ class Access extends LDAPUtility implements user\IUserTools {
return $dn;
}
/**
* returns a DN-string that is cleaned from not domain parts, e.g.
* cn=foo,cn=bar,dc=foobar,dc=server,dc=org
* becomes dc=foobar,dc=server,dc=org
* @param string $dn
* @return string
*/
public function getDomainDNFromDN($dn) {
$allParts = $this->ldap->explodeDN($dn, 0);
if($allParts === false) {
//not a valid DN
return '';
}
$domainParts = array();
$dcFound = false;
foreach($allParts as $part) {
if(!$dcFound && strpos($part, 'dc=') === 0) {
$dcFound = true;
}
if($dcFound) {
$domainParts[] = $part;
}
}
$domainDN = implode(',', $domainParts);
return $domainDN;
}
/**
* gives back the database table for the query
* @param bool $isUser
@@ -534,7 +564,7 @@ class Access extends LDAPUtility implements user\IUserTools {
if(!\OC_Group::groupExists($altName)) {
return $altName;
}
$altName = $name . '_' . $lastNo + $attempts;
$altName = $name . '_' . ($lastNo + $attempts);
$attempts++;
}
return false;
@@ -581,6 +611,7 @@ class Access extends LDAPUtility implements user\IUserTools {
/**
* @param boolean $isUsers
* @return array
*/
private function mappedComponents($isUsers) {
$table = $this->getMapTable($isUsers);
@@ -834,7 +865,7 @@ class Access extends LDAPUtility implements user\IUserTools {
private function count($filter, $base, $attr = null, $limit = null, $offset = null, $skipHandling = false) {
\OCP\Util::writeLog('user_ldap', 'Count filter: '.print_r($filter, true), \OCP\Util::DEBUG);
if(is_null($limit)) {
if(is_null($limit) || $limit <= 0) {
$limit = intval($this->connection->ldapPagingSize);
}
@@ -894,6 +925,10 @@ class Access extends LDAPUtility implements user\IUserTools {
* @return array with the search result
*/
private function search($filter, $base, $attr = null, $limit = null, $offset = null, $skipHandling = false) {
if($limit <= 0) {
//otherwise search will fail
$limit = null;
}
$search = $this->executeSearch($filter, $base, $attr, $limit, $offset);
if($search === false) {
return array();
@@ -908,7 +943,7 @@ class Access extends LDAPUtility implements user\IUserTools {
$this->processPagedSearchStatus($sr, $filter, $base, 1, $limit,
$offset, $pagedSearchOK,
$skipHandling);
return;
return array();
}
// Do the server-side sorting
@@ -1232,6 +1267,55 @@ class Access extends LDAPUtility implements user\IUserTools {
return strtoupper($hex_guid_to_guid_str);
}
/**
* gets a SID of the domain of the given dn
* @param string $dn
* @return string|bool
*/
public function getSID($dn) {
$domainDN = $this->getDomainDNFromDN($dn);
$cacheKey = 'getSID-'.$domainDN;
if($this->connection->isCached($cacheKey)) {
return $this->connection->getFromCache($cacheKey);
}
$objectSid = $this->readAttribute($domainDN, 'objectsid');
if(!is_array($objectSid) || empty($objectSid)) {
$this->connection->writeToCache($cacheKey, false);
return false;
}
$domainObjectSid = $this->convertSID2Str($objectSid[0]);
$this->connection->writeToCache($cacheKey, $domainObjectSid);
return $domainObjectSid;
}
/**
* converts a binary SID into a string representation
* @param string $sid
* @return string
* @link http://blogs.freebsdish.org/tmclaugh/2010/07/21/finding-a-users-primary-group-in-ad/#comment-2855
*/
public function convertSID2Str($sid) {
try {
$srl = ord($sid[0]);
$numberSubID = ord($sid[1]);
$x = substr($sid, 2, 6);
$h = unpack('N', "\x0\x0" . substr($x,0,2));
$l = unpack('N', substr($x,2,6));
$iav = bcadd(bcmul($h[1], bcpow(2,32)), $l[1]);
$subIDs = array();
for ($i=0; $i<$numberSubID; $i++) {
$subID = unpack('V', substr($sid, 8+4*$i, 4));
$subIDs[] = $subID[1];
}
} catch (\Exception $e) {
return '';
}
return sprintf('S-%d-%d-%s', $srl, $iav, implode('-', $subIDs));
}
/**
* converts a stored DN so it can be used as base parameter for LDAP queries, internally we store them for usage in LDAP filters
* @param string $dn the DN
+7
View File
@@ -23,6 +23,13 @@
namespace OCA\user_ldap\lib;
//magic properties (incomplete)
/**
* responsible for LDAP connections in context with the provided configuration
* @property string ldapUserFilter
* @property string ldapUserDisplayName
* @property boolean hasPagedResultSupport
*/
class Connection extends LDAPUtility {
private $ldapConnectionRes = null;
private $configPrefix;
+9
View File
@@ -89,6 +89,15 @@ interface ILDAPWrapper {
*/
public function error($link);
/**
* Splits DN into its component parts
* @param string $dn
* @param int @withAttrib
* @return array|false
* @link http://www.php.net/manual/en/function.ldap-explode-dn.php
*/
public function explodeDN($dn, $withAttrib);
/**
* Return first result id
* @param resource $link LDAP link resource
+11
View File
@@ -98,6 +98,17 @@ class LDAP implements ILDAPWrapper {
return $this->invokeLDAPMethod('error', $link);
}
/**
* Splits DN into its component parts
* @param string $dn
* @param int @withAttrib
* @return array|false
* @link http://www.php.net/manual/en/function.ldap-explode-dn.php
*/
public function explodeDN($dn, $withAttrib) {
return $this->invokeLDAPMethod('ldap_explode_dn', $dn, $withAttrib);
}
/**
* @param LDAP $link
* @param LDAP $result
+51 -1
View File
@@ -77,4 +77,54 @@ class Test_Access extends \PHPUnit_Framework_TestCase {
$expected = 'foo\\\\*bar';
$this->assertTrue($expected === $access->escapeFilterPart($input));
}
}
public function testConvertSID2StrSuccess() {
list($lw, $con, $um) = $this->getConnecterAndLdapMock();
$access = new Access($con, $lw, $um);
$sidBinary = file_get_contents(__DIR__ . '/data/sid.dat');
$sidExpected = 'S-1-5-21-249921958-728525901-1594176202';
$this->assertSame($sidExpected, $access->convertSID2Str($sidBinary));
}
public function testConvertSID2StrInputError() {
list($lw, $con, $um) = $this->getConnecterAndLdapMock();
$access = new Access($con, $lw, $um);
$sidIllegal = 'foobar';
$sidExpected = '';
$this->assertSame($sidExpected, $access->convertSID2Str($sidIllegal));
}
public function testGetDomainDNFromDNSuccess() {
list($lw, $con, $um) = $this->getConnecterAndLdapMock();
$access = new Access($con, $lw, $um);
$inputDN = 'uid=zaphod,cn=foobar,dc=my,dc=server,dc=com';
$domainDN = 'dc=my,dc=server,dc=com';
$lw->expects($this->once())
->method('explodeDN')
->with($inputDN, 0)
->will($this->returnValue(explode(',', $inputDN)));
$this->assertSame($domainDN, $access->getDomainDNFromDN($inputDN));
}
public function testGetDomainDNFromDNError() {
list($lw, $con, $um) = $this->getConnecterAndLdapMock();
$access = new Access($con, $lw, $um);
$inputDN = 'foobar';
$expected = '';
$lw->expects($this->once())
->method('explodeDN')
->with($inputDN, 0)
->will($this->returnValue(false));
$this->assertSame($expected, $access->getDomainDNFromDN($inputDN));
}
}
Binary file not shown.
+151 -1
View File
@@ -95,6 +95,10 @@ class Test_Group_Ldap extends \PHPUnit_Framework_TestCase {
->method('groupname2dn')
->will($this->returnValue('cn=group,dc=foo,dc=bar'));
$access->expects($this->any())
->method('fetchListOfUsers')
->will($this->returnValue(array()));
$access->expects($this->any())
->method('readAttribute')
->will($this->returnCallback(function($name) {
@@ -111,7 +115,9 @@ class Test_Group_Ldap extends \PHPUnit_Framework_TestCase {
$access->expects($this->any())
->method('dn2username')
->will($this->returnValue('foobar'));
->will($this->returnCallback(function() {
return 'foobar' . \OCP\Util::generateRandomBytes(7);
}));
$groupBackend = new GroupLDAP($access);
$users = $groupBackend->countUsersInGroup('group', '3');
@@ -119,4 +125,148 @@ class Test_Group_Ldap extends \PHPUnit_Framework_TestCase {
$this->assertSame(2, $users);
}
public function testPrimaryGroupID2NameSuccess() {
$access = $this->getAccessMock();
$this->enableGroups($access);
$userDN = 'cn=alice,cn=foo,dc=barfoo,dc=bar';
$access->expects($this->once())
->method('getSID')
->with($userDN)
->will($this->returnValue('S-1-5-21-249921958-728525901-1594176202'));
$access->expects($this->once())
->method('searchGroups')
->will($this->returnValue(array('cn=foo,dc=barfoo,dc=bar')));
$access->expects($this->once())
->method('dn2groupname')
->with('cn=foo,dc=barfoo,dc=bar')
->will($this->returnValue('MyGroup'));
$groupBackend = new GroupLDAP($access);
$group = $groupBackend->primaryGroupID2Name('3117', $userDN);
$this->assertSame('MyGroup', $group);
}
public function testPrimaryGroupID2NameNoSID() {
$access = $this->getAccessMock();
$this->enableGroups($access);
$userDN = 'cn=alice,cn=foo,dc=barfoo,dc=bar';
$access->expects($this->once())
->method('getSID')
->with($userDN)
->will($this->returnValue(false));
$access->expects($this->never())
->method('searchGroups');
$access->expects($this->never())
->method('dn2groupname');
$groupBackend = new GroupLDAP($access);
$group = $groupBackend->primaryGroupID2Name('3117', $userDN);
$this->assertSame(false, $group);
}
public function testPrimaryGroupID2NameNoGroup() {
$access = $this->getAccessMock();
$this->enableGroups($access);
$userDN = 'cn=alice,cn=foo,dc=barfoo,dc=bar';
$access->expects($this->once())
->method('getSID')
->with($userDN)
->will($this->returnValue('S-1-5-21-249921958-728525901-1594176202'));
$access->expects($this->once())
->method('searchGroups')
->will($this->returnValue(array()));
$access->expects($this->never())
->method('dn2groupname');
$groupBackend = new GroupLDAP($access);
$group = $groupBackend->primaryGroupID2Name('3117', $userDN);
$this->assertSame(false, $group);
}
public function testPrimaryGroupID2NameNoName() {
$access = $this->getAccessMock();
$this->enableGroups($access);
$userDN = 'cn=alice,cn=foo,dc=barfoo,dc=bar';
$access->expects($this->once())
->method('getSID')
->with($userDN)
->will($this->returnValue('S-1-5-21-249921958-728525901-1594176202'));
$access->expects($this->once())
->method('searchGroups')
->will($this->returnValue(array('cn=foo,dc=barfoo,dc=bar')));
$access->expects($this->once())
->method('dn2groupname')
->will($this->returnValue(false));
$groupBackend = new GroupLDAP($access);
$group = $groupBackend->primaryGroupID2Name('3117', $userDN);
$this->assertSame(false, $group);
}
public function testGetEntryGroupIDValue() {
//tests getEntryGroupID via getGroupPrimaryGroupID
//which is basically identical to getUserPrimaryGroupIDs
$access = $this->getAccessMock();
$this->enableGroups($access);
$dn = 'cn=foobar,cn=foo,dc=barfoo,dc=bar';
$attr = 'primaryGroupToken';
$access->expects($this->once())
->method('readAttribute')
->with($dn, $attr)
->will($this->returnValue(array('3117')));
$groupBackend = new GroupLDAP($access);
$gid = $groupBackend->getGroupPrimaryGroupID($dn);
$this->assertSame('3117', $gid);
}
public function testGetEntryGroupIDNoValue() {
//tests getEntryGroupID via getGroupPrimaryGroupID
//which is basically identical to getUserPrimaryGroupIDs
$access = $this->getAccessMock();
$this->enableGroups($access);
$dn = 'cn=foobar,cn=foo,dc=barfoo,dc=bar';
$attr = 'primaryGroupToken';
$access->expects($this->once())
->method('readAttribute')
->with($dn, $attr)
->will($this->returnValue(false));
$groupBackend = new GroupLDAP($access);
$gid = $groupBackend->getGroupPrimaryGroupID($dn);
$this->assertSame(false, $gid);
}
}
-1
View File
@@ -73,7 +73,6 @@ class USER_LDAP extends BackendUtility implements \OCP\UserInterface {
}
$user->markLogin();
$user->update();
return $user->getUsername();
}
+1
View File
@@ -12,4 +12,5 @@
<types>
<authentication/>
</types>
<ocsid>166062</ocsid>
</info>
+1 -1
View File
@@ -1 +1 @@
1.1.0.0
1.1.0.1
+4 -4
View File
@@ -41,16 +41,16 @@ $CONFIG = array(
/* Blacklist a specific file and disallow the upload of files with this name - WARNING: USE THIS ONLY IF YOU KNOW WHAT YOU ARE DOING. */
"blacklisted_files" => array('.htaccess'),
/* The automatic hostname detection of ownCloud can fail in certain reverse proxy situations. This option allows to manually override the automatic detection. You can also add a port. For example "www.example.com:88" */
/* The automatic hostname detection of ownCloud can fail in certain reverse proxy and CLI/cron situations. This option allows to manually override the automatic detection. You can also add a port. For example "www.example.com:88" */
"overwritehost" => "",
/* The automatic protocol detection of ownCloud can fail in certain reverse proxy situations. This option allows to manually override the protocol detection. For example "https" */
/* The automatic protocol detection of ownCloud can fail in certain reverse proxy and CLI/cron situations. This option allows to manually override the protocol detection. For example "https" */
"overwriteprotocol" => "",
/* The automatic webroot detection of ownCloud can fail in certain reverse proxy situations. This option allows to manually override the automatic detection. For example "/domain.tld/ownCloud". The value "/" can be used to remove the root. */
/* The automatic webroot detection of ownCloud can fail in certain reverse proxy and CLI/cron situations. This option allows to manually override the automatic detection. For example "/domain.tld/ownCloud". The value "/" can be used to remove the root. */
"overwritewebroot" => "",
/* The automatic detection of ownCloud can fail in certain reverse proxy situations. This option allows to define a manually override condition as regular expression for the remote ip address. For example "^10\.0\.0\.[1-3]$" */
/* The automatic detection of ownCloud can fail in certain reverse proxy and CLI/cron situations. This option allows to define a manually override condition as regular expression for the remote ip address. For example "^10\.0\.0\.[1-3]$" */
"overwritecondaddr" => "",
/* A proxy to use to connect to the internet. For example "myproxy.org:88" */
+6
View File
@@ -241,20 +241,26 @@
float: left;
display: inline-block;
margin-right: 5px;
cursor: pointer;
}
#header .avatardiv img {
opacity: 1;
cursor: pointer;
}
#settings {
float: right;
color: #bbb;
cursor: pointer;
}
#expand {
display: block;
padding: 7px 12px 6px 7px;
cursor: pointer;
}
#expand * {
cursor: pointer;
}
#expand:hover, #expand:focus, #expand:active { color:#fff; }
#expand img { -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=70)"; filter:alpha(opacity=70); opacity:.7; margin-bottom:-2px; }
#expand:hover img, #expand:focus img, #expand:active img { -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; filter:alpha(opacity=100); opacity:1; }
+11
View File
@@ -31,6 +31,17 @@
margin-top: 10px;
width: 100%;
}
/* align primary button to right, other buttons to left */
.oc-dialog-buttonrow.twobuttons button:nth-child(1) {
float: left;
}
.oc-dialog-buttonrow.twobuttons button:nth-child(2) {
float: right;
}
.oc-dialog-buttonrow.onebutton button {
float: right;
}
.oc-dialog-close {
position:absolute;
+14 -3
View File
@@ -420,13 +420,13 @@ input[name='password-clone'] {
#password-icon {
position: absolute;
left: 16px;
top: 20px;
top: 22px;
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=30)";
filter: alpha(opacity=30);
opacity: .3;
}
#adminpass-icon, #password-icon {
top: 15px;
top: 17px;
}
/* General new input field look */
@@ -474,10 +474,10 @@ label.infield {
#body-login form input[type="checkbox"]+label {
position: relative;
margin: 0;
font-size: 13px;
padding: 14px;
padding-left: 28px;
margin-left: -28px;
vertical-align: middle;
}
#body-login form .errors { background:#fed7d7; border:1px solid #f00; list-style-indent:inside; margin:0 0 2em; padding:1em; }
#body-login .success { background:#d7fed7; border:1px solid #0f0; width: 35%; margin: 30px auto; padding:1em; text-align: center;}
@@ -812,6 +812,7 @@ div.crumb {
display: block;
background: url('../img/breadcrumb.svg') no-repeat right center;
height: 44px;
background-size: auto 24px;
}
div.crumb.hidden {
display: none;
@@ -853,6 +854,16 @@ div.crumb:active {
opacity: 0;
}
/* LEGACY FIX only - do not use fieldsets for settings */
fieldset.warning legend, fieldset.update legend {
top: 18px;
position: relative;
}
fieldset.warning legend + p, fieldset.update legend + p {
margin-top: 12px;
}
/* for IE10 */
@-ms-viewport {
width: device-width;
+7
View File
@@ -111,6 +111,13 @@
var $buttonrow = $('<div class="oc-dialog-buttonrow" />');
this.$buttonrow = $buttonrow.appendTo(this.$dialog);
}
if (value.length === 1) {
this.$buttonrow.addClass('onebutton');
} else if (value.length === 2) {
this.$buttonrow.addClass('twobuttons');
} else if (value.length === 3) {
this.$buttonrow.addClass('threebuttons');
}
$.each(value, function(idx, val) {
var $button = $('<button>').text(val.text);
if (val.classes) {
+5 -3
View File
@@ -380,7 +380,7 @@ var OC={
* Do a search query and display the results
* @param {string} query the search query
*/
search:function(query){
search: _.debounce(function(query){
if(query){
OC.addStyle('search','results');
$.getJSON(OC.filePath('search','ajax','search.php')+'?query='+encodeURIComponent(query), function(results){
@@ -388,7 +388,7 @@ var OC={
OC.search.showResults(results);
});
}
},
}, 500),
dialogs:OCdialogs,
mtime2date:function(mtime) {
mtime = parseInt(mtime,10);
@@ -1138,7 +1138,9 @@ function initCore() {
if(!$app.is('a')) {
$app = $app.closest('a');
}
$app.addClass('app-loading');
if(!event.ctrlKey) {
$app.addClass('app-loading');
}
});
}
+22 -24
View File
@@ -19,7 +19,7 @@
*
*/
/* global OC, t, alert, $ */
/* global alert */
/**
* this class to ease the usage of jquery dialogs
@@ -66,7 +66,7 @@ var OCdialogs = {
* @param modal make the dialog modal
*/
confirm:function(text, title, callback, modal) {
this.message(
return this.message(
text,
title,
'notice',
@@ -86,7 +86,7 @@ var OCdialogs = {
* @param password whether the input should be a password input
*/
prompt: function (text, title, callback, modal, name, password) {
$.when(this._getMessageTemplate()).then(function ($tmpl) {
return $.when(this._getMessageTemplate()).then(function ($tmpl) {
var dialogName = 'oc-dialog-' + OCdialogs.dialogsCounter + '-content';
var dialogId = '#' + dialogName;
var $dlg = $tmpl.octemplate({
@@ -104,8 +104,15 @@ var OCdialogs = {
modal = false;
}
$('body').append($dlg);
var buttonlist = [
{
var buttonlist = [{
text : t('core', 'No'),
click: function () {
if (callback !== undefined) {
callback(false, input.val());
}
$(dialogId).ocdialog('close');
}
}, {
text : t('core', 'Yes'),
click : function () {
if (callback !== undefined) {
@@ -114,15 +121,6 @@ var OCdialogs = {
$(dialogId).ocdialog('close');
},
defaultButton: true
},
{
text : t('core', 'No'),
click: function () {
if (callback !== undefined) {
callback(false, input.val());
}
$(dialogId).ocdialog('close');
}
}
];
@@ -237,7 +235,7 @@ var OCdialogs = {
* You better use a wrapper instead ...
*/
message:function(content, title, dialogType, buttons, callback, modal) {
$.when(this._getMessageTemplate()).then(function($tmpl) {
return $.when(this._getMessageTemplate()).then(function($tmpl) {
var dialogName = 'oc-dialog-' + OCdialogs.dialogsCounter + '-content';
var dialogId = '#' + dialogName;
var $dlg = $tmpl.octemplate({
@@ -254,6 +252,15 @@ var OCdialogs = {
switch (buttons) {
case OCdialogs.YES_NO_BUTTONS:
buttonlist = [{
text: t('core', 'No'),
click: function(){
if (callback !== undefined) {
callback(false);
}
$(dialogId).ocdialog('close');
}
},
{
text: t('core', 'Yes'),
click: function(){
if (callback !== undefined) {
@@ -262,15 +269,6 @@ var OCdialogs = {
$(dialogId).ocdialog('close');
},
defaultButton: true
},
{
text: t('core', 'No'),
click: function(){
if (callback !== undefined) {
callback(false);
}
$(dialogId).ocdialog('close');
}
}];
break;
case OCdialogs.OK_BUTTON:
+4
View File
@@ -52,6 +52,10 @@ $(document).ready(function() {
$(':submit', this).attr('disabled','disabled').val($(':submit', this).data('finishing'));
$('input', this).addClass('ui-state-disabled').attr('disabled','disabled');
$('#selectDbType').buttonset('disable');
$('.strengthify-wrapper, .tipsy')
.css('-ms-filter', '"progid:DXImageTransform.Microsoft.Alpha(Opacity=30)"')
.css('filter', 'alpha(opacity=30)')
.css('opacity', .3);
// Create the form
var form = $('<form>');
+760 -543
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -41,7 +41,7 @@ class Controller {
'dbpass' => '',
'dbname' => '',
'dbtablespace' => '',
'dbhost' => '',
'dbhost' => 'localhost',
'dbtype' => '',
);
$parameters = array_merge($defaults, $post);
+1
View File
@@ -113,6 +113,7 @@
<a href="<?php print_unescaped(OC_Helper::linkToRoute('settings_apps').'?installed'); ?>" title=""
<?php if( $_['appsmanagement_active'] ): ?> class="active"<?php endif; ?>>
<img class="app-icon svg" alt="" src="<?php print_unescaped(OC_Helper::imagePath('settings', 'apps.svg')); ?>"/>
<div class="icon-loading-dark" style="display:none;"></div>
<span>
<?php p($l->t('Apps')); ?>
</span>
+1 -1
View File
@@ -557,7 +557,7 @@ msgid ""
msgstr ""
#: templates/admin.php:229
msgid "Use systems cron service to call the cron.php file every 15 minutes."
msgid "Use system's cron service to call the cron.php file every 15 minutes."
msgstr ""
#: templates/admin.php:234
+1 -1
View File
@@ -557,7 +557,7 @@ msgid ""
msgstr ""
#: templates/admin.php:229
msgid "Use systems cron service to call the cron.php file every 15 minutes."
msgid "Use system's cron service to call the cron.php file every 15 minutes."
msgstr ""
#: templates/admin.php:234
+1 -1
View File
@@ -467,7 +467,7 @@ msgid ""
msgstr ""
#: templates/admin.php:200
msgid "Use systems cron service to call the cron.php file every 15 minutes."
msgid "Use system's cron service to call the cron.php file every 15 minutes."
msgstr ""
#: templates/admin.php:205
+1 -1
View File
@@ -557,7 +557,7 @@ msgid ""
msgstr ""
#: templates/admin.php:229
msgid "Use systems cron service to call the cron.php file every 15 minutes."
msgid "Use system's cron service to call the cron.php file every 15 minutes."
msgstr ""
#: templates/admin.php:234
+1 -1
View File
@@ -557,7 +557,7 @@ msgid ""
msgstr ""
#: templates/admin.php:229
msgid "Use systems cron service to call the cron.php file every 15 minutes."
msgid "Use system's cron service to call the cron.php file every 15 minutes."
msgstr ""
#: templates/admin.php:234
+1 -1
View File
@@ -557,7 +557,7 @@ msgid ""
msgstr ""
#: templates/admin.php:229
msgid "Use systems cron service to call the cron.php file every 15 minutes."
msgid "Use system's cron service to call the cron.php file every 15 minutes."
msgstr ""
#: templates/admin.php:234
+1 -1
View File
@@ -560,7 +560,7 @@ msgid ""
msgstr "الملف cron.php تم تسجيله فى خدمه webcron لاستدعاء الملف cron.php كل 15 دقيقه"
#: templates/admin.php:229
msgid "Use systems cron service to call the cron.php file every 15 minutes."
msgid "Use system's cron service to call the cron.php file every 15 minutes."
msgstr "استخدم نظام خدمة cron لـ استدعاء ملف cron.php كل 15 دقيقة "
#: templates/admin.php:234
+1 -1
View File
@@ -559,7 +559,7 @@ msgid ""
msgstr "cron.php rexístrase nun serviciu webcron pa llamar a cron.php cada 15 minutos al traviés de HTTP."
#: templates/admin.php:229
msgid "Use systems cron service to call the cron.php file every 15 minutes."
msgid "Use system's cron service to call the cron.php file every 15 minutes."
msgstr "Usa'l serviciu cron del sistema pa llamar al ficheru cron.php cada 15 minutos."
#: templates/admin.php:234
+1 -1
View File
@@ -557,7 +557,7 @@ msgid ""
msgstr ""
#: templates/admin.php:229
msgid "Use systems cron service to call the cron.php file every 15 minutes."
msgid "Use system's cron service to call the cron.php file every 15 minutes."
msgstr ""
#: templates/admin.php:234
+1 -1
View File
@@ -557,7 +557,7 @@ msgid ""
msgstr ""
#: templates/admin.php:229
msgid "Use systems cron service to call the cron.php file every 15 minutes."
msgid "Use system's cron service to call the cron.php file every 15 minutes."
msgstr ""
#: templates/admin.php:234
+1 -1
View File
@@ -557,7 +557,7 @@ msgid ""
msgstr ""
#: templates/admin.php:229
msgid "Use systems cron service to call the cron.php file every 15 minutes."
msgid "Use system's cron service to call the cron.php file every 15 minutes."
msgstr ""
#: templates/admin.php:234
+1 -1
View File
@@ -557,7 +557,7 @@ msgid ""
msgstr ""
#: templates/admin.php:229
msgid "Use systems cron service to call the cron.php file every 15 minutes."
msgid "Use system's cron service to call the cron.php file every 15 minutes."
msgstr ""
#: templates/admin.php:234
+1 -1
View File
@@ -557,7 +557,7 @@ msgid ""
msgstr ""
#: templates/admin.php:229
msgid "Use systems cron service to call the cron.php file every 15 minutes."
msgid "Use system's cron service to call the cron.php file every 15 minutes."
msgstr ""
#: templates/admin.php:234
+1 -1
View File
@@ -557,7 +557,7 @@ msgid ""
msgstr ""
#: templates/admin.php:229
msgid "Use systems cron service to call the cron.php file every 15 minutes."
msgid "Use system's cron service to call the cron.php file every 15 minutes."
msgstr ""
#: templates/admin.php:234
+1 -1
View File
@@ -560,7 +560,7 @@ msgid ""
msgstr "cron.php està registrat en un servei webcron que fa una crida a cron.php cada 15 minuts a través de http."
#: templates/admin.php:229
msgid "Use systems cron service to call the cron.php file every 15 minutes."
msgid "Use system's cron service to call the cron.php file every 15 minutes."
msgstr "Utilitza el servei cron del sistema per fer una crida al fitxer cron.php cada 15 minuts."
#: templates/admin.php:234
+1 -1
View File
@@ -557,7 +557,7 @@ msgid ""
msgstr ""
#: templates/admin.php:229
msgid "Use systems cron service to call the cron.php file every 15 minutes."
msgid "Use system's cron service to call the cron.php file every 15 minutes."
msgstr ""
#: templates/admin.php:234
+1 -1
View File
@@ -566,7 +566,7 @@ msgid ""
msgstr "cron.php je registrován u služby webcron, aby volal cron.php jednou za 15 minut přes http."
#: templates/admin.php:229
msgid "Use systems cron service to call the cron.php file every 15 minutes."
msgid "Use system's cron service to call the cron.php file every 15 minutes."
msgstr "Použít systémovou službu cron pro volání cron.php každých 15 minut."
#: templates/admin.php:234
+1 -1
View File
@@ -557,7 +557,7 @@ msgid ""
msgstr ""
#: templates/admin.php:229
msgid "Use systems cron service to call the cron.php file every 15 minutes."
msgid "Use system's cron service to call the cron.php file every 15 minutes."
msgstr ""
#: templates/admin.php:234
+1 -1
View File
@@ -561,7 +561,7 @@ msgid ""
msgstr "cron.php er registreret til at en webcron service skal kalde cron.php hvert 15 minut over http."
#: templates/admin.php:229
msgid "Use systems cron service to call the cron.php file every 15 minutes."
msgid "Use system's cron service to call the cron.php file every 15 minutes."
msgstr "Brug systemets cron service til at kalde cron.php hvert 15. minut."
#: templates/admin.php:234
+1 -1
View File
@@ -567,7 +567,7 @@ msgid ""
msgstr "cron.php ist als Webcron-Dienst registriert, der die cron.php alle 15 Minuten per HTTP aufruft."
#: templates/admin.php:229
msgid "Use systems cron service to call the cron.php file every 15 minutes."
msgid "Use system's cron service to call the cron.php file every 15 minutes."
msgstr "Benutze den System-Crondienst um die cron.php alle 15 Minuten aufzurufen."
#: templates/admin.php:234
+1 -1
View File
@@ -558,7 +558,7 @@ msgid ""
msgstr ""
#: templates/admin.php:229
msgid "Use systems cron service to call the cron.php file every 15 minutes."
msgid "Use system's cron service to call the cron.php file every 15 minutes."
msgstr ""
#: templates/admin.php:234
+1 -1
View File
@@ -566,7 +566,7 @@ msgid ""
msgstr ""
#: templates/admin.php:229
msgid "Use systems cron service to call the cron.php file every 15 minutes."
msgid "Use system's cron service to call the cron.php file every 15 minutes."
msgstr ""
#: templates/admin.php:234
+1 -1
View File
@@ -567,7 +567,7 @@ msgid ""
msgstr "cron.php ist als Webcron-Dienst registriert, der die cron.php alle 15 Minuten per HTTP aufruft."
#: templates/admin.php:229
msgid "Use systems cron service to call the cron.php file every 15 minutes."
msgid "Use system's cron service to call the cron.php file every 15 minutes."
msgstr "Benutzen Sie den System-Crondienst, um die cron.php alle 15 Minuten aufzurufen."
#: templates/admin.php:234
+1 -1
View File
@@ -566,7 +566,7 @@ msgid ""
msgstr "Το cron.php είναι καταχωρημένο σε μια υπηρεσία webcron ώστε να καλεί το cron.php κάθε 15 λεπτά μέσω http."
#: templates/admin.php:229
msgid "Use systems cron service to call the cron.php file every 15 minutes."
msgid "Use system's cron service to call the cron.php file every 15 minutes."
msgstr "Χρήση της υπηρεσίας cron του συστήματος για να καλεστεί το αρχείο cron.php κάθε 15 λεπτά."
#: templates/admin.php:234
+1 -1
View File
@@ -557,7 +557,7 @@ msgid ""
msgstr ""
#: templates/admin.php:229
msgid "Use systems cron service to call the cron.php file every 15 minutes."
msgid "Use system's cron service to call the cron.php file every 15 minutes."
msgstr ""
#: templates/admin.php:234
+1 -1
View File
@@ -558,7 +558,7 @@ msgid ""
msgstr "cron.php is registered at a webcron service to call cron.php every 15 minutes over http."
#: templates/admin.php:229
msgid "Use systems cron service to call the cron.php file every 15 minutes."
msgid "Use system's cron service to call the cron.php file every 15 minutes."
msgstr "Use system's cron service to call the cron.php file every 15 minutes."
#: templates/admin.php:234
+1 -1
View File
@@ -557,7 +557,7 @@ msgid ""
msgstr ""
#: templates/admin.php:229
msgid "Use systems cron service to call the cron.php file every 15 minutes."
msgid "Use system's cron service to call the cron.php file every 15 minutes."
msgstr ""
#: templates/admin.php:234
+1 -1
View File
@@ -558,7 +558,7 @@ msgid ""
msgstr ""
#: templates/admin.php:229
msgid "Use systems cron service to call the cron.php file every 15 minutes."
msgid "Use system's cron service to call the cron.php file every 15 minutes."
msgstr ""
#: templates/admin.php:234
+1 -1
View File
@@ -574,7 +574,7 @@ msgid ""
msgstr "cron.php se registra en un servicio webcron para llamar a cron.php cada 15 minutos a través de HTTP."
#: templates/admin.php:229
msgid "Use systems cron service to call the cron.php file every 15 minutes."
msgid "Use system's cron service to call the cron.php file every 15 minutes."
msgstr "Utiliza el servicio cron del sistema para llamar al archivo cron.php cada 15 minutos."
#: templates/admin.php:234
+1 -1
View File
@@ -561,7 +561,7 @@ msgid ""
msgstr "cron.php está registrado en el servicio webcron para llamarlo cada 15 minutos usando http."
#: templates/admin.php:229
msgid "Use systems cron service to call the cron.php file every 15 minutes."
msgid "Use system's cron service to call the cron.php file every 15 minutes."
msgstr "Usar el servicio cron del sistema para llamar al archivo cron.php cada 15 minutos."
#: templates/admin.php:234
+1 -1
View File
@@ -557,7 +557,7 @@ msgid ""
msgstr ""
#: templates/admin.php:229
msgid "Use systems cron service to call the cron.php file every 15 minutes."
msgid "Use system's cron service to call the cron.php file every 15 minutes."
msgstr ""
#: templates/admin.php:234
+1 -1
View File
@@ -557,7 +557,7 @@ msgid ""
msgstr ""
#: templates/admin.php:229
msgid "Use systems cron service to call the cron.php file every 15 minutes."
msgid "Use system's cron service to call the cron.php file every 15 minutes."
msgstr ""
#: templates/admin.php:234
+1 -1
View File
@@ -557,7 +557,7 @@ msgid ""
msgstr ""
#: templates/admin.php:229
msgid "Use systems cron service to call the cron.php file every 15 minutes."
msgid "Use system's cron service to call the cron.php file every 15 minutes."
msgstr ""
#: templates/admin.php:234
+1 -1
View File
@@ -557,7 +557,7 @@ msgid ""
msgstr ""
#: templates/admin.php:229
msgid "Use systems cron service to call the cron.php file every 15 minutes."
msgid "Use system's cron service to call the cron.php file every 15 minutes."
msgstr ""
#: templates/admin.php:234

Some files were not shown because too many files have changed in this diff Show More