diff --git a/.gitignore b/.gitignore index e61ec6f035930b0cf4da454aab61374f46b82c1c..b24edc912828e8f0f8653c3207bca152f6c0737e 100644 --- a/.gitignore +++ b/.gitignore @@ -86,6 +86,11 @@ nbproject # Node Modules /build/node_modules/ +# nodejs +/build/lib/ +/npm-debug.log + + # Tests - auto-generated files /data-autotest /tests/coverage* diff --git a/.gitmodules b/.gitmodules index b9c1a3702cfb2f5dbc88f54aca9a46a246c97550..bc2beee81adcc479e0b251f09063dcffb08f8d81 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "3rdparty"] path = 3rdparty - url = git://github.com/owncloud/3rdparty.git + url = https://github.com/owncloud/3rdparty.git diff --git a/apps/files/ajax/download.php b/apps/files/ajax/download.php index 1f7e42e0d3e3b282487bd2e811e032ef93b95788..4b4a7f8948d4d2bbd1a2a19fec65484b79fa1285 100644 --- a/apps/files/ajax/download.php +++ b/apps/files/ajax/download.php @@ -21,12 +21,6 @@ * */ -// only need filesystem apps -$RUNTIME_APPTYPES=array('filesystem'); - -// Init owncloud - - // Check if we are a user OCP\User::checkLoggedIn(); \OC::$session->close(); diff --git a/apps/files/ajax/getstoragestats.php b/apps/files/ajax/getstoragestats.php index 17415b6933f09d4b1ef35241bada4d35db3ec946..dd8af39bada3ec55abb2c0fde56b000463823faa 100644 --- a/apps/files/ajax/getstoragestats.php +++ b/apps/files/ajax/getstoragestats.php @@ -1,8 +1,5 @@ <?php -// only need filesystem apps -$RUNTIME_APPTYPES = array('filesystem'); - $dir = '/'; if (isset($_GET['dir'])) { diff --git a/apps/files/ajax/list.php b/apps/files/ajax/list.php index 667209599a012a45a9c72c2a352ae42a40fa3d0f..3bb35579d5fd177f5e5e86818428347735af823a 100644 --- a/apps/files/ajax/list.php +++ b/apps/files/ajax/list.php @@ -1,11 +1,5 @@ <?php -// only need filesystem apps -$RUNTIME_APPTYPES=array('filesystem'); - -// Init owncloud - - OCP\JSON::checkLoggedIn(); \OC::$session->close(); diff --git a/apps/files/ajax/rawlist.php b/apps/files/ajax/rawlist.php index 6c2569e2ebbc903d86d196ce55561724e3ea407a..f18bbffb74af980339038515439244d787ec0634 100644 --- a/apps/files/ajax/rawlist.php +++ b/apps/files/ajax/rawlist.php @@ -1,8 +1,5 @@ <?php -// only need filesystem apps -$RUNTIME_APPTYPES = array('filesystem'); - OCP\JSON::checkLoggedIn(); \OC::$session->close(); diff --git a/apps/files/appinfo/remote.php b/apps/files/appinfo/remote.php index 826f72fb0e61f3b75fd73973823e659bb22fd661..a8acfdb6e6e3c52ced7bf1c8603d97844c8905eb 100644 --- a/apps/files/appinfo/remote.php +++ b/apps/files/appinfo/remote.php @@ -22,12 +22,6 @@ * License along with this library. If not, see <http://www.gnu.org/licenses/>. * */ -// load needed apps -$RUNTIME_APPTYPES = array('filesystem', 'authentication', 'logging'); - -OC_App::loadApps($RUNTIME_APPTYPES); - -OC_Util::obEnd(); // Backends $authBackend = new OC_Connector_Sabre_Auth(); diff --git a/apps/files/command/scan.php b/apps/files/command/scan.php index f334f29a939e3002600212d079b22b45c6087c5f..25ab70af3629a7d50940c589f1b6cd41c52dd62b 100644 --- a/apps/files/command/scan.php +++ b/apps/files/command/scan.php @@ -58,7 +58,6 @@ class Scan extends Command { protected function execute(InputInterface $input, OutputInterface $output) { if ($input->getOption('all')) { - \OC_App::loadApps('authentication'); $users = $this->userManager->search(''); } else { $users = $input->getArgument('user_id'); diff --git a/apps/files/css/files.css b/apps/files/css/files.css index af863aca33ebe14aac52dbfaa0ddb100c2334222..1bac5d2b7db26bdb2857d5d7c794debed1a67489 100644 --- a/apps/files/css/files.css +++ b/apps/files/css/files.css @@ -77,10 +77,10 @@ } /* make sure there's enough room for the file actions */ #body-user #filestable { - min-width: 750px; + min-width: 688px; /* 768 (mobile break) - 80 (nav width) */ } #body-user #controls { - min-width: 600px; + min-width: 688px; /* 768 (mobile break) - 80 (nav width) */ } #filestable tbody tr { background-color:#fff; height:40px; } diff --git a/apps/files/css/mobile.css b/apps/files/css/mobile.css new file mode 100644 index 0000000000000000000000000000000000000000..3ad7d63483869f99358a24f0c84afd611bb54354 --- /dev/null +++ b/apps/files/css/mobile.css @@ -0,0 +1,68 @@ +@media only screen and (max-width: 768px) { + +/* don’t require a minimum width for files table */ +#body-user #filestable { + min-width: initial !important; +} + +/* do not show Deleted Files on mobile, not optimized yet and button too long */ +#controls #trash { + display: none; +} + +/* hide size and date columns */ +table th#headerSize, +table td.filesize, +table th#headerDate, +table td.date { + display: none; +} + +/* remove shift for multiselect bar to account for missing navigation */ +table.multiselect thead { + padding-left: 0; +} + +/* restrict length of displayed filename to prevent overflow */ +table td.filename .nametext { + max-width: 75% !important; +} + +/* always show actions on mobile, not only on hover */ +#fileList a.action { + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=20)" !important; + filter: alpha(opacity=20) !important; + opacity: .2 !important; + display: inline !important; +} +/* do not show Rename or Versions on mobile */ +#fileList .action.action-rename, +#fileList .action.action-versions { + display: none !important; +} +/* some padding for better clickability */ +#fileList a.action img { + padding: 0 6px 0 12px; +} +/* hide text of the actions on mobile */ +#fileList a.action span { + display: none; +} + +/* ellipsis on file names */ +.nametext { + width: 60%; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +/* proper notification area for multi line messages */ +#notification-container { + display: -webkit-box; + display: -moz-box; + display: -ms-flexbox; + display: -webkit-flex; + display: flex; +} +} diff --git a/apps/files/index.php b/apps/files/index.php index c66cd40fb56150a981079c7536f413cb40617ceb..73601d26217b44fb00a4a7ef9be9a3b245bf4457 100644 --- a/apps/files/index.php +++ b/apps/files/index.php @@ -27,6 +27,7 @@ OCP\User::checkLoggedIn(); // Load the files we need OCP\Util::addStyle('files', 'files'); OCP\Util::addStyle('files', 'upload'); +OCP\Util::addStyle('files', 'mobile'); OCP\Util::addscript('files', 'file-upload'); OCP\Util::addscript('files', 'jquery.iframe-transport'); OCP\Util::addscript('files', 'jquery.fileupload'); diff --git a/apps/files/js/fileactions.js b/apps/files/js/fileactions.js index 9a69d7b368869138bf05948894de9e0f28dbdad7..a7d1fa9d8a2785b266fba23be6f474c31e08c7a3 100644 --- a/apps/files/js/fileactions.js +++ b/apps/files/js/fileactions.js @@ -15,21 +15,33 @@ var FileActions = { defaults: {}, icons: {}, currentFile: null, - register: function (mime, name, permissions, icon, action) { + register: function (mime, name, permissions, icon, action, displayName) { if (!FileActions.actions[mime]) { FileActions.actions[mime] = {}; } if (!FileActions.actions[mime][name]) { FileActions.actions[mime][name] = {}; } + if (!displayName) { + displayName = t('files', name); + } FileActions.actions[mime][name]['action'] = action; FileActions.actions[mime][name]['permissions'] = permissions; + FileActions.actions[mime][name]['displayName'] = displayName; FileActions.icons[name] = icon; }, setDefault: function (mime, name) { FileActions.defaults[mime] = name; }, get: function (mime, type, permissions) { + var actions = this.getActions(mime, type, permissions); + var filteredActions = {}; + $.each(actions, function (name, action) { + filteredActions[name] = action.action; + }); + return filteredActions; + }, + getActions: function (mime, type, permissions) { var actions = {}; if (FileActions.actions.all) { actions = $.extend(actions, FileActions.actions.all); @@ -51,7 +63,7 @@ var FileActions = { var filteredActions = {}; $.each(actions, function (name, action) { if (action.permissions & permissions) { - filteredActions[name] = action.action; + filteredActions[name] = action; } }); return filteredActions; @@ -82,7 +94,7 @@ var FileActions = { */ display: function (parent, triggerEvent) { FileActions.currentFile = parent; - var actions = FileActions.get(FileActions.getCurrentMimeType(), FileActions.getCurrentType(), FileActions.getCurrentPermissions()); + var actions = FileActions.getActions(FileActions.getCurrentMimeType(), FileActions.getCurrentType(), FileActions.getCurrentPermissions()); var file = FileActions.getCurrentFile(); var nameLinks; if (FileList.findFileEl(file).data('renaming')) { @@ -105,15 +117,16 @@ var FileActions = { event.data.actionFunc(file); }; - var addAction = function (name, action) { + var addAction = function (name, action, displayName) { // NOTE: Temporary fix to prevent rename action in root of Shared directory if (name === 'Rename' && $('#dir').val() === '/Shared') { return true; } if ((name === 'Download' || action !== defaultAction) && name !== 'Delete') { + var img = FileActions.icons[name], - actionText = t('files', name), + actionText = displayName, actionContainer = 'a.name>span.fileactions'; if (name === 'Rename') { @@ -125,7 +138,7 @@ var FileActions = { if (img.call) { img = img(file); } - var html = '<a href="#" class="action" data-action="' + name + '">'; + var html = '<a href="#" class="action action-' + name.toLowerCase() + '" data-action="' + name + '">'; if (img) { html += '<img class ="svg" src="' + img + '" />'; } @@ -133,8 +146,7 @@ var FileActions = { var element = $(html); element.data('action', name); - //alert(element); - element.on('click', {a: null, elem: parent, actionFunc: actions[name]}, actionHandler); + element.on('click', {a: null, elem: parent, actionFunc: actions[name].action}, actionHandler); parent.find(actionContainer).append(element); } @@ -142,12 +154,15 @@ var FileActions = { $.each(actions, function (name, action) { if (name !== 'Share') { - addAction(name, action); + displayName = action.displayName; + ah = action.action; + + addAction(name, ah, displayName); } }); if(actions.Share && !($('#dir').val() === '/' && file === 'Shared')){ - // t('files', 'Share') - addAction('Share', actions.Share); + displayName = t('files', 'Share'); + addAction('Share', actions.Share, displayName); } // remove the existing delete action diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 503bf6811394453e9e0291338a1da1cae7ccc4f8..cda4e823a73b9eb4acfc4610f92a5db8ed40667f 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -71,7 +71,7 @@ window.FileList={ }); // filename td td = $('<td></td>').attr({ - "class": "filename", + "class": "filename svg", "style": 'background-image:url('+iconurl+'); background-size: 32px;' }); var rand = Math.random().toString(16).slice(2); diff --git a/apps/files/js/files.js b/apps/files/js/files.js index 1186a72a44f4cab38544409f4b1c9b8998954e3d..1137364db4a8c2b91a3839d9989eb7ab8cffed68 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -196,11 +196,14 @@ var Files = { if (width !== Files.lastWidth) { if ((width < Files.lastWidth || firstRun) && width < Files.breadcrumbsWidth) { if (Files.hiddenBreadcrumbs === 0) { - Files.breadcrumbsWidth -= $(Files.breadcrumbs[1]).get(0).offsetWidth; - $(Files.breadcrumbs[1]).find('a').hide(); - $(Files.breadcrumbs[1]).append('<span>...</span>'); - Files.breadcrumbsWidth += $(Files.breadcrumbs[1]).get(0).offsetWidth; - Files.hiddenBreadcrumbs = 2; + bc = $(Files.breadcrumbs[1]).get(0); + if (typeof bc != 'undefined') { + Files.breadcrumbsWidth -= bc.offsetWidth; + $(Files.breadcrumbs[1]).find('a').hide(); + $(Files.breadcrumbs[1]).append('<span>...</span>'); + Files.breadcrumbsWidth += bc.offsetWidth; + Files.hiddenBreadcrumbs = 2; + } } var i = Files.hiddenBreadcrumbs; while (width < Files.breadcrumbsWidth && i > 1 && i < Files.breadcrumbs.length - 1) { diff --git a/apps/files/l10n/ru.php b/apps/files/l10n/ru.php index 2c0335f3cc302ad26cd1930cf0ae5ba2c45c3b4f..ac958e5dfd39cd8a8b322f692b338f4e594d5811 100644 --- a/apps/files/l10n/ru.php +++ b/apps/files/l10n/ru.php @@ -3,7 +3,9 @@ $TRANSLATIONS = array( "Could not move %s - File with this name already exists" => "Ðевозможно перемеÑтить %s - файл Ñ Ñ‚Ð°ÐºÐ¸Ð¼ именем уже ÑущеÑтвует", "Could not move %s" => "Ðевозможно перемеÑтить %s", "File name cannot be empty." => "Ð˜Ð¼Ñ Ñ„Ð°Ð¹Ð»Ð° не может быть пуÑтым.", +"\"%s\" is an invalid file name." => "\"%s\" Ñто не правильное Ð¸Ð¼Ñ Ñ„Ð°Ð¹Ð»Ð°.", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Ðеправильное имÑ: Ñимволы '\\', '/', '<', '>', ':', '\"', '|', '?' и '*' недопуÑтимы.", +"The target folder has been moved or deleted." => "Целевой каталог был перемещен или удален.", "The name %s is already used in the folder %s. Please choose a different name." => "Ð˜Ð¼Ñ %s уже иÑпользуетÑÑ Ð´Ð»Ñ ÐºÐ°Ñ‚Ð°Ð»Ð¾Ð³Ð° %s. ПожалуйÑта, выберите другое имÑ.", "Not a valid source" => "Ðеправильный иÑточник", "Server is not allowed to open URLs, please check the server configuration" => "Сервер не позволÑет открывать URL-адреÑа, пожалуйÑта, проверьте наÑтройки Ñервера", @@ -27,6 +29,8 @@ $TRANSLATIONS = array( "Invalid directory." => "Ðеверный каталог.", "Files" => "Файлы", "Unable to upload {filename} as it is a directory or has 0 bytes" => "Ðевозможно загрузить {filename}, так как Ñто либо каталог, либо файл нулевого размера", +"Total file size {size1} exceeds upload limit {size2}" => "Полный размер файла {size1} превышает лимит по загрузке {size2}", +"Not enough free space, you are uploading {size1} but only {size2} is left" => "Ðе доÑтаточно Ñвободного меÑта, Ð’Ñ‹ загружаете {size1} но оÑталоÑÑŒ только {size2}", "Upload cancelled." => "Загрузка отменена.", "Could not get result from server." => "Ðе удалоÑÑŒ получить ответ от Ñервера.", "File upload is in progress. Leaving the page now will cancel the upload." => "Идёт загрузка файла. Покинув Ñтраницу, вы прервёте загрузку.", @@ -48,6 +52,7 @@ $TRANSLATIONS = array( "_%n file_::_%n files_" => array("%n файл","%n файла","%n файлов"), "{dirs} and {files}" => "{dirs} и {files}", "_Uploading %n file_::_Uploading %n files_" => array("Закачка %n файла","Закачка %n файлов","Закачка %n файлов"), +"\"{name}\" is an invalid file name." => "\"{name}\" Ñто не правильное Ð¸Ð¼Ñ Ñ„Ð°Ð¹Ð»Ð°.", "Your storage is full, files can not be updated or synced anymore!" => "Ваше хранилище заполнено, произведите очиÑтку перед загрузкой новых файлов.", "Your storage is almost full ({usedSpacePercent}%)" => "Ваше хранилище почти заполнено ({usedSpacePercent}%)", "Encryption App is enabled but your keys are not initialized, please log-out and log-in again" => "Приложение Ð´Ð»Ñ ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð°ÐºÑ‚Ð¸Ð²Ð½Ð¾, но ваши ключи не инициализированы, пожалуйÑта, перелогиньтеÑÑŒ", diff --git a/apps/files/templates/index.php b/apps/files/templates/index.php index 5b0bad7f3418fabff253ac34a812dcc2d5eb2b43..34acd9c4f51260745a43324c6293380501c560b1 100644 --- a/apps/files/templates/index.php +++ b/apps/files/templates/index.php @@ -5,15 +5,15 @@ <div id="new" class="button"> <a><?php p($l->t('New'));?></a> <ul> - <li class="icon-filetype-text" + <li class="icon-filetype-text svg" data-type="file" data-newname="<?php p($l->t('New text file')) ?>.txt"> <p><?php p($l->t('Text file'));?></p> </li> - <li class="icon-filetype-folder" + <li class="icon-filetype-folder svg" data-type="folder" data-newname="<?php p($l->t('New folder')) ?>"> <p><?php p($l->t('Folder'));?></p> </li> - <li class="icon-link" data-type="web"> + <li class="icon-link svg" data-type="web"> <p><?php p($l->t('From link'));?></p> </li> </ul> diff --git a/apps/files/templates/part.breadcrumb.php b/apps/files/templates/part.breadcrumb.php index 2a0df6227679307c6840d5cb5fcf839c422c0d24..69b4cbca10d559d443091368111a746977ccbefb 100644 --- a/apps/files/templates/part.breadcrumb.php +++ b/apps/files/templates/part.breadcrumb.php @@ -1,4 +1,4 @@ -<div class="crumb <?php if(!count($_["breadcrumb"])) p('last');?>" data-dir=''> +<div class="crumb svg <?php if(!count($_["breadcrumb"])) p('last');?>" data-dir=''> <a href="<?php print_unescaped($_['baseURL']); ?>"> <?php if(isset($_['rootBreadCrumb'])): echo $_['rootBreadCrumb']; diff --git a/apps/files_encryption/l10n/ru.php b/apps/files_encryption/l10n/ru.php index bce245ce680296ccee29f14a91401a5ec530d5e9..ba98486893200cf01f125805184b6ecf976fa17a 100644 --- a/apps/files_encryption/l10n/ru.php +++ b/apps/files_encryption/l10n/ru.php @@ -16,6 +16,7 @@ $TRANSLATIONS = array( "Please make sure that PHP 5.3.3 or newer is installed and that OpenSSL together with the PHP extension is enabled and configured properly. For now, the encryption app has been disabled." => "ПожалуйÑта, убедитеÑÑŒ, что верÑÐ¸Ñ PHP 5.3.3 или новее, а также, что OpenSSL и ÑоответÑтвующее раÑширение PHP включены и правильно наÑтроены. Ðа данный момент приложение ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¾Ñ‚ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¾.", "Following users are not set up for encryption:" => "Ð”Ð»Ñ Ñледующих пользователей шифрование не наÑтроено:", "Initial encryption started... This can take some time. Please wait." => "Ðачато начальное шифрование... Ðто может занÑÑ‚ÑŒ какое-то времÑ. ПожалуйÑта, подождите.", +"Initial encryption running... Please try again later." => "Работает первоначальное шифрование... ПожалуйÑта, повторите попытку позже.", "Go directly to your " => "Перейти прÑмо в", "personal settings" => "перÑональные наÑтройки", "Encryption" => "Шифрование", diff --git a/apps/files_encryption/lib/proxy.php b/apps/files_encryption/lib/proxy.php index bae1fded53d3376b7b076ab92535548d27a7aaa1..6549273c8f1e5539554548560f5d4b3c8429654f 100644 --- a/apps/files_encryption/lib/proxy.php +++ b/apps/files_encryption/lib/proxy.php @@ -128,6 +128,8 @@ class Proxy extends \OC_FileProxy { // re-enable proxy - our work is done \OC_FileProxy::$enabled = $proxyStatus; + } else { + return false; } } } diff --git a/apps/files_external/js/settings.js b/apps/files_external/js/settings.js index cd2a3103eb72365656fddc715ecf7edc2883e73f..00793a614c20f5e3be668404cb84b003350e9ebe 100644 --- a/apps/files_external/js/settings.js +++ b/apps/files_external/js/settings.js @@ -181,16 +181,21 @@ $(document).ready(function() { $.each(configurations, function(backend, parameters) { if (backend == backendClass) { $.each(parameters['configuration'], function(parameter, placeholder) { - if (placeholder.indexOf('*') != -1) { - td.append('<input type="password" data-parameter="'+parameter+'" placeholder="'+placeholder.substring(1)+'" />'); - } else if (placeholder.indexOf('!') != -1) { + var is_optional = false; + if (placeholder.indexOf('&') === 0) { + is_optional = true; + placeholder = placeholder.substring(1); + } + if (placeholder.indexOf('*') === 0) { + var class_string = is_optional ? ' class="optional"' : ''; + td.append('<input type="password"' + class_string + ' data-parameter="'+parameter+'" placeholder="'+placeholder.substring(1)+'" />'); + } else if (placeholder.indexOf('!') === 0) { td.append('<label><input type="checkbox" data-parameter="'+parameter+'" />'+placeholder.substring(1)+'</label>'); - } else if (placeholder.indexOf('&') != -1) { - td.append('<input type="text" class="optional" data-parameter="'+parameter+'" placeholder="'+placeholder.substring(1)+'" />'); - } else if (placeholder.indexOf('#') != -1) { + } else if (placeholder.indexOf('#') === 0) { td.append('<input type="hidden" data-parameter="'+parameter+'" />'); } else { - td.append('<input type="text" data-parameter="'+parameter+'" placeholder="'+placeholder+'" />'); + var class_string = is_optional ? ' class="optional"' : ''; + td.append('<input type="text"' + class_string + ' data-parameter="'+parameter+'" placeholder="'+placeholder+'" />'); } }); if (parameters['custom'] && $('#externalStorage tbody tr.'+backendClass.replace(/\\/g, '\\\\')).length == 1) { diff --git a/apps/files_external/l10n/ru.php b/apps/files_external/l10n/ru.php index 66d6f9fa6b8d44dff6bcd23be701564025b56b39..8ed437839cd4452c4f076bbd853b818984481a88 100644 --- a/apps/files_external/l10n/ru.php +++ b/apps/files_external/l10n/ru.php @@ -5,6 +5,7 @@ $TRANSLATIONS = array( "Grant access" => "ПредоÑтавление доÑтупа", "Please provide a valid Dropbox app key and secret." => "ПожалуйÑта, предоÑтавьте дейÑтвующий ключ Dropbox и пароль.", "Error configuring Google Drive storage" => "Ошибка при наÑтройке хранилища Google Drive", +"Saved" => "Сохранено", "<b>Warning:</b> \"smbclient\" is not installed. Mounting of CIFS/SMB shares is not possible. Please ask your system administrator to install it." => "<b>Внимание:</b> \"smbclient\" не уÑтановлен. Подключение по CIFS/SMB невозможно. ПожалуйÑта, обратитеÑÑŒ к ÑиÑтемному админиÑтратору, чтобы уÑтановить его.", "<b>Warning:</b> The FTP support in PHP is not enabled or installed. Mounting of FTP shares is not possible. Please ask your system administrator to install it." => "<b>Внимание:</b> Поддержка FTP не включена в PHP. Подключение по FTP невозможно. ПожалуйÑта, обратитеÑÑŒ к ÑиÑтемному админиÑтратору, чтобы включить.", "<b>Warning:</b> The Curl support in PHP is not enabled or installed. Mounting of ownCloud / WebDAV or GoogleDrive is not possible. Please ask your system administrator to install it." => "<b>Внимание:</b> Поддержка Curl в PHP не включена или не уÑтановлена. Подключение ownCloud / WebDAV или GoogleDrive невозможно. ПопроÑите вашего ÑиÑтемного админиÑтратора уÑтановить его.", @@ -21,6 +22,7 @@ $TRANSLATIONS = array( "Users" => "Пользователи", "Delete" => "Удалить", "Enable User External Storage" => "Включить пользовательÑкие внешние ноÑители", +"Allow users to mount the following external storage" => "Разрешить пользователÑм монтировать Ñледующую внешнюю ÑиÑтему Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð´Ð°Ð½Ð½Ñ‹Ñ…", "SSL root certificates" => "Корневые Ñертификаты SSL", "Import Root Certificate" => "Импортировать корневые Ñертификаты" ); diff --git a/apps/files_external/lib/swift.php b/apps/files_external/lib/swift.php index a6955d400f46fc7db0ba405bee0761d98a28b495..1337d9f581d30afa9bb5844c4749bbfbae319ba2 100644 --- a/apps/files_external/lib/swift.php +++ b/apps/files_external/lib/swift.php @@ -374,7 +374,7 @@ class Swift extends \OC\Files\Storage\Common { 'X-Object-Meta-Timestamp' => $mtime ) ); - return $object->Update($settings); + return $object->UpdateMetadata($settings); } else { $object = $this->container->DataObject(); if (is_null($mtime)) { diff --git a/apps/files_external/templates/settings.php b/apps/files_external/templates/settings.php index 8b01b7677e5c0b78affd2f42f9d87563c30e409e..a2bdbcf4632bcd461c00560b6e19e1c195962c64 100644 --- a/apps/files_external/templates/settings.php +++ b/apps/files_external/templates/settings.php @@ -43,29 +43,32 @@ <?php if (isset($mount['options'])): ?> <?php foreach ($mount['options'] as $parameter => $value): ?> <?php if (isset($_['backends'][$mount['class']]['configuration'][$parameter])): ?> - <?php $placeholder = $_['backends'][$mount['class']]['configuration'][$parameter]; ?> - <?php if (strpos($placeholder, '*') !== false): ?> + <?php + $placeholder = $_['backends'][$mount['class']]['configuration'][$parameter]; + $is_optional = FALSE; + if (strpos($placeholder, '&') === 0) { + $is_optional = TRUE; + $placeholder = substr($placeholder, 1); + } + ?> + <?php if (strpos($placeholder, '*') === 0): ?> <input type="password" + <?php if ($is_optional): ?> class="optional"<?php endif; ?> data-parameter="<?php p($parameter); ?>" value="<?php p($value); ?>" placeholder="<?php p(substr($placeholder, 1)); ?>" /> - <?php elseif (strpos($placeholder, '!') !== false): ?> + <?php elseif (strpos($placeholder, '!') === 0): ?> <label><input type="checkbox" data-parameter="<?php p($parameter); ?>" <?php if ($value == 'true'): ?> checked="checked"<?php endif; ?> /><?php p(substr($placeholder, 1)); ?></label> - <?php elseif (strpos($placeholder, '&') !== false): ?> - <input type="text" - class="optional" - data-parameter="<?php p($parameter); ?>" - value="<?php p($value); ?>" - placeholder="<?php p(substr($placeholder, 1)); ?>" /> - <?php elseif (strpos($placeholder, '#') !== false): ?> + <?php elseif (strpos($placeholder, '#') === 0): ?> <input type="hidden" data-parameter="<?php p($parameter); ?>" value="<?php p($value); ?>" /> <?php else: ?> <input type="text" + <?php if ($is_optional): ?> class="optional"<?php endif; ?> data-parameter="<?php p($parameter); ?>" value="<?php p($value); ?>" placeholder="<?php p($placeholder); ?>" /> diff --git a/apps/files_sharing/css/mobile.css b/apps/files_sharing/css/mobile.css index 7d2116d190dc542564310b9ef8618304b8e8f242..333c4c77fc999c6acd16723d900384a1d3d110ee 100644 --- a/apps/files_sharing/css/mobile.css +++ b/apps/files_sharing/css/mobile.css @@ -1,4 +1,4 @@ -@media only screen and (max-width: 600px) { +@media only screen and (max-width: 768px) { /* make header scroll up for single shares, more view of content on small screens */ #header.share-file { @@ -45,5 +45,13 @@ table td.filename .nametext { display: none; } +/* ellipsis on file names */ +.nametext { + width: 60%; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + } diff --git a/apps/files_sharing/tests/api.php b/apps/files_sharing/tests/api.php index e2bbb548182498f8c08b6d3c551cefb45fd3a91f..e3c5b6e4315ab9c706faa778c46fe28a13cd49bd 100644 --- a/apps/files_sharing/tests/api.php +++ b/apps/files_sharing/tests/api.php @@ -886,5 +886,5 @@ class Test_Files_Sharing_Api extends Test_Files_Sharing_Base { class TestShareApi extends \OCA\Files\Share\Api { public function correctPathTest($path, $folder) { return self::correctPath($path, $folder); -} + } } diff --git a/apps/files_trashbin/ajax/list.php b/apps/files_trashbin/ajax/list.php index 124a236bcbd505ea904181e4f2ce909766bb28b2..cec18c46525dd63dd61daccf2dc7695e05bb2ea2 100644 --- a/apps/files_trashbin/ajax/list.php +++ b/apps/files_trashbin/ajax/list.php @@ -1,11 +1,5 @@ <?php -// only need filesystem apps -$RUNTIME_APPTYPES=array('filesystem'); - -// Init owncloud - - OCP\JSON::checkLoggedIn(); // Load the files diff --git a/apps/files_trashbin/js/trash.js b/apps/files_trashbin/js/trash.js index 6aade210505e78cca7d1a94cde032d628a71234b..efe1e89f0bf3fc2c67a1d5e2f643d860995996bd 100644 --- a/apps/files_trashbin/js/trash.js +++ b/apps/files_trashbin/js/trash.js @@ -25,6 +25,11 @@ $(document).ready(function() { enableActions(); } + Files.updateStorageStatistics = function() { + // no op because the trashbin doesn't have + // storage info like free space / used space + }; + if (typeof FileActions !== 'undefined') { FileActions.register('all', 'Restore', OC.PERMISSION_READ, OC.imagePath('core', 'actions/history'), function(filename) { var tr = FileList.findFileEl(filename); diff --git a/apps/files_versions/js/versions.js b/apps/files_versions/js/versions.js index 4adf14745de06c54fd495e63568ebaa89e882cc7..b452bc25b13d4c3c9bf5dd6d5afcdea78d8fb96d 100644 --- a/apps/files_versions/js/versions.js +++ b/apps/files_versions/js/versions.js @@ -11,7 +11,7 @@ $(document).ready(function(){ // Add versions button to 'files/index.php' FileActions.register( 'file' - , t('files_versions', 'Versions') + , 'Versions' , OC.PERMISSION_UPDATE , function() { // Specify icon for hitory button @@ -36,6 +36,7 @@ $(document).ready(function(){ createVersionsDropdown(filename, file); } } + , t('files_versions', 'Versions') ); } diff --git a/apps/user_ldap/l10n/sl.php b/apps/user_ldap/l10n/sl.php index b2a21b6e0296ee9901a4c8517001db62b5e6ad4e..b6df62ffb79f3eb0cba0964e33d7dd37c8657532 100644 --- a/apps/user_ldap/l10n/sl.php +++ b/apps/user_ldap/l10n/sl.php @@ -85,6 +85,7 @@ $TRANSLATIONS = array( "One Group Base DN per line" => "Eno osnovno ime skupine na vrstico", "Group Search Attributes" => "Skupinski atributi iskanja", "Group-Member association" => "Povezava Älan-skupina", +"Nested Groups" => "Gnezdene skupine", "Special Attributes" => "Posebni atributi", "Quota Field" => "Polje koliÄinske omejitve", "Quota Default" => "Privzeta koliÄinska omejitev", diff --git a/build/package.json b/build/package.json index c9ed7b96c6c5b6af998c1f86a8a76f9bba4ea373..0c395839cf9cd00db2a12fd1dcfe4ec75330dbac 100644 --- a/build/package.json +++ b/build/package.json @@ -14,7 +14,9 @@ "karma": "*", "karma-jasmine": "*", "karma-junit-reporter": "*", - "karma-coverage": "*" + "karma-coverage": "*", + "karma-phantomjs-launcher": "*", + "phantomjs": "*" }, "engine": "node >= 0.8" } diff --git a/config/config.sample.php b/config/config.sample.php index 891c2eb5fa1fe92afe0539239abb8f964379e7ac..140b75706ea59e016281a6cbb0faccd097039f06 100755 --- a/config/config.sample.php +++ b/config/config.sample.php @@ -276,6 +276,15 @@ $CONFIG = array( /* all css and js files will be served by the web server statically in one js file and ons css file*/ 'asset-pipeline.enabled' => false, - /* where mount.json file should be stored, defaults to data/mount.json */ - 'mount_file' => '', +/* where mount.json file should be stored, defaults to data/mount.json */ +'mount_file' => '', + +/* + * Location of the cache folder, defaults to "data/$user/cache" where "$user" is the current user. + * + * When specified, the format will change to "$cache_path/$user" where "$cache_path" is the configured + * cache directory and "$user" is the user. + * + */ +'cache_path' => '' ); diff --git a/console.php b/console.php index 25b8b312539309e2c4ff8b4dcdb29d98602e7470..dd2c1026e477ef1d345062517204db0d712961db 100644 --- a/console.php +++ b/console.php @@ -8,7 +8,6 @@ use Symfony\Component\Console\Application; -$RUNTIME_NOAPPS = true; require_once 'lib/base.php'; // Don't do anything if ownCloud has not been installed yet @@ -22,6 +21,9 @@ if (!OC::$CLI) { exit(0); } +// load all apps to get all api routes properly setup +OC_App::loadApps(); + $defaults = new OC_Defaults; $application = new Application($defaults->getName(), \OC_Util::getVersionString()); require_once 'core/register_command.php'; diff --git a/core/ajax/share.php b/core/ajax/share.php index 3f04e1e4ad14691830b8f6d06ef6c7191aaa36b9..e667d9b5faae6a282845ff361976d4aabd31daca 100644 --- a/core/ajax/share.php +++ b/core/ajax/share.php @@ -21,7 +21,6 @@ OC_JSON::checkLoggedIn(); OCP\JSON::callCheck(); -OC_App::loadApps(); $defaults = new \OCP\Defaults(); diff --git a/core/ajax/update.php b/core/ajax/update.php index 2a0cbb2036d6e59be5121e57abfc60942a169a6e..55e8ab15ec22800be2aee0787a990577b21e2269 100644 --- a/core/ajax/update.php +++ b/core/ajax/update.php @@ -1,6 +1,5 @@ <?php set_time_limit(0); -$RUNTIME_NOAPPS = true; require_once '../../lib/base.php'; if (OC::checkUpgrade(false)) { diff --git a/core/command/upgrade.php b/core/command/upgrade.php index cfccfb5d2f0363928c3d5cb5242c09c6e47b9c61..ed72d136e24b3c461dd4bcbafe029d80171b0065 100644 --- a/core/command/upgrade.php +++ b/core/command/upgrade.php @@ -34,9 +34,6 @@ class Upgrade extends Command { * @param OutputInterface $output output interface */ protected function execute(InputInterface $input, OutputInterface $output) { - global $RUNTIME_NOAPPS; - - $RUNTIME_NOAPPS = true; //no apps, yet require_once \OC::$SERVERROOT . '/lib/base.php'; diff --git a/core/command/user/report.php b/core/command/user/report.php index 70c5a8566b722bf432504fd1b4df923766d08cd5..a5159310af1b45d6f17a0b8db84b37acc2fe4877 100644 --- a/core/command/user/report.php +++ b/core/command/user/report.php @@ -46,7 +46,6 @@ class Report extends Command { } private function countUsers() { - \OC_App::loadApps(array('authentication')); $userManager = \OC::$server->getUserManager(); return $userManager->countUsers(); } @@ -56,4 +55,4 @@ class Report extends Command { $userDirectories = $dataview->getDirectoryContent('/', 'httpd/unix-directory'); return count($userDirectories); } -} \ No newline at end of file +} diff --git a/core/css/jquery.ocdialog.css b/core/css/jquery.ocdialog.css index 236968e3245b68e3ad7ba9138ec18c51aeac5be4..a1221137bc447da3394c3140d763f3ea35574dd7 100644 --- a/core/css/jquery.ocdialog.css +++ b/core/css/jquery.ocdialog.css @@ -43,7 +43,7 @@ background-color: #000; opacity: .20;filter:Alpha(Opacity=20); z-index: 999; - position: absolute; + position: fixed; top: 0; left: 0; width: 100%; height: 100%; } diff --git a/core/css/mobile.css b/core/css/mobile.css index a63aa902d347ce73850feac1a86ac1ac4e6b9522..c67ac3e5ecf82d3a3563c9ad2e7fade5593721f2 100644 --- a/core/css/mobile.css +++ b/core/css/mobile.css @@ -1,4 +1,12 @@ -@media only screen and (max-width: 600px) { +@media only screen and (max-width: 768px) { + +/* show caret indicator next to logo to make clear it is tappable */ +#owncloud.menutoggle { + background-image: url('../img/actions/caret.svg'); + background-repeat: no-repeat; + background-position: right 26px; + padding-right: 16px !important; +} /* compress search box on mobile, expand when focused */ .searchbox input[type="search"] { @@ -18,5 +26,80 @@ display: none; } +/* toggle navigation */ +#content-wrapper { + padding-left: 0; +} + +#navigation { + top: 45px; + bottom: initial; + width: 255px; + max-height: 90%; + margin-top: 0; + top: 45px; + background-color: rgba(36, 40, 47, .97); + overflow-x: initial; + border-bottom-right-radius: 7px; + border-bottom: 1px #333 solid; + border-right: 1px #333 solid; + box-shadow: 0 0 7px rgba(29,45,68,.97); + display: none; +} +#navigation, #navigation * { + box-sizing:border-box; -moz-box-sizing:border-box; +} +#navigation li { + display: inline-block; +} +#navigation a { + width: 80px; + height: 80px; + display: inline-block; + text-align: center; + padding: 20px 0; +} +#navigation a span { + display: inline-block; + font-size: 13px; + padding-bottom: 0; + padding-left: 0; + width: 80px; +} +#navigation .icon { + margin: 0 auto; + padding: 0; +} +#navigation li:first-child .icon { + padding-top: 0; +} +/* Apps management as sticky footer */ +#navigation .wrapper { + min-height: initial; + margin: 0; +} +#apps-management, #navigation .push { + height: initial; +} + + + +/* shift to account for missing navigation */ +#body-user #controls, +#body-settings #controls { + padding-left: 0; +} + +/* don’t require a minimum width for controls bar */ +#controls { + min-width: initial !important; +} + +/* position share dropdown */ +#dropdown { + margin-right: 10% !important; + width: 80% !important; +} + } diff --git a/core/css/styles.css b/core/css/styles.css index 4420633e34ea7111dc7cc2121b9fda9216adf407..50e0314475ff60232ece5265f69d85b79deae6b5 100644 --- a/core/css/styles.css +++ b/core/css/styles.css @@ -77,6 +77,7 @@ body { background:#fefefe; font:normal .8em/1.6em "Helvetica Neue",Helvetica,Ari #header .logo { background-image: url(../img/logo.svg); + background-repeat: no-repeat; width: 250px; height: 118px; margin: 0 auto; @@ -84,6 +85,7 @@ body { background:#fefefe; font:normal .8em/1.6em "Helvetica Neue",Helvetica,Ari #header .logo-wide { background-image: url(../img/logo-wide.svg); + background-repeat: no-repeat; width: 147px; height: 32px; } @@ -246,6 +248,7 @@ input[type="submit"].enabled { -webkit-box-sizing: border-box; box-sizing: border-box; position: fixed; + top:45px; right: 0; left: 0; height: 44px; diff --git a/core/js/js.js b/core/js/js.js index 3d3185f12c14ce2d4545dd0a9c0da3b9a6a54e51..302b6b4d9faba8a990fdf6838304e7286a682a95 100644 --- a/core/js/js.js +++ b/core/js/js.js @@ -482,6 +482,53 @@ var OC={ }).show(); }, 'html'); } + }, + + // for menu toggling + registerMenu: function($toggle, $menuEl) { + $menuEl.addClass('menu'); + $toggle.addClass('menutoggle'); + $toggle.on('click.menu', function(event) { + if ($menuEl.is(OC._currentMenu)) { + $menuEl.hide(); + OC._currentMenu = null; + OC._currentMenuToggle = null; + return false; + } + // another menu was open? + else if (OC._currentMenu) { + // close it + OC._currentMenu.hide(); + } + $menuEl.show(); + OC._currentMenu = $menuEl; + OC._currentMenuToggle = $toggle; + return false + }); + }, + + unregisterMenu: function($toggle, $menuEl) { + // close menu if opened + if ($menuEl.is(OC._currentMenu)) { + $menuEl.hide(); + OC._currentMenu = null; + OC._currentMenuToggle = null; + } + $toggle.off('click.menu').removeClass('menutoggle'); + $menuEl.removeClass('menu'); + }, + + /** + * Wrapper for matchMedia + * + * This is makes it possible for unit tests to + * stub matchMedia (which doesn't work in PhantomJS) + */ + _matchMedia: function(media) { + if (window.matchMedia) { + return window.matchMedia(media); + } + return false; } }; OC.search.customResults={}; @@ -712,11 +759,11 @@ SVGSupport.checkMimeType=function(){ if(value[0]==='"'){ value=value.substr(1,value.length-2); } - headers[parts[0]]=value; + headers[parts[0].toLowerCase()]=value; } } }); - if(headers["Content-Type"]!=='image/svg+xml'){ + if(headers["content-type"]!=='image/svg+xml'){ replaceSVG(); SVGSupport.checkMimeType.correct=false; } @@ -940,6 +987,67 @@ function initCore() { $('a.action').tipsy({gravity:'s', fade:true, live:true}); $('td .modified').tipsy({gravity:'s', fade:true, live:true}); $('input').tipsy({gravity:'w', fade:true}); + + // toggle for menus + $(document).on('mouseup.closemenus', function(event) { + var $el = $(event.target); + if ($el.closest('.menu').length || $el.closest('.menutoggle').length) { + // don't close when clicking on the menu directly or a menu toggle + return false; + } + if (OC._currentMenu) { + OC._currentMenu.hide(); + } + OC._currentMenu = null; + OC._currentMenuToggle = null; + }); + + + /** + * Set up the main menu toggle to react to media query changes. + * If the screen is small enough, the main menu becomes a toggle. + * If the screen is bigger, the main menu is not a toggle any more. + */ + function setupMainMenu() { + // toggle the navigation on mobile + if (!OC._matchMedia) { + return; + } + var mq = OC._matchMedia('(max-width: 768px)'); + var lastMatch = mq.matches; + var $toggle = $('#header #owncloud'); + var $navigation = $('#navigation'); + + function updateMainMenu() { + // mobile mode ? + if (lastMatch && !$toggle.hasClass('menutoggle')) { + // init the menu + OC.registerMenu($toggle, $navigation); + $toggle.data('oldhref', $toggle.attr('href')); + $toggle.attr('href', '#'); + $navigation.hide(); + } + else { + OC.unregisterMenu($toggle, $navigation); + $toggle.attr('href', $toggle.data('oldhref')); + $navigation.show(); + } + } + + updateMainMenu(); + + // TODO: debounce this + $(window).resize(function() { + if (lastMatch !== mq.matches) { + lastMatch = mq.matches; + updateMainMenu(); + } + }); + } + + if (window.matchMedia) { + setupMainMenu(); + } } $(document).ready(initCore); diff --git a/core/js/share.js b/core/js/share.js index 9ee50ff6963473b11e6d1683dd340565f76d2cfc..e769edd0a216d811f84a157bc4fc212a401bb38b 100644 --- a/core/js/share.js +++ b/core/js/share.js @@ -48,7 +48,7 @@ OC.Share={ var action = $(file).find('.fileactions .action[data-action="Share"]'); var img = action.find('img').attr('src', image); action.addClass('permanent'); - action.html(' '+t('core', 'Shared')).prepend(img); + action.html(' <span>'+t('core', 'Shared')+'</span>').prepend(img); } else { var dir = $('#dir').val(); if (dir.length > 1) { @@ -63,7 +63,7 @@ OC.Share={ if (img.attr('src') != OC.imagePath('core', 'actions/public')) { img.attr('src', image); $(action).addClass('permanent'); - $(action).html(' '+t('core', 'Shared')).prepend(img); + $(action).html(' <span>'+t('core', 'Shared')+'</span>').prepend(img); } }); } @@ -103,10 +103,10 @@ OC.Share={ var img = action.find('img').attr('src', image); if (shares) { action.addClass('permanent'); - action.html(' '+ escapeHTML(t('core', 'Shared'))).prepend(img); + action.html(' <span>'+ escapeHTML(t('core', 'Shared'))+'</span>').prepend(img); } else { action.removeClass('permanent'); - action.html(' '+ escapeHTML(t('core', 'Share'))).prepend(img); + action.html(' <span>'+ escapeHTML(t('core', 'Share'))+'</span>').prepend(img); } } } diff --git a/core/js/tests/specs/coreSpec.js b/core/js/tests/specs/coreSpec.js index 069546387c71c23483c7db84ff3b1f2c41a51263..57ea5be8be051599c9b91584bbe00d4c032e367b 100644 --- a/core/js/tests/specs/coreSpec.js +++ b/core/js/tests/specs/coreSpec.js @@ -279,5 +279,109 @@ describe('Core base tests', function() { expect(OC.generateUrl('apps/files/download{file}', {file: '/Welcome.txt'})).toEqual(OC.webroot + '/index.php/apps/files/download/Welcome.txt'); }); }); + describe('Main menu mobile toggle', function() { + var oldMatchMedia; + var $toggle; + var $navigation; + + beforeEach(function() { + oldMatchMedia = OC._matchMedia; + // a separate method was needed because window.matchMedia + // cannot be stubbed due to a bug in PhantomJS: + // https://github.com/ariya/phantomjs/issues/12069 + OC._matchMedia = sinon.stub(); + $('#testArea').append('<div id="header">' + + '<a id="owncloud" href="#"></a>' + + '</div>' + + '<div id="navigation"></div>'); + $toggle = $('#owncloud'); + $navigation = $('#navigation'); + }); + + afterEach(function() { + OC._matchMedia = oldMatchMedia; + }); + it('Sets up menu toggle in mobile mode', function() { + OC._matchMedia.returns({matches: true}); + window.initCore(); + expect($toggle.hasClass('menutoggle')).toEqual(true); + expect($navigation.hasClass('menu')).toEqual(true); + }); + it('Does not set up menu toggle in desktop mode', function() { + OC._matchMedia.returns({matches: false}); + window.initCore(); + expect($toggle.hasClass('menutoggle')).toEqual(false); + expect($navigation.hasClass('menu')).toEqual(false); + }); + it('Switches on menu toggle when mobile mode changes', function() { + var mq = {matches: false}; + OC._matchMedia.returns(mq); + window.initCore(); + expect($toggle.hasClass('menutoggle')).toEqual(false); + mq.matches = true; + $(window).trigger('resize'); + expect($toggle.hasClass('menutoggle')).toEqual(true); + }); + it('Switches off menu toggle when mobile mode changes', function() { + var mq = {matches: true}; + OC._matchMedia.returns(mq); + window.initCore(); + expect($toggle.hasClass('menutoggle')).toEqual(true); + mq.matches = false; + $(window).trigger('resize'); + expect($toggle.hasClass('menutoggle')).toEqual(false); + }); + it('Clicking menu toggle toggles navigation in mobile mode', function() { + OC._matchMedia.returns({matches: true}); + window.initCore(); + $navigation.hide(); // normally done through media query triggered CSS + expect($navigation.is(':visible')).toEqual(false); + $toggle.click(); + expect($navigation.is(':visible')).toEqual(true); + $toggle.click(); + expect($navigation.is(':visible')).toEqual(false); + }); + it('Clicking menu toggle does not toggle navigation in desktop mode', function() { + OC._matchMedia.returns({matches: false}); + window.initCore(); + expect($navigation.is(':visible')).toEqual(true); + $toggle.click(); + expect($navigation.is(':visible')).toEqual(true); + }); + it('Switching to mobile mode hides navigation', function() { + var mq = {matches: false}; + OC._matchMedia.returns(mq); + window.initCore(); + expect($navigation.is(':visible')).toEqual(true); + mq.matches = true; + $(window).trigger('resize'); + expect($navigation.is(':visible')).toEqual(false); + }); + it('Switching to desktop mode shows navigation', function() { + var mq = {matches: true}; + OC._matchMedia.returns(mq); + window.initCore(); + expect($navigation.is(':visible')).toEqual(false); + mq.matches = false; + $(window).trigger('resize'); + expect($navigation.is(':visible')).toEqual(true); + }); + it('Switch to desktop with opened menu then back to mobile resets toggle', function() { + var mq = {matches: true}; + OC._matchMedia.returns(mq); + window.initCore(); + expect($navigation.is(':visible')).toEqual(false); + $toggle.click(); + expect($navigation.is(':visible')).toEqual(true); + mq.matches = false; + $(window).trigger('resize'); + expect($navigation.is(':visible')).toEqual(true); + mq.matches = true; + $(window).trigger('resize'); + expect($navigation.is(':visible')).toEqual(false); + $toggle.click(); + expect($navigation.is(':visible')).toEqual(true); + }); + }); }); diff --git a/core/l10n/es.php b/core/l10n/es.php index 7f4d46aa1f1a75feaa8401dc73198f57653b043e..52f1a15089abfdf211fee27f7ff527a6d37d09cd 100644 --- a/core/l10n/es.php +++ b/core/l10n/es.php @@ -1,6 +1,6 @@ <?php $TRANSLATIONS = array( -"Expiration date is in the past." => "La fecha de caducidad está en el pasado.", +"Expiration date is in the past." => "Ha pasado la fecha de caducidad", "Couldn't send mail to following users: %s " => "No se pudo enviar el mensaje a los siguientes usuarios: %s", "Turned on maintenance mode" => "Modo mantenimiento activado", "Turned off maintenance mode" => "Modo mantenimiento desactivado", @@ -105,7 +105,7 @@ $TRANSLATIONS = array( "Edit tags" => "Editar etiquetas", "Error loading dialog template: {error}" => "Error cargando plantilla de diálogo: {error}", "No tags selected for deletion." => "No hay etiquetas seleccionadas para borrar.", -"Please reload the page." => "Vuelva a cargar la página.", +"Please reload the page." => "Recargue/Actualice la página", "The update was unsuccessful. Please report this issue to the <a href=\"https://github.com/owncloud/core/issues\" target=\"_blank\">ownCloud community</a>." => "La actualización ha fracasado. Por favor, informe de este problema a la <a href=\"https://github.com/owncloud/core/issues\" target=\"_blank\">Comunidad de ownCloud</a>.", "The update was successful. Redirecting you to ownCloud now." => "La actualización se ha realizado con éxito. Redireccionando a ownCloud ahora.", "%s password reset" => "%s restablecer contraseña", @@ -179,6 +179,6 @@ $TRANSLATIONS = array( "Thank you for your patience." => "Gracias por su paciencia.", "Updating ownCloud to version %s, this may take a while." => "Actualizando ownCloud a la versión %s, esto puede demorar un tiempo.", "This ownCloud instance is currently being updated, which may take a while." => "Esta versión de owncloud se está actualizando, esto puede demorar un tiempo.", -"Please reload this page after a short time to continue using ownCloud." => "Por favor , recargue esta instancia de onwcloud tras un corto periodo de tiempo y continue usándolo." +"Please reload this page after a short time to continue using ownCloud." => "Por favor, recargue la página tras un corto periodo de tiempo para continuar usando ownCloud" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/core/l10n/ru.php b/core/l10n/ru.php index 8d84e7d31bc33888af8ae26b1ce18321517e03c0..e2fdc36be0be768010438c8f3b3ac3b10332fe95 100644 --- a/core/l10n/ru.php +++ b/core/l10n/ru.php @@ -1,5 +1,6 @@ <?php $TRANSLATIONS = array( +"Expiration date is in the past." => "Дата иÑÑ‚ÐµÑ‡ÐµÐ½Ð¸Ñ Ñрока дейÑÑ‚Ð²Ð¸Ñ Ð² прошлом.", "Couldn't send mail to following users: %s " => "Ðевозможно отправить пиÑьмо Ñледующим пользователÑм: %s", "Turned on maintenance mode" => "Режим отладки включён", "Turned off maintenance mode" => "Режим отладки отключён", @@ -56,6 +57,11 @@ $TRANSLATIONS = array( "(all selected)" => "(выбраны вÑе)", "({count} selected)" => "({count} выбрано)", "Error loading file exists template" => "Ошибка при загрузке шаблона ÑущеÑтвующего файла", +"Very weak password" => "Очень Ñлабый пароль", +"Weak password" => "Слабый пароль", +"So-so password" => "Так Ñебе пароль", +"Good password" => "Хороший пароль", +"Strong password" => "УÑтойчивый к взлому пароль", "Shared" => "Общие", "Share" => "Открыть доÑтуп", "Error" => "Ошибка", @@ -103,6 +109,7 @@ $TRANSLATIONS = array( "The update was unsuccessful. Please report this issue to the <a href=\"https://github.com/owncloud/core/issues\" target=\"_blank\">ownCloud community</a>." => "При обновлении произошла ошибка. ПожалуйÑта Ñообщите об Ñтом в <a href=\"https://github.com/owncloud/core/issues\" target=\"_blank\">ownCloud ÑообщеÑтво</a>.", "The update was successful. Redirecting you to ownCloud now." => "Обновление прошло уÑпешно. ПеренаправлÑемÑÑ Ð² Ваш ownCloud...", "%s password reset" => "%s ÑÐ±Ñ€Ð¾Ñ Ð¿Ð°Ñ€Ð¾Ð»Ñ", +"A problem has occurred whilst sending the email, please contact your administrator." => "Произошла ошибка при отправке ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ñлектронной почты, пожалуйÑта, ÑвÑжитеÑÑŒ Ñ Ð’Ð°ÑˆÐ¸Ð¼ админиÑтратором.", "Use the following link to reset your password: {link}" => "ИÑпользуйте Ñледующую ÑÑылку чтобы ÑброÑить пароль: {link}", "The link to reset your password has been sent to your email.<br>If you do not receive it within a reasonable amount of time, check your spam/junk folders.<br>If it is not there ask your local administrator ." => "СÑылка Ð´Ð»Ñ ÑброÑа Ð¿Ð°Ñ€Ð¾Ð»Ñ Ð¾Ñ‚Ð¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð° вам ​​по Ñлектронной почте.<br>ЕÑли вы не получите пиÑьмо в пределах одной-двух минут, проверьте папку Спам. <br>ЕÑли пиÑьма там нет, обратитеÑÑŒ к Ñвоему админиÑтратору.", "Request failed!<br>Did you make sure your email/username was right?" => "Ð—Ð°Ð¿Ñ€Ð¾Ñ Ð½Ðµ удалÑÑ. Ð’Ñ‹ уверены, что email или Ð¸Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ ÑƒÐºÐ°Ð·Ð°Ð½Ñ‹ верно?", @@ -115,6 +122,8 @@ $TRANSLATIONS = array( "To login page" => "Ðа Ñтраницу авторизации", "New password" => "Ðовый пароль", "Reset password" => "СброÑить пароль", +"Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " => "Mac OS X не поддерживаетÑÑ Ð¸ %s не будет работать правильно на Ñтой платформе. ИÑпользуйте ее на Ñвой Ñтрах и риÑк!", +"For the best results, please consider using a GNU/Linux server instead." => "Ð”Ð»Ñ Ð´Ð¾ÑÑ‚Ð¸Ð¶ÐµÐ½Ð¸Ñ Ð½Ð°Ð¸Ð»ÑƒÑ‡ÑˆÐ¸Ñ… результатов, пожалуйÑта, раÑÑмотрите возможноÑÑ‚ÑŒ иÑпользовать взамен GNU/Linux Ñервер.", "Personal" => "Личное", "Users" => "Пользователи", "Apps" => "ПриложениÑ", @@ -140,6 +149,7 @@ $TRANSLATIONS = array( "Your data directory and files are probably accessible from the internet because the .htaccess file does not work." => "Ваша папка Ñ Ð´Ð°Ð½Ð½Ñ‹Ð¼Ð¸ и файлы возможно доÑтупны из интернета потому что файл .htaccess не работает.", "For information how to properly configure your server, please see the <a href=\"%s\" target=\"_blank\">documentation</a>." => "Ð”Ð»Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ð¸, как правильно наÑтроить Ваш Ñервер, пожалуйÑта заглÑните в <a hrev=\"%s\"target=\"blank\">документацию</a>.", "Create an <strong>admin account</strong>" => "Создать <strong>учётную запиÑÑŒ админиÑтратора</strong>", +"Storage & database" => "СиÑтема Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð´Ð°Ð½Ð½Ñ‹Ñ… & база данных", "Data folder" => "Ð”Ð¸Ñ€ÐµÐºÑ‚Ð¾Ñ€Ð¸Ñ Ñ Ð´Ð°Ð½Ð½Ñ‹Ð¼Ð¸", "Configure the database" => "ÐаÑтройка базы данных", "will be used" => "будет иÑпользовано", @@ -162,6 +172,7 @@ $TRANSLATIONS = array( "remember" => "запомнить", "Log in" => "Войти", "Alternative Logins" => "Ðльтернативные имена пользователÑ", +"Hey there,<br><br>just letting you know that %s shared <strong>%s</strong> with you.<br><a href=\"%s\">View it!</a><br><br>" => "ЗдравÑтвуйте,<br><br>проÑто даём вам знать, что %s открыл доÑтуп к %s Ð´Ð»Ñ Ð²Ð°Ñ.<br><a href=\"%s\">ПоÑмотреть!</a><br><br>", "This ownCloud instance is currently in single user mode." => "Ðта уÑтановка ownCloud в наÑтоÑщее Ð²Ñ€ÐµÐ¼Ñ Ð² однопользовательÑком режиме.", "This means only administrators can use the instance." => "Ðто значит, что только админиÑтраторы могут иÑпользовать Ñту уÑтановку.", "Contact your system administrator if this message persists or appeared unexpectedly." => "ОбратитеÑÑŒ к вашему ÑиÑтемному админиÑтратору еÑли Ñто Ñообщение не иÑчезает или поÑвлÑетÑÑ Ð½ÐµÐ¾Ð¶Ð¸Ð´Ð°Ð½Ð½Ð¾.", diff --git a/core/l10n/sl.php b/core/l10n/sl.php index 2cfdfd11899c88d5096b448a82f93f2a4bba7490..49eb4f9aa6976b83f98880855bee642d87ed8534 100644 --- a/core/l10n/sl.php +++ b/core/l10n/sl.php @@ -122,6 +122,8 @@ $TRANSLATIONS = array( "To login page" => "Na prijavno stran", "New password" => "Novo geslo", "Reset password" => "Ponastavi geslo", +"Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " => "Sistem Mac OS X ni podprt, zato %s ne bo deloval zanesljivo v tem okolju. Program uporabljate na lastno odgovornost! ", +"For the best results, please consider using a GNU/Linux server instead." => "Za najbolj Å¡e rezultate je priporoÄljivo uporabljati strežnik GNU/Linux.", "Personal" => "Osebno", "Users" => "Uporabniki", "Apps" => "Programi", diff --git a/core/l10n/tr.php b/core/l10n/tr.php index 03544cd3c0fb63fbd532450ef1180d295731c1c7..affa1b063d0ac9ec7b3e1b98235d1da9c1d7278f 100644 --- a/core/l10n/tr.php +++ b/core/l10n/tr.php @@ -111,7 +111,7 @@ $TRANSLATIONS = array( "%s password reset" => "%s parola sıfırlama", "A problem has occurred whilst sending the email, please contact your administrator." => "E-posta gönderilirken bir hata oluÅŸtu. Lütfen yönetinizle iletiÅŸime geçin.", "Use the following link to reset your password: {link}" => "Parolanızı sıfırlamak için bu baÄŸlantıyı kullanın: {link}", -"The link to reset your password has been sent to your email.<br>If you do not receive it within a reasonable amount of time, check your spam/junk folders.<br>If it is not there ask your local administrator ." => "Parolanızı deÄŸiÅŸtirme baÄŸlantısı e-posta adresinize gönderildi.<br>EÄŸer makül bir süre içerisinde mesajı almadıysanız spam/junk dizinini kontrol ediniz.<br> EÄŸer orada da bulamazsanız sistem yöneticinize sorunuz.", +"The link to reset your password has been sent to your email.<br>If you do not receive it within a reasonable amount of time, check your spam/junk folders.<br>If it is not there ask your local administrator ." => "Parolanızı deÄŸiÅŸtirme baÄŸlantısı e-posta adresinize gönderildi.<br>EÄŸer makül bir süre içerisinde mesajı almadıysanız spam/junk/gereksiz dizinini kontrol ediniz.<br> EÄŸer yine bulamazsanız sistem yöneticinize sorunuz.", "Request failed!<br>Did you make sure your email/username was right?" => "Ä°stek baÅŸarısız!<br>E-posta ve/veya kullanıcı adınızın doÄŸru olduÄŸundan emin misiniz?", "You will receive a link to reset your password via Email." => "Parolanızı sıfırlamak için bir baÄŸlantıyı e-posta olarak alacaksınız.", "Username" => "Kullanıcı Adı", diff --git a/core/setup/controller.php b/core/setup/controller.php index bb9c9101fe29a22d2a5c41bde836f7b85c474baf..1a8e9b2b764ade3b7d7a0a0cf532fe970ac5dd9d 100644 --- a/core/setup/controller.php +++ b/core/setup/controller.php @@ -20,7 +20,7 @@ class Controller { $errors = array('errors' => $e); if(count($e) > 0) { - $options = array_merge($post, $opts, $errors); + $options = array_merge($opts, $post, $errors); $this->display($options); } else { @@ -28,7 +28,8 @@ class Controller { } } else { - $this->display($opts); + $options = array_merge($opts, $post); + $this->display($options); } } @@ -41,6 +42,7 @@ class Controller { 'dbname' => '', 'dbtablespace' => '', 'dbhost' => '', + 'dbtype' => '', ); $parameters = array_merge($defaults, $post); diff --git a/core/templates/layout.guest.php b/core/templates/layout.guest.php index 91157b923a5cdc57b954888bc456d834212976bc..5788d1d5bd3aa60f3432b26f75e6d150fed1fc4a 100644 --- a/core/templates/layout.guest.php +++ b/core/templates/layout.guest.php @@ -36,7 +36,7 @@ <body id="body-login"> <div class="wrapper"><!-- for sticky footer --> <header><div id="header"> - <div class='logo'></div> + <div class="logo svg"></div> <div id="logo-claim" style="display:none;"><?php p($theme->getLogoClaim()); ?></div> </div></header> diff --git a/core/templates/layout.user.php b/core/templates/layout.user.php index 3d89750348073ee79458d2017436f53c258eb89c..ba5f6ef9b541ae7f9705925ac49e05b84e990969 100644 --- a/core/templates/layout.user.php +++ b/core/templates/layout.user.php @@ -15,7 +15,7 @@ </title> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> - <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=0.5, maximum-scale=1.0"> + <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0"> <meta name="apple-itunes-app" content="app-id=543672169"> <link rel="shortcut icon" href="<?php print_unescaped(image_path('', 'favicon.png')); ?>" /> <link rel="apple-touch-icon-precomposed" href="<?php print_unescaped(image_path('', 'favicon-touch.png')); ?>" /> @@ -46,7 +46,7 @@ </div> <header><div id="header"> <a href="<?php print_unescaped(link_to('', 'index.php')); ?>" title="" id="owncloud"> - <div class='logo-wide'></div> + <div class="logo-wide svg"></div> </a> <div id="logo-claim" style="display:none;"><?php p($theme->getLogoClaim()); ?></div> <div id="settings" class="svg"> diff --git a/cron.php b/cron.php index 05d47124da062764b0681b7c5622085df00ef3dc..8fa72a319c0122405cb40c34ae232da4c0faceed 100644 --- a/cron.php +++ b/cron.php @@ -48,6 +48,9 @@ try { require_once 'lib/base.php'; + // load all apps to get all api routes properly setup + OC_App::loadApps(); + \OC::$session->close(); $logger = \OC_Log::$object; diff --git a/index.php b/index.php index 0a2f15f9f5e74de24e2b5da2d908b8ee62796b00..bd94d0e908d641123a814246ffa59ccbc69cffd1 100755 --- a/index.php +++ b/index.php @@ -21,8 +21,6 @@ * */ -$RUNTIME_NOAPPS = true; //no apps, yet - try { require_once 'lib/base.php'; diff --git a/l10n/cs_CZ/settings.po b/l10n/cs_CZ/settings.po index b092cba79c61c4f6a96ea885a1b26e6ddc52c8a9..3a2d8b6029bee06681cdbdbbbbfd1a91fd1fd112 100644 --- a/l10n/cs_CZ/settings.po +++ b/l10n/cs_CZ/settings.po @@ -14,9 +14,9 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2014-03-26 01:55-0400\n" -"PO-Revision-Date: 2014-03-26 05:55+0000\n" -"Last-Translator: I Robot\n" +"POT-Creation-Date: 2014-03-28 01:55-0400\n" +"PO-Revision-Date: 2014-03-27 21:10+0000\n" +"Last-Translator: m23 <black23@gmail.com>\n" "Language-Team: Czech (Czech Republic) (http://www.transifex.com/projects/p/owncloud/language/cs_CZ/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -436,18 +436,18 @@ msgstr "Cron" #: templates/admin.php:167 #, php-format msgid "Last cron was executed at %s." -msgstr "" +msgstr "Poslednà cron byl spuÅ¡tÄ›n v %s." #: templates/admin.php:170 #, php-format msgid "" "Last cron was executed at %s. This is more than an hour ago, something seems" " wrong." -msgstr "" +msgstr "Poslednà cron byl spuÅ¡tÄ›n v %s. To se stalo pÅ™ed vÃce než hodinu. Vypadá to, že nenà nÄ›co v pořádku." #: templates/admin.php:174 msgid "Cron was not executed yet!" -msgstr "" +msgstr "Cron jeÅ¡tÄ› nebyl spuÅ¡tÄ›n!" #: templates/admin.php:184 msgid "Execute one task with each page loaded" diff --git a/l10n/es/core.po b/l10n/es/core.po index 10724eb14340f941b4d2dd14f15efdc310e9c0b3..758f1024ad2f2c7bf1c69de9ae503f9fab1651e8 100644 --- a/l10n/es/core.po +++ b/l10n/es/core.po @@ -21,9 +21,9 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2014-03-23 01:55-0400\n" -"PO-Revision-Date: 2014-03-22 18:40+0000\n" -"Last-Translator: Art O. Pal <artopal@fastmail.fm>\n" +"POT-Creation-Date: 2014-03-29 01:55-0400\n" +"PO-Revision-Date: 2014-03-28 06:06+0000\n" +"Last-Translator: victormce <victormce@gmail.com>\n" "Language-Team: Spanish (http://www.transifex.com/projects/p/owncloud/language/es/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -31,24 +31,24 @@ msgstr "" "Language: es\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: ajax/share.php:88 +#: ajax/share.php:87 msgid "Expiration date is in the past." -msgstr "La fecha de caducidad está en el pasado." +msgstr "Ha pasado la fecha de caducidad" -#: ajax/share.php:120 ajax/share.php:162 +#: ajax/share.php:119 ajax/share.php:161 #, php-format msgid "Couldn't send mail to following users: %s " msgstr "No se pudo enviar el mensaje a los siguientes usuarios: %s" -#: ajax/update.php:11 +#: ajax/update.php:10 msgid "Turned on maintenance mode" msgstr "Modo mantenimiento activado" -#: ajax/update.php:14 +#: ajax/update.php:13 msgid "Turned off maintenance mode" msgstr "Modo mantenimiento desactivado" -#: ajax/update.php:17 +#: ajax/update.php:16 msgid "Updated database" msgstr "Base de datos actualizada" @@ -152,59 +152,59 @@ msgstr "Diciembre" msgid "Settings" msgstr "Ajustes" -#: js/js.js:496 +#: js/js.js:543 msgid "Saving..." msgstr "Guardando..." -#: js/js.js:995 +#: js/js.js:1103 msgid "seconds ago" msgstr "segundos antes" -#: js/js.js:996 +#: js/js.js:1104 msgid "%n minute ago" msgid_plural "%n minutes ago" msgstr[0] "Hace %n minuto" msgstr[1] "Hace %n minutos" -#: js/js.js:997 +#: js/js.js:1105 msgid "%n hour ago" msgid_plural "%n hours ago" msgstr[0] "Hace %n hora" msgstr[1] "Hace %n horas" -#: js/js.js:998 +#: js/js.js:1106 msgid "today" msgstr "hoy" -#: js/js.js:999 +#: js/js.js:1107 msgid "yesterday" msgstr "ayer" -#: js/js.js:1000 +#: js/js.js:1108 msgid "%n day ago" msgid_plural "%n days ago" msgstr[0] "Hace %n dÃa" msgstr[1] "Hace %n dÃas" -#: js/js.js:1001 +#: js/js.js:1109 msgid "last month" msgstr "el mes pasado" -#: js/js.js:1002 +#: js/js.js:1110 msgid "%n month ago" msgid_plural "%n months ago" msgstr[0] "Hace %n mes" msgstr[1] "Hace %n meses" -#: js/js.js:1003 +#: js/js.js:1111 msgid "months ago" msgstr "meses antes" -#: js/js.js:1004 +#: js/js.js:1112 msgid "last year" msgstr "el año pasado" -#: js/js.js:1005 +#: js/js.js:1113 msgid "years ago" msgstr "años antes" @@ -467,7 +467,7 @@ msgstr "No hay etiquetas seleccionadas para borrar." #: js/update.js:8 msgid "Please reload the page." -msgstr "Vuelva a cargar la página." +msgstr "Recargue/Actualice la página" #: js/update.js:17 msgid "" @@ -547,14 +547,14 @@ msgstr "Nueva contraseña" msgid "Reset password" msgstr "Restablecer contraseña" -#: setup/controller.php:138 +#: setup/controller.php:140 #, php-format msgid "" "Mac OS X is not supported and %s will not work properly on this platform. " "Use it at your own risk! " msgstr "Mac OS X no está soportado y %s no funcionará bien en esta plataforma. ¡Úsela a su propio riesgo! " -#: setup/controller.php:142 +#: setup/controller.php:144 msgid "" "For the best results, please consider using a GNU/Linux server instead." msgstr "Para óptimos resultados, considere utilizar un servidor GNU/Linux." @@ -812,4 +812,4 @@ msgstr "Esta versión de owncloud se está actualizando, esto puede demorar un t #: templates/update.user.php:4 msgid "Please reload this page after a short time to continue using ownCloud." -msgstr "Por favor , recargue esta instancia de onwcloud tras un corto periodo de tiempo y continue usándolo." +msgstr "Por favor, recargue la página tras un corto periodo de tiempo para continuar usando ownCloud" diff --git a/l10n/es/files_sharing.po b/l10n/es/files_sharing.po index dd65ca15edc1307f5dd6b5781b3e9ee7a4e94699..f44f8bcb5b90dba4b4f8a157f04aba76d187e71f 100644 --- a/l10n/es/files_sharing.po +++ b/l10n/es/files_sharing.po @@ -11,9 +11,9 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2014-03-12 01:54-0400\n" -"PO-Revision-Date: 2014-03-11 15:20+0000\n" -"Last-Translator: Art O. Pal <artopal@fastmail.fm>\n" +"POT-Creation-Date: 2014-03-29 01:55-0400\n" +"PO-Revision-Date: 2014-03-28 06:16+0000\n" +"Last-Translator: victormce <victormce@gmail.com>\n" "Language-Team: Spanish (http://www.transifex.com/projects/p/owncloud/language/es/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" diff --git a/l10n/es/files_trashbin.po b/l10n/es/files_trashbin.po index c4e1766460885d854cc7628a87654612b8729b56..2586fb2af3d704e0a474f53af33b89fea6dffc77 100644 --- a/l10n/es/files_trashbin.po +++ b/l10n/es/files_trashbin.po @@ -9,9 +9,9 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2014-03-15 02:13-0400\n" -"PO-Revision-Date: 2014-03-15 05:40+0000\n" -"Last-Translator: I Robot\n" +"POT-Creation-Date: 2014-03-29 01:55-0400\n" +"PO-Revision-Date: 2014-03-28 06:24+0000\n" +"Last-Translator: victormce <victormce@gmail.com>\n" "Language-Team: Spanish (http://www.transifex.com/projects/p/owncloud/language/es/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -33,11 +33,11 @@ msgstr "No se puede restaurar %s" msgid "Deleted files" msgstr "Archivos eliminados" -#: js/trash.js:16 js/trash.js:103 js/trash.js:152 +#: js/trash.js:16 js/trash.js:108 js/trash.js:157 msgid "Error" msgstr "Error" -#: lib/trashbin.php:853 lib/trashbin.php:855 +#: lib/trashbin.php:859 lib/trashbin.php:861 msgid "restored" msgstr "recuperado" diff --git a/l10n/gl/settings.po b/l10n/gl/settings.po index 8cb40f5bbb99728491bf75ea52f8e64c33167c41..2403bc7cc3e4c60a02ec8e86e86ca030d9cc5543 100644 --- a/l10n/gl/settings.po +++ b/l10n/gl/settings.po @@ -9,9 +9,9 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2014-03-26 01:55-0400\n" -"PO-Revision-Date: 2014-03-26 05:55+0000\n" -"Last-Translator: I Robot\n" +"POT-Creation-Date: 2014-03-29 01:55-0400\n" +"PO-Revision-Date: 2014-03-28 19:40+0000\n" +"Last-Translator: mbouzada <mbouzada@gmail.com>\n" "Language-Team: Galician (http://www.transifex.com/projects/p/owncloud/language/gl/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -66,7 +66,7 @@ msgid "Unable to load list from App Store" msgstr "Non foi posÃbel cargar a lista desde a App Store" #: ajax/changedisplayname.php:25 ajax/removeuser.php:15 ajax/setquota.php:17 -#: ajax/togglegroups.php:20 changepassword/controller.php:55 +#: ajax/togglegroups.php:20 changepassword/controller.php:49 msgid "Authentication error" msgstr "Produciuse un erro de autenticación" @@ -128,32 +128,32 @@ msgstr "Non é posÃbel eliminar o usuario do grupo %s" msgid "Couldn't update app." msgstr "Non foi posÃbel actualizar o aplicativo." -#: changepassword/controller.php:20 +#: changepassword/controller.php:17 msgid "Wrong password" msgstr "Contrasinal incorrecto" -#: changepassword/controller.php:42 +#: changepassword/controller.php:36 msgid "No user supplied" msgstr "Non subministrado polo usuario" -#: changepassword/controller.php:74 +#: changepassword/controller.php:68 msgid "" "Please provide an admin recovery password, otherwise all user data will be " "lost" msgstr "Forneza un contrasinal de recuperación do administrador de recuperación, senón perderanse todos os datos do usuario" -#: changepassword/controller.php:79 +#: changepassword/controller.php:73 msgid "" "Wrong admin recovery password. Please check the password and try again." msgstr "Contrasinal de recuperación do administrador incorrecto. Comprobe o contrasinal e tenteo de novo." -#: changepassword/controller.php:87 +#: changepassword/controller.php:81 msgid "" "Back-end doesn't support password change, but the users encryption key was " "successfully updated." msgstr "A infraestrutura non admite o cambio de contrasinal, mais a chave de cifrado dos usuarios foi actualizada correctamente." -#: changepassword/controller.php:92 changepassword/controller.php:103 +#: changepassword/controller.php:86 changepassword/controller.php:97 msgid "Unable to change password" msgstr "Non é posÃbel cambiar o contrasinal" @@ -286,7 +286,7 @@ msgstr "Debe fornecer un contrasinal" msgid "Warning: Home directory for user \"{user}\" already exists" msgstr "Aviso: O directorio persoal para o usuario «{user}» xa existe" -#: personal.php:49 personal.php:50 +#: personal.php:48 personal.php:49 msgid "__language_name__" msgstr "Galego" @@ -431,18 +431,18 @@ msgstr "Cron" #: templates/admin.php:167 #, php-format msgid "Last cron was executed at %s." -msgstr "" +msgstr "O último «cron» executouse ás %s." #: templates/admin.php:170 #, php-format msgid "" "Last cron was executed at %s. This is more than an hour ago, something seems" " wrong." -msgstr "" +msgstr "O último «cron» executouse ás %s. Isto supón que pasou máis dunha hora. polo que semella que algo vai mal." #: templates/admin.php:174 msgid "Cron was not executed yet!" -msgstr "" +msgstr "«Cron» aÃnda non foi executado!" #: templates/admin.php:184 msgid "Execute one task with each page loaded" diff --git a/l10n/pt_BR/settings.po b/l10n/pt_BR/settings.po index 2de5517c11ffe145528a5f0d2f9abba5368a1a57..aed155f749131c6f38b6a7409efdd656f780183a 100644 --- a/l10n/pt_BR/settings.po +++ b/l10n/pt_BR/settings.po @@ -9,9 +9,9 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2014-03-26 01:55-0400\n" -"PO-Revision-Date: 2014-03-26 05:55+0000\n" -"Last-Translator: I Robot\n" +"POT-Creation-Date: 2014-03-28 01:55-0400\n" +"PO-Revision-Date: 2014-03-27 19:31+0000\n" +"Last-Translator: Flávio Veras <flaviove@gmail.com>\n" "Language-Team: Portuguese (Brazil) (http://www.transifex.com/projects/p/owncloud/language/pt_BR/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -431,18 +431,18 @@ msgstr "Cron" #: templates/admin.php:167 #, php-format msgid "Last cron was executed at %s." -msgstr "" +msgstr "Último cron foi executado em %s." #: templates/admin.php:170 #, php-format msgid "" "Last cron was executed at %s. This is more than an hour ago, something seems" " wrong." -msgstr "" +msgstr "Última cron foi executado em %s. Isso é, mais do que uma hora atrás, algo parece errado." #: templates/admin.php:174 msgid "Cron was not executed yet!" -msgstr "" +msgstr "Cron não foi executado ainda!" #: templates/admin.php:184 msgid "Execute one task with each page loaded" diff --git a/l10n/ru/core.po b/l10n/ru/core.po index 1c955e4977698dc939d3c6e463305f8a6be7e3ea..fec52335def9c2233702d5a6c0166472a2294d73 100644 --- a/l10n/ru/core.po +++ b/l10n/ru/core.po @@ -13,6 +13,7 @@ # stushev, 2013 # eurekafag <rkfg@rkfg.me>, 2013 # sk.avenger <sk.avenger@adygnet.ru>, 2013 +# Swab <swab@i.ua>, 2014 # Victor Bravo <>, 2013 # vsapronov <vladimir.sapronov@gmail.com>, 2013 # not_your_conscience <hex.void@gmail.com>, 2013 @@ -23,9 +24,9 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2014-03-22 01:55-0400\n" -"PO-Revision-Date: 2014-03-22 05:55+0000\n" -"Last-Translator: I Robot\n" +"POT-Creation-Date: 2014-03-28 01:55-0400\n" +"PO-Revision-Date: 2014-03-27 14:31+0000\n" +"Last-Translator: Swab <swab@i.ua>\n" "Language-Team: Russian (http://www.transifex.com/projects/p/owncloud/language/ru/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -35,7 +36,7 @@ msgstr "" #: ajax/share.php:88 msgid "Expiration date is in the past." -msgstr "" +msgstr "Дата иÑÑ‚ÐµÑ‡ÐµÐ½Ð¸Ñ Ñрока дейÑÑ‚Ð²Ð¸Ñ Ð² прошлом." #: ajax/share.php:120 ajax/share.php:162 #, php-format @@ -281,23 +282,23 @@ msgstr "Ошибка при загрузке шаблона ÑущеÑтвующ #: js/setup.js:84 msgid "Very weak password" -msgstr "" +msgstr "Очень Ñлабый пароль" #: js/setup.js:85 msgid "Weak password" -msgstr "" +msgstr "Слабый пароль" #: js/setup.js:86 msgid "So-so password" -msgstr "" +msgstr "Так Ñебе пароль" #: js/setup.js:87 msgid "Good password" -msgstr "" +msgstr "Хороший пароль" #: js/setup.js:88 msgid "Strong password" -msgstr "" +msgstr "УÑтойчивый к взлому пароль" #: js/share.js:51 js/share.js:66 js/share.js:106 msgid "Shared" @@ -496,7 +497,7 @@ msgstr "%s ÑÐ±Ñ€Ð¾Ñ Ð¿Ð°Ñ€Ð¾Ð»Ñ" msgid "" "A problem has occurred whilst sending the email, please contact your " "administrator." -msgstr "" +msgstr "Произошла ошибка при отправке ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ñлектронной почты, пожалуйÑта, ÑвÑжитеÑÑŒ Ñ Ð’Ð°ÑˆÐ¸Ð¼ админиÑтратором." #: lostpassword/templates/email.php:2 msgid "Use the following link to reset your password: {link}" @@ -559,12 +560,12 @@ msgstr "СброÑить пароль" msgid "" "Mac OS X is not supported and %s will not work properly on this platform. " "Use it at your own risk! " -msgstr "" +msgstr "Mac OS X не поддерживаетÑÑ Ð¸ %s не будет работать правильно на Ñтой платформе. ИÑпользуйте ее на Ñвой Ñтрах и риÑк!" #: setup/controller.php:142 msgid "" "For the best results, please consider using a GNU/Linux server instead." -msgstr "" +msgstr "Ð”Ð»Ñ Ð´Ð¾ÑÑ‚Ð¸Ð¶ÐµÐ½Ð¸Ñ Ð½Ð°Ð¸Ð»ÑƒÑ‡ÑˆÐ¸Ñ… результатов, пожалуйÑта, раÑÑмотрите возможноÑÑ‚ÑŒ иÑпользовать взамен GNU/Linux Ñервер." #: strings.php:5 msgid "Personal" @@ -686,7 +687,7 @@ msgstr "Создать <strong>учётную запиÑÑŒ админиÑтра #: templates/installation.php:70 msgid "Storage & database" -msgstr "" +msgstr "СиÑтема Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð´Ð°Ð½Ð½Ñ‹Ñ… & база данных" #: templates/installation.php:77 msgid "Data folder" @@ -787,7 +788,7 @@ msgstr "Ðльтернативные имена пользователÑ" msgid "" "Hey there,<br><br>just letting you know that %s shared <strong>%s</strong> " "with you.<br><a href=\"%s\">View it!</a><br><br>" -msgstr "" +msgstr "ЗдравÑтвуйте,<br><br>проÑто даём вам знать, что %s открыл доÑтуп к %s Ð´Ð»Ñ Ð²Ð°Ñ.<br><a href=\"%s\">ПоÑмотреть!</a><br><br>" #: templates/singleuser.user.php:3 msgid "This ownCloud instance is currently in single user mode." diff --git a/l10n/ru/files.po b/l10n/ru/files.po index 8e80384b7c89ce7e8e75b8e81dfe2f8bc38ac05c..4b5c985dfc97a347bc6ea3b0647737be5ca0b35a 100644 --- a/l10n/ru/files.po +++ b/l10n/ru/files.po @@ -8,6 +8,7 @@ # jekader <jekader@gmail.com>, 2013 # mogarych <mogarych@mail.ru>, 2014 # eurekafag <rkfg@rkfg.me>, 2013 +# Swab <swab@i.ua>, 2014 # Victor Bravo <>, 2013 # vsapronov <vladimir.sapronov@gmail.com>, 2013 # not_your_conscience <hex.void@gmail.com>, 2013 @@ -18,9 +19,9 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2014-03-27 01:55-0400\n" -"PO-Revision-Date: 2014-03-26 06:01+0000\n" -"Last-Translator: I Robot\n" +"POT-Creation-Date: 2014-03-28 01:55-0400\n" +"PO-Revision-Date: 2014-03-27 14:22+0000\n" +"Last-Translator: Swab <swab@i.ua>\n" "Language-Team: Russian (http://www.transifex.com/projects/p/owncloud/language/ru/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -45,7 +46,7 @@ msgstr "Ð˜Ð¼Ñ Ñ„Ð°Ð¹Ð»Ð° не может быть пуÑтым." #: ajax/newfile.php:63 #, php-format msgid "\"%s\" is an invalid file name." -msgstr "" +msgstr "\"%s\" Ñто не правильное Ð¸Ð¼Ñ Ñ„Ð°Ð¹Ð»Ð°." #: ajax/newfile.php:69 ajax/newfolder.php:28 js/files.js:105 msgid "" @@ -56,7 +57,7 @@ msgstr "Ðеправильное имÑ: Ñимволы '\\', '/', '<', '>', ':' #: ajax/newfile.php:76 ajax/newfolder.php:35 ajax/upload.php:141 #: lib/app.php:65 msgid "The target folder has been moved or deleted." -msgstr "" +msgstr "Целевой каталог был перемещен или удален." #: ajax/newfile.php:88 ajax/newfolder.php:47 lib/app.php:74 #, php-format @@ -160,12 +161,12 @@ msgstr "Ðевозможно загрузить {filename}, так как Ñто #: js/file-upload.js:258 msgid "Total file size {size1} exceeds upload limit {size2}" -msgstr "" +msgstr "Полный размер файла {size1} превышает лимит по загрузке {size2}" #: js/file-upload.js:268 msgid "" "Not enough free space, you are uploading {size1} but only {size2} is left" -msgstr "" +msgstr "Ðе доÑтаточно Ñвободного меÑта, Ð’Ñ‹ загружаете {size1} но оÑталоÑÑŒ только {size2}" #: js/file-upload.js:340 msgid "Upload cancelled." @@ -263,7 +264,7 @@ msgstr[2] "Закачка %n файлов" #: js/files.js:96 msgid "\"{name}\" is an invalid file name." -msgstr "" +msgstr "\"{name}\" Ñто не правильное Ð¸Ð¼Ñ Ñ„Ð°Ð¹Ð»Ð°." #: js/files.js:117 msgid "Your storage is full, files can not be updated or synced anymore!" diff --git a/l10n/ru/files_encryption.po b/l10n/ru/files_encryption.po index 07f348b79def6269aba866b7ac2988f3bde0add4..d0f028c8df8e63dfc2a2accd90adc7e8500986a6 100644 --- a/l10n/ru/files_encryption.po +++ b/l10n/ru/files_encryption.po @@ -8,15 +8,16 @@ # lord93 <lordakryl@gmail.com>, 2013 # jekader <jekader@gmail.com>, 2013 # eurekafag <rkfg@rkfg.me>, 2013 +# Swab <swab@i.ua>, 2014 # Victor Bravo <>, 2013 # vsapronov <vladimir.sapronov@gmail.com>, 2013 msgid "" msgstr "" "Project-Id-Version: ownCloud\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2014-03-11 01:54-0400\n" -"PO-Revision-Date: 2014-03-11 05:55+0000\n" -"Last-Translator: I Robot\n" +"POT-Creation-Date: 2014-03-28 01:55-0400\n" +"PO-Revision-Date: 2014-03-27 14:31+0000\n" +"Last-Translator: Swab <swab@i.ua>\n" "Language-Team: Russian (http://www.transifex.com/projects/p/owncloud/language/ru/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -108,7 +109,7 @@ msgstr "Ðачато начальное шифрование... Ðто може #: js/detect-migration.js:25 msgid "Initial encryption running... Please try again later." -msgstr "" +msgstr "Работает первоначальное шифрование... ПожалуйÑта, повторите попытку позже." #: templates/invalid_private_key.php:8 msgid "Go directly to your " diff --git a/l10n/ru/files_external.po b/l10n/ru/files_external.po index 6af3711cc643a10f7cb9d32d6d192f0fb9119d92..74a21556d84d73e0137ae37092924a4b5450ef3a 100644 --- a/l10n/ru/files_external.po +++ b/l10n/ru/files_external.po @@ -3,13 +3,14 @@ # This file is distributed under the same license as the PACKAGE package. # # Translators: +# Swab <swab@i.ua>, 2014 msgid "" msgstr "" "Project-Id-Version: ownCloud\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2014-03-11 01:54-0400\n" -"PO-Revision-Date: 2014-03-11 05:55+0000\n" -"Last-Translator: I Robot\n" +"POT-Creation-Date: 2014-03-28 01:55-0400\n" +"PO-Revision-Date: 2014-03-27 14:41+0000\n" +"Last-Translator: Swab <swab@i.ua>\n" "Language-Team: Russian (http://www.transifex.com/projects/p/owncloud/language/ru/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -39,22 +40,22 @@ msgstr "Ошибка при наÑтройке хранилища Google Drive" #: js/settings.js:313 js/settings.js:320 msgid "Saved" -msgstr "" +msgstr "Сохранено" -#: lib/config.php:512 +#: lib/config.php:654 msgid "" "<b>Warning:</b> \"smbclient\" is not installed. Mounting of CIFS/SMB shares " "is not possible. Please ask your system administrator to install it." msgstr "<b>Внимание:</b> \"smbclient\" не уÑтановлен. Подключение по CIFS/SMB невозможно. ПожалуйÑта, обратитеÑÑŒ к ÑиÑтемному админиÑтратору, чтобы уÑтановить его." -#: lib/config.php:516 +#: lib/config.php:658 msgid "" "<b>Warning:</b> The FTP support in PHP is not enabled or installed. Mounting" " of FTP shares is not possible. Please ask your system administrator to " "install it." msgstr "<b>Внимание:</b> Поддержка FTP не включена в PHP. Подключение по FTP невозможно. ПожалуйÑта, обратитеÑÑŒ к ÑиÑтемному админиÑтратору, чтобы включить." -#: lib/config.php:519 +#: lib/config.php:661 msgid "" "<b>Warning:</b> The Curl support in PHP is not enabled or installed. " "Mounting of ownCloud / WebDAV or GoogleDrive is not possible. Please ask " @@ -116,7 +117,7 @@ msgstr "Включить пользовательÑкие внешние Ð½Ð¾Ñ #: templates/settings.php:130 msgid "Allow users to mount the following external storage" -msgstr "" +msgstr "Разрешить пользователÑм монтировать Ñледующую внешнюю ÑиÑтему Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð´Ð°Ð½Ð½Ñ‹Ñ…" #: templates/settings.php:147 msgid "SSL root certificates" diff --git a/l10n/ru/settings.po b/l10n/ru/settings.po index 5ee5f5e35c3e766624a2ba74726e482935b73a1b..5654edd1a8241e2ac2ead6ad91fc734de281c60d 100644 --- a/l10n/ru/settings.po +++ b/l10n/ru/settings.po @@ -22,8 +22,8 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2014-03-26 01:55-0400\n" -"PO-Revision-Date: 2014-03-26 05:55+0000\n" +"POT-Creation-Date: 2014-03-28 01:55-0400\n" +"PO-Revision-Date: 2014-03-27 14:41+0000\n" "Last-Translator: I Robot\n" "Language-Team: Russian (http://www.transifex.com/projects/p/owncloud/language/ru/)\n" "MIME-Version: 1.0\n" @@ -39,7 +39,7 @@ msgstr "" #: admin/controller.php:73 msgid "Saved" -msgstr "" +msgstr "Сохранено" #: admin/controller.php:90 msgid "test email settings" @@ -232,23 +232,23 @@ msgstr "Выберите картинку профилÑ" #: js/personal.js:277 msgid "Very weak password" -msgstr "" +msgstr "Очень Ñлабый пароль" #: js/personal.js:278 msgid "Weak password" -msgstr "" +msgstr "Слабый пароль" #: js/personal.js:279 msgid "So-so password" -msgstr "" +msgstr "Так Ñебе пароль" #: js/personal.js:280 msgid "Good password" -msgstr "" +msgstr "Хороший пароль" #: js/personal.js:281 msgid "Strong password" -msgstr "" +msgstr "УÑтойчивый к взлому пароль" #: js/personal.js:316 msgid "Decrypting files... Please wait, this can take some time." diff --git a/l10n/sl/core.po b/l10n/sl/core.po index ddd78cae32fcc6b679698125f4ca41bd8b3c9bd4..e3285d025c880bb5f58d61f720f244c9ff5d9b05 100644 --- a/l10n/sl/core.po +++ b/l10n/sl/core.po @@ -9,9 +9,9 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2014-03-22 01:55-0400\n" -"PO-Revision-Date: 2014-03-22 05:55+0000\n" -"Last-Translator: I Robot\n" +"POT-Creation-Date: 2014-03-30 01:55-0400\n" +"PO-Revision-Date: 2014-03-29 20:30+0000\n" +"Last-Translator: mateju <>\n" "Language-Team: Slovenian (http://www.transifex.com/projects/p/owncloud/language/sl/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -19,24 +19,24 @@ msgstr "" "Language: sl\n" "Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3);\n" -#: ajax/share.php:88 +#: ajax/share.php:87 msgid "Expiration date is in the past." msgstr "Datum preteka je v preteklosti." -#: ajax/share.php:120 ajax/share.php:162 +#: ajax/share.php:119 ajax/share.php:161 #, php-format msgid "Couldn't send mail to following users: %s " msgstr "Ni mogoÄe poslati sporoÄila za: %s" -#: ajax/update.php:11 +#: ajax/update.php:10 msgid "Turned on maintenance mode" msgstr "Vzdrževalni naÄin je omogoÄen" -#: ajax/update.php:14 +#: ajax/update.php:13 msgid "Turned off maintenance mode" msgstr "Vzdrževalni naÄin je onemogoÄen" -#: ajax/update.php:17 +#: ajax/update.php:16 msgid "Updated database" msgstr "Posodobljena podatkovna zbirka" @@ -140,15 +140,15 @@ msgstr "december" msgid "Settings" msgstr "Nastavitve" -#: js/js.js:496 +#: js/js.js:543 msgid "Saving..." msgstr "Poteka shranjevanje ..." -#: js/js.js:995 +#: js/js.js:1103 msgid "seconds ago" msgstr "pred nekaj sekundami" -#: js/js.js:996 +#: js/js.js:1104 msgid "%n minute ago" msgid_plural "%n minutes ago" msgstr[0] "pred %n minuto" @@ -156,7 +156,7 @@ msgstr[1] "pred %n minutama" msgstr[2] "pred %n minutami" msgstr[3] "pred %n minutami" -#: js/js.js:997 +#: js/js.js:1105 msgid "%n hour ago" msgid_plural "%n hours ago" msgstr[0] "pred %n uro" @@ -164,15 +164,15 @@ msgstr[1] "pred %n urama" msgstr[2] "pred %n urami" msgstr[3] "pred %n urami" -#: js/js.js:998 +#: js/js.js:1106 msgid "today" msgstr "danes" -#: js/js.js:999 +#: js/js.js:1107 msgid "yesterday" msgstr "vÄeraj" -#: js/js.js:1000 +#: js/js.js:1108 msgid "%n day ago" msgid_plural "%n days ago" msgstr[0] "pred %n dnevom" @@ -180,11 +180,11 @@ msgstr[1] "pred %n dnevoma" msgstr[2] "pred %n dnevi" msgstr[3] "pred %n dnevi" -#: js/js.js:1001 +#: js/js.js:1109 msgid "last month" msgstr "zadnji mesec" -#: js/js.js:1002 +#: js/js.js:1110 msgid "%n month ago" msgid_plural "%n months ago" msgstr[0] "pred %n mesecem" @@ -192,15 +192,15 @@ msgstr[1] "pred %n mesecema" msgstr[2] "pred %n meseci" msgstr[3] "pred %n meseci" -#: js/js.js:1003 +#: js/js.js:1111 msgid "months ago" msgstr "mesecev nazaj" -#: js/js.js:1004 +#: js/js.js:1112 msgid "last year" msgstr "lansko leto" -#: js/js.js:1005 +#: js/js.js:1113 msgid "years ago" msgstr "let nazaj" @@ -545,17 +545,17 @@ msgstr "Novo geslo" msgid "Reset password" msgstr "Ponastavi geslo" -#: setup/controller.php:138 +#: setup/controller.php:140 #, php-format msgid "" "Mac OS X is not supported and %s will not work properly on this platform. " "Use it at your own risk! " -msgstr "" +msgstr "Sistem Mac OS X ni podprt, zato %s ne bo deloval zanesljivo v tem okolju. Program uporabljate na lastno odgovornost! " -#: setup/controller.php:142 +#: setup/controller.php:144 msgid "" "For the best results, please consider using a GNU/Linux server instead." -msgstr "" +msgstr "Za najbolj Å¡e rezultate je priporoÄljivo uporabljati strežnik GNU/Linux." #: strings.php:5 msgid "Personal" diff --git a/l10n/sl/settings.po b/l10n/sl/settings.po index 91e310d8f24f9abcfd65623cb7b9fd8933d0cdaf..dbde28d85dd9c000f420371f76cdaf8a5991ae3f 100644 --- a/l10n/sl/settings.po +++ b/l10n/sl/settings.po @@ -9,9 +9,9 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2014-03-26 01:55-0400\n" -"PO-Revision-Date: 2014-03-26 05:55+0000\n" -"Last-Translator: I Robot\n" +"POT-Creation-Date: 2014-03-30 01:55-0400\n" +"PO-Revision-Date: 2014-03-29 20:50+0000\n" +"Last-Translator: mateju <>\n" "Language-Team: Slovenian (http://www.transifex.com/projects/p/owncloud/language/sl/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -22,7 +22,7 @@ msgstr "" #: admin/controller.php:66 #, php-format msgid "Invalid value supplied for %s" -msgstr "" +msgstr "Navedena je napaÄna vrednost za %s" #: admin/controller.php:73 msgid "Saved" @@ -66,7 +66,7 @@ msgid "Unable to load list from App Store" msgstr "Ni mogoÄe naložiti seznama iz programskega srediÅ¡Äa" #: ajax/changedisplayname.php:25 ajax/removeuser.php:15 ajax/setquota.php:17 -#: ajax/togglegroups.php:20 changepassword/controller.php:55 +#: ajax/togglegroups.php:20 changepassword/controller.php:49 msgid "Authentication error" msgstr "Napaka med overjanjem" @@ -128,32 +128,32 @@ msgstr "Uporabnika ni mogoÄe odstraniti iz skupine %s" msgid "Couldn't update app." msgstr "Programa ni mogoÄe posodobiti." -#: changepassword/controller.php:20 +#: changepassword/controller.php:17 msgid "Wrong password" msgstr "NapaÄno geslo" -#: changepassword/controller.php:42 +#: changepassword/controller.php:36 msgid "No user supplied" msgstr "Ni navedenega uporabnika" -#: changepassword/controller.php:74 +#: changepassword/controller.php:68 msgid "" "Please provide an admin recovery password, otherwise all user data will be " "lost" msgstr "Podati je treba skrbniÅ¡ko obnovitveno geslo, sicer bodo vsi uporabniÅ¡ki podatki izgubljeni." -#: changepassword/controller.php:79 +#: changepassword/controller.php:73 msgid "" "Wrong admin recovery password. Please check the password and try again." msgstr "NapaÄno navedeno skrbniÅ¡ko obnovitveno geslo. Preverite geslo in poskusite znova." -#: changepassword/controller.php:87 +#: changepassword/controller.php:81 msgid "" "Back-end doesn't support password change, but the users encryption key was " "successfully updated." msgstr "HrbtiÅ¡Äe programa ne podpira spreminjanja gesla, je pa uspeÅ¡no posodobljeno uporabniÅ¡ko Å¡ifriranje." -#: changepassword/controller.php:92 changepassword/controller.php:103 +#: changepassword/controller.php:86 changepassword/controller.php:97 msgid "Unable to change password" msgstr "Ni mogoÄe spremeniti gesla" @@ -286,7 +286,7 @@ msgstr "Navedeno mora biti veljavno geslo" msgid "Warning: Home directory for user \"{user}\" already exists" msgstr "Opozorilo: osebna mapa uporabnika \"{user}\" že obstaja" -#: personal.php:49 personal.php:50 +#: personal.php:48 personal.php:49 msgid "__language_name__" msgstr "SlovenÅ¡Äina" diff --git a/l10n/sl/user_ldap.po b/l10n/sl/user_ldap.po index 5a24ba9ffa7072d057e8ea5a4f71eba845c92cfb..222415e4d01680a720b6e65ca4ed6c98512a1f21 100644 --- a/l10n/sl/user_ldap.po +++ b/l10n/sl/user_ldap.po @@ -4,14 +4,14 @@ # # Translators: # barbarak <barbarak@arnes.si>, 2013 -# mateju <>, 2013 +# mateju <>, 2013-2014 msgid "" msgstr "" "Project-Id-Version: ownCloud\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2014-03-27 01:55-0400\n" -"PO-Revision-Date: 2014-03-26 06:01+0000\n" -"Last-Translator: I Robot\n" +"POT-Creation-Date: 2014-03-30 01:55-0400\n" +"PO-Revision-Date: 2014-03-29 20:50+0000\n" +"Last-Translator: mateju <>\n" "Language-Team: Slovenian (http://www.transifex.com/projects/p/owncloud/language/sl/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -417,7 +417,7 @@ msgstr "Povezava Älan-skupina" #: templates/settings.php:39 msgid "Nested Groups" -msgstr "" +msgstr "Gnezdene skupine" #: templates/settings.php:39 msgid "" diff --git a/l10n/templates/core.pot b/l10n/templates/core.pot index 8f14123eedf85b0e8343724304eb20cc54dc4bb9..eacfde04d8dd886e55482580190572a1100f6abe 100644 --- a/l10n/templates/core.pot +++ b/l10n/templates/core.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud Core 6.0.0\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2014-03-27 01:55-0400\n" +"POT-Creation-Date: 2014-03-31 01:55-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -18,24 +18,24 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" -#: ajax/share.php:88 +#: ajax/share.php:87 msgid "Expiration date is in the past." msgstr "" -#: ajax/share.php:120 ajax/share.php:162 +#: ajax/share.php:119 ajax/share.php:161 #, php-format msgid "Couldn't send mail to following users: %s " msgstr "" -#: ajax/update.php:11 +#: ajax/update.php:10 msgid "Turned on maintenance mode" msgstr "" -#: ajax/update.php:14 +#: ajax/update.php:13 msgid "Turned off maintenance mode" msgstr "" -#: ajax/update.php:17 +#: ajax/update.php:16 msgid "Updated database" msgstr "" @@ -139,59 +139,59 @@ msgstr "" msgid "Settings" msgstr "" -#: js/js.js:496 +#: js/js.js:543 msgid "Saving..." msgstr "" -#: js/js.js:995 +#: js/js.js:1103 msgid "seconds ago" msgstr "" -#: js/js.js:996 +#: js/js.js:1104 msgid "%n minute ago" msgid_plural "%n minutes ago" msgstr[0] "" msgstr[1] "" -#: js/js.js:997 +#: js/js.js:1105 msgid "%n hour ago" msgid_plural "%n hours ago" msgstr[0] "" msgstr[1] "" -#: js/js.js:998 +#: js/js.js:1106 msgid "today" msgstr "" -#: js/js.js:999 +#: js/js.js:1107 msgid "yesterday" msgstr "" -#: js/js.js:1000 +#: js/js.js:1108 msgid "%n day ago" msgid_plural "%n days ago" msgstr[0] "" msgstr[1] "" -#: js/js.js:1001 +#: js/js.js:1109 msgid "last month" msgstr "" -#: js/js.js:1002 +#: js/js.js:1110 msgid "%n month ago" msgid_plural "%n months ago" msgstr[0] "" msgstr[1] "" -#: js/js.js:1003 +#: js/js.js:1111 msgid "months ago" msgstr "" -#: js/js.js:1004 +#: js/js.js:1112 msgid "last year" msgstr "" -#: js/js.js:1005 +#: js/js.js:1113 msgid "years ago" msgstr "" @@ -534,14 +534,14 @@ msgstr "" msgid "Reset password" msgstr "" -#: setup/controller.php:138 +#: setup/controller.php:140 #, php-format msgid "" "Mac OS X is not supported and %s will not work properly on this platform. " "Use it at your own risk! " msgstr "" -#: setup/controller.php:142 +#: setup/controller.php:144 msgid "For the best results, please consider using a GNU/Linux server instead." msgstr "" diff --git a/l10n/templates/files.pot b/l10n/templates/files.pot index f2639f9303089cdad47d200fd910764c51613eb8..bc931be91d8f1191c7a8bf5300563ce60c1ae24e 100644 --- a/l10n/templates/files.pot +++ b/l10n/templates/files.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud Core 6.0.0\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2014-03-27 01:55-0400\n" +"POT-Creation-Date: 2014-03-31 01:55-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -193,15 +193,15 @@ msgstr "" msgid "Error fetching URL" msgstr "" -#: js/fileactions.js:149 +#: js/fileactions.js:164 msgid "Share" msgstr "" -#: js/fileactions.js:162 +#: js/fileactions.js:177 msgid "Delete permanently" msgstr "" -#: js/fileactions.js:223 +#: js/fileactions.js:238 msgid "Rename" msgstr "" @@ -225,13 +225,13 @@ msgstr "" msgid "Error deleting file." msgstr "" -#: js/filelist.js:687 js/filelist.js:761 js/files.js:691 +#: js/filelist.js:687 js/filelist.js:761 js/files.js:694 msgid "%n folder" msgid_plural "%n folders" msgstr[0] "" msgstr[1] "" -#: js/filelist.js:688 js/filelist.js:762 js/files.js:697 +#: js/filelist.js:688 js/filelist.js:762 js/files.js:700 msgid "%n file" msgid_plural "%n files" msgstr[0] "" @@ -277,29 +277,29 @@ msgid "" "your personal settings to decrypt your files." msgstr "" -#: js/files.js:379 +#: js/files.js:382 msgid "" "Your download is being prepared. This might take some time if the files are " "big." msgstr "" -#: js/files.js:610 js/files.js:654 +#: js/files.js:613 js/files.js:657 msgid "Error moving file" msgstr "" -#: js/files.js:610 js/files.js:654 +#: js/files.js:613 js/files.js:657 msgid "Error" msgstr "" -#: js/files.js:672 templates/index.php:68 +#: js/files.js:675 templates/index.php:68 msgid "Name" msgstr "" -#: js/files.js:673 templates/index.php:80 +#: js/files.js:676 templates/index.php:80 msgid "Size" msgstr "" -#: js/files.js:674 templates/index.php:82 +#: js/files.js:677 templates/index.php:82 msgid "Modified" msgstr "" diff --git a/l10n/templates/files_encryption.pot b/l10n/templates/files_encryption.pot index 1ef7c2cab99e50b91b05871e2f6e3c19f5bbf438..99f9cf030b692ae06bb36c3ddc37fa1477f5d541 100644 --- a/l10n/templates/files_encryption.pot +++ b/l10n/templates/files_encryption.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud Core 6.0.0\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2014-03-27 01:55-0400\n" +"POT-Creation-Date: 2014-03-31 01:55-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" diff --git a/l10n/templates/files_external.pot b/l10n/templates/files_external.pot index 965c851209060f540b07f7d60d492849f814ef65..6334e403e412098bc801255c5edc71584b007f15 100644 --- a/l10n/templates/files_external.pot +++ b/l10n/templates/files_external.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud Core 6.0.0\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2014-03-27 01:55-0400\n" +"POT-Creation-Date: 2014-03-31 01:55-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -37,24 +37,24 @@ msgstr "" msgid "Error configuring Google Drive storage" msgstr "" -#: js/settings.js:313 js/settings.js:320 +#: js/settings.js:318 js/settings.js:325 msgid "Saved" msgstr "" -#: lib/config.php:646 +#: lib/config.php:654 msgid "" "<b>Warning:</b> \"smbclient\" is not installed. Mounting of CIFS/SMB shares " "is not possible. Please ask your system administrator to install it." msgstr "" -#: lib/config.php:650 +#: lib/config.php:658 msgid "" "<b>Warning:</b> The FTP support in PHP is not enabled or installed. Mounting " "of FTP shares is not possible. Please ask your system administrator to " "install it." msgstr "" -#: lib/config.php:653 +#: lib/config.php:661 msgid "" "<b>Warning:</b> The Curl support in PHP is not enabled or installed. " "Mounting of ownCloud / WebDAV or GoogleDrive is not possible. Please ask " @@ -89,39 +89,39 @@ msgstr "" msgid "Add storage" msgstr "" -#: templates/settings.php:90 +#: templates/settings.php:93 msgid "None set" msgstr "" -#: templates/settings.php:91 +#: templates/settings.php:94 msgid "All Users" msgstr "" -#: templates/settings.php:92 +#: templates/settings.php:95 msgid "Groups" msgstr "" -#: templates/settings.php:100 +#: templates/settings.php:103 msgid "Users" msgstr "" -#: templates/settings.php:113 templates/settings.php:114 -#: templates/settings.php:155 templates/settings.php:156 +#: templates/settings.php:116 templates/settings.php:117 +#: templates/settings.php:158 templates/settings.php:159 msgid "Delete" msgstr "" -#: templates/settings.php:127 +#: templates/settings.php:130 msgid "Enable User External Storage" msgstr "" -#: templates/settings.php:130 +#: templates/settings.php:133 msgid "Allow users to mount the following external storage" msgstr "" -#: templates/settings.php:147 +#: templates/settings.php:150 msgid "SSL root certificates" msgstr "" -#: templates/settings.php:165 +#: templates/settings.php:168 msgid "Import Root Certificate" msgstr "" diff --git a/l10n/templates/files_sharing.pot b/l10n/templates/files_sharing.pot index 2f8678ef6e2a07d4ee2ec7bce38491e6627284a9..8fbff462614e0e8cf3cb60c0ccb16992f950aace 100644 --- a/l10n/templates/files_sharing.pot +++ b/l10n/templates/files_sharing.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud Core 6.0.0\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2014-03-27 01:55-0400\n" +"POT-Creation-Date: 2014-03-31 01:55-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" diff --git a/l10n/templates/files_trashbin.pot b/l10n/templates/files_trashbin.pot index d4dcc61a85522cb9ee25e458b4c47e8db59f1b3f..f9b4401c785d047d7a6afc09a25115c1f2bb2ec4 100644 --- a/l10n/templates/files_trashbin.pot +++ b/l10n/templates/files_trashbin.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud Core 6.0.0\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2014-03-27 01:55-0400\n" +"POT-Creation-Date: 2014-03-31 01:55-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -31,7 +31,7 @@ msgstr "" msgid "Deleted files" msgstr "" -#: js/trash.js:16 js/trash.js:103 js/trash.js:152 +#: js/trash.js:16 js/trash.js:108 js/trash.js:157 msgid "Error" msgstr "" diff --git a/l10n/templates/files_versions.pot b/l10n/templates/files_versions.pot index 5bae82bfe6995dc40da0ae624cabdf0c5f4ad492..3381aea0a7d6c50936b639bc573cd8efded0f703 100644 --- a/l10n/templates/files_versions.pot +++ b/l10n/templates/files_versions.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud Core 6.0.0\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2014-03-27 01:55-0400\n" +"POT-Creation-Date: 2014-03-31 01:55-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -22,22 +22,22 @@ msgstr "" msgid "Could not revert: %s" msgstr "" -#: js/versions.js:14 +#: js/versions.js:39 msgid "Versions" msgstr "" -#: js/versions.js:60 +#: js/versions.js:61 msgid "Failed to revert {file} to revision {timestamp}." msgstr "" -#: js/versions.js:87 +#: js/versions.js:88 msgid "More versions..." msgstr "" -#: js/versions.js:125 +#: js/versions.js:126 msgid "No other versions available" msgstr "" -#: js/versions.js:155 +#: js/versions.js:156 msgid "Restore" msgstr "" diff --git a/l10n/templates/lib.pot b/l10n/templates/lib.pot index 90e076435a35bfb75653ffa755b1bc994599c91a..10017472d1b75c6b4a8438109ea6eb0cdeab871e 100644 --- a/l10n/templates/lib.pot +++ b/l10n/templates/lib.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud Core 6.0.0\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2014-03-27 01:55-0400\n" +"POT-Creation-Date: 2014-03-31 01:55-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -66,23 +66,23 @@ msgstr "" msgid "web services under your control" msgstr "" -#: private/files.php:231 +#: private/files.php:232 msgid "ZIP download is turned off." msgstr "" -#: private/files.php:232 +#: private/files.php:233 msgid "Files need to be downloaded one by one." msgstr "" -#: private/files.php:233 private/files.php:261 +#: private/files.php:234 private/files.php:262 msgid "Back to Files" msgstr "" -#: private/files.php:258 +#: private/files.php:259 msgid "Selected files too large to generate zip file." msgstr "" -#: private/files.php:259 +#: private/files.php:260 msgid "" "Please download the files separately in smaller chunks or kindly ask your " "administrator." diff --git a/l10n/templates/private.pot b/l10n/templates/private.pot index 2925d3cdfdd7ccafe0393546a935539d2b228efb..12fa6ad93081ef2b6bb955a151d7a30daa1acc23 100644 --- a/l10n/templates/private.pot +++ b/l10n/templates/private.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud Core 6.0.0\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2014-03-27 01:55-0400\n" +"POT-Creation-Date: 2014-03-31 01:55-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -66,23 +66,23 @@ msgstr "" msgid "web services under your control" msgstr "" -#: files.php:231 +#: files.php:232 msgid "ZIP download is turned off." msgstr "" -#: files.php:232 +#: files.php:233 msgid "Files need to be downloaded one by one." msgstr "" -#: files.php:233 files.php:261 +#: files.php:234 files.php:262 msgid "Back to Files" msgstr "" -#: files.php:258 +#: files.php:259 msgid "Selected files too large to generate zip file." msgstr "" -#: files.php:259 +#: files.php:260 msgid "" "Please download the files separately in smaller chunks or kindly ask your " "administrator." diff --git a/l10n/templates/settings.pot b/l10n/templates/settings.pot index 2c810d328e0f543c1119cfbfeedf89c4c5fccf94..892d64a2733cdbdfc127312a0bd0eca6442ebabf 100644 --- a/l10n/templates/settings.pot +++ b/l10n/templates/settings.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud Core 6.0.0\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2014-03-27 01:55-0400\n" +"POT-Creation-Date: 2014-03-31 01:55-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -64,7 +64,7 @@ msgid "Unable to load list from App Store" msgstr "" #: ajax/changedisplayname.php:25 ajax/removeuser.php:15 ajax/setquota.php:17 -#: ajax/togglegroups.php:20 changepassword/controller.php:55 +#: ajax/togglegroups.php:20 changepassword/controller.php:49 msgid "Authentication error" msgstr "" @@ -126,31 +126,31 @@ msgstr "" msgid "Couldn't update app." msgstr "" -#: changepassword/controller.php:20 +#: changepassword/controller.php:17 msgid "Wrong password" msgstr "" -#: changepassword/controller.php:42 +#: changepassword/controller.php:36 msgid "No user supplied" msgstr "" -#: changepassword/controller.php:74 +#: changepassword/controller.php:68 msgid "" "Please provide an admin recovery password, otherwise all user data will be " "lost" msgstr "" -#: changepassword/controller.php:79 +#: changepassword/controller.php:73 msgid "Wrong admin recovery password. Please check the password and try again." msgstr "" -#: changepassword/controller.php:87 +#: changepassword/controller.php:81 msgid "" "Back-end doesn't support password change, but the users encryption key was " "successfully updated." msgstr "" -#: changepassword/controller.php:92 changepassword/controller.php:103 +#: changepassword/controller.php:86 changepassword/controller.php:97 msgid "Unable to change password" msgstr "" @@ -283,7 +283,7 @@ msgstr "" msgid "Warning: Home directory for user \"{user}\" already exists" msgstr "" -#: personal.php:49 personal.php:50 +#: personal.php:48 personal.php:49 msgid "__language_name__" msgstr "" diff --git a/l10n/templates/user_ldap.pot b/l10n/templates/user_ldap.pot index 73d07c3e4d25a8e2a10ae60e6429c052a2ab7da4..d16fe0386eb0c13cf7c775b3b89a4246a32e1a24 100644 --- a/l10n/templates/user_ldap.pot +++ b/l10n/templates/user_ldap.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud Core 6.0.0\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2014-03-27 01:55-0400\n" +"POT-Creation-Date: 2014-03-31 01:55-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" diff --git a/l10n/templates/user_webdavauth.pot b/l10n/templates/user_webdavauth.pot index a01f6858bc6f45a1003d3fafdf7e5a714cd6e9ab..d2e917267ce154db840cf72e6821bc25d0c08328 100644 --- a/l10n/templates/user_webdavauth.pot +++ b/l10n/templates/user_webdavauth.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud Core 6.0.0\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2014-03-27 01:55-0400\n" +"POT-Creation-Date: 2014-03-31 01:55-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" diff --git a/l10n/tr/core.po b/l10n/tr/core.po index 177ac786519540450da8df9a8ccf0e4941a1010d..09a1e3d278e863c0487d8aaba6e550a28f626b76 100644 --- a/l10n/tr/core.po +++ b/l10n/tr/core.po @@ -11,8 +11,8 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2014-03-24 01:55-0400\n" -"PO-Revision-Date: 2014-03-23 13:20+0000\n" +"POT-Creation-Date: 2014-03-28 01:55-0400\n" +"PO-Revision-Date: 2014-03-27 19:31+0000\n" "Last-Translator: volkangezer <volkangezer@gmail.com>\n" "Language-Team: Turkish (http://www.transifex.com/projects/p/owncloud/language/tr/)\n" "MIME-Version: 1.0\n" @@ -490,7 +490,7 @@ msgid "" "The link to reset your password has been sent to your email.<br>If you do " "not receive it within a reasonable amount of time, check your spam/junk " "folders.<br>If it is not there ask your local administrator ." -msgstr "Parolanızı deÄŸiÅŸtirme baÄŸlantısı e-posta adresinize gönderildi.<br>EÄŸer makül bir süre içerisinde mesajı almadıysanız spam/junk dizinini kontrol ediniz.<br> EÄŸer orada da bulamazsanız sistem yöneticinize sorunuz." +msgstr "Parolanızı deÄŸiÅŸtirme baÄŸlantısı e-posta adresinize gönderildi.<br>EÄŸer makül bir süre içerisinde mesajı almadıysanız spam/junk/gereksiz dizinini kontrol ediniz.<br> EÄŸer yine bulamazsanız sistem yöneticinize sorunuz." #: lostpassword/templates/lostpassword.php:15 msgid "Request failed!<br>Did you make sure your email/username was right?" diff --git a/l10n/tr/settings.po b/l10n/tr/settings.po index 9cf4c3306ca11749d0bd9e5a1b329f5f00db26bc..cb9da3017de72c5a90b082e768b47d3bb2ae2acd 100644 --- a/l10n/tr/settings.po +++ b/l10n/tr/settings.po @@ -11,9 +11,9 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2014-03-26 01:55-0400\n" -"PO-Revision-Date: 2014-03-26 05:55+0000\n" -"Last-Translator: I Robot\n" +"POT-Creation-Date: 2014-03-28 01:55-0400\n" +"PO-Revision-Date: 2014-03-27 19:31+0000\n" +"Last-Translator: volkangezer <volkangezer@gmail.com>\n" "Language-Team: Turkish (http://www.transifex.com/projects/p/owncloud/language/tr/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -433,18 +433,18 @@ msgstr "Cron" #: templates/admin.php:167 #, php-format msgid "Last cron was executed at %s." -msgstr "" +msgstr "Son cron %s zamanında çalıştırıldı." #: templates/admin.php:170 #, php-format msgid "" "Last cron was executed at %s. This is more than an hour ago, something seems" " wrong." -msgstr "" +msgstr "Son cron %s zamanında çalıştırıldı. Bu bir saatten daha uzun bir süre, bir ÅŸeyler yanlış gibi görünüyor." #: templates/admin.php:174 msgid "Cron was not executed yet!" -msgstr "" +msgstr "Cron henüz çalıştırılmadı!" #: templates/admin.php:184 msgid "Execute one task with each page loaded" diff --git a/lib/base.php b/lib/base.php index 2515b9657cbc8e5b5af626340ea238c83bbc42ac..15a3ec8bc8aca22dce7af30dca55b9bf60989c06 100644 --- a/lib/base.php +++ b/lib/base.php @@ -549,16 +549,10 @@ class OC { OC_User::logout(); } - // Load Apps - // This includes plugins for users and filesystems as well - global $RUNTIME_NOAPPS; - global $RUNTIME_APPTYPES; - if (!$RUNTIME_NOAPPS && !self::checkUpgrade(false)) { - if ($RUNTIME_APPTYPES) { - OC_App::loadApps($RUNTIME_APPTYPES); - } else { - OC_App::loadApps(); - } + // Load minimum set of apps - which is filesystem, authentication and logging + if (!self::checkUpgrade(false)) { + OC_App::loadApps(array('authentication')); + OC_App::loadApps(array('filesystem', 'logging')); } //setup extra user backends @@ -659,10 +653,10 @@ class OC { */ public static function registerShareHooks() { if (\OC_Config::getValue('installed')) { - OC_Hook::connect('OC_User', 'post_deleteUser', 'OCP\Share', 'post_deleteUser'); - OC_Hook::connect('OC_User', 'post_addToGroup', 'OCP\Share', 'post_addToGroup'); - OC_Hook::connect('OC_User', 'post_removeFromGroup', 'OCP\Share', 'post_removeFromGroup'); - OC_Hook::connect('OC_User', 'post_deleteGroup', 'OCP\Share', 'post_deleteGroup'); + OC_Hook::connect('OC_User', 'post_deleteUser', 'OC\Share\Hooks', 'post_deleteUser'); + OC_Hook::connect('OC_User', 'post_addToGroup', 'OC\Share\Hooks', 'post_addToGroup'); + OC_Hook::connect('OC_User', 'post_removeFromGroup', 'OC\Share\Hooks', 'post_removeFromGroup'); + OC_Hook::connect('OC_User', 'post_deleteGroup', 'OC\Share\Hooks', 'post_deleteGroup'); } } @@ -851,7 +845,7 @@ class OC { ) { return false; } - OC_App::loadApps(array('authentication')); + if (defined("DEBUG") && DEBUG) { OC_Log::write('core', 'Trying to login from cookie', OC_Log::DEBUG); } @@ -923,7 +917,7 @@ class OC { ) { return false; } - OC_App::loadApps(array('authentication')); + if (OC_User::login($_SERVER["PHP_AUTH_USER"], $_SERVER["PHP_AUTH_PW"])) { //OC_Log::write('core',"Logged in with HTTP Authentication", OC_Log::DEBUG); OC_User::unsetMagicInCookie(); @@ -934,11 +928,6 @@ class OC { } -// define runtime variables - unless this already has been done -if (!isset($RUNTIME_NOAPPS)) { - $RUNTIME_NOAPPS = false; -} - if (!function_exists('get_temp_dir')) { function get_temp_dir() { if ($temp = ini_get('upload_tmp_dir')) return $temp; @@ -957,4 +946,3 @@ if (!function_exists('get_temp_dir')) { } OC::init(); - diff --git a/lib/private/cache/file.php b/lib/private/cache/file.php index 8a6ef39f61bb8061e51184ee06916781ebe5257c..be6805a9a57aea7e95419e38382e1f498f7b1fbc 100644 --- a/lib/private/cache/file.php +++ b/lib/private/cache/file.php @@ -1,6 +1,7 @@ <?php /** * Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl> + * Copyright (c) 2014 Vincent Petry <pvince81@owncloud.com> * This file is licensed under the Affero General Public License version 3 or * later. * See the COPYING-README file. @@ -10,22 +11,22 @@ namespace OC\Cache; class File { protected $storage; + + /** + * Returns the cache storage for the logged in user + * @return cache storage + */ protected function getStorage() { if (isset($this->storage)) { return $this->storage; } if(\OC_User::isLoggedIn()) { \OC\Files\Filesystem::initMountPoints(\OC_User::getUser()); - $subdir = 'cache'; - $view = new \OC\Files\View('/' . \OC_User::getUser()); - if(!$view->file_exists($subdir)) { - $view->mkdir($subdir); - } - $this->storage = new \OC\Files\View('/' . \OC_User::getUser().'/'.$subdir); + $this->storage = new \OC\Files\View('/' . \OC_User::getUser() . '/cache'); return $this->storage; }else{ \OC_Log::write('core', 'Can\'t get cache storage, user not logged in', \OC_Log::ERROR); - return false; + throw new \OC\ForbiddenException('Can\t get cache storage, user not logged in'); } } diff --git a/lib/private/db/statementwrapper.php b/lib/private/db/statementwrapper.php index eaf215c72313be1a4cbc9f9457a8036209cc9384..492209b883bd94134cacd548dcb308ef0937c730 100644 --- a/lib/private/db/statementwrapper.php +++ b/lib/private/db/statementwrapper.php @@ -8,6 +8,11 @@ /** * small wrapper around \Doctrine\DBAL\Driver\Statement to make it behave, more like an MDB2 Statement + * + * @method boolean bindValue(mixed $param, mixed $value, integer $type = null); + * @method string errorCode(); + * @method array errorInfo(); + * @method integer rowCount(); */ class OC_DB_StatementWrapper { /** @@ -161,6 +166,8 @@ class OC_DB_StatementWrapper { /** * provide an alias for fetch + * + * @return mixed */ public function fetchRow() { return $this->statement->fetch(); @@ -168,12 +175,13 @@ class OC_DB_StatementWrapper { /** * Provide a simple fetchOne. + * * fetch single column from the next row - * @param int $colnum the column number to fetch + * @param int $column the column number to fetch * @return string */ - public function fetchOne($colnum = 0) { - return $this->statement->fetchColumn($colnum); + public function fetchOne($column = 0) { + return $this->statement->fetchColumn($column); } /** diff --git a/lib/private/files/filesystem.php b/lib/private/files/filesystem.php index c31e0c381805812332bfaa6b9d5acf01a226c7fb..7e27650c5574d80bb57ac9b38d47afbe032e05ff 100644 --- a/lib/private/files/filesystem.php +++ b/lib/private/files/filesystem.php @@ -321,10 +321,35 @@ class Filesystem { self::mount('\OC\Files\Storage\Local', array('datadir' => $root), $user); } + self::mountCacheDir($user); + // Chance to mount for other storages \OC_Hook::emit('OC_Filesystem', 'post_initMountPoints', array('user' => $user, 'user_dir' => $root)); } + /** + * Mounts the cache directory + * @param string $user user name + */ + private static function mountCacheDir($user) { + $cacheBaseDir = \OC_Config::getValue('cache_path', ''); + if ($cacheBaseDir === '') { + // use local cache dir relative to the user's home + $subdir = 'cache'; + $view = new \OC\Files\View('/' . $user); + if(!$view->file_exists($subdir)) { + $view->mkdir($subdir); + } + } else { + $cacheDir = rtrim($cacheBaseDir, '/') . '/' . $user; + if (!file_exists($cacheDir)) { + mkdir($cacheDir, 0770, true); + } + // mount external cache dir to "/$user/cache" mount point + self::mount('\OC\Files\Storage\Local', array('datadir' => $cacheDir), '/' . $user . '/cache'); + } + } + /** * get the default filesystem view * diff --git a/lib/private/files/storage/common.php b/lib/private/files/storage/common.php index 3c078d7b1b4b8f9a6f5924d3cf5855b7df06d11b..2b697141515655801dae3b75fcd2aa583952156e 100644 --- a/lib/private/files/storage/common.php +++ b/lib/private/files/storage/common.php @@ -160,8 +160,7 @@ abstract class Common implements \OC\Files\Storage\Storage { public function hash($type, $path, $raw = false) { $tmpFile = $this->getLocalFile($path); - $hash = hash($type, $tmpFile, $raw); - unlink($tmpFile); + $hash = hash_file($type, $tmpFile, $raw); return $hash; } diff --git a/lib/private/files/storage/local.php b/lib/private/files/storage/local.php index 0f906ec55b4e1be61db6df16e9baf7255ca698bd..571bf7f97c1af3deead5280dc167abd66c4a039f 100644 --- a/lib/private/files/storage/local.php +++ b/lib/private/files/storage/local.php @@ -256,7 +256,7 @@ if (\OC_Util::runningOnWindows()) { return 0; } - public function hash($path, $type, $raw = false) { + public function hash($type, $path, $raw = false) { return hash_file($type, $this->datadir . $path, $raw); } diff --git a/lib/private/files/storage/mappedlocal.php b/lib/private/files/storage/mappedlocal.php index 026f6ec895ed7f68eddc4704bd1a86b8244d3276..94ee28ca763c2c5340811cf92a498728ea060c36 100644 --- a/lib/private/files/storage/mappedlocal.php +++ b/lib/private/files/storage/mappedlocal.php @@ -276,7 +276,7 @@ class MappedLocal extends \OC\Files\Storage\Common{ return 0; } - public function hash($path, $type, $raw=false) { + public function hash($type, $path, $raw=false) { return hash_file($type, $this->buildPath($path), $raw); } diff --git a/lib/private/forbiddenexception.php b/lib/private/forbiddenexception.php new file mode 100644 index 0000000000000000000000000000000000000000..14a4cd14984cd83020061e6014c822e8f808411a --- /dev/null +++ b/lib/private/forbiddenexception.php @@ -0,0 +1,16 @@ +<?php +/** + * Copyright (c) 2014 Vincent Petry <pvince81@owncloud.com> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC; + +/** + * Exception thrown whenever access to a resource has + * been forbidden or whenever a user isn't authenticated. + */ +class ForbiddenException extends \Exception { +} diff --git a/lib/private/helper.php b/lib/private/helper.php index 98a86388d2088d71d6b691f9d5b8434c7dda3155..d7ac0b5f4fa64846adad31d58c9c6ed960a3653d 100644 --- a/lib/private/helper.php +++ b/lib/private/helper.php @@ -78,8 +78,7 @@ class OC_Helper { * Returns a absolute url to the given app and file. */ public static function linkToAbsolute($app, $file, $args = array()) { - $urlLinkTo = self::linkTo($app, $file, $args); - return self::makeURLAbsolute($urlLinkTo); + return self::linkTo($app, $file, $args); } /** diff --git a/lib/private/share/constants.php b/lib/private/share/constants.php new file mode 100644 index 0000000000000000000000000000000000000000..7e4223d10fae18c5a7b9e27097e6aee6c0d4c315 --- /dev/null +++ b/lib/private/share/constants.php @@ -0,0 +1,44 @@ +<?php +/** + * ownCloud + * + * @author Bjoern Schiessle + * @copyright 2014 Bjoern Schiessle <schiessle@owncloud.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + */ + +namespace OC\Share; + +class Constants { + + const SHARE_TYPE_USER = 0; + const SHARE_TYPE_GROUP = 1; + const SHARE_TYPE_LINK = 3; + const SHARE_TYPE_EMAIL = 4; + const SHARE_TYPE_CONTACT = 5; + const SHARE_TYPE_REMOTE = 6; + + const FORMAT_NONE = -1; + const FORMAT_STATUSES = -2; + const FORMAT_SOURCES = -3; + + const TOKEN_LENGTH = 32; // see db_structure.xml + + protected static $shareTypeUserAndGroups = -1; + protected static $shareTypeGroupUserUnique = 2; + protected static $backends = array(); + protected static $backendTypes = array(); + protected static $isResharingAllowed; +} diff --git a/lib/private/share/helper.php b/lib/private/share/helper.php new file mode 100644 index 0000000000000000000000000000000000000000..fde55667281214a39e49454931362a2bf0c646d9 --- /dev/null +++ b/lib/private/share/helper.php @@ -0,0 +1,202 @@ +<?php +/** + * ownCloud + * + * @author Bjoern Schiessle + * @copyright 2014 Bjoern Schiessle <schiessle@owncloud.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + */ + +namespace OC\Share; + +class Helper extends \OC\Share\Constants { + + /** + * Generate a unique target for the item + * @param string Item type + * @param string Item source + * @param int SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK + * @param string User or group the item is being shared with + * @param string User that is the owner of shared item + * @param string The suggested target originating from a reshare (optional) + * @param int The id of the parent group share (optional) + * @return string Item target + */ + public static function generateTarget($itemType, $itemSource, $shareType, $shareWith, $uidOwner, + $suggestedTarget = null, $groupParent = null) { + $backend = \OC\Share\Share::getBackend($itemType); + if ($shareType == self::SHARE_TYPE_LINK) { + if (isset($suggestedTarget)) { + return $suggestedTarget; + } + return $backend->generateTarget($itemSource, false); + } else { + if ($itemType == 'file' || $itemType == 'folder') { + $column = 'file_target'; + $columnSource = 'file_source'; + } else { + $column = 'item_target'; + $columnSource = 'item_source'; + } + if ($shareType == self::SHARE_TYPE_USER) { + // Share with is a user, so set share type to user and groups + $shareType = self::$shareTypeUserAndGroups; + $userAndGroups = array_merge(array($shareWith), \OC_Group::getUserGroups($shareWith)); + } else { + $userAndGroups = false; + } + $exclude = null; + // Backend has 3 opportunities to generate a unique target + for ($i = 0; $i < 2; $i++) { + // Check if suggested target exists first + if ($i == 0 && isset($suggestedTarget)) { + $target = $suggestedTarget; + } else { + if ($shareType == self::SHARE_TYPE_GROUP) { + $target = $backend->generateTarget($itemSource, false, $exclude); + } else { + $target = $backend->generateTarget($itemSource, $shareWith, $exclude); + } + if (is_array($exclude) && in_array($target, $exclude)) { + break; + } + } + // Check if target already exists + $checkTarget = \OC\Share\Share::getItems($itemType, $target, $shareType, $shareWith); + if (!empty($checkTarget)) { + foreach ($checkTarget as $item) { + // Skip item if it is the group parent row + if (isset($groupParent) && $item['id'] == $groupParent) { + if (count($checkTarget) == 1) { + return $target; + } else { + continue; + } + } + if ($item['uid_owner'] == $uidOwner) { + if ($itemType == 'file' || $itemType == 'folder') { + $meta = \OC\Files\Filesystem::getFileInfo($itemSource); + if ($item['file_source'] == $meta['fileid']) { + return $target; + } + } else if ($item['item_source'] == $itemSource) { + return $target; + } + } + } + if (!isset($exclude)) { + $exclude = array(); + } + // Find similar targets to improve backend's chances to generate a unqiue target + if ($userAndGroups) { + if ($column == 'file_target') { + $checkTargets = \OC_DB::prepare('SELECT `'.$column.'` FROM `*PREFIX*share`' + .' WHERE `item_type` IN (\'file\', \'folder\')' + .' AND `share_type` IN (?,?,?)' + .' AND `share_with` IN (\''.implode('\',\'', $userAndGroups).'\')'); + $result = $checkTargets->execute(array(self::SHARE_TYPE_USER, self::SHARE_TYPE_GROUP, + self::$shareTypeGroupUserUnique)); + } else { + $checkTargets = \OC_DB::prepare('SELECT `'.$column.'` FROM `*PREFIX*share`' + .' WHERE `item_type` = ? AND `share_type` IN (?,?,?)' + .' AND `share_with` IN (\''.implode('\',\'', $userAndGroups).'\')'); + $result = $checkTargets->execute(array($itemType, self::SHARE_TYPE_USER, + self::SHARE_TYPE_GROUP, self::$shareTypeGroupUserUnique)); + } + } else { + if ($column == 'file_target') { + $checkTargets = \OC_DB::prepare('SELECT `'.$column.'` FROM `*PREFIX*share`' + .' WHERE `item_type` IN (\'file\', \'folder\')' + .' AND `share_type` = ? AND `share_with` = ?'); + $result = $checkTargets->execute(array(self::SHARE_TYPE_GROUP, $shareWith)); + } else { + $checkTargets = \OC_DB::prepare('SELECT `'.$column.'` FROM `*PREFIX*share`' + .' WHERE `item_type` = ? AND `share_type` = ? AND `share_with` = ?'); + $result = $checkTargets->execute(array($itemType, self::SHARE_TYPE_GROUP, $shareWith)); + } + } + while ($row = $result->fetchRow()) { + $exclude[] = $row[$column]; + } + } else { + return $target; + } + } + } + $message = 'Sharing backend registered for '.$itemType.' did not generate a unique target for '.$itemSource; + \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); + throw new \Exception($message); + } + + /** + * Delete all reshares of an item + * @param int Id of item to delete + * @param bool If true, exclude the parent from the delete (optional) + * @param string The user that the parent was shared with (optinal) + */ + public static function delete($parent, $excludeParent = false, $uidOwner = null) { + $ids = array($parent); + $parents = array($parent); + while (!empty($parents)) { + $parents = "'".implode("','", $parents)."'"; + // Check the owner on the first search of reshares, useful for + // finding and deleting the reshares by a single user of a group share + if (count($ids) == 1 && isset($uidOwner)) { + $query = \OC_DB::prepare('SELECT `id`, `uid_owner`, `item_type`, `item_target`, `parent`' + .' FROM `*PREFIX*share` WHERE `parent` IN ('.$parents.') AND `uid_owner` = ?'); + $result = $query->execute(array($uidOwner)); + } else { + $query = \OC_DB::prepare('SELECT `id`, `item_type`, `item_target`, `parent`, `uid_owner`' + .' FROM `*PREFIX*share` WHERE `parent` IN ('.$parents.')'); + $result = $query->execute(); + } + // Reset parents array, only go through loop again if items are found + $parents = array(); + while ($item = $result->fetchRow()) { + // Search for a duplicate parent share, this occurs when an + // item is shared to the same user through a group and user or the + // same item is shared by different users + $userAndGroups = array_merge(array($item['uid_owner']), \OC_Group::getUserGroups($item['uid_owner'])); + $query = \OC_DB::prepare('SELECT `id`, `permissions` FROM `*PREFIX*share`' + .' WHERE `item_type` = ?' + .' AND `item_target` = ?' + .' AND `share_type` IN (?,?,?)' + .' AND `share_with` IN (\''.implode('\',\'', $userAndGroups).'\')' + .' AND `uid_owner` != ? AND `id` != ?'); + $duplicateParent = $query->execute(array($item['item_type'], $item['item_target'], + self::SHARE_TYPE_USER, self::SHARE_TYPE_GROUP, self::$shareTypeGroupUserUnique, + $item['uid_owner'], $item['parent']))->fetchRow(); + if ($duplicateParent) { + // Change the parent to the other item id if share permission is granted + if ($duplicateParent['permissions'] & \OCP\PERMISSION_SHARE) { + $query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `parent` = ? WHERE `id` = ?'); + $query->execute(array($duplicateParent['id'], $item['id'])); + continue; + } + } + $ids[] = $item['id']; + $parents[] = $item['id']; + } + } + if ($excludeParent) { + unset($ids[0]); + } + if (!empty($ids)) { + $ids = "'".implode("','", $ids)."'"; + $query = \OC_DB::prepare('DELETE FROM `*PREFIX*share` WHERE `id` IN ('.$ids.')'); + $query->execute(); + } + } +} diff --git a/lib/private/share/hooks.php b/lib/private/share/hooks.php new file mode 100644 index 0000000000000000000000000000000000000000..a33c71eedd2f80baf04a6907166a54b2a8b00a0a --- /dev/null +++ b/lib/private/share/hooks.php @@ -0,0 +1,108 @@ +<?php +/** + * ownCloud + * + * @author Bjoern Schiessle + * @copyright 2014 Bjoern Schiessle <schiessle@owncloud.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + */ + +namespace OC\Share; + +class Hooks extends \OC\Share\Constants { + /** + * Function that is called after a user is deleted. Cleans up the shares of that user. + * @param array arguments + */ + public static function post_deleteUser($arguments) { + // Delete any items shared with the deleted user + $query = \OC_DB::prepare('DELETE FROM `*PREFIX*share`' + .' WHERE `share_with` = ? AND `share_type` = ? OR `share_type` = ?'); + $result = $query->execute(array($arguments['uid'], self::SHARE_TYPE_USER, self::$shareTypeGroupUserUnique)); + // Delete any items the deleted user shared + $query = \OC_DB::prepare('SELECT `id` FROM `*PREFIX*share` WHERE `uid_owner` = ?'); + $result = $query->execute(array($arguments['uid'])); + while ($item = $result->fetchRow()) { + Helper::delete($item['id']); + } + } + + /** + * Function that is called after a user is added to a group. + * TODO what does it do? + * @param array arguments + */ + public static function post_addToGroup($arguments) { + // Find the group shares and check if the user needs a unique target + $query = \OC_DB::prepare('SELECT * FROM `*PREFIX*share` WHERE `share_type` = ? AND `share_with` = ?'); + $result = $query->execute(array(self::SHARE_TYPE_GROUP, $arguments['gid'])); + $query = \OC_DB::prepare('INSERT INTO `*PREFIX*share` (`item_type`, `item_source`,' + .' `item_target`, `parent`, `share_type`, `share_with`, `uid_owner`, `permissions`,' + .' `stime`, `file_source`, `file_target`) VALUES (?,?,?,?,?,?,?,?,?,?,?)'); + while ($item = $result->fetchRow()) { + if ($item['item_type'] == 'file' || $item['item_type'] == 'file') { + $itemTarget = null; + } else { + $itemTarget = Helper::generateTarget($item['item_type'], $item['item_source'], self::SHARE_TYPE_USER, + $arguments['uid'], $item['uid_owner'], $item['item_target'], $item['id']); + } + if (isset($item['file_source'])) { + $fileTarget = Helper::generateTarget($item['item_type'], $item['item_source'], self::SHARE_TYPE_USER, + $arguments['uid'], $item['uid_owner'], $item['file_target'], $item['id']); + } else { + $fileTarget = null; + } + // Insert an extra row for the group share if the item or file target is unique for this user + if ($itemTarget != $item['item_target'] || $fileTarget != $item['file_target']) { + $query->execute(array($item['item_type'], $item['item_source'], $itemTarget, $item['id'], + self::$shareTypeGroupUserUnique, $arguments['uid'], $item['uid_owner'], $item['permissions'], + $item['stime'], $item['file_source'], $fileTarget)); + \OC_DB::insertid('*PREFIX*share'); + } + } + } + + /** + * Function that is called after a user is removed from a group. Shares are cleaned up. + * @param array arguments + */ + public static function post_removeFromGroup($arguments) { + $sql = 'SELECT `id`, `share_type` FROM `*PREFIX*share`' + .' WHERE (`share_type` = ? AND `share_with` = ?) OR (`share_type` = ? AND `share_with` = ?)'; + $result = \OC_DB::executeAudited($sql, array(self::SHARE_TYPE_GROUP, $arguments['gid'], + self::$shareTypeGroupUserUnique, $arguments['uid'])); + while ($item = $result->fetchRow()) { + if ($item['share_type'] == self::SHARE_TYPE_GROUP) { + // Delete all reshares by this user of the group share + Helper::delete($item['id'], true, $arguments['uid']); + } else { + Helper::delete($item['id']); + } + } + } + + /** + * Function that is called after a group is removed. Cleans up the shares to that group. + * @param array arguments + */ + public static function post_deleteGroup($arguments) { + $sql = 'SELECT `id` FROM `*PREFIX*share` WHERE `share_type` = ? AND `share_with` = ?'; + $result = \OC_DB::executeAudited($sql, array(self::SHARE_TYPE_GROUP, $arguments['gid'])); + while ($item = $result->fetchRow()) { + Helper::delete($item['id']); + } + } + +} diff --git a/lib/private/share/share.php b/lib/private/share/share.php new file mode 100644 index 0000000000000000000000000000000000000000..8238797600ead3b19868395dac53f4b42a45916b --- /dev/null +++ b/lib/private/share/share.php @@ -0,0 +1,1619 @@ +<?php +/** + * ownCloud + * + * @author Bjoern Schiessle, Michael Gapczynski + * @copyright 2012 Michael Gapczynski <mtgap@owncloud.com> + * 2014 Bjoern Schiessle <schiessle@owncloud.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + */ + +namespace OC\Share; + +/** + * This class provides the ability for apps to share their content between users. + * Apps must create a backend class that implements OCP\Share_Backend and register it with this class. + * + * It provides the following hooks: + * - post_shared + */ +class Share extends \OC\Share\Constants { + + /** CRUDS permissions (Create, Read, Update, Delete, Share) using a bitmask + * Construct permissions for share() and setPermissions with Or (|) e.g. + * Give user read and update permissions: PERMISSION_READ | PERMISSION_UPDATE + * + * Check if permission is granted with And (&) e.g. Check if delete is + * granted: if ($permissions & PERMISSION_DELETE) + * + * Remove permissions with And (&) and Not (~) e.g. Remove the update + * permission: $permissions &= ~PERMISSION_UPDATE + * + * Apps are required to handle permissions on their own, this class only + * stores and manages the permissions of shares + * @see lib/public/constants.php + */ + + /** + * Register a sharing backend class that implements OCP\Share_Backend for an item type + * @param string Item type + * @param string Backend class + * @param string (optional) Depends on item type + * @param array (optional) List of supported file extensions if this item type depends on files + * @return Returns true if backend is registered or false if error + */ + public static function registerBackend($itemType, $class, $collectionOf = null, $supportedFileExtensions = null) { + if (self::isEnabled()) { + if (!isset(self::$backendTypes[$itemType])) { + self::$backendTypes[$itemType] = array( + 'class' => $class, + 'collectionOf' => $collectionOf, + 'supportedFileExtensions' => $supportedFileExtensions + ); + if(count(self::$backendTypes) === 1) { + \OC_Util::addScript('core', 'share'); + \OC_Util::addStyle('core', 'share'); + } + return true; + } + \OC_Log::write('OCP\Share', + 'Sharing backend '.$class.' not registered, '.self::$backendTypes[$itemType]['class'] + .' is already registered for '.$itemType, + \OC_Log::WARN); + } + return false; + } + + /** + * Check if the Share API is enabled + * @return Returns true if enabled or false + * + * The Share API is enabled by default if not configured + */ + public static function isEnabled() { + if (\OC_Appconfig::getValue('core', 'shareapi_enabled', 'yes') == 'yes') { + return true; + } + return false; + } + + /** + * Find which users can access a shared item + * @param $path to the file + * @param $user owner of the file + * @param include owner to the list of users with access to the file + * @return array + * @note $path needs to be relative to user data dir, e.g. 'file.txt' + * not '/admin/data/file.txt' + */ + public static function getUsersSharingFile($path, $user, $includeOwner = false) { + + $shares = array(); + $publicShare = false; + $source = -1; + $cache = false; + + $view = new \OC\Files\View('/' . $user . '/files'); + if ($view->file_exists($path)) { + $meta = $view->getFileInfo($path); + } else { + // if the file doesn't exists yet we start with the parent folder + $meta = $view->getFileInfo(dirname($path)); + } + + if($meta !== false) { + $source = $meta['fileid']; + $cache = new \OC\Files\Cache\Cache($meta['storage']); + } + + while ($source !== -1) { + + // Fetch all shares with another user + $query = \OC_DB::prepare( + 'SELECT `share_with` + FROM + `*PREFIX*share` + WHERE + `item_source` = ? AND `share_type` = ? AND `item_type` IN (\'file\', \'folder\')' + ); + + $result = $query->execute(array($source, self::SHARE_TYPE_USER)); + + if (\OCP\DB::isError($result)) { + \OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage($result), \OC_Log::ERROR); + } else { + while ($row = $result->fetchRow()) { + $shares[] = $row['share_with']; + } + } + // We also need to take group shares into account + + $query = \OC_DB::prepare( + 'SELECT `share_with` + FROM + `*PREFIX*share` + WHERE + `item_source` = ? AND `share_type` = ? AND `item_type` IN (\'file\', \'folder\')' + ); + + $result = $query->execute(array($source, self::SHARE_TYPE_GROUP)); + + if (\OCP\DB::isError($result)) { + \OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage($result), \OC_Log::ERROR); + } else { + while ($row = $result->fetchRow()) { + $usersInGroup = \OC_Group::usersInGroup($row['share_with']); + $shares = array_merge($shares, $usersInGroup); + } + } + + //check for public link shares + if (!$publicShare) { + $query = \OC_DB::prepare( + 'SELECT `share_with` + FROM + `*PREFIX*share` + WHERE + `item_source` = ? AND `share_type` = ? AND `item_type` IN (\'file\', \'folder\')' + ); + + $result = $query->execute(array($source, self::SHARE_TYPE_LINK)); + + if (\OCP\DB::isError($result)) { + \OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage($result), \OC_Log::ERROR); + } else { + if ($result->fetchRow()) { + $publicShare = true; + } + } + } + + // let's get the parent for the next round + $meta = $cache->get((int)$source); + if($meta !== false) { + $source = (int)$meta['parent']; + } else { + $source = -1; + } + } + // Include owner in list of users, if requested + if ($includeOwner) { + $shares[] = $user; + } + + return array("users" => array_unique($shares), "public" => $publicShare); + } + + /** + * Get the items of item type shared with the current user + * @param string Item type + * @param int Format (optional) Format type must be defined by the backend + * @param mixed Parameters (optional) + * @param int Number of items to return (optional) Returns all by default + * @param bool include collections (optional) + * @return Return depends on format + */ + public static function getItemsSharedWith($itemType, $format = self::FORMAT_NONE, + $parameters = null, $limit = -1, $includeCollections = false) { + return self::getItems($itemType, null, self::$shareTypeUserAndGroups, \OC_User::getUser(), null, $format, + $parameters, $limit, $includeCollections); + } + + /** + * Get the item of item type shared with the current user + * @param string $itemType + * @param string $itemTarget + * @param int $format (optional) Format type must be defined by the backend + * @param mixed Parameters (optional) + * @param bool include collections (optional) + * @return Return depends on format + */ + public static function getItemSharedWith($itemType, $itemTarget, $format = self::FORMAT_NONE, + $parameters = null, $includeCollections = false) { + return self::getItems($itemType, $itemTarget, self::$shareTypeUserAndGroups, \OC_User::getUser(), null, $format, + $parameters, 1, $includeCollections); + } + + /** + * Get the item of item type shared with a given user by source + * @param string $itemType + * @param string $itemSource + * @param string $user User user to whom the item was shared + * @return array Return list of items with file_target, permissions and expiration + */ + public static function getItemSharedWithUser($itemType, $itemSource, $user) { + + $shares = array(); + + // first check if there is a db entry for the specific user + $query = \OC_DB::prepare( + 'SELECT `file_target`, `permissions`, `expiration` + FROM + `*PREFIX*share` + WHERE + `item_source` = ? AND `item_type` = ? AND `share_with` = ?' + ); + + $result = \OC_DB::executeAudited($query, array($itemSource, $itemType, $user)); + + while ($row = $result->fetchRow()) { + $shares[] = $row; + } + + //if didn't found a result than let's look for a group share. + if(empty($shares)) { + $groups = \OC_Group::getUserGroups($user); + + $query = \OC_DB::prepare( + 'SELECT `file_target`, `permissions`, `expiration` + FROM + `*PREFIX*share` + WHERE + `item_source` = ? AND `item_type` = ? AND `share_with` in (?)' + ); + + $result = \OC_DB::executeAudited($query, array($itemSource, $itemType, implode(',', $groups))); + + while ($row = $result->fetchRow()) { + $shares[] = $row; + } + } + + return $shares; + + } + + /** + * Get the item of item type shared with the current user by source + * @param string Item type + * @param string Item source + * @param int Format (optional) Format type must be defined by the backend + * @param mixed Parameters + * @param bool include collections + * @return Return depends on format + */ + public static function getItemSharedWithBySource($itemType, $itemSource, $format = self::FORMAT_NONE, + $parameters = null, $includeCollections = false) { + return self::getItems($itemType, $itemSource, self::$shareTypeUserAndGroups, \OC_User::getUser(), null, $format, + $parameters, 1, $includeCollections, true); + } + + /** + * Get the item of item type shared by a link + * @param string Item type + * @param string Item source + * @param string Owner of link + * @return Item + */ + public static function getItemSharedWithByLink($itemType, $itemSource, $uidOwner) { + return self::getItems($itemType, $itemSource, self::SHARE_TYPE_LINK, null, $uidOwner, self::FORMAT_NONE, + null, 1); + } + + /** + * Based on the given token the share information will be returned - password protected shares will be verified + * @param string $token + * @return array | bool false will be returned in case the token is unknown or unauthorized + */ + public static function getShareByToken($token, $checkPasswordProtection = true) { + $query = \OC_DB::prepare('SELECT * FROM `*PREFIX*share` WHERE `token` = ?', 1); + $result = $query->execute(array($token)); + if (\OC_DB::isError($result)) { + \OC_Log::write('OCP\Share', \OC_DB::getErrorMessage($result) . ', token=' . $token, \OC_Log::ERROR); + } + $row = $result->fetchRow(); + if ($row === false) { + return false; + } + if (is_array($row) and self::expireItem($row)) { + return false; + } + + // password protected shares need to be authenticated + if ($checkPasswordProtection && !\OCP\Share::checkPasswordProtectedShare($row)) { + return false; + } + + return $row; + } + + /** + * resolves reshares down to the last real share + * @param $linkItem + * @return $fileOwner + */ + public static function resolveReShare($linkItem) + { + if (isset($linkItem['parent'])) { + $parent = $linkItem['parent']; + while (isset($parent)) { + $query = \OC_DB::prepare('SELECT * FROM `*PREFIX*share` WHERE `id` = ?', 1); + $item = $query->execute(array($parent))->fetchRow(); + if (isset($item['parent'])) { + $parent = $item['parent']; + } else { + return $item; + } + } + } + return $linkItem; + } + + + /** + * Get the shared items of item type owned by the current user + * @param string Item type + * @param int Format (optional) Format type must be defined by the backend + * @param mixed Parameters + * @param int Number of items to return (optional) Returns all by default + * @param bool include collections + * @return Return depends on format + */ + public static function getItemsShared($itemType, $format = self::FORMAT_NONE, $parameters = null, + $limit = -1, $includeCollections = false) { + return self::getItems($itemType, null, null, null, \OC_User::getUser(), $format, + $parameters, $limit, $includeCollections); + } + + /** + * Get the shared item of item type owned by the current user + * @param string Item type + * @param string Item source + * @param int Format (optional) Format type must be defined by the backend + * @param mixed Parameters + * @param bool include collections + * @return Return depends on format + */ + public static function getItemShared($itemType, $itemSource, $format = self::FORMAT_NONE, + $parameters = null, $includeCollections = false) { + return self::getItems($itemType, $itemSource, null, null, \OC_User::getUser(), $format, + $parameters, -1, $includeCollections); + } + + /** + * Get all users an item is shared with + * @param string Item type + * @param string Item source + * @param string Owner + * @param bool Include collections + * @praram bool check expire date + * @return Return array of users + */ + public static function getUsersItemShared($itemType, $itemSource, $uidOwner, $includeCollections = false, $checkExpireDate = true) { + + $users = array(); + $items = self::getItems($itemType, $itemSource, null, null, $uidOwner, self::FORMAT_NONE, null, -1, $includeCollections, false, $checkExpireDate); + if ($items) { + foreach ($items as $item) { + if ((int)$item['share_type'] === self::SHARE_TYPE_USER) { + $users[] = $item['share_with']; + } else if ((int)$item['share_type'] === self::SHARE_TYPE_GROUP) { + $users = array_merge($users, \OC_Group::usersInGroup($item['share_with'])); + } + } + } + return $users; + } + + /** + * Share an item with a user, group, or via private link + * @param string $itemType + * @param string $itemSource + * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK + * @param string $shareWith User or group the item is being shared with + * @param int $permissions CRUDS + * @param null $itemSourceName + * @throws \Exception + * @internal param \OCP\Item $string type + * @internal param \OCP\Item $string source + * @internal param \OCP\SHARE_TYPE_USER $int , SHARE_TYPE_GROUP, or SHARE_TYPE_LINK + * @internal param \OCP\User $string or group the item is being shared with + * @internal param \OCP\CRUDS $int permissions + * @return bool|string Returns true on success or false on failure, Returns token on success for links + */ + public static function shareItem($itemType, $itemSource, $shareType, $shareWith, $permissions, $itemSourceName = null) { + $uidOwner = \OC_User::getUser(); + $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global'); + + if (is_null($itemSourceName)) { + $itemSourceName = $itemSource; + } + + // Verify share type and sharing conditions are met + if ($shareType === self::SHARE_TYPE_USER) { + if ($shareWith == $uidOwner) { + $message = 'Sharing '.$itemSourceName.' failed, because the user '.$shareWith.' is the item owner'; + \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); + throw new \Exception($message); + } + if (!\OC_User::userExists($shareWith)) { + $message = 'Sharing '.$itemSourceName.' failed, because the user '.$shareWith.' does not exist'; + \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); + throw new \Exception($message); + } + if ($sharingPolicy == 'groups_only') { + $inGroup = array_intersect(\OC_Group::getUserGroups($uidOwner), \OC_Group::getUserGroups($shareWith)); + if (empty($inGroup)) { + $message = 'Sharing '.$itemSourceName.' failed, because the user ' + .$shareWith.' is not a member of any groups that '.$uidOwner.' is a member of'; + \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); + throw new \Exception($message); + } + } + // Check if the item source is already shared with the user, either from the same owner or a different user + if ($checkExists = self::getItems($itemType, $itemSource, self::$shareTypeUserAndGroups, + $shareWith, null, self::FORMAT_NONE, null, 1, true, true)) { + // Only allow the same share to occur again if it is the same + // owner and is not a user share, this use case is for increasing + // permissions for a specific user + if ($checkExists['uid_owner'] != $uidOwner || $checkExists['share_type'] == $shareType) { + $message = 'Sharing '.$itemSourceName.' failed, because this item is already shared with '.$shareWith; + \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); + throw new \Exception($message); + } + } + } else if ($shareType === self::SHARE_TYPE_GROUP) { + if (!\OC_Group::groupExists($shareWith)) { + $message = 'Sharing '.$itemSourceName.' failed, because the group '.$shareWith.' does not exist'; + \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); + throw new \Exception($message); + } + if ($sharingPolicy == 'groups_only' && !\OC_Group::inGroup($uidOwner, $shareWith)) { + $message = 'Sharing '.$itemSourceName.' failed, because ' + .$uidOwner.' is not a member of the group '.$shareWith; + \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); + throw new \Exception($message); + } + // Check if the item source is already shared with the group, either from the same owner or a different user + // The check for each user in the group is done inside the put() function + if ($checkExists = self::getItems($itemType, $itemSource, self::SHARE_TYPE_GROUP, $shareWith, + null, self::FORMAT_NONE, null, 1, true, true)) { + // Only allow the same share to occur again if it is the same + // owner and is not a group share, this use case is for increasing + // permissions for a specific user + if ($checkExists['uid_owner'] != $uidOwner || $checkExists['share_type'] == $shareType) { + $message = 'Sharing '.$itemSourceName.' failed, because this item is already shared with '.$shareWith; + \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); + throw new \Exception($message); + } + } + // Convert share with into an array with the keys group and users + $group = $shareWith; + $shareWith = array(); + $shareWith['group'] = $group; + $shareWith['users'] = array_diff(\OC_Group::usersInGroup($group), array($uidOwner)); + } else if ($shareType === self::SHARE_TYPE_LINK) { + if (\OC_Appconfig::getValue('core', 'shareapi_allow_links', 'yes') == 'yes') { + // when updating a link share + if ($checkExists = self::getItems($itemType, $itemSource, self::SHARE_TYPE_LINK, null, + $uidOwner, self::FORMAT_NONE, null, 1)) { + // remember old token + $oldToken = $checkExists['token']; + $oldPermissions = $checkExists['permissions']; + //delete the old share + Helper::delete($checkExists['id']); + } + + // Generate hash of password - same method as user passwords + if (isset($shareWith)) { + $forcePortable = (CRYPT_BLOWFISH != 1); + $hasher = new \PasswordHash(8, $forcePortable); + $shareWith = $hasher->HashPassword($shareWith.\OC_Config::getValue('passwordsalt', '')); + } else { + // reuse the already set password, but only if we change permissions + // otherwise the user disabled the password protection + if ($checkExists && (int)$permissions !== (int)$oldPermissions) { + $shareWith = $checkExists['share_with']; + } + } + + // Generate token + if (isset($oldToken)) { + $token = $oldToken; + } else { + $token = \OC_Util::generateRandomBytes(self::TOKEN_LENGTH); + } + $result = self::put($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions, + null, $token, $itemSourceName); + if ($result) { + return $token; + } else { + return false; + } + } + $message = 'Sharing '.$itemSourceName.' failed, because sharing with links is not allowed'; + \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); + throw new \Exception($message); + return false; + } else { + // Future share types need to include their own conditions + $message = 'Share type '.$shareType.' is not valid for '.$itemSource; + \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); + throw new \Exception($message); + } + // Put the item into the database + return self::put($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions, null, null, $itemSourceName); + } + + /** + * Unshare an item from a user, group, or delete a private link + * @param string Item type + * @param string Item source + * @param int SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK + * @param string User or group the item is being shared with + * @return Returns true on success or false on failure + */ + public static function unshare($itemType, $itemSource, $shareType, $shareWith) { + $item = self::getItems($itemType, $itemSource, $shareType, $shareWith, \OC_User::getUser(),self::FORMAT_NONE, null, 1); + if (!empty($item)) { + self::unshareItem($item); + return true; + } + return false; + } + + /** + * Unshare an item from all users, groups, and remove all links + * @param string Item type + * @param string Item source + * @return Returns true on success or false on failure + */ + public static function unshareAll($itemType, $itemSource) { + // Get all of the owners of shares of this item. + $query = \OC_DB::prepare( 'SELECT `uid_owner` from `*PREFIX*share` WHERE `item_type`=? AND `item_source`=?' ); + $result = $query->execute(array($itemType, $itemSource)); + $shares = array(); + // Add each owner's shares to the array of all shares for this item. + while ($row = $result->fetchRow()) { + $shares = array_merge($shares, self::getItems($itemType, $itemSource, null, null, $row['uid_owner'])); + } + if (!empty($shares)) { + // Pass all the vars we have for now, they may be useful + $hookParams = array( + 'itemType' => $itemType, + 'itemSource' => $itemSource, + 'shares' => $shares, + ); + \OC_Hook::emit('OCP\Share', 'pre_unshareAll', $hookParams); + foreach ($shares as $share) { + self::unshareItem($share); + } + \OC_Hook::emit('OCP\Share', 'post_unshareAll', $hookParams); + return true; + } + return false; + } + + /** + * Unshare an item shared with the current user + * @param string Item type + * @param string Item target + * @return Returns true on success or false on failure + * + * Unsharing from self is not allowed for items inside collections + */ + public static function unshareFromSelf($itemType, $itemTarget) { + $item = self::getItemSharedWith($itemType, $itemTarget); + if (!empty($item)) { + if ((int)$item['share_type'] === self::SHARE_TYPE_GROUP) { + // Insert an extra row for the group share and set permission + // to 0 to prevent it from showing up for the user + $query = \OC_DB::prepare('INSERT INTO `*PREFIX*share`' + .' (`item_type`, `item_source`, `item_target`, `parent`, `share_type`,' + .' `share_with`, `uid_owner`, `permissions`, `stime`, `file_source`, `file_target`)' + .' VALUES (?,?,?,?,?,?,?,?,?,?,?)'); + $query->execute(array($item['item_type'], $item['item_source'], $item['item_target'], + $item['id'], self::$shareTypeGroupUserUnique, + \OC_User::getUser(), $item['uid_owner'], 0, $item['stime'], $item['file_source'], + $item['file_target'])); + \OC_DB::insertid('*PREFIX*share'); + // Delete all reshares by this user of the group share + Helper::delete($item['id'], true, \OC_User::getUser()); + } else if ((int)$item['share_type'] === self::$shareTypeGroupUserUnique) { + // Set permission to 0 to prevent it from showing up for the user + $query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `permissions` = ? WHERE `id` = ?'); + $query->execute(array(0, $item['id'])); + Helper::delete($item['id'], true); + } else { + Helper::delete($item['id']); + } + return true; + } + return false; + } + /** + * sent status if users got informed by mail about share + * @param string $itemType + * @param string $itemSource + * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK + * @param bool $status + */ + public static function setSendMailStatus($itemType, $itemSource, $shareType, $status) { + $status = $status ? 1 : 0; + + $query = \OC_DB::prepare( + 'UPDATE `*PREFIX*share` + SET `mail_send` = ? + WHERE `item_type` = ? AND `item_source` = ? AND `share_type` = ?'); + + $result = $query->execute(array($status, $itemType, $itemSource, $shareType)); + + if($result === false) { + \OC_Log::write('OCP\Share', 'Couldn\'t set send mail status', \OC_Log::ERROR); + } + } + + /** + * Set the permissions of an item for a specific user or group + * @param string Item type + * @param string Item source + * @param int SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK + * @param string User or group the item is being shared with + * @param int CRUDS permissions + * @return Returns true on success or false on failure + */ + public static function setPermissions($itemType, $itemSource, $shareType, $shareWith, $permissions) { + if ($item = self::getItems($itemType, $itemSource, $shareType, $shareWith, + \OC_User::getUser(), self::FORMAT_NONE, null, 1, false)) { + // Check if this item is a reshare and verify that the permissions + // granted don't exceed the parent shared item + if (isset($item['parent'])) { + $query = \OC_DB::prepare('SELECT `permissions` FROM `*PREFIX*share` WHERE `id` = ?', 1); + $result = $query->execute(array($item['parent']))->fetchRow(); + if (~(int)$result['permissions'] & $permissions) { + $message = 'Setting permissions for '.$itemSource.' failed,' + .' because the permissions exceed permissions granted to '.\OC_User::getUser(); + \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); + throw new \Exception($message); + } + } + $query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `permissions` = ? WHERE `id` = ?'); + $query->execute(array($permissions, $item['id'])); + if ($itemType === 'file' || $itemType === 'folder') { + \OC_Hook::emit('OCP\Share', 'post_update_permissions', array( + 'itemType' => $itemType, + 'itemSource' => $itemSource, + 'shareType' => $shareType, + 'shareWith' => $shareWith, + 'uidOwner' => \OC_User::getUser(), + 'permissions' => $permissions, + 'path' => $item['path'], + )); + } + // Check if permissions were removed + if ($item['permissions'] & ~$permissions) { + // If share permission is removed all reshares must be deleted + if (($item['permissions'] & \OCP\PERMISSION_SHARE) && (~$permissions & \OCP\PERMISSION_SHARE)) { + Helper::delete($item['id'], true); + } else { + $ids = array(); + $parents = array($item['id']); + while (!empty($parents)) { + $parents = "'".implode("','", $parents)."'"; + $query = \OC_DB::prepare('SELECT `id`, `permissions` FROM `*PREFIX*share`' + .' WHERE `parent` IN ('.$parents.')'); + $result = $query->execute(); + // Reset parents array, only go through loop again if + // items are found that need permissions removed + $parents = array(); + while ($item = $result->fetchRow()) { + // Check if permissions need to be removed + if ($item['permissions'] & ~$permissions) { + // Add to list of items that need permissions removed + $ids[] = $item['id']; + $parents[] = $item['id']; + } + } + } + // Remove the permissions for all reshares of this item + if (!empty($ids)) { + $ids = "'".implode("','", $ids)."'"; + // TODO this should be done with Doctrine platform objects + if (\OC_Config::getValue( "dbtype") === 'oci') { + $andOp = 'BITAND(`permissions`, ?)'; + } else { + $andOp = '`permissions` & ?'; + } + $query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `permissions` = '.$andOp + .' WHERE `id` IN ('.$ids.')'); + $query->execute(array($permissions)); + } + } + } + return true; + } + $message = 'Setting permissions for '.$itemSource.' failed, because the item was not found'; + \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); + throw new \Exception($message); + } + + /** + * Set expiration date for a share + * @param string $itemType + * @param string $itemSource + * @param string $date expiration date + * @return \OCP\Share_Backend + */ + public static function setExpirationDate($itemType, $itemSource, $date) { + $user = \OC_User::getUser(); + $items = self::getItems($itemType, $itemSource, null, null, $user, self::FORMAT_NONE, null, -1, false); + if (!empty($items)) { + if ($date == '') { + $date = null; + } else { + $date = new \DateTime($date); + } + $query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `expiration` = ? WHERE `id` = ?'); + $query->bindValue(1, $date, 'datetime'); + foreach ($items as $item) { + $query->bindValue(2, (int) $item['id']); + $query->execute(); + \OC_Hook::emit('OCP\Share', 'post_set_expiration_date', array( + 'itemType' => $itemType, + 'itemSource' => $itemSource, + 'date' => $date, + 'uidOwner' => $user + )); + } + return true; + } + return false; + } + + /** + * Checks whether a share has expired, calls unshareItem() if yes. + * @param array $item Share data (usually database row) + * @return bool True if item was expired, false otherwise. + */ + protected static function expireItem(array $item) { + if (!empty($item['expiration'])) { + $now = new \DateTime(); + $expires = new \DateTime($item['expiration']); + if ($now > $expires) { + self::unshareItem($item); + return true; + } + } + return false; + } + + /** + * Unshares a share given a share data array + * @param array $item Share data (usually database row) + * @return null + */ + protected static function unshareItem(array $item) { + // Pass all the vars we have for now, they may be useful + $hookParams = array( + 'itemType' => $item['item_type'], + 'itemSource' => $item['item_source'], + 'shareType' => $item['share_type'], + 'shareWith' => $item['share_with'], + 'itemParent' => $item['parent'], + 'uidOwner' => $item['uid_owner'], + ); + + \OC_Hook::emit('OCP\Share', 'pre_unshare', $hookParams + array( + 'fileSource' => $item['file_source'], + )); + Helper::delete($item['id']); + \OC_Hook::emit('OCP\Share', 'post_unshare', $hookParams); + } + + /** + * Get the backend class for the specified item type + * @param string $itemType + * @return \OCP\Share_Backend + */ + public static function getBackend($itemType) { + if (isset(self::$backends[$itemType])) { + return self::$backends[$itemType]; + } else if (isset(self::$backendTypes[$itemType]['class'])) { + $class = self::$backendTypes[$itemType]['class']; + if (class_exists($class)) { + self::$backends[$itemType] = new $class; + if (!(self::$backends[$itemType] instanceof \OCP\Share_Backend)) { + $message = 'Sharing backend '.$class.' must implement the interface OCP\Share_Backend'; + \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); + throw new \Exception($message); + } + return self::$backends[$itemType]; + } else { + $message = 'Sharing backend '.$class.' not found'; + \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); + throw new \Exception($message); + } + } + $message = 'Sharing backend for '.$itemType.' not found'; + \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); + throw new \Exception($message); + } + + /** + * Check if resharing is allowed + * @return Returns true if allowed or false + * + * Resharing is allowed by default if not configured + */ + private static function isResharingAllowed() { + if (!isset(self::$isResharingAllowed)) { + if (\OC_Appconfig::getValue('core', 'shareapi_allow_resharing', 'yes') == 'yes') { + self::$isResharingAllowed = true; + } else { + self::$isResharingAllowed = false; + } + } + return self::$isResharingAllowed; + } + + /** + * Get a list of collection item types for the specified item type + * @param string Item type + * @return array + */ + private static function getCollectionItemTypes($itemType) { + $collectionTypes = array($itemType); + foreach (self::$backendTypes as $type => $backend) { + if (in_array($backend['collectionOf'], $collectionTypes)) { + $collectionTypes[] = $type; + } + } + // TODO Add option for collections to be collection of themselves, only 'folder' does it now... + if (!self::getBackend($itemType) instanceof \OCP\Share_Backend_Collection || $itemType != 'folder') { + unset($collectionTypes[0]); + } + // Return array if collections were found or the item type is a + // collection itself - collections can be inside collections + if (count($collectionTypes) > 0) { + return $collectionTypes; + } + return false; + } + + /** + * Get shared items from the database + * @param string Item type + * @param string Item source or target (optional) + * @param int SHARE_TYPE_USER, SHARE_TYPE_GROUP, SHARE_TYPE_LINK, $shareTypeUserAndGroups, or $shareTypeGroupUserUnique + * @param string User or group the item is being shared with + * @param string User that is the owner of shared items (optional) + * @param int Format to convert items to with formatItems() + * @param mixed Parameters to pass to formatItems() + * @param int Number of items to return, -1 to return all matches (optional) + * @param bool Include collection item types (optional) + * @param bool TODO (optional) + * @prams bool check expire date + * @return array + * + * See public functions getItem(s)... for parameter usage + * + */ + public static function getItems($itemType, $item = null, $shareType = null, $shareWith = null, + $uidOwner = null, $format = self::FORMAT_NONE, $parameters = null, $limit = -1, + $includeCollections = false, $itemShareWithBySource = false, $checkExpireDate = true) { + if (!self::isEnabled()) { + return array(); + } + $backend = self::getBackend($itemType); + $collectionTypes = false; + // Get filesystem root to add it to the file target and remove from the + // file source, match file_source with the file cache + if ($itemType == 'file' || $itemType == 'folder') { + if(!is_null($uidOwner)) { + $root = \OC\Files\Filesystem::getRoot(); + } else { + $root = ''; + } + $where = 'INNER JOIN `*PREFIX*filecache` ON `file_source` = `*PREFIX*filecache`.`fileid`'; + if (!isset($item)) { + $where .= ' WHERE `file_target` IS NOT NULL'; + } + $fileDependent = true; + $queryArgs = array(); + } else { + $fileDependent = false; + $root = ''; + $collectionTypes = self::getCollectionItemTypes($itemType); + if ($includeCollections && !isset($item) && $collectionTypes) { + // If includeCollections is true, find collections of this item type, e.g. a music album contains songs + if (!in_array($itemType, $collectionTypes)) { + $itemTypes = array_merge(array($itemType), $collectionTypes); + } else { + $itemTypes = $collectionTypes; + } + $placeholders = join(',', array_fill(0, count($itemTypes), '?')); + $where = ' WHERE `item_type` IN ('.$placeholders.'))'; + $queryArgs = $itemTypes; + } else { + $where = ' WHERE `item_type` = ?'; + $queryArgs = array($itemType); + } + } + if (\OC_Appconfig::getValue('core', 'shareapi_allow_links', 'yes') !== 'yes') { + $where .= ' AND `share_type` != ?'; + $queryArgs[] = self::SHARE_TYPE_LINK; + } + if (isset($shareType)) { + // Include all user and group items + if ($shareType == self::$shareTypeUserAndGroups && isset($shareWith)) { + $where .= ' AND `share_type` IN (?,?,?)'; + $queryArgs[] = self::SHARE_TYPE_USER; + $queryArgs[] = self::SHARE_TYPE_GROUP; + $queryArgs[] = self::$shareTypeGroupUserUnique; + $userAndGroups = array_merge(array($shareWith), \OC_Group::getUserGroups($shareWith)); + $placeholders = join(',', array_fill(0, count($userAndGroups), '?')); + $where .= ' AND `share_with` IN ('.$placeholders.')'; + $queryArgs = array_merge($queryArgs, $userAndGroups); + // Don't include own group shares + $where .= ' AND `uid_owner` != ?'; + $queryArgs[] = $shareWith; + } else { + $where .= ' AND `share_type` = ?'; + $queryArgs[] = $shareType; + if (isset($shareWith)) { + $where .= ' AND `share_with` = ?'; + $queryArgs[] = $shareWith; + } + } + } + if (isset($uidOwner)) { + $where .= ' AND `uid_owner` = ?'; + $queryArgs[] = $uidOwner; + if (!isset($shareType)) { + // Prevent unique user targets for group shares from being selected + $where .= ' AND `share_type` != ?'; + $queryArgs[] = self::$shareTypeGroupUserUnique; + } + if ($fileDependent) { + $column = 'file_source'; + } else { + $column = 'item_source'; + } + } else { + if ($fileDependent) { + $column = 'file_target'; + } else { + $column = 'item_target'; + } + } + if (isset($item)) { + $collectionTypes = self::getCollectionItemTypes($itemType); + if ($includeCollections && $collectionTypes) { + $where .= ' AND ('; + } else { + $where .= ' AND'; + } + // If looking for own shared items, check item_source else check item_target + if (isset($uidOwner) || $itemShareWithBySource) { + // If item type is a file, file source needs to be checked in case the item was converted + if ($fileDependent) { + $where .= ' `file_source` = ?'; + $column = 'file_source'; + } else { + $where .= ' `item_source` = ?'; + $column = 'item_source'; + } + } else { + if ($fileDependent) { + $where .= ' `file_target` = ?'; + $item = \OC\Files\Filesystem::normalizePath($item); + } else { + $where .= ' `item_target` = ?'; + } + } + $queryArgs[] = $item; + if ($includeCollections && $collectionTypes) { + $placeholders = join(',', array_fill(0, count($collectionTypes), '?')); + $where .= ' OR `item_type` IN ('.$placeholders.'))'; + $queryArgs = array_merge($queryArgs, $collectionTypes); + } + } + if ($limit != -1 && !$includeCollections) { + if ($shareType == self::$shareTypeUserAndGroups) { + // Make sure the unique user target is returned if it exists, + // unique targets should follow the group share in the database + // If the limit is not 1, the filtering can be done later + $where .= ' ORDER BY `*PREFIX*share`.`id` DESC'; + } + // The limit must be at least 3, because filtering needs to be done + if ($limit < 3) { + $queryLimit = 3; + } else { + $queryLimit = $limit; + } + } else { + $queryLimit = null; + } + $select = self::createSelectStatement($format, $fileDependent, $uidOwner); + $root = strlen($root); + $query = \OC_DB::prepare('SELECT '.$select.' FROM `*PREFIX*share` '.$where, $queryLimit); + $result = $query->execute($queryArgs); + if (\OC_DB::isError($result)) { + \OC_Log::write('OCP\Share', + \OC_DB::getErrorMessage($result) . ', select=' . $select . ' where=' . $where, + \OC_Log::ERROR); + } + $items = array(); + $targets = array(); + $switchedItems = array(); + $mounts = array(); + while ($row = $result->fetchRow()) { + self::transformDBResults($row); + // Filter out duplicate group shares for users with unique targets + if ($row['share_type'] == self::$shareTypeGroupUserUnique && isset($items[$row['parent']])) { + $row['share_type'] = self::SHARE_TYPE_GROUP; + $row['share_with'] = $items[$row['parent']]['share_with']; + // Remove the parent group share + unset($items[$row['parent']]); + if ($row['permissions'] == 0) { + continue; + } + } else if (!isset($uidOwner)) { + // Check if the same target already exists + if (isset($targets[$row[$column]])) { + // Check if the same owner shared with the user twice + // through a group and user share - this is allowed + $id = $targets[$row[$column]]; + if (isset($items[$id]) && $items[$id]['uid_owner'] == $row['uid_owner']) { + // Switch to group share type to ensure resharing conditions aren't bypassed + if ($items[$id]['share_type'] != self::SHARE_TYPE_GROUP) { + $items[$id]['share_type'] = self::SHARE_TYPE_GROUP; + $items[$id]['share_with'] = $row['share_with']; + } + // Switch ids if sharing permission is granted on only + // one share to ensure correct parent is used if resharing + if (~(int)$items[$id]['permissions'] & \OCP\PERMISSION_SHARE + && (int)$row['permissions'] & \OCP\PERMISSION_SHARE) { + $items[$row['id']] = $items[$id]; + $switchedItems[$id] = $row['id']; + unset($items[$id]); + $id = $row['id']; + } + // Combine the permissions for the item + $items[$id]['permissions'] |= (int)$row['permissions']; + continue; + } + } else { + $targets[$row[$column]] = $row['id']; + } + } + // Remove root from file source paths if retrieving own shared items + if (isset($uidOwner) && isset($row['path'])) { + if (isset($row['parent'])) { + // FIXME: Doesn't always construct the correct path, example: + // Folder '/a/b', share '/a' and '/a/b' to user2 + // user2 reshares /Shared/b and ask for share status of /Shared/a/b + // expected result: path=/Shared/a/b; actual result /Shared/b because of the parent + $query = \OC_DB::prepare('SELECT `file_target` FROM `*PREFIX*share` WHERE `id` = ?'); + $parentResult = $query->execute(array($row['parent'])); + if (\OC_DB::isError($result)) { + \OC_Log::write('OCP\Share', 'Can\'t select parent: ' . + \OC_DB::getErrorMessage($result) . ', select=' . $select . ' where=' . $where, + \OC_Log::ERROR); + } else { + $parentRow = $parentResult->fetchRow(); + $tmpPath = '/Shared' . $parentRow['file_target']; + // find the right position where the row path continues from the target path + $pos = strrpos($row['path'], $parentRow['file_target']); + $subPath = substr($row['path'], $pos); + $splitPath = explode('/', $subPath); + foreach (array_slice($splitPath, 2) as $pathPart) { + $tmpPath = $tmpPath . '/' . $pathPart; + } + $row['path'] = $tmpPath; + } + } else { + if (!isset($mounts[$row['storage']])) { + $mountPoints = \OC\Files\Filesystem::getMountByNumericId($row['storage']); + if (is_array($mountPoints)) { + $mounts[$row['storage']] = current($mountPoints); + } + } + if ($mounts[$row['storage']]) { + $path = $mounts[$row['storage']]->getMountPoint().$row['path']; + $row['path'] = substr($path, $root); + } + } + } + if($checkExpireDate) { + if (self::expireItem($row)) { + continue; + } + } + // Check if resharing is allowed, if not remove share permission + if (isset($row['permissions']) && !self::isResharingAllowed()) { + $row['permissions'] &= ~\OCP\PERMISSION_SHARE; + } + // Add display names to result + if ( isset($row['share_with']) && $row['share_with'] != '') { + $row['share_with_displayname'] = \OCP\User::getDisplayName($row['share_with']); + } + if ( isset($row['uid_owner']) && $row['uid_owner'] != '') { + $row['displayname_owner'] = \OCP\User::getDisplayName($row['uid_owner']); + } + + $items[$row['id']] = $row; + } + if (!empty($items)) { + $collectionItems = array(); + foreach ($items as &$row) { + // Return only the item instead of a 2-dimensional array + if ($limit == 1 && $row[$column] == $item && ($row['item_type'] == $itemType || $itemType == 'file')) { + if ($format == self::FORMAT_NONE) { + return $row; + } else { + break; + } + } + // Check if this is a collection of the requested item type + if ($includeCollections && $collectionTypes && in_array($row['item_type'], $collectionTypes)) { + if (($collectionBackend = self::getBackend($row['item_type'])) + && $collectionBackend instanceof \OCP\Share_Backend_Collection) { + // Collections can be inside collections, check if the item is a collection + if (isset($item) && $row['item_type'] == $itemType && $row[$column] == $item) { + $collectionItems[] = $row; + } else { + $collection = array(); + $collection['item_type'] = $row['item_type']; + if ($row['item_type'] == 'file' || $row['item_type'] == 'folder') { + $collection['path'] = basename($row['path']); + } + $row['collection'] = $collection; + // Fetch all of the children sources + $children = $collectionBackend->getChildren($row[$column]); + foreach ($children as $child) { + $childItem = $row; + $childItem['item_type'] = $itemType; + if ($row['item_type'] != 'file' && $row['item_type'] != 'folder') { + $childItem['item_source'] = $child['source']; + $childItem['item_target'] = $child['target']; + } + if ($backend instanceof \OCP\Share_Backend_File_Dependent) { + if ($row['item_type'] == 'file' || $row['item_type'] == 'folder') { + $childItem['file_source'] = $child['source']; + } else { // TODO is this really needed if we already know that we use the file backend? + $meta = \OC\Files\Filesystem::getFileInfo($child['file_path']); + $childItem['file_source'] = $meta['fileid']; + } + $childItem['file_target'] = + \OC\Files\Filesystem::normalizePath($child['file_path']); + } + if (isset($item)) { + if ($childItem[$column] == $item) { + // Return only the item instead of a 2-dimensional array + if ($limit == 1) { + if ($format == self::FORMAT_NONE) { + return $childItem; + } else { + // Unset the items array and break out of both loops + $items = array(); + $items[] = $childItem; + break 2; + } + } else { + $collectionItems[] = $childItem; + } + } + } else { + $collectionItems[] = $childItem; + } + } + } + } + // Remove collection item + $toRemove = $row['id']; + if (array_key_exists($toRemove, $switchedItems)) { + $toRemove = $switchedItems[$toRemove]; + } + unset($items[$toRemove]); + } + } + if (!empty($collectionItems)) { + $items = array_merge($items, $collectionItems); + } + + return self::formatResult($items, $column, $backend, $format, $parameters); + } + + return array(); + } + + /** + * Put shared item into the database + * @param string Item type + * @param string Item source + * @param int SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK + * @param string User or group the item is being shared with + * @param string User that is the owner of shared item + * @param int CRUDS permissions + * @param bool|array Parent folder target (optional) + * @param string token (optional) + * @param string name of the source item (optional) + * @return bool Returns true on success or false on failure + */ + private static function put($itemType, $itemSource, $shareType, $shareWith, $uidOwner, + $permissions, $parentFolder = null, $token = null, $itemSourceName = null) { + $backend = self::getBackend($itemType); + + // Check if this is a reshare + if ($checkReshare = self::getItemSharedWithBySource($itemType, $itemSource, self::FORMAT_NONE, null, true)) { + + // Check if attempting to share back to owner + if ($checkReshare['uid_owner'] == $shareWith && $shareType == self::SHARE_TYPE_USER) { + $message = 'Sharing '.$itemSourceName.' failed, because the user '.$shareWith.' is the original sharer'; + \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); + throw new \Exception($message); + } + // Check if share permissions is granted + if (self::isResharingAllowed() && (int)$checkReshare['permissions'] & \OCP\PERMISSION_SHARE) { + if (~(int)$checkReshare['permissions'] & $permissions) { + $message = 'Sharing '.$itemSourceName + .' failed, because the permissions exceed permissions granted to '.$uidOwner; + \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); + throw new \Exception($message); + } else { + // TODO Don't check if inside folder + $parent = $checkReshare['id']; + $itemSource = $checkReshare['item_source']; + $fileSource = $checkReshare['file_source']; + $suggestedItemTarget = $checkReshare['item_target']; + $suggestedFileTarget = $checkReshare['file_target']; + $filePath = $checkReshare['file_target']; + } + } else { + $message = 'Sharing '.$itemSourceName.' failed, because resharing is not allowed'; + \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); + throw new \Exception($message); + } + } else { + $parent = null; + $suggestedItemTarget = null; + $suggestedFileTarget = null; + if (!$backend->isValidSource($itemSource, $uidOwner)) { + $message = 'Sharing '.$itemSource.' failed, because the sharing backend for ' + .$itemType.' could not find its source'; + \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); + throw new \Exception($message); + } + if ($backend instanceof \OCP\Share_Backend_File_Dependent) { + $filePath = $backend->getFilePath($itemSource, $uidOwner); + if ($itemType == 'file' || $itemType == 'folder') { + $fileSource = $itemSource; + } else { + $meta = \OC\Files\Filesystem::getFileInfo($filePath); + $fileSource = $meta['fileid']; + } + if ($fileSource == -1) { + $message = 'Sharing '.$itemSource.' failed, because the file could not be found in the file cache'; + \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); + throw new \Exception($message); + } + } else { + $filePath = null; + $fileSource = null; + } + } + $query = \OC_DB::prepare('INSERT INTO `*PREFIX*share` (`item_type`, `item_source`, `item_target`,' + .' `parent`, `share_type`, `share_with`, `uid_owner`, `permissions`, `stime`, `file_source`,' + .' `file_target`, `token`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)'); + // Share with a group + if ($shareType == self::SHARE_TYPE_GROUP) { + $groupItemTarget = Helper::generateTarget($itemType, $itemSource, $shareType, $shareWith['group'], + $uidOwner, $suggestedItemTarget); + $run = true; + $error = ''; + \OC_Hook::emit('OCP\Share', 'pre_shared', array( + 'itemType' => $itemType, + 'itemSource' => $itemSource, + 'itemTarget' => $groupItemTarget, + 'shareType' => $shareType, + 'shareWith' => $shareWith['group'], + 'uidOwner' => $uidOwner, + 'permissions' => $permissions, + 'fileSource' => $fileSource, + 'token' => $token, + 'run' => &$run, + 'error' => &$error + )); + + if ($run === false) { + throw new \Exception($error); + } + + if (isset($fileSource)) { + if ($parentFolder) { + if ($parentFolder === true) { + $groupFileTarget = Helper::generateTarget('file', $filePath, $shareType, + $shareWith['group'], $uidOwner, $suggestedFileTarget); + // Set group default file target for future use + $parentFolders[0]['folder'] = $groupFileTarget; + } else { + // Get group default file target + $groupFileTarget = $parentFolder[0]['folder'].$itemSource; + $parent = $parentFolder[0]['id']; + } + } else { + $groupFileTarget = Helper::generateTarget('file', $filePath, $shareType, $shareWith['group'], + $uidOwner, $suggestedFileTarget); + } + } else { + $groupFileTarget = null; + } + $query->execute(array($itemType, $itemSource, $groupItemTarget, $parent, $shareType, + $shareWith['group'], $uidOwner, $permissions, time(), $fileSource, $groupFileTarget, $token)); + // Save this id, any extra rows for this group share will need to reference it + $parent = \OC_DB::insertid('*PREFIX*share'); + // Loop through all users of this group in case we need to add an extra row + foreach ($shareWith['users'] as $uid) { + $itemTarget = Helper::generateTarget($itemType, $itemSource, self::SHARE_TYPE_USER, $uid, + $uidOwner, $suggestedItemTarget, $parent); + if (isset($fileSource)) { + if ($parentFolder) { + if ($parentFolder === true) { + $fileTarget = Helper::generateTarget('file', $filePath, self::SHARE_TYPE_USER, $uid, + $uidOwner, $suggestedFileTarget, $parent); + if ($fileTarget != $groupFileTarget) { + $parentFolders[$uid]['folder'] = $fileTarget; + } + } else if (isset($parentFolder[$uid])) { + $fileTarget = $parentFolder[$uid]['folder'].$itemSource; + $parent = $parentFolder[$uid]['id']; + } + } else { + $fileTarget = Helper::generateTarget('file', $filePath, self::SHARE_TYPE_USER, + $uid, $uidOwner, $suggestedFileTarget, $parent); + } + } else { + $fileTarget = null; + } + // Insert an extra row for the group share if the item or file target is unique for this user + if ($itemTarget != $groupItemTarget || (isset($fileSource) && $fileTarget != $groupFileTarget)) { + $query->execute(array($itemType, $itemSource, $itemTarget, $parent, + self::$shareTypeGroupUserUnique, $uid, $uidOwner, $permissions, time(), + $fileSource, $fileTarget, $token)); + $id = \OC_DB::insertid('*PREFIX*share'); + } + } + \OC_Hook::emit('OCP\Share', 'post_shared', array( + 'itemType' => $itemType, + 'itemSource' => $itemSource, + 'itemTarget' => $groupItemTarget, + 'parent' => $parent, + 'shareType' => $shareType, + 'shareWith' => $shareWith['group'], + 'uidOwner' => $uidOwner, + 'permissions' => $permissions, + 'fileSource' => $fileSource, + 'fileTarget' => $groupFileTarget, + 'id' => $parent, + 'token' => $token + )); + + if ($parentFolder === true) { + // Return parent folders to preserve file target paths for potential children + return $parentFolders; + } + } else { + $itemTarget = Helper::generateTarget($itemType, $itemSource, $shareType, $shareWith, $uidOwner, + $suggestedItemTarget); + $run = true; + $error = ''; + \OC_Hook::emit('OCP\Share', 'pre_shared', array( + 'itemType' => $itemType, + 'itemSource' => $itemSource, + 'itemTarget' => $itemTarget, + 'shareType' => $shareType, + 'shareWith' => $shareWith, + 'uidOwner' => $uidOwner, + 'permissions' => $permissions, + 'fileSource' => $fileSource, + 'token' => $token, + 'run' => &$run, + 'error' => &$error + )); + + if ($run === false) { + throw new \Exception($error); + } + + if (isset($fileSource)) { + if ($parentFolder) { + if ($parentFolder === true) { + $fileTarget = Helper::generateTarget('file', $filePath, $shareType, $shareWith, + $uidOwner, $suggestedFileTarget); + $parentFolders['folder'] = $fileTarget; + } else { + $fileTarget = $parentFolder['folder'].$itemSource; + $parent = $parentFolder['id']; + } + } else { + $fileTarget = Helper::generateTarget('file', $filePath, $shareType, $shareWith, $uidOwner, + $suggestedFileTarget); + } + } else { + $fileTarget = null; + } + $query->execute(array($itemType, $itemSource, $itemTarget, $parent, $shareType, $shareWith, $uidOwner, + $permissions, time(), $fileSource, $fileTarget, $token)); + $id = \OC_DB::insertid('*PREFIX*share'); + \OC_Hook::emit('OCP\Share', 'post_shared', array( + 'itemType' => $itemType, + 'itemSource' => $itemSource, + 'itemTarget' => $itemTarget, + 'parent' => $parent, + 'shareType' => $shareType, + 'shareWith' => $shareWith, + 'uidOwner' => $uidOwner, + 'permissions' => $permissions, + 'fileSource' => $fileSource, + 'fileTarget' => $fileTarget, + 'id' => $id, + 'token' => $token + )); + if ($parentFolder === true) { + $parentFolders['id'] = $id; + // Return parent folder to preserve file target paths for potential children + return $parentFolders; + } + } + return true; + } + + /** + * Delete all shares with type SHARE_TYPE_LINK + */ + public static function removeAllLinkShares() { + // Delete any link shares + $query = \OC_DB::prepare('SELECT `id` FROM `*PREFIX*share` WHERE `share_type` = ?'); + $result = $query->execute(array(self::SHARE_TYPE_LINK)); + while ($item = $result->fetchRow()) { + Helper::delete($item['id']); + } + } + + /** + * In case a password protected link is not yet authenticated this function will return false + * + * @param array $linkItem + * @return bool + */ + public static function checkPasswordProtectedShare(array $linkItem) { + if (!isset($linkItem['share_with'])) { + return true; + } + if (!isset($linkItem['share_type'])) { + return true; + } + if (!isset($linkItem['id'])) { + return true; + } + + if ($linkItem['share_type'] != \OCP\Share::SHARE_TYPE_LINK) { + return true; + } + + if ( \OC::$session->exists('public_link_authenticated') + && \OC::$session->get('public_link_authenticated') === $linkItem['id'] ) { + return true; + } + + return false; + } + + /** + * @breif construct select statement + * @param int $format + * @param bool $fileDependent ist it a file/folder share or a generla share + * @param string $uidOwner + * @return string select statement + */ + private static function createSelectStatement($format, $fileDependent, $uidOwner = null) { + $select = '*'; + if ($format == self::FORMAT_STATUSES) { + if ($fileDependent) { + $select = '`*PREFIX*share`.`id`, `*PREFIX*share`.`parent`, `share_type`, `path`, `share_with`, `uid_owner`'; + } else { + $select = '`id`, `parent`, `share_type`, `share_with`, `uid_owner`'; + } + } else { + if (isset($uidOwner)) { + if ($fileDependent) { + $select = '`*PREFIX*share`.`id`, `item_type`, `item_source`, `*PREFIX*share`.`parent`,' + . ' `share_type`, `share_with`, `file_source`, `path`, `permissions`, `stime`,' + . ' `expiration`, `token`, `storage`, `mail_send`, `uid_owner`'; + } else { + $select = '`id`, `item_type`, `item_source`, `parent`, `share_type`, `share_with`, `permissions`,' + . ' `stime`, `file_source`, `expiration`, `token`, `mail_send`, `uid_owner`'; + } + } else { + if ($fileDependent) { + if ($format == \OC_Share_Backend_File::FORMAT_GET_FOLDER_CONTENTS || $format == \OC_Share_Backend_File::FORMAT_FILE_APP_ROOT) { + $select = '`*PREFIX*share`.`id`, `item_type`, `item_source`, `*PREFIX*share`.`parent`, `uid_owner`, ' + . '`share_type`, `share_with`, `file_source`, `path`, `file_target`, ' + . '`permissions`, `expiration`, `storage`, `*PREFIX*filecache`.`parent` as `file_parent`, ' + . '`name`, `mtime`, `mimetype`, `mimepart`, `size`, `unencrypted_size`, `encrypted`, `etag`, `mail_send`'; + } else { + $select = '`*PREFIX*share`.`id`, `item_type`, `item_source`, `item_target`, + `*PREFIX*share`.`parent`, `share_type`, `share_with`, `uid_owner`, + `file_source`, `path`, `file_target`, `permissions`, `stime`, `expiration`, `token`, `storage`, `mail_send`'; + } + } + } + } + return $select; + } + + + /** + * @brief transform db results + * @param array $row result + */ + private static function transformDBResults(&$row) { + if (isset($row['id'])) { + $row['id'] = (int) $row['id']; + } + if (isset($row['share_type'])) { + $row['share_type'] = (int) $row['share_type']; + } + if (isset($row['parent'])) { + $row['parent'] = (int) $row['parent']; + } + if (isset($row['file_parent'])) { + $row['file_parent'] = (int) $row['file_parent']; + } + if (isset($row['file_source'])) { + $row['file_source'] = (int) $row['file_source']; + } + if (isset($row['permissions'])) { + $row['permissions'] = (int) $row['permissions']; + } + if (isset($row['storage'])) { + $row['storage'] = (int) $row['storage']; + } + if (isset($row['stime'])) { + $row['stime'] = (int) $row['stime']; + } + } + + /** + * @brief format result + * @param array $items result + * @prams string $column is it a file share or a general share ('file_target' or 'item_target') + * @params \OCP\Share_Backend $backend sharing backend + * @param int $format + * @param array additional format parameters + * @return array formate result + */ + private static function formatResult($items, $column, $backend, $format = self::FORMAT_NONE , $parameters = null) { + if ($format === self::FORMAT_NONE) { + return $items; + } else if ($format === self::FORMAT_STATUSES) { + $statuses = array(); + foreach ($items as $item) { + if ($item['share_type'] === self::SHARE_TYPE_LINK) { + $statuses[$item[$column]]['link'] = true; + } else if (!isset($statuses[$item[$column]])) { + $statuses[$item[$column]]['link'] = false; + } + if ('file_target') { + $statuses[$item[$column]]['path'] = $item['path']; + } + } + return $statuses; + } else { + return $backend->formatItems($items, $format, $parameters); + } + } +} diff --git a/lib/private/urlgenerator.php b/lib/private/urlgenerator.php index b7ae8dd0f60d4d984e1da4fc430d3bf532800e1b..0d238737dde4e9af37fba54232a060025b8691fb 100644 --- a/lib/private/urlgenerator.php +++ b/lib/private/urlgenerator.php @@ -148,6 +148,7 @@ class URLGenerator implements IURLGenerator { */ public function getAbsoluteURL($url) { $separator = $url[0] === '/' ? '' : '/'; - return \OC_Request::serverProtocol() . '://' . \OC_Request::serverHost() . $separator . $url; + + return \OC_Request::serverProtocol() . '://' . \OC_Request::serverHost(). \OC::$WEBROOT . $separator . $url; } } diff --git a/lib/private/user.php b/lib/private/user.php index a89b7286c1009e48525cf039ccd4ac41228a9fe4..dc4c7ec3b611fb7cc278080a8dab615bb4b4f697 100644 --- a/lib/private/user.php +++ b/lib/private/user.php @@ -321,8 +321,6 @@ class OC_User { */ public static function isLoggedIn() { if (\OC::$session->get('user_id') && self::$incognitoMode === false) { - OC_App::loadApps(array('authentication')); - self::setupBackends(); return self::userExists(\OC::$session->get('user_id')); } return false; diff --git a/lib/private/util.php b/lib/private/util.php index 87e173fa765da6af5e10580ef03251fccc91370f..731b7c975038f9e8dac48e71cc714be67bf459f0 100755 --- a/lib/private/util.php +++ b/lib/private/util.php @@ -30,9 +30,7 @@ class OC_Util { } // load all filesystem apps before, so no setup-hook gets lost - if(!isset($RUNTIME_NOAPPS) || !$RUNTIME_NOAPPS) { - OC_App::loadApps(array('filesystem')); - } + OC_App::loadApps(array('filesystem')); // the filesystem will finish when $user is not empty, // mark fs setup here to avoid doing the setup from loading @@ -703,17 +701,18 @@ class OC_Util { * @return void */ public static function redirectToDefaultPage() { + $urlGenerator = \OC::$server->getURLGenerator(); if(isset($_REQUEST['redirect_url'])) { - $location = OC_Helper::makeURLAbsolute(urldecode($_REQUEST['redirect_url'])); + $location = urldecode($_REQUEST['redirect_url']); } else if (isset(OC::$REQUESTEDAPP) && !empty(OC::$REQUESTEDAPP)) { - $location = OC_Helper::linkToAbsolute( OC::$REQUESTEDAPP, 'index.php' ); + $location = $urlGenerator->getAbsoluteURL('/index.php/apps/'.OC::$REQUESTEDAPP.'/index.php'); } else { $defaultPage = OC_Appconfig::getValue('core', 'defaultpage'); if ($defaultPage) { - $location = OC_Helper::makeURLAbsolute(OC::$WEBROOT.'/'.$defaultPage); + $location = $urlGenerator->getAbsoluteURL($defaultPage); } else { - $location = OC_Helper::linkToAbsolute( 'files', 'index.php' ); + $location = $urlGenerator->getAbsoluteURL('/index.php/files/index.php'); } } OC_Log::write('core', 'redirectToDefaultPage: '.$location, OC_Log::DEBUG); @@ -1074,13 +1073,13 @@ class OC_Util { public static function getUrlContent($url) { if (function_exists('curl_init')) { $curl = curl_init(); + $max_redirects = 10; curl_setopt($curl, CURLOPT_HEADER, 0); curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 10); curl_setopt($curl, CURLOPT_URL, $url); - curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); - curl_setopt($curl, CURLOPT_MAXREDIRS, 10); + curl_setopt($curl, CURLOPT_USERAGENT, "ownCloud Server Crawler"); if(OC_Config::getValue('proxy', '') != '') { @@ -1089,9 +1088,50 @@ class OC_Util { if(OC_Config::getValue('proxyuserpwd', '') != '') { curl_setopt($curl, CURLOPT_PROXYUSERPWD, OC_Config::getValue('proxyuserpwd')); } - $data = curl_exec($curl); + + if (ini_get('open_basedir') === '' && ini_get('safe_mode' === 'Off')) { + curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); + curl_setopt($curl, CURLOPT_MAXREDIRS, $max_redirects); + $data = curl_exec($curl); + } else { + curl_setopt($curl, CURLOPT_FOLLOWLOCATION, false); + $mr = $max_redirects; + if ($mr > 0) { + $newurl = curl_getinfo($curl, CURLINFO_EFFECTIVE_URL); + + $rcurl = curl_copy_handle($curl); + curl_setopt($rcurl, CURLOPT_HEADER, true); + curl_setopt($rcurl, CURLOPT_NOBODY, true); + curl_setopt($rcurl, CURLOPT_FORBID_REUSE, false); + curl_setopt($rcurl, CURLOPT_RETURNTRANSFER, true); + do { + curl_setopt($rcurl, CURLOPT_URL, $newurl); + $header = curl_exec($rcurl); + if (curl_errno($rcurl)) { + $code = 0; + } else { + $code = curl_getinfo($rcurl, CURLINFO_HTTP_CODE); + if ($code == 301 || $code == 302) { + preg_match('/Location:(.*?)\n/', $header, $matches); + $newurl = trim(array_pop($matches)); + } else { + $code = 0; + } + } + } while ($code && --$mr); + curl_close($rcurl); + if ($mr > 0) { + curl_setopt($curl, CURLOPT_URL, $newurl); + } + } + + if($mr == 0 && $max_redirects > 0) { + $data = false; + } else { + $data = curl_exec($curl); + } + } curl_close($curl); - } else { $contextArray = null; diff --git a/lib/public/share.php b/lib/public/share.php index 5066d40354d21b699da27da04a00857383e47445..a08134b3837789e9a7fc614b7909a366179e601c 100644 --- a/lib/public/share.php +++ b/lib/public/share.php @@ -2,8 +2,9 @@ /** * ownCloud * - * @author Michael Gapczynski - * @copyright 2012 Michael Gapczynski mtgap@owncloud.com + * @author Bjoern Schiessle, Michael Gapczynski + * @copyright 2012 Michael Gapczynski <mtgap@owncloud.com> + * 2014 Bjoern Schiessle <schiessle@owncloud.com> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -36,41 +37,7 @@ namespace OCP; * It provides the following hooks: * - post_shared */ -class Share { - - const SHARE_TYPE_USER = 0; - const SHARE_TYPE_GROUP = 1; - const SHARE_TYPE_LINK = 3; - const SHARE_TYPE_EMAIL = 4; - const SHARE_TYPE_CONTACT = 5; - const SHARE_TYPE_REMOTE = 6; - - /** CRUDS permissions (Create, Read, Update, Delete, Share) using a bitmask - * Construct permissions for share() and setPermissions with Or (|) e.g. - * Give user read and update permissions: PERMISSION_READ | PERMISSION_UPDATE - * - * Check if permission is granted with And (&) e.g. Check if delete is - * granted: if ($permissions & PERMISSION_DELETE) - * - * Remove permissions with And (&) and Not (~) e.g. Remove the update - * permission: $permissions &= ~PERMISSION_UPDATE - * - * Apps are required to handle permissions on their own, this class only - * stores and manages the permissions of shares - * @see lib/public/constants.php - */ - - const FORMAT_NONE = -1; - const FORMAT_STATUSES = -2; - const FORMAT_SOURCES = -3; - - const TOKEN_LENGTH = 32; // see db_structure.xml - - private static $shareTypeUserAndGroups = -1; - private static $shareTypeGroupUserUnique = 2; - private static $backends = array(); - private static $backendTypes = array(); - private static $isResharingAllowed; +class Share extends \OC\Share\Constants { /** * Register a sharing backend class that implements OCP\Share_Backend for an item type @@ -78,67 +45,25 @@ class Share { * @param string Backend class * @param string (optional) Depends on item type * @param array (optional) List of supported file extensions if this item type depends on files - * @param string $itemType - * @param string $class - * @param string $collectionOf - * @return boolean true if backend is registered or false if error + * @return Returns true if backend is registered or false if error */ public static function registerBackend($itemType, $class, $collectionOf = null, $supportedFileExtensions = null) { - if (self::isEnabled()) { - if (!isset(self::$backendTypes[$itemType])) { - self::$backendTypes[$itemType] = array( - 'class' => $class, - 'collectionOf' => $collectionOf, - 'supportedFileExtensions' => $supportedFileExtensions - ); - if(count(self::$backendTypes) === 1) { - \OC_Util::addScript('core', 'share'); - \OC_Util::addStyle('core', 'share'); - } - return true; - } - \OC_Log::write('OCP\Share', - 'Sharing backend '.$class.' not registered, '.self::$backendTypes[$itemType]['class'] - .' is already registered for '.$itemType, - \OC_Log::WARN); - } - return false; + return \OC\Share\Share::registerBackend($itemType, $class, $collectionOf, $supportedFileExtensions); } /** * Check if the Share API is enabled - * @return boolean true if enabled or false + * @return Returns true if enabled or false * * The Share API is enabled by default if not configured */ public static function isEnabled() { - if (\OC_Appconfig::getValue('core', 'shareapi_enabled', 'yes') == 'yes') { - return true; - } - return false; - } - - /** - * Prepare a path to be passed to DB as file_target - * @param string $path path - * @return string Prepared path - */ - public static function prepFileTarget( $path ) { - - // Paths in DB are stored with leading slashes, so add one if necessary - if ( substr( $path, 0, 1 ) !== '/' ) { - - $path = '/' . $path; - - } - - return $path; - + return \OC\Share\Share::isEnabled(); } /** * Find which users can access a shared item - * @param string $path to the file + * @param $path to the file * @param $user owner of the file * @param include owner to the list of users with access to the file * @return array @@ -146,101 +71,7 @@ class Share { * not '/admin/data/file.txt' */ public static function getUsersSharingFile($path, $user, $includeOwner = false) { - - $shares = array(); - $publicShare = false; - $source = -1; - $cache = false; - - $view = new \OC\Files\View('/' . $user . '/files'); - if ($view->file_exists($path)) { - $meta = $view->getFileInfo($path); - } else { - // if the file doesn't exists yet we start with the parent folder - $meta = $view->getFileInfo(dirname($path)); - } - - if($meta !== false) { - $source = $meta['fileid']; - $cache = new \OC\Files\Cache\Cache($meta['storage']); - } - - while ($source !== -1) { - - // Fetch all shares with another user - $query = \OC_DB::prepare( - 'SELECT `share_with` - FROM - `*PREFIX*share` - WHERE - `item_source` = ? AND `share_type` = ? AND `item_type` IN (\'file\', \'folder\')' - ); - - $result = $query->execute(array($source, self::SHARE_TYPE_USER)); - - if (\OCP\DB::isError($result)) { - \OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage($result), \OC_Log::ERROR); - } else { - while ($row = $result->fetchRow()) { - $shares[] = $row['share_with']; - } - } - // We also need to take group shares into account - - $query = \OC_DB::prepare( - 'SELECT `share_with` - FROM - `*PREFIX*share` - WHERE - `item_source` = ? AND `share_type` = ? AND `item_type` IN (\'file\', \'folder\')' - ); - - $result = $query->execute(array($source, self::SHARE_TYPE_GROUP)); - - if (\OCP\DB::isError($result)) { - \OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage($result), \OC_Log::ERROR); - } else { - while ($row = $result->fetchRow()) { - $usersInGroup = \OC_Group::usersInGroup($row['share_with']); - $shares = array_merge($shares, $usersInGroup); - } - } - - //check for public link shares - if (!$publicShare) { - $query = \OC_DB::prepare( - 'SELECT `share_with` - FROM - `*PREFIX*share` - WHERE - `item_source` = ? AND `share_type` = ? AND `item_type` IN (\'file\', \'folder\')' - ); - - $result = $query->execute(array($source, self::SHARE_TYPE_LINK)); - - if (\OCP\DB::isError($result)) { - \OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage($result), \OC_Log::ERROR); - } else { - if ($result->fetchRow()) { - $publicShare = true; - } - } - } - - // let's get the parent for the next round - $meta = $cache->get((int)$source); - if($meta !== false) { - $source = (int)$meta['parent']; - } else { - $source = -1; - } - } - // Include owner in list of users, if requested - if ($includeOwner) { - $shares[] = $user; - } - - return array("users" => array_unique($shares), "public" => $publicShare); + return \OC\Share\Share::getUsersSharingFile($path, $user, $includeOwner); } /** @@ -250,13 +81,12 @@ class Share { * @param mixed Parameters (optional) * @param int Number of items to return (optional) Returns all by default * @param bool include collections (optional) - * @param string $itemType * @return Return depends on format */ public static function getItemsSharedWith($itemType, $format = self::FORMAT_NONE, $parameters = null, $limit = -1, $includeCollections = false) { - return self::getItems($itemType, null, self::$shareTypeUserAndGroups, \OC_User::getUser(), null, $format, - $parameters, $limit, $includeCollections); + + return \OC\Share\Share::getItemsSharedWith($itemType, $format, $parameters, $limit, $includeCollections); } /** @@ -266,12 +96,12 @@ class Share { * @param int $format (optional) Format type must be defined by the backend * @param mixed Parameters (optional) * @param bool include collections (optional) - * @return string depends on format + * @return Return depends on format */ public static function getItemSharedWith($itemType, $itemTarget, $format = self::FORMAT_NONE, $parameters = null, $includeCollections = false) { - return self::getItems($itemType, $itemTarget, self::$shareTypeUserAndGroups, \OC_User::getUser(), null, $format, - $parameters, 1, $includeCollections); + + return \OC\Share\Share::getItemSharedWith($itemType, $itemTarget, $format, $parameters, $includeCollections); } /** @@ -282,45 +112,7 @@ class Share { * @return array Return list of items with file_target, permissions and expiration */ public static function getItemSharedWithUser($itemType, $itemSource, $user) { - - $shares = array(); - - // first check if there is a db entry for the specific user - $query = \OC_DB::prepare( - 'SELECT `file_target`, `permissions`, `expiration` - FROM - `*PREFIX*share` - WHERE - `item_source` = ? AND `item_type` = ? AND `share_with` = ?' - ); - - $result = \OC_DB::executeAudited($query, array($itemSource, $itemType, $user)); - - while ($row = $result->fetchRow()) { - $shares[] = $row; - } - - //if didn't found a result than let's look for a group share. - if(empty($shares)) { - $groups = \OC_Group::getUserGroups($user); - - $query = \OC_DB::prepare( - 'SELECT `file_target`, `permissions`, `expiration` - FROM - `*PREFIX*share` - WHERE - `item_source` = ? AND `item_type` = ? AND `share_with` in (?)' - ); - - $result = \OC_DB::executeAudited($query, array($itemSource, $itemType, implode(',', $groups))); - - while ($row = $result->fetchRow()) { - $shares[] = $row; - } - } - - return $shares; - + return \OC\Share\Share::getItemSharedWithUser($itemType, $itemSource, $user); } /** @@ -334,8 +126,7 @@ class Share { */ public static function getItemSharedWithBySource($itemType, $itemSource, $format = self::FORMAT_NONE, $parameters = null, $includeCollections = false) { - return self::getItems($itemType, $itemSource, self::$shareTypeUserAndGroups, \OC_User::getUser(), null, $format, - $parameters, 1, $includeCollections, true); + return \OC\Share\Share::getItemSharedWithBySource($itemType, $itemSource, $format, $parameters, $includeCollections); } /** @@ -346,8 +137,7 @@ class Share { * @return Item */ public static function getItemSharedWithByLink($itemType, $itemSource, $uidOwner) { - return self::getItems($itemType, $itemSource, self::SHARE_TYPE_LINK, null, $uidOwner, self::FORMAT_NONE, - null, 1); + return \OC\Share\Share::getItemSharedWithByLink($itemType, $itemSource, $uidOwner); } /** @@ -356,25 +146,7 @@ class Share { * @return array | bool false will be returned in case the token is unknown or unauthorized */ public static function getShareByToken($token, $checkPasswordProtection = true) { - $query = \OC_DB::prepare('SELECT * FROM `*PREFIX*share` WHERE `token` = ?', 1); - $result = $query->execute(array($token)); - if (\OC_DB::isError($result)) { - \OC_Log::write('OCP\Share', \OC_DB::getErrorMessage($result) . ', token=' . $token, \OC_Log::ERROR); - } - $row = $result->fetchRow(); - if ($row === false) { - return false; - } - if (is_array($row) and self::expireItem($row)) { - return false; - } - - // password protected shares need to be authenticated - if ($checkPasswordProtection && !\OCP\Share::checkPasswordProtectedShare($row)) { - return false; - } - - return $row; + return \OC\Share\Share::getShareByToken($token, $checkPasswordProtection); } /** @@ -382,21 +154,8 @@ class Share { * @param $linkItem * @return $fileOwner */ - public static function resolveReShare($linkItem) - { - if (isset($linkItem['parent'])) { - $parent = $linkItem['parent']; - while (isset($parent)) { - $query = \OC_DB::prepare('SELECT * FROM `*PREFIX*share` WHERE `id` = ?', 1); - $item = $query->execute(array($parent))->fetchRow(); - if (isset($item['parent'])) { - $parent = $item['parent']; - } else { - return $item; - } - } - } - return $linkItem; + public static function resolveReShare($linkItem) { + return \OC\Share\Share::resolveReShare($linkItem); } @@ -407,13 +166,12 @@ class Share { * @param mixed Parameters * @param int Number of items to return (optional) Returns all by default * @param bool include collections - * @param string $itemType * @return Return depends on format */ public static function getItemsShared($itemType, $format = self::FORMAT_NONE, $parameters = null, $limit = -1, $includeCollections = false) { - return self::getItems($itemType, null, null, null, \OC_User::getUser(), $format, - $parameters, $limit, $includeCollections); + + return \OC\Share\Share::getItemsShared($itemType, $format, $parameters, $limit, $includeCollections); } /** @@ -427,8 +185,8 @@ class Share { */ public static function getItemShared($itemType, $itemSource, $format = self::FORMAT_NONE, $parameters = null, $includeCollections = false) { - return self::getItems($itemType, $itemSource, null, null, \OC_User::getUser(), $format, - $parameters, -1, $includeCollections); + + return \OC\Share\Share::getItemShared($itemType, $itemSource, $format, $parameters, $includeCollections); } /** @@ -441,19 +199,7 @@ class Share { * @return Return array of users */ public static function getUsersItemShared($itemType, $itemSource, $uidOwner, $includeCollections = false, $checkExpireDate = true) { - - $users = array(); - $items = self::getItems($itemType, $itemSource, null, null, $uidOwner, self::FORMAT_NONE, null, -1, $includeCollections, false, $checkExpireDate); - if ($items) { - foreach ($items as $item) { - if ((int)$item['share_type'] === self::SHARE_TYPE_USER) { - $users[] = $item['share_with']; - } else if ((int)$item['share_type'] === self::SHARE_TYPE_GROUP) { - $users = array_merge($users, \OC_Group::usersInGroup($item['share_with'])); - } - } - } - return $users; + return \OC\Share\Share::getUsersItemShared($itemType, $itemSource, $uidOwner, $includeCollections, $checkExpireDate); } /** @@ -473,176 +219,7 @@ class Share { * @return bool|string Returns true on success or false on failure, Returns token on success for links */ public static function shareItem($itemType, $itemSource, $shareType, $shareWith, $permissions, $itemSourceName = null) { - $uidOwner = \OC_User::getUser(); - $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global'); - - if (is_null($itemSourceName)) { - $itemSourceName = $itemSource; - } - - // Verify share type and sharing conditions are met - if ($shareType === self::SHARE_TYPE_USER) { - if ($shareWith == $uidOwner) { - $message = 'Sharing '.$itemSourceName.' failed, because the user '.$shareWith.' is the item owner'; - \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); - throw new \Exception($message); - } - if (!\OC_User::userExists($shareWith)) { - $message = 'Sharing '.$itemSourceName.' failed, because the user '.$shareWith.' does not exist'; - \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); - throw new \Exception($message); - } - if ($sharingPolicy == 'groups_only') { - $inGroup = array_intersect(\OC_Group::getUserGroups($uidOwner), \OC_Group::getUserGroups($shareWith)); - if (empty($inGroup)) { - $message = 'Sharing '.$itemSourceName.' failed, because the user ' - .$shareWith.' is not a member of any groups that '.$uidOwner.' is a member of'; - \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); - throw new \Exception($message); - } - } - // Check if the item source is already shared with the user, either from the same owner or a different user - if ($checkExists = self::getItems($itemType, $itemSource, self::$shareTypeUserAndGroups, - $shareWith, null, self::FORMAT_NONE, null, 1, true, true)) { - // Only allow the same share to occur again if it is the same - // owner and is not a user share, this use case is for increasing - // permissions for a specific user - if ($checkExists['uid_owner'] != $uidOwner || $checkExists['share_type'] == $shareType) { - $message = 'Sharing '.$itemSourceName.' failed, because this item is already shared with '.$shareWith; - \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); - throw new \Exception($message); - } - } - } else if ($shareType === self::SHARE_TYPE_GROUP) { - if (!\OC_Group::groupExists($shareWith)) { - $message = 'Sharing '.$itemSourceName.' failed, because the group '.$shareWith.' does not exist'; - \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); - throw new \Exception($message); - } - if ($sharingPolicy == 'groups_only' && !\OC_Group::inGroup($uidOwner, $shareWith)) { - $message = 'Sharing '.$itemSourceName.' failed, because ' - .$uidOwner.' is not a member of the group '.$shareWith; - \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); - throw new \Exception($message); - } - // Check if the item source is already shared with the group, either from the same owner or a different user - // The check for each user in the group is done inside the put() function - if ($checkExists = self::getItems($itemType, $itemSource, self::SHARE_TYPE_GROUP, $shareWith, - null, self::FORMAT_NONE, null, 1, true, true)) { - // Only allow the same share to occur again if it is the same - // owner and is not a group share, this use case is for increasing - // permissions for a specific user - if ($checkExists['uid_owner'] != $uidOwner || $checkExists['share_type'] == $shareType) { - $message = 'Sharing '.$itemSourceName.' failed, because this item is already shared with '.$shareWith; - \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); - throw new \Exception($message); - } - } - // Convert share with into an array with the keys group and users - $group = $shareWith; - $shareWith = array(); - $shareWith['group'] = $group; - $shareWith['users'] = array_diff(\OC_Group::usersInGroup($group), array($uidOwner)); - } else if ($shareType === self::SHARE_TYPE_LINK) { - if (\OC_Appconfig::getValue('core', 'shareapi_allow_links', 'yes') == 'yes') { - // when updating a link share - if ($checkExists = self::getItems($itemType, $itemSource, self::SHARE_TYPE_LINK, null, - $uidOwner, self::FORMAT_NONE, null, 1)) { - // remember old token - $oldToken = $checkExists['token']; - $oldPermissions = $checkExists['permissions']; - //delete the old share - self::delete($checkExists['id']); - } - - // Generate hash of password - same method as user passwords - if (isset($shareWith)) { - $forcePortable = (CRYPT_BLOWFISH != 1); - $hasher = new \PasswordHash(8, $forcePortable); - $shareWith = $hasher->HashPassword($shareWith.\OC_Config::getValue('passwordsalt', '')); - } else { - // reuse the already set password, but only if we change permissions - // otherwise the user disabled the password protection - if ($checkExists && (int)$permissions !== (int)$oldPermissions) { - $shareWith = $checkExists['share_with']; - } - } - - // Generate token - if (isset($oldToken)) { - $token = $oldToken; - } else { - $token = \OC_Util::generateRandomBytes(self::TOKEN_LENGTH); - } - $result = self::put($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions, - null, $token, $itemSourceName); - if ($result) { - return $token; - } else { - return false; - } - } - $message = 'Sharing '.$itemSourceName.' failed, because sharing with links is not allowed'; - \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); - throw new \Exception($message); - return false; -// } else if ($shareType === self::SHARE_TYPE_CONTACT) { -// if (!\OC_App::isEnabled('contacts')) { -// $message = 'Sharing '.$itemSource.' failed, because the contacts app is not enabled'; -// \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); -// return false; -// } -// $vcard = \OC_Contacts_App::getContactVCard($shareWith); -// if (!isset($vcard)) { -// $message = 'Sharing '.$itemSource.' failed, because the contact does not exist'; -// \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); -// throw new \Exception($message); -// } -// $details = \OC_Contacts_VCard::structureContact($vcard); -// // TODO Add ownCloud user to contacts vcard -// if (!isset($details['EMAIL'])) { -// $message = 'Sharing '.$itemSource.' failed, because no email address is associated with the contact'; -// \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); -// throw new \Exception($message); -// } -// return self::shareItem($itemType, $itemSource, self::SHARE_TYPE_EMAIL, $details['EMAIL'], $permissions); - } else { - // Future share types need to include their own conditions - $message = 'Share type '.$shareType.' is not valid for '.$itemSource; - \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); - throw new \Exception($message); - } - // If the item is a folder, scan through the folder looking for equivalent item types -// if ($itemType == 'folder') { -// $parentFolder = self::put('folder', $itemSource, $shareType, $shareWith, $uidOwner, $permissions, true); -// if ($parentFolder && $files = \OC\Files\Filesystem::getDirectoryContent($itemSource)) { -// for ($i = 0; $i < count($files); $i++) { -// $name = substr($files[$i]['name'], strpos($files[$i]['name'], $itemSource) - strlen($itemSource)); -// if ($files[$i]['mimetype'] == 'httpd/unix-directory' -// && $children = \OC\Files\Filesystem::getDirectoryContent($name, '/') -// ) { -// // Continue scanning into child folders -// array_push($files, $children); -// } else { -// // Check file extension for an equivalent item type to convert to -// $extension = strtolower(substr($itemSource, strrpos($itemSource, '.') + 1)); -// foreach (self::$backends as $type => $backend) { -// if (isset($backend->dependsOn) && $backend->dependsOn == 'file' && isset($backend->supportedFileExtensions) && in_array($extension, $backend->supportedFileExtensions)) { -// $itemType = $type; -// break; -// } -// } -// // Pass on to put() to check if this item should be converted, the item won't be inserted into the database unless it can be converted -// self::put($itemType, $name, $shareType, $shareWith, $uidOwner, $permissions, $parentFolder); -// } -// } -// return true; -// } -// return false; -// } else { - // Put the item into the database - return self::put($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions, null, null, $itemSourceName); -// } + return \OC\Share\Share::shareItem($itemType, $itemSource, $shareType, $shareWith, $permissions, $itemSourceName); } /** @@ -651,88 +228,32 @@ class Share { * @param string Item source * @param int SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK * @param string User or group the item is being shared with - * @return boolean true on success or false on failure + * @return Returns true on success or false on failure */ public static function unshare($itemType, $itemSource, $shareType, $shareWith) { - if ($item = self::getItems($itemType, $itemSource, $shareType, $shareWith, \OC_User::getUser(), - self::FORMAT_NONE, null, 1)) { - self::unshareItem($item); - return true; - } - return false; + return \OC\Share\Share::unshare($itemType, $itemSource, $shareType, $shareWith); } /** * Unshare an item from all users, groups, and remove all links * @param string Item type * @param string Item source - * @param string $itemType - * @param string $itemSource - * @return boolean true on success or false on failure + * @return Returns true on success or false on failure */ public static function unshareAll($itemType, $itemSource) { - // Get all of the owners of shares of this item. - $query = \OC_DB::prepare( 'SELECT `uid_owner` from `*PREFIX*share` WHERE `item_type`=? AND `item_source`=?' ); - $result = $query->execute(array($itemType, $itemSource)); - $shares = array(); - // Add each owner's shares to the array of all shares for this item. - while ($row = $result->fetchRow()) { - $shares = array_merge($shares, self::getItems($itemType, $itemSource, null, null, $row['uid_owner'])); - } - if (!empty($shares)) { - // Pass all the vars we have for now, they may be useful - $hookParams = array( - 'itemType' => $itemType, - 'itemSource' => $itemSource, - 'shares' => $shares, - ); - \OC_Hook::emit('OCP\Share', 'pre_unshareAll', $hookParams); - foreach ($shares as $share) { - self::unshareItem($share); - } - \OC_Hook::emit('OCP\Share', 'post_unshareAll', $hookParams); - return true; - } - return false; + return \OC\Share\Share::unshareAll($itemType, $itemSource); } /** * Unshare an item shared with the current user * @param string Item type * @param string Item target - * @param string $itemType - * @param string $itemTarget - * @return boolean true on success or false on failure + * @return Returns true on success or false on failure * * Unsharing from self is not allowed for items inside collections */ public static function unshareFromSelf($itemType, $itemTarget) { - if ($item = self::getItemSharedWith($itemType, $itemTarget)) { - if ((int)$item['share_type'] === self::SHARE_TYPE_GROUP) { - // Insert an extra row for the group share and set permission - // to 0 to prevent it from showing up for the user - $query = \OC_DB::prepare('INSERT INTO `*PREFIX*share`' - .' (`item_type`, `item_source`, `item_target`, `parent`, `share_type`,' - .' `share_with`, `uid_owner`, `permissions`, `stime`, `file_source`, `file_target`)' - .' VALUES (?,?,?,?,?,?,?,?,?,?,?)'); - $query->execute(array($item['item_type'], $item['item_source'], $item['item_target'], - $item['id'], self::$shareTypeGroupUserUnique, - \OC_User::getUser(), $item['uid_owner'], 0, $item['stime'], $item['file_source'], - $item['file_target'])); - \OC_DB::insertid('*PREFIX*share'); - // Delete all reshares by this user of the group share - self::delete($item['id'], true, \OC_User::getUser()); - } else if ((int)$item['share_type'] === self::$shareTypeGroupUserUnique) { - // Set permission to 0 to prevent it from showing up for the user - $query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `permissions` = ? WHERE `id` = ?'); - $query->execute(array(0, $item['id'])); - self::delete($item['id'], true); - } else { - self::delete($item['id']); - } - return true; - } - return false; + return \OC\Share\Share::unshareFromSelf($itemType, $itemTarget); } /** * sent status if users got informed by mail about share @@ -742,102 +263,20 @@ class Share { * @param bool $status */ public static function setSendMailStatus($itemType, $itemSource, $shareType, $status) { - $status = $status ? 1 : 0; - - $query = \OC_DB::prepare( - 'UPDATE `*PREFIX*share` - SET `mail_send` = ? - WHERE `item_type` = ? AND `item_source` = ? AND `share_type` = ?'); - - $result = $query->execute(array($status, $itemType, $itemSource, $shareType)); - - if($result === false) { - \OC_Log::write('OCP\Share', 'Couldn\'t set send mail status', \OC_Log::ERROR); - } + return \OC\Share\Share::setSendMailStatus($itemType, $itemSource, $shareType, $status); } /** * Set the permissions of an item for a specific user or group - * @param string $itemType Item type - * @param string $itemSource Item source - * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK - * @param string $shareWith User or group the item is being shared with - * @param integer|null $permissions CRUDS - * @return boolean true on success or false on failure + * @param string Item type + * @param string Item source + * @param int SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK + * @param string User or group the item is being shared with + * @param int CRUDS permissions + * @return Returns true on success or false on failure */ public static function setPermissions($itemType, $itemSource, $shareType, $shareWith, $permissions) { - if ($item = self::getItems($itemType, $itemSource, $shareType, $shareWith, - \OC_User::getUser(), self::FORMAT_NONE, null, 1, false)) { - // Check if this item is a reshare and verify that the permissions - // granted don't exceed the parent shared item - if (isset($item['parent'])) { - $query = \OC_DB::prepare('SELECT `permissions` FROM `*PREFIX*share` WHERE `id` = ?', 1); - $result = $query->execute(array($item['parent']))->fetchRow(); - if (~(int)$result['permissions'] & $permissions) { - $message = 'Setting permissions for '.$itemSource.' failed,' - .' because the permissions exceed permissions granted to '.\OC_User::getUser(); - \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); - throw new \Exception($message); - } - } - $query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `permissions` = ? WHERE `id` = ?'); - $query->execute(array($permissions, $item['id'])); - if ($itemType === 'file' || $itemType === 'folder') { - \OC_Hook::emit('OCP\Share', 'post_update_permissions', array( - 'itemType' => $itemType, - 'itemSource' => $itemSource, - 'shareType' => $shareType, - 'shareWith' => $shareWith, - 'uidOwner' => \OC_User::getUser(), - 'permissions' => $permissions, - 'path' => $item['path'], - )); - } - // Check if permissions were removed - if ($item['permissions'] & ~$permissions) { - // If share permission is removed all reshares must be deleted - if (($item['permissions'] & PERMISSION_SHARE) && (~$permissions & PERMISSION_SHARE)) { - self::delete($item['id'], true); - } else { - $ids = array(); - $parents = array($item['id']); - while (!empty($parents)) { - $parents = "'".implode("','", $parents)."'"; - $query = \OC_DB::prepare('SELECT `id`, `permissions` FROM `*PREFIX*share`' - .' WHERE `parent` IN ('.$parents.')'); - $result = $query->execute(); - // Reset parents array, only go through loop again if - // items are found that need permissions removed - $parents = array(); - while ($item = $result->fetchRow()) { - // Check if permissions need to be removed - if ($item['permissions'] & ~$permissions) { - // Add to list of items that need permissions removed - $ids[] = $item['id']; - $parents[] = $item['id']; - } - } - } - // Remove the permissions for all reshares of this item - if (!empty($ids)) { - $ids = "'".implode("','", $ids)."'"; - // TODO this should be done with Doctrine platform objects - if (\OC_Config::getValue( "dbtype") === 'oci') { - $andOp = 'BITAND(`permissions`, ?)'; - } else { - $andOp = '`permissions` & ?'; - } - $query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `permissions` = '.$andOp - .' WHERE `id` IN ('.$ids.')'); - $query->execute(array($permissions)); - } - } - } - return true; - } - $message = 'Setting permissions for '.$itemSource.' failed, because the item was not found'; - \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); - throw new \Exception($message); + return \OC\Share\Share::setPermissions($itemType, $itemSource, $shareType, $shareWith, $permissions); } /** @@ -845,67 +284,10 @@ class Share { * @param string $itemType * @param string $itemSource * @param string $date expiration date - * @return boolean + * @return Share_Backend */ public static function setExpirationDate($itemType, $itemSource, $date) { - if ($items = self::getItems($itemType, $itemSource, null, null, \OC_User::getUser(), - self::FORMAT_NONE, null, -1, false)) { - if (!empty($items)) { - if ($date == '') { - $date = null; - } else { - $date = new \DateTime($date); - } - $query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `expiration` = ? WHERE `id` = ?'); - $query->bindValue(1, $date, 'datetime'); - foreach ($items as $item) { - $query->bindValue(2, (int) $item['id']); - $query->execute(); - } - return true; - } - } - return false; - } - - /** - * Checks whether a share has expired, calls unshareItem() if yes. - * @param array $item Share data (usually database row) - * @return bool True if item was expired, false otherwise. - */ - protected static function expireItem(array $item) { - if (!empty($item['expiration'])) { - $now = new \DateTime(); - $expires = new \DateTime($item['expiration']); - if ($now > $expires) { - self::unshareItem($item); - return true; - } - } - return false; - } - - /** - * Unshares a share given a share data array - * @param array $item Share data (usually database row) - * @return null - */ - protected static function unshareItem(array $item) { - // Pass all the vars we have for now, they may be useful - $hookParams = array( - 'itemType' => $item['item_type'], - 'itemSource' => $item['item_source'], - 'shareType' => $item['share_type'], - 'shareWith' => $item['share_with'], - 'itemParent' => $item['parent'], - 'uidOwner' => $item['uid_owner'], - ); - - \OC_Hook::emit('OCP\Share', 'pre_unshare', $hookParams + array( - 'fileSource' => $item['file_source'], - )); - self::delete($item['id']); - \OC_Hook::emit('OCP\Share', 'post_unshare', $hookParams); + return \OC\Share\Share::setExpirationDate($itemType, $itemSource, $date); } /** @@ -914,1018 +296,14 @@ class Share { * @return Share_Backend */ public static function getBackend($itemType) { - if (isset(self::$backends[$itemType])) { - return self::$backends[$itemType]; - } else if (isset(self::$backendTypes[$itemType]['class'])) { - $class = self::$backendTypes[$itemType]['class']; - if (class_exists($class)) { - self::$backends[$itemType] = new $class; - if (!(self::$backends[$itemType] instanceof Share_Backend)) { - $message = 'Sharing backend '.$class.' must implement the interface OCP\Share_Backend'; - \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); - throw new \Exception($message); - } - return self::$backends[$itemType]; - } else { - $message = 'Sharing backend '.$class.' not found'; - \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); - throw new \Exception($message); - } - } - $message = 'Sharing backend for '.$itemType.' not found'; - \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); - throw new \Exception($message); - } - - /** - * Check if resharing is allowed - * @return boolean true if allowed or false - * - * Resharing is allowed by default if not configured - */ - private static function isResharingAllowed() { - if (!isset(self::$isResharingAllowed)) { - if (\OC_Appconfig::getValue('core', 'shareapi_allow_resharing', 'yes') == 'yes') { - self::$isResharingAllowed = true; - } else { - self::$isResharingAllowed = false; - } - } - return self::$isResharingAllowed; - } - - /** - * Get a list of collection item types for the specified item type - * @param string Item type - * @return array - */ - private static function getCollectionItemTypes($itemType) { - $collectionTypes = array($itemType); - foreach (self::$backendTypes as $type => $backend) { - if (in_array($backend['collectionOf'], $collectionTypes)) { - $collectionTypes[] = $type; - } - } - // TODO Add option for collections to be collection of themselves, only 'folder' does it now... - if (!self::getBackend($itemType) instanceof Share_Backend_Collection || $itemType != 'folder') { - unset($collectionTypes[0]); - } - // Return array if collections were found or the item type is a - // collection itself - collections can be inside collections - if (count($collectionTypes) > 0) { - return $collectionTypes; - } - return false; - } - - /** - * Get shared items from the database - * @param string Item type - * @param string Item source or target (optional) - * @param int SHARE_TYPE_USER, SHARE_TYPE_GROUP, SHARE_TYPE_LINK, $shareTypeUserAndGroups, or $shareTypeGroupUserUnique - * @param string User or group the item is being shared with - * @param string User that is the owner of shared items (optional) - * @param int Format to convert items to with formatItems() - * @param mixed Parameters to pass to formatItems() - * @param int Number of items to return, -1 to return all matches (optional) - * @param bool Include collection item types (optional) - * @param bool TODO (optional) - * @prams bool check expire date - * @return mixed - * - * See public functions getItem(s)... for parameter usage - * - */ - private static function getItems($itemType, $item = null, $shareType = null, $shareWith = null, - $uidOwner = null, $format = self::FORMAT_NONE, $parameters = null, $limit = -1, - $includeCollections = false, $itemShareWithBySource = false, $checkExpireDate = true) { - if (!self::isEnabled()) { - if ($limit == 1 || (isset($uidOwner) && isset($item))) { - return false; - } else { - return array(); - } - } - $backend = self::getBackend($itemType); - $collectionTypes = false; - // Get filesystem root to add it to the file target and remove from the - // file source, match file_source with the file cache - if ($itemType == 'file' || $itemType == 'folder') { - if(!is_null($uidOwner)) { - $root = \OC\Files\Filesystem::getRoot(); - } else { - $root = ''; - } - $where = 'INNER JOIN `*PREFIX*filecache` ON `file_source` = `*PREFIX*filecache`.`fileid`'; - if (!isset($item)) { - $where .= ' WHERE `file_target` IS NOT NULL'; - } - $fileDependent = true; - $queryArgs = array(); - } else { - $fileDependent = false; - $root = ''; - if ($includeCollections && !isset($item) && ($collectionTypes = self::getCollectionItemTypes($itemType))) { - // If includeCollections is true, find collections of this item type, e.g. a music album contains songs - if (!in_array($itemType, $collectionTypes)) { - $itemTypes = array_merge(array($itemType), $collectionTypes); - } else { - $itemTypes = $collectionTypes; - } - $placeholders = join(',', array_fill(0, count($itemTypes), '?')); - $where = ' WHERE `item_type` IN ('.$placeholders.'))'; - $queryArgs = $itemTypes; - } else { - $where = ' WHERE `item_type` = ?'; - $queryArgs = array($itemType); - } - } - if (\OC_Appconfig::getValue('core', 'shareapi_allow_links', 'yes') !== 'yes') { - $where .= ' AND `share_type` != ?'; - $queryArgs[] = self::SHARE_TYPE_LINK; - } - if (isset($shareType)) { - // Include all user and group items - if ($shareType == self::$shareTypeUserAndGroups && isset($shareWith)) { - $where .= ' AND `share_type` IN (?,?,?)'; - $queryArgs[] = self::SHARE_TYPE_USER; - $queryArgs[] = self::SHARE_TYPE_GROUP; - $queryArgs[] = self::$shareTypeGroupUserUnique; - $userAndGroups = array_merge(array($shareWith), \OC_Group::getUserGroups($shareWith)); - $placeholders = join(',', array_fill(0, count($userAndGroups), '?')); - $where .= ' AND `share_with` IN ('.$placeholders.')'; - $queryArgs = array_merge($queryArgs, $userAndGroups); - // Don't include own group shares - $where .= ' AND `uid_owner` != ?'; - $queryArgs[] = $shareWith; - } else { - $where .= ' AND `share_type` = ?'; - $queryArgs[] = $shareType; - if (isset($shareWith)) { - $where .= ' AND `share_with` = ?'; - $queryArgs[] = $shareWith; - } - } - } - if (isset($uidOwner)) { - $where .= ' AND `uid_owner` = ?'; - $queryArgs[] = $uidOwner; - if (!isset($shareType)) { - // Prevent unique user targets for group shares from being selected - $where .= ' AND `share_type` != ?'; - $queryArgs[] = self::$shareTypeGroupUserUnique; - } - if ($itemType == 'file' || $itemType == 'folder') { - $column = 'file_source'; - } else { - $column = 'item_source'; - } - } else { - if ($itemType == 'file' || $itemType == 'folder') { - $column = 'file_target'; - } else { - $column = 'item_target'; - } - } - if (isset($item)) { - if ($includeCollections && $collectionTypes = self::getCollectionItemTypes($itemType)) { - $where .= ' AND ('; - } else { - $where .= ' AND'; - } - // If looking for own shared items, check item_source else check item_target - if (isset($uidOwner) || $itemShareWithBySource) { - // If item type is a file, file source needs to be checked in case the item was converted - if ($itemType == 'file' || $itemType == 'folder') { - $where .= ' `file_source` = ?'; - $column = 'file_source'; - } else { - $where .= ' `item_source` = ?'; - $column = 'item_source'; - } - } else { - if ($itemType == 'file' || $itemType == 'folder') { - $where .= ' `file_target` = ?'; - $item = \OC\Files\Filesystem::normalizePath($item); - } else { - $where .= ' `item_target` = ?'; - } - } - $queryArgs[] = $item; - if ($includeCollections && $collectionTypes) { - $placeholders = join(',', array_fill(0, count($collectionTypes), '?')); - $where .= ' OR `item_type` IN ('.$placeholders.'))'; - $queryArgs = array_merge($queryArgs, $collectionTypes); - } - } - if ($limit != -1 && !$includeCollections) { - if ($shareType == self::$shareTypeUserAndGroups) { - // Make sure the unique user target is returned if it exists, - // unique targets should follow the group share in the database - // If the limit is not 1, the filtering can be done later - $where .= ' ORDER BY `*PREFIX*share`.`id` DESC'; - } - // The limit must be at least 3, because filtering needs to be done - if ($limit < 3) { - $queryLimit = 3; - } else { - $queryLimit = $limit; - } - } else { - $queryLimit = null; - } - // TODO Optimize selects - if ($format == self::FORMAT_STATUSES) { - if ($itemType == 'file' || $itemType == 'folder') { - $select = '`*PREFIX*share`.`id`, `item_type`, `item_source`, `*PREFIX*share`.`parent`,' - .' `share_type`, `file_source`, `path`, `expiration`, `storage`, `share_with`, `mail_send`, `uid_owner`'; - } else { - $select = '`id`, `item_type`, `item_source`, `parent`, `share_type`, `share_with`, `expiration`, `mail_send`, `uid_owner`'; - } - } else { - if (isset($uidOwner)) { - if ($itemType == 'file' || $itemType == 'folder') { - $select = '`*PREFIX*share`.`id`, `item_type`, `item_source`, `*PREFIX*share`.`parent`,' - .' `share_type`, `share_with`, `file_source`, `path`, `permissions`, `stime`,' - .' `expiration`, `token`, `storage`, `mail_send`, `uid_owner`'; - } else { - $select = '`id`, `item_type`, `item_source`, `parent`, `share_type`, `share_with`, `permissions`,' - .' `stime`, `file_source`, `expiration`, `token`, `mail_send`, `uid_owner`'; - } - } else { - if ($fileDependent) { - if (($itemType == 'file' || $itemType == 'folder') - && $format == \OC_Share_Backend_File::FORMAT_GET_FOLDER_CONTENTS - || $format == \OC_Share_Backend_File::FORMAT_FILE_APP_ROOT - ) { - $select = '`*PREFIX*share`.`id`, `item_type`, `item_source`, `*PREFIX*share`.`parent`, `uid_owner`, ' - .'`share_type`, `share_with`, `file_source`, `path`, `file_target`, ' - .'`permissions`, `expiration`, `storage`, `*PREFIX*filecache`.`parent` as `file_parent`, ' - .'`name`, `mtime`, `mimetype`, `mimepart`, `size`, `unencrypted_size`, `encrypted`, `etag`, `mail_send`'; - } else { - $select = '`*PREFIX*share`.`id`, `item_type`, `item_source`, `item_target`, - `*PREFIX*share`.`parent`, `share_type`, `share_with`, `uid_owner`, - `file_source`, `path`, `file_target`, `permissions`, `stime`, `expiration`, `token`, `storage`, `mail_send`'; - } - } else { - $select = '*'; - } - } - } - $root = strlen($root); - $query = \OC_DB::prepare('SELECT '.$select.' FROM `*PREFIX*share` '.$where, $queryLimit); - $result = $query->execute($queryArgs); - if (\OC_DB::isError($result)) { - \OC_Log::write('OCP\Share', - \OC_DB::getErrorMessage($result) . ', select=' . $select . ' where=' . $where, - \OC_Log::ERROR); - } - $items = array(); - $targets = array(); - $switchedItems = array(); - $mounts = array(); - while ($row = $result->fetchRow()) { - if (isset($row['id'])) { - $row['id']=(int)$row['id']; - } - if (isset($row['share_type'])) { - $row['share_type']=(int)$row['share_type']; - } - if (isset($row['parent'])) { - $row['parent']=(int)$row['parent']; - } - if (isset($row['file_parent'])) { - $row['file_parent']=(int)$row['file_parent']; - } - if (isset($row['file_source'])) { - $row['file_source']=(int)$row['file_source']; - } - if (isset($row['permissions'])) { - $row['permissions']=(int)$row['permissions']; - } - if (isset($row['storage'])) { - $row['storage']=(int)$row['storage']; - } - if (isset($row['stime'])) { - $row['stime']=(int)$row['stime']; - } - // Filter out duplicate group shares for users with unique targets - if ($row['share_type'] == self::$shareTypeGroupUserUnique && isset($items[$row['parent']])) { - $row['share_type'] = self::SHARE_TYPE_GROUP; - $row['share_with'] = $items[$row['parent']]['share_with']; - // Remove the parent group share - unset($items[$row['parent']]); - if ($row['permissions'] == 0) { - continue; - } - } else if (!isset($uidOwner)) { - // Check if the same target already exists - if (isset($targets[$row[$column]])) { - // Check if the same owner shared with the user twice - // through a group and user share - this is allowed - $id = $targets[$row[$column]]; - if (isset($items[$id]) && $items[$id]['uid_owner'] == $row['uid_owner']) { - // Switch to group share type to ensure resharing conditions aren't bypassed - if ($items[$id]['share_type'] != self::SHARE_TYPE_GROUP) { - $items[$id]['share_type'] = self::SHARE_TYPE_GROUP; - $items[$id]['share_with'] = $row['share_with']; - } - // Switch ids if sharing permission is granted on only - // one share to ensure correct parent is used if resharing - if (~(int)$items[$id]['permissions'] & PERMISSION_SHARE - && (int)$row['permissions'] & PERMISSION_SHARE) { - $items[$row['id']] = $items[$id]; - $switchedItems[$id] = $row['id']; - unset($items[$id]); - $id = $row['id']; - } - // Combine the permissions for the item - $items[$id]['permissions'] |= (int)$row['permissions']; - continue; - } - } else { - $targets[$row[$column]] = $row['id']; - } - } - // Remove root from file source paths if retrieving own shared items - if (isset($uidOwner) && isset($row['path'])) { - if (isset($row['parent'])) { - // FIXME: Doesn't always construct the correct path, example: - // Folder '/a/b', share '/a' and '/a/b' to user2 - // user2 reshares /Shared/b and ask for share status of /Shared/a/b - // expected result: path=/Shared/a/b; actual result /Shared/b because of the parent - $query = \OC_DB::prepare('SELECT `file_target` FROM `*PREFIX*share` WHERE `id` = ?'); - $parentResult = $query->execute(array($row['parent'])); - if (\OC_DB::isError($result)) { - \OC_Log::write('OCP\Share', 'Can\'t select parent: ' . - \OC_DB::getErrorMessage($result) . ', select=' . $select . ' where=' . $where, - \OC_Log::ERROR); - } else { - $parentRow = $parentResult->fetchRow(); - $tmpPath = '/Shared' . $parentRow['file_target']; - // find the right position where the row path continues from the target path - $pos = strrpos($row['path'], $parentRow['file_target']); - $subPath = substr($row['path'], $pos); - $splitPath = explode('/', $subPath); - foreach (array_slice($splitPath, 2) as $pathPart) { - $tmpPath = $tmpPath . '/' . $pathPart; - } - $row['path'] = $tmpPath; - } - } else { - if (!isset($mounts[$row['storage']])) { - $mountPoints = \OC\Files\Filesystem::getMountByNumericId($row['storage']); - if (is_array($mountPoints)) { - $mounts[$row['storage']] = current($mountPoints); - } - } - if ($mounts[$row['storage']]) { - $path = $mounts[$row['storage']]->getMountPoint().$row['path']; - $row['path'] = substr($path, $root); - } - } - } - if($checkExpireDate) { - if (self::expireItem($row)) { - continue; - } - } - // Check if resharing is allowed, if not remove share permission - if (isset($row['permissions']) && !self::isResharingAllowed()) { - $row['permissions'] &= ~PERMISSION_SHARE; - } - // Add display names to result - if ( isset($row['share_with']) && $row['share_with'] != '') { - $row['share_with_displayname'] = \OCP\User::getDisplayName($row['share_with']); - } - if ( isset($row['uid_owner']) && $row['uid_owner'] != '') { - $row['displayname_owner'] = \OCP\User::getDisplayName($row['uid_owner']); - } - - $items[$row['id']] = $row; - } - if (!empty($items)) { - $collectionItems = array(); - foreach ($items as &$row) { - // Return only the item instead of a 2-dimensional array - if ($limit == 1 && $row[$column] == $item && ($row['item_type'] == $itemType || $itemType == 'file')) { - if ($format == self::FORMAT_NONE) { - return $row; - } else { - break; - } - } - // Check if this is a collection of the requested item type - if ($includeCollections && $collectionTypes && in_array($row['item_type'], $collectionTypes)) { - if (($collectionBackend = self::getBackend($row['item_type'])) - && $collectionBackend instanceof Share_Backend_Collection) { - // Collections can be inside collections, check if the item is a collection - if (isset($item) && $row['item_type'] == $itemType && $row[$column] == $item) { - $collectionItems[] = $row; - } else { - $collection = array(); - $collection['item_type'] = $row['item_type']; - if ($row['item_type'] == 'file' || $row['item_type'] == 'folder') { - $collection['path'] = basename($row['path']); - } - $row['collection'] = $collection; - // Fetch all of the children sources - $children = $collectionBackend->getChildren($row[$column]); - foreach ($children as $child) { - $childItem = $row; - $childItem['item_type'] = $itemType; - if ($row['item_type'] != 'file' && $row['item_type'] != 'folder') { - $childItem['item_source'] = $child['source']; - $childItem['item_target'] = $child['target']; - } - if ($backend instanceof Share_Backend_File_Dependent) { - if ($row['item_type'] == 'file' || $row['item_type'] == 'folder') { - $childItem['file_source'] = $child['source']; - } else { - $meta = \OC\Files\Filesystem::getFileInfo($child['file_path']); - $childItem['file_source'] = $meta['fileid']; - } - $childItem['file_target'] = - \OC\Files\Filesystem::normalizePath($child['file_path']); - } - if (isset($item)) { - if ($childItem[$column] == $item) { - // Return only the item instead of a 2-dimensional array - if ($limit == 1) { - if ($format == self::FORMAT_NONE) { - return $childItem; - } else { - // Unset the items array and break out of both loops - $items = array(); - $items[] = $childItem; - break 2; - } - } else { - $collectionItems[] = $childItem; - } - } - } else { - $collectionItems[] = $childItem; - } - } - } - } - // Remove collection item - $toRemove = $row['id']; - if (array_key_exists($toRemove, $switchedItems)) { - $toRemove = $switchedItems[$toRemove]; - } - unset($items[$toRemove]); - } - } - if (!empty($collectionItems)) { - $items = array_merge($items, $collectionItems); - } - if (empty($items) && $limit == 1) { - return false; - } - if ($format == self::FORMAT_NONE) { - return $items; - } else if ($format == self::FORMAT_STATUSES) { - $statuses = array(); - foreach ($items as $item) { - if ($item['share_type'] == self::SHARE_TYPE_LINK) { - $statuses[$item[$column]]['link'] = true; - } else if (!isset($statuses[$item[$column]])) { - $statuses[$item[$column]]['link'] = false; - } - if ($itemType == 'file' || $itemType == 'folder') { - $statuses[$item[$column]]['path'] = $item['path']; - } - } - return $statuses; - } else { - return $backend->formatItems($items, $format, $parameters); - } - } else if ($limit == 1 || (isset($uidOwner) && isset($item))) { - return false; - } - return array(); - } - - /** - * Put shared item into the database - * @param string $itemType Item type - * @param string $itemSource Item source - * @param integer $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK - * @param string $shareWith User or group the item is being shared with - * @param string $uidOwner User that is the owner of shared item - * @param int $permissions CRUDS permissions - * @param bool|array, $parentFolder Parent folder target (optional) - * @param string $token (optional) - * @param string $itemSourceName name of the source item (optional) - * @return bool Returns true on success or false on failure - */ - private static function put($itemType, $itemSource, $shareType, $shareWith, $uidOwner, - $permissions, $parentFolder = null, $token = null, $itemSourceName = null) { - $backend = self::getBackend($itemType); - - // Check if this is a reshare - if ($checkReshare = self::getItemSharedWithBySource($itemType, $itemSource, self::FORMAT_NONE, null, true)) { - - // Check if attempting to share back to owner - if ($checkReshare['uid_owner'] == $shareWith && $shareType == self::SHARE_TYPE_USER) { - $message = 'Sharing '.$itemSourceName.' failed, because the user '.$shareWith.' is the original sharer'; - \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); - throw new \Exception($message); - } - // Check if share permissions is granted - if (self::isResharingAllowed() && (int)$checkReshare['permissions'] & PERMISSION_SHARE) { - if (~(int)$checkReshare['permissions'] & $permissions) { - $message = 'Sharing '.$itemSourceName - .' failed, because the permissions exceed permissions granted to '.$uidOwner; - \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); - throw new \Exception($message); - } else { - // TODO Don't check if inside folder - $parent = $checkReshare['id']; - $itemSource = $checkReshare['item_source']; - $fileSource = $checkReshare['file_source']; - $suggestedItemTarget = $checkReshare['item_target']; - $suggestedFileTarget = $checkReshare['file_target']; - $filePath = $checkReshare['file_target']; - } - } else { - $message = 'Sharing '.$itemSourceName.' failed, because resharing is not allowed'; - \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); - throw new \Exception($message); - } - } else { - $parent = null; - $suggestedItemTarget = null; - $suggestedFileTarget = null; - if (!$backend->isValidSource($itemSource, $uidOwner)) { - $message = 'Sharing '.$itemSource.' failed, because the sharing backend for ' - .$itemType.' could not find its source'; - \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); - throw new \Exception($message); - } - $parent = null; - if ($backend instanceof Share_Backend_File_Dependent) { - $filePath = $backend->getFilePath($itemSource, $uidOwner); - if ($itemType == 'file' || $itemType == 'folder') { - $fileSource = $itemSource; - } else { - $meta = \OC\Files\Filesystem::getFileInfo($filePath); - $fileSource = $meta['fileid']; - } - if ($fileSource == -1) { - $message = 'Sharing '.$itemSource.' failed, because the file could not be found in the file cache'; - \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); - throw new \Exception($message); - } - } else { - $filePath = null; - $fileSource = null; - } - } - $query = \OC_DB::prepare('INSERT INTO `*PREFIX*share` (`item_type`, `item_source`, `item_target`,' - .' `parent`, `share_type`, `share_with`, `uid_owner`, `permissions`, `stime`, `file_source`,' - .' `file_target`, `token`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)'); - // Share with a group - if ($shareType == self::SHARE_TYPE_GROUP) { - $groupItemTarget = self::generateTarget($itemType, $itemSource, $shareType, $shareWith['group'], - $uidOwner, $suggestedItemTarget); - $run = true; - $error = ''; - \OC_Hook::emit('OCP\Share', 'pre_shared', array( - 'itemType' => $itemType, - 'itemSource' => $itemSource, - 'itemTarget' => $groupItemTarget, - 'shareType' => $shareType, - 'shareWith' => $shareWith['group'], - 'uidOwner' => $uidOwner, - 'permissions' => $permissions, - 'fileSource' => $fileSource, - 'token' => $token, - 'run' => &$run, - 'error' => &$error - )); - - if ($run === false) { - throw new \Exception($error); - } - - if (isset($fileSource)) { - if ($parentFolder) { - if ($parentFolder === true) { - $groupFileTarget = self::generateTarget('file', $filePath, $shareType, - $shareWith['group'], $uidOwner, $suggestedFileTarget); - // Set group default file target for future use - $parentFolders[0]['folder'] = $groupFileTarget; - } else { - // Get group default file target - $groupFileTarget = $parentFolder[0]['folder'].$itemSource; - $parent = $parentFolder[0]['id']; - } - } else { - $groupFileTarget = self::generateTarget('file', $filePath, $shareType, $shareWith['group'], - $uidOwner, $suggestedFileTarget); - } - } else { - $groupFileTarget = null; - } - $query->execute(array($itemType, $itemSource, $groupItemTarget, $parent, $shareType, - $shareWith['group'], $uidOwner, $permissions, time(), $fileSource, $groupFileTarget, $token)); - // Save this id, any extra rows for this group share will need to reference it - $parent = \OC_DB::insertid('*PREFIX*share'); - // Loop through all users of this group in case we need to add an extra row - foreach ($shareWith['users'] as $uid) { - $itemTarget = self::generateTarget($itemType, $itemSource, self::SHARE_TYPE_USER, $uid, - $uidOwner, $suggestedItemTarget, $parent); - if (isset($fileSource)) { - if ($parentFolder) { - if ($parentFolder === true) { - $fileTarget = self::generateTarget('file', $filePath, self::SHARE_TYPE_USER, $uid, - $uidOwner, $suggestedFileTarget, $parent); - if ($fileTarget != $groupFileTarget) { - $parentFolders[$uid]['folder'] = $fileTarget; - } - } else if (isset($parentFolder[$uid])) { - $fileTarget = $parentFolder[$uid]['folder'].$itemSource; - $parent = $parentFolder[$uid]['id']; - } - } else { - $fileTarget = self::generateTarget('file', $filePath, self::SHARE_TYPE_USER, - $uid, $uidOwner, $suggestedFileTarget, $parent); - } - } else { - $fileTarget = null; - } - // Insert an extra row for the group share if the item or file target is unique for this user - if ($itemTarget != $groupItemTarget || (isset($fileSource) && $fileTarget != $groupFileTarget)) { - $query->execute(array($itemType, $itemSource, $itemTarget, $parent, - self::$shareTypeGroupUserUnique, $uid, $uidOwner, $permissions, time(), - $fileSource, $fileTarget, $token)); - $id = \OC_DB::insertid('*PREFIX*share'); - } - } - \OC_Hook::emit('OCP\Share', 'post_shared', array( - 'itemType' => $itemType, - 'itemSource' => $itemSource, - 'itemTarget' => $groupItemTarget, - 'parent' => $parent, - 'shareType' => $shareType, - 'shareWith' => $shareWith['group'], - 'uidOwner' => $uidOwner, - 'permissions' => $permissions, - 'fileSource' => $fileSource, - 'fileTarget' => $groupFileTarget, - 'id' => $parent, - 'token' => $token - )); - - if ($parentFolder === true) { - // Return parent folders to preserve file target paths for potential children - return $parentFolders; - } - } else { - $itemTarget = self::generateTarget($itemType, $itemSource, $shareType, $shareWith, $uidOwner, - $suggestedItemTarget); - $run = true; - $error = ''; - \OC_Hook::emit('OCP\Share', 'pre_shared', array( - 'itemType' => $itemType, - 'itemSource' => $itemSource, - 'itemTarget' => $itemTarget, - 'shareType' => $shareType, - 'shareWith' => $shareWith, - 'uidOwner' => $uidOwner, - 'permissions' => $permissions, - 'fileSource' => $fileSource, - 'token' => $token, - 'run' => &$run, - 'error' => &$error - )); - - if ($run === false) { - throw new \Exception($error); - } - - if (isset($fileSource)) { - if ($parentFolder) { - if ($parentFolder === true) { - $fileTarget = self::generateTarget('file', $filePath, $shareType, $shareWith, - $uidOwner, $suggestedFileTarget); - $parentFolders['folder'] = $fileTarget; - } else { - $fileTarget = $parentFolder['folder'].$itemSource; - $parent = $parentFolder['id']; - } - } else { - $fileTarget = self::generateTarget('file', $filePath, $shareType, $shareWith, $uidOwner, - $suggestedFileTarget); - } - } else { - $fileTarget = null; - } - $query->execute(array($itemType, $itemSource, $itemTarget, $parent, $shareType, $shareWith, $uidOwner, - $permissions, time(), $fileSource, $fileTarget, $token)); - $id = \OC_DB::insertid('*PREFIX*share'); - \OC_Hook::emit('OCP\Share', 'post_shared', array( - 'itemType' => $itemType, - 'itemSource' => $itemSource, - 'itemTarget' => $itemTarget, - 'parent' => $parent, - 'shareType' => $shareType, - 'shareWith' => $shareWith, - 'uidOwner' => $uidOwner, - 'permissions' => $permissions, - 'fileSource' => $fileSource, - 'fileTarget' => $fileTarget, - 'id' => $id, - 'token' => $token - )); - if ($parentFolder === true) { - $parentFolders['id'] = $id; - // Return parent folder to preserve file target paths for potential children - return $parentFolders; - } - } - return true; - } - - /** - * Generate a unique target for the item - * @param string Item type - * @param string Item source - * @param int SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK - * @param string User or group the item is being shared with - * @param string User that is the owner of shared item - * @param string The suggested target originating from a reshare (optional) - * @param int The id of the parent group share (optional) - * @param integer $shareType - * @return string Item target - */ - private static function generateTarget($itemType, $itemSource, $shareType, $shareWith, $uidOwner, - $suggestedTarget = null, $groupParent = null) { - $backend = self::getBackend($itemType); - if ($shareType == self::SHARE_TYPE_LINK) { - if (isset($suggestedTarget)) { - return $suggestedTarget; - } - return $backend->generateTarget($itemSource, false); - } else { - if ($itemType == 'file' || $itemType == 'folder') { - $column = 'file_target'; - $columnSource = 'file_source'; - } else { - $column = 'item_target'; - $columnSource = 'item_source'; - } - if ($shareType == self::SHARE_TYPE_USER) { - // Share with is a user, so set share type to user and groups - $shareType = self::$shareTypeUserAndGroups; - $userAndGroups = array_merge(array($shareWith), \OC_Group::getUserGroups($shareWith)); - } else { - $userAndGroups = false; - } - $exclude = null; - // Backend has 3 opportunities to generate a unique target - for ($i = 0; $i < 2; $i++) { - // Check if suggested target exists first - if ($i == 0 && isset($suggestedTarget)) { - $target = $suggestedTarget; - } else { - if ($shareType == self::SHARE_TYPE_GROUP) { - $target = $backend->generateTarget($itemSource, false, $exclude); - } else { - $target = $backend->generateTarget($itemSource, $shareWith, $exclude); - } - if (is_array($exclude) && in_array($target, $exclude)) { - break; - } - } - // Check if target already exists - $checkTarget = self::getItems($itemType, $target, $shareType, $shareWith); - if (!empty($checkTarget)) { - foreach ($checkTarget as $item) { - // Skip item if it is the group parent row - if (isset($groupParent) && $item['id'] == $groupParent) { - if (count($checkTarget) == 1) { - return $target; - } else { - continue; - } - } - if ($item['uid_owner'] == $uidOwner) { - if ($itemType == 'file' || $itemType == 'folder') { - $meta = \OC\Files\Filesystem::getFileInfo($itemSource); - if ($item['file_source'] == $meta['fileid']) { - return $target; - } - } else if ($item['item_source'] == $itemSource) { - return $target; - } - } - } - if (!isset($exclude)) { - $exclude = array(); - } - // Find similar targets to improve backend's chances to generate a unqiue target - if ($userAndGroups) { - if ($column == 'file_target') { - $checkTargets = \OC_DB::prepare('SELECT `'.$column.'` FROM `*PREFIX*share`' - .' WHERE `item_type` IN (\'file\', \'folder\')' - .' AND `share_type` IN (?,?,?)' - .' AND `share_with` IN (\''.implode('\',\'', $userAndGroups).'\')'); - $result = $checkTargets->execute(array(self::SHARE_TYPE_USER, self::SHARE_TYPE_GROUP, - self::$shareTypeGroupUserUnique)); - } else { - $checkTargets = \OC_DB::prepare('SELECT `'.$column.'` FROM `*PREFIX*share`' - .' WHERE `item_type` = ? AND `share_type` IN (?,?,?)' - .' AND `share_with` IN (\''.implode('\',\'', $userAndGroups).'\')'); - $result = $checkTargets->execute(array($itemType, self::SHARE_TYPE_USER, - self::SHARE_TYPE_GROUP, self::$shareTypeGroupUserUnique)); - } - } else { - if ($column == 'file_target') { - $checkTargets = \OC_DB::prepare('SELECT `'.$column.'` FROM `*PREFIX*share`' - .' WHERE `item_type` IN (\'file\', \'folder\')' - .' AND `share_type` = ? AND `share_with` = ?'); - $result = $checkTargets->execute(array(self::SHARE_TYPE_GROUP, $shareWith)); - } else { - $checkTargets = \OC_DB::prepare('SELECT `'.$column.'` FROM `*PREFIX*share`' - .' WHERE `item_type` = ? AND `share_type` = ? AND `share_with` = ?'); - $result = $checkTargets->execute(array($itemType, self::SHARE_TYPE_GROUP, $shareWith)); - } - } - while ($row = $result->fetchRow()) { - $exclude[] = $row[$column]; - } - } else { - return $target; - } - } - } - $message = 'Sharing backend registered for '.$itemType.' did not generate a unique target for '.$itemSource; - \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); - throw new \Exception($message); - } - - /** - * Delete all reshares of an item - * @param int Id of item to delete - * @param bool If true, exclude the parent from the delete (optional) - * @param string The user that the parent was shared with (optinal) - */ - private static function delete($parent, $excludeParent = false, $uidOwner = null) { - $ids = array($parent); - $parents = array($parent); - while (!empty($parents)) { - $parents = "'".implode("','", $parents)."'"; - // Check the owner on the first search of reshares, useful for - // finding and deleting the reshares by a single user of a group share - if (count($ids) == 1 && isset($uidOwner)) { - $query = \OC_DB::prepare('SELECT `id`, `uid_owner`, `item_type`, `item_target`, `parent`' - .' FROM `*PREFIX*share` WHERE `parent` IN ('.$parents.') AND `uid_owner` = ?'); - $result = $query->execute(array($uidOwner)); - } else { - $query = \OC_DB::prepare('SELECT `id`, `item_type`, `item_target`, `parent`, `uid_owner`' - .' FROM `*PREFIX*share` WHERE `parent` IN ('.$parents.')'); - $result = $query->execute(); - } - // Reset parents array, only go through loop again if items are found - $parents = array(); - while ($item = $result->fetchRow()) { - // Search for a duplicate parent share, this occurs when an - // item is shared to the same user through a group and user or the - // same item is shared by different users - $userAndGroups = array_merge(array($item['uid_owner']), \OC_Group::getUserGroups($item['uid_owner'])); - $query = \OC_DB::prepare('SELECT `id`, `permissions` FROM `*PREFIX*share`' - .' WHERE `item_type` = ?' - .' AND `item_target` = ?' - .' AND `share_type` IN (?,?,?)' - .' AND `share_with` IN (\''.implode('\',\'', $userAndGroups).'\')' - .' AND `uid_owner` != ? AND `id` != ?'); - $duplicateParent = $query->execute(array($item['item_type'], $item['item_target'], - self::SHARE_TYPE_USER, self::SHARE_TYPE_GROUP, self::$shareTypeGroupUserUnique, - $item['uid_owner'], $item['parent']))->fetchRow(); - if ($duplicateParent) { - // Change the parent to the other item id if share permission is granted - if ($duplicateParent['permissions'] & PERMISSION_SHARE) { - $query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `parent` = ? WHERE `id` = ?'); - $query->execute(array($duplicateParent['id'], $item['id'])); - continue; - } - } - $ids[] = $item['id']; - $parents[] = $item['id']; - } - } - if ($excludeParent) { - unset($ids[0]); - } - if (!empty($ids)) { - $ids = "'".implode("','", $ids)."'"; - $query = \OC_DB::prepare('DELETE FROM `*PREFIX*share` WHERE `id` IN ('.$ids.')'); - $query->execute(); - } + return \OC\Share\Share::getBackend($itemType); } /** * Delete all shares with type SHARE_TYPE_LINK */ public static function removeAllLinkShares() { - // Delete any link shares - $query = \OC_DB::prepare('SELECT `id` FROM `*PREFIX*share` WHERE `share_type` = ?'); - $result = $query->execute(array(self::SHARE_TYPE_LINK)); - while ($item = $result->fetchRow()) { - self::delete($item['id']); - } - } - - /** - * Hook Listeners - */ - - /** - * Function that is called after a user is deleted. Cleans up the shares of that user. - * @param array arguments - */ - public static function post_deleteUser($arguments) { - // Delete any items shared with the deleted user - $query = \OC_DB::prepare('DELETE FROM `*PREFIX*share`' - .' WHERE `share_with` = ? AND `share_type` = ? OR `share_type` = ?'); - $result = $query->execute(array($arguments['uid'], self::SHARE_TYPE_USER, self::$shareTypeGroupUserUnique)); - // Delete any items the deleted user shared - $query = \OC_DB::prepare('SELECT `id` FROM `*PREFIX*share` WHERE `uid_owner` = ?'); - $result = $query->execute(array($arguments['uid'])); - while ($item = $result->fetchRow()) { - self::delete($item['id']); - } - } - - /** - * Function that is called after a user is added to a group. - * TODO what does it do? - * @param array arguments - */ - public static function post_addToGroup($arguments) { - // Find the group shares and check if the user needs a unique target - $query = \OC_DB::prepare('SELECT * FROM `*PREFIX*share` WHERE `share_type` = ? AND `share_with` = ?'); - $result = $query->execute(array(self::SHARE_TYPE_GROUP, $arguments['gid'])); - $query = \OC_DB::prepare('INSERT INTO `*PREFIX*share` (`item_type`, `item_source`,' - .' `item_target`, `parent`, `share_type`, `share_with`, `uid_owner`, `permissions`,' - .' `stime`, `file_source`, `file_target`) VALUES (?,?,?,?,?,?,?,?,?,?,?)'); - while ($item = $result->fetchRow()) { - if ($item['item_type'] == 'file' || $item['item_type'] == 'file') { - $itemTarget = null; - } else { - $itemTarget = self::generateTarget($item['item_type'], $item['item_source'], self::SHARE_TYPE_USER, - $arguments['uid'], $item['uid_owner'], $item['item_target'], $item['id']); - } - if (isset($item['file_source'])) { - $fileTarget = self::generateTarget($item['item_type'], $item['item_source'], self::SHARE_TYPE_USER, - $arguments['uid'], $item['uid_owner'], $item['file_target'], $item['id']); - } else { - $fileTarget = null; - } - // Insert an extra row for the group share if the item or file target is unique for this user - if ($itemTarget != $item['item_target'] || $fileTarget != $item['file_target']) { - $query->execute(array($item['item_type'], $item['item_source'], $itemTarget, $item['id'], - self::$shareTypeGroupUserUnique, $arguments['uid'], $item['uid_owner'], $item['permissions'], - $item['stime'], $item['file_source'], $fileTarget)); - \OC_DB::insertid('*PREFIX*share'); - } - } - } - - /** - * Function that is called after a user is removed from a group. Shares are cleaned up. - * @param array arguments - */ - public static function post_removeFromGroup($arguments) { - // TODO Don't call if user deleted? - $sql = 'SELECT `id`, `share_type` FROM `*PREFIX*share`' - .' WHERE (`share_type` = ? AND `share_with` = ?) OR (`share_type` = ? AND `share_with` = ?)'; - $result = \OC_DB::executeAudited($sql, array(self::SHARE_TYPE_GROUP, $arguments['gid'], - self::$shareTypeGroupUserUnique, $arguments['uid'])); - while ($item = $result->fetchRow()) { - if ($item['share_type'] == self::SHARE_TYPE_GROUP) { - // Delete all reshares by this user of the group share - self::delete($item['id'], true, $arguments['uid']); - } else { - self::delete($item['id']); - } - } - } - - /** - * Function that is called after a group is removed. Cleans up the shares to that group. - * @param array arguments - */ - public static function post_deleteGroup($arguments) { - $sql = 'SELECT `id` FROM `*PREFIX*share` WHERE `share_type` = ? AND `share_with` = ?'; - $result = \OC_DB::executeAudited($sql, array(self::SHARE_TYPE_GROUP, $arguments['gid'])); - while ($item = $result->fetchRow()) { - self::delete($item['id']); - } + return \OC\Share\Share::removeAllLinkShares(); } /** @@ -1935,26 +313,7 @@ class Share { * @return bool */ public static function checkPasswordProtectedShare(array $linkItem) { - if (!isset($linkItem['share_with'])) { - return true; - } - if (!isset($linkItem['share_type'])) { - return true; - } - if (!isset($linkItem['id'])) { - return true; - } - - if ($linkItem['share_type'] != \OCP\Share::SHARE_TYPE_LINK) { - return true; - } - - if ( \OC::$session->exists('public_link_authenticated') - && \OC::$session->get('public_link_authenticated') === $linkItem['id'] ) { - return true; - } - - return false; + return \OC\Share\Share::checkPasswordProtectedShare($linkItem); } } @@ -1967,9 +326,7 @@ interface Share_Backend { * Get the source of the item to be stored in the database * @param string Item source * @param string Owner of the item - * @param string $itemSource - * @param string $uidOwner - * @return boolean Source + * @return mixed|array|false Source * * Return an array if the item is file dependent, the array needs two keys: 'item' and 'file' * Return false if the item does not exist for the user @@ -1992,8 +349,8 @@ interface Share_Backend { /** * Converts the shared item sources back into the item in the specified format - * @param array $items Shared items - * @param integer $format + * @param array Shared items + * @param int Format * @return TODO * * The items array is a 3-dimensional array with the item_source as the @@ -2025,9 +382,6 @@ interface Share_Backend_File_Dependent extends Share_Backend { * Get the file path of the item * @param string Item source * @param string User that is the owner of shared item - * @param string $itemSource - * @param string $uidOwner - * @return boolean */ public function getFilePath($itemSource, $uidOwner); diff --git a/ocs/v1.php b/ocs/v1.php index 5d360c530a9d35faa9f407decbac89f643f7b2b1..62a3511e611b6a40bb8d20f4295899a420b385db 100644 --- a/ocs/v1.php +++ b/ocs/v1.php @@ -21,11 +21,15 @@ * */ -require_once('../lib/base.php'); +require_once '../lib/base.php'; + use Symfony\Component\Routing\Exception\ResourceNotFoundException; use Symfony\Component\Routing\Exception\MethodNotAllowedException; try { + // load all apps to get all api routes properly setup + OC_App::loadApps(); + OC::$server->getRouter()->match('/ocs'.OC_Request::getRawPathInfo()); } catch (ResourceNotFoundException $e) { OC_API::setContentType(); diff --git a/public.php b/public.php index 767295b98db100476f1119488443df88191a3835..e072db93d29dc7b0daf6c896269ec17b3cc39168 100644 --- a/public.php +++ b/public.php @@ -1,5 +1,4 @@ <?php -$RUNTIME_NOAPPS = true; try { diff --git a/remote.php b/remote.php index 9e18c8f80a9dd207d001f7c2912b2e4b38fb9690..15dfa8256ff3c2e93158d4d484320995b711c550 100644 --- a/remote.php +++ b/remote.php @@ -1,5 +1,4 @@ <?php -$RUNTIME_NOAPPS = true; try { diff --git a/search/ajax/search.php b/search/ajax/search.php index f0ca5752b9a731a91a5ed6102c5d3b67c2259966..0cc1f9d30cd21c7b10c74d85e6c0fae25eefc67e 100644 --- a/search/ajax/search.php +++ b/search/ajax/search.php @@ -23,7 +23,6 @@ // Check if we are a user OC_JSON::checkLoggedIn(); -OC_App::loadApps(); $query=(isset($_GET['query']))?$_GET['query']:''; if($query) { diff --git a/settings/admin.php b/settings/admin.php index 23b3a2d5a0e765dab5f1fc86b1a2394d0b7e506c..ea8aa7af5d0a4bab2f895d9df6806409ae193018 100755 --- a/settings/admin.php +++ b/settings/admin.php @@ -6,7 +6,6 @@ */ OC_Util::checkAdminUser(); -OC_App::loadApps(); OC_Util::addStyle( "settings", "settings" ); OC_Util::addScript( "settings", "admin" ); diff --git a/settings/apps.php b/settings/apps.php index 96b6d21b50214d717423a27aa32d1a894543778d..6fd2efc2018020397daa17099692afa76a608e5c 100644 --- a/settings/apps.php +++ b/settings/apps.php @@ -22,7 +22,6 @@ */ OC_Util::checkAdminUser(); -OC_App::loadApps(); // Load the files we need OC_Util::addStyle( "settings", "settings" ); diff --git a/settings/changepassword/controller.php b/settings/changepassword/controller.php index e8c2a1943f3b0e28d156b7410c2662a83cd5fe07..9f1e732996412455e0dc9b5bb41a0815915ddf5b 100644 --- a/settings/changepassword/controller.php +++ b/settings/changepassword/controller.php @@ -8,9 +8,6 @@ class Controller { \OC_JSON::callCheck(); \OC_JSON::checkLoggedIn(); - // Manually load apps to ensure hooks work correctly (workaround for issue 1503) - \OC_App::loadApps(); - $username = \OC_User::getUser(); $password = isset($_POST['personal-password']) ? $_POST['personal-password'] : null; $oldPassword = isset($_POST['oldpassword']) ? $_POST['oldpassword'] : ''; @@ -32,9 +29,6 @@ class Controller { \OC_JSON::callCheck(); \OC_JSON::checkLoggedIn(); - // Manually load apps to ensure hooks work correctly (workaround for issue 1503) - \OC_App::loadApps(); - if (isset($_POST['username'])) { $username = $_POST['username']; } else { diff --git a/settings/help.php b/settings/help.php index 88693939b848375bea86c54265f797487f36de78..301f50592aed4396552774862b5479d0641f9025 100644 --- a/settings/help.php +++ b/settings/help.php @@ -6,7 +6,6 @@ */ OC_Util::checkLoggedIn(); -OC_App::loadApps(); // Load the files we need OC_Util::addStyle( "settings", "settings" ); diff --git a/settings/l10n/cs_CZ.php b/settings/l10n/cs_CZ.php index 09bfe89d8af01c117373c21f235998b109d5648a..960938b1c6847364ccbc54cf835b57a52c200cdc 100644 --- a/settings/l10n/cs_CZ.php +++ b/settings/l10n/cs_CZ.php @@ -93,6 +93,9 @@ $TRANSLATIONS = array( "Internet connection not working" => "PÅ™ipojenà k internetu nefunguje", "This server has no working internet connection. This means that some of the features like mounting of external storage, notifications about updates or installation of 3rd party apps don´t work. Accessing files from remote and sending of notification emails might also not work. We suggest to enable internet connection for this server if you want to have all features." => "Server nemá funkÄnà pÅ™ipojenà k internetu. NÄ›které moduly jako napÅ™. externà úložiÅ¡tÄ›, oznámenà o dostupných aktualizacÃch nebo instalace aplikacà tÅ™etÃch stran nebudou fungovat. PÅ™Ãstup k souborům z jiných mÃst a odesÃlánà oznamovacÃch e-mailů také nemusà fungovat. Pokud si pÅ™ejete využÃvat vÅ¡ech vlastnostà ownCloud, doporuÄujeme povolit pÅ™ipojenà k internetu tomuto serveru.", "Cron" => "Cron", +"Last cron was executed at %s." => "Poslednà cron byl spuÅ¡tÄ›n v %s.", +"Last cron was executed at %s. This is more than an hour ago, something seems wrong." => "Poslednà cron byl spuÅ¡tÄ›n v %s. To se stalo pÅ™ed vÃce než hodinu. Vypadá to, že nenà nÄ›co v pořádku.", +"Cron was not executed yet!" => "Cron jeÅ¡tÄ› nebyl spuÅ¡tÄ›n!", "Execute one task with each page loaded" => "Spustit jednu úlohu s každým naÄtenÃm stránky", "cron.php is registered at a webcron service to call cron.php every 15 minutes over http." => "cron.php je registrován u služby webcron, aby volal cron.php jednou za 15 minut pÅ™es http.", "Use systems cron service to call the cron.php file every 15 minutes." => "PoužÃt systémovou službu cron pro volánà cron.php každých 15 minut.", diff --git a/settings/l10n/gl.php b/settings/l10n/gl.php index d98d812d9445415e26d1c7f3ba3148226beba6d7..2d23efd96a3c08d4f66e74075b042b5c70c76afe 100644 --- a/settings/l10n/gl.php +++ b/settings/l10n/gl.php @@ -93,6 +93,9 @@ $TRANSLATIONS = array( "Internet connection not working" => "A conexión á Internet non funciona", "This server has no working internet connection. This means that some of the features like mounting of external storage, notifications about updates or installation of 3rd party apps don´t work. Accessing files from remote and sending of notification emails might also not work. We suggest to enable internet connection for this server if you want to have all features." => "Este servidor non ten conexión a Internet. Isto significa que algunhas das funcionalidades como a montaxe de almacenamento externo, as notificacións sobre actualizacións ou instalación de aplicativos de terceiros non funcionan. O acceso aos ficheiros de forma remota e o envÃo de mensaxes de notificación poderÃan non funcionar. SuxerÃmoslle que active a conexión a Internet deste servidor se quere dispor de todas as funcionalidades.", "Cron" => "Cron", +"Last cron was executed at %s." => "O último «cron» executouse ás %s.", +"Last cron was executed at %s. This is more than an hour ago, something seems wrong." => "O último «cron» executouse ás %s. Isto supón que pasou máis dunha hora. polo que semella que algo vai mal.", +"Cron was not executed yet!" => "«Cron» aÃnda non foi executado!", "Execute one task with each page loaded" => "Executar unha tarefa con cada páxina cargada", "cron.php is registered at a webcron service to call cron.php every 15 minutes over http." => "cron.php está rexistrado nun servizo de WebCron para chamar a cron.php cada 15 minutos a través de HTTP.", "Use systems cron service to call the cron.php file every 15 minutes." => "Use o servizo de sistema cron para chamar ao ficheiro cron.php cada 15 minutos.", diff --git a/settings/l10n/pt_BR.php b/settings/l10n/pt_BR.php index 4a7554499d5ecf432be2b639f847b80259a1b93d..dad6773d6afd5e0d9ba3773c4f71d8c3e4921ddb 100644 --- a/settings/l10n/pt_BR.php +++ b/settings/l10n/pt_BR.php @@ -93,6 +93,9 @@ $TRANSLATIONS = array( "Internet connection not working" => "Sem conexão com a internet", "This server has no working internet connection. This means that some of the features like mounting of external storage, notifications about updates or installation of 3rd party apps don´t work. Accessing files from remote and sending of notification emails might also not work. We suggest to enable internet connection for this server if you want to have all features." => "Este servidor não tem conexão com a internet. Isso significa que algumas das caracterÃsticas como a montagem de armazenamento externo, notificações sobre atualizações ou instalação de aplicativos de 3ºs terceiros não funcionam. Acessar arquivos remotamente e envio de e-mails de notificação também não podem funcionar. Sugerimos permitir conexão com a internet para esse servidor, se você deseja ter todas as funcionalidades.", "Cron" => "Cron", +"Last cron was executed at %s." => "Último cron foi executado em %s.", +"Last cron was executed at %s. This is more than an hour ago, something seems wrong." => "Última cron foi executado em %s. Isso é, mais do que uma hora atrás, algo parece errado.", +"Cron was not executed yet!" => "Cron não foi executado ainda!", "Execute one task with each page loaded" => "Execute uma tarefa com cada página carregada", "cron.php is registered at a webcron service to call cron.php every 15 minutes over http." => "cron.php está registrado no serviço webcron para chamar cron.php a cada 15 minutos sobre http.", "Use systems cron service to call the cron.php file every 15 minutes." => "Use o sistema de serviço cron para chamar o arquivo cron.php a cada 15 minutos.", diff --git a/settings/l10n/ru.php b/settings/l10n/ru.php index 49f3aeeb65d969c6333782edb23f4678a3fde1f8..9cbdeee2c23b35bc95fca77a9b2474805751bef2 100644 --- a/settings/l10n/ru.php +++ b/settings/l10n/ru.php @@ -1,5 +1,6 @@ <?php $TRANSLATIONS = array( +"Saved" => "Сохранено", "Email sent" => "ПиÑьмо отправлено", "Encryption" => "Шифрование", "Unable to load list from App Store" => "Ðе удалоÑÑŒ загрузить ÑпиÑок из App Store", @@ -37,6 +38,11 @@ $TRANSLATIONS = array( "Update" => "Обновить", "Updated" => "Обновлено", "Select a profile picture" => "Выберите картинку профилÑ", +"Very weak password" => "Очень Ñлабый пароль", +"Weak password" => "Слабый пароль", +"So-so password" => "Так Ñебе пароль", +"Good password" => "Хороший пароль", +"Strong password" => "УÑтойчивый к взлому пароль", "Decrypting files... Please wait, this can take some time." => "РаÑшифровка файлов... ПожалуйÑта, подождите, Ñто может занÑÑ‚ÑŒ некоторое времÑ.", "deleted" => "удален", "undo" => "отмена", diff --git a/settings/l10n/sl.php b/settings/l10n/sl.php index fea2d4cc3ad3e40e0f2a6c239997a92191d706cb..414f46712e388ec44d4b94cc89f51cf60be4da37 100644 --- a/settings/l10n/sl.php +++ b/settings/l10n/sl.php @@ -1,5 +1,6 @@ <?php $TRANSLATIONS = array( +"Invalid value supplied for %s" => "Navedena je napaÄna vrednost za %s", "Saved" => "Shranjeno", "Email sent" => "Elektronska poÅ¡ta je poslana", "Send mode" => "NaÄin poÅ¡iljanja", diff --git a/settings/l10n/tr.php b/settings/l10n/tr.php index e02c6701a9816d810a09ed3a3dcf6e5261611990..68b464a5cc2c66cd458268921ad3f5336febdcaa 100644 --- a/settings/l10n/tr.php +++ b/settings/l10n/tr.php @@ -93,6 +93,9 @@ $TRANSLATIONS = array( "Internet connection not working" => "Ä°nternet baÄŸlantısı çalışmıyor", "This server has no working internet connection. This means that some of the features like mounting of external storage, notifications about updates or installation of 3rd party apps don´t work. Accessing files from remote and sending of notification emails might also not work. We suggest to enable internet connection for this server if you want to have all features." => "Bu sunucunun çalışan bir internet baÄŸlantısı yok. Bu, harici depolama alanı baÄŸlama, güncelleÅŸtirme bildirimleri veya 3. parti uygulama kurma gibi bazı özellikler çalışmayacak demektir. Uzak dosyalara eriÅŸim ve e-posta ile bildirim gönderme de çalışmayacaktır. EÄŸer bu özelliklerin tamamını kullanmak istiyorsanız, sunucu için internet baÄŸlantısını etkinleÅŸtirmenizi öneriyoruz.", "Cron" => "Cron", +"Last cron was executed at %s." => "Son cron %s zamanında çalıştırıldı.", +"Last cron was executed at %s. This is more than an hour ago, something seems wrong." => "Son cron %s zamanında çalıştırıldı. Bu bir saatten daha uzun bir süre, bir ÅŸeyler yanlış gibi görünüyor.", +"Cron was not executed yet!" => "Cron henüz çalıştırılmadı!", "Execute one task with each page loaded" => "Yüklenen her sayfa ile bir görev çalıştır", "cron.php is registered at a webcron service to call cron.php every 15 minutes over http." => "cron.php, http üzerinden her 15 dakikada bir çaÄŸrılması için webcron hizmetine kaydedilir.", "Use systems cron service to call the cron.php file every 15 minutes." => "Cron.php dosyasını her 15 dakikada bir çağırmak için sistem cron hizmetini kullan.", diff --git a/settings/personal.php b/settings/personal.php index cf1a496bdf018ef7b7ff8d59d71540b82563aa32..be1aa6400bf63457f1d60201481bd6f6bb731352 100644 --- a/settings/personal.php +++ b/settings/personal.php @@ -6,7 +6,6 @@ */ OC_Util::checkLoggedIn(); -OC_App::loadApps(); $defaults = new OC_Defaults(); // initialize themable default strings and urls diff --git a/settings/settings.php b/settings/settings.php index 1e05452ec4d1b612e43720f9006f17275c97477c..c08732fcf66203aa6c647bc4ab8552725314d292 100644 --- a/settings/settings.php +++ b/settings/settings.php @@ -6,7 +6,6 @@ */ OC_Util::checkLoggedIn(); -OC_App::loadApps(); OC_Util::addStyle( 'settings', 'settings' ); OC_App::setActiveNavigationEntry( 'settings' ); diff --git a/settings/users.php b/settings/users.php index 2f1c63a0b59856900109f277c0ff93e5aa3366bd..f09d0e90d3c6bde5265a085450163153af0db743 100644 --- a/settings/users.php +++ b/settings/users.php @@ -6,7 +6,6 @@ */ OC_Util::checkSubAdminUser(); -OC_App::loadApps(); // We have some javascript foo! OC_Util::addScript( 'settings', 'users' ); diff --git a/status.php b/status.php index 88422100f14cf96afb6b637ecb139520efa8807e..861eaed9cd2638c3fe8ca3099d9f4ae811bb3981 100644 --- a/status.php +++ b/status.php @@ -21,8 +21,6 @@ * */ -$RUNTIME_NOAPPS = true; //no apps, yet - try { require_once 'lib/base.php'; diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 70de7cd1c44a20112d5769f763f54251f97b8cb9..99374d68a5c59971215144727ddb9fe65c55c2b0 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -16,3 +16,4 @@ if (!class_exists('PHPUnit_Framework_TestCase')) { OC_Hook::clear(); OC_Log::$enabled = false; +OC_FileProxy::clearProxies(); diff --git a/tests/lib/files/filesystem.php b/tests/lib/files/filesystem.php index 90f1dfe581b12674d3169c6fa66242e54ae876ac..53f528af793db009a4b1bfc5b4ecbd543c974fde 100644 --- a/tests/lib/files/filesystem.php +++ b/tests/lib/files/filesystem.php @@ -226,4 +226,55 @@ class Filesystem extends \PHPUnit_Framework_TestCase { $path = $arguments['path']; $this->assertEquals($path, \OC\Files\Filesystem::normalizePath($path)); //the path passed to the hook should already be normalized } + + /** + * Test that the default cache dir is part of the user's home + */ + public function testMountDefaultCacheDir() { + $userId = uniqid('user_'); + $oldCachePath = \OC_Config::getValue('cache_path', ''); + // no cache path configured + \OC_Config::setValue('cache_path', ''); + + \OC_User::createUser($userId, $userId); + \OC\Files\Filesystem::initMountPoints($userId); + + $this->assertEquals( + '/' . $userId . '/', + \OC\Files\Filesystem::getMountPoint('/' . $userId . '/cache') + ); + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath('/' . $userId . '/cache'); + $this->assertInstanceOf('\OC\Files\Storage\Home', $storage); + $this->assertEquals('cache', $internalPath); + \OC_User::deleteUser($userId); + + \OC_Config::setValue('cache_path', $oldCachePath); + } + + /** + * Test that an external cache is mounted into + * the user's home + */ + public function testMountExternalCacheDir() { + $userId = uniqid('user_'); + + $oldCachePath = \OC_Config::getValue('cache_path', ''); + // set cache path to temp dir + $cachePath = \OC_Helper::tmpFolder() . '/extcache'; + \OC_Config::setValue('cache_path', $cachePath); + + \OC_User::createUser($userId, $userId); + \OC\Files\Filesystem::initMountPoints($userId); + + $this->assertEquals( + '/' . $userId . '/cache/', + \OC\Files\Filesystem::getMountPoint('/' . $userId . '/cache') + ); + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath('/' . $userId . '/cache'); + $this->assertInstanceOf('\OC\Files\Storage\Local', $storage); + $this->assertEquals('', $internalPath); + \OC_User::deleteUser($userId); + + \OC_Config::setValue('cache_path', $oldCachePath); + } } diff --git a/tests/lib/files/storage/storage.php b/tests/lib/files/storage/storage.php index f9291758606dc3cb327ac459ef264c9f1ca822d5..f3bfba3feb8e64171464b315ab283904d16dbf94 100644 --- a/tests/lib/files/storage/storage.php +++ b/tests/lib/files/storage/storage.php @@ -64,17 +64,17 @@ abstract class Storage extends \PHPUnit_Framework_TestCase { * @dataProvider directoryProvider */ public function testDirectories($directory) { - $this->assertFalse($this->instance->file_exists('/'.$directory)); + $this->assertFalse($this->instance->file_exists('/' . $directory)); - $this->assertTrue($this->instance->mkdir('/'.$directory)); + $this->assertTrue($this->instance->mkdir('/' . $directory)); - $this->assertTrue($this->instance->file_exists('/'.$directory)); - $this->assertTrue($this->instance->is_dir('/'.$directory)); - $this->assertFalse($this->instance->is_file('/'.$directory)); - $this->assertEquals('dir', $this->instance->filetype('/'.$directory)); - $this->assertEquals(0, $this->instance->filesize('/'.$directory)); - $this->assertTrue($this->instance->isReadable('/'.$directory)); - $this->assertTrue($this->instance->isUpdatable('/'.$directory)); + $this->assertTrue($this->instance->file_exists('/' . $directory)); + $this->assertTrue($this->instance->is_dir('/' . $directory)); + $this->assertFalse($this->instance->is_file('/' . $directory)); + $this->assertEquals('dir', $this->instance->filetype('/' . $directory)); + $this->assertEquals(0, $this->instance->filesize('/' . $directory)); + $this->assertTrue($this->instance->isReadable('/' . $directory)); + $this->assertTrue($this->instance->isUpdatable('/' . $directory)); $dh = $this->instance->opendir('/'); $content = array(); @@ -85,13 +85,13 @@ abstract class Storage extends \PHPUnit_Framework_TestCase { } $this->assertEquals(array($directory), $content); - $this->assertFalse($this->instance->mkdir('/'.$directory)); //cant create existing folders - $this->assertTrue($this->instance->rmdir('/'.$directory)); + $this->assertFalse($this->instance->mkdir('/' . $directory)); //cant create existing folders + $this->assertTrue($this->instance->rmdir('/' . $directory)); $this->wait(); - $this->assertFalse($this->instance->file_exists('/'.$directory)); + $this->assertFalse($this->instance->file_exists('/' . $directory)); - $this->assertFalse($this->instance->rmdir('/'.$directory)); //cant remove non existing folders + $this->assertFalse($this->instance->rmdir('/' . $directory)); //cant remove non existing folders $dh = $this->instance->opendir('/'); $content = array(); @@ -103,8 +103,7 @@ abstract class Storage extends \PHPUnit_Framework_TestCase { $this->assertEquals(array(), $content); } - public function directoryProvider() - { + public function directoryProvider() { return array( array('folder'), array(' folder'), @@ -113,6 +112,7 @@ abstract class Storage extends \PHPUnit_Framework_TestCase { array('spéciäl földer'), ); } + /** * test the various uses of file_get_contents and file_put_contents */ @@ -298,4 +298,21 @@ abstract class Storage extends \PHPUnit_Framework_TestCase { $this->assertFalse($this->instance->file_exists('folder/bar')); $this->assertFalse($this->instance->file_exists('folder')); } + + public function hashProvider(){ + return array( + array('Foobar', 'md5'), + array('Foobar', 'sha1'), + array('Foobar', 'sha256'), + ); + } + + /** + * @dataProvider hashProvider + */ + public function testHash($data, $type) { + $this->instance->file_put_contents('hash.txt', $data); + $this->assertEquals(hash($type, $data), $this->instance->hash($type, 'hash.txt')); + $this->assertEquals(hash($type, $data, true), $this->instance->hash($type, 'hash.txt', true)); + } } diff --git a/tests/lib/share/share.php b/tests/lib/share/share.php index b5cba9430aac998bb547f03de03525fcc4847a1a..aae91fa1087e6c576bae24ea9e4b748dd4938658 100644 --- a/tests/lib/share/share.php +++ b/tests/lib/share/share.php @@ -282,7 +282,7 @@ class Test_Share extends PHPUnit_Framework_TestCase { OC_User::setUserId($this->user2); $this->assertEquals(array(OCP\PERMISSION_READ), OCP\Share::getItemSharedWith('test', 'test.txt', Test_Share_Backend::FORMAT_PERMISSIONS)); OC_User::setUserId($this->user3); - $this->assertFalse(OCP\Share::getItemSharedWith('test', 'test.txt')); + $this->assertSame(array(), OCP\Share::getItemSharedWith('test', 'test.txt')); // Reshare again, and then have owner unshare OC_User::setUserId($this->user1); @@ -292,9 +292,9 @@ class Test_Share extends PHPUnit_Framework_TestCase { OC_User::setUserId($this->user1); $this->assertTrue(OCP\Share::unshare('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user2)); OC_User::setUserId($this->user2); - $this->assertFalse(OCP\Share::getItemSharedWith('test', 'test.txt')); + $this->assertSame(array(), OCP\Share::getItemSharedWith('test', 'test.txt')); OC_User::setUserId($this->user3); - $this->assertFalse(OCP\Share::getItemSharedWith('test', 'test.txt')); + $this->assertSame(array(), OCP\Share::getItemSharedWith('test', 'test.txt')); // Attempt target conflict OC_User::setUserId($this->user1); @@ -325,7 +325,7 @@ class Test_Share extends PHPUnit_Framework_TestCase { ); OC_User::setUserId($this->user2); - $this->assertFalse( + $this->assertSame(array(), OCP\Share::getItemSharedWith('test', 'test.txt', Test_Share_Backend::FORMAT_SOURCE), 'Failed asserting that user 2 no longer has access to test.txt after expiration.' ); @@ -526,13 +526,13 @@ class Test_Share extends PHPUnit_Framework_TestCase { ); OC_User::setUserId($this->user2); - $this->assertFalse( + $this->assertSame(array(), OCP\Share::getItemSharedWith('test', 'test.txt', Test_Share_Backend::FORMAT_SOURCE), 'Failed asserting that user 2 no longer has access to test.txt after expiration.' ); OC_User::setUserId($this->user3); - $this->assertFalse( + $this->assertSame(array(), OCP\Share::getItemSharedWith('test', 'test.txt', Test_Share_Backend::FORMAT_SOURCE), 'Failed asserting that user 3 no longer has access to test.txt after expiration.' ); diff --git a/tests/lib/urlgenerator.php b/tests/lib/urlgenerator.php index 875a7f06580bb87ebe5292f3e781ed7b4a0f0f6f..8e605d88f32f26b13f275ca1dbe39d95e709a23c 100644 --- a/tests/lib/urlgenerator.php +++ b/tests/lib/urlgenerator.php @@ -12,17 +12,32 @@ class Test_Urlgenerator extends PHPUnit_Framework_TestCase { /** * @small * @brief test absolute URL construction - * @dataProvider provideURLs + * @dataProvider provideDocRootURLs */ - function testGetAbsoluteURL($url, $expectedResult) { + function testGetAbsoluteURLDocRoot($url, $expectedResult) { + \OC::$WEBROOT = ''; $urlGenerator = new \OC\URLGenerator(null); $result = $urlGenerator->getAbsoluteURL($url); $this->assertEquals($expectedResult, $result); } - public function provideURLs() { + /** + * @small + * @brief test absolute URL construction + * @dataProvider provideSubDirURLs + */ + function testGetAbsoluteURLSubDir($url, $expectedResult) { + + \OC::$WEBROOT = '/owncloud'; + $urlGenerator = new \OC\URLGenerator(null); + $result = $urlGenerator->getAbsoluteURL($url); + + $this->assertEquals($expectedResult, $result); + } + + public function provideDocRootURLs() { return array( array("index.php", "http://localhost/index.php"), array("/index.php", "http://localhost/index.php"), @@ -30,5 +45,14 @@ class Test_Urlgenerator extends PHPUnit_Framework_TestCase { array("apps/index.php", "http://localhost/apps/index.php"), ); } + + public function provideSubDirURLs() { + return array( + array("index.php", "http://localhost/owncloud/index.php"), + array("/index.php", "http://localhost/owncloud/index.php"), + array("/apps/index.php", "http://localhost/owncloud/apps/index.php"), + array("apps/index.php", "http://localhost/owncloud/apps/index.php"), + ); + } }