Merge branch 'addressbook-uid-check-migration' of https://github.com/nextcloud/server into addressbook-uid-check-migration
Signed-off-by: John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
This commit is contained in:
commit
98f30c2dab
|
@ -170,7 +170,7 @@ class IMipPlugin extends SabreIMipPlugin {
|
|||
$vevent = $iTipMessage->message->VEVENT;
|
||||
|
||||
$attendee = $this->getCurrentAttendee($iTipMessage);
|
||||
$defaultLang = $this->config->getUserValue($this->userId, 'core', 'lang', $this->l10nFactory->findLanguage());
|
||||
$defaultLang = $this->l10nFactory->findLanguage();
|
||||
$lang = $this->getAttendeeLangOrDefault($defaultLang, $attendee);
|
||||
$l10n = $this->l10nFactory->get('dav', $lang);
|
||||
|
||||
|
|
|
@ -88,16 +88,26 @@ class AddressBookImpl implements IAddressBook {
|
|||
/**
|
||||
* @param string $pattern which should match within the $searchProperties
|
||||
* @param array $searchProperties defines the properties within the query pattern should match
|
||||
* @param array $options - for future use. One should always have options!
|
||||
* @param array $options Options to define the output format
|
||||
* - types boolean (since 15.0.0) If set to true, fields that come with a TYPE property will be an array
|
||||
* example: ['id' => 5, 'FN' => 'Thomas Tanghus', 'EMAIL' => ['type => 'HOME', 'value' => 'g@h.i']]
|
||||
* @return array an array of contacts which are arrays of key-value-pairs
|
||||
* example result:
|
||||
* [
|
||||
* ['id' => 0, 'FN' => 'Thomas Müller', 'EMAIL' => 'a@b.c', 'GEO' => '37.386013;-122.082932'],
|
||||
* ['id' => 5, 'FN' => 'Thomas Tanghus', 'EMAIL' => ['d@e.f', 'g@h.i']]
|
||||
* ]
|
||||
* @return array an array of contacts which are arrays of key-value-pairs
|
||||
* @since 5.0.0
|
||||
*/
|
||||
public function search($pattern, $searchProperties, $options) {
|
||||
$results = $this->backend->search($this->getKey(), $pattern, $searchProperties);
|
||||
|
||||
$withTypes = \array_key_exists('types', $options) && $options['types'] === true;
|
||||
|
||||
$vCards = [];
|
||||
foreach ($results as $result) {
|
||||
$vCards[] = $this->vCard2Array($result['uri'], $this->readCard($result['carddata']));
|
||||
$vCards[] = $this->vCard2Array($result['uri'], $this->readCard($result['carddata']), $withTypes);
|
||||
}
|
||||
|
||||
return $vCards;
|
||||
|
@ -220,7 +230,7 @@ class AddressBookImpl implements IAddressBook {
|
|||
* @param VCard $vCard
|
||||
* @return array
|
||||
*/
|
||||
protected function vCard2Array($uri, VCard $vCard) {
|
||||
protected function vCard2Array($uri, VCard $vCard, $withTypes = false) {
|
||||
$result = [
|
||||
'URI' => $uri,
|
||||
];
|
||||
|
@ -255,15 +265,28 @@ class AddressBookImpl implements IAddressBook {
|
|||
$result[$property->name] = [];
|
||||
}
|
||||
|
||||
$result[$property->name][] = $property->getValue();
|
||||
$type = $this->getTypeFromProperty($property);
|
||||
if ($withTypes) {
|
||||
$result[$property->name][] = [
|
||||
'type' => $type,
|
||||
'value' => $property->getValue()
|
||||
];
|
||||
} else {
|
||||
$result[$property->name][] = $property->getValue();
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
$result[$property->name] = $property->getValue();
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->addressBookInfo['principaluri'] === 'principals/system/system' &&
|
||||
$this->addressBookInfo['uri'] === 'system') {
|
||||
if (
|
||||
$this->addressBookInfo['principaluri'] === 'principals/system/system' && (
|
||||
$this->addressBookInfo['uri'] === 'system' ||
|
||||
$this->addressBookInfo['{DAV:}displayname'] === $this->urlGenerator->getBaseUrl()
|
||||
)
|
||||
) {
|
||||
$result['isLocalSystemBook'] = true;
|
||||
}
|
||||
return $result;
|
||||
|
|
|
@ -1134,8 +1134,8 @@ class CardDavBackend implements BackendInterface, SyncSupport {
|
|||
* Extract UID from vcard
|
||||
*
|
||||
* @param string $cardData the vcard raw data
|
||||
* @return string the uid or empty if none
|
||||
* @throws BadRequest
|
||||
* @return string the uid
|
||||
* @throws BadRequest if no UID is available
|
||||
*/
|
||||
private function getUID($cardData) {
|
||||
$vCard = Reader::read($cardData);
|
||||
|
|
|
@ -164,14 +164,19 @@ class File extends Node implements IFile {
|
|||
$this->changeLock(ILockingProvider::LOCK_EXCLUSIVE);
|
||||
}
|
||||
|
||||
$target = $partStorage->fopen($internalPartPath, 'wb');
|
||||
if ($target === false) {
|
||||
\OC::$server->getLogger()->error('\OC\Files\Filesystem::fopen() failed', ['app' => 'webdav']);
|
||||
// because we have no clue about the cause we can only throw back a 500/Internal Server Error
|
||||
throw new Exception('Could not write file contents');
|
||||
if ($partStorage->instanceOfStorage(Storage\IWriteStreamStorage::class)) {
|
||||
$count = $partStorage->writeStream($internalPartPath, $data);
|
||||
$result = $count > 0;
|
||||
} else {
|
||||
$target = $partStorage->fopen($internalPartPath, 'wb');
|
||||
if ($target === false) {
|
||||
\OC::$server->getLogger()->error('\OC\Files\Filesystem::fopen() failed', ['app' => 'webdav']);
|
||||
// because we have no clue about the cause we can only throw back a 500/Internal Server Error
|
||||
throw new Exception('Could not write file contents');
|
||||
}
|
||||
list($count, $result) = \OC_Helper::streamCopy($data, $target);
|
||||
fclose($target);
|
||||
}
|
||||
list($count, $result) = \OC_Helper::streamCopy($data, $target);
|
||||
fclose($target);
|
||||
|
||||
if ($result === false) {
|
||||
$expected = -1;
|
||||
|
@ -185,7 +190,7 @@ class File extends Node implements IFile {
|
|||
// double check if the file was fully received
|
||||
// compare expected and actual size
|
||||
if (isset($_SERVER['CONTENT_LENGTH']) && $_SERVER['REQUEST_METHOD'] === 'PUT') {
|
||||
$expected = (int) $_SERVER['CONTENT_LENGTH'];
|
||||
$expected = (int)$_SERVER['CONTENT_LENGTH'];
|
||||
if ($count !== $expected) {
|
||||
throw new BadRequest('expected filesize ' . $expected . ' got ' . $count);
|
||||
}
|
||||
|
@ -219,7 +224,7 @@ class File extends Node implements IFile {
|
|||
$renameOkay = $storage->moveFromStorage($partStorage, $internalPartPath, $internalPath);
|
||||
$fileExists = $storage->file_exists($internalPath);
|
||||
if ($renameOkay === false || $fileExists === false) {
|
||||
\OC::$server->getLogger()->error('renaming part file to final file failed ($run: ' . ( $run ? 'true' : 'false' ) . ', $renameOkay: ' . ( $renameOkay ? 'true' : 'false' ) . ', $fileExists: ' . ( $fileExists ? 'true' : 'false' ) . ')', ['app' => 'webdav']);
|
||||
\OC::$server->getLogger()->error('renaming part file to final file failed $renameOkay: ' . ($renameOkay ? 'true' : 'false') . ', $fileExists: ' . ($fileExists ? 'true' : 'false') . ')', ['app' => 'webdav']);
|
||||
throw new Exception('Could not rename part file to final file');
|
||||
}
|
||||
} catch (ForbiddenException $ex) {
|
||||
|
@ -246,7 +251,7 @@ class File extends Node implements IFile {
|
|||
$this->header('X-OC-MTime: accepted');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ($view) {
|
||||
$this->emitPostHooks($exists);
|
||||
}
|
||||
|
@ -443,7 +448,7 @@ class File extends Node implements IFile {
|
|||
//detect aborted upload
|
||||
if (isset ($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'PUT') {
|
||||
if (isset($_SERVER['CONTENT_LENGTH'])) {
|
||||
$expected = (int) $_SERVER['CONTENT_LENGTH'];
|
||||
$expected = (int)$_SERVER['CONTENT_LENGTH'];
|
||||
if ($bytesWritten !== $expected) {
|
||||
$chunk_handler->remove($info['index']);
|
||||
throw new BadRequest(
|
||||
|
|
|
@ -164,7 +164,7 @@ class FileTest extends \Test\TestCase {
|
|||
public function testSimplePutFails($thrownException, $expectedException, $checkPreviousClass = true) {
|
||||
// setup
|
||||
$storage = $this->getMockBuilder(Local::class)
|
||||
->setMethods(['fopen'])
|
||||
->setMethods(['writeStream'])
|
||||
->setConstructorArgs([['datadir' => \OC::$server->getTempManager()->getTemporaryFolder()]])
|
||||
->getMock();
|
||||
\OC\Files\Filesystem::mount($storage, [], $this->user . '/');
|
||||
|
@ -182,11 +182,11 @@ class FileTest extends \Test\TestCase {
|
|||
|
||||
if ($thrownException !== null) {
|
||||
$storage->expects($this->once())
|
||||
->method('fopen')
|
||||
->method('writeStream')
|
||||
->will($this->throwException($thrownException));
|
||||
} else {
|
||||
$storage->expects($this->once())
|
||||
->method('fopen')
|
||||
->method('writeStream')
|
||||
->will($this->returnValue(false));
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,13 @@
|
|||
|
||||
#app-sidebar .mainFileInfoView .permalink {
|
||||
padding: 6px 10px;
|
||||
vertical-align: text-top;
|
||||
vertical-align: top;
|
||||
opacity: .6;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
#app-sidebar .mainFileInfoView .permalink-field>input {
|
||||
clear: both;
|
||||
|
@ -87,7 +93,7 @@
|
|||
}
|
||||
|
||||
#app-sidebar .fileName h3 {
|
||||
width: calc(100% - 36px); /* 36px is the with of the copy link icon */
|
||||
width: calc(100% - 42px); /* 36px is the with of the copy link icon, but this breaks so we add some more to be sure */
|
||||
display: inline-block;
|
||||
padding: 5px 0;
|
||||
margin: -5px 0;
|
||||
|
|
|
@ -8,7 +8,12 @@
|
|||
}
|
||||
|
||||
/* FILE MENU */
|
||||
.actions { padding:5px; height:32px; display: inline-block; float: left; }
|
||||
.actions {
|
||||
padding: 5px;
|
||||
height: 100%;
|
||||
display: inline-block;
|
||||
float: left;
|
||||
}
|
||||
.actions input, .actions button, .actions .button { margin:0; float:left; }
|
||||
.actions .button a { color: #555; }
|
||||
.actions .button a:hover,
|
||||
|
@ -316,6 +321,7 @@ table td.filename .thumbnail {
|
|||
background-size: 32px;
|
||||
margin-left: 9px;
|
||||
margin-top: 9px;
|
||||
border-radius: var(--border-radius);
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
z-index: 4;
|
||||
|
@ -658,8 +664,14 @@ table.dragshadow td.size {
|
|||
top: 100%;
|
||||
margin-top: 4px;
|
||||
min-width: 100px;
|
||||
margin-left: 7px;
|
||||
margin-left: 22px; /* Align left edge below center of + button … */
|
||||
transform: translateX(-50%); /* … then center it below button */
|
||||
z-index: 1001;
|
||||
|
||||
/* Center triangle */
|
||||
&::after {
|
||||
left: calc(50% - 8px) !important;
|
||||
}
|
||||
}
|
||||
|
||||
#filestable .filename .action .icon,
|
||||
|
|
|
@ -174,6 +174,9 @@
|
|||
// hide other tabs
|
||||
$tabsContainer.find('.tab').addClass('hidden');
|
||||
|
||||
$tabsContainer.attr('class', 'tabsContainer');
|
||||
$tabsContainer.addClass(tabView.getTabsContainerExtraClasses());
|
||||
|
||||
// tab already rendered ?
|
||||
if (!$tabEl.length) {
|
||||
// render tab
|
||||
|
|
|
@ -40,6 +40,21 @@
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the extra CSS classes used by the tabs container when this
|
||||
* tab is the selected one.
|
||||
*
|
||||
* In general you should not extend this method, as tabs should not
|
||||
* modify the classes of its container; this is reserved as a last
|
||||
* resort for very specific cases in which there is no other way to get
|
||||
* the proper style or behaviour.
|
||||
*
|
||||
* @return {String} space-separated CSS classes
|
||||
*/
|
||||
getTabsContainerExtraClasses: function() {
|
||||
return '';
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the tab label
|
||||
*
|
||||
|
|
|
@ -142,6 +142,7 @@ OC.L10N.register(
|
|||
"WebDAV" : "WebDAV",
|
||||
"Use this address to <a href=\"%s\" target=\"_blank\" rel=\"noreferrer noopener\">access your Files via WebDAV</a>" : "Použijte tuto adresu pro <a href=\"%s\" target=\"_blank\" rel=\"noreferrer noopener\">přístup k vašim souborům přes WebDAV</a>",
|
||||
"Cancel upload" : "Zrušit nahrávání",
|
||||
"Toggle grid view" : "Přepnout zobrazení mřížky",
|
||||
"No files in here" : "Žádné soubory",
|
||||
"Upload some content or sync with your devices!" : "Nahrajte nějaký obsah nebo synchronizujte se svými přístroji!",
|
||||
"No entries found in this folder" : "V této složce nebylo nic nalezeno",
|
||||
|
|
|
@ -140,6 +140,7 @@
|
|||
"WebDAV" : "WebDAV",
|
||||
"Use this address to <a href=\"%s\" target=\"_blank\" rel=\"noreferrer noopener\">access your Files via WebDAV</a>" : "Použijte tuto adresu pro <a href=\"%s\" target=\"_blank\" rel=\"noreferrer noopener\">přístup k vašim souborům přes WebDAV</a>",
|
||||
"Cancel upload" : "Zrušit nahrávání",
|
||||
"Toggle grid view" : "Přepnout zobrazení mřížky",
|
||||
"No files in here" : "Žádné soubory",
|
||||
"Upload some content or sync with your devices!" : "Nahrajte nějaký obsah nebo synchronizujte se svými přístroji!",
|
||||
"No entries found in this folder" : "V této složce nebylo nic nalezeno",
|
||||
|
|
|
@ -8,16 +8,16 @@
|
|||
*
|
||||
*/
|
||||
|
||||
if (!OCA.External) {
|
||||
if (!OCA.Files_External) {
|
||||
/**
|
||||
* @namespace
|
||||
*/
|
||||
OCA.External = {};
|
||||
OCA.Files_External = {};
|
||||
}
|
||||
/**
|
||||
* @namespace
|
||||
*/
|
||||
OCA.External.App = {
|
||||
OCA.Files_External.App = {
|
||||
|
||||
fileList: null,
|
||||
|
||||
|
@ -26,7 +26,7 @@ OCA.External.App = {
|
|||
return this.fileList;
|
||||
}
|
||||
|
||||
this.fileList = new OCA.External.FileList(
|
||||
this.fileList = new OCA.Files_External.FileList(
|
||||
$el,
|
||||
{
|
||||
fileActions: this._createFileActions()
|
||||
|
@ -67,10 +67,10 @@ OCA.External.App = {
|
|||
|
||||
$(document).ready(function() {
|
||||
$('#app-content-extstoragemounts').on('show', function(e) {
|
||||
OCA.External.App.initList($(e.target));
|
||||
OCA.Files_External.App.initList($(e.target));
|
||||
});
|
||||
$('#app-content-extstoragemounts').on('hide', function() {
|
||||
OCA.External.App.removeList();
|
||||
OCA.Files_External.App.removeList();
|
||||
});
|
||||
|
||||
/* Status Manager */
|
||||
|
@ -82,27 +82,27 @@ $(document).ready(function() {
|
|||
if (e.dir === '/') {
|
||||
var mount_point = e.previousDir.split('/', 2)[1];
|
||||
// Every time that we return to / root folder from a mountpoint, mount_point status is rechecked
|
||||
OCA.External.StatusManager.getMountPointList(function() {
|
||||
OCA.External.StatusManager.recheckConnectivityForMount([mount_point], true);
|
||||
OCA.Files_External.StatusManager.getMountPointList(function() {
|
||||
OCA.Files_External.StatusManager.recheckConnectivityForMount([mount_point], true);
|
||||
});
|
||||
}
|
||||
})
|
||||
.on('fileActionsReady', function(e){
|
||||
if ($.isArray(e.$files)) {
|
||||
if (OCA.External.StatusManager.mountStatus === null ||
|
||||
OCA.External.StatusManager.mountPointList === null ||
|
||||
_.size(OCA.External.StatusManager.mountStatus) !== _.size(OCA.External.StatusManager.mountPointList)) {
|
||||
if (OCA.Files_External.StatusManager.mountStatus === null ||
|
||||
OCA.Files_External.StatusManager.mountPointList === null ||
|
||||
_.size(OCA.Files_External.StatusManager.mountStatus) !== _.size(OCA.Files_External.StatusManager.mountPointList)) {
|
||||
// Will be the very first check when the files view will be loaded
|
||||
OCA.External.StatusManager.launchFullConnectivityCheckOneByOne();
|
||||
OCA.Files_External.StatusManager.launchFullConnectivityCheckOneByOne();
|
||||
} else {
|
||||
// When we change between general files view and external files view
|
||||
OCA.External.StatusManager.getMountPointList(function(){
|
||||
OCA.Files_External.StatusManager.getMountPointList(function(){
|
||||
var fileNames = [];
|
||||
$.each(e.$files, function(key, value){
|
||||
fileNames.push(value.attr('data-file'));
|
||||
});
|
||||
// Recheck if launched but work from cache
|
||||
OCA.External.StatusManager.recheckConnectivityForMount(fileNames, false);
|
||||
OCA.Files_External.StatusManager.recheckConnectivityForMount(fileNames, false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
(function() {
|
||||
|
||||
/**
|
||||
* @class OCA.External.FileList
|
||||
* @class OCA.Files_External.FileList
|
||||
* @augments OCA.Files.FileList
|
||||
*
|
||||
* @classdesc External storage file list.
|
||||
|
@ -27,7 +27,7 @@
|
|||
};
|
||||
|
||||
FileList.prototype = _.extend({}, OCA.Files.FileList.prototype,
|
||||
/** @lends OCA.External.FileList.prototype */ {
|
||||
/** @lends OCA.Files_External.FileList.prototype */ {
|
||||
appName: 'External storages',
|
||||
|
||||
_allowSelection: false,
|
||||
|
@ -43,7 +43,7 @@
|
|||
},
|
||||
|
||||
/**
|
||||
* @param {OCA.External.MountPointInfo} fileData
|
||||
* @param {OCA.Files_External.MountPointInfo} fileData
|
||||
*/
|
||||
_createRow: function(fileData) {
|
||||
// TODO: hook earlier and render the whole row here
|
||||
|
@ -138,12 +138,12 @@
|
|||
/**
|
||||
* Mount point info attributes.
|
||||
*
|
||||
* @typedef {Object} OCA.External.MountPointInfo
|
||||
* @typedef {Object} OCA.Files_External.MountPointInfo
|
||||
*
|
||||
* @property {String} name mount point name
|
||||
* @property {String} scope mount point scope "personal" or "system"
|
||||
* @property {String} backend external storage backend name
|
||||
*/
|
||||
|
||||
OCA.External.FileList = FileList;
|
||||
OCA.Files_External.FileList = FileList;
|
||||
})();
|
||||
|
|
|
@ -4,7 +4,7 @@ $(document).ready(function() {
|
|||
$tr.find('.configuration input.auth-param').attr('disabled', 'disabled').addClass('disabled-success');
|
||||
}
|
||||
|
||||
OCA.External.Settings.mountConfig.whenSelectAuthMechanism(function($tr, authMechanism, scheme, onCompletion) {
|
||||
OCA.Files_External.Settings.mountConfig.whenSelectAuthMechanism(function($tr, authMechanism, scheme, onCompletion) {
|
||||
if (authMechanism === 'oauth1::oauth1') {
|
||||
var config = $tr.find('.configuration');
|
||||
config.append($(document.createElement('input'))
|
||||
|
@ -34,7 +34,7 @@ $(document).ready(function() {
|
|||
$(token).val(result.access_token);
|
||||
$(token_secret).val(result.access_token_secret);
|
||||
$(configured).val('true');
|
||||
OCA.External.Settings.mountConfig.saveStorageConfig($tr, function(status) {
|
||||
OCA.Files_External.Settings.mountConfig.saveStorageConfig($tr, function(status) {
|
||||
if (status) {
|
||||
displayGranted($tr);
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ $(document).ready(function() {
|
|||
$(configured).val('false');
|
||||
$(token).val(result.data.request_token);
|
||||
$(token_secret).val(result.data.request_token_secret);
|
||||
OCA.External.Settings.mountConfig.saveStorageConfig(tr, function() {
|
||||
OCA.Files_External.Settings.mountConfig.saveStorageConfig(tr, function() {
|
||||
window.location = result.data.url;
|
||||
});
|
||||
} else {
|
||||
|
|
|
@ -4,7 +4,7 @@ $(document).ready(function() {
|
|||
$tr.find('.configuration input.auth-param').attr('disabled', 'disabled').addClass('disabled-success');
|
||||
}
|
||||
|
||||
OCA.External.Settings.mountConfig.whenSelectAuthMechanism(function($tr, authMechanism, scheme, onCompletion) {
|
||||
OCA.Files_External.Settings.mountConfig.whenSelectAuthMechanism(function($tr, authMechanism, scheme, onCompletion) {
|
||||
if (authMechanism === 'oauth2::oauth2') {
|
||||
var config = $tr.find('.configuration');
|
||||
config.append($(document.createElement('input'))
|
||||
|
@ -43,7 +43,7 @@ $(document).ready(function() {
|
|||
if (result && result.status == 'success') {
|
||||
$(token).val(result.data.token);
|
||||
$(configured).val('true');
|
||||
OCA.External.Settings.mountConfig.saveStorageConfig($tr, function(status) {
|
||||
OCA.Files_External.Settings.mountConfig.saveStorageConfig($tr, function(status) {
|
||||
if (status) {
|
||||
displayGranted($tr);
|
||||
}
|
||||
|
@ -80,7 +80,7 @@ $(document).ready(function() {
|
|||
if (result && result.status == 'success') {
|
||||
$(configured).val('false');
|
||||
$(token).val('false');
|
||||
OCA.External.Settings.mountConfig.saveStorageConfig(tr, function(status) {
|
||||
OCA.Files_External.Settings.mountConfig.saveStorageConfig(tr, function(status) {
|
||||
window.location = result.data.url;
|
||||
});
|
||||
} else {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
$(document).ready(function() {
|
||||
|
||||
OCA.External.Settings.mountConfig.whenSelectAuthMechanism(function($tr, authMechanism, scheme, onCompletion) {
|
||||
OCA.Files_External.Settings.mountConfig.whenSelectAuthMechanism(function($tr, authMechanism, scheme, onCompletion) {
|
||||
if (scheme === 'publickey' && authMechanism === 'publickey::rsa') {
|
||||
var config = $tr.find('.configuration');
|
||||
if ($(config).find('[name="public_key_generate"]').length === 0) {
|
||||
|
@ -53,7 +53,7 @@ $(document).ready(function() {
|
|||
if (result && result.status === 'success') {
|
||||
$(config).find('[data-parameter="public_key"]').val(result.data.public_key).keyup();
|
||||
$(config).find('[data-parameter="private_key"]').val(result.data.private_key);
|
||||
OCA.External.Settings.mountConfig.saveStorageConfig(tr, function() {
|
||||
OCA.Files_External.Settings.mountConfig.saveStorageConfig(tr, function() {
|
||||
// Nothing to do
|
||||
});
|
||||
} else {
|
||||
|
|
|
@ -124,14 +124,14 @@ var RollingQueue = function (functionList, queueWindow, callback) {
|
|||
};
|
||||
};
|
||||
|
||||
if (!OCA.External) {
|
||||
OCA.External = {};
|
||||
if (!OCA.Files_External) {
|
||||
OCA.Files_External = {};
|
||||
}
|
||||
|
||||
if (!OCA.External.StatusManager) {
|
||||
OCA.External.StatusManager = {};
|
||||
if (!OCA.Files_External.StatusManager) {
|
||||
OCA.Files_External.StatusManager = {};
|
||||
}
|
||||
|
||||
OCA.External.StatusManager.RollingQueue = RollingQueue;
|
||||
OCA.Files_External.StatusManager.RollingQueue = RollingQueue;
|
||||
|
||||
})();
|
||||
|
|
|
@ -163,7 +163,7 @@ function addSelect2 ($elements, userListLimit) {
|
|||
}
|
||||
|
||||
/**
|
||||
* @class OCA.External.Settings.StorageConfig
|
||||
* @class OCA.Files_External.Settings.StorageConfig
|
||||
*
|
||||
* @classdesc External storage config
|
||||
*/
|
||||
|
@ -185,7 +185,7 @@ StorageConfig.Visibility = {
|
|||
DEFAULT: 3
|
||||
};
|
||||
/**
|
||||
* @memberof OCA.External.Settings
|
||||
* @memberof OCA.Files_External.Settings
|
||||
*/
|
||||
StorageConfig.prototype = {
|
||||
_url: null,
|
||||
|
@ -348,8 +348,8 @@ StorageConfig.prototype = {
|
|||
};
|
||||
|
||||
/**
|
||||
* @class OCA.External.Settings.GlobalStorageConfig
|
||||
* @augments OCA.External.Settings.StorageConfig
|
||||
* @class OCA.Files_External.Settings.GlobalStorageConfig
|
||||
* @augments OCA.Files_External.Settings.StorageConfig
|
||||
*
|
||||
* @classdesc Global external storage config
|
||||
*/
|
||||
|
@ -359,10 +359,10 @@ var GlobalStorageConfig = function(id) {
|
|||
this.applicableGroups = [];
|
||||
};
|
||||
/**
|
||||
* @memberOf OCA.External.Settings
|
||||
* @memberOf OCA.Files_External.Settings
|
||||
*/
|
||||
GlobalStorageConfig.prototype = _.extend({}, StorageConfig.prototype,
|
||||
/** @lends OCA.External.Settings.GlobalStorageConfig.prototype */ {
|
||||
/** @lends OCA.Files_External.Settings.GlobalStorageConfig.prototype */ {
|
||||
_url: 'apps/files_external/globalstorages',
|
||||
|
||||
/**
|
||||
|
@ -402,8 +402,8 @@ GlobalStorageConfig.prototype = _.extend({}, StorageConfig.prototype,
|
|||
});
|
||||
|
||||
/**
|
||||
* @class OCA.External.Settings.UserStorageConfig
|
||||
* @augments OCA.External.Settings.StorageConfig
|
||||
* @class OCA.Files_External.Settings.UserStorageConfig
|
||||
* @augments OCA.Files_External.Settings.StorageConfig
|
||||
*
|
||||
* @classdesc User external storage config
|
||||
*/
|
||||
|
@ -411,13 +411,13 @@ var UserStorageConfig = function(id) {
|
|||
this.id = id;
|
||||
};
|
||||
UserStorageConfig.prototype = _.extend({}, StorageConfig.prototype,
|
||||
/** @lends OCA.External.Settings.UserStorageConfig.prototype */ {
|
||||
/** @lends OCA.Files_External.Settings.UserStorageConfig.prototype */ {
|
||||
_url: 'apps/files_external/userstorages'
|
||||
});
|
||||
|
||||
/**
|
||||
* @class OCA.External.Settings.UserGlobalStorageConfig
|
||||
* @augments OCA.External.Settings.StorageConfig
|
||||
* @class OCA.Files_External.Settings.UserGlobalStorageConfig
|
||||
* @augments OCA.Files_External.Settings.StorageConfig
|
||||
*
|
||||
* @classdesc User external storage config
|
||||
*/
|
||||
|
@ -425,13 +425,13 @@ var UserGlobalStorageConfig = function (id) {
|
|||
this.id = id;
|
||||
};
|
||||
UserGlobalStorageConfig.prototype = _.extend({}, StorageConfig.prototype,
|
||||
/** @lends OCA.External.Settings.UserStorageConfig.prototype */ {
|
||||
/** @lends OCA.Files_External.Settings.UserStorageConfig.prototype */ {
|
||||
|
||||
_url: 'apps/files_external/userglobalstorages'
|
||||
});
|
||||
|
||||
/**
|
||||
* @class OCA.External.Settings.MountOptionsDropdown
|
||||
* @class OCA.Files_External.Settings.MountOptionsDropdown
|
||||
*
|
||||
* @classdesc Dropdown for mount options
|
||||
*
|
||||
|
@ -440,7 +440,7 @@ UserGlobalStorageConfig.prototype = _.extend({}, StorageConfig.prototype,
|
|||
var MountOptionsDropdown = function() {
|
||||
};
|
||||
/**
|
||||
* @memberof OCA.External.Settings
|
||||
* @memberof OCA.Files_External.Settings
|
||||
*/
|
||||
MountOptionsDropdown.prototype = {
|
||||
/**
|
||||
|
@ -462,7 +462,7 @@ MountOptionsDropdown.prototype = {
|
|||
MountOptionsDropdown._last.hide();
|
||||
}
|
||||
|
||||
var $el = $(OCA.External.Templates.mountOptionsDropDown({
|
||||
var $el = $(OCA.Files_External.Templates.mountOptionsDropDown({
|
||||
mountOptionsEncodingLabel: t('files_external', 'Compatibility with Mac NFD encoding (slow)'),
|
||||
mountOptionsEncryptLabel: t('files_external', 'Enable encryption'),
|
||||
mountOptionsPreviewsLabel: t('files_external', 'Enable previews'),
|
||||
|
@ -549,7 +549,7 @@ MountOptionsDropdown.prototype = {
|
|||
};
|
||||
|
||||
/**
|
||||
* @class OCA.External.Settings.MountConfigListView
|
||||
* @class OCA.Files_External.Settings.MountConfigListView
|
||||
*
|
||||
* @classdesc Mount configuration list view
|
||||
*
|
||||
|
@ -574,7 +574,7 @@ MountConfigListView.ParameterTypes = {
|
|||
};
|
||||
|
||||
/**
|
||||
* @memberOf OCA.External.Settings
|
||||
* @memberOf OCA.Files_External.Settings
|
||||
*/
|
||||
MountConfigListView.prototype = _.extend({
|
||||
|
||||
|
@ -633,9 +633,9 @@ MountConfigListView.prototype = _.extend({
|
|||
this.$el = $el;
|
||||
this._isPersonal = ($el.data('admin') !== true);
|
||||
if (this._isPersonal) {
|
||||
this._storageConfigClass = OCA.External.Settings.UserStorageConfig;
|
||||
this._storageConfigClass = OCA.Files_External.Settings.UserStorageConfig;
|
||||
} else {
|
||||
this._storageConfigClass = OCA.External.Settings.GlobalStorageConfig;
|
||||
this._storageConfigClass = OCA.Files_External.Settings.GlobalStorageConfig;
|
||||
}
|
||||
|
||||
if (options && !_.isUndefined(options.userListLimit)) {
|
||||
|
@ -1008,7 +1008,7 @@ MountConfigListView.prototype = _.extend({
|
|||
* Gets the storage model from the given row
|
||||
*
|
||||
* @param $tr row element
|
||||
* @return {OCA.External.StorageConfig} storage model instance
|
||||
* @return {OCA.Files_External.StorageConfig} storage model instance
|
||||
*/
|
||||
getStorageConfig: function($tr) {
|
||||
var storageId = $tr.data('id');
|
||||
|
@ -1367,13 +1367,13 @@ $(document).ready(function() {
|
|||
});
|
||||
|
||||
// global instance
|
||||
OCA.External.Settings.mountConfig = mountConfigListView;
|
||||
OCA.Files_External.Settings.mountConfig = mountConfigListView;
|
||||
|
||||
/**
|
||||
* Legacy
|
||||
*
|
||||
* @namespace
|
||||
* @deprecated use OCA.External.Settings.mountConfig instead
|
||||
* @deprecated use OCA.Files_External.Settings.mountConfig instead
|
||||
*/
|
||||
OC.MountConfig = {
|
||||
saveStorage: _.bind(mountConfigListView.saveStorageConfig, mountConfigListView)
|
||||
|
@ -1382,14 +1382,14 @@ $(document).ready(function() {
|
|||
|
||||
// export
|
||||
|
||||
OCA.External = OCA.External || {};
|
||||
OCA.Files_External = OCA.Files_External || {};
|
||||
/**
|
||||
* @namespace
|
||||
*/
|
||||
OCA.External.Settings = OCA.External.Settings || {};
|
||||
OCA.Files_External.Settings = OCA.Files_External.Settings || {};
|
||||
|
||||
OCA.External.Settings.GlobalStorageConfig = GlobalStorageConfig;
|
||||
OCA.External.Settings.UserStorageConfig = UserStorageConfig;
|
||||
OCA.External.Settings.MountConfigListView = MountConfigListView;
|
||||
OCA.Files_External.Settings.GlobalStorageConfig = GlobalStorageConfig;
|
||||
OCA.Files_External.Settings.UserStorageConfig = UserStorageConfig;
|
||||
OCA.Files_External.Settings.MountConfigListView = MountConfigListView;
|
||||
|
||||
})();
|
||||
|
|
|
@ -14,15 +14,15 @@
|
|||
|
||||
/** @global Handlebars */
|
||||
|
||||
if (!OCA.External) {
|
||||
OCA.External = {};
|
||||
if (!OCA.Files_External) {
|
||||
OCA.Files_External = {};
|
||||
}
|
||||
|
||||
if (!OCA.External.StatusManager) {
|
||||
OCA.External.StatusManager = {};
|
||||
if (!OCA.Files_External.StatusManager) {
|
||||
OCA.Files_External.StatusManager = {};
|
||||
}
|
||||
|
||||
OCA.External.StatusManager = {
|
||||
OCA.Files_External.StatusManager = {
|
||||
|
||||
mountStatus: null,
|
||||
mountPointList: null,
|
||||
|
@ -209,18 +209,18 @@ OCA.External.StatusManager = {
|
|||
|
||||
var mountPoint = mountData.mount_point;
|
||||
if (mountStatus.status > 0) {
|
||||
var trElement = FileList.findFileEl(OCA.External.StatusManager.Utils.jqSelEscape(mountPoint));
|
||||
var trElement = FileList.findFileEl(OCA.Files_External.StatusManager.Utils.jqSelEscape(mountPoint));
|
||||
|
||||
var route = OCA.External.StatusManager.Utils.getIconRoute(trElement) + '-error';
|
||||
var route = OCA.Files_External.StatusManager.Utils.getIconRoute(trElement) + '-error';
|
||||
|
||||
if (OCA.External.StatusManager.Utils.isCorrectViewAndRootFolder()) {
|
||||
OCA.External.StatusManager.Utils.showIconError(mountPoint, $.proxy(OCA.External.StatusManager.manageMountPointError, OCA.External.StatusManager), route);
|
||||
if (OCA.Files_External.StatusManager.Utils.isCorrectViewAndRootFolder()) {
|
||||
OCA.Files_External.StatusManager.Utils.showIconError(mountPoint, $.proxy(OCA.Files_External.StatusManager.manageMountPointError, OCA.Files_External.StatusManager), route);
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
if (OCA.External.StatusManager.Utils.isCorrectViewAndRootFolder()) {
|
||||
OCA.External.StatusManager.Utils.restoreFolder(mountPoint);
|
||||
OCA.External.StatusManager.Utils.toggleLink(mountPoint, true, true);
|
||||
if (OCA.Files_External.StatusManager.Utils.isCorrectViewAndRootFolder()) {
|
||||
OCA.Files_External.StatusManager.Utils.restoreFolder(mountPoint);
|
||||
OCA.Files_External.StatusManager.Utils.toggleLink(mountPoint, true, true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -235,7 +235,7 @@ OCA.External.StatusManager = {
|
|||
processMountList: function (mountList) {
|
||||
var elementList = null;
|
||||
$.each(mountList, function (name, value) {
|
||||
var trElement = $('#fileList tr[data-file=\"' + OCA.External.StatusManager.Utils.jqSelEscape(value.mount_point) + '\"]'); //FileList.findFileEl(OCA.External.StatusManager.Utils.jqSelEscape(value.mount_point));
|
||||
var trElement = $('#fileList tr[data-file=\"' + OCA.Files_External.StatusManager.Utils.jqSelEscape(value.mount_point) + '\"]'); //FileList.findFileEl(OCA.Files_External.StatusManager.Utils.jqSelEscape(value.mount_point));
|
||||
trElement.attr('data-external-backend', value.backend);
|
||||
if (elementList) {
|
||||
elementList = elementList.add(trElement);
|
||||
|
@ -245,14 +245,14 @@ OCA.External.StatusManager = {
|
|||
});
|
||||
|
||||
if (elementList instanceof $) {
|
||||
if (OCA.External.StatusManager.Utils.isCorrectViewAndRootFolder()) {
|
||||
if (OCA.Files_External.StatusManager.Utils.isCorrectViewAndRootFolder()) {
|
||||
// Put their custom icon
|
||||
OCA.External.StatusManager.Utils.changeFolderIcon(elementList);
|
||||
OCA.Files_External.StatusManager.Utils.changeFolderIcon(elementList);
|
||||
// Save default view
|
||||
OCA.External.StatusManager.Utils.storeDefaultFolderIconAndBgcolor(elementList);
|
||||
OCA.Files_External.StatusManager.Utils.storeDefaultFolderIconAndBgcolor(elementList);
|
||||
// Disable row until check status
|
||||
elementList.addClass('externalDisabledRow');
|
||||
OCA.External.StatusManager.Utils.toggleLink(elementList.find('a.name'), false, false);
|
||||
OCA.Files_External.StatusManager.Utils.toggleLink(elementList.find('a.name'), false, false);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -289,7 +289,7 @@ OCA.External.StatusManager = {
|
|||
ajaxQueue.push(queueElement);
|
||||
});
|
||||
|
||||
var rolQueue = new OCA.External.StatusManager.RollingQueue(ajaxQueue, 4, function () {
|
||||
var rolQueue = new OCA.Files_External.StatusManager.RollingQueue(ajaxQueue, 4, function () {
|
||||
if (!self.notificationHasShown) {
|
||||
var showNotification = false;
|
||||
$.each(self.mountStatus, function (key, value) {
|
||||
|
@ -335,7 +335,7 @@ OCA.External.StatusManager = {
|
|||
};
|
||||
ajaxQueue.push(queueElement);
|
||||
});
|
||||
new OCA.External.StatusManager.RollingQueue(ajaxQueue, 4).runQueue();
|
||||
new OCA.Files_External.StatusManager.RollingQueue(ajaxQueue, 4).runQueue();
|
||||
},
|
||||
|
||||
|
||||
|
@ -392,7 +392,7 @@ OCA.External.StatusManager = {
|
|||
* @param mountData
|
||||
*/
|
||||
showCredentialsDialog: function (mountPoint, mountData) {
|
||||
var dialog = $(OCA.External.Templates.credentialsDialog({
|
||||
var dialog = $(OCA.Files_External.Templates.credentialsDialog({
|
||||
credentials_text: t('files_external', 'Please enter the credentials for the {mount} mount', {
|
||||
'mount': mountPoint
|
||||
}),
|
||||
|
@ -422,7 +422,7 @@ OCA.External.StatusManager = {
|
|||
OC.Notification.show(t('files_external', 'Credentials saved'), {type: 'error'});
|
||||
dialog.ocdialog('close');
|
||||
/* Trigger status check again */
|
||||
OCA.External.StatusManager.recheckConnectivityForMount([OC.basename(data.mountPoint)], true);
|
||||
OCA.Files_External.StatusManager.recheckConnectivityForMount([OC.basename(data.mountPoint)], true);
|
||||
},
|
||||
error: function () {
|
||||
$('.oc-dialog-close').show();
|
||||
|
@ -461,11 +461,11 @@ OCA.External.StatusManager = {
|
|||
}
|
||||
};
|
||||
|
||||
OCA.External.StatusManager.Utils = {
|
||||
OCA.Files_External.StatusManager.Utils = {
|
||||
|
||||
showIconError: function (folder, clickAction, errorImageUrl) {
|
||||
var imageUrl = "url(" + errorImageUrl + ")";
|
||||
var trFolder = $('#fileList tr[data-file=\"' + OCA.External.StatusManager.Utils.jqSelEscape(folder) + '\"]'); //FileList.findFileEl(OCA.External.StatusManager.Utils.jqSelEscape(folder));
|
||||
var trFolder = $('#fileList tr[data-file=\"' + OCA.Files_External.StatusManager.Utils.jqSelEscape(folder) + '\"]'); //FileList.findFileEl(OCA.Files_External.StatusManager.Utils.jqSelEscape(folder));
|
||||
this.changeFolderIcon(folder, imageUrl);
|
||||
this.toggleLink(folder, false, clickAction);
|
||||
trFolder.addClass('externalErroredRow');
|
||||
|
@ -479,7 +479,7 @@ OCA.External.StatusManager.Utils = {
|
|||
if (folder instanceof $) {
|
||||
trFolder = folder;
|
||||
} else {
|
||||
trFolder = $('#fileList tr[data-file=\"' + OCA.External.StatusManager.Utils.jqSelEscape(folder) + '\"]'); //FileList.findFileEl(OCA.External.StatusManager.Utils.jqSelEscape(folder)); //$('#fileList tr[data-file=\"' + OCA.External.StatusManager.Utils.jqSelEscape(folder) + '\"]');
|
||||
trFolder = $('#fileList tr[data-file=\"' + OCA.Files_External.StatusManager.Utils.jqSelEscape(folder) + '\"]'); //FileList.findFileEl(OCA.Files_External.StatusManager.Utils.jqSelEscape(folder)); //$('#fileList tr[data-file=\"' + OCA.Files_External.StatusManager.Utils.jqSelEscape(folder) + '\"]');
|
||||
}
|
||||
trFolder.each(function () {
|
||||
var thisElement = $(this);
|
||||
|
@ -505,8 +505,8 @@ OCA.External.StatusManager.Utils = {
|
|||
if (folder instanceof $) {
|
||||
trFolder = folder;
|
||||
} else {
|
||||
// can't use here FileList.findFileEl(OCA.External.StatusManager.Utils.jqSelEscape(folder)); return incorrect instance of filelist
|
||||
trFolder = $('#fileList tr[data-file=\"' + OCA.External.StatusManager.Utils.jqSelEscape(folder) + '\"]');
|
||||
// can't use here FileList.findFileEl(OCA.Files_External.StatusManager.Utils.jqSelEscape(folder)); return incorrect instance of filelist
|
||||
trFolder = $('#fileList tr[data-file=\"' + OCA.Files_External.StatusManager.Utils.jqSelEscape(folder) + '\"]');
|
||||
}
|
||||
trFolder.removeClass('externalErroredRow').removeClass('externalDisabledRow');
|
||||
var tdChilds = trFolder.find("td.filename div.thumbnail");
|
||||
|
@ -526,14 +526,14 @@ OCA.External.StatusManager.Utils = {
|
|||
if (filename instanceof $) {
|
||||
//trElementList
|
||||
$.each(filename, function (index) {
|
||||
route = OCA.External.StatusManager.Utils.getIconRoute($(this));
|
||||
route = OCA.Files_External.StatusManager.Utils.getIconRoute($(this));
|
||||
$(this).attr("data-icon", route);
|
||||
$(this).find('td.filename div.thumbnail').css('background-image', "url(" + route + ")").css('display', 'none').css('display', 'inline');
|
||||
});
|
||||
} else {
|
||||
file = $("#fileList tr[data-file=\"" + this.jqSelEscape(filename) + "\"] > td.filename div.thumbnail");
|
||||
var parentTr = file.parents('tr:first');
|
||||
route = OCA.External.StatusManager.Utils.getIconRoute(parentTr);
|
||||
route = OCA.Files_External.StatusManager.Utils.getIconRoute(parentTr);
|
||||
parentTr.attr("data-icon", route);
|
||||
file.css('background-image', "url(" + route + ")").css('display', 'none').css('display', 'inline');
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
(function() {
|
||||
var template = Handlebars.template, templates = OCA.External.Templates = OCA.External.Templates || {};
|
||||
var template = Handlebars.template, templates = OCA.Files_External.Templates = OCA.Files_External.Templates || {};
|
||||
templates['credentialsDialog'] = template({"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) {
|
||||
var helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=helpers.helperMissing, alias3="function", alias4=container.escapeExpression;
|
||||
|
||||
|
|
|
@ -19,8 +19,8 @@
|
|||
*
|
||||
*/
|
||||
|
||||
describe('OCA.External.App tests', function() {
|
||||
var App = OCA.External.App;
|
||||
describe('OCA.Files_External.App tests', function() {
|
||||
var App = OCA.Files_External.App;
|
||||
var fileList;
|
||||
|
||||
beforeEach(function() {
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*/
|
||||
|
||||
describe('OCA.External.FileList tests', function() {
|
||||
describe('OCA.Files_External.FileList tests', function() {
|
||||
var testFiles, alertStub, notificationStub, fileList;
|
||||
|
||||
beforeEach(function() {
|
||||
|
@ -62,7 +62,7 @@ describe('OCA.External.FileList tests', function() {
|
|||
var ocsResponse;
|
||||
|
||||
beforeEach(function() {
|
||||
fileList = new OCA.External.FileList(
|
||||
fileList = new OCA.Files_External.FileList(
|
||||
$('#app-content-container')
|
||||
);
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*/
|
||||
|
||||
describe('OCA.External.Settings tests', function() {
|
||||
describe('OCA.Files_External.Settings tests', function() {
|
||||
var clock;
|
||||
var select2Stub;
|
||||
var select2ApplicableUsers;
|
||||
|
@ -156,7 +156,7 @@ describe('OCA.External.Settings tests', function() {
|
|||
|
||||
beforeEach(function() {
|
||||
var $el = $('#externalStorage');
|
||||
view = new OCA.External.Settings.MountConfigListView($el, {encryptionEnabled: false});
|
||||
view = new OCA.Files_External.Settings.MountConfigListView($el, {encryptionEnabled: false});
|
||||
});
|
||||
afterEach(function() {
|
||||
view = null;
|
||||
|
|
|
@ -4,6 +4,10 @@
|
|||
|
||||
.share-autocomplete-item {
|
||||
display: flex;
|
||||
|
||||
&.merged {
|
||||
margin-left: 32px;
|
||||
}
|
||||
.autocomplete-item-text {
|
||||
margin-left: 10px;
|
||||
margin-right: 10px;
|
||||
|
@ -12,6 +16,27 @@
|
|||
overflow: hidden;
|
||||
line-height: 32px;
|
||||
vertical-align: middle;
|
||||
flex-grow: 1;
|
||||
.ui-state-highlight {
|
||||
border: none;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
&.with-description {
|
||||
.autocomplete-item-text {
|
||||
line-height: 100%;
|
||||
}
|
||||
}
|
||||
.autocomplete-item-details {
|
||||
display: block;
|
||||
line-height: 130%;
|
||||
font-size: 90%;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.icon {
|
||||
opacity: .7;
|
||||
margin-right: 7px;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -204,8 +229,8 @@
|
|||
}
|
||||
|
||||
.ui-autocomplete {
|
||||
/* limit dropdown height to 4 1/2 entries */
|
||||
max-height: calc(36px * 4.5);;
|
||||
/* limit dropdown height to 6 1/2 entries */
|
||||
max-height: calc(36px * 6.5);
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
z-index: 1550 !important;
|
||||
|
|
|
@ -252,6 +252,7 @@ class ShareAPIController extends OCSController {
|
|||
|
||||
|
||||
$result['mail_send'] = $share->getMailSend() ? 1 : 0;
|
||||
$result['hide_download'] = $share->getHideDownload() ? 1 : 0;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
@ -745,6 +746,7 @@ class ShareAPIController extends OCSController {
|
|||
* @param string $publicUpload
|
||||
* @param string $expireDate
|
||||
* @param string $note
|
||||
* @param string $hideDownload
|
||||
* @return DataResponse
|
||||
* @throws LockedException
|
||||
* @throws NotFoundException
|
||||
|
@ -759,7 +761,8 @@ class ShareAPIController extends OCSController {
|
|||
string $sendPasswordByTalk = null,
|
||||
string $publicUpload = null,
|
||||
string $expireDate = null,
|
||||
string $note = null
|
||||
string $note = null,
|
||||
string $hideDownload = null
|
||||
): DataResponse {
|
||||
try {
|
||||
$share = $this->getShareById($id);
|
||||
|
@ -773,7 +776,7 @@ class ShareAPIController extends OCSController {
|
|||
throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist'));
|
||||
}
|
||||
|
||||
if ($permissions === null && $password === null && $sendPasswordByTalk === null && $publicUpload === null && $expireDate === null && $note === null) {
|
||||
if ($permissions === null && $password === null && $sendPasswordByTalk === null && $publicUpload === null && $expireDate === null && $note === null && $hideDownload === null) {
|
||||
throw new OCSBadRequestException($this->l->t('Wrong or no update parameter given'));
|
||||
}
|
||||
|
||||
|
@ -786,6 +789,13 @@ class ShareAPIController extends OCSController {
|
|||
*/
|
||||
if ($share->getShareType() === Share::SHARE_TYPE_LINK) {
|
||||
|
||||
// Update hide download state
|
||||
if ($hideDownload === 'true') {
|
||||
$share->setHideDownload(true);
|
||||
} else if ($hideDownload === 'false') {
|
||||
$share->setHideDownload(false);
|
||||
}
|
||||
|
||||
$newPermissions = null;
|
||||
if ($publicUpload === 'true') {
|
||||
$newPermissions = Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE;
|
||||
|
|
|
@ -321,6 +321,7 @@ class ShareController extends AuthPublicShareController {
|
|||
$shareTmpl['dir'] = '';
|
||||
$shareTmpl['nonHumanFileSize'] = $share->getNode()->getSize();
|
||||
$shareTmpl['fileSize'] = \OCP\Util::humanFileSize($share->getNode()->getSize());
|
||||
$shareTmpl['hideDownload'] = $share->getHideDownload();
|
||||
|
||||
// Show file list
|
||||
$hideFileList = false;
|
||||
|
@ -444,12 +445,14 @@ class ShareController extends AuthPublicShareController {
|
|||
$response = new PublicTemplateResponse($this->appName, 'public', $shareTmpl);
|
||||
$response->setHeaderTitle($shareTmpl['filename']);
|
||||
$response->setHeaderDetails($this->l10n->t('shared by %s', [$shareTmpl['displayName']]));
|
||||
$response->setHeaderActions([
|
||||
new SimpleMenuAction('download', $this->l10n->t('Download'), 'icon-download-white', $shareTmpl['downloadURL'], 0),
|
||||
new SimpleMenuAction('download', $this->l10n->t('Download'), 'icon-download', $shareTmpl['downloadURL'], 10, $shareTmpl['fileSize']),
|
||||
new LinkMenuAction($this->l10n->t('Direct link'), 'icon-public', $shareTmpl['previewURL']),
|
||||
new ExternalShareMenuAction($this->l10n->t('Add to your Nextcloud'), 'icon-external', $shareTmpl['owner'], $shareTmpl['displayName'], $shareTmpl['filename']),
|
||||
]);
|
||||
if (!$share->getHideDownload()) {
|
||||
$response->setHeaderActions([
|
||||
new SimpleMenuAction('download', $this->l10n->t('Download'), 'icon-download-white', $shareTmpl['downloadURL'], 0),
|
||||
new SimpleMenuAction('download', $this->l10n->t('Download'), 'icon-download', $shareTmpl['downloadURL'], 10, $shareTmpl['fileSize']),
|
||||
new LinkMenuAction($this->l10n->t('Direct link'), 'icon-public', $shareTmpl['previewURL']),
|
||||
new ExternalShareMenuAction($this->l10n->t('Add to your Nextcloud'), 'icon-external', $shareTmpl['owner'], $shareTmpl['displayName'], $shareTmpl['filename']),
|
||||
]);
|
||||
}
|
||||
|
||||
$response->setContentSecurityPolicy($csp);
|
||||
|
||||
|
|
|
@ -11,13 +11,16 @@
|
|||
<input type="hidden" id="filesApp" name="filesApp" value="1">
|
||||
<input type="hidden" id="isPublic" name="isPublic" value="1">
|
||||
<input type="hidden" name="dir" value="<?php p($_['dir']) ?>" id="dir">
|
||||
<input type="hidden" name="downloadURL" value="<?php p($_['downloadURL']) ?>" id="downloadURL">
|
||||
<?php if (!$_['hideDownload']): ?>
|
||||
<input type="hidden" name="downloadURL" value="<?php p($_['downloadURL']) ?>" id="downloadURL">
|
||||
<?php endif; ?>
|
||||
<input type="hidden" name="previewURL" value="<?php p($_['previewURL']) ?>" id="previewURL">
|
||||
<input type="hidden" name="sharingToken" value="<?php p($_['sharingToken']) ?>" id="sharingToken">
|
||||
<input type="hidden" name="filename" value="<?php p($_['filename']) ?>" id="filename">
|
||||
<input type="hidden" name="mimetype" value="<?php p($_['mimetype']) ?>" id="mimetype">
|
||||
<input type="hidden" name="previewSupported" value="<?php p($_['previewSupported'] ? 'true' : 'false'); ?>" id="previewSupported">
|
||||
<input type="hidden" name="mimetypeIcon" value="<?php p(\OC::$server->getMimeTypeDetector()->mimeTypeIcon($_['mimetype'])); ?>" id="mimetypeIcon">
|
||||
<input type="hidden" name="hideDownload" value="<?php p($_['hideDownload'] ? 'true' : 'false'); ?>" id="hideDownload">
|
||||
<?php
|
||||
$upload_max_filesize = OC::$server->getIniWrapper()->getBytes('upload_max_filesize');
|
||||
$post_max_size = OC::$server->getIniWrapper()->getBytes('post_max_size');
|
||||
|
@ -58,7 +61,7 @@ $maxUploadFilesize = min($upload_max_filesize, $post_max_size);
|
|||
<!-- Preview frame is filled via JS to support SVG images for modern browsers -->
|
||||
<div id="imgframe"></div>
|
||||
<?php endif; ?>
|
||||
<?php if ($_['previewURL'] === $_['downloadURL']): ?>
|
||||
<?php if ($_['previewURL'] === $_['downloadURL'] && !$_['hideDownload']): ?>
|
||||
<div class="directDownload">
|
||||
<a href="<?php p($_['downloadURL']); ?>" id="downloadFile" class="button">
|
||||
<span class="icon icon-download"></span>
|
||||
|
@ -97,4 +100,4 @@ $maxUploadFilesize = min($upload_max_filesize, $post_max_size);
|
|||
data-url="<?php p(\OC::$server->getURLGenerator()->linkTo('files', 'ajax/upload.php')); ?>" />
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -353,6 +353,7 @@ class ShareAPIControllerTest extends TestCase {
|
|||
'note' => 'personal note',
|
||||
'displayname_file_owner' => 'ownerDisplay',
|
||||
'mimetype' => 'myMimeType',
|
||||
'hide_download' => 0,
|
||||
];
|
||||
$data[] = [$share, $expected];
|
||||
|
||||
|
@ -397,6 +398,7 @@ class ShareAPIControllerTest extends TestCase {
|
|||
'note' => 'personal note',
|
||||
'displayname_file_owner' => 'ownerDisplay',
|
||||
'mimetype' => 'myFolderMimeType',
|
||||
'hide_download' => 0,
|
||||
];
|
||||
$data[] = [$share, $expected];
|
||||
|
||||
|
@ -445,6 +447,7 @@ class ShareAPIControllerTest extends TestCase {
|
|||
'note' => 'personal note',
|
||||
'displayname_file_owner' => 'ownerDisplay',
|
||||
'mimetype' => 'myFolderMimeType',
|
||||
'hide_download' => 0,
|
||||
];
|
||||
$data[] = [$share, $expected];
|
||||
|
||||
|
@ -2175,6 +2178,7 @@ class ShareAPIControllerTest extends TestCase {
|
|||
'note' => 'personal note',
|
||||
'mail_send' => 0,
|
||||
'mimetype' => 'myMimeType',
|
||||
'hide_download' => 0,
|
||||
], $share, [], false
|
||||
];
|
||||
// User backend up
|
||||
|
@ -2204,6 +2208,7 @@ class ShareAPIControllerTest extends TestCase {
|
|||
'share_with_displayname' => 'recipientDN',
|
||||
'mail_send' => 0,
|
||||
'mimetype' => 'myMimeType',
|
||||
'hide_download' => 0,
|
||||
], $share, [
|
||||
['owner', $owner],
|
||||
['initiator', $initiator],
|
||||
|
@ -2249,6 +2254,7 @@ class ShareAPIControllerTest extends TestCase {
|
|||
'share_with_displayname' => 'recipient',
|
||||
'mail_send' => 0,
|
||||
'mimetype' => 'myMimeType',
|
||||
'hide_download' => 0,
|
||||
], $share, [], false
|
||||
];
|
||||
|
||||
|
@ -2292,6 +2298,7 @@ class ShareAPIControllerTest extends TestCase {
|
|||
'share_with_displayname' => 'recipientGroupDisplayName',
|
||||
'mail_send' => 0,
|
||||
'mimetype' => 'myMimeType',
|
||||
'hide_download' => 0,
|
||||
], $share, [], false
|
||||
];
|
||||
|
||||
|
@ -2333,6 +2340,7 @@ class ShareAPIControllerTest extends TestCase {
|
|||
'share_with_displayname' => 'recipientGroup2',
|
||||
'mail_send' => 0,
|
||||
'mimetype' => 'myMimeType',
|
||||
'hide_download' => 0,
|
||||
], $share, [], false
|
||||
];
|
||||
|
||||
|
@ -2377,6 +2385,7 @@ class ShareAPIControllerTest extends TestCase {
|
|||
'mail_send' => 0,
|
||||
'url' => 'myLink',
|
||||
'mimetype' => 'myMimeType',
|
||||
'hide_download' => 0,
|
||||
], $share, [], false
|
||||
];
|
||||
|
||||
|
@ -2418,6 +2427,7 @@ class ShareAPIControllerTest extends TestCase {
|
|||
'share_with_displayname' => 'foobar',
|
||||
'mail_send' => 0,
|
||||
'mimetype' => 'myFolderMimeType',
|
||||
'hide_download' => 0,
|
||||
], $share, [], false
|
||||
];
|
||||
|
||||
|
@ -2462,6 +2472,7 @@ class ShareAPIControllerTest extends TestCase {
|
|||
'share_with_avatar' => 'path/to/the/avatar',
|
||||
'mail_send' => 0,
|
||||
'mimetype' => 'myFolderMimeType',
|
||||
'hide_download' => 0,
|
||||
], $share, [], false
|
||||
];
|
||||
|
||||
|
@ -2504,6 +2515,7 @@ class ShareAPIControllerTest extends TestCase {
|
|||
'share_with_avatar' => '',
|
||||
'mail_send' => 0,
|
||||
'mimetype' => 'myFolderMimeType',
|
||||
'hide_download' => 0,
|
||||
], $share, [], false
|
||||
];
|
||||
|
||||
|
@ -2546,6 +2558,7 @@ class ShareAPIControllerTest extends TestCase {
|
|||
'share_with_avatar' => '',
|
||||
'mail_send' => 0,
|
||||
'mimetype' => 'myFolderMimeType',
|
||||
'hide_download' => 0,
|
||||
], $share, [], false
|
||||
];
|
||||
|
||||
|
@ -2603,7 +2616,8 @@ class ShareAPIControllerTest extends TestCase {
|
|||
'mail_send' => 0,
|
||||
'mimetype' => 'myFolderMimeType',
|
||||
'password' => 'password',
|
||||
'send_password_by_talk' => false
|
||||
'send_password_by_talk' => false,
|
||||
'hide_download' => 0,
|
||||
], $share, [], false
|
||||
];
|
||||
|
||||
|
@ -2647,7 +2661,8 @@ class ShareAPIControllerTest extends TestCase {
|
|||
'mail_send' => 0,
|
||||
'mimetype' => 'myFolderMimeType',
|
||||
'password' => 'password',
|
||||
'send_password_by_talk' => true
|
||||
'send_password_by_talk' => true,
|
||||
'hide_download' => 0,
|
||||
], $share, [], false
|
||||
];
|
||||
|
||||
|
@ -2787,6 +2802,7 @@ class ShareAPIControllerTest extends TestCase {
|
|||
'share_with_displayname' => '',
|
||||
'mail_send' => 0,
|
||||
'mimetype' => 'myMimeType',
|
||||
'hide_download' => 0,
|
||||
], $share, false, []
|
||||
];
|
||||
|
||||
|
@ -2828,6 +2844,7 @@ class ShareAPIControllerTest extends TestCase {
|
|||
'share_with_displayname' => 'recipientRoomName',
|
||||
'mail_send' => 0,
|
||||
'mimetype' => 'myMimeType',
|
||||
'hide_download' => 0,
|
||||
], $share, true, [
|
||||
'share_with_displayname' => 'recipientRoomName'
|
||||
]
|
||||
|
|
|
@ -287,7 +287,8 @@ class ShareControllerTest extends \Test\TestCase {
|
|||
'shareUrl' => null,
|
||||
'previewImage' => null,
|
||||
'previewURL' => 'downloadURL',
|
||||
'note' => $note
|
||||
'note' => $note,
|
||||
'hideDownload' => false
|
||||
);
|
||||
|
||||
$csp = new \OCP\AppFramework\Http\ContentSecurityPolicy();
|
||||
|
@ -306,6 +307,120 @@ class ShareControllerTest extends \Test\TestCase {
|
|||
$this->assertEquals($expectedResponse, $response);
|
||||
}
|
||||
|
||||
public function testShowShareHideDownload() {
|
||||
$note = 'personal note';
|
||||
|
||||
$this->shareController->setToken('token');
|
||||
|
||||
$owner = $this->getMockBuilder(IUser::class)->getMock();
|
||||
$owner->method('getDisplayName')->willReturn('ownerDisplay');
|
||||
$owner->method('getUID')->willReturn('ownerUID');
|
||||
|
||||
$file = $this->getMockBuilder('OCP\Files\File')->getMock();
|
||||
$file->method('getName')->willReturn('file1.txt');
|
||||
$file->method('getMimetype')->willReturn('text/plain');
|
||||
$file->method('getSize')->willReturn(33);
|
||||
$file->method('isReadable')->willReturn(true);
|
||||
$file->method('isShareable')->willReturn(true);
|
||||
|
||||
$share = \OC::$server->getShareManager()->newShare();
|
||||
$share->setId(42);
|
||||
$share->setPassword('password')
|
||||
->setShareOwner('ownerUID')
|
||||
->setNode($file)
|
||||
->setNote($note)
|
||||
->setTarget('/file1.txt')
|
||||
->setHideDownload(true);
|
||||
|
||||
$this->session->method('exists')->with('public_link_authenticated')->willReturn(true);
|
||||
$this->session->method('get')->with('public_link_authenticated')->willReturn('42');
|
||||
|
||||
// Even if downloads are disabled the "downloadURL" parameter is
|
||||
// provided to the template, as it is needed to preview audio and GIF
|
||||
// files.
|
||||
$this->urlGenerator->expects($this->at(0))
|
||||
->method('linkToRouteAbsolute')
|
||||
->with('files_sharing.sharecontroller.downloadShare', ['token' => 'token'])
|
||||
->willReturn('downloadURL');
|
||||
|
||||
$this->previewManager->method('isMimeSupported')->with('text/plain')->willReturn(true);
|
||||
|
||||
$this->config->method('getSystemValue')
|
||||
->willReturnMap(
|
||||
[
|
||||
['max_filesize_animated_gifs_public_sharing', 10, 10],
|
||||
['enable_previews', true, true],
|
||||
['preview_max_x', 1024, 1024],
|
||||
['preview_max_y', 1024, 1024],
|
||||
]
|
||||
);
|
||||
$shareTmpl['maxSizeAnimateGif'] = $this->config->getSystemValue('max_filesize_animated_gifs_public_sharing', 10);
|
||||
$shareTmpl['previewEnabled'] = $this->config->getSystemValue('enable_previews', true);
|
||||
|
||||
$this->shareManager
|
||||
->expects($this->once())
|
||||
->method('getShareByToken')
|
||||
->with('token')
|
||||
->willReturn($share);
|
||||
$this->config
|
||||
->expects($this->once())
|
||||
->method('getAppValue')
|
||||
->with('core', 'shareapi_public_link_disclaimertext', null)
|
||||
->willReturn('My disclaimer text');
|
||||
|
||||
$this->userManager->method('get')->with('ownerUID')->willReturn($owner);
|
||||
|
||||
$this->eventDispatcher->expects($this->once())
|
||||
->method('dispatch')
|
||||
->with('OCA\Files_Sharing::loadAdditionalScripts');
|
||||
|
||||
$this->l10n->expects($this->any())
|
||||
->method('t')
|
||||
->will($this->returnCallback(function($text, $parameters) {
|
||||
return vsprintf($text, $parameters);
|
||||
}));
|
||||
|
||||
$response = $this->shareController->showShare();
|
||||
$sharedTmplParams = array(
|
||||
'displayName' => 'ownerDisplay',
|
||||
'owner' => 'ownerUID',
|
||||
'filename' => 'file1.txt',
|
||||
'directory_path' => '/file1.txt',
|
||||
'mimetype' => 'text/plain',
|
||||
'dirToken' => 'token',
|
||||
'sharingToken' => 'token',
|
||||
'server2serversharing' => true,
|
||||
'protected' => 'true',
|
||||
'dir' => '',
|
||||
'downloadURL' => 'downloadURL',
|
||||
'fileSize' => '33 B',
|
||||
'nonHumanFileSize' => 33,
|
||||
'maxSizeAnimateGif' => 10,
|
||||
'previewSupported' => true,
|
||||
'previewEnabled' => true,
|
||||
'previewMaxX' => 1024,
|
||||
'previewMaxY' => 1024,
|
||||
'hideFileList' => false,
|
||||
'shareOwner' => 'ownerDisplay',
|
||||
'disclaimer' => 'My disclaimer text',
|
||||
'shareUrl' => null,
|
||||
'previewImage' => null,
|
||||
'previewURL' => 'downloadURL',
|
||||
'note' => $note,
|
||||
'hideDownload' => true
|
||||
);
|
||||
|
||||
$csp = new \OCP\AppFramework\Http\ContentSecurityPolicy();
|
||||
$csp->addAllowedFrameDomain('\'self\'');
|
||||
$expectedResponse = new PublicTemplateResponse($this->appName, 'public', $sharedTmplParams);
|
||||
$expectedResponse->setContentSecurityPolicy($csp);
|
||||
$expectedResponse->setHeaderTitle($sharedTmplParams['filename']);
|
||||
$expectedResponse->setHeaderDetails('shared by ' . $sharedTmplParams['displayName']);
|
||||
$expectedResponse->setHeaderActions([]);
|
||||
|
||||
$this->assertEquals($expectedResponse, $response);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \OCP\Files\NotFoundException
|
||||
*/
|
||||
|
|
|
@ -41,4 +41,8 @@
|
|||
<collection>OCA\Files_Versions\Sabre\RootCollection</collection>
|
||||
</collections>
|
||||
</sabre>
|
||||
|
||||
<versions>
|
||||
<backend for="OCP\Files\Storage\IStorage">OCA\Files_Versions\Versions\LegacyVersionsBackend</backend>
|
||||
</versions>
|
||||
</info>
|
||||
|
|
|
@ -23,4 +23,11 @@ return array(
|
|||
'OCA\\Files_Versions\\Sabre\\VersionHome' => $baseDir . '/../lib/Sabre/VersionHome.php',
|
||||
'OCA\\Files_Versions\\Sabre\\VersionRoot' => $baseDir . '/../lib/Sabre/VersionRoot.php',
|
||||
'OCA\\Files_Versions\\Storage' => $baseDir . '/../lib/Storage.php',
|
||||
'OCA\\Files_Versions\\Versions\\BackendNotFoundException' => $baseDir . '/../lib/Versions/BackendNotFoundException.php',
|
||||
'OCA\\Files_Versions\\Versions\\IVersion' => $baseDir . '/../lib/Versions/IVersion.php',
|
||||
'OCA\\Files_Versions\\Versions\\IVersionBackend' => $baseDir . '/../lib/Versions/IVersionBackend.php',
|
||||
'OCA\\Files_Versions\\Versions\\IVersionManager' => $baseDir . '/../lib/Versions/IVersionManager.php',
|
||||
'OCA\\Files_Versions\\Versions\\LegacyVersionsBackend' => $baseDir . '/../lib/Versions/LegacyVersionsBackend.php',
|
||||
'OCA\\Files_Versions\\Versions\\Version' => $baseDir . '/../lib/Versions/Version.php',
|
||||
'OCA\\Files_Versions\\Versions\\VersionManager' => $baseDir . '/../lib/Versions/VersionManager.php',
|
||||
);
|
||||
|
|
|
@ -38,6 +38,13 @@ class ComposerStaticInitFiles_Versions
|
|||
'OCA\\Files_Versions\\Sabre\\VersionHome' => __DIR__ . '/..' . '/../lib/Sabre/VersionHome.php',
|
||||
'OCA\\Files_Versions\\Sabre\\VersionRoot' => __DIR__ . '/..' . '/../lib/Sabre/VersionRoot.php',
|
||||
'OCA\\Files_Versions\\Storage' => __DIR__ . '/..' . '/../lib/Storage.php',
|
||||
'OCA\\Files_Versions\\Versions\\BackendNotFoundException' => __DIR__ . '/..' . '/../lib/Versions/BackendNotFoundException.php',
|
||||
'OCA\\Files_Versions\\Versions\\IVersion' => __DIR__ . '/..' . '/../lib/Versions/IVersion.php',
|
||||
'OCA\\Files_Versions\\Versions\\IVersionBackend' => __DIR__ . '/..' . '/../lib/Versions/IVersionBackend.php',
|
||||
'OCA\\Files_Versions\\Versions\\IVersionManager' => __DIR__ . '/..' . '/../lib/Versions/IVersionManager.php',
|
||||
'OCA\\Files_Versions\\Versions\\LegacyVersionsBackend' => __DIR__ . '/..' . '/../lib/Versions/LegacyVersionsBackend.php',
|
||||
'OCA\\Files_Versions\\Versions\\Version' => __DIR__ . '/..' . '/../lib/Versions/Version.php',
|
||||
'OCA\\Files_Versions\\Versions\\VersionManager' => __DIR__ . '/..' . '/../lib/Versions/VersionManager.php',
|
||||
);
|
||||
|
||||
public static function getInitializer(ClassLoader $loader)
|
||||
|
|
|
@ -38,6 +38,10 @@
|
|||
return t('files_versions', 'Versions');
|
||||
},
|
||||
|
||||
getIcon: function() {
|
||||
return 'icon-history';
|
||||
},
|
||||
|
||||
nextPage: function() {
|
||||
if (this._loading) {
|
||||
return;
|
||||
|
|
|
@ -24,9 +24,10 @@
|
|||
namespace OCA\Files_Versions\AppInfo;
|
||||
|
||||
use OCA\DAV\Connector\Sabre\Principal;
|
||||
use OCA\Files_Versions\Versions\IVersionManager;
|
||||
use OCA\Files_Versions\Versions\VersionManager;
|
||||
use OCP\AppFramework\App;
|
||||
use OCA\Files_Versions\Expiration;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\AppFramework\IAppContainer;
|
||||
use OCA\Files_Versions\Capabilities;
|
||||
|
||||
class Application extends App {
|
||||
|
@ -43,14 +44,45 @@ class Application extends App {
|
|||
/*
|
||||
* Register $principalBackend for the DAV collection
|
||||
*/
|
||||
$container->registerService('principalBackend', function () {
|
||||
$container->registerService('principalBackend', function (IAppContainer $c) {
|
||||
$server = $c->getServer();
|
||||
return new Principal(
|
||||
\OC::$server->getUserManager(),
|
||||
\OC::$server->getGroupManager(),
|
||||
\OC::$server->getShareManager(),
|
||||
\OC::$server->getUserSession(),
|
||||
\OC::$server->getConfig()
|
||||
$server->getUserManager(),
|
||||
$server->getGroupManager(),
|
||||
$server->getShareManager(),
|
||||
$server->getUserSession(),
|
||||
$server->getConfig()
|
||||
);
|
||||
});
|
||||
|
||||
$container->registerService(IVersionManager::class, function(IAppContainer $c) {
|
||||
return new VersionManager();
|
||||
});
|
||||
|
||||
$this->registerVersionBackends();
|
||||
}
|
||||
|
||||
public function registerVersionBackends() {
|
||||
$server = $this->getContainer()->getServer();
|
||||
$logger = $server->getLogger();
|
||||
$appManager = $server->getAppManager();
|
||||
/** @var IVersionManager $versionManager */
|
||||
$versionManager = $this->getContainer()->getServer()->query(IVersionManager::class);
|
||||
foreach($appManager->getInstalledApps() as $app) {
|
||||
$appInfo = $appManager->getAppInfo($app);
|
||||
if (isset($appInfo['versions'])) {
|
||||
$backends = $appInfo['versions'];
|
||||
foreach($backends as $backend) {
|
||||
$class = $backend['@value'];
|
||||
$for = $backend['@attributes']['for'];
|
||||
try {
|
||||
$backendObject = $server->query($class);
|
||||
$versionManager->registerBackend($for, $backendObject);
|
||||
} catch (\Exception $e) {
|
||||
$logger->logException($e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,45 +21,53 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\Files_Versions\Controller;
|
||||
|
||||
use OCA\Files_Versions\Versions\IVersionManager;
|
||||
use OCP\AppFramework\Controller;
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\AppFramework\Http\DataResponse;
|
||||
use OCP\AppFramework\Http\FileDisplayResponse;
|
||||
use OCP\Files\File;
|
||||
use OCP\Files\Folder;
|
||||
use OCP\Files\IMimeTypeDetector;
|
||||
use OCP\Files\IRootFolder;
|
||||
use OCP\Files\NotFoundException;
|
||||
use OCP\IPreview;
|
||||
use OCP\IRequest;
|
||||
use OCP\IUserSession;
|
||||
|
||||
class PreviewController extends Controller {
|
||||
|
||||
/** @var IRootFolder */
|
||||
private $rootFolder;
|
||||
|
||||
/** @var string */
|
||||
private $userId;
|
||||
/** @var IUserSession */
|
||||
private $userSession;
|
||||
|
||||
/** @var IMimeTypeDetector */
|
||||
private $mimeTypeDetector;
|
||||
|
||||
/** @var IVersionManager */
|
||||
private $versionManager;
|
||||
|
||||
/** @var IPreview */
|
||||
private $previewManager;
|
||||
|
||||
public function __construct($appName,
|
||||
IRequest $request,
|
||||
IRootFolder $rootFolder,
|
||||
$userId,
|
||||
IMimeTypeDetector $mimeTypeDetector,
|
||||
IPreview $previewManager) {
|
||||
public function __construct(
|
||||
$appName,
|
||||
IRequest $request,
|
||||
IRootFolder $rootFolder,
|
||||
IUserSession $userSession,
|
||||
IMimeTypeDetector $mimeTypeDetector,
|
||||
IVersionManager $versionManager,
|
||||
IPreview $previewManager
|
||||
) {
|
||||
parent::__construct($appName, $request);
|
||||
|
||||
$this->rootFolder = $rootFolder;
|
||||
$this->userId = $userId;
|
||||
$this->userSession = $userSession;
|
||||
$this->mimeTypeDetector = $mimeTypeDetector;
|
||||
$this->versionManager = $versionManager;
|
||||
$this->previewManager = $previewManager;
|
||||
}
|
||||
|
||||
|
@ -79,20 +87,17 @@ class PreviewController extends Controller {
|
|||
$y = 44,
|
||||
$version = ''
|
||||
) {
|
||||
if($file === '' || $version === '' || $x === 0 || $y === 0) {
|
||||
if ($file === '' || $version === '' || $x === 0 || $y === 0) {
|
||||
return new DataResponse([], Http::STATUS_BAD_REQUEST);
|
||||
}
|
||||
|
||||
try {
|
||||
$userFolder = $this->rootFolder->getUserFolder($this->userId);
|
||||
/** @var Folder $versionFolder */
|
||||
$versionFolder = $userFolder->getParent()->get('files_versions');
|
||||
$mimeType = $this->mimeTypeDetector->detectPath($file);
|
||||
$file = $versionFolder->get($file.'.v'.$version);
|
||||
|
||||
/** @var File $file */
|
||||
$f = $this->previewManager->getPreview($file, $x, $y, true, IPreview::MODE_FILL, $mimeType);
|
||||
return new FileDisplayResponse($f, Http::STATUS_OK, ['Content-Type' => $f->getMimeType()]);
|
||||
$user = $this->userSession->getUser();
|
||||
$userFolder = $this->rootFolder->getUserFolder($user->getUID());
|
||||
$file = $userFolder->get($file);
|
||||
$versionFile = $this->versionManager->getVersionFile($user, $file, (int)$version);
|
||||
$preview = $this->previewManager->getPreview($versionFile, $x, $y, true, IPreview::MODE_FILL, $versionFile->getMimetype());
|
||||
return new FileDisplayResponse($preview, Http::STATUS_OK, ['Content-Type' => $preview->getMimeType()]);
|
||||
} catch (NotFoundException $e) {
|
||||
return new DataResponse([], Http::STATUS_NOT_FOUND);
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
|
|
|
@ -24,6 +24,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace OCA\Files_Versions\Sabre;
|
||||
|
||||
use OCP\IUser;
|
||||
use Sabre\DAV\Exception\Forbidden;
|
||||
use Sabre\DAV\ICollection;
|
||||
use Sabre\DAV\IMoveTarget;
|
||||
|
@ -31,14 +32,6 @@ use Sabre\DAV\INode;
|
|||
|
||||
|
||||
class RestoreFolder implements ICollection, IMoveTarget {
|
||||
|
||||
/** @var string */
|
||||
protected $userId;
|
||||
|
||||
public function __construct(string $userId) {
|
||||
$this->userId = $userId;
|
||||
}
|
||||
|
||||
public function createFile($name, $data = null) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
@ -80,7 +73,8 @@ class RestoreFolder implements ICollection, IMoveTarget {
|
|||
return false;
|
||||
}
|
||||
|
||||
return $sourceNode->rollBack();
|
||||
$sourceNode->rollBack();
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -20,10 +20,13 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\Files_Versions\Sabre;
|
||||
|
||||
use OCA\Files_Versions\Versions\IVersionManager;
|
||||
use OCP\Files\IRootFolder;
|
||||
use OCP\IConfig;
|
||||
use OCP\IUserManager;
|
||||
use Sabre\DAV\INode;
|
||||
use Sabre\DAVACL\AbstractPrincipalCollection;
|
||||
use Sabre\DAVACL\PrincipalBackend;
|
||||
|
@ -33,12 +36,24 @@ class RootCollection extends AbstractPrincipalCollection {
|
|||
/** @var IRootFolder */
|
||||
private $rootFolder;
|
||||
|
||||
public function __construct(PrincipalBackend\BackendInterface $principalBackend,
|
||||
IRootFolder $rootFolder,
|
||||
IConfig $config) {
|
||||
/** @var IUserManager */
|
||||
private $userManager;
|
||||
|
||||
/** @var IVersionManager */
|
||||
private $versionManager;
|
||||
|
||||
public function __construct(
|
||||
PrincipalBackend\BackendInterface $principalBackend,
|
||||
IRootFolder $rootFolder,
|
||||
IConfig $config,
|
||||
IUserManager $userManager,
|
||||
IVersionManager $versionManager
|
||||
) {
|
||||
parent::__construct($principalBackend, 'principals/users');
|
||||
|
||||
$this->rootFolder = $rootFolder;
|
||||
$this->userManager = $userManager;
|
||||
$this->versionManager = $versionManager;
|
||||
|
||||
$this->disableListing = !$config->getSystemValue('debug', false);
|
||||
}
|
||||
|
@ -54,12 +69,12 @@ class RootCollection extends AbstractPrincipalCollection {
|
|||
* @return INode
|
||||
*/
|
||||
public function getChildForPrincipal(array $principalInfo) {
|
||||
list(,$name) = \Sabre\Uri\split($principalInfo['uri']);
|
||||
list(, $name) = \Sabre\Uri\split($principalInfo['uri']);
|
||||
$user = \OC::$server->getUserSession()->getUser();
|
||||
if (is_null($user) || $name !== $user->getUID()) {
|
||||
throw new \Sabre\DAV\Exception\Forbidden();
|
||||
}
|
||||
return new VersionHome($principalInfo, $this->rootFolder);
|
||||
return new VersionHome($principalInfo, $this->rootFolder, $this->userManager, $this->versionManager);
|
||||
}
|
||||
|
||||
public function getName() {
|
||||
|
|
|
@ -21,11 +21,15 @@ declare(strict_types=1);
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\Files_Versions\Sabre;
|
||||
|
||||
use OCA\Files_Versions\Storage;
|
||||
use OCA\Files_Versions\Versions\IVersion;
|
||||
use OCA\Files_Versions\Versions\IVersionManager;
|
||||
use OCP\Files\File;
|
||||
use OCP\Files\Folder;
|
||||
use OCP\IUser;
|
||||
use Sabre\DAV\Exception\Forbidden;
|
||||
use Sabre\DAV\Exception\NotFound;
|
||||
use Sabre\DAV\ICollection;
|
||||
|
@ -37,13 +41,17 @@ class VersionCollection implements ICollection {
|
|||
/** @var File */
|
||||
private $file;
|
||||
|
||||
/** @var string */
|
||||
private $userId;
|
||||
/** @var IUser */
|
||||
private $user;
|
||||
|
||||
public function __construct(Folder $userFolder, File $file, string $userId) {
|
||||
/** @var IVersionManager */
|
||||
private $versionManager;
|
||||
|
||||
public function __construct(Folder $userFolder, File $file, IUser $user, IVersionManager $versionManager) {
|
||||
$this->userFolder = $userFolder;
|
||||
$this->file = $file;
|
||||
$this->userId = $userId;
|
||||
$this->user = $user;
|
||||
$this->versionManager = $versionManager;
|
||||
}
|
||||
|
||||
public function createFile($name, $data = null) {
|
||||
|
@ -68,10 +76,10 @@ class VersionCollection implements ICollection {
|
|||
}
|
||||
|
||||
public function getChildren(): array {
|
||||
$versions = Storage::getVersions($this->userId, $this->userFolder->getRelativePath($this->file->getPath()));
|
||||
$versions = $this->versionManager->getVersionsForFile($this->user, $this->file);
|
||||
|
||||
return array_map(function (array $data) {
|
||||
return new VersionFile($data, $this->userFolder->getParent());
|
||||
return array_map(function (IVersion $version) {
|
||||
return new VersionFile($version, $this->versionManager);
|
||||
}, $versions);
|
||||
}
|
||||
|
||||
|
|
|
@ -21,26 +21,26 @@ declare(strict_types=1);
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\Files_Versions\Sabre;
|
||||
|
||||
use OCA\Files_Versions\Storage;
|
||||
use OCP\Files\File;
|
||||
use OCP\Files\Folder;
|
||||
use OCA\Files_Versions\Versions\IVersion;
|
||||
use OCA\Files_Versions\Versions\IVersionManager;
|
||||
use OCP\Files\NotFoundException;
|
||||
use Sabre\DAV\Exception\Forbidden;
|
||||
use Sabre\DAV\Exception\NotFound;
|
||||
use Sabre\DAV\IFile;
|
||||
|
||||
class VersionFile implements IFile {
|
||||
/** @var array */
|
||||
private $data;
|
||||
/** @var IVersion */
|
||||
private $version;
|
||||
|
||||
/** @var Folder */
|
||||
private $userRoot;
|
||||
/** @var IVersionManager */
|
||||
private $versionManager;
|
||||
|
||||
public function __construct(array $data, Folder $userRoot) {
|
||||
$this->data = $data;
|
||||
$this->userRoot = $userRoot;
|
||||
public function __construct(IVersion $version, IVersionManager $versionManager) {
|
||||
$this->version = $version;
|
||||
$this->versionManager = $versionManager;
|
||||
}
|
||||
|
||||
public function put($data) {
|
||||
|
@ -49,27 +49,22 @@ class VersionFile implements IFile {
|
|||
|
||||
public function get() {
|
||||
try {
|
||||
/** @var Folder $versions */
|
||||
$versions = $this->userRoot->get('files_versions');
|
||||
/** @var File $version */
|
||||
$version = $versions->get($this->data['path'].'.v'.$this->data['version']);
|
||||
return $this->versionManager->read($this->version);
|
||||
} catch (NotFoundException $e) {
|
||||
throw new NotFound();
|
||||
}
|
||||
|
||||
return $version->fopen('rb');
|
||||
}
|
||||
|
||||
public function getContentType(): string {
|
||||
return $this->data['mimetype'];
|
||||
return $this->version->getMimeType();
|
||||
}
|
||||
|
||||
public function getETag(): string {
|
||||
return $this->data['version'];
|
||||
return (string)$this->version->getRevisionId();
|
||||
}
|
||||
|
||||
public function getSize(): int {
|
||||
return $this->data['size'];
|
||||
return $this->version->getSize();
|
||||
}
|
||||
|
||||
public function delete() {
|
||||
|
@ -77,7 +72,7 @@ class VersionFile implements IFile {
|
|||
}
|
||||
|
||||
public function getName(): string {
|
||||
return $this->data['version'];
|
||||
return (string)$this->version->getRevisionId();
|
||||
}
|
||||
|
||||
public function setName($name) {
|
||||
|
@ -85,10 +80,10 @@ class VersionFile implements IFile {
|
|||
}
|
||||
|
||||
public function getLastModified(): int {
|
||||
return (int)$this->data['version'];
|
||||
return $this->version->getTimestamp();
|
||||
}
|
||||
|
||||
public function rollBack(): bool {
|
||||
return Storage::rollback($this->data['path'], $this->data['version']);
|
||||
public function rollBack() {
|
||||
$this->versionManager->rollback($this->version);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,9 +20,13 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\Files_Versions\Sabre;
|
||||
|
||||
use OC\User\NoUserException;
|
||||
use OCA\Files_Versions\Versions\IVersionManager;
|
||||
use OCP\Files\IRootFolder;
|
||||
use OCP\IUserManager;
|
||||
use Sabre\DAV\Exception\Forbidden;
|
||||
use Sabre\DAV\ICollection;
|
||||
|
||||
|
@ -34,9 +38,25 @@ class VersionHome implements ICollection {
|
|||
/** @var IRootFolder */
|
||||
private $rootFolder;
|
||||
|
||||
public function __construct(array $principalInfo, IRootFolder $rootFolder) {
|
||||
/** @var IUserManager */
|
||||
private $userManager;
|
||||
|
||||
/** @var IVersionManager */
|
||||
private $versionManager;
|
||||
|
||||
public function __construct(array $principalInfo, IRootFolder $rootFolder, IUserManager $userManager, IVersionManager $versionManager) {
|
||||
$this->principalInfo = $principalInfo;
|
||||
$this->rootFolder = $rootFolder;
|
||||
$this->userManager = $userManager;
|
||||
$this->versionManager = $versionManager;
|
||||
}
|
||||
|
||||
private function getUser() {
|
||||
list(, $name) = \Sabre\Uri\split($this->principalInfo['uri']);
|
||||
$user = $this->userManager->get($name);
|
||||
if (!$user) {
|
||||
throw new NoUserException();
|
||||
}
|
||||
}
|
||||
|
||||
public function delete() {
|
||||
|
@ -44,8 +64,7 @@ class VersionHome implements ICollection {
|
|||
}
|
||||
|
||||
public function getName(): string {
|
||||
list(,$name) = \Sabre\Uri\split($this->principalInfo['uri']);
|
||||
return $name;
|
||||
return $this->getUser()->getUID();
|
||||
}
|
||||
|
||||
public function setName($name) {
|
||||
|
@ -61,22 +80,22 @@ class VersionHome implements ICollection {
|
|||
}
|
||||
|
||||
public function getChild($name) {
|
||||
list(,$userId) = \Sabre\Uri\split($this->principalInfo['uri']);
|
||||
$user = $this->getUser();
|
||||
|
||||
if ($name === 'versions') {
|
||||
return new VersionRoot($userId, $this->rootFolder);
|
||||
return new VersionRoot($user, $this->rootFolder, $this->versionManager);
|
||||
}
|
||||
if ($name === 'restore') {
|
||||
return new RestoreFolder($userId);
|
||||
return new RestoreFolder();
|
||||
}
|
||||
}
|
||||
|
||||
public function getChildren() {
|
||||
list(,$userId) = \Sabre\Uri\split($this->principalInfo['uri']);
|
||||
$user = $this->getUser();
|
||||
|
||||
return [
|
||||
new VersionRoot($userId, $this->rootFolder),
|
||||
new RestoreFolder($userId),
|
||||
new VersionRoot($user, $this->rootFolder, $this->versionManager),
|
||||
new RestoreFolder(),
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
@ -23,23 +23,29 @@ declare(strict_types=1);
|
|||
*/
|
||||
namespace OCA\Files_Versions\Sabre;
|
||||
|
||||
use OCA\Files_Versions\Versions\IVersionManager;
|
||||
use OCP\Files\File;
|
||||
use OCP\Files\IRootFolder;
|
||||
use OCP\IUser;
|
||||
use Sabre\DAV\Exception\Forbidden;
|
||||
use Sabre\DAV\Exception\NotFound;
|
||||
use Sabre\DAV\ICollection;
|
||||
|
||||
class VersionRoot implements ICollection {
|
||||
|
||||
/** @var string */
|
||||
private $userId;
|
||||
/** @var IUser */
|
||||
private $user;
|
||||
|
||||
/** @var IRootFolder */
|
||||
private $rootFolder;
|
||||
|
||||
public function __construct(string $userId, IRootFolder $rootFolder) {
|
||||
$this->userId = $userId;
|
||||
/** @var IVersionManager */
|
||||
private $versionManager;
|
||||
|
||||
public function __construct(IUser $user, IRootFolder $rootFolder, IVersionManager $versionManager) {
|
||||
$this->user = $user;
|
||||
$this->rootFolder = $rootFolder;
|
||||
$this->versionManager = $versionManager;
|
||||
}
|
||||
|
||||
public function delete() {
|
||||
|
@ -63,7 +69,7 @@ class VersionRoot implements ICollection {
|
|||
}
|
||||
|
||||
public function getChild($name) {
|
||||
$userFolder = $this->rootFolder->getUserFolder($this->userId);
|
||||
$userFolder = $this->rootFolder->getUserFolder($this->user->getUID());
|
||||
|
||||
$fileId = (int)$name;
|
||||
$nodes = $userFolder->getById($fileId);
|
||||
|
@ -78,7 +84,7 @@ class VersionRoot implements ICollection {
|
|||
throw new NotFound();
|
||||
}
|
||||
|
||||
return new VersionCollection($userFolder, $node, $this->userId);
|
||||
return new VersionCollection($userFolder, $node, $this->user, $this->versionManager);
|
||||
}
|
||||
|
||||
public function getChildren(): array {
|
||||
|
|
|
@ -48,6 +48,7 @@ use OC\Files\View;
|
|||
use OCA\Files_Versions\AppInfo\Application;
|
||||
use OCA\Files_Versions\Command\Expire;
|
||||
use OCA\Files_Versions\Events\CreateVersionEvent;
|
||||
use OCA\Files_Versions\Versions\IVersionManager;
|
||||
use OCP\Files\NotFoundException;
|
||||
use OCP\Lock\ILockingProvider;
|
||||
use OCP\User;
|
||||
|
@ -178,10 +179,10 @@ class Storage {
|
|||
list($uid, $filename) = self::getUidAndFilename($filename);
|
||||
|
||||
$files_view = new View('/'.$uid .'/files');
|
||||
$users_view = new View('/'.$uid);
|
||||
|
||||
$eventDispatcher = \OC::$server->getEventDispatcher();
|
||||
$id = $files_view->getFileInfo($filename)->getId();
|
||||
$fileInfo = $files_view->getFileInfo($filename);
|
||||
$id = $fileInfo->getId();
|
||||
$nodes = \OC::$server->getRootFolder()->getById($id);
|
||||
foreach ($nodes as $node) {
|
||||
$event = new CreateVersionEvent($node);
|
||||
|
@ -192,20 +193,16 @@ class Storage {
|
|||
}
|
||||
|
||||
// no use making versions for empty files
|
||||
if ($files_view->filesize($filename) === 0) {
|
||||
if ($fileInfo->getSize() === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// create all parent folders
|
||||
self::createMissingDirectories($filename, $users_view);
|
||||
/** @var IVersionManager $versionManager */
|
||||
$versionManager = \OC::$server->query(IVersionManager::class);
|
||||
$userManager = \OC::$server->getUserManager();
|
||||
$user = $userManager->get($uid);
|
||||
|
||||
self::scheduleExpire($uid, $filename);
|
||||
|
||||
// store a new version of a file
|
||||
$mtime = $users_view->filemtime('files/' . $filename);
|
||||
$users_view->copy('files/' . $filename, 'files_versions/' . $filename . '.v' . $mtime);
|
||||
// call getFileInfo to enforce a file cache entry for the new version
|
||||
$users_view->getFileInfo('files_versions/' . $filename . '.v' . $mtime);
|
||||
$versionManager->createVersion($user, $fileInfo);
|
||||
}
|
||||
|
||||
|
||||
|
@ -695,7 +692,7 @@ class Storage {
|
|||
* @param string $uid owner of the file
|
||||
* @param string $fileName file/folder for which to schedule expiration
|
||||
*/
|
||||
private static function scheduleExpire($uid, $fileName) {
|
||||
public static function scheduleExpire($uid, $fileName) {
|
||||
// let the admin disable auto expire
|
||||
$expiration = self::getExpiration();
|
||||
if ($expiration->isEnabled()) {
|
||||
|
@ -833,7 +830,7 @@ class Storage {
|
|||
* "files" folder
|
||||
* @param View $view view on data/user/
|
||||
*/
|
||||
private static function createMissingDirectories($filename, $view) {
|
||||
public static function createMissingDirectories($filename, $view) {
|
||||
$dirname = Filesystem::normalizePath(dirname($filename));
|
||||
$dirParts = explode('/', $dirname);
|
||||
$dir = "/files_versions";
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2018 Robin Appelman <robin@icewind.nl>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\Files_Versions\Versions;
|
||||
|
||||
class BackendNotFoundException extends \Exception {
|
||||
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright Copyright (c) 2018 Robin Appelman <robin@icewind.nl>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\Files_Versions\Versions;
|
||||
|
||||
use OCP\Files\FileInfo;
|
||||
use OCP\IUser;
|
||||
|
||||
/**
|
||||
* @since 15.0.0
|
||||
*/
|
||||
interface IVersion {
|
||||
/**
|
||||
* @return IVersionBackend
|
||||
* @since 15.0.0
|
||||
*/
|
||||
public function getBackend(): IVersionBackend;
|
||||
|
||||
/**
|
||||
* Get the file info of the source file
|
||||
*
|
||||
* @return FileInfo
|
||||
* @since 15.0.0
|
||||
*/
|
||||
public function getSourceFile(): FileInfo;
|
||||
|
||||
/**
|
||||
* Get the id of the revision for the file
|
||||
*
|
||||
* @return int
|
||||
* @since 15.0.0
|
||||
*/
|
||||
public function getRevisionId(): int;
|
||||
|
||||
/**
|
||||
* Get the timestamp this version was created
|
||||
*
|
||||
* @return int
|
||||
* @since 15.0.0
|
||||
*/
|
||||
public function getTimestamp(): int;
|
||||
|
||||
/**
|
||||
* Get the size of this version
|
||||
*
|
||||
* @return int
|
||||
* @since 15.0.0
|
||||
*/
|
||||
public function getSize(): int;
|
||||
|
||||
/**
|
||||
* Get the name of the source file at the time of making this version
|
||||
*
|
||||
* @return string
|
||||
* @since 15.0.0
|
||||
*/
|
||||
public function getSourceFileName(): string;
|
||||
|
||||
/**
|
||||
* Get the mimetype of this version
|
||||
*
|
||||
* @return string
|
||||
* @since 15.0.0
|
||||
*/
|
||||
public function getMimeType(): string;
|
||||
|
||||
/**
|
||||
* Get the path of this version
|
||||
*
|
||||
* @return string
|
||||
* @since 15.0.0
|
||||
*/
|
||||
public function getVersionPath(): string;
|
||||
|
||||
/**
|
||||
* @return IUser
|
||||
* @since 15.0.0
|
||||
*/
|
||||
public function getUser(): IUser;
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
<?php declare(strict_types=1);
|
||||
/**
|
||||
* @copyright Copyright (c) 2018 Robin Appelman <robin@icewind.nl>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\Files_Versions\Versions;
|
||||
|
||||
use OCP\Files\File;
|
||||
use OCP\Files\FileInfo;
|
||||
use OCP\Files\NotFoundException;
|
||||
use OCP\Files\SimpleFS\ISimpleFile;
|
||||
use OCP\IUser;
|
||||
|
||||
/**
|
||||
* @since 15.0.0
|
||||
*/
|
||||
interface IVersionBackend {
|
||||
/**
|
||||
* Get all versions for a file
|
||||
*
|
||||
* @param IUser $user
|
||||
* @param FileInfo $file
|
||||
* @return IVersion[]
|
||||
* @since 15.0.0
|
||||
*/
|
||||
public function getVersionsForFile(IUser $user, FileInfo $file): array;
|
||||
|
||||
/**
|
||||
* Create a new version for a file
|
||||
*
|
||||
* @param IUser $user
|
||||
* @param FileInfo $file
|
||||
* @since 15.0.0
|
||||
*/
|
||||
public function createVersion(IUser $user, FileInfo $file);
|
||||
|
||||
/**
|
||||
* Restore this version
|
||||
*
|
||||
* @param IVersion $version
|
||||
* @since 15.0.0
|
||||
*/
|
||||
public function rollback(IVersion $version);
|
||||
|
||||
/**
|
||||
* Open the file for reading
|
||||
*
|
||||
* @param IVersion $version
|
||||
* @return resource
|
||||
* @throws NotFoundException
|
||||
* @since 15.0.0
|
||||
*/
|
||||
public function read(IVersion $version);
|
||||
|
||||
/**
|
||||
* Get the preview for a specific version of a file
|
||||
*
|
||||
* @param IUser $user
|
||||
* @param FileInfo $sourceFile
|
||||
* @param int $revision
|
||||
* @return ISimpleFile
|
||||
* @since 15.0.0
|
||||
*/
|
||||
public function getVersionFile(IUser $user, FileInfo $sourceFile, int $revision): File;
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
<?php declare(strict_types=1);
|
||||
/**
|
||||
* @copyright Copyright (c) 2018 Robin Appelman <robin@icewind.nl>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\Files_Versions\Versions;
|
||||
|
||||
/**
|
||||
* @since 15.0.0
|
||||
*/
|
||||
interface IVersionManager extends IVersionBackend {
|
||||
/**
|
||||
* Register a new backend
|
||||
*
|
||||
* @param string $storageType
|
||||
* @param IVersionBackend $backend
|
||||
* @since 15.0.0
|
||||
*/
|
||||
public function registerBackend(string $storageType, IVersionBackend $backend);
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
<?php declare(strict_types=1);
|
||||
/**
|
||||
* @copyright Copyright (c) 2018 Robin Appelman <robin@icewind.nl>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\Files_Versions\Versions;
|
||||
|
||||
use OC\Files\View;
|
||||
use OCA\Files_Versions\Storage;
|
||||
use OCP\Files\File;
|
||||
use OCP\Files\FileInfo;
|
||||
use OCP\Files\Folder;
|
||||
use OCP\Files\IRootFolder;
|
||||
use OCP\Files\NotFoundException;
|
||||
use OCP\IUser;
|
||||
|
||||
class LegacyVersionsBackend implements IVersionBackend {
|
||||
/** @var IRootFolder */
|
||||
private $rootFolder;
|
||||
|
||||
public function __construct(IRootFolder $rootFolder) {
|
||||
$this->rootFolder = $rootFolder;
|
||||
}
|
||||
|
||||
public function getVersionsForFile(IUser $user, FileInfo $file): array {
|
||||
$userFolder = $this->rootFolder->getUserFolder($user->getUID());
|
||||
$versions = Storage::getVersions($user->getUID(), $userFolder->getRelativePath($file->getPath()));
|
||||
|
||||
return array_map(function (array $data) use ($file, $user) {
|
||||
return new Version(
|
||||
(int)$data['version'],
|
||||
(int)$data['version'],
|
||||
$data['name'],
|
||||
(int)$data['size'],
|
||||
$data['mimetype'],
|
||||
$data['path'],
|
||||
$file,
|
||||
$this,
|
||||
$user
|
||||
);
|
||||
}, $versions);
|
||||
}
|
||||
|
||||
public function createVersion(IUser $user, FileInfo $file) {
|
||||
$userFolder = $this->rootFolder->getUserFolder($user->getUID());
|
||||
$relativePath = $userFolder->getRelativePath($file->getPath());
|
||||
$userView = new View('/' . $user->getUID());
|
||||
// create all parent folders
|
||||
Storage::createMissingDirectories($relativePath, $userView);
|
||||
|
||||
Storage::scheduleExpire($user->getUID(), $relativePath);
|
||||
|
||||
// store a new version of a file
|
||||
$userView->copy('files/' . $relativePath, 'files_versions/' . $relativePath . '.v' . $file->getMtime());
|
||||
// ensure the file is scanned
|
||||
$userView->getFileInfo('files_versions/' . $relativePath . '.v' . $file->getMtime());
|
||||
}
|
||||
|
||||
public function rollback(IVersion $version) {
|
||||
return Storage::rollback($version->getVersionPath(), $version->getRevisionId());
|
||||
}
|
||||
|
||||
private function getVersionFolder(IUser $user): Folder {
|
||||
$userRoot = $this->rootFolder->getUserFolder($user->getUID())
|
||||
->getParent();
|
||||
try {
|
||||
/** @var Folder $folder */
|
||||
$folder = $userRoot->get('files_versions');
|
||||
return $folder;
|
||||
} catch (NotFoundException $e) {
|
||||
return $userRoot->newFolder('files_versions');
|
||||
}
|
||||
}
|
||||
|
||||
public function read(IVersion $version) {
|
||||
$versions = $this->getVersionFolder($version->getUser());
|
||||
/** @var File $file */
|
||||
$file = $versions->get($version->getVersionPath() . '.v' . $version->getRevisionId());
|
||||
return $file->fopen('r');
|
||||
}
|
||||
|
||||
public function getVersionFile(IUser $user, FileInfo $sourceFile, int $revision): File {
|
||||
$userFolder = $this->rootFolder->getUserFolder($user->getUID());
|
||||
$versionFolder = $this->getVersionFolder($user);
|
||||
/** @var File $file */
|
||||
$file = $versionFolder->get($userFolder->getRelativePath($sourceFile->getPath()) . '.v' . $revision);
|
||||
return $file;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright Copyright (c) 2018 Robin Appelman <robin@icewind.nl>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\Files_Versions\Versions;
|
||||
|
||||
use OCP\Files\FileInfo;
|
||||
use OCP\IUser;
|
||||
|
||||
class Version implements IVersion {
|
||||
/** @var int */
|
||||
private $timestamp;
|
||||
|
||||
/** @var int */
|
||||
private $revisionId;
|
||||
|
||||
/** @var string */
|
||||
private $name;
|
||||
|
||||
/** @var int */
|
||||
private $size;
|
||||
|
||||
/** @var string */
|
||||
private $mimetype;
|
||||
|
||||
/** @var string */
|
||||
private $path;
|
||||
|
||||
/** @var FileInfo */
|
||||
private $sourceFileInfo;
|
||||
|
||||
/** @var IVersionBackend */
|
||||
private $backend;
|
||||
|
||||
/** @var IUser */
|
||||
private $user;
|
||||
|
||||
public function __construct(
|
||||
int $timestamp,
|
||||
int $revisionId,
|
||||
string $name,
|
||||
int $size,
|
||||
string $mimetype,
|
||||
string $path,
|
||||
FileInfo $sourceFileInfo,
|
||||
IVersionBackend $backend,
|
||||
IUser $user
|
||||
) {
|
||||
$this->timestamp = $timestamp;
|
||||
$this->revisionId = $revisionId;
|
||||
$this->name = $name;
|
||||
$this->size = $size;
|
||||
$this->mimetype = $mimetype;
|
||||
$this->path = $path;
|
||||
$this->sourceFileInfo = $sourceFileInfo;
|
||||
$this->backend = $backend;
|
||||
$this->user = $user;
|
||||
}
|
||||
|
||||
public function getBackend(): IVersionBackend {
|
||||
return $this->backend;
|
||||
}
|
||||
|
||||
public function getSourceFile(): FileInfo {
|
||||
return $this->sourceFileInfo;
|
||||
}
|
||||
|
||||
public function getRevisionId(): int {
|
||||
return $this->revisionId;
|
||||
}
|
||||
|
||||
public function getTimestamp(): int {
|
||||
return $this->timestamp;
|
||||
}
|
||||
|
||||
public function getSize(): int {
|
||||
return $this->size;
|
||||
}
|
||||
|
||||
public function getSourceFileName(): string {
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function getMimeType(): string {
|
||||
return $this->mimetype;
|
||||
}
|
||||
|
||||
public function getVersionPath(): string {
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
public function getUser(): IUser {
|
||||
return $this->user;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
<?php declare(strict_types=1);
|
||||
/**
|
||||
* @copyright Copyright (c) 2018 Robin Appelman <robin@icewind.nl>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\Files_Versions\Versions;
|
||||
|
||||
use OCP\Files\File;
|
||||
use OCP\Files\FileInfo;
|
||||
use OCP\Files\Storage\IStorage;
|
||||
use OCP\IUser;
|
||||
|
||||
class VersionManager implements IVersionManager {
|
||||
/** @var IVersionBackend[] */
|
||||
private $backends = [];
|
||||
|
||||
public function registerBackend(string $storageType, IVersionBackend $backend) {
|
||||
$this->backends[$storageType] = $backend;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return IVersionBackend[]
|
||||
*/
|
||||
private function getBackends(): array {
|
||||
return $this->backends;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param IStorage $storage
|
||||
* @return IVersionBackend
|
||||
* @throws BackendNotFoundException
|
||||
*/
|
||||
public function getBackendForStorage(IStorage $storage): IVersionBackend {
|
||||
$fullType = get_class($storage);
|
||||
$backends = $this->getBackends();
|
||||
$foundType = array_reduce(array_keys($backends), function ($type, $registeredType) use ($storage) {
|
||||
if (
|
||||
$storage->instanceOfStorage($registeredType) &&
|
||||
($type === '' || is_subclass_of($registeredType, $type))
|
||||
) {
|
||||
return $registeredType;
|
||||
} else {
|
||||
return $type;
|
||||
}
|
||||
}, '');
|
||||
if ($foundType === '') {
|
||||
throw new BackendNotFoundException("Version backend for $fullType not found");
|
||||
} else {
|
||||
return $backends[$foundType];
|
||||
}
|
||||
}
|
||||
|
||||
public function getVersionsForFile(IUser $user, FileInfo $file): array {
|
||||
$backend = $this->getBackendForStorage($file->getStorage());
|
||||
return $backend->getVersionsForFile($user, $file);
|
||||
}
|
||||
|
||||
public function createVersion(IUser $user, FileInfo $file) {
|
||||
$backend = $this->getBackendForStorage($file->getStorage());
|
||||
$backend->createVersion($user, $file);
|
||||
}
|
||||
|
||||
public function rollback(IVersion $version) {
|
||||
$backend = $version->getBackend();
|
||||
return $backend->rollback($version);
|
||||
}
|
||||
|
||||
public function read(IVersion $version) {
|
||||
$backend = $version->getBackend();
|
||||
return $backend->read($version);
|
||||
}
|
||||
|
||||
public function getVersionFile(IUser $user, FileInfo $sourceFile, int $revision): File {
|
||||
$backend = $this->getBackendForStorage($sourceFile->getStorage());
|
||||
return $backend->getVersionFile($user, $sourceFile, $revision);
|
||||
}
|
||||
}
|
|
@ -20,9 +20,12 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\Files_Versions\Tests\Controller;
|
||||
|
||||
use OC\User\User;
|
||||
use OCA\Files_Versions\Controller\PreviewController;
|
||||
use OCA\Files_Versions\Versions\IVersionManager;
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\AppFramework\Http\DataResponse;
|
||||
use OCP\AppFramework\Http\FileDisplayResponse;
|
||||
|
@ -34,6 +37,8 @@ use OCP\Files\NotFoundException;
|
|||
use OCP\Files\SimpleFS\ISimpleFile;
|
||||
use OCP\IPreview;
|
||||
use OCP\IRequest;
|
||||
use OCP\IUser;
|
||||
use OCP\IUserSession;
|
||||
use Test\TestCase;
|
||||
|
||||
class PreviewControllerTest extends TestCase {
|
||||
|
@ -50,23 +55,39 @@ class PreviewControllerTest extends TestCase {
|
|||
/** @var IPreview|\PHPUnit_Framework_MockObject_MockObject */
|
||||
private $previewManager;
|
||||
|
||||
/** @var PreviewController */
|
||||
/** @var PreviewController|\PHPUnit_Framework_MockObject_MockObject */
|
||||
private $controller;
|
||||
|
||||
/** @var IUserSession|\PHPUnit_Framework_MockObject_MockObject */
|
||||
private $userSession;
|
||||
|
||||
/** @var IVersionManager|\PHPUnit_Framework_MockObject_MockObject */
|
||||
private $versionManager;
|
||||
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->rootFolder = $this->createMock(IRootFolder::class);
|
||||
$this->userId = 'user';
|
||||
$user = $this->createMock(IUser::class);
|
||||
$user->expects($this->any())
|
||||
->method('getUID')
|
||||
->willReturn($this->userId);
|
||||
$this->mimeTypeDetector = $this->createMock(IMimeTypeDetector::class);
|
||||
$this->previewManager = $this->createMock(IPreview::class);
|
||||
$this->userSession = $this->createMock(IUserSession::class);
|
||||
$this->userSession->expects($this->any())
|
||||
->method('getUser')
|
||||
->willReturn($user);
|
||||
$this->versionManager = $this->createMock(IVersionManager::class);
|
||||
|
||||
$this->controller = new PreviewController(
|
||||
'files_versions',
|
||||
$this->createMock(IRequest::class),
|
||||
$this->rootFolder,
|
||||
$this->userId,
|
||||
$this->userSession,
|
||||
$this->mimeTypeDetector,
|
||||
$this->versionManager,
|
||||
$this->previewManager
|
||||
);
|
||||
}
|
||||
|
@ -102,24 +123,23 @@ class PreviewControllerTest extends TestCase {
|
|||
public function testValidPreview() {
|
||||
$userFolder = $this->createMock(Folder::class);
|
||||
$userRoot = $this->createMock(Folder::class);
|
||||
$versions = $this->createMock(Folder::class);
|
||||
|
||||
$this->rootFolder->method('getUserFolder')
|
||||
->with($this->userId)
|
||||
->willReturn($userFolder);
|
||||
$userFolder->method('getParent')
|
||||
->willReturn($userRoot);
|
||||
$userRoot->method('get')
|
||||
->with('files_versions')
|
||||
->willReturn($versions);
|
||||
|
||||
$this->mimeTypeDetector->method('detectPath')
|
||||
->with($this->equalTo('file'))
|
||||
->willReturn('myMime');
|
||||
$sourceFile = $this->createMock(File::class);
|
||||
$userFolder->method('get')
|
||||
->with('file')
|
||||
->willReturn($sourceFile);
|
||||
|
||||
$file = $this->createMock(File::class);
|
||||
$versions->method('get')
|
||||
->with($this->equalTo('file.v42'))
|
||||
$file->method('getMimetype')
|
||||
->willReturn('myMime');
|
||||
|
||||
$this->versionManager->method('getVersionFile')
|
||||
->willReturn($file);
|
||||
|
||||
$preview = $this->createMock(ISimpleFile::class);
|
||||
|
@ -138,24 +158,23 @@ class PreviewControllerTest extends TestCase {
|
|||
public function testVersionNotFound() {
|
||||
$userFolder = $this->createMock(Folder::class);
|
||||
$userRoot = $this->createMock(Folder::class);
|
||||
$versions = $this->createMock(Folder::class);
|
||||
|
||||
$this->rootFolder->method('getUserFolder')
|
||||
->with($this->userId)
|
||||
->willReturn($userFolder);
|
||||
$userFolder->method('getParent')
|
||||
->willReturn($userRoot);
|
||||
$userRoot->method('get')
|
||||
->with('files_versions')
|
||||
->willReturn($versions);
|
||||
|
||||
$sourceFile = $this->createMock(File::class);
|
||||
$userFolder->method('get')
|
||||
->with('file')
|
||||
->willReturn($sourceFile);
|
||||
|
||||
$this->mimeTypeDetector->method('detectPath')
|
||||
->with($this->equalTo('file'))
|
||||
->willReturn('myMime');
|
||||
|
||||
$file = $this->createMock(File::class);
|
||||
$versions->method('get')
|
||||
->with($this->equalTo('file.v42'))
|
||||
$this->versionManager->method('getVersionFile')
|
||||
->willThrowException(new NotFoundException());
|
||||
|
||||
$res = $this->controller->getPreview('file', 10, 10, '42');
|
||||
|
|
|
@ -22,8 +22,9 @@
|
|||
namespace OCA\OAuth2\Controller;
|
||||
|
||||
use OC\Authentication\Exceptions\InvalidTokenException;
|
||||
use OC\Authentication\Token\ExpiredTokenException;
|
||||
use OC\Authentication\Exceptions\ExpiredTokenException;
|
||||
use OC\Authentication\Token\IProvider as TokenProvider;
|
||||
use OC\Security\Bruteforce\Throttler;
|
||||
use OCA\OAuth2\Db\AccessTokenMapper;
|
||||
use OCA\OAuth2\Db\ClientMapper;
|
||||
use OCA\OAuth2\Exceptions\AccessTokenNotFoundException;
|
||||
|
@ -49,6 +50,8 @@ class OauthApiController extends Controller {
|
|||
private $secureRandom;
|
||||
/** @var ITimeFactory */
|
||||
private $time;
|
||||
/** @var Throttler */
|
||||
private $throttler;
|
||||
|
||||
/**
|
||||
* @param string $appName
|
||||
|
@ -59,6 +62,7 @@ class OauthApiController extends Controller {
|
|||
* @param TokenProvider $tokenProvider
|
||||
* @param ISecureRandom $secureRandom
|
||||
* @param ITimeFactory $time
|
||||
* @param Throttler $throttler
|
||||
*/
|
||||
public function __construct($appName,
|
||||
IRequest $request,
|
||||
|
@ -67,7 +71,8 @@ class OauthApiController extends Controller {
|
|||
ClientMapper $clientMapper,
|
||||
TokenProvider $tokenProvider,
|
||||
ISecureRandom $secureRandom,
|
||||
ITimeFactory $time) {
|
||||
ITimeFactory $time,
|
||||
Throttler $throttler) {
|
||||
parent::__construct($appName, $request);
|
||||
$this->crypto = $crypto;
|
||||
$this->accessTokenMapper = $accessTokenMapper;
|
||||
|
@ -75,6 +80,7 @@ class OauthApiController extends Controller {
|
|||
$this->tokenProvider = $tokenProvider;
|
||||
$this->secureRandom = $secureRandom;
|
||||
$this->time = $time;
|
||||
$this->throttler = $throttler;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -164,6 +170,8 @@ class OauthApiController extends Controller {
|
|||
$accessToken->setEncryptedToken($this->crypto->encrypt($newToken, $newCode));
|
||||
$this->accessTokenMapper->update($accessToken);
|
||||
|
||||
$this->throttler->resetDelay($this->request->getRemoteAddress(), 'login', ['user' => $appToken->getUID()]);
|
||||
|
||||
return new JSONResponse(
|
||||
[
|
||||
'access_token' => $newToken,
|
||||
|
|
|
@ -22,11 +22,10 @@
|
|||
namespace OCA\OAuth2\Tests\Controller;
|
||||
|
||||
use OC\Authentication\Exceptions\InvalidTokenException;
|
||||
use OC\Authentication\Exceptions\ExpiredTokenException;
|
||||
use OC\Authentication\Token\DefaultToken;
|
||||
use OC\Authentication\Token\DefaultTokenMapper;
|
||||
use OC\Authentication\Token\ExpiredTokenException;
|
||||
use OC\Authentication\Token\IProvider as TokenProvider;
|
||||
use OC\Authentication\Token\IToken;
|
||||
use OC\Security\Bruteforce\Throttler;
|
||||
use OCA\OAuth2\Controller\OauthApiController;
|
||||
use OCA\OAuth2\Db\AccessToken;
|
||||
use OCA\OAuth2\Db\AccessTokenMapper;
|
||||
|
@ -57,6 +56,8 @@ class OauthApiControllerTest extends TestCase {
|
|||
private $secureRandom;
|
||||
/** @var ITimeFactory|\PHPUnit_Framework_MockObject_MockObject */
|
||||
private $time;
|
||||
/** @var Throttler|\PHPUnit_Framework_MockObject_MockObject */
|
||||
private $throttler;
|
||||
/** @var OauthApiController */
|
||||
private $oauthApiController;
|
||||
|
||||
|
@ -70,6 +71,7 @@ class OauthApiControllerTest extends TestCase {
|
|||
$this->tokenProvider = $this->createMock(TokenProvider::class);
|
||||
$this->secureRandom = $this->createMock(ISecureRandom::class);
|
||||
$this->time = $this->createMock(ITimeFactory::class);
|
||||
$this->throttler = $this->createMock(Throttler::class);
|
||||
|
||||
$this->oauthApiController = new OauthApiController(
|
||||
'oauth2',
|
||||
|
@ -79,7 +81,8 @@ class OauthApiControllerTest extends TestCase {
|
|||
$this->clientMapper,
|
||||
$this->tokenProvider,
|
||||
$this->secureRandom,
|
||||
$this->time
|
||||
$this->time,
|
||||
$this->throttler
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -286,6 +289,17 @@ class OauthApiControllerTest extends TestCase {
|
|||
'user_id' => 'userId',
|
||||
]);
|
||||
|
||||
$this->request->method('getRemoteAddress')
|
||||
->willReturn('1.2.3.4');
|
||||
|
||||
$this->throttler->expects($this->once())
|
||||
->method('resetDelay')
|
||||
->with(
|
||||
'1.2.3.4',
|
||||
'login',
|
||||
['user' => 'userId']
|
||||
);
|
||||
|
||||
$this->assertEquals($expected, $this->oauthApiController->getToken('refresh_token', null, 'validrefresh', 'clientId', 'clientSecret'));
|
||||
}
|
||||
|
||||
|
@ -370,6 +384,17 @@ class OauthApiControllerTest extends TestCase {
|
|||
$this->request->server['PHP_AUTH_USER'] = 'clientId';
|
||||
$this->request->server['PHP_AUTH_PW'] = 'clientSecret';
|
||||
|
||||
$this->request->method('getRemoteAddress')
|
||||
->willReturn('1.2.3.4');
|
||||
|
||||
$this->throttler->expects($this->once())
|
||||
->method('resetDelay')
|
||||
->with(
|
||||
'1.2.3.4',
|
||||
'login',
|
||||
['user' => 'userId']
|
||||
);
|
||||
|
||||
$this->assertEquals($expected, $this->oauthApiController->getToken('refresh_token', null, 'validrefresh', null, null));
|
||||
}
|
||||
|
||||
|
@ -451,6 +476,17 @@ class OauthApiControllerTest extends TestCase {
|
|||
'user_id' => 'userId',
|
||||
]);
|
||||
|
||||
$this->request->method('getRemoteAddress')
|
||||
->willReturn('1.2.3.4');
|
||||
|
||||
$this->throttler->expects($this->once())
|
||||
->method('resetDelay')
|
||||
->with(
|
||||
'1.2.3.4',
|
||||
'login',
|
||||
['user' => 'userId']
|
||||
);
|
||||
|
||||
$this->assertEquals($expected, $this->oauthApiController->getToken('refresh_token', null, 'validrefresh', 'clientId', 'clientSecret'));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
/**
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\ShareByMail\Tests;
|
||||
|
||||
use OCA\ShareByMail\Capabilities;
|
||||
use Test\TestCase;
|
||||
|
||||
class CapabilitiesTest extends TestCase {
|
||||
/** @var Capabilities */
|
||||
private $capabilities;
|
||||
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->capabilities = new Capabilities();
|
||||
}
|
||||
|
||||
public function testGetCapabilities() {
|
||||
$capabilities = [
|
||||
'files_sharing' =>
|
||||
[
|
||||
'sharebymail' =>
|
||||
[
|
||||
'enabled' => true,
|
||||
'upload_files_drop' => ['enabled' => true],
|
||||
'password' => ['enabled' => true],
|
||||
'expire_date' => ['enabled' => true]
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
$this->assertSame($capabilities, $this->capabilities->getCapabilities());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
/**
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\SystemTags\Tests\Activity;
|
||||
|
||||
use OCA\SystemTags\Activity\Setting;
|
||||
use OCP\IL10N;
|
||||
use Test\TestCase;
|
||||
|
||||
class SettingTest extends TestCase {
|
||||
/** @var IL10N|\PHPUnit_Framework_MockObject_MockObject */
|
||||
private $l;
|
||||
/** @var Setting */
|
||||
private $setting;
|
||||
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
$this->l = $this->createMock(IL10N::class);
|
||||
|
||||
$this->setting = new Setting($this->l);
|
||||
}
|
||||
|
||||
public function testGetIdentifier() {
|
||||
$this->assertSame('systemtags', $this->setting->getIdentifier());
|
||||
}
|
||||
|
||||
public function testGetName() {
|
||||
$this->l
|
||||
->expects($this->once())
|
||||
->method('t')
|
||||
->with('<strong>System tags</strong> for a file have been modified')
|
||||
->willReturn('<strong>System tags</strong> for a file have been modified');
|
||||
|
||||
$this->assertSame('<strong>System tags</strong> for a file have been modified', $this->setting->getName());
|
||||
}
|
||||
|
||||
public function testGetPriority() {
|
||||
$this->assertSame(50, $this->setting->getPriority());
|
||||
}
|
||||
|
||||
public function testCanChangeStream() {
|
||||
$this->assertSame(true, $this->setting->canChangeStream());
|
||||
}
|
||||
|
||||
public function testIsDefaultEnabledStream() {
|
||||
$this->assertSame(true, $this->setting->isDefaultEnabledStream());
|
||||
}
|
||||
|
||||
public function testCanChangeMail() {
|
||||
$this->assertSame(true, $this->setting->canChangeMail());
|
||||
}
|
||||
|
||||
public function testIsDefaultEnabledMail() {
|
||||
$this->assertSame(false, $this->setting->isDefaultEnabledMail());
|
||||
}
|
||||
}
|
|
@ -56,7 +56,6 @@ return array(
|
|||
'OCA\\User_LDAP\\Settings\\Section' => $baseDir . '/../lib/Settings/Section.php',
|
||||
'OCA\\User_LDAP\\UserPluginManager' => $baseDir . '/../lib/UserPluginManager.php',
|
||||
'OCA\\User_LDAP\\User\\DeletedUsersIndex' => $baseDir . '/../lib/User/DeletedUsersIndex.php',
|
||||
'OCA\\User_LDAP\\User\\IUserTools' => $baseDir . '/../lib/User/IUserTools.php',
|
||||
'OCA\\User_LDAP\\User\\Manager' => $baseDir . '/../lib/User/Manager.php',
|
||||
'OCA\\User_LDAP\\User\\OfflineUser' => $baseDir . '/../lib/User/OfflineUser.php',
|
||||
'OCA\\User_LDAP\\User\\User' => $baseDir . '/../lib/User/User.php',
|
||||
|
|
|
@ -71,7 +71,6 @@ class ComposerStaticInitUser_LDAP
|
|||
'OCA\\User_LDAP\\Settings\\Section' => __DIR__ . '/..' . '/../lib/Settings/Section.php',
|
||||
'OCA\\User_LDAP\\UserPluginManager' => __DIR__ . '/..' . '/../lib/UserPluginManager.php',
|
||||
'OCA\\User_LDAP\\User\\DeletedUsersIndex' => __DIR__ . '/..' . '/../lib/User/DeletedUsersIndex.php',
|
||||
'OCA\\User_LDAP\\User\\IUserTools' => __DIR__ . '/..' . '/../lib/User/IUserTools.php',
|
||||
'OCA\\User_LDAP\\User\\Manager' => __DIR__ . '/..' . '/../lib/User/Manager.php',
|
||||
'OCA\\User_LDAP\\User\\OfflineUser' => __DIR__ . '/..' . '/../lib/User/OfflineUser.php',
|
||||
'OCA\\User_LDAP\\User\\User' => __DIR__ . '/..' . '/../lib/User/User.php',
|
||||
|
|
|
@ -46,7 +46,6 @@ namespace OCA\User_LDAP;
|
|||
use OC\HintException;
|
||||
use OC\Hooks\PublicEmitter;
|
||||
use OCA\User_LDAP\Exceptions\ConstraintViolationException;
|
||||
use OCA\User_LDAP\User\IUserTools;
|
||||
use OCA\User_LDAP\User\Manager;
|
||||
use OCA\User_LDAP\User\OfflineUser;
|
||||
use OCA\User_LDAP\Mapping\AbstractMapping;
|
||||
|
@ -59,7 +58,7 @@ use OCP\IUserManager;
|
|||
* Class Access
|
||||
* @package OCA\User_LDAP
|
||||
*/
|
||||
class Access extends LDAPUtility implements IUserTools {
|
||||
class Access extends LDAPUtility {
|
||||
const UUID_ATTRIBUTES = ['entryuuid', 'nsuniqueid', 'objectguid', 'guid', 'ipauniqueid'];
|
||||
|
||||
/** @var \OCA\User_LDAP\Connection */
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2016, ownCloud, Inc.
|
||||
*
|
||||
* @author Arthur Schiwon <blizzz@arthur-schiwon.de>
|
||||
* @author Joas Schilling <coding@schilljs.com>
|
||||
* @author Morris Jobke <hey@morrisjobke.de>
|
||||
*
|
||||
* @license AGPL-3.0
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License, version 3,
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\User_LDAP\User;
|
||||
|
||||
/**
|
||||
* IUserTools
|
||||
*
|
||||
* defines methods that are required by User class for LDAP interaction
|
||||
*/
|
||||
interface IUserTools {
|
||||
public function getConnection();
|
||||
|
||||
public function readAttribute($dn, $attr, $filter = 'objectClass=*');
|
||||
|
||||
public function stringResemblesDN($string);
|
||||
|
||||
public function dn2username($dn, $ldapname = null);
|
||||
|
||||
public function username2dn($name);
|
||||
}
|
|
@ -45,7 +45,7 @@ use OCP\Notification\IManager as INotificationManager;
|
|||
* cache
|
||||
*/
|
||||
class Manager {
|
||||
/** @var IUserTools */
|
||||
/** @var Access */
|
||||
protected $access;
|
||||
|
||||
/** @var IConfig */
|
||||
|
@ -110,11 +110,11 @@ class Manager {
|
|||
}
|
||||
|
||||
/**
|
||||
* @brief binds manager to an instance of IUserTools (implemented by
|
||||
* Access). It needs to be assigned first before the manager can be used.
|
||||
* @param IUserTools
|
||||
* Binds manager to an instance of Access.
|
||||
* It needs to be assigned first before the manager can be used.
|
||||
* @param Access
|
||||
*/
|
||||
public function setLdapAccess(IUserTools $access) {
|
||||
public function setLdapAccess(Access $access) {
|
||||
$this->access = $access;
|
||||
}
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
|
||||
namespace OCA\User_LDAP\User;
|
||||
|
||||
use OCA\User_LDAP\Access;
|
||||
use OCA\User_LDAP\Connection;
|
||||
use OCA\User_LDAP\FilesystemHelper;
|
||||
use OCA\User_LDAP\LogWrapper;
|
||||
|
@ -48,7 +49,7 @@ use OCP\Notification\IManager as INotificationManager;
|
|||
*/
|
||||
class User {
|
||||
/**
|
||||
* @var IUserTools
|
||||
* @var Access
|
||||
*/
|
||||
protected $access;
|
||||
/**
|
||||
|
@ -110,8 +111,7 @@ class User {
|
|||
* @brief constructor, make sure the subclasses call this one!
|
||||
* @param string $username the internal username
|
||||
* @param string $dn the LDAP DN
|
||||
* @param IUserTools $access an instance that implements IUserTools for
|
||||
* LDAP interaction
|
||||
* @param Access $access
|
||||
* @param IConfig $config
|
||||
* @param FilesystemHelper $fs
|
||||
* @param Image $image any empty instance
|
||||
|
@ -120,7 +120,7 @@ class User {
|
|||
* @param IUserManager $userManager
|
||||
* @param INotificationManager $notificationManager
|
||||
*/
|
||||
public function __construct($username, $dn, IUserTools $access,
|
||||
public function __construct($username, $dn, Access $access,
|
||||
IConfig $config, FilesystemHelper $fs, Image $image,
|
||||
LogWrapper $log, IAvatarManager $avatarManager, IUserManager $userManager,
|
||||
INotificationManager $notificationManager) {
|
||||
|
@ -414,14 +414,23 @@ class User {
|
|||
*
|
||||
* @param string $displayName
|
||||
* @param string $displayName2
|
||||
* @returns string the effective display name
|
||||
* @return string the effective display name
|
||||
*/
|
||||
public function composeAndStoreDisplayName($displayName, $displayName2 = '') {
|
||||
$displayName2 = (string)$displayName2;
|
||||
if($displayName2 !== '') {
|
||||
$displayName .= ' (' . $displayName2 . ')';
|
||||
}
|
||||
$this->store('displayName', $displayName);
|
||||
$oldName = $this->config->getUserValue($this->uid, 'user_ldap', 'displayName', null);
|
||||
if ($oldName !== $displayName) {
|
||||
$this->store('displayName', $displayName);
|
||||
$user = $this->userManager->get($this->getUsername());
|
||||
if (!empty($oldName) && $user instanceof \OC\User\User) {
|
||||
// if it was empty, it would be a new record, not a change emitting the trigger could
|
||||
// potentially cause a UniqueConstraintViolationException, depending on some factors.
|
||||
$user->triggerChange('displayName', $displayName);
|
||||
}
|
||||
}
|
||||
return $displayName;
|
||||
}
|
||||
|
||||
|
|
|
@ -28,11 +28,13 @@
|
|||
|
||||
namespace OCA\User_LDAP\Tests\User;
|
||||
|
||||
use OCA\User_LDAP\Access;
|
||||
use OCA\User_LDAP\Connection;
|
||||
use OCA\User_LDAP\FilesystemHelper;
|
||||
use OCA\User_LDAP\ILDAPWrapper;
|
||||
use OCA\User_LDAP\LogWrapper;
|
||||
use OCA\User_LDAP\User\IUserTools;
|
||||
use OCA\User_LDAP\User\Manager;
|
||||
use OCA\User_LDAP\User\User;
|
||||
use OCP\IAvatarManager;
|
||||
use OCP\IConfig;
|
||||
use OCP\IDBConnection;
|
||||
|
@ -48,200 +50,181 @@ use OCP\Notification\IManager as INotificationManager;
|
|||
* @package OCA\User_LDAP\Tests\User
|
||||
*/
|
||||
class ManagerTest extends \Test\TestCase {
|
||||
/** @var Access|\PHPUnit_Framework_MockObject_MockObject */
|
||||
protected $access;
|
||||
|
||||
private function getTestInstances() {
|
||||
$access = $this->createMock(IUserTools::class);
|
||||
$config = $this->createMock(IConfig::class);
|
||||
$filesys = $this->createMock(FilesystemHelper::class);
|
||||
$log = $this->createMock(LogWrapper::class);
|
||||
$avaMgr = $this->createMock(IAvatarManager::class);
|
||||
$image = $this->createMock(Image::class);
|
||||
$dbc = $this->createMock(IDBConnection::class);
|
||||
$userMgr = $this->createMock(IUserManager::class);
|
||||
$notiMgr = $this->createMock(INotificationManager::class);
|
||||
/** @var IConfig|\PHPUnit_Framework_MockObject_MockObject */
|
||||
protected $config;
|
||||
|
||||
$connection = new \OCA\User_LDAP\Connection(
|
||||
$lw = $this->createMock(ILDAPWrapper::class),
|
||||
'',
|
||||
null
|
||||
/** @var FilesystemHelper|\PHPUnit_Framework_MockObject_MockObject */
|
||||
protected $fileSystemHelper;
|
||||
|
||||
/** @var LogWrapper|\PHPUnit_Framework_MockObject_MockObject */
|
||||
protected $log;
|
||||
|
||||
/** @var IAvatarManager|\PHPUnit_Framework_MockObject_MockObject */
|
||||
protected $avatarManager;
|
||||
|
||||
/** @var Image|\PHPUnit_Framework_MockObject_MockObject */
|
||||
protected $image;
|
||||
|
||||
/** @var IDBConnection|\PHPUnit_Framework_MockObject_MockObject */
|
||||
protected $dbc;
|
||||
|
||||
/** @var IUserManager|\PHPUnit_Framework_MockObject_MockObject */
|
||||
protected $ncUserManager;
|
||||
|
||||
/** @var INotificationManager|\PHPUnit_Framework_MockObject_MockObject */
|
||||
protected $notificationManager;
|
||||
|
||||
/** @var ILDAPWrapper|\PHPUnit_Framework_MockObject_MockObject */
|
||||
protected $ldapWrapper;
|
||||
|
||||
/** @var Connection */
|
||||
protected $connection;
|
||||
|
||||
/** @var Manager */
|
||||
protected $manager;
|
||||
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->access = $this->createMock(Access::class);
|
||||
$this->config = $this->createMock(IConfig::class);
|
||||
$this->fileSystemHelper = $this->createMock(FilesystemHelper::class);
|
||||
$this->log = $this->createMock(LogWrapper::class);
|
||||
$this->avatarManager = $this->createMock(IAvatarManager::class);
|
||||
$this->image = $this->createMock(Image::class);
|
||||
$this->dbc = $this->createMock(IDBConnection::class);
|
||||
$this->ncUserManager = $this->createMock(IUserManager::class);
|
||||
$this->notificationManager = $this->createMock(INotificationManager::class);
|
||||
|
||||
$this->ldapWrapper = $this->createMock(ILDAPWrapper::class);
|
||||
$this->connection = new Connection($this->ldapWrapper, '', null);
|
||||
|
||||
$this->access->expects($this->any())
|
||||
->method('getConnection')
|
||||
->will($this->returnValue($this->connection));
|
||||
|
||||
/** @noinspection PhpUnhandledExceptionInspection */
|
||||
$this->manager = new Manager(
|
||||
$this->config,
|
||||
$this->fileSystemHelper,
|
||||
$this->log,
|
||||
$this->avatarManager,
|
||||
$this->image,
|
||||
$this->dbc,
|
||||
$this->ncUserManager,
|
||||
$this->notificationManager
|
||||
);
|
||||
|
||||
$access->expects($this->any())
|
||||
->method('getConnection')
|
||||
->will($this->returnValue($connection));
|
||||
|
||||
return array($access, $config, $filesys, $image, $log, $avaMgr, $dbc, $userMgr, $notiMgr);
|
||||
$this->manager->setLdapAccess($this->access);
|
||||
}
|
||||
|
||||
public function testGetByDNExisting() {
|
||||
list($access, $config, $filesys, $image, $log, $avaMgr, $dbc, $userMgr, $notiMgr) =
|
||||
$this->getTestInstances();
|
||||
public function dnProvider() {
|
||||
return [
|
||||
['cn=foo,dc=foobar,dc=bar'],
|
||||
['uid=foo,o=foobar,c=bar'],
|
||||
['ab=cde,f=ghei,mno=pq'],
|
||||
];
|
||||
}
|
||||
|
||||
$inputDN = 'cn=foo,dc=foobar,dc=bar';
|
||||
/**
|
||||
* @dataProvider dnProvider
|
||||
*/
|
||||
public function testGetByDNExisting(string $inputDN) {
|
||||
$uid = '563418fc-423b-1033-8d1c-ad5f418ee02e';
|
||||
|
||||
$access->expects($this->once())
|
||||
$this->access->expects($this->once())
|
||||
->method('stringResemblesDN')
|
||||
->with($this->equalTo($inputDN))
|
||||
->will($this->returnValue(true));
|
||||
|
||||
$access->expects($this->once())
|
||||
$this->access->expects($this->once())
|
||||
->method('dn2username')
|
||||
->with($this->equalTo($inputDN))
|
||||
->will($this->returnValue($uid));
|
||||
|
||||
$access->expects($this->never())
|
||||
$this->access->expects($this->never())
|
||||
->method('username2dn');
|
||||
|
||||
$manager = new Manager($config, $filesys, $log, $avaMgr, $image, $dbc, $userMgr, $notiMgr);
|
||||
$manager->setLdapAccess($access);
|
||||
$user = $manager->get($inputDN);
|
||||
/** @noinspection PhpUnhandledExceptionInspection */
|
||||
$this->manager->get($inputDN);
|
||||
|
||||
// Now we fetch the user again. If this leads to a failing test,
|
||||
// runtime caching the manager is broken.
|
||||
$user = $manager->get($inputDN);
|
||||
/** @noinspection PhpUnhandledExceptionInspection */
|
||||
$user = $this->manager->get($inputDN);
|
||||
|
||||
$this->assertInstanceOf('\OCA\User_LDAP\User\User', $user);
|
||||
}
|
||||
|
||||
public function testGetByEDirectoryDN() {
|
||||
list($access, $config, $filesys, $image, $log, $avaMgr, $dbc, $userMgr, $notiMgr) =
|
||||
$this->getTestInstances();
|
||||
|
||||
$inputDN = 'uid=foo,o=foobar,c=bar';
|
||||
$uid = '563418fc-423b-1033-8d1c-ad5f418ee02e';
|
||||
|
||||
$access->expects($this->once())
|
||||
->method('stringResemblesDN')
|
||||
->with($this->equalTo($inputDN))
|
||||
->will($this->returnValue(true));
|
||||
|
||||
$access->expects($this->once())
|
||||
->method('dn2username')
|
||||
->with($this->equalTo($inputDN))
|
||||
->will($this->returnValue($uid));
|
||||
|
||||
$access->expects($this->never())
|
||||
->method('username2dn');
|
||||
|
||||
$manager = new Manager($config, $filesys, $log, $avaMgr, $image, $dbc, $userMgr, $notiMgr);
|
||||
$manager->setLdapAccess($access);
|
||||
$user = $manager->get($inputDN);
|
||||
|
||||
$this->assertInstanceOf('\OCA\User_LDAP\User\User', $user);
|
||||
}
|
||||
|
||||
public function testGetByExoticDN() {
|
||||
list($access, $config, $filesys, $image, $log, $avaMgr, $dbc, $userMgr, $notiMgr) =
|
||||
$this->getTestInstances();
|
||||
|
||||
$inputDN = 'ab=cde,f=ghei,mno=pq';
|
||||
$uid = '563418fc-423b-1033-8d1c-ad5f418ee02e';
|
||||
|
||||
$access->expects($this->once())
|
||||
->method('stringResemblesDN')
|
||||
->with($this->equalTo($inputDN))
|
||||
->will($this->returnValue(true));
|
||||
|
||||
$access->expects($this->once())
|
||||
->method('dn2username')
|
||||
->with($this->equalTo($inputDN))
|
||||
->will($this->returnValue($uid));
|
||||
|
||||
$access->expects($this->never())
|
||||
->method('username2dn');
|
||||
|
||||
$manager = new Manager($config, $filesys, $log, $avaMgr, $image, $dbc, $userMgr, $notiMgr);
|
||||
$manager->setLdapAccess($access);
|
||||
$user = $manager->get($inputDN);
|
||||
|
||||
$this->assertInstanceOf('\OCA\User_LDAP\User\User', $user);
|
||||
$this->assertInstanceOf(User::class, $user);
|
||||
}
|
||||
|
||||
public function testGetByDNNotExisting() {
|
||||
list($access, $config, $filesys, $image, $log, $avaMgr, $dbc, $userMgr, $notiMgr) =
|
||||
$this->getTestInstances();
|
||||
|
||||
$inputDN = 'cn=gone,dc=foobar,dc=bar';
|
||||
|
||||
$access->expects($this->once())
|
||||
$this->access->expects($this->once())
|
||||
->method('stringResemblesDN')
|
||||
->with($this->equalTo($inputDN))
|
||||
->will($this->returnValue(true));
|
||||
|
||||
$access->expects($this->once())
|
||||
$this->access->expects($this->once())
|
||||
->method('dn2username')
|
||||
->with($this->equalTo($inputDN))
|
||||
->will($this->returnValue(false));
|
||||
|
||||
$access->expects($this->once())
|
||||
$this->access->expects($this->once())
|
||||
->method('username2dn')
|
||||
->with($this->equalTo($inputDN))
|
||||
->will($this->returnValue(false));
|
||||
|
||||
$manager = new Manager($config, $filesys, $log, $avaMgr, $image, $dbc, $userMgr, $notiMgr);
|
||||
$manager->setLdapAccess($access);
|
||||
$user = $manager->get($inputDN);
|
||||
/** @noinspection PhpUnhandledExceptionInspection */
|
||||
$user = $this->manager->get($inputDN);
|
||||
|
||||
$this->assertNull($user);
|
||||
}
|
||||
|
||||
public function testGetByUidExisting() {
|
||||
list($access, $config, $filesys, $image, $log, $avaMgr, $dbc, $userMgr, $notiMgr) =
|
||||
$this->getTestInstances();
|
||||
|
||||
$dn = 'cn=foo,dc=foobar,dc=bar';
|
||||
$uid = '563418fc-423b-1033-8d1c-ad5f418ee02e';
|
||||
|
||||
$access->expects($this->never())
|
||||
$this->access->expects($this->never())
|
||||
->method('dn2username');
|
||||
|
||||
$access->expects($this->once())
|
||||
$this->access->expects($this->once())
|
||||
->method('username2dn')
|
||||
->with($this->equalTo($uid))
|
||||
->will($this->returnValue($dn));
|
||||
|
||||
$access->expects($this->once())
|
||||
$this->access->expects($this->once())
|
||||
->method('stringResemblesDN')
|
||||
->with($this->equalTo($uid))
|
||||
->will($this->returnValue(false));
|
||||
|
||||
$manager = new Manager($config, $filesys, $log, $avaMgr, $image, $dbc, $userMgr, $notiMgr);
|
||||
$manager->setLdapAccess($access);
|
||||
$user = $manager->get($uid);
|
||||
/** @noinspection PhpUnhandledExceptionInspection */
|
||||
$this->manager->get($uid);
|
||||
|
||||
// Now we fetch the user again. If this leads to a failing test,
|
||||
// runtime caching the manager is broken.
|
||||
$user = $manager->get($uid);
|
||||
/** @noinspection PhpUnhandledExceptionInspection */
|
||||
$user = $this->manager->get($uid);
|
||||
|
||||
$this->assertInstanceOf('\OCA\User_LDAP\User\User', $user);
|
||||
$this->assertInstanceOf(User::class, $user);
|
||||
}
|
||||
|
||||
public function testGetByUidNotExisting() {
|
||||
list($access, $config, $filesys, $image, $log, $avaMgr, $dbc, $userMgr, $notiMgr) =
|
||||
$this->getTestInstances();
|
||||
|
||||
$uid = 'gone';
|
||||
|
||||
$access->expects($this->never())
|
||||
$this->access->expects($this->never())
|
||||
->method('dn2username');
|
||||
|
||||
$access->expects($this->exactly(1))
|
||||
$this->access->expects($this->exactly(1))
|
||||
->method('username2dn')
|
||||
->with($this->equalTo($uid))
|
||||
->will($this->returnValue(false));
|
||||
|
||||
$manager = new Manager($config, $filesys, $log, $avaMgr, $image, $dbc, $userMgr, $notiMgr);
|
||||
$manager->setLdapAccess($access);
|
||||
$user = $manager->get($uid);
|
||||
/** @noinspection PhpUnhandledExceptionInspection */
|
||||
$user = $this->manager->get($uid);
|
||||
|
||||
$this->assertNull($user);
|
||||
}
|
||||
|
||||
public function attributeRequestProvider() {
|
||||
return [
|
||||
[ false ],
|
||||
[ true ],
|
||||
[false],
|
||||
[true],
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -249,23 +232,16 @@ class ManagerTest extends \Test\TestCase {
|
|||
* @dataProvider attributeRequestProvider
|
||||
*/
|
||||
public function testGetAttributes($minimal) {
|
||||
list($access, $config, $filesys, $image, $log, $avaMgr, $dbc, $userMgr, $notiMgr) =
|
||||
$this->getTestInstances();
|
||||
|
||||
$manager = new Manager($config, $filesys, $log, $avaMgr, $image, $dbc, $userMgr, $notiMgr);
|
||||
$manager->setLdapAccess($access);
|
||||
|
||||
$connection = $access->getConnection();
|
||||
$connection->setConfiguration([
|
||||
$this->connection->setConfiguration([
|
||||
'ldapEmailAttribute' => 'mail',
|
||||
'ldapUserAvatarRule' => 'default',
|
||||
'ldapQuotaAttribute' => '',
|
||||
]);
|
||||
|
||||
$attributes = $manager->getAttributes($minimal);
|
||||
$attributes = $this->manager->getAttributes($minimal);
|
||||
|
||||
$this->assertTrue(in_array('dn', $attributes));
|
||||
$this->assertTrue(in_array($access->getConnection()->ldapEmailAttribute, $attributes));
|
||||
$this->assertTrue(in_array($this->access->getConnection()->ldapEmailAttribute, $attributes));
|
||||
$this->assertFalse(in_array('', $attributes));
|
||||
$this->assertSame(!$minimal, in_array('jpegphoto', $attributes));
|
||||
$this->assertSame(!$minimal, in_array('thumbnailphoto', $attributes));
|
||||
|
|
|
@ -998,23 +998,58 @@ class UserTest extends \Test\TestCase {
|
|||
|
||||
public function displayNameProvider() {
|
||||
return [
|
||||
['Roland Deschain', '', 'Roland Deschain'],
|
||||
['Roland Deschain', null, 'Roland Deschain'],
|
||||
['Roland Deschain', 'gunslinger@darktower.com', 'Roland Deschain (gunslinger@darktower.com)'],
|
||||
['Roland Deschain', '', 'Roland Deschain', false],
|
||||
['Roland Deschain', '', 'Roland Deschain', true],
|
||||
['Roland Deschain', null, 'Roland Deschain', false],
|
||||
['Roland Deschain', 'gunslinger@darktower.com', 'Roland Deschain (gunslinger@darktower.com)', false],
|
||||
['Roland Deschain', 'gunslinger@darktower.com', 'Roland Deschain (gunslinger@darktower.com)', true],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider displayNameProvider
|
||||
*/
|
||||
public function testComposeAndStoreDisplayName($part1, $part2, $expected) {
|
||||
public function testComposeAndStoreDisplayName($part1, $part2, $expected, $expectTriggerChange) {
|
||||
$this->config->expects($this->once())
|
||||
->method('setUserValue');
|
||||
$oldName = $expectTriggerChange ? 'xxGunslingerxx' : null;
|
||||
$this->config->expects($this->once())
|
||||
->method('getUserValue')
|
||||
->with($this->user->getUsername(), 'user_ldap', 'displayName', null)
|
||||
->willReturn($oldName);
|
||||
|
||||
$ncUserObj = $this->createMock(\OC\User\User::class);
|
||||
if ($expectTriggerChange) {
|
||||
$ncUserObj->expects($this->once())
|
||||
->method('triggerChange')
|
||||
->with('displayName', $expected);
|
||||
} else {
|
||||
$ncUserObj->expects($this->never())
|
||||
->method('triggerChange');
|
||||
}
|
||||
$this->userManager->expects($this->once())
|
||||
->method('get')
|
||||
->willReturn($ncUserObj);
|
||||
|
||||
$displayName = $this->user->composeAndStoreDisplayName($part1, $part2);
|
||||
$this->assertSame($expected, $displayName);
|
||||
}
|
||||
|
||||
public function testComposeAndStoreDisplayNameNoOverwrite() {
|
||||
$displayName = 'Randall Flagg';
|
||||
$this->config->expects($this->never())
|
||||
->method('setUserValue');
|
||||
$this->config->expects($this->once())
|
||||
->method('getUserValue')
|
||||
->willReturn($displayName);
|
||||
|
||||
$this->userManager->expects($this->never())
|
||||
->method('get'); // Implicit: no triggerChange can be called
|
||||
|
||||
$composedDisplayName = $this->user->composeAndStoreDisplayName($displayName);
|
||||
$this->assertSame($composedDisplayName, $displayName);
|
||||
}
|
||||
|
||||
public function testHandlePasswordExpiryWarningDefaultPolicy() {
|
||||
$this->connection->expects($this->any())
|
||||
->method('__get')
|
||||
|
|
|
@ -32,7 +32,7 @@ handlebars -n OCA.WorkflowEngine.Templates apps/workflowengine/js/templates -f a
|
|||
handlebars -n OCA.Sharing.Templates apps/files_sharing/js/templates -f apps/files_sharing/js/templates.js
|
||||
|
||||
# Files external
|
||||
handlebars -n OCA.External.Templates apps/files_external/js/templates -f apps/files_external/js/templates.js
|
||||
handlebars -n OCA.Files_External.Templates apps/files_external/js/templates -f apps/files_external/js/templates.js
|
||||
|
||||
if [[ $(git diff --name-only) ]]; then
|
||||
echo "Please submit your compiled handlebars templates"
|
||||
|
|
|
@ -1499,11 +1499,26 @@ $CONFIG = array(
|
|||
/**
|
||||
* List of trusted proxy servers
|
||||
*
|
||||
* If you configure these also consider setting `forwarded_for_headers` which
|
||||
* otherwise defaults to `HTTP_X_FORWARDED_FOR` (the `X-Forwarded-For` header).
|
||||
* You may set this to an array containing a combination of
|
||||
* - IPv4 addresses, e.g. `192.168.2.123`
|
||||
* - IPv4 ranges in CIDR notation, e.g. `192.168.2.0/24`
|
||||
* - IPv6 addresses, e.g. `fd9e:21a7:a92c:2323::1`
|
||||
*
|
||||
* _(CIDR notation for IPv6 is currently work in progress and thus not
|
||||
* available as of yet)_
|
||||
*
|
||||
* When an incoming request's `REMOTE_ADDR` matches any of the IP addresses
|
||||
* specified here, it is assumed to be a proxy instead of a client. Thus, the
|
||||
* client IP will be read from the HTTP header specified in
|
||||
* `forwarded_for_headers` instead of from `REMOTE_ADDR`.
|
||||
*
|
||||
* So if you configure `trusted_proxies`, also consider setting
|
||||
* `forwarded_for_headers` which otherwise defaults to `HTTP_X_FORWARDED_FOR`
|
||||
* (the `X-Forwarded-For` header).
|
||||
*
|
||||
* Defaults to an empty array.
|
||||
*/
|
||||
'trusted_proxies' => array('203.0.113.45', '198.51.100.128'),
|
||||
'trusted_proxies' => array('203.0.113.45', '198.51.100.128', '192.168.2.0/24'),
|
||||
|
||||
/**
|
||||
* Headers that should be trusted as client IP address in combination with
|
||||
|
@ -1648,4 +1663,14 @@ $CONFIG = array(
|
|||
* If this is set to "false" it will not show the link.
|
||||
*/
|
||||
'simpleSignUpLink.shown' => true,
|
||||
|
||||
/**
|
||||
* By default autocompletion is enabled for the login form on Nextcloud's login page.
|
||||
* While this is enabled, browsers are allowed to "remember" login names and such.
|
||||
* Some companies require it to be disabled to comply with their security policy.
|
||||
*
|
||||
* Simply set this property to "false", if you want to turn this feature off.
|
||||
*/
|
||||
|
||||
'login_form_autocomplete' => true,
|
||||
);
|
||||
|
|
|
@ -171,6 +171,14 @@ class LoginController extends Controller {
|
|||
$parameters['loginName'] = '';
|
||||
$parameters['user_autofocus'] = true;
|
||||
}
|
||||
|
||||
$autocomplete = $this->config->getSystemValue('login_form_autocomplete', true);
|
||||
if ($autocomplete){
|
||||
$parameters['login_form_autocomplete'] = 'on';
|
||||
} else {
|
||||
$parameters['login_form_autocomplete'] = 'off';
|
||||
}
|
||||
|
||||
if (!empty($redirect_url)) {
|
||||
$parameters['redirect_url'] = $redirect_url;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright Copyright (c) 2018 Roeland Jago Douma <roeland@famdouma.nl>
|
||||
*
|
||||
* @author Roeland Jago Douma <roeland@famdouma.nl>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OC\Core\Migrations;
|
||||
|
||||
use Closure;
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
use OCP\DB\ISchemaWrapper;
|
||||
use OCP\Migration\SimpleMigrationStep;
|
||||
use OCP\Migration\IOutput;
|
||||
|
||||
class Version15000Date20181015062942 extends SimpleMigrationStep {
|
||||
|
||||
/**
|
||||
* @param IOutput $output
|
||||
* @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
|
||||
* @param array $options
|
||||
* @return null|ISchemaWrapper
|
||||
*/
|
||||
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options) {
|
||||
/** @var ISchemaWrapper $schema */
|
||||
$schema = $schemaClosure();
|
||||
|
||||
$table = $schema->getTable('share');
|
||||
$table->addColumn('hide_download', 'smallint', [
|
||||
'notnull' => true,
|
||||
'length' => 1,
|
||||
'default' => 0,
|
||||
]);
|
||||
|
||||
return $schema;
|
||||
}
|
||||
}
|
|
@ -781,27 +781,47 @@ kbd {
|
|||
|
||||
/* TABS ------------------------------------------------------------ */
|
||||
.tabHeaders {
|
||||
display: inline-block;
|
||||
margin: 15px;
|
||||
display: flex;
|
||||
margin-bottom: 16px;
|
||||
|
||||
.tabHeader {
|
||||
float: left;
|
||||
padding: 12px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
cursor: pointer;
|
||||
color: var(--color-text-lighter);
|
||||
margin-bottom: 1px;
|
||||
padding: 5px;
|
||||
|
||||
/* Use same amount as sidebar padding */
|
||||
&:first-child {
|
||||
padding-left: 15px;
|
||||
}
|
||||
&:last-child {
|
||||
padding-right: 15px;
|
||||
}
|
||||
|
||||
.icon {
|
||||
display: inline-block;
|
||||
width: 16px;
|
||||
width: 100%;
|
||||
height: 16px;
|
||||
background-size: 16px;
|
||||
vertical-align: middle;
|
||||
margin-top: -2px;
|
||||
margin-right: 3px;
|
||||
opacity: .7;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-text-lighter);
|
||||
margin-bottom: 1px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
&.selected {
|
||||
font-weight: bold;
|
||||
|
|
|
@ -39,4 +39,6 @@
|
|||
--border-radius-pill: $border-radius-pill;
|
||||
|
||||
--font-face: $font-face;
|
||||
|
||||
--animation-quick: $animation-quick;
|
||||
}
|
||||
|
|
|
@ -25,12 +25,12 @@
|
|||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
display: none !important; /* Hiding should take precedence */
|
||||
}
|
||||
|
||||
.hidden-visually {
|
||||
position: absolute;
|
||||
left:-10000px;
|
||||
left: -10000px;
|
||||
top: auto;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
|
@ -47,4 +47,4 @@
|
|||
|
||||
.inlineblock {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -513,25 +513,90 @@ nav[role='navigation'] {
|
|||
height: 20px;
|
||||
}
|
||||
|
||||
/* app title popup */
|
||||
/* App title */
|
||||
li span {
|
||||
display: none;
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
overflow: visible;
|
||||
background-color: var(--color-main-background);
|
||||
white-space: nowrap;
|
||||
border: none;
|
||||
border-radius: var(--border-radius);
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
color: var(--color-text-lighter);
|
||||
width: auto;
|
||||
left: 50%;
|
||||
top: 100%;
|
||||
transform: translateX(-50%);
|
||||
padding: 4px 10px;
|
||||
filter: drop-shadow(0 1px 10px var(--color-box-shadow));
|
||||
z-index: 100;
|
||||
color: var(--color-primary-text);
|
||||
bottom: -5px;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
|
||||
/* Set up transitions for showing app titles on hover */
|
||||
li {
|
||||
/* Prevent flicker effect because of low-hanging span element */
|
||||
overflow-y: hidden;
|
||||
|
||||
/* App icon */
|
||||
svg,
|
||||
.icon-more-white {
|
||||
transition: transform var(--animation-quick) ease;
|
||||
}
|
||||
|
||||
/* App title */
|
||||
span {
|
||||
transition: all var(--animation-quick) ease;
|
||||
}
|
||||
|
||||
/* Triangle */
|
||||
a::before {
|
||||
transition: border var(--animation-quick) ease;
|
||||
}
|
||||
}
|
||||
|
||||
/* Show all app titles on hovering app menu area */
|
||||
&:hover {
|
||||
li {
|
||||
/* Move up app icon */
|
||||
svg,
|
||||
.icon-more-white {
|
||||
transform: translateY(-7px);
|
||||
}
|
||||
|
||||
/* Show app title */
|
||||
span {
|
||||
opacity: .6;
|
||||
bottom: 2px;
|
||||
z-index: -1; /* fix clickability issue - otherwise we need to move the span into the link */
|
||||
}
|
||||
|
||||
/* Prominent app title for current and hovered/focused app */
|
||||
&:hover span,
|
||||
&:focus span,
|
||||
.active + span {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* Smaller triangle because of limited space */
|
||||
a::before {
|
||||
border-width: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Also show app title on focusing single entry (showing all on focus is only possible with CSS4 and parent selectors) */
|
||||
li a:focus {
|
||||
/* Move up app icon */
|
||||
svg,
|
||||
.icon-more-white, {
|
||||
transform: translateY(-7px);
|
||||
}
|
||||
|
||||
/* Show app title */
|
||||
& + span,
|
||||
span {
|
||||
opacity: 1;
|
||||
bottom: 2px;
|
||||
}
|
||||
|
||||
/* Smaller triangle because of limited space */
|
||||
&::before {
|
||||
border-width: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
/* show triangle below active app */
|
||||
|
@ -549,6 +614,7 @@ nav[role='navigation'] {
|
|||
bottom: 0;
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* triangle focus feedback */
|
||||
li a.active::before,
|
||||
li:hover a::before,
|
||||
|
@ -560,7 +626,6 @@ nav[role='navigation'] {
|
|||
z-index: 99;
|
||||
}
|
||||
li:hover a::before,
|
||||
li:hover a::before,
|
||||
li a.active:hover::before,
|
||||
li a:focus::before {
|
||||
z-index: 101;
|
||||
|
|
|
@ -466,3 +466,7 @@ img, object, video, button, textarea, input, select, div[contenteditable='true']
|
|||
@include icon-color('search', 'actions', $color-black, 1, true);
|
||||
}
|
||||
|
||||
|
||||
.icon-talk {
|
||||
@include icon-color('app-dark', 'spreed', $color-black, 1);
|
||||
}
|
||||
|
|
|
@ -163,6 +163,7 @@ input[type='reset'] {
|
|||
padding: 6px 12px;
|
||||
width: auto;
|
||||
min-height: 34px;
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
box-sizing: border-box;
|
||||
background-color: var(--color-background-dark);
|
||||
|
|
|
@ -70,7 +70,8 @@
|
|||
.ui-widget-header .ui-state-highlight {
|
||||
border: 1px solid var(--color-main-background);
|
||||
background: var(--color-main-background) none;
|
||||
color: var(--color-text-lighter);
|
||||
color: var(--color-text-light);
|
||||
font-weight: 600;
|
||||
}
|
||||
.ui-state-highlight a,
|
||||
.ui-widget-content .ui-state-highlight a,
|
||||
|
@ -171,9 +172,12 @@
|
|||
&.ui-menu {
|
||||
padding: 0;
|
||||
.ui-menu-item a {
|
||||
color: var(--color-text-lighter);
|
||||
padding: 4px 4px 4px 14px;
|
||||
|
||||
&.ui-state-focus, &.ui-state-active {
|
||||
font-weight: inherit;
|
||||
box-shadow: inset 4px 0 var(--color-primary);
|
||||
color: var(--color-text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -798,7 +798,7 @@ code {
|
|||
&.view-grid {
|
||||
$grid-size: 120px;
|
||||
$grid-pad: 10px;
|
||||
$name-height: 20px;
|
||||
$name-height: 30px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
|
@ -818,20 +818,22 @@ code {
|
|||
flex-direction: column;
|
||||
width: $grid-size - 2 * $grid-pad;
|
||||
|
||||
|
||||
td {
|
||||
border: none;
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
border-radius: var(--border-radius);
|
||||
|
||||
&.filename {
|
||||
padding: #{$grid-size - 2 * $grid-pad} 0 0 0;
|
||||
background-position: center top;
|
||||
background-size: contain;
|
||||
line-height: $name-height;
|
||||
height: $name-height;
|
||||
}
|
||||
&.filesize {
|
||||
line-height: $name-height;
|
||||
text-align: left;
|
||||
line-height: $name-height / 3;
|
||||
width: 100%;
|
||||
}
|
||||
&.date {
|
||||
display: none;
|
||||
|
|
|
@ -80,6 +80,7 @@ $border-radius-pill: 100px !default;
|
|||
|
||||
$font-face: 'Nunito', 'Open Sans', Frutiger, Calibri, 'Myriad Pro', Myriad, sans-serif !default;
|
||||
|
||||
$animation-quick: 100ms;
|
||||
|
||||
// various structure data
|
||||
$header-height: 50px;
|
||||
|
|
|
@ -11,6 +11,16 @@
|
|||
<input id="linkText-{{cid}}" class="linkText" type="text" readonly="readonly" value="{{shareLinkURL}}" />
|
||||
</span>
|
||||
</li>
|
||||
{{#if showHideDownloadCheckbox}}
|
||||
<li>
|
||||
<span class="shareOption menuitem">
|
||||
<span class="icon-loading-small hidden"></span>
|
||||
<input type="checkbox" name="hideDownload" id="sharingDialogHideDownload-{{cid}}" class="checkbox hideDownloadCheckbox"
|
||||
{{#if hideDownload}}checked="checked"{{/if}} />
|
||||
<label for="sharingDialogHideDownload-{{cid}}">{{hideDownloadLabel}}</label>
|
||||
</span>
|
||||
</li>
|
||||
{{/if}}
|
||||
{{#if publicUpload}}
|
||||
<li>
|
||||
<span class="shareOption menuitem">
|
||||
|
|
|
@ -47,6 +47,8 @@
|
|||
'change .linkCheckbox': 'onLinkCheckBoxChange',
|
||||
// open menu
|
||||
'click .share-menu .icon-more': 'onToggleMenu',
|
||||
// hide download
|
||||
'change .hideDownloadCheckbox': 'onHideDownloadChange',
|
||||
// password
|
||||
'focusout input.linkPassText': 'onPasswordEntered',
|
||||
'keyup input.linkPassText': 'onPasswordKeyUp',
|
||||
|
@ -179,6 +181,20 @@
|
|||
$el.select();
|
||||
},
|
||||
|
||||
onHideDownloadChange: function() {
|
||||
var $checkbox = this.$('.hideDownloadCheckbox');
|
||||
$checkbox.siblings('.icon-loading-small').removeClass('hidden').addClass('inlineblock');
|
||||
|
||||
var hideDownload = false;
|
||||
if($checkbox.is(':checked')) {
|
||||
hideDownload = true;
|
||||
}
|
||||
|
||||
this.model.saveLinkShare({
|
||||
hideDownload: hideDownload
|
||||
});
|
||||
},
|
||||
|
||||
onShowPasswordClick: function() {
|
||||
this.$el.find('.linkPass').slideToggle(OC.menuSpeed);
|
||||
this.$el.find('.linkPassMenu').toggleClass('hidden');
|
||||
|
@ -401,6 +417,9 @@
|
|||
var passwordPlaceholderInitial = this.configModel.get('enforcePasswordForPublicLink')
|
||||
? PASSWORD_PLACEHOLDER_MESSAGE : PASSWORD_PLACEHOLDER_MESSAGE_OPTIONAL;
|
||||
|
||||
var showHideDownloadCheckbox = !this.model.isFolder();
|
||||
var hideDownload = this.model.get('linkShare').hideDownload;
|
||||
|
||||
var publicEditable =
|
||||
!this.model.isFolder()
|
||||
&& isLinkShare
|
||||
|
@ -464,6 +483,9 @@
|
|||
|
||||
shareLinkURL: this.model.get('linkShare').link,
|
||||
urlLabel: t('core', 'Link'),
|
||||
showHideDownloadCheckbox: showHideDownloadCheckbox,
|
||||
hideDownload: hideDownload,
|
||||
hideDownloadLabel: t('core', 'Hide download'),
|
||||
enablePasswordLabel: t('core', 'Password protect'),
|
||||
passwordLabel: t('core', 'Password'),
|
||||
passwordPlaceholder: isPasswordSet ? PASSWORD_PLACEHOLDER : PASSWORD_PLACEHOLDER_MESSAGE,
|
||||
|
|
|
@ -314,7 +314,9 @@
|
|||
var $edit = _this.$('#canEdit-' + _this.cid + '-' + sharee.shareId);
|
||||
if($edit.length === 1) {
|
||||
$edit.prop('checked', sharee.editPermissionState === 'checked');
|
||||
$edit.prop('indeterminate', sharee.editPermissionState === 'indeterminate');
|
||||
if (sharee.isFolder) {
|
||||
$edit.prop('indeterminate', sharee.editPermissionState === 'indeterminate');
|
||||
}
|
||||
}
|
||||
});
|
||||
this.$('.popovermenu').on('afterHide', function() {
|
||||
|
|
|
@ -312,6 +312,41 @@
|
|||
|
||||
var suggestions = exactMatches.concat(users).concat(groups).concat(remotes).concat(remoteGroups).concat(emails).concat(circles).concat(rooms).concat(lookup);
|
||||
|
||||
function dynamicSort(property) {
|
||||
return function (a,b) {
|
||||
var aProperty = '';
|
||||
var bProperty = '';
|
||||
if (typeof a[property] !== 'undefined') {
|
||||
aProperty = a[property];
|
||||
}
|
||||
if (typeof b[property] !== 'undefined') {
|
||||
bProperty = b[property];
|
||||
}
|
||||
return (aProperty < bProperty) ? -1 : (aProperty > bProperty) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort share entries by uuid to properly group them
|
||||
*/
|
||||
var grouped = suggestions.sort(dynamicSort('uuid'));
|
||||
|
||||
var previousUuid = null;
|
||||
var groupedLength = grouped.length;
|
||||
var result = [];
|
||||
/**
|
||||
* build the result array that only contains all contact entries from
|
||||
* merged contacts, if the search term matches its contact name
|
||||
*/
|
||||
for (i = 0; i < groupedLength; i++) {
|
||||
if (typeof grouped[i].uuid !== 'undefined' && grouped[i].uuid === previousUuid) {
|
||||
grouped[i].merged = true;
|
||||
}
|
||||
if (searchTerm === grouped[i].name || typeof grouped[i].merged === 'undefined') {
|
||||
result.push(grouped[i]);
|
||||
}
|
||||
previousUuid = grouped[i].uuid;
|
||||
}
|
||||
var moreResultsAvailable =
|
||||
(
|
||||
oc_config['sharing.maxAutocompleteResults'] > 0
|
||||
|
@ -328,7 +363,7 @@
|
|||
)
|
||||
);
|
||||
|
||||
deferred.resolve(suggestions, exactMatches, moreResultsAvailable);
|
||||
deferred.resolve(result, exactMatches, moreResultsAvailable);
|
||||
} else {
|
||||
deferred.reject(result.ocs.meta.message);
|
||||
}
|
||||
|
@ -441,33 +476,72 @@
|
|||
},
|
||||
|
||||
autocompleteRenderItem: function(ul, item) {
|
||||
|
||||
var icon = 'icon-user';
|
||||
var text = item.label;
|
||||
if (typeof item.name !== 'undefined') {
|
||||
text = item.name;
|
||||
}
|
||||
if (item.value.shareType === OC.Share.SHARE_TYPE_GROUP) {
|
||||
text = t('core', '{sharee} (group)', { sharee: text }, undefined, { escape: false });
|
||||
icon = 'icon-contacts-dark';
|
||||
} else if (item.value.shareType === OC.Share.SHARE_TYPE_REMOTE) {
|
||||
text = t('core', '{sharee} (remote)', {sharee: text}, undefined, {escape: false});
|
||||
icon = 'icon-shared';
|
||||
} else if (item.value.shareType === OC.Share.SHARE_TYPE_REMOTE_GROUP) {
|
||||
text = t('core', '{sharee} (remote group)', { sharee: text }, undefined, { escape: false });
|
||||
icon = 'icon-shared';
|
||||
} else if (item.value.shareType === OC.Share.SHARE_TYPE_EMAIL) {
|
||||
text = t('core', '{sharee} (email)', { sharee: text }, undefined, { escape: false });
|
||||
icon = 'icon-mail';
|
||||
} else if (item.value.shareType === OC.Share.SHARE_TYPE_CIRCLE) {
|
||||
text = t('core', '{sharee} ({type}, {owner})', {sharee: text, type: item.value.circleInfo, owner: item.value.circleOwner}, undefined, {escape: false});
|
||||
icon = 'icon-circle';
|
||||
} else if (item.value.shareType === OC.Share.SHARE_TYPE_ROOM) {
|
||||
text = t('core', '{sharee} (conversation)', { sharee: text }, undefined, { escape: false });
|
||||
icon = 'icon-talk';
|
||||
}
|
||||
var description = '';
|
||||
var getTranslatedType = function(type) {
|
||||
switch (type) {
|
||||
case 'HOME':
|
||||
return t('core', 'Home');
|
||||
case 'WORK':
|
||||
return t('core', 'Home');
|
||||
case 'OTHER':
|
||||
return t('core', 'Other');
|
||||
default:
|
||||
return type;
|
||||
}
|
||||
};
|
||||
if (typeof item.type !== 'undefined' && item.type !== null) {
|
||||
description = getTranslatedType(item.type);
|
||||
}
|
||||
var insert = $("<div class='share-autocomplete-item'/>");
|
||||
var avatar = $("<div class='avatardiv'></div>").appendTo(insert);
|
||||
if (item.value.shareType === OC.Share.SHARE_TYPE_USER || item.value.shareType === OC.Share.SHARE_TYPE_CIRCLE) {
|
||||
avatar.avatar(item.value.shareWith, 32, undefined, undefined, undefined, item.label);
|
||||
if (item.merged) {
|
||||
insert.addClass('merged');
|
||||
text = item.value.shareWith;
|
||||
} else {
|
||||
avatar.imageplaceholder(text, undefined, 32);
|
||||
var avatar = $("<div class='avatardiv'></div>").appendTo(insert);
|
||||
if (item.value.shareType === OC.Share.SHARE_TYPE_USER || item.value.shareType === OC.Share.SHARE_TYPE_CIRCLE) {
|
||||
avatar.avatar(item.value.shareWith, 32, undefined, undefined, undefined, item.label);
|
||||
} else {
|
||||
if (typeof item.uuid === 'undefined') {
|
||||
item.uuid = text;
|
||||
}
|
||||
avatar.imageplaceholder(item.uuid, text, 32);
|
||||
}
|
||||
description = item.value.shareWith;
|
||||
}
|
||||
if (description !== '') {
|
||||
insert.addClass('with-description');
|
||||
}
|
||||
|
||||
$("<div class='autocomplete-item-text'></div>")
|
||||
.text(text)
|
||||
.html(
|
||||
text.replace(
|
||||
new RegExp(this.term, "gi"),
|
||||
"<span class='ui-state-highlight'>$&</span>")
|
||||
+ '<span class="autocomplete-item-details">' + description + '</span>'
|
||||
)
|
||||
.appendTo(insert);
|
||||
insert.attr('title', item.value.shareWith);
|
||||
insert.append('<span class="icon '+icon+'" title="' + text + '"></span>');
|
||||
insert = $("<a>")
|
||||
.append(insert);
|
||||
return $("<li>")
|
||||
|
@ -479,6 +553,20 @@
|
|||
_onSelectRecipient: function(e, s) {
|
||||
var self = this;
|
||||
|
||||
if (e.keyCode == 9) {
|
||||
e.preventDefault();
|
||||
if (typeof s.item.name !== 'undefined') {
|
||||
e.target.value = s.item.name;
|
||||
} else {
|
||||
e.target.value = s.item.label;
|
||||
}
|
||||
setTimeout(function() {
|
||||
$(e.target).attr('disabled', false)
|
||||
.autocomplete('search', $(e.target).val());
|
||||
}, 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
// Ensure that the keydown handler for the input field is not
|
||||
// called; otherwise it would try to add the recipient again, which
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
* @typedef {object} OC.Share.Types.LinkShareInfo
|
||||
* @property {bool} isLinkShare
|
||||
* @property {string} token
|
||||
* @property {bool} hideDownload
|
||||
* @property {string|null} password
|
||||
* @property {string} link
|
||||
* @property {number} permissions
|
||||
|
@ -136,6 +137,7 @@
|
|||
call = this.updateShare(shareId, attributes, options);
|
||||
} else {
|
||||
attributes = _.defaults(attributes, {
|
||||
hideDownload: false,
|
||||
password: '',
|
||||
passwordChanged: false,
|
||||
permissions: OC.PERMISSION_READ,
|
||||
|
@ -614,6 +616,12 @@
|
|||
var hcp = this.hasCreatePermission(shareIndex);
|
||||
var hup = this.hasUpdatePermission(shareIndex);
|
||||
var hdp = this.hasDeletePermission(shareIndex);
|
||||
if (this.isFile()) {
|
||||
if (hcp || hup || hdp) {
|
||||
return 'checked';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
if (!hcp && !hup && !hdp) {
|
||||
return '';
|
||||
}
|
||||
|
@ -866,6 +874,9 @@
|
|||
isLinkShare: true,
|
||||
id: share.id,
|
||||
token: share.token,
|
||||
// hide_download is returned as an int, so force it
|
||||
// to a boolean
|
||||
hideDownload: !!share.hide_download,
|
||||
password: share.share_with,
|
||||
link: link,
|
||||
permissions: share.permissions,
|
||||
|
|
|
@ -61,6 +61,20 @@ templates['sharedialoglinkshareview'] = template({"1":function(container,depth0,
|
|||
templates['sharedialoglinkshareview_popover_menu'] = template({"1":function(container,depth0,helpers,partials,data) {
|
||||
var stack1, helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=helpers.helperMissing, alias3="function", alias4=container.escapeExpression;
|
||||
|
||||
return " <li>\n <span class=\"shareOption menuitem\">\n <span class=\"icon-loading-small hidden\"></span>\n <input type=\"checkbox\" name=\"hideDownload\" id=\"sharingDialogHideDownload-"
|
||||
+ alias4(((helper = (helper = helpers.cid || (depth0 != null ? depth0.cid : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"cid","hash":{},"data":data}) : helper)))
|
||||
+ "\" class=\"checkbox hideDownloadCheckbox\"\n "
|
||||
+ ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.hideDownload : depth0),{"name":"if","hash":{},"fn":container.program(2, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
|
||||
+ " />\n <label for=\"sharingDialogHideDownload-"
|
||||
+ alias4(((helper = (helper = helpers.cid || (depth0 != null ? depth0.cid : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"cid","hash":{},"data":data}) : helper)))
|
||||
+ "\">"
|
||||
+ alias4(((helper = (helper = helpers.hideDownloadLabel || (depth0 != null ? depth0.hideDownloadLabel : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"hideDownloadLabel","hash":{},"data":data}) : helper)))
|
||||
+ "</label>\n </span>\n </li>\n";
|
||||
},"2":function(container,depth0,helpers,partials,data) {
|
||||
return "checked=\"checked\"";
|
||||
},"4":function(container,depth0,helpers,partials,data) {
|
||||
var stack1, helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=helpers.helperMissing, alias3="function", alias4=container.escapeExpression;
|
||||
|
||||
return " <li>\n <span class=\"shareOption menuitem\">\n <span class=\"icon-loading-small hidden\"></span>\n <input type=\"radio\" name=\"publicUpload\" value=\""
|
||||
+ alias4(((helper = (helper = helpers.publicUploadRValue || (depth0 != null ? depth0.publicUploadRValue : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"publicUploadRValue","hash":{},"data":data}) : helper)))
|
||||
+ "\" id=\"sharingDialogAllowPublicUpload-r-"
|
||||
|
@ -92,7 +106,7 @@ templates['sharedialoglinkshareview_popover_menu'] = template({"1":function(cont
|
|||
+ "\">"
|
||||
+ alias4(((helper = (helper = helpers.publicUploadWLabel || (depth0 != null ? depth0.publicUploadWLabel : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"publicUploadWLabel","hash":{},"data":data}) : helper)))
|
||||
+ "</label>\n </span>\n </li>\n";
|
||||
},"3":function(container,depth0,helpers,partials,data) {
|
||||
},"6":function(container,depth0,helpers,partials,data) {
|
||||
var stack1, helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=helpers.helperMissing, alias3="function", alias4=container.escapeExpression;
|
||||
|
||||
return " <li id=\"allowPublicEditingWrapper\">\n <span class=\"shareOption menuitem\">\n <span class=\"icon-loading-small hidden\"></span>\n <input type=\"checkbox\" name=\"allowPublicEditing\" id=\"sharingDialogAllowPublicEditing-"
|
||||
|
@ -104,41 +118,39 @@ templates['sharedialoglinkshareview_popover_menu'] = template({"1":function(cont
|
|||
+ "\">"
|
||||
+ alias4(((helper = (helper = helpers.publicEditingLabel || (depth0 != null ? depth0.publicEditingLabel : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"publicEditingLabel","hash":{},"data":data}) : helper)))
|
||||
+ "</label>\n </span>\n </li>\n";
|
||||
},"5":function(container,depth0,helpers,partials,data) {
|
||||
},"8":function(container,depth0,helpers,partials,data) {
|
||||
var stack1, helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=helpers.helperMissing, alias3="function", alias4=container.escapeExpression;
|
||||
|
||||
return " <li>\n <span class=\"shareOption menuitem\">\n <input type=\"checkbox\" name=\"showPassword\" id=\"showPassword-"
|
||||
+ alias4(((helper = (helper = helpers.cid || (depth0 != null ? depth0.cid : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"cid","hash":{},"data":data}) : helper)))
|
||||
+ "\" class=\"checkbox showPasswordCheckbox\"\n "
|
||||
+ ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.isPasswordSet : depth0),{"name":"if","hash":{},"fn":container.program(6, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
|
||||
+ ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.isPasswordSet : depth0),{"name":"if","hash":{},"fn":container.program(2, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
|
||||
+ " "
|
||||
+ ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.isPasswordEnforced : depth0),{"name":"if","hash":{},"fn":container.program(8, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
|
||||
+ ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.isPasswordEnforced : depth0),{"name":"if","hash":{},"fn":container.program(9, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
|
||||
+ " value=\"1\" />\n <label for=\"showPassword-"
|
||||
+ alias4(((helper = (helper = helpers.cid || (depth0 != null ? depth0.cid : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"cid","hash":{},"data":data}) : helper)))
|
||||
+ "\">"
|
||||
+ alias4(((helper = (helper = helpers.enablePasswordLabel || (depth0 != null ? depth0.enablePasswordLabel : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"enablePasswordLabel","hash":{},"data":data}) : helper)))
|
||||
+ "</label>\n </span>\n </li>\n <li class=\""
|
||||
+ ((stack1 = helpers.unless.call(alias1,(depth0 != null ? depth0.isPasswordSet : depth0),{"name":"unless","hash":{},"fn":container.program(10, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
|
||||
+ ((stack1 = helpers.unless.call(alias1,(depth0 != null ? depth0.isPasswordSet : depth0),{"name":"unless","hash":{},"fn":container.program(11, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
|
||||
+ " linkPassMenu\">\n <span class=\"shareOption menuitem icon-share-pass\">\n <input id=\"linkPassText-"
|
||||
+ alias4(((helper = (helper = helpers.cid || (depth0 != null ? depth0.cid : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"cid","hash":{},"data":data}) : helper)))
|
||||
+ "\" class=\"linkPassText\" type=\"password\" placeholder=\""
|
||||
+ alias4(((helper = (helper = helpers.passwordPlaceholder || (depth0 != null ? depth0.passwordPlaceholder : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"passwordPlaceholder","hash":{},"data":data}) : helper)))
|
||||
+ "\" autocomplete=\"new-password\" />\n <span class=\"icon icon-loading-small hidden\"></span>\n </span>\n </li>\n";
|
||||
},"6":function(container,depth0,helpers,partials,data) {
|
||||
return "checked=\"checked\"";
|
||||
},"8":function(container,depth0,helpers,partials,data) {
|
||||
},"9":function(container,depth0,helpers,partials,data) {
|
||||
return "disabled=\"disabled\"";
|
||||
},"10":function(container,depth0,helpers,partials,data) {
|
||||
},"11":function(container,depth0,helpers,partials,data) {
|
||||
return "hidden";
|
||||
},"12":function(container,depth0,helpers,partials,data) {
|
||||
},"13":function(container,depth0,helpers,partials,data) {
|
||||
var helper;
|
||||
|
||||
return container.escapeExpression(((helper = (helper = helpers.expireDate || (depth0 != null ? depth0.expireDate : depth0)) != null ? helper : helpers.helperMissing),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),{"name":"expireDate","hash":{},"data":data}) : helper)));
|
||||
},"14":function(container,depth0,helpers,partials,data) {
|
||||
},"15":function(container,depth0,helpers,partials,data) {
|
||||
var helper;
|
||||
|
||||
return container.escapeExpression(((helper = (helper = helpers.defaultExpireDate || (depth0 != null ? depth0.defaultExpireDate : depth0)) != null ? helper : helpers.helperMissing),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),{"name":"defaultExpireDate","hash":{},"data":data}) : helper)));
|
||||
},"16":function(container,depth0,helpers,partials,data) {
|
||||
},"17":function(container,depth0,helpers,partials,data) {
|
||||
var helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=helpers.helperMissing, alias3="function", alias4=container.escapeExpression;
|
||||
|
||||
return " <li>\n <a href=\"#\" class=\"shareOption menuitem pop-up\" data-url=\""
|
||||
|
@ -162,21 +174,22 @@ templates['sharedialoglinkshareview_popover_menu'] = template({"1":function(cont
|
|||
+ "\" class=\"linkText\" type=\"text\" readonly=\"readonly\" value=\""
|
||||
+ alias4(((helper = (helper = helpers.shareLinkURL || (depth0 != null ? depth0.shareLinkURL : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"shareLinkURL","hash":{},"data":data}) : helper)))
|
||||
+ "\" />\n </span>\n </li>\n"
|
||||
+ ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.publicUpload : depth0),{"name":"if","hash":{},"fn":container.program(1, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
|
||||
+ ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.publicEditing : depth0),{"name":"if","hash":{},"fn":container.program(3, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
|
||||
+ ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.showPasswordCheckBox : depth0),{"name":"if","hash":{},"fn":container.program(5, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
|
||||
+ ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.showHideDownloadCheckbox : depth0),{"name":"if","hash":{},"fn":container.program(1, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
|
||||
+ ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.publicUpload : depth0),{"name":"if","hash":{},"fn":container.program(4, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
|
||||
+ ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.publicEditing : depth0),{"name":"if","hash":{},"fn":container.program(6, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
|
||||
+ ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.showPasswordCheckBox : depth0),{"name":"if","hash":{},"fn":container.program(8, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
|
||||
+ " <li>\n <span class=\"shareOption menuitem\">\n <input id=\"expireDate-"
|
||||
+ alias4(((helper = (helper = helpers.cid || (depth0 != null ? depth0.cid : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"cid","hash":{},"data":data}) : helper)))
|
||||
+ "\" type=\"checkbox\" name=\"expirationDate\" class=\"expireDate checkbox\"\n "
|
||||
+ ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.hasExpireDate : depth0),{"name":"if","hash":{},"fn":container.program(6, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
|
||||
+ ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.hasExpireDate : depth0),{"name":"if","hash":{},"fn":container.program(2, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
|
||||
+ " "
|
||||
+ ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.isExpirationEnforced : depth0),{"name":"if","hash":{},"fn":container.program(8, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
|
||||
+ ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.isExpirationEnforced : depth0),{"name":"if","hash":{},"fn":container.program(9, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
|
||||
+ "\" />\n <label for=\"expireDate-"
|
||||
+ alias4(((helper = (helper = helpers.cid || (depth0 != null ? depth0.cid : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"cid","hash":{},"data":data}) : helper)))
|
||||
+ "\">"
|
||||
+ alias4(((helper = (helper = helpers.expireDateLabel || (depth0 != null ? depth0.expireDateLabel : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"expireDateLabel","hash":{},"data":data}) : helper)))
|
||||
+ "</label>\n </span>\n </li>\n <li class=\""
|
||||
+ ((stack1 = helpers.unless.call(alias1,(depth0 != null ? depth0.hasExpireDate : depth0),{"name":"unless","hash":{},"fn":container.program(10, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
|
||||
+ ((stack1 = helpers.unless.call(alias1,(depth0 != null ? depth0.hasExpireDate : depth0),{"name":"unless","hash":{},"fn":container.program(11, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
|
||||
+ "\">\n <span class=\"menuitem icon-expiredate expirationDateContainer-"
|
||||
+ alias4(((helper = (helper = helpers.cid || (depth0 != null ? depth0.cid : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"cid","hash":{},"data":data}) : helper)))
|
||||
+ "\">\n <label for=\"expirationDatePicker-"
|
||||
|
@ -190,7 +203,7 @@ templates['sharedialoglinkshareview_popover_menu'] = template({"1":function(cont
|
|||
+ "\" class=\"datepicker\" type=\"text\" placeholder=\""
|
||||
+ alias4(((helper = (helper = helpers.expirationDatePlaceholder || (depth0 != null ? depth0.expirationDatePlaceholder : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"expirationDatePlaceholder","hash":{},"data":data}) : helper)))
|
||||
+ "\" value=\""
|
||||
+ ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.hasExpireDate : depth0),{"name":"if","hash":{},"fn":container.program(12, data, 0),"inverse":container.program(14, data, 0),"data":data})) != null ? stack1 : "")
|
||||
+ ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.hasExpireDate : depth0),{"name":"if","hash":{},"fn":container.program(13, data, 0),"inverse":container.program(15, data, 0),"data":data})) != null ? stack1 : "")
|
||||
+ "\" />\n </span>\n </li>\n <li>\n <a href=\"#\" class=\"share-add\">\n <span class=\"icon-loading-small hidden\"></span>\n <span class=\"icon icon-edit\"></span>\n <span>"
|
||||
+ alias4(((helper = (helper = helpers.addNoteLabel || (depth0 != null ? depth0.addNoteLabel : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"addNoteLabel","hash":{},"data":data}) : helper)))
|
||||
+ "</span>\n <input type=\"button\" class=\"share-note-delete icon-delete\">\n </a>\n </li>\n <li class=\"share-note-form share-note-link hidden\">\n <span class=\"menuitem icon-note\">\n <textarea class=\"share-note\">"
|
||||
|
@ -198,7 +211,7 @@ templates['sharedialoglinkshareview_popover_menu'] = template({"1":function(cont
|
|||
+ "</textarea>\n <input type=\"submit\" class=\"icon-confirm share-note-submit\" value=\"\" id=\"add-note-"
|
||||
+ alias4(((helper = (helper = helpers.shareId || (depth0 != null ? depth0.shareId : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"shareId","hash":{},"data":data}) : helper)))
|
||||
+ "\" />\n </span>\n </li>\n"
|
||||
+ ((stack1 = helpers.each.call(alias1,(depth0 != null ? depth0.social : depth0),{"name":"each","hash":{},"fn":container.program(16, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
|
||||
+ ((stack1 = helpers.each.call(alias1,(depth0 != null ? depth0.social : depth0),{"name":"each","hash":{},"fn":container.program(17, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
|
||||
+ " </ul>\n</div>\n";
|
||||
},"useData":true});
|
||||
templates['sharedialoglinkshareview_popover_menu_pending'] = template({"1":function(container,depth0,helpers,partials,data) {
|
||||
|
|
|
@ -72,6 +72,100 @@ describe('OC.Share.ShareDialogLinkShareView', function () {
|
|||
configModel.isShareWithLinkAllowed.restore();
|
||||
});
|
||||
|
||||
describe('hide download', function () {
|
||||
|
||||
var $hideDownloadCheckbox;
|
||||
var $workingIcon;
|
||||
|
||||
beforeEach(function () {
|
||||
// Needed to render the view
|
||||
configModel.isShareWithLinkAllowed.returns(true);
|
||||
|
||||
// Setting the share also triggers the rendering
|
||||
shareModel.set({
|
||||
linkShare: {
|
||||
isLinkShare: true,
|
||||
}
|
||||
});
|
||||
|
||||
$hideDownloadCheckbox = view.$el.find('.hideDownloadCheckbox');
|
||||
$workingIcon = $hideDownloadCheckbox.prev('.icon-loading-small');
|
||||
|
||||
sinon.stub(shareModel, 'saveLinkShare');
|
||||
|
||||
expect($workingIcon.hasClass('hidden')).toBeTruthy();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
shareModel.saveLinkShare.restore();
|
||||
});
|
||||
|
||||
it('is shown if the share is a file', function() {
|
||||
expect($hideDownloadCheckbox.length).toBeTruthy();
|
||||
});
|
||||
|
||||
it('is not shown if the share is a folder', function() {
|
||||
shareModel.fileInfoModel.set('mimetype', 'httpd/unix-directory');
|
||||
|
||||
// Setting the item type also triggers the rendering
|
||||
shareModel.set({
|
||||
itemType: 'folder'
|
||||
});
|
||||
|
||||
$hideDownloadCheckbox = view.$el.find('.hideDownloadCheckbox');
|
||||
|
||||
expect($hideDownloadCheckbox.length).toBeFalsy();
|
||||
});
|
||||
|
||||
it('checkbox is checked when the setting is enabled', function () {
|
||||
shareModel.set({
|
||||
linkShare: {
|
||||
isLinkShare: true,
|
||||
hideDownload: true
|
||||
}
|
||||
});
|
||||
|
||||
$hideDownloadCheckbox = view.$el.find('.hideDownloadCheckbox');
|
||||
|
||||
expect($hideDownloadCheckbox.is(':checked')).toEqual(true);
|
||||
});
|
||||
|
||||
it('checkbox is not checked when the setting is disabled', function () {
|
||||
expect($hideDownloadCheckbox.is(':checked')).toEqual(false);
|
||||
});
|
||||
|
||||
it('enables the setting if clicked when unchecked', function () {
|
||||
// Simulate the click by checking the checkbox and then triggering
|
||||
// the "change" event.
|
||||
$hideDownloadCheckbox.prop('checked', true);
|
||||
$hideDownloadCheckbox.change();
|
||||
|
||||
expect($workingIcon.hasClass('hidden')).toBeFalsy();
|
||||
expect(shareModel.saveLinkShare.withArgs({ hideDownload: true }).calledOnce).toBeTruthy();
|
||||
});
|
||||
|
||||
it('disables the setting if clicked when checked', function () {
|
||||
shareModel.set({
|
||||
linkShare: {
|
||||
isLinkShare: true,
|
||||
hideDownload: true
|
||||
}
|
||||
});
|
||||
|
||||
$hideDownloadCheckbox = view.$el.find('.hideDownloadCheckbox');
|
||||
$workingIcon = $hideDownloadCheckbox.prev('.icon-loading-small');
|
||||
|
||||
// Simulate the click by unchecking the checkbox and then triggering
|
||||
// the "change" event.
|
||||
$hideDownloadCheckbox.prop('checked', false);
|
||||
$hideDownloadCheckbox.change();
|
||||
|
||||
expect($workingIcon.hasClass('hidden')).toBeFalsy();
|
||||
expect(shareModel.saveLinkShare.withArgs({ hideDownload: false }).calledOnce).toBeTruthy();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('onPasswordEntered', function () {
|
||||
|
||||
var $passwordText;
|
||||
|
|
|
@ -90,6 +90,37 @@ describe('OC.Share.ShareDialogShareeListView', function () {
|
|||
});
|
||||
|
||||
describe('Sets correct initial checkbox state', function () {
|
||||
|
||||
it('marks edit box as unchecked for file shares without edit permissions', function () {
|
||||
shareModel.set('shares', [{
|
||||
id: 100,
|
||||
item_source: 123,
|
||||
permissions: 1,
|
||||
share_type: OC.Share.SHARE_TYPE_USER,
|
||||
share_with: 'user1',
|
||||
share_with_displayname: 'User One',
|
||||
uid_owner: oc_current_user,
|
||||
itemType: 'file'
|
||||
}]);
|
||||
listView.render();
|
||||
expect(listView.$el.find("input[name='edit']").is(':not(:checked)')).toEqual(true);
|
||||
});
|
||||
|
||||
it('marks edit box as checked for file shares', function () {
|
||||
shareModel.set('shares', [{
|
||||
id: 100,
|
||||
item_source: 123,
|
||||
permissions: 1 | OC.PERMISSION_UPDATE,
|
||||
share_type: OC.Share.SHARE_TYPE_USER,
|
||||
share_with: 'user1',
|
||||
share_with_displayname: 'User One',
|
||||
uid_owner: oc_current_user,
|
||||
itemType: 'file'
|
||||
}]);
|
||||
listView.render();
|
||||
expect(listView.$el.find("input[name='edit']").is(':checked')).toEqual(true);
|
||||
});
|
||||
|
||||
it('marks edit box as indeterminate when only some permissions are given', function () {
|
||||
shareModel.set('shares', [{
|
||||
id: 100,
|
||||
|
|
|
@ -168,7 +168,8 @@ describe('OC.Share.ShareItemModel', function() {
|
|||
stime: 1403884258,
|
||||
storage: 1,
|
||||
token: 'tehtoken',
|
||||
uid_owner: 'root'
|
||||
uid_owner: 'root',
|
||||
hide_download: 1
|
||||
}
|
||||
]));
|
||||
|
||||
|
@ -186,6 +187,7 @@ describe('OC.Share.ShareItemModel', function() {
|
|||
|
||||
var linkShare = model.get('linkShare');
|
||||
expect(linkShare.isLinkShare).toEqual(true);
|
||||
expect(linkShare.hideDownload).toEqual(true);
|
||||
|
||||
// TODO: check more attributes
|
||||
});
|
||||
|
@ -289,7 +291,8 @@ describe('OC.Share.ShareItemModel', function() {
|
|||
stime: 1403884258,
|
||||
storage: 1,
|
||||
token: 'tehtoken',
|
||||
uid_owner: 'root'
|
||||
uid_owner: 'root',
|
||||
hide_download: 0
|
||||
}, {
|
||||
displayname_owner: 'root',
|
||||
expiration: '2015-10-15 00:00:00',
|
||||
|
@ -307,7 +310,8 @@ describe('OC.Share.ShareItemModel', function() {
|
|||
stime: 1403884509,
|
||||
storage: 1,
|
||||
token: 'anothertoken',
|
||||
uid_owner: 'root'
|
||||
uid_owner: 'root',
|
||||
hide_download: 1
|
||||
}]
|
||||
));
|
||||
OC.currentUser = 'root';
|
||||
|
@ -320,6 +324,7 @@ describe('OC.Share.ShareItemModel', function() {
|
|||
var linkShare = model.get('linkShare');
|
||||
expect(linkShare.isLinkShare).toEqual(true);
|
||||
expect(linkShare.token).toEqual('tehtoken');
|
||||
expect(linkShare.hideDownload).toEqual(false);
|
||||
|
||||
// TODO: check child too
|
||||
});
|
||||
|
@ -579,6 +584,7 @@ describe('OC.Share.ShareItemModel', function() {
|
|||
|
||||
expect(addShareStub.calledOnce).toEqual(true);
|
||||
expect(addShareStub.firstCall.args[0]).toEqual({
|
||||
hideDownload: false,
|
||||
password: '',
|
||||
passwordChanged: false,
|
||||
permissions: OC.PERMISSION_READ,
|
||||
|
@ -603,6 +609,7 @@ describe('OC.Share.ShareItemModel', function() {
|
|||
|
||||
expect(addShareStub.calledOnce).toEqual(true);
|
||||
expect(addShareStub.firstCall.args[0]).toEqual({
|
||||
hideDownload: false,
|
||||
password: '',
|
||||
passwordChanged: false,
|
||||
permissions: OC.PERMISSION_READ,
|
||||
|
|
|
@ -141,9 +141,6 @@ OC.L10N.register(
|
|||
"No users found for {search}" : "Geen gebruiker gevind vir {search}",
|
||||
"An error occurred (\"{message}\"). Please try again" : "'n Fout het voorgekom (\"{message}\"). Probeer asseblief weer",
|
||||
"An error occurred. Please try again" : "'n Fout het voorgekom. Probeer asseblief weer",
|
||||
"{sharee} (group)" : "{sharee} (groep)",
|
||||
"{sharee} (remote)" : "{sharee} (afgeleë)",
|
||||
"{sharee} (email)" : "{sharee} (e-pos)",
|
||||
"{sharee} ({type}, {owner})" : "{sharee} ({type}, {owner})",
|
||||
"Share" : "Deel",
|
||||
"Name or email address..." : "Naam of e-posadres...",
|
||||
|
@ -243,6 +240,9 @@ OC.L10N.register(
|
|||
"Error setting expiration date" : "Fout terwyl vervaldatum stel",
|
||||
"The public link will expire no later than {days} days after it is created" : "Die publieke skakel sal presies {days} na dit geskep is verval",
|
||||
"{{shareInitiatorDisplayName}} shared via link" : "{{shareInitiatorDisplayName}} gedeel via skakel",
|
||||
"{sharee} (group)" : "{sharee} (groep)",
|
||||
"{sharee} (remote)" : "{sharee} (afgeleë)",
|
||||
"{sharee} (email)" : "{sharee} (e-pos)",
|
||||
"Share with other people by entering a user or group or an email address." : "Deel met ander deur 'n gebruiker, groep of e-posadres in te vul. ",
|
||||
"The specified document has not been found on the server." : "Die gekose dokument was nie op die bediener gevind nie.",
|
||||
"You can click here to return to %s." : "U kan hier klik om terug te keer na %s",
|
||||
|
|
|
@ -139,9 +139,6 @@
|
|||
"No users found for {search}" : "Geen gebruiker gevind vir {search}",
|
||||
"An error occurred (\"{message}\"). Please try again" : "'n Fout het voorgekom (\"{message}\"). Probeer asseblief weer",
|
||||
"An error occurred. Please try again" : "'n Fout het voorgekom. Probeer asseblief weer",
|
||||
"{sharee} (group)" : "{sharee} (groep)",
|
||||
"{sharee} (remote)" : "{sharee} (afgeleë)",
|
||||
"{sharee} (email)" : "{sharee} (e-pos)",
|
||||
"{sharee} ({type}, {owner})" : "{sharee} ({type}, {owner})",
|
||||
"Share" : "Deel",
|
||||
"Name or email address..." : "Naam of e-posadres...",
|
||||
|
@ -241,6 +238,9 @@
|
|||
"Error setting expiration date" : "Fout terwyl vervaldatum stel",
|
||||
"The public link will expire no later than {days} days after it is created" : "Die publieke skakel sal presies {days} na dit geskep is verval",
|
||||
"{{shareInitiatorDisplayName}} shared via link" : "{{shareInitiatorDisplayName}} gedeel via skakel",
|
||||
"{sharee} (group)" : "{sharee} (groep)",
|
||||
"{sharee} (remote)" : "{sharee} (afgeleë)",
|
||||
"{sharee} (email)" : "{sharee} (e-pos)",
|
||||
"Share with other people by entering a user or group or an email address." : "Deel met ander deur 'n gebruiker, groep of e-posadres in te vul. ",
|
||||
"The specified document has not been found on the server." : "Die gekose dokument was nie op die bediener gevind nie.",
|
||||
"You can click here to return to %s." : "U kan hier klik om terug te keer na %s",
|
||||
|
|
|
@ -130,8 +130,6 @@ OC.L10N.register(
|
|||
"No users or groups found for {search}" : "Nun s'alcontraron usuarios o grupos pa {search}",
|
||||
"No users found for {search}" : "Nun s'alcontraron usuarios pa {search}",
|
||||
"An error occurred. Please try again" : "Asocedió un fallu. Volvi tentalo, por favor",
|
||||
"{sharee} (group)" : "{sharee} (grupu)",
|
||||
"{sharee} (email)" : "{sharee} (corréu)",
|
||||
"{sharee} ({type}, {owner})" : "{sharee} ({type}, {owner})",
|
||||
"Share" : "Compartir",
|
||||
"Name or email address..." : "Nome o direición de corréu...",
|
||||
|
@ -230,6 +228,8 @@ OC.L10N.register(
|
|||
"Error setting expiration date" : "Fallu afitando la fecha de caducidá",
|
||||
"The public link will expire no later than {days} days after it is created" : "L'enllaz públicu va caducar enantes de {days} díes dende la so creación",
|
||||
"{{shareInitiatorDisplayName}} shared via link" : "{{shareInitiatorDisplayName}} compartió per enllaz",
|
||||
"{sharee} (group)" : "{sharee} (grupu)",
|
||||
"{sharee} (email)" : "{sharee} (corréu)",
|
||||
"Share with other people by entering a user or group, a federated cloud ID or an email address." : "Comparti con otra xente introduciendo un usuariu, grupu, ID de ñube federada o direición de corréu.",
|
||||
"Share with other people by entering a user or group or a federated cloud ID." : "Comparti con otra xente introduciendo un usuariu, grupu o ID de ñube federada.",
|
||||
"Share with other people by entering a user or group or an email address." : "Comparti con otra xente introduciendo un usuariu, grupu o direición de corréu.",
|
||||
|
|
|
@ -128,8 +128,6 @@
|
|||
"No users or groups found for {search}" : "Nun s'alcontraron usuarios o grupos pa {search}",
|
||||
"No users found for {search}" : "Nun s'alcontraron usuarios pa {search}",
|
||||
"An error occurred. Please try again" : "Asocedió un fallu. Volvi tentalo, por favor",
|
||||
"{sharee} (group)" : "{sharee} (grupu)",
|
||||
"{sharee} (email)" : "{sharee} (corréu)",
|
||||
"{sharee} ({type}, {owner})" : "{sharee} ({type}, {owner})",
|
||||
"Share" : "Compartir",
|
||||
"Name or email address..." : "Nome o direición de corréu...",
|
||||
|
@ -228,6 +226,8 @@
|
|||
"Error setting expiration date" : "Fallu afitando la fecha de caducidá",
|
||||
"The public link will expire no later than {days} days after it is created" : "L'enllaz públicu va caducar enantes de {days} díes dende la so creación",
|
||||
"{{shareInitiatorDisplayName}} shared via link" : "{{shareInitiatorDisplayName}} compartió per enllaz",
|
||||
"{sharee} (group)" : "{sharee} (grupu)",
|
||||
"{sharee} (email)" : "{sharee} (corréu)",
|
||||
"Share with other people by entering a user or group, a federated cloud ID or an email address." : "Comparti con otra xente introduciendo un usuariu, grupu, ID de ñube federada o direición de corréu.",
|
||||
"Share with other people by entering a user or group or a federated cloud ID." : "Comparti con otra xente introduciendo un usuariu, grupu o ID de ñube federada.",
|
||||
"Share with other people by entering a user or group or an email address." : "Comparti con otra xente introduciendo un usuariu, grupu o direición de corréu.",
|
||||
|
|
|
@ -151,12 +151,8 @@ OC.L10N.register(
|
|||
"No users or groups found for {search}" : "Няма потребители или групи за {search}",
|
||||
"No users found for {search}" : "Няма потребители за {search}",
|
||||
"An error occurred. Please try again" : "Възникна грешка. Моля, опитайте отново",
|
||||
"{sharee} (group)" : "{sharee} (група)",
|
||||
"{sharee} (remote)" : "{sharee} (отдалечен)",
|
||||
"{sharee} (remote group)" : "{sharee} (отдалечена група)",
|
||||
"{sharee} (email)" : "{sharee} (имейл)",
|
||||
"{sharee} ({type}, {owner})" : "{sharee} ({type}, {owner})",
|
||||
"{sharee} (conversation)" : "{sharee} (разговор)",
|
||||
"Share" : "Споделяне",
|
||||
"Name or email address..." : "Име или имейл адрес...",
|
||||
"Name..." : "Име...",
|
||||
|
@ -278,6 +274,9 @@ OC.L10N.register(
|
|||
"Error setting expiration date" : "Грешка при задаване на срок на валидност",
|
||||
"The public link will expire no later than {days} days after it is created" : "Общодостъпната връзка ще изтече не по-късно от {days} дни след създаването ѝ.",
|
||||
"{{shareInitiatorDisplayName}} shared via link" : "{{shareInitiatorDisplayName}} споделен с връзка",
|
||||
"{sharee} (group)" : "{sharee} (група)",
|
||||
"{sharee} (remote)" : "{sharee} (отдалечен)",
|
||||
"{sharee} (email)" : "{sharee} (имейл)",
|
||||
"The specified document has not been found on the server." : "Избраният документ не е намерен на сървъра.",
|
||||
"You can click here to return to %s." : "Можете да натиснете тук, за да се върнете на %s.",
|
||||
"The server encountered an internal error and was unable to complete your request." : "Поради вътрешно сървърна грешка, сървърът не можа да изпълни заявката ви.",
|
||||
|
@ -297,6 +296,7 @@ OC.L10N.register(
|
|||
"This page will refresh itself when the %s instance is available again." : "Страницата ще се зареди автоматично, когато %s е отново на линия.",
|
||||
"Thank you for your patience." : "Благодарим ви за търпението.",
|
||||
"You are about to grant %s access to your %s account." : "Ще разрешите на %s да ползва профила %s.",
|
||||
"{sharee} (conversation)" : "{sharee} (разговор)",
|
||||
"Please log in before granting %s access to your %s account." : "Необходимо е да се впишете, преди да дадете достъп на %s до вашия %s профил."
|
||||
},
|
||||
"nplurals=2; plural=(n != 1);");
|
||||
|
|
|
@ -149,12 +149,8 @@
|
|||
"No users or groups found for {search}" : "Няма потребители или групи за {search}",
|
||||
"No users found for {search}" : "Няма потребители за {search}",
|
||||
"An error occurred. Please try again" : "Възникна грешка. Моля, опитайте отново",
|
||||
"{sharee} (group)" : "{sharee} (група)",
|
||||
"{sharee} (remote)" : "{sharee} (отдалечен)",
|
||||
"{sharee} (remote group)" : "{sharee} (отдалечена група)",
|
||||
"{sharee} (email)" : "{sharee} (имейл)",
|
||||
"{sharee} ({type}, {owner})" : "{sharee} ({type}, {owner})",
|
||||
"{sharee} (conversation)" : "{sharee} (разговор)",
|
||||
"Share" : "Споделяне",
|
||||
"Name or email address..." : "Име или имейл адрес...",
|
||||
"Name..." : "Име...",
|
||||
|
@ -276,6 +272,9 @@
|
|||
"Error setting expiration date" : "Грешка при задаване на срок на валидност",
|
||||
"The public link will expire no later than {days} days after it is created" : "Общодостъпната връзка ще изтече не по-късно от {days} дни след създаването ѝ.",
|
||||
"{{shareInitiatorDisplayName}} shared via link" : "{{shareInitiatorDisplayName}} споделен с връзка",
|
||||
"{sharee} (group)" : "{sharee} (група)",
|
||||
"{sharee} (remote)" : "{sharee} (отдалечен)",
|
||||
"{sharee} (email)" : "{sharee} (имейл)",
|
||||
"The specified document has not been found on the server." : "Избраният документ не е намерен на сървъра.",
|
||||
"You can click here to return to %s." : "Можете да натиснете тук, за да се върнете на %s.",
|
||||
"The server encountered an internal error and was unable to complete your request." : "Поради вътрешно сървърна грешка, сървърът не можа да изпълни заявката ви.",
|
||||
|
@ -295,6 +294,7 @@
|
|||
"This page will refresh itself when the %s instance is available again." : "Страницата ще се зареди автоматично, когато %s е отново на линия.",
|
||||
"Thank you for your patience." : "Благодарим ви за търпението.",
|
||||
"You are about to grant %s access to your %s account." : "Ще разрешите на %s да ползва профила %s.",
|
||||
"{sharee} (conversation)" : "{sharee} (разговор)",
|
||||
"Please log in before granting %s access to your %s account." : "Необходимо е да се впишете, преди да дадете достъп на %s до вашия %s профил."
|
||||
},"pluralForm" :"nplurals=2; plural=(n != 1);"
|
||||
}
|
|
@ -210,12 +210,8 @@ OC.L10N.register(
|
|||
"No users found for {search}" : "No s'han trobat usuaris per {search}",
|
||||
"An error occurred (\"{message}\"). Please try again" : "S'ha produït un error (\"{message}\"). Si us plau, torni a intentar-ho",
|
||||
"An error occurred. Please try again" : "S'ha produït un error. Si us plau, torni a intentar-ho",
|
||||
"{sharee} (group)" : "{sharee} (grup)",
|
||||
"{sharee} (remote)" : "{sharee} (remot)",
|
||||
"{sharee} (remote group)" : "{sharee} (grup remot)",
|
||||
"{sharee} (email)" : "{sharee} (email)",
|
||||
"{sharee} ({type}, {owner})" : "{sharee} ({type}, {owner})",
|
||||
"{sharee} (conversation)" : "{sharee} (conversation)",
|
||||
"Share" : "Comparteix",
|
||||
"Name or email address..." : "Nom o adreça electrònica...",
|
||||
"Name or federated cloud ID..." : "Nom o ID de Núvol Federat…",
|
||||
|
@ -382,6 +378,9 @@ OC.L10N.register(
|
|||
"Error setting expiration date" : "Error en establir la data de venciment",
|
||||
"The public link will expire no later than {days} days after it is created" : "L'enllaç públic tindrà venciment abans de {days} dies després de crear-lo",
|
||||
"{{shareInitiatorDisplayName}} shared via link" : "{{shareInitiatorDisplayName}} ha compartit per enllaç",
|
||||
"{sharee} (group)" : "{sharee} (grup)",
|
||||
"{sharee} (remote)" : "{sharee} (remot)",
|
||||
"{sharee} (email)" : "{sharee} (email)",
|
||||
"Share with other people by entering a user or group, a federated cloud ID or an email address." : "Compartir amb altres persones introduint un usuari o grup, un ID de núvol federat o una adreça d’email.",
|
||||
"Share with other people by entering a user or group or a federated cloud ID." : "Compartir amb altres persones introduint un usuari o grup o ID de núvol federat.",
|
||||
"Share with other people by entering a user or group or an email address." : "Compartir amb altres persones introduint un usuari o grup o una adreça d’email.",
|
||||
|
@ -413,6 +412,7 @@ OC.L10N.register(
|
|||
"You are about to grant %s access to your %s account." : "Estàs a punt d'autoritzar a %s a accedir al teu compte %s.",
|
||||
"Depending on your configuration, this button could also work to trust the domain:" : "Depenent de la teva configuració, aquest botó també podria funcionar per confiar en el domini:",
|
||||
"Copy URL" : "Copiar URL",
|
||||
"{sharee} (conversation)" : "{sharee} (conversation)",
|
||||
"Please log in before granting %s access to your %s account." : "Si us plau entrar abans de donar %s accés al teu compte %s.",
|
||||
"Further information how to configure this can be found in the %sdocumentation%s." : "Més informació de com configurar això es pot trobar a la %sdocumentation%s."
|
||||
},
|
||||
|
|
|
@ -208,12 +208,8 @@
|
|||
"No users found for {search}" : "No s'han trobat usuaris per {search}",
|
||||
"An error occurred (\"{message}\"). Please try again" : "S'ha produït un error (\"{message}\"). Si us plau, torni a intentar-ho",
|
||||
"An error occurred. Please try again" : "S'ha produït un error. Si us plau, torni a intentar-ho",
|
||||
"{sharee} (group)" : "{sharee} (grup)",
|
||||
"{sharee} (remote)" : "{sharee} (remot)",
|
||||
"{sharee} (remote group)" : "{sharee} (grup remot)",
|
||||
"{sharee} (email)" : "{sharee} (email)",
|
||||
"{sharee} ({type}, {owner})" : "{sharee} ({type}, {owner})",
|
||||
"{sharee} (conversation)" : "{sharee} (conversation)",
|
||||
"Share" : "Comparteix",
|
||||
"Name or email address..." : "Nom o adreça electrònica...",
|
||||
"Name or federated cloud ID..." : "Nom o ID de Núvol Federat…",
|
||||
|
@ -380,6 +376,9 @@
|
|||
"Error setting expiration date" : "Error en establir la data de venciment",
|
||||
"The public link will expire no later than {days} days after it is created" : "L'enllaç públic tindrà venciment abans de {days} dies després de crear-lo",
|
||||
"{{shareInitiatorDisplayName}} shared via link" : "{{shareInitiatorDisplayName}} ha compartit per enllaç",
|
||||
"{sharee} (group)" : "{sharee} (grup)",
|
||||
"{sharee} (remote)" : "{sharee} (remot)",
|
||||
"{sharee} (email)" : "{sharee} (email)",
|
||||
"Share with other people by entering a user or group, a federated cloud ID or an email address." : "Compartir amb altres persones introduint un usuari o grup, un ID de núvol federat o una adreça d’email.",
|
||||
"Share with other people by entering a user or group or a federated cloud ID." : "Compartir amb altres persones introduint un usuari o grup o ID de núvol federat.",
|
||||
"Share with other people by entering a user or group or an email address." : "Compartir amb altres persones introduint un usuari o grup o una adreça d’email.",
|
||||
|
@ -411,6 +410,7 @@
|
|||
"You are about to grant %s access to your %s account." : "Estàs a punt d'autoritzar a %s a accedir al teu compte %s.",
|
||||
"Depending on your configuration, this button could also work to trust the domain:" : "Depenent de la teva configuració, aquest botó també podria funcionar per confiar en el domini:",
|
||||
"Copy URL" : "Copiar URL",
|
||||
"{sharee} (conversation)" : "{sharee} (conversation)",
|
||||
"Please log in before granting %s access to your %s account." : "Si us plau entrar abans de donar %s accés al teu compte %s.",
|
||||
"Further information how to configure this can be found in the %sdocumentation%s." : "Més informació de com configurar això es pot trobar a la %sdocumentation%s."
|
||||
},"pluralForm" :"nplurals=2; plural=(n != 1);"
|
||||
|
|
|
@ -210,12 +210,8 @@ OC.L10N.register(
|
|||
"No users found for {search}" : "Nebyli nalezeni žádní uživatelé pro {search}",
|
||||
"An error occurred (\"{message}\"). Please try again" : "Došlo k chybě („{message}“). Zkuste to znovu",
|
||||
"An error occurred. Please try again" : "Došlo k chybě. Zkuste to znovu",
|
||||
"{sharee} (group)" : "{sharee} (skupina)",
|
||||
"{sharee} (remote)" : "{sharee} (na protějšku)",
|
||||
"{sharee} (remote group)" : "{sharee} (skupina na protějšku)",
|
||||
"{sharee} (email)" : "{sharee} (e-mail)",
|
||||
"{sharee} ({type}, {owner})" : "{sharee} ({type}, {owner})",
|
||||
"{sharee} (conversation)" : "{sharee} (konverzace)",
|
||||
"Share" : "Sdílet",
|
||||
"Name or email address..." : "Jméno nebo e-mailová adresa…",
|
||||
"Name or federated cloud ID..." : "Jméno nebo identifikátor v rámci sdruženého cloudu…",
|
||||
|
@ -356,6 +352,7 @@ OC.L10N.register(
|
|||
"For help, see the <a target=\"_blank\" rel=\"noreferrer noopener\" href=\"%s\">documentation</a>." : "Pro pomoc, nahlédněte do <a target=\"_blank\" rel=\"noreferrer noopener\" href=\"%s\">dokumentace</a>.",
|
||||
"I know that if I continue doing the update via web UI has the risk, that the request runs into a timeout and could cause data loss, but I have a backup and know how to restore my instance in case of a failure." : "Beru na vědomí, že při aktualizaci skrze webové rozhraní hrozí nebezpečí vypršení požadavku, který může vyústit ve ztrátu dat. Mám pro takový případ zálohu a vím, jak ji v případě selhání obnovit.",
|
||||
"Upgrade via web on my own risk" : "Na vlastní nebezpečí aktualizovat skrze web",
|
||||
"Maintenance mode" : "Režim údržby",
|
||||
"This %s instance is currently in maintenance mode, which may take a while." : "Tato instalace %s je právě ve stavu údržby a ta může chvíli trvat.",
|
||||
"Contact your system administrator if this message persists or appeared unexpectedly." : "Pokud se tato zpráva objevuje opakovaně nebo nečekaně, obraťte se správce systému.",
|
||||
"Updated \"%s\" to %s" : "Aktualizováno z „%s“ na %s",
|
||||
|
@ -380,6 +377,9 @@ OC.L10N.register(
|
|||
"Error setting expiration date" : "Chyba při nastavení data skončení platnosti",
|
||||
"The public link will expire no later than {days} days after it is created" : "Veřejný odkaz vyprší nejpozději {days} dní od svého vytvoření",
|
||||
"{{shareInitiatorDisplayName}} shared via link" : "{{shareInitiatorDisplayName}} sdílí pomocí odkazu",
|
||||
"{sharee} (group)" : "{sharee} (skupina)",
|
||||
"{sharee} (remote)" : "{sharee} (na protějšku)",
|
||||
"{sharee} (email)" : "{sharee} (e-mail)",
|
||||
"Share with other people by entering a user or group, a federated cloud ID or an email address." : "Sdílejte s dalšími lidmi zadáním uživatelského jména, skupiny, federovaného cloud ID, nebo e-mailové adresy.",
|
||||
"Share with other people by entering a user or group or a federated cloud ID." : "Sdílejte s dalšími lidmi zadáním uživatelského jména, skupiny, nebo sdruženého cloud ID.",
|
||||
"Share with other people by entering a user or group or an email address." : "Sdílejte s dalšími lidmi zadáním uživatelského jména, jména skupiny, nebo e-mailové adresy.",
|
||||
|
@ -411,6 +411,7 @@ OC.L10N.register(
|
|||
"You are about to grant %s access to your %s account." : "Chystáte se povolit %s přístup k vašemu %s účtu.",
|
||||
"Depending on your configuration, this button could also work to trust the domain:" : "V závislosti na vaší konfiguraci by pro označení domény za důvěryhodnou mohlo fungovat i toto tlačítko:",
|
||||
"Copy URL" : "Kopírovat URL",
|
||||
"{sharee} (conversation)" : "{sharee} (konverzace)",
|
||||
"Please log in before granting %s access to your %s account." : "Přihlaste se před udělením %s přístupu k vašemu %s účtu.",
|
||||
"Further information how to configure this can be found in the %sdocumentation%s." : "Více informací o tom, jak toto nastavit, jsou k dispozici v%sdokumentaci%s."
|
||||
},
|
||||
|
|
|
@ -208,12 +208,8 @@
|
|||
"No users found for {search}" : "Nebyli nalezeni žádní uživatelé pro {search}",
|
||||
"An error occurred (\"{message}\"). Please try again" : "Došlo k chybě („{message}“). Zkuste to znovu",
|
||||
"An error occurred. Please try again" : "Došlo k chybě. Zkuste to znovu",
|
||||
"{sharee} (group)" : "{sharee} (skupina)",
|
||||
"{sharee} (remote)" : "{sharee} (na protějšku)",
|
||||
"{sharee} (remote group)" : "{sharee} (skupina na protějšku)",
|
||||
"{sharee} (email)" : "{sharee} (e-mail)",
|
||||
"{sharee} ({type}, {owner})" : "{sharee} ({type}, {owner})",
|
||||
"{sharee} (conversation)" : "{sharee} (konverzace)",
|
||||
"Share" : "Sdílet",
|
||||
"Name or email address..." : "Jméno nebo e-mailová adresa…",
|
||||
"Name or federated cloud ID..." : "Jméno nebo identifikátor v rámci sdruženého cloudu…",
|
||||
|
@ -354,6 +350,7 @@
|
|||
"For help, see the <a target=\"_blank\" rel=\"noreferrer noopener\" href=\"%s\">documentation</a>." : "Pro pomoc, nahlédněte do <a target=\"_blank\" rel=\"noreferrer noopener\" href=\"%s\">dokumentace</a>.",
|
||||
"I know that if I continue doing the update via web UI has the risk, that the request runs into a timeout and could cause data loss, but I have a backup and know how to restore my instance in case of a failure." : "Beru na vědomí, že při aktualizaci skrze webové rozhraní hrozí nebezpečí vypršení požadavku, který může vyústit ve ztrátu dat. Mám pro takový případ zálohu a vím, jak ji v případě selhání obnovit.",
|
||||
"Upgrade via web on my own risk" : "Na vlastní nebezpečí aktualizovat skrze web",
|
||||
"Maintenance mode" : "Režim údržby",
|
||||
"This %s instance is currently in maintenance mode, which may take a while." : "Tato instalace %s je právě ve stavu údržby a ta může chvíli trvat.",
|
||||
"Contact your system administrator if this message persists or appeared unexpectedly." : "Pokud se tato zpráva objevuje opakovaně nebo nečekaně, obraťte se správce systému.",
|
||||
"Updated \"%s\" to %s" : "Aktualizováno z „%s“ na %s",
|
||||
|
@ -378,6 +375,9 @@
|
|||
"Error setting expiration date" : "Chyba při nastavení data skončení platnosti",
|
||||
"The public link will expire no later than {days} days after it is created" : "Veřejný odkaz vyprší nejpozději {days} dní od svého vytvoření",
|
||||
"{{shareInitiatorDisplayName}} shared via link" : "{{shareInitiatorDisplayName}} sdílí pomocí odkazu",
|
||||
"{sharee} (group)" : "{sharee} (skupina)",
|
||||
"{sharee} (remote)" : "{sharee} (na protějšku)",
|
||||
"{sharee} (email)" : "{sharee} (e-mail)",
|
||||
"Share with other people by entering a user or group, a federated cloud ID or an email address." : "Sdílejte s dalšími lidmi zadáním uživatelského jména, skupiny, federovaného cloud ID, nebo e-mailové adresy.",
|
||||
"Share with other people by entering a user or group or a federated cloud ID." : "Sdílejte s dalšími lidmi zadáním uživatelského jména, skupiny, nebo sdruženého cloud ID.",
|
||||
"Share with other people by entering a user or group or an email address." : "Sdílejte s dalšími lidmi zadáním uživatelského jména, jména skupiny, nebo e-mailové adresy.",
|
||||
|
@ -409,6 +409,7 @@
|
|||
"You are about to grant %s access to your %s account." : "Chystáte se povolit %s přístup k vašemu %s účtu.",
|
||||
"Depending on your configuration, this button could also work to trust the domain:" : "V závislosti na vaší konfiguraci by pro označení domény za důvěryhodnou mohlo fungovat i toto tlačítko:",
|
||||
"Copy URL" : "Kopírovat URL",
|
||||
"{sharee} (conversation)" : "{sharee} (konverzace)",
|
||||
"Please log in before granting %s access to your %s account." : "Přihlaste se před udělením %s přístupu k vašemu %s účtu.",
|
||||
"Further information how to configure this can be found in the %sdocumentation%s." : "Více informací o tom, jak toto nastavit, jsou k dispozici v%sdokumentaci%s."
|
||||
},"pluralForm" :"nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n <= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;"
|
||||
|
|
|
@ -170,9 +170,6 @@ OC.L10N.register(
|
|||
"No users found for {search}" : "Ingen brugere fundet for {search}",
|
||||
"An error occurred (\"{message}\"). Please try again" : "Der opstor den fejl (\"{message}\"). Prøv igen",
|
||||
"An error occurred. Please try again" : "Der opstor den fejl. Prøv igen",
|
||||
"{sharee} (group)" : "{sharee} (gruppe)",
|
||||
"{sharee} (remote)" : "{sharee} (ekstern)",
|
||||
"{sharee} (email)" : "{sharee} (e-mail)",
|
||||
"{sharee} ({type}, {owner})" : "{share} ({type}, {owner})",
|
||||
"Share" : "Del",
|
||||
"Name or email address..." : "Navn eller e-mail adresse...",
|
||||
|
@ -319,6 +316,9 @@ OC.L10N.register(
|
|||
"Error setting expiration date" : "Fejl under sætning af udløbsdato",
|
||||
"The public link will expire no later than {days} days after it is created" : "Det offentlige link udløber senest {days} dage efter det blev oprettet",
|
||||
"{{shareInitiatorDisplayName}} shared via link" : "{{shareInitiatorDisplayName}} delt via link",
|
||||
"{sharee} (group)" : "{sharee} (gruppe)",
|
||||
"{sharee} (remote)" : "{sharee} (ekstern)",
|
||||
"{sharee} (email)" : "{sharee} (e-mail)",
|
||||
"Share with other people by entering a user or group, a federated cloud ID or an email address." : "Del med andre ved at indtaste et brugernavn, en gruppe, et federated cloud id eller en e-mail adresse.",
|
||||
"Share with other people by entering a user or group or a federated cloud ID." : "Del med andre ved at indtaste et brugernavn, en gruppe eller et federated cloud id.",
|
||||
"Share with other people by entering a user or group or an email address." : "Del med andre ved at indtaste et brugernavn, en gruppe eller e-mail adresse.",
|
||||
|
|
|
@ -168,9 +168,6 @@
|
|||
"No users found for {search}" : "Ingen brugere fundet for {search}",
|
||||
"An error occurred (\"{message}\"). Please try again" : "Der opstor den fejl (\"{message}\"). Prøv igen",
|
||||
"An error occurred. Please try again" : "Der opstor den fejl. Prøv igen",
|
||||
"{sharee} (group)" : "{sharee} (gruppe)",
|
||||
"{sharee} (remote)" : "{sharee} (ekstern)",
|
||||
"{sharee} (email)" : "{sharee} (e-mail)",
|
||||
"{sharee} ({type}, {owner})" : "{share} ({type}, {owner})",
|
||||
"Share" : "Del",
|
||||
"Name or email address..." : "Navn eller e-mail adresse...",
|
||||
|
@ -317,6 +314,9 @@
|
|||
"Error setting expiration date" : "Fejl under sætning af udløbsdato",
|
||||
"The public link will expire no later than {days} days after it is created" : "Det offentlige link udløber senest {days} dage efter det blev oprettet",
|
||||
"{{shareInitiatorDisplayName}} shared via link" : "{{shareInitiatorDisplayName}} delt via link",
|
||||
"{sharee} (group)" : "{sharee} (gruppe)",
|
||||
"{sharee} (remote)" : "{sharee} (ekstern)",
|
||||
"{sharee} (email)" : "{sharee} (e-mail)",
|
||||
"Share with other people by entering a user or group, a federated cloud ID or an email address." : "Del med andre ved at indtaste et brugernavn, en gruppe, et federated cloud id eller en e-mail adresse.",
|
||||
"Share with other people by entering a user or group or a federated cloud ID." : "Del med andre ved at indtaste et brugernavn, en gruppe eller et federated cloud id.",
|
||||
"Share with other people by entering a user or group or an email address." : "Del med andre ved at indtaste et brugernavn, en gruppe eller e-mail adresse.",
|
||||
|
|
|
@ -167,6 +167,7 @@ OC.L10N.register(
|
|||
"Share to {name}" : "Mit {name} teilen",
|
||||
"Copy link" : "Link kopieren",
|
||||
"Link" : "Link",
|
||||
"Hide download" : "Download verbergen",
|
||||
"Password protect" : "Passwortschutz",
|
||||
"Allow editing" : "Bearbeitung erlauben",
|
||||
"Email link to person" : "Link per E-Mail verschicken",
|
||||
|
@ -210,12 +211,10 @@ OC.L10N.register(
|
|||
"No users found for {search}" : "Keine Benutzer für {search} gefunden",
|
||||
"An error occurred (\"{message}\"). Please try again" : "Benötigt keine Übersetzung. Für iOS wird nur die formelle Übersetzung verwendet (de_DE). ",
|
||||
"An error occurred. Please try again" : "Es ist ein Fehler aufgetreten. Bitte versuche es noch einmal",
|
||||
"{sharee} (group)" : "{sharee} (Gruppe)",
|
||||
"{sharee} (remote)" : "{sharee} (remote)",
|
||||
"{sharee} (remote group)" : "{sharee} (externe Gruppe)",
|
||||
"{sharee} (email)" : "{sharee} (E-Mail)",
|
||||
"{sharee} ({type}, {owner})" : "{sharee} ({type}, {owner})",
|
||||
"{sharee} (conversation)" : "{sharee} (conversation)",
|
||||
"Home" : "Start",
|
||||
"Other" : "Andere",
|
||||
"Share" : "Teilen",
|
||||
"Name or email address..." : "Name oder E-Mail-Adresse…",
|
||||
"Name or federated cloud ID..." : "Name oder Federated-Cloud-ID…",
|
||||
|
@ -382,6 +381,9 @@ OC.L10N.register(
|
|||
"Error setting expiration date" : "Fehler beim Setzen des Ablaufdatums",
|
||||
"The public link will expire no later than {days} days after it is created" : "Der öffentliche Link wird spätestens {days} Tage nach seiner Erstellung ablaufen",
|
||||
"{{shareInitiatorDisplayName}} shared via link" : "{{shareInitiatorDisplayName}} mittels Link geteilt",
|
||||
"{sharee} (group)" : "{sharee} (Gruppe)",
|
||||
"{sharee} (remote)" : "{sharee} (remote)",
|
||||
"{sharee} (email)" : "{sharee} (E-Mail)",
|
||||
"Share with other people by entering a user or group, a federated cloud ID or an email address." : "Teile mit Anderen, indem Du einen Benutzer, eine Gruppe, eine Federated-Cloud-ID oder eine E-Mail-Adressen eingibst.",
|
||||
"Share with other people by entering a user or group or a federated cloud ID." : "Teile mit Anderen, indem Du einen Benutzer, eine Gruppe, oder eine Federated-Cloud-ID eingibst.",
|
||||
"Share with other people by entering a user or group or an email address." : "Teile mit Anderen, indem Du einen Benutzer, eine Gruppe, oder eine E-Mail-Adresse eingibst.",
|
||||
|
@ -413,6 +415,7 @@ OC.L10N.register(
|
|||
"You are about to grant %s access to your %s account." : "Du bist dabei, %s Zugriff auf Dein %s-Konto zu gewähren.",
|
||||
"Depending on your configuration, this button could also work to trust the domain:" : "Abhängig von Deiner Konfiguration kann diese Schaltfläche verwandt werden, um die Domain als vertrauenswürdig einzustufen:",
|
||||
"Copy URL" : "URL kopieren",
|
||||
"{sharee} (conversation)" : "{sharee} (conversation)",
|
||||
"Please log in before granting %s access to your %s account." : "Bitte anmelden, bevor Du %s Zugriff auf Dein %s-Konto gewährst.",
|
||||
"Further information how to configure this can be found in the %sdocumentation%s." : "Weitere Informationen zur Konfiguration findest du in der %sDokumentation%s."
|
||||
},
|
||||
|
|
|
@ -165,6 +165,7 @@
|
|||
"Share to {name}" : "Mit {name} teilen",
|
||||
"Copy link" : "Link kopieren",
|
||||
"Link" : "Link",
|
||||
"Hide download" : "Download verbergen",
|
||||
"Password protect" : "Passwortschutz",
|
||||
"Allow editing" : "Bearbeitung erlauben",
|
||||
"Email link to person" : "Link per E-Mail verschicken",
|
||||
|
@ -208,12 +209,10 @@
|
|||
"No users found for {search}" : "Keine Benutzer für {search} gefunden",
|
||||
"An error occurred (\"{message}\"). Please try again" : "Benötigt keine Übersetzung. Für iOS wird nur die formelle Übersetzung verwendet (de_DE). ",
|
||||
"An error occurred. Please try again" : "Es ist ein Fehler aufgetreten. Bitte versuche es noch einmal",
|
||||
"{sharee} (group)" : "{sharee} (Gruppe)",
|
||||
"{sharee} (remote)" : "{sharee} (remote)",
|
||||
"{sharee} (remote group)" : "{sharee} (externe Gruppe)",
|
||||
"{sharee} (email)" : "{sharee} (E-Mail)",
|
||||
"{sharee} ({type}, {owner})" : "{sharee} ({type}, {owner})",
|
||||
"{sharee} (conversation)" : "{sharee} (conversation)",
|
||||
"Home" : "Start",
|
||||
"Other" : "Andere",
|
||||
"Share" : "Teilen",
|
||||
"Name or email address..." : "Name oder E-Mail-Adresse…",
|
||||
"Name or federated cloud ID..." : "Name oder Federated-Cloud-ID…",
|
||||
|
@ -380,6 +379,9 @@
|
|||
"Error setting expiration date" : "Fehler beim Setzen des Ablaufdatums",
|
||||
"The public link will expire no later than {days} days after it is created" : "Der öffentliche Link wird spätestens {days} Tage nach seiner Erstellung ablaufen",
|
||||
"{{shareInitiatorDisplayName}} shared via link" : "{{shareInitiatorDisplayName}} mittels Link geteilt",
|
||||
"{sharee} (group)" : "{sharee} (Gruppe)",
|
||||
"{sharee} (remote)" : "{sharee} (remote)",
|
||||
"{sharee} (email)" : "{sharee} (E-Mail)",
|
||||
"Share with other people by entering a user or group, a federated cloud ID or an email address." : "Teile mit Anderen, indem Du einen Benutzer, eine Gruppe, eine Federated-Cloud-ID oder eine E-Mail-Adressen eingibst.",
|
||||
"Share with other people by entering a user or group or a federated cloud ID." : "Teile mit Anderen, indem Du einen Benutzer, eine Gruppe, oder eine Federated-Cloud-ID eingibst.",
|
||||
"Share with other people by entering a user or group or an email address." : "Teile mit Anderen, indem Du einen Benutzer, eine Gruppe, oder eine E-Mail-Adresse eingibst.",
|
||||
|
@ -411,6 +413,7 @@
|
|||
"You are about to grant %s access to your %s account." : "Du bist dabei, %s Zugriff auf Dein %s-Konto zu gewähren.",
|
||||
"Depending on your configuration, this button could also work to trust the domain:" : "Abhängig von Deiner Konfiguration kann diese Schaltfläche verwandt werden, um die Domain als vertrauenswürdig einzustufen:",
|
||||
"Copy URL" : "URL kopieren",
|
||||
"{sharee} (conversation)" : "{sharee} (conversation)",
|
||||
"Please log in before granting %s access to your %s account." : "Bitte anmelden, bevor Du %s Zugriff auf Dein %s-Konto gewährst.",
|
||||
"Further information how to configure this can be found in the %sdocumentation%s." : "Weitere Informationen zur Konfiguration findest du in der %sDokumentation%s."
|
||||
},"pluralForm" :"nplurals=2; plural=(n != 1);"
|
||||
|
|
|
@ -167,6 +167,7 @@ OC.L10N.register(
|
|||
"Share to {name}" : "Mit {name} teilen",
|
||||
"Copy link" : "Link kopieren",
|
||||
"Link" : "Link",
|
||||
"Hide download" : "Download verbergen",
|
||||
"Password protect" : "Passwortschutz",
|
||||
"Allow editing" : "Bearbeitung erlauben",
|
||||
"Email link to person" : "Link per E-Mail verschicken",
|
||||
|
@ -210,12 +211,10 @@ OC.L10N.register(
|
|||
"No users found for {search}" : "Keine Benutzer für {search} gefunden",
|
||||
"An error occurred (\"{message}\"). Please try again" : "Es ist ein Fehler aufgetreten (\"{message}\"). Bitte erneut versuchen.",
|
||||
"An error occurred. Please try again" : "Es ist ein Fehler aufgetreten. Bitte versuchen Sie es noch einmal",
|
||||
"{sharee} (group)" : "{sharee} (Gruppe)",
|
||||
"{sharee} (remote)" : "{sharee} (remote)",
|
||||
"{sharee} (remote group)" : "{sharee} (Externe Gruppe)",
|
||||
"{sharee} (email)" : "{sharee} (E-Mail)",
|
||||
"{sharee} ({type}, {owner})" : "{sharee} ({type}, {owner})",
|
||||
"{sharee} (conversation)" : "{sharee} (conversation)",
|
||||
"Home" : "Start",
|
||||
"Other" : "Andere",
|
||||
"Share" : "Teilen",
|
||||
"Name or email address..." : "Name oder E-Mail-Adresse…",
|
||||
"Name or federated cloud ID..." : "Name oder Federated-Cloud-ID…",
|
||||
|
@ -382,6 +381,9 @@ OC.L10N.register(
|
|||
"Error setting expiration date" : "Fehler beim Setzen des Ablaufdatums",
|
||||
"The public link will expire no later than {days} days after it is created" : "Der öffentliche Link wird spätestens {days} Tage nach seiner Erstellung ablaufen",
|
||||
"{{shareInitiatorDisplayName}} shared via link" : "{{shareInitiatorDisplayName}} mittels Link geteilt",
|
||||
"{sharee} (group)" : "{sharee} (Gruppe)",
|
||||
"{sharee} (remote)" : "{sharee} (remote)",
|
||||
"{sharee} (email)" : "{sharee} (E-Mail)",
|
||||
"Share with other people by entering a user or group, a federated cloud ID or an email address." : "Teilen mit Anderen, indem Sie einen Benutzer, eine Gruppe, eine Federated-Cloud-ID oder eine E-Mail-Adresse eingeben.",
|
||||
"Share with other people by entering a user or group or a federated cloud ID." : "Teilen mit Anderen, indem Sie einen Benutzer, eine Gruppe, oder eine Federated-Cloud-ID eingeben.",
|
||||
"Share with other people by entering a user or group or an email address." : "Teilen mit Anderen, indem Sie einen Benutzer, eine Gruppe, oder eine E-Mail-Adresse eingeben.",
|
||||
|
@ -413,6 +415,7 @@ OC.L10N.register(
|
|||
"You are about to grant %s access to your %s account." : "Sie sind dabei, %s Zugriff auf Ihr %s-Konto zu gewähren.",
|
||||
"Depending on your configuration, this button could also work to trust the domain:" : "Abhängig von Ihrer Konfiguration kann diese Schaltfläche verwandt werden, um die Domain als vertrauenswürdig einzustufen:",
|
||||
"Copy URL" : "URL kopieren",
|
||||
"{sharee} (conversation)" : "{sharee} (conversation)",
|
||||
"Please log in before granting %s access to your %s account." : "Bitte anmelden, bevor Du %s Zugriff auf Dein %s-Konto gewährst.",
|
||||
"Further information how to configure this can be found in the %sdocumentation%s." : "Weitere Informationen zur Konfiguration finden Sie in der %sDokumentation%s."
|
||||
},
|
||||
|
|
|
@ -165,6 +165,7 @@
|
|||
"Share to {name}" : "Mit {name} teilen",
|
||||
"Copy link" : "Link kopieren",
|
||||
"Link" : "Link",
|
||||
"Hide download" : "Download verbergen",
|
||||
"Password protect" : "Passwortschutz",
|
||||
"Allow editing" : "Bearbeitung erlauben",
|
||||
"Email link to person" : "Link per E-Mail verschicken",
|
||||
|
@ -208,12 +209,10 @@
|
|||
"No users found for {search}" : "Keine Benutzer für {search} gefunden",
|
||||
"An error occurred (\"{message}\"). Please try again" : "Es ist ein Fehler aufgetreten (\"{message}\"). Bitte erneut versuchen.",
|
||||
"An error occurred. Please try again" : "Es ist ein Fehler aufgetreten. Bitte versuchen Sie es noch einmal",
|
||||
"{sharee} (group)" : "{sharee} (Gruppe)",
|
||||
"{sharee} (remote)" : "{sharee} (remote)",
|
||||
"{sharee} (remote group)" : "{sharee} (Externe Gruppe)",
|
||||
"{sharee} (email)" : "{sharee} (E-Mail)",
|
||||
"{sharee} ({type}, {owner})" : "{sharee} ({type}, {owner})",
|
||||
"{sharee} (conversation)" : "{sharee} (conversation)",
|
||||
"Home" : "Start",
|
||||
"Other" : "Andere",
|
||||
"Share" : "Teilen",
|
||||
"Name or email address..." : "Name oder E-Mail-Adresse…",
|
||||
"Name or federated cloud ID..." : "Name oder Federated-Cloud-ID…",
|
||||
|
@ -380,6 +379,9 @@
|
|||
"Error setting expiration date" : "Fehler beim Setzen des Ablaufdatums",
|
||||
"The public link will expire no later than {days} days after it is created" : "Der öffentliche Link wird spätestens {days} Tage nach seiner Erstellung ablaufen",
|
||||
"{{shareInitiatorDisplayName}} shared via link" : "{{shareInitiatorDisplayName}} mittels Link geteilt",
|
||||
"{sharee} (group)" : "{sharee} (Gruppe)",
|
||||
"{sharee} (remote)" : "{sharee} (remote)",
|
||||
"{sharee} (email)" : "{sharee} (E-Mail)",
|
||||
"Share with other people by entering a user or group, a federated cloud ID or an email address." : "Teilen mit Anderen, indem Sie einen Benutzer, eine Gruppe, eine Federated-Cloud-ID oder eine E-Mail-Adresse eingeben.",
|
||||
"Share with other people by entering a user or group or a federated cloud ID." : "Teilen mit Anderen, indem Sie einen Benutzer, eine Gruppe, oder eine Federated-Cloud-ID eingeben.",
|
||||
"Share with other people by entering a user or group or an email address." : "Teilen mit Anderen, indem Sie einen Benutzer, eine Gruppe, oder eine E-Mail-Adresse eingeben.",
|
||||
|
@ -411,6 +413,7 @@
|
|||
"You are about to grant %s access to your %s account." : "Sie sind dabei, %s Zugriff auf Ihr %s-Konto zu gewähren.",
|
||||
"Depending on your configuration, this button could also work to trust the domain:" : "Abhängig von Ihrer Konfiguration kann diese Schaltfläche verwandt werden, um die Domain als vertrauenswürdig einzustufen:",
|
||||
"Copy URL" : "URL kopieren",
|
||||
"{sharee} (conversation)" : "{sharee} (conversation)",
|
||||
"Please log in before granting %s access to your %s account." : "Bitte anmelden, bevor Du %s Zugriff auf Dein %s-Konto gewährst.",
|
||||
"Further information how to configure this can be found in the %sdocumentation%s." : "Weitere Informationen zur Konfiguration finden Sie in der %sDokumentation%s."
|
||||
},"pluralForm" :"nplurals=2; plural=(n != 1);"
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue