diff options
119 files changed, 3046 insertions, 2126 deletions
diff --git a/.gitignore b/.gitignore index e61ec6f0359..b24edc91282 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 b9c1a3702cf..bc2beee81ad 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 1f7e42e0d3e..4b4a7f8948d 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 17415b6933f..dd8af39bada 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 667209599a0..3bb35579d5f 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 6c2569e2ebb..f18bbffb74a 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 826f72fb0e6..a8acfdb6e6e 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 f334f29a939..25ab70af362 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 af863aca33e..1bac5d2b7db 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 00000000000..3ad7d634838 --- /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 c66cd40fb56..73601d26217 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 9a69d7b3688..a7d1fa9d8a2 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 503bf681139..cda4e823a73 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 1186a72a44f..1137364db4a 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 2c0335f3cc3..ac958e5dfd3 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 5b0bad7f341..34acd9c4f51 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 2a0df622767..69b4cbca10d 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 bce245ce680..ba984868932 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 bae1fded53d..6549273c8f1 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 cd2a3103eb7..00793a614c2 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 66d6f9fa6b8..8ed437839cd 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 a6955d400f4..1337d9f581d 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 8b01b7677e5..a2bdbcf4632 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 7d2116d190d..333c4c77fc9 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 e2bbb548182..e3c5b6e4315 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 124a236bcbd..cec18c46525 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 6aade210505..efe1e89f0bf 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 4adf14745de..b452bc25b13 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 b2a21b6e029..b6df62ffb79 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 c9ed7b96c6c..0c395839cf9 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 891c2eb5fa1..140b75706ea 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 25b8b312539..dd2c1026e47 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 3f04e1e4ad1..e667d9b5faa 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 2a0cbb2036d..55e8ab15ec2 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 cfccfb5d2f0..ed72d136e24 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 70c5a8566b7..a5159310af1 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 236968e3245..a1221137bc4 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 a63aa902d34..c67ac3e5ecf 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 4420633e34e..50e0314475f 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 3d3185f12c1..302b6b4d9fa 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 9ee50ff6963..e769edd0a21 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 069546387c7..57ea5be8be0 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 7f4d46aa1f1..52f1a15089a 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 8d84e7d31bc..e2fdc36be0b 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 2cfdfd11899..49eb4f9aa69 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 03544cd3c0f..affa1b063d0 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 bb9c9101fe2..1a8e9b2b764 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 91157b923a5..5788d1d5bd3 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 3d897503480..ba5f6ef9b54 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"> @@ -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 0a2f15f9f5e..bd94d0e908d 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 b092cba79c6..3a2d8b6029b 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 10724eb1434..758f1024ad2 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 dd65ca15edc..f44f8bcb5b9 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 c4e17664608..2586fb2af3d 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 8cb40f5bbb9..2403bc7cc3e 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 2de5517c11f..aed155f7491 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 1c955e49776..fec52335def 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 8e80384b7c8..4b5c985dfc9 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 07f348b79de..d0f028c8df8 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 6af3711cc64..74a21556d84 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 5ee5f5e35c3..5654edd1a82 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 ddd78cae32f..e3285d025c8 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 91e310d8f24..dbde28d85dd 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 5a24ba9ffa7..222415e4d01 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 8f14123eedf..eacfde04d8d 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 f2639f93030..bc931be91d8 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 1ef7c2cab99..99f9cf030b6 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 965c8512090..6334e403e41 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 2f8678ef6e2..8fbff462614 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 d4dcc61a855..f9b4401c785 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 5bae82bfe69..3381aea0a7d 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 90e076435a3..10017472d1b 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 2925d3cdfdd..12fa6ad9308 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 2c810d328e0..892d64a2733 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 73d07c3e4d2..d16fe0386eb 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 a01f6858bc6..d2e917267ce 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 177ac786519..09a1e3d278e 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 9cf4c3306ca..cb9da3017de 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 2515b9657cb..15a3ec8bc8a 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 8a6ef39f61b..be6805a9a57 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 eaf215c7231..492209b883b 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 c31e0c38180..7e27650c557 100644 --- a/lib/private/files/filesystem.php +++ b/lib/private/files/filesystem.php @@ -321,11 +321,36 @@ 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 * * @return View diff --git a/lib/private/files/storage/common.php b/lib/private/files/storage/common.php index 3c078d7b1b4..2b697141515 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 0f906ec55b4..571bf7f97c1 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 026f6ec895e..94ee28ca763 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 00000000000..14a4cd14984 --- /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 98a86388d20..d7ac0b5f4fa 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 00000000000..7e4223d10fa --- /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 00000000000..fde55667281 --- /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 00000000000..a33c71eedd2 --- /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 00000000000..8238797600e --- /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 b7ae8dd0f60..0d238737dde 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 a89b7286c10..dc4c7ec3b61 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 87e173fa765..731b7c97503 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 5066d40354d..a08134b3837 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 5d360c530a9..62a3511e611 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 767295b98db..e072db93d29 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 9e18c8f80a9..15dfa8256ff 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 f0ca5752b9a..0cc1f9d30cd 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 23b3a2d5a0e..ea8aa7af5d0 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 96b6d21b502..6fd2efc2018 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 e8c2a1943f3..9f1e7329964 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 88693939b84..301f50592ae 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 09bfe89d8af..960938b1c68 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 d98d812d944..2d23efd96a3 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 4a7554499d5..dad6773d6af 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 49f3aeeb65d..9cbdeee2c23 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 fea2d4cc3ad..414f46712e3 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 e02c6701a98..68b464a5cc2 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 cf1a496bdf0..be1aa6400bf 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 1e05452ec4d..c08732fcf66 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 2f1c63a0b59..f09d0e90d3c 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 88422100f14..861eaed9cd2 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 70de7cd1c44..99374d68a5c 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 90f1dfe581b..53f528af793 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 f9291758606..f3bfba3feb8 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 b5cba9430aa..aae91fa1087 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 875a7f06580..8e605d88f32 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"), + ); + } } |