Compare commits
66 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e3d50804f7 | |||
| 14ea0ea3bc | |||
| 66130ad336 | |||
| a310415b09 | |||
| 10bac56551 | |||
| 32c6afba90 | |||
| 31d8bce383 | |||
| 2fb022fbc3 | |||
| 5911188211 | |||
| 9ccadcfe80 | |||
| 9ee46bbe91 | |||
| bc1ff4744b | |||
| 6043a90a71 | |||
| 0e62a7dc59 | |||
| 7a6b76f96e | |||
| a78293dd3f | |||
| 1ce9ba1ebc | |||
| 2f15ae988f | |||
| c02e95ff40 | |||
| 03c13021db | |||
| 01c0601d39 | |||
| 8bd5c6e04d | |||
| 76b310de9d | |||
| ee2886331e | |||
| ed1c918d9e | |||
| 18f5f85160 | |||
| 19dedf3d61 | |||
| efadfedbaa | |||
| 60e3195600 | |||
| db72e90504 | |||
| cf982e9818 | |||
| dccab5d20f | |||
| f5e4ebf2ba | |||
| 2bfdd02c2a | |||
| 8b97073e13 | |||
| d2fd78a0c9 | |||
| 59fd9d7517 | |||
| 5344d9beab | |||
| 127ce3c5d9 | |||
| 04604dae0d | |||
| 0fe1f25a9e | |||
| a5d34b435f | |||
| 67cf1d61e1 | |||
| 5549148039 | |||
| 0319ee3894 | |||
| 2bd3aa6b21 | |||
| 36d2aab945 | |||
| 0a4e95cc07 | |||
| b429a71660 | |||
| b33c61798c | |||
| 11a2c0249d | |||
| 86545a90d0 | |||
| 71261decf1 | |||
| f390ae53ba | |||
| 195cf273f8 | |||
| 3a1c7182e6 | |||
| c272338897 | |||
| 74edbd5df0 | |||
| c28fb2de4e | |||
| b343b18cd9 | |||
| 2224d1c0d3 | |||
| b42215a3c9 | |||
| 2115a9bf1a | |||
| 843ad18bf3 | |||
| ed4ba00b77 | |||
| 4a7aaa8914 |
@@ -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
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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 + '/';
|
||||
}
|
||||
|
||||
@@ -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('('));
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,4 +15,5 @@
|
||||
<types>
|
||||
<filesystem/>
|
||||
</types>
|
||||
<ocsid>166047</ocsid>
|
||||
</info>
|
||||
|
||||
@@ -1 +1 @@
|
||||
0.6
|
||||
0.6.1
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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)));
|
||||
|
||||
@@ -10,4 +10,5 @@
|
||||
<types>
|
||||
<filesystem/>
|
||||
</types>
|
||||
<ocsid>166048</ocsid>
|
||||
</info>
|
||||
|
||||
@@ -1 +1 @@
|
||||
0.2
|
||||
0.2.1
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -15,4 +15,5 @@
|
||||
<files>public.php</files>
|
||||
<webdav>publicwebdav.php</webdav>
|
||||
</public>
|
||||
<ocsid>166050</ocsid>
|
||||
</info>
|
||||
|
||||
@@ -1 +1 @@
|
||||
0.5.2
|
||||
0.5.3
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 () {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -24,4 +24,5 @@
|
||||
<types>
|
||||
<filesystem/>
|
||||
</types>
|
||||
<ocsid>166052</ocsid>
|
||||
</info>
|
||||
|
||||
@@ -1 +1 @@
|
||||
0.6.1
|
||||
0.6.2
|
||||
|
||||
@@ -29,4 +29,5 @@
|
||||
<filesystem/>
|
||||
</types>
|
||||
<default_enable/>
|
||||
<ocsid>166053</ocsid>
|
||||
</info>
|
||||
|
||||
@@ -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 @@
|
||||
1.0.4
|
||||
1.0.5
|
||||
|
||||
@@ -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 @@
|
||||
0.4.2
|
||||
0.4.3
|
||||
|
||||
+198
-37
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -73,7 +73,6 @@ class USER_LDAP extends BackendUtility implements \OCP\UserInterface {
|
||||
}
|
||||
|
||||
$user->markLogin();
|
||||
$user->update();
|
||||
|
||||
return $user->getUsername();
|
||||
}
|
||||
|
||||
@@ -12,4 +12,5 @@
|
||||
<types>
|
||||
<authentication/>
|
||||
</types>
|
||||
<ocsid>166062</ocsid>
|
||||
</info>
|
||||
|
||||
@@ -1 +1 @@
|
||||
1.1.0.0
|
||||
1.1.0.1
|
||||
|
||||
@@ -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" */
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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:
|
||||
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -41,7 +41,7 @@ class Controller {
|
||||
'dbpass' => '',
|
||||
'dbname' => '',
|
||||
'dbtablespace' => '',
|
||||
'dbhost' => '',
|
||||
'dbhost' => 'localhost',
|
||||
'dbtype' => '',
|
||||
);
|
||||
$parameters = array_merge($defaults, $post);
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user