diff options
Diffstat (limited to 'apps')
42 files changed, 667 insertions, 166 deletions
diff --git a/apps/files/ajax/upload.php b/apps/files/ajax/upload.php index 37c600e91da..4ed51c52775 100644 --- a/apps/files/ajax/upload.php +++ b/apps/files/ajax/upload.php @@ -58,7 +58,10 @@ if (empty($_POST['dirToken'])) { OCP\JSON::callCheck(); -\OC::$session->close(); +if (!\OCP\App::isEnabled('files_encryption')) { + // encryption app need to create keys later, so can't close too early + \OC::$session->close(); +} // get array with current storage stats (e.g. max file size) diff --git a/apps/files/appinfo/app.php b/apps/files/appinfo/app.php index 909baca92ea..15a29133789 100644 --- a/apps/files/appinfo/app.php +++ b/apps/files/appinfo/app.php @@ -12,13 +12,6 @@ OCP\App::addNavigationEntry(array("id" => "files_index", OC_Search::registerProvider('OC_Search_Provider_File'); -// cache hooks must be connected before all other apps. -// since 'files' is always loaded first the hooks need to be connected here -\OC_Hook::connect('OC_Filesystem', 'post_write', '\OC\Files\Cache\Updater', 'writeHook'); -\OC_Hook::connect('OC_Filesystem', 'post_touch', '\OC\Files\Cache\Updater', 'touchHook'); -\OC_Hook::connect('OC_Filesystem', 'post_delete', '\OC\Files\Cache\Updater', 'deleteHook'); -\OC_Hook::connect('OC_Filesystem', 'post_rename', '\OC\Files\Cache\Updater', 'renameHook'); - \OCP\BackgroundJob::addRegularTask('\OC\Files\Cache\BackgroundWatcher', 'checkNext'); $templateManager = OC_Helper::getFileTemplateManager(); 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/am_ET.php b/apps/files/l10n/am_ET.php new file mode 100644 index 00000000000..0157af093e9 --- /dev/null +++ b/apps/files/l10n/am_ET.php @@ -0,0 +1,7 @@ +<?php +$TRANSLATIONS = array( +"_%n folder_::_%n folders_" => array("",""), +"_%n file_::_%n files_" => array("",""), +"_Uploading %n file_::_Uploading %n files_" => array("","") +); +$PLURAL_FORMS = "nplurals=2; plural=(n != 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/appinfo/info.xml b/apps/files_encryption/appinfo/info.xml index ab47de828b9..15a09a29f51 100644 --- a/apps/files_encryption/appinfo/info.xml +++ b/apps/files_encryption/appinfo/info.xml @@ -2,7 +2,7 @@ <info> <id>files_encryption</id> <name>Encryption</name> - <description>The ownCloud files encryption system provides server side-encryption. After the app was enabled you need to re-login to initialize your encryption keys. Please note that server side encryption requires that the ownCloud server admin can be trusted. The main purpose of this app is the encryption of files that are stored on externally mounted storages.</description> + <description>The ownCloud files encryption system provides server side-encryption. After the app is enabled you need to re-login to initialize your encryption keys. Please note that server side encryption requires that the ownCloud server admin can be trusted. The main purpose of this app is the encryption of files that are stored on externally mounted storages.</description> <licence>AGPL</licence> <author>Sam Tuke, Bjoern Schiessle, Florin Peter</author> <require>4</require> diff --git a/apps/files_encryption/l10n/cs_CZ.php b/apps/files_encryption/l10n/cs_CZ.php index 18848978a8a..cdeaf5b192b 100644 --- a/apps/files_encryption/l10n/cs_CZ.php +++ b/apps/files_encryption/l10n/cs_CZ.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." => "Ujistěte se prosím, že máte nainstalované PHP 5.3.3 nebo novější a že máte povolené a správně nakonfigurované OpenSSL včetně jeho rozšíření pro PHP. Prozatím byla aplikace pro šifrování vypnuta.", "Following users are not set up for encryption:" => "Následující uživatelé nemají nastavené šifrování:", "Initial encryption started... This can take some time. Please wait." => "Počáteční šifrování zahájeno... Toto může chvíli trvat. Počkejte prosím.", +"Initial encryption running... Please try again later." => "Probíhá počáteční šifrování... Zkuste to prosím znovu později.", "Go directly to your " => "Běžte přímo do vašeho", "personal settings" => "osobní nastavení", "Encryption" => "Šifrování", 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_external/3rdparty/smb4php/smb.php b/apps/files_external/3rdparty/smb4php/smb.php index 656930514f0..e325506fa14 100644 --- a/apps/files_external/3rdparty/smb4php/smb.php +++ b/apps/files_external/3rdparty/smb4php/smb.php @@ -8,6 +8,8 @@ # Homepage: http://www.phpclasses.org/smb4php # # Copyright (c) 2007 Victor M. Varela <vmvarela@gmail.com> +# Copyright (c) 2012 Frank Karlitschek <frank@owncloud.org> +# Copyright (c) 2014 Robin McCorkell <rmccorkell@karoshi.org.uk> # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -19,8 +21,6 @@ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # -# Addition 17/12/2012 Frank Karlitschek (frank@owncloud.org) -# Addition 17/03/2014 Robin McCorkell (rmccorkell@karoshi.org.uk) # On the official website http://www.phpclasses.org/smb4php the # license is listed as LGPL so we assume that this is # dual-licensed GPL/LGPL @@ -44,6 +44,42 @@ $GLOBALS['__smb_cache'] = array ('stat' => array (), 'dir' => array ()); class smb { + private static $regexp = array ( + '^added interface ip=(.*) bcast=(.*) nmask=(.*)$' => 'skip', + 'Anonymous login successful' => 'skip', + '^Domain=\[(.*)\] OS=\[(.*)\] Server=\[(.*)\]$' => 'skip', + '^\tSharename[ ]+Type[ ]+Comment$' => 'shares', + '^\t---------[ ]+----[ ]+-------$' => 'skip', + '^\tServer [ ]+Comment$' => 'servers', + '^\t---------[ ]+-------$' => 'skip', + '^\tWorkgroup[ ]+Master$' => 'workg', + '^\t(.*)[ ]+(Disk|IPC)[ ]+IPC.*$' => 'skip', + '^\tIPC\\\$(.*)[ ]+IPC' => 'skip', + '^\t(.*)[ ]+(Disk)[ ]+(.*)$' => 'share', + '^\t(.*)[ ]+(Printer)[ ]+(.*)$' => 'skip', + '([0-9]+) blocks of size ([0-9]+)\. ([0-9]+) blocks available' => 'skip', + 'Got a positive name query response from ' => 'skip', + '^(session setup failed): (.*)$' => 'error', + '^(.*): ERRSRV - ERRbadpw' => 'error', + '^Error returning browse list: (.*)$' => 'error', + '^tree connect failed: (.*)$' => 'error', + '^(Connection to .* failed)(.*)$' => 'error-connect', + '^NT_STATUS_(.*) ' => 'error', + '^NT_STATUS_(.*)\$' => 'error', + 'ERRDOS - ERRbadpath \((.*).\)' => 'error', + 'cd (.*): (.*)$' => 'error', + '^cd (.*): NT_STATUS_(.*)' => 'error', + '^\t(.*)$' => 'srvorwg', + '^([0-9]+)[ ]+([0-9]+)[ ]+(.*)$' => 'skip', + '^Job ([0-9]+) cancelled' => 'skip', + '^[ ]+(.*)[ ]+([0-9]+)[ ]+(Mon|Tue|Wed|Thu|Fri|Sat|Sun)[ ](Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[ ]+([0-9]+)[ ]+([0-9]{2}:[0-9]{2}:[0-9]{2})[ ]([0-9]{4})$' => 'files', + '^message start: ERRSRV - (ERRmsgoff)' => 'error' + ); + + function getRegexp() { + return self::$regexp; + } + function parse_url ($url) { $pu = parse_url (trim($url)); foreach (array ('domain', 'user', 'pass', 'host', 'port', 'path') as $i) { @@ -75,46 +111,16 @@ class smb { } - function execute ($command, $purl) { + function execute ($command, $purl, $regexp = NULL) { return smb::client ('-d 0 ' . escapeshellarg ('//' . $purl['host'] . '/' . $purl['share']) - . ' -c ' . escapeshellarg ($command), $purl + . ' -c ' . escapeshellarg ($command), $purl, $regexp ); } - function client ($params, $purl) { - - static $regexp = array ( - '^added interface ip=(.*) bcast=(.*) nmask=(.*)$' => 'skip', - 'Anonymous login successful' => 'skip', - '^Domain=\[(.*)\] OS=\[(.*)\] Server=\[(.*)\]$' => 'skip', - '^\tSharename[ ]+Type[ ]+Comment$' => 'shares', - '^\t---------[ ]+----[ ]+-------$' => 'skip', - '^\tServer [ ]+Comment$' => 'servers', - '^\t---------[ ]+-------$' => 'skip', - '^\tWorkgroup[ ]+Master$' => 'workg', - '^\t(.*)[ ]+(Disk|IPC)[ ]+IPC.*$' => 'skip', - '^\tIPC\\\$(.*)[ ]+IPC' => 'skip', - '^\t(.*)[ ]+(Disk)[ ]+(.*)$' => 'share', - '^\t(.*)[ ]+(Printer)[ ]+(.*)$' => 'skip', - '([0-9]+) blocks of size ([0-9]+)\. ([0-9]+) blocks available' => 'skip', - 'Got a positive name query response from ' => 'skip', - '^(session setup failed): (.*)$' => 'error', - '^(.*): ERRSRV - ERRbadpw' => 'error', - '^Error returning browse list: (.*)$' => 'error', - '^tree connect failed: (.*)$' => 'error', - '^(Connection to .* failed)(.*)$' => 'error-connect', - '^NT_STATUS_(.*) ' => 'error', - '^NT_STATUS_(.*)\$' => 'error', - 'ERRDOS - ERRbadpath \((.*).\)' => 'error', - 'cd (.*): (.*)$' => 'error', - '^cd (.*): NT_STATUS_(.*)' => 'error', - '^\t(.*)$' => 'srvorwg', - '^([0-9]+)[ ]+([0-9]+)[ ]+(.*)$' => 'skip', - '^Job ([0-9]+) cancelled' => 'skip', - '^[ ]+(.*)[ ]+([0-9]+)[ ]+(Mon|Tue|Wed|Thu|Fri|Sat|Sun)[ ](Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[ ]+([0-9]+)[ ]+([0-9]{2}:[0-9]{2}:[0-9]{2})[ ]([0-9]{4})$' => 'files', - '^message start: ERRSRV - (ERRmsgoff)' => 'error' - ); + function client ($params, $purl, $regexp = NULL) { + + if ($regexp === NULL) $regexp = smb::$regexp; if (SMB4PHP_AUTHMODE == 'env') { putenv("USER={$purl['user']}%{$purl['pass']}"); diff --git a/apps/files_external/appinfo/app.php b/apps/files_external/appinfo/app.php index 0e83660f845..aeb7a2cb23a 100644 --- a/apps/files_external/appinfo/app.php +++ b/apps/files_external/appinfo/app.php @@ -1,6 +1,7 @@ <?php /** * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com> + * Copyright (c) 2014 Robin McCorkell <rmccorkell@karoshi.org.uk> * This file is licensed under the Affero General Public License version 3 or * later. * See the COPYING-README file. @@ -13,6 +14,7 @@ OC::$CLASSPATH['OC\Files\Storage\OwnCloud'] = 'files_external/lib/owncloud.php'; OC::$CLASSPATH['OC\Files\Storage\Google'] = 'files_external/lib/google.php'; OC::$CLASSPATH['OC\Files\Storage\Swift'] = 'files_external/lib/swift.php'; OC::$CLASSPATH['OC\Files\Storage\SMB'] = 'files_external/lib/smb.php'; +OC::$CLASSPATH['OC\Files\Storage\SMB_OC'] = 'files_external/lib/smb_oc.php'; OC::$CLASSPATH['OC\Files\Storage\AmazonS3'] = 'files_external/lib/amazons3.php'; OC::$CLASSPATH['OC\Files\Storage\Dropbox'] = 'files_external/lib/dropbox.php'; OC::$CLASSPATH['OC\Files\Storage\SFTP'] = 'files_external/lib/sftp.php'; @@ -27,4 +29,5 @@ if (OCP\Config::getAppValue('files_external', 'allow_user_mounting', 'yes') == ' // connecting hooks OCP\Util::connectHook('OC_Filesystem', 'post_initMountPoints', '\OC_Mount_Config', 'initMountPointsHook'); OCP\Util::connectHook('OC_User', 'post_login', 'OC\Files\Storage\iRODS', 'login'); +OCP\Util::connectHook('OC_User', 'post_login', 'OC\Files\Storage\SMB_OC', 'login'); diff --git a/apps/files_external/css/settings.css b/apps/files_external/css/settings.css index 11aeb10184b..1d3489f7f57 100644 --- a/apps/files_external/css/settings.css +++ b/apps/files_external/css/settings.css @@ -4,13 +4,6 @@ td.status > span { width: 16px; vertical-align: text-bottom; } -span.success { - background: #37ce02; - border-radius: 8px; -} -span.error { - background: #ce3702; -} td.mountPoint, td.backend { width:160px; } td.remove>img { visibility:hidden; padding-top:13px; } diff --git a/apps/files_external/l10n/cs_CZ.php b/apps/files_external/l10n/cs_CZ.php index 2f7bbd0c4a3..1b92e35c456 100644 --- a/apps/files_external/l10n/cs_CZ.php +++ b/apps/files_external/l10n/cs_CZ.php @@ -22,7 +22,7 @@ $TRANSLATIONS = array( "Users" => "Uživatelé", "Delete" => "Smazat", "Enable User External Storage" => "Zapnout externí uživatelské úložiště", -"Allow users to mount the following external storage" => "Povolit uživatelů připojit externí úložiště", +"Allow users to mount the following external storage" => "Povolit uživatelů připojit následující externí úložiště", "SSL root certificates" => "Kořenové certifikáty SSL", "Import Root Certificate" => "Importovat kořenového certifikátu" ); diff --git a/apps/files_external/l10n/pt_PT.php b/apps/files_external/l10n/pt_PT.php index e331d55726b..eb4d8feb41a 100644 --- a/apps/files_external/l10n/pt_PT.php +++ b/apps/files_external/l10n/pt_PT.php @@ -22,6 +22,7 @@ $TRANSLATIONS = array( "Users" => "Utilizadores", "Delete" => "Eliminar", "Enable User External Storage" => "Activar Armazenamento Externo para o Utilizador", +"Allow users to mount the following external storage" => "Permitir que os utilizadores montem o seguinte armazenamento externo", "SSL root certificates" => "Certificados SSL de raiz", "Import Root Certificate" => "Importar Certificado Root" ); 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/config.php b/apps/files_external/lib/config.php index ffbab7bca80..472c3963d51 100755 --- a/apps/files_external/lib/config.php +++ b/apps/files_external/lib/config.php @@ -5,6 +5,7 @@ * @author Michael Gapczynski * @copyright 2012 Michael Gapczynski mtgap@owncloud.com * @copyright 2014 Vincent Petry <pvince81@owncloud.com> +* @copyright 2014 Robin McCorkell <rmccorkell@karoshi.org.uk> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -122,11 +123,18 @@ class OC_Mount_Config { 'password' => '*Password', 'share' => 'Share', 'root' => '&Root')); + $backends['\OC\Files\Storage\SMB_OC'] = array( + 'backend' => 'SMB / CIFS using OC login', + 'configuration' => array( + 'host' => 'URL', + 'username_as_share' => '!Username as share', + 'share' => '&Share', + 'root' => '&Root')); } } if(OC_Mount_Config::checkcurl()){ - $backends['\OC\Files\Storage\DAV']=array( + $backends['\OC\Files\Storage\DAV']=array( 'backend' => 'WebDAV', 'configuration' => array( 'host' => 'URL', @@ -134,7 +142,7 @@ class OC_Mount_Config { 'password' => '*Password', 'root' => '&Root', 'secure' => '!Secure https://')); - $backends['\OC\Files\Storage\OwnCloud']=array( + $backends['\OC\Files\Storage\OwnCloud']=array( 'backend' => 'ownCloud', 'configuration' => array( 'host' => 'URL', @@ -185,7 +193,7 @@ class OC_Mount_Config { * @return array of mount point string as key, mountpoint config as value */ public static function getAbsoluteMountPoints($user) { - $mountPoints = array(); + $mountPoints = array(); $datadir = \OC_Config::getValue("datadirectory", \OC::$SERVERROOT . "/data"); $mount_file = \OC_Config::getValue("mount_file", $datadir . "/mount.json"); @@ -304,18 +312,23 @@ class OC_Mount_Config { $mount['options'] = self::decryptPasswords($mount['options']); // Remove '/$user/files/' from mount point $mountPoint = substr($mountPoint, 13); - // Merge the mount point into the current mount points - if (isset($system[$mountPoint]) && $system[$mountPoint]['configuration'] == $mount['options']) { - $system[$mountPoint]['applicable']['groups'] - = array_merge($system[$mountPoint]['applicable']['groups'], array($group)); + + $config = array( + 'class' => $mount['class'], + 'mountpoint' => $mountPoint, + 'backend' => $backends[$mount['class']]['backend'], + 'options' => $mount['options'], + 'applicable' => array('groups' => array($group), 'users' => array()), + 'status' => self::getBackendStatus($mount['class'], $mount['options'], false) + ); + $hash = self::makeConfigHash($config); + // If an existing config exists (with same class, mountpoint and options) + if (isset($system[$hash])) { + // add the groups into that config + $system[$hash]['applicable']['groups'] + = array_merge($system[$hash]['applicable']['groups'], array($group)); } else { - $system[$mountPoint] = array( - 'class' => $mount['class'], - 'backend' => $backends[$mount['class']]['backend'], - 'configuration' => $mount['options'], - 'applicable' => array('groups' => array($group), 'users' => array()), - 'status' => self::getBackendStatus($mount['class'], $mount['options']) - ); + $system[$hash] = $config; } } } @@ -330,23 +343,27 @@ class OC_Mount_Config { $mount['options'] = self::decryptPasswords($mount['options']); // Remove '/$user/files/' from mount point $mountPoint = substr($mountPoint, 13); - // Merge the mount point into the current mount points - if (isset($system[$mountPoint]) && $system[$mountPoint]['configuration'] == $mount['options']) { - $system[$mountPoint]['applicable']['users'] - = array_merge($system[$mountPoint]['applicable']['users'], array($user)); + $config = array( + 'class' => $mount['class'], + 'mountpoint' => $mountPoint, + 'backend' => $backends[$mount['class']]['backend'], + 'options' => $mount['options'], + 'applicable' => array('groups' => array(), 'users' => array($user)), + 'status' => self::getBackendStatus($mount['class'], $mount['options'], false) + ); + $hash = self::makeConfigHash($config); + // If an existing config exists (with same class, mountpoint and options) + if (isset($system[$hash])) { + // add the users into that config + $system[$hash]['applicable']['users'] + = array_merge($system[$hash]['applicable']['users'], array($user)); } else { - $system[$mountPoint] = array( - 'class' => $mount['class'], - 'backend' => $backends[$mount['class']]['backend'], - 'configuration' => $mount['options'], - 'applicable' => array('groups' => array(), 'users' => array($user)), - 'status' => self::getBackendStatus($mount['class'], $mount['options']) - ); + $system[$hash] = $config; } } } } - return $system; + return array_values($system); } /** @@ -366,12 +383,13 @@ class OC_Mount_Config { $mount['class'] = '\OC\Files\Storage\\'.substr($mount['class'], 15); } $mount['options'] = self::decryptPasswords($mount['options']); - // Remove '/uid/files/' from mount point - $personal[substr($mountPoint, strlen($uid) + 8)] = array( + $personal[] = array( 'class' => $mount['class'], + // Remove '/uid/files/' from mount point + 'mountpoint' => substr($mountPoint, strlen($uid) + 8), 'backend' => $backends[$mount['class']]['backend'], - 'configuration' => $mount['options'], - 'status' => self::getBackendStatus($mount['class'], $mount['options']) + 'options' => $mount['options'], + 'status' => self::getBackendStatus($mount['class'], $mount['options'], true) ); } } @@ -384,7 +402,7 @@ class OC_Mount_Config { * @param array $options backend configuration options * @return bool true if the connection succeeded, false otherwise */ - private static function getBackendStatus($class, $options) { + private static function getBackendStatus($class, $options, $isPersonal) { if (self::$skipTest) { return true; } @@ -394,7 +412,7 @@ class OC_Mount_Config { if (class_exists($class)) { try { $storage = new $class($options); - return $storage->test(); + return $storage->test($isPersonal); } catch (Exception $exception) { \OCP\Util::logException('files_external', $exception); return false; @@ -461,7 +479,7 @@ class OC_Mount_Config { $mountPoints[$mountType] = $mount; } self::writeData($isPersonal, $mountPoints); - return self::getBackendStatus($class, $classOptions); + return self::getBackendStatus($class, $classOptions, $isPersonal); } /** @@ -538,7 +556,12 @@ class OC_Mount_Config { $datadir = \OC_Config::getValue('datadirectory', \OC::$SERVERROOT . '/data/'); $file = \OC_Config::getValue('mount_file', $datadir . '/mount.json'); } - $content = json_encode($data); + $options = 0; + if (defined('JSON_PRETTY_PRINT')) { + // only for PHP >= 5.4 + $options = JSON_PRETTY_PRINT; + } + $content = json_encode($data, $options); @file_put_contents($file, $content); @chmod($file, 0640); } @@ -707,4 +730,20 @@ class OC_Mount_Config { $cipher->setKey(\OCP\Config::getSystemValue('passwordsalt')); return $cipher; } + + /** + * Computes a hash based on the given configuration. + * This is mostly used to find out whether configurations + * are the same. + */ + private static function makeConfigHash($config) { + $data = json_encode( + array( + 'c' => $config['class'], + 'm' => $config['mountpoint'], + 'o' => $config['options'] + ) + ); + return hash('md5', $data); + } } diff --git a/apps/files_external/lib/smb_oc.php b/apps/files_external/lib/smb_oc.php new file mode 100644 index 00000000000..0c79c06c5df --- /dev/null +++ b/apps/files_external/lib/smb_oc.php @@ -0,0 +1,93 @@ +<?php +/** + * Copyright (c) 2014 Robin McCorkell <rmccorkell@karoshi.org.uk> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\Files\Storage; + +require_once __DIR__ . '/../3rdparty/smb4php/smb.php'; + +class SMB_OC extends \OC\Files\Storage\SMB { + private $username_as_share; + + public function __construct($params) { + if (isset($params['host']) && \OC::$session->exists('smb-credentials')) { + $host=$params['host']; + $this->username_as_share = ($params['username_as_share'] === 'true'); + + $params_auth = \OC::$session->get('smb-credentials'); + $user = \OC::$session->get('loginname'); + $password = $params_auth['password']; + + $root=isset($params['root'])?$params['root']:'/'; + $share = ''; + + if ($this->username_as_share) { + $share = '/'.$user; + } elseif (isset($params['share'])) { + $share = $params['share']; + } else { + throw new \Exception(); + } + parent::__construct(array( + "user" => $user, + "password" => $password, + "host" => $host, + "share" => $share, + "root" => $root + )); + } else { + throw new \Exception(); + } + } + + public static function login( $params ) { + \OC::$session->set('smb-credentials', $params); + } + + public function isSharable($path) { + return false; + } + + public function test($isPersonal = true) { + if ($isPersonal) { + if ($this->stat('')) { + return true; + } + return false; + } else { + $smb = new \smb(); + $pu = $smb->parse_url($this->constructUrl('')); + + // Attempt to connect anonymously + $pu['user'] = ''; + $pu['pass'] = ''; + + // Share cannot be checked if dynamic + if ($this->username_as_share) { + if ($smb->look($pu)) { + return true; + } else { + return false; + } + } + if (!$pu['share']) { + return false; + } + + // The following error messages are expected due to anonymous login + $regexp = array( + '(NT_STATUS_ACCESS_DENIED)' => 'skip' + ) + $smb->getRegexp(); + + if ($smb->client("-d 0 " . escapeshellarg('//' . $pu['host'] . '/' . $pu['share']) . " -c exit", $pu, $regexp)) { + return true; + } else { + return false; + } + } + } +} diff --git a/apps/files_external/lib/swift.php b/apps/files_external/lib/swift.php index 7a56fcfc8b7..1337d9f581d 100644 --- a/apps/files_external/lib/swift.php +++ b/apps/files_external/lib/swift.php @@ -251,6 +251,10 @@ class Swift extends \OC\Files\Storage\Common { $mtime = $object->extra_headers['X-Object-Meta-Timestamp']; } + if (!empty($mtime)) { + $mtime = floor($mtime); + } + $stat = array(); $stat['size'] = $object->content_length; $stat['mtime'] = $mtime; @@ -370,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 de44d3c8644..5e84fa8a252 100644 --- a/apps/files_external/templates/settings.php +++ b/apps/files_external/templates/settings.php @@ -16,17 +16,17 @@ </thead> <tbody width="100%"> <?php $_['mounts'] = array_merge($_['mounts'], array('' => array())); ?> - <?php foreach ($_['mounts'] as $mountPoint => $mount): ?> - <tr <?php print_unescaped(($mountPoint != '') ? 'class="'.OC_Util::sanitizeHTML($mount['class']).'"' : 'id="addMountPoint"'); ?>> + <?php foreach ($_['mounts'] as $mount): ?> + <tr <?php print_unescaped(($mount['mountpoint'] !== '') ? 'class="'.OC_Util::sanitizeHTML($mount['class']).'"' : 'id="addMountPoint"'); ?>> <td class="status"> <?php if (isset($mount['status'])): ?> <span class="<?php p(($mount['status']) ? 'success' : 'error'); ?>"></span> <?php endif; ?> </td> <td class="mountPoint"><input type="text" name="mountPoint" - value="<?php p($mountPoint); ?>" + value="<?php p($mount['mountpoint']); ?>" placeholder="<?php p($l->t('Folder name')); ?>" /></td> - <?php if ($mountPoint == ''): ?> + <?php if ($mount['mountpoint'] == ''): ?> <td class="backend"> <select id="selectBackend" data-configurations='<?php print_unescaped(json_encode($_['backends'])); ?>'> <option value="" disabled selected @@ -41,8 +41,8 @@ data-class="<?php p($mount['class']); ?>"><?php p($mount['backend']); ?></td> <?php endif; ?> <td class ="configuration" width="100%"> - <?php if (isset($mount['configuration'])): ?> - <?php foreach ($mount['configuration'] as $parameter => $value): ?> + <?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): ?> @@ -108,7 +108,7 @@ </select> </td> <?php endif; ?> - <td <?php if ($mountPoint != ''): ?>class="remove" + <td <?php if ($mount['mountpoint'] != ''): ?>class="remove" <?php else: ?>style="visibility:hidden;" <?php endif ?>><img alt="<?php p($l->t('Delete')); ?>" title="<?php p($l->t('Delete')); ?>" diff --git a/apps/files_external/tests/mountconfig.php b/apps/files_external/tests/mountconfig.php index bf43bb31c38..c89874c94d5 100644 --- a/apps/files_external/tests/mountconfig.php +++ b/apps/files_external/tests/mountconfig.php @@ -212,13 +212,51 @@ class Test_Mount_Config extends \PHPUnit_Framework_TestCase { } /** + * Provider for testing configurations with different + * "applicable" values (all, user, groups) + */ + public function applicableConfigProvider() { + return array( + // applicable to "all" + array( + OC_Mount_Config::MOUNT_TYPE_USER, + 'all', + array( + 'users' => array('all'), + 'groups' => array() + ) + ), + // applicable to single user + array( + OC_Mount_Config::MOUNT_TYPE_USER, + self::TEST_USER1, + array( + 'users' => array(self::TEST_USER1), + 'groups' => array() + ) + ), + // applicable to single group + array( + OC_Mount_Config::MOUNT_TYPE_GROUP, + self::TEST_GROUP1, + array( + 'users' => array(), + 'groups' => array(self::TEST_GROUP1) + ) + ), + ); + } + + /** * Test reading and writing global config + * + * @dataProvider applicableConfigProvider */ - public function testReadWriteGlobalConfig() { - $mountType = OC_Mount_Config::MOUNT_TYPE_USER; - $applicable = 'all'; + public function testReadWriteGlobalConfig($mountType, $applicable, $expectApplicableArray) { + $mountType = $mountType; + $applicable = $applicable; $isPersonal = false; - $mountConfig = array( + $options = array( 'host' => 'smbhost', 'user' => 'smbuser', 'password' => 'smbpassword', @@ -231,7 +269,7 @@ class Test_Mount_Config extends \PHPUnit_Framework_TestCase { OC_Mount_Config::addMountPoint( '/ext', '\OC\Files\Storage\SMB', - $mountConfig, + $options, $mountType, $applicable, $isPersonal @@ -241,12 +279,13 @@ class Test_Mount_Config extends \PHPUnit_Framework_TestCase { // re-read config $config = OC_Mount_Config::getSystemMountPoints(); $this->assertEquals(1, count($config)); - $this->assertTrue(isset($config['ext'])); - $this->assertEquals('\OC\Files\Storage\SMB', $config['ext']['class']); - $savedMountConfig = $config['ext']['configuration']; - $this->assertEquals($mountConfig, $savedMountConfig); + $this->assertEquals('\OC\Files\Storage\SMB', $config[0]['class']); + $this->assertEquals('ext', $config[0]['mountpoint']); + $this->assertEquals($expectApplicableArray, $config[0]['applicable']); + $savedOptions = $config[0]['options']; + $this->assertEquals($options, $savedOptions); // key order needs to be preserved for the UI... - $this->assertEquals(array_keys($mountConfig), array_keys($savedMountConfig)); + $this->assertEquals(array_keys($options), array_keys($savedOptions)); } /** @@ -256,7 +295,7 @@ class Test_Mount_Config extends \PHPUnit_Framework_TestCase { $mountType = OC_Mount_Config::MOUNT_TYPE_USER; $applicable = self::TEST_USER1; $isPersonal = true; - $mountConfig = array( + $options = array( 'host' => 'smbhost', 'user' => 'smbuser', 'password' => 'smbpassword', @@ -269,7 +308,7 @@ class Test_Mount_Config extends \PHPUnit_Framework_TestCase { OC_Mount_Config::addMountPoint( '/ext', '\OC\Files\Storage\SMB', - $mountConfig, + $options, $mountType, $applicable, $isPersonal @@ -279,12 +318,12 @@ class Test_Mount_Config extends \PHPUnit_Framework_TestCase { // re-read config $config = OC_Mount_Config::getPersonalMountPoints(); $this->assertEquals(1, count($config)); - $this->assertTrue(isset($config['ext'])); - $this->assertEquals('\OC\Files\Storage\SMB', $config['ext']['class']); - $savedMountConfig = $config['ext']['configuration']; - $this->assertEquals($mountConfig, $savedMountConfig); + $this->assertEquals('\OC\Files\Storage\SMB', $config[0]['class']); + $this->assertEquals('ext', $config[0]['mountpoint']); + $savedOptions = $config[0]['options']; + $this->assertEquals($options, $savedOptions); // key order needs to be preserved for the UI... - $this->assertEquals(array_keys($mountConfig), array_keys($savedMountConfig)); + $this->assertEquals(array_keys($options), array_keys($savedOptions)); } /** @@ -362,7 +401,7 @@ class Test_Mount_Config extends \PHPUnit_Framework_TestCase { // re-read config, password was read correctly $config = OC_Mount_Config::getPersonalMountPoints(); - $savedMountConfig = $config['ext']['configuration']; + $savedMountConfig = $config[0]['options']; $this->assertEquals($mountConfig, $savedMountConfig); } @@ -475,4 +514,130 @@ class Test_Mount_Config extends \PHPUnit_Framework_TestCase { $this->assertEquals(0, count($mountPoints)); } } + + /** + * Test the same config for multiple users. + * The config will be merged by getSystemMountPoints(). + */ + public function testConfigMerging() { + $mountType = OC_Mount_Config::MOUNT_TYPE_USER; + $isPersonal = false; + $options = array( + 'host' => 'smbhost', + 'user' => 'smbuser', + 'password' => 'smbpassword', + 'share' => 'smbshare', + 'root' => 'smbroot' + ); + + // write config + $this->assertTrue( + OC_Mount_Config::addMountPoint( + '/ext', + '\OC\Files\Storage\SMB', + $options, + OC_Mount_Config::MOUNT_TYPE_USER, + self::TEST_USER1, + $isPersonal + ) + ); + + $this->assertTrue( + OC_Mount_Config::addMountPoint( + '/ext', + '\OC\Files\Storage\SMB', + $options, + OC_Mount_Config::MOUNT_TYPE_USER, + self::TEST_USER2, + $isPersonal + ) + ); + + $this->assertTrue( + OC_Mount_Config::addMountPoint( + '/ext', + '\OC\Files\Storage\SMB', + $options, + OC_Mount_Config::MOUNT_TYPE_GROUP, + self::TEST_GROUP2, + $isPersonal + ) + ); + + $this->assertTrue( + OC_Mount_Config::addMountPoint( + '/ext', + '\OC\Files\Storage\SMB', + $options, + OC_Mount_Config::MOUNT_TYPE_GROUP, + self::TEST_GROUP1, + $isPersonal + ) + ); + + // re-read config + $config = OC_Mount_Config::getSystemMountPoints(); + $this->assertEquals(1, count($config)); + $this->assertEquals('\OC\Files\Storage\SMB', $config[0]['class']); + $this->assertEquals('ext', $config[0]['mountpoint']); + $this->assertEquals($options, $config[0]['options']); + $this->assertEquals(array(self::TEST_USER1, self::TEST_USER2), $config[0]['applicable']['users']); + $this->assertEquals(array(self::TEST_GROUP2, self::TEST_GROUP1), $config[0]['applicable']['groups']); + } + + /** + * Create then re-read mount points configs where the mount points + * have the same path, the config must NOT be merged. + */ + public function testRereadMountpointWithSamePath() { + $mountType = OC_Mount_Config::MOUNT_TYPE_USER; + $isPersonal = false; + $options1 = array( + 'host' => 'smbhost', + 'user' => 'smbuser', + 'password' => 'smbpassword', + 'share' => 'smbshare', + 'root' => 'smbroot' + ); + + // write config + $this->assertTrue( + OC_Mount_Config::addMountPoint( + '/ext', + '\OC\Files\Storage\SMB', + $options1, + $mountType, + self::TEST_USER1, + $isPersonal + ) + ); + + $options2 = array( + 'host' => 'anothersmbhost', + 'user' => 'anothersmbuser', + 'password' => 'anothersmbpassword', + 'share' => 'anothersmbshare', + 'root' => 'anothersmbroot' + ); + $this->assertTrue( + OC_Mount_Config::addMountPoint( + '/ext', + '\OC\Files\Storage\SMB', + $options2, + $mountType, + self::TEST_USER2, + $isPersonal + ) + ); + + // re-read config + $config = OC_Mount_Config::getSystemMountPoints(); + $this->assertEquals(2, count($config)); + $this->assertEquals('\OC\Files\Storage\SMB', $config[0]['class']); + $this->assertEquals('ext', $config[0]['mountpoint']); + $this->assertEquals($options1, $config[0]['options']); + $this->assertEquals('\OC\Files\Storage\SMB', $config[1]['class']); + $this->assertEquals('ext', $config[1]['mountpoint']); + $this->assertEquals($options2, $config[1]['options']); + } } 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/l10n/ar.php b/apps/files_sharing/l10n/ar.php index e5f919aac3f..f796f7ec59d 100644 --- a/apps/files_sharing/l10n/ar.php +++ b/apps/files_sharing/l10n/ar.php @@ -1,7 +1,17 @@ <?php $TRANSLATIONS = array( -"Shared by {owner}" => "مشاركة بواسطة المالك {owner}", +"Shared by {owner}" => "مشاركة من طرف {owner}", +"This share is password-protected" => "هذه المشاركة محمية بكلمة مرور", +"The password is wrong. Try again." => "كلمة المرور خاطئة. حاول مرة أخرى", "Password" => "كلمة المرور", -"shared by %s" => "مشاركة من قبل %s" +"Sorry, this link doesn’t seem to work anymore." => "عذرا، يبدو أن هذا الرابط لم يعد يعمل.", +"Reasons might be:" => "الأسباب الممكنة :", +"the item was removed" => "تم حذف العنصر المطلوب", +"the link expired" => "انتهت صلاحية الرابط", +"sharing is disabled" => "المشاركة غير مفعلة", +"For more info, please ask the person who sent this link." => "لمزيد من المعلومات، يرجى سؤال الشخص الذي أرسل هذا الرابط", +"shared by %s" => "مشاركة من قبل %s", +"Download %s" => "تحميل %s", +"Direct link" => "رابط مباشر" ); $PLURAL_FORMS = "nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;"; diff --git a/apps/files_sharing/l10n/cs_CZ.php b/apps/files_sharing/l10n/cs_CZ.php index 5d50ff41316..e7a8b16f000 100644 --- a/apps/files_sharing/l10n/cs_CZ.php +++ b/apps/files_sharing/l10n/cs_CZ.php @@ -10,7 +10,7 @@ $TRANSLATIONS = array( "the link expired" => "platnost odkazu vypršela", "sharing is disabled" => "sdílení je zakázané", "For more info, please ask the person who sent this link." => "Pro více informací kontaktujte osobu, která vám zaslala tento odkaz.", -"shared by %s" => "sdíleno %s", +"shared by %s" => "sdílí %s", "Download %s" => "Stáhnout %s", "Direct link" => "Přímý odkaz" ); diff --git a/apps/files_sharing/l10n/da.php b/apps/files_sharing/l10n/da.php index 6aae750cb78..6c6a39c2954 100644 --- a/apps/files_sharing/l10n/da.php +++ b/apps/files_sharing/l10n/da.php @@ -11,6 +11,7 @@ $TRANSLATIONS = array( "sharing is disabled" => "deling er deaktiveret", "For more info, please ask the person who sent this link." => "For yderligere information, kontakt venligst personen der sendte linket. ", "shared by %s" => "delt af %s", +"Download %s" => "Download %s", "Direct link" => "Direkte link" ); $PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/apps/files_sharing/lib/cache.php b/apps/files_sharing/lib/cache.php index 10f2182655f..01db29d72e2 100644 --- a/apps/files_sharing/lib/cache.php +++ b/apps/files_sharing/lib/cache.php @@ -357,9 +357,10 @@ class Shared_Cache extends Cache { * get the size of a folder and set it in the cache * * @param string $path + * @param array $entry (optional) meta data of the folder * @return int */ - public function calculateFolderSize($path) { + public function calculateFolderSize($path, $entry = null) { if ($cache = $this->getSourceCache($path)) { return $cache->calculateFolderSize($this->files[$path]); } diff --git a/apps/files_trashbin/l10n/ar.php b/apps/files_trashbin/l10n/ar.php index 68c51d1cc7d..4084daa127a 100644 --- a/apps/files_trashbin/l10n/ar.php +++ b/apps/files_trashbin/l10n/ar.php @@ -4,6 +4,7 @@ $TRANSLATIONS = array( "Couldn't restore %s" => "تعذّر استرجاع %s ", "Deleted files" => "حذف الملفات", "Error" => "خطأ", +"restored" => "تمت الاستعادة", "Nothing in here. Your trash bin is empty!" => "لا يوجد شيء هنا. سلة المهملات خاليه.", "Name" => "اسم", "Restore" => "استعيد", diff --git a/apps/files_trashbin/lib/trashbin.php b/apps/files_trashbin/lib/trashbin.php index 7e91f8a59ff..7b14a4ec081 100644 --- a/apps/files_trashbin/lib/trashbin.php +++ b/apps/files_trashbin/lib/trashbin.php @@ -78,8 +78,8 @@ class Trashbin { $view = new \OC\Files\View('/'); - $source = \OCP\User::getUser().'/files_trashbin/files/' . $sourceFilename . '.d' . $timestamp; - $target = $owner.'/files_trashbin/files/' . $ownerFilename . '.d' . $timestamp; + $source = \OCP\User::getUser() . '/files_trashbin/files/' . $sourceFilename . '.d' . $timestamp; + $target = $owner . '/files_trashbin/files/' . $ownerFilename . '.d' . $timestamp; self::copy_recursive($source, $target, $view); @@ -117,7 +117,7 @@ class Trashbin { $proxyStatus = \OC_FileProxy::$enabled; \OC_FileProxy::$enabled = false; $trashPath = '/files_trashbin/files/' . $filename . '.d' . $timestamp; - $sizeOfAddedFiles = self::copy_recursive('/files/'.$file_path, $trashPath, $view); + $sizeOfAddedFiles = self::copy_recursive('/files/' . $file_path, $trashPath, $view); \OC_FileProxy::$enabled = $proxyStatus; if ($view->file_exists('files_trashbin/files/' . $filename . '.d' . $timestamp)) { @@ -145,7 +145,7 @@ class Trashbin { $userTrashSize -= self::expire($userTrashSize, $user); // if owner !== user we also need to update the owners trash size - if($owner !== $user) { + if ($owner !== $user) { $ownerTrashSize = self::getTrashbinSize($owner); $ownerTrashSize += $size; $ownerTrashSize -= self::expire($ownerTrashSize, $owner); @@ -298,6 +298,7 @@ class Trashbin { /** * restore files from trash bin + * * @param $file path to the deleted file * @param $filename name of the file * @param $timestamp time when the file was deleted @@ -312,7 +313,7 @@ class Trashbin { $location = ''; if ($timestamp) { $query = \OC_DB::prepare('SELECT `location` FROM `*PREFIX*files_trash`' - . ' WHERE `user`=? AND `id`=? AND `timestamp`=?'); + . ' WHERE `user`=? AND `id`=? AND `timestamp`=?'); $result = $query->execute(array($user, $filename, $timestamp))->fetchAll(); if (count($result) !== 1) { \OC_Log::write('files_trashbin', 'trash bin database inconsistent!', \OC_Log::ERROR); @@ -320,8 +321,9 @@ class Trashbin { $location = $result[0]['location']; // if location no longer exists, restore file in the root directory if ($location !== '/' && - (!$view->is_dir('files' . $location) || - !$view->isUpdatable('files' . $location))) { + (!$view->is_dir('files' . $location) || + !$view->isUpdatable('files' . $location)) + ) { $location = ''; } } @@ -631,6 +633,7 @@ class Trashbin { /** * check to see whether a file exists in trashbin + * * @param $filename path to the file * @param $timestamp of deletion time * @return true if file exists, otherwise false @@ -720,6 +723,7 @@ class Trashbin { /** * clean up the trash bin + * * @param int $trashbinSize current size of the trash bin * @param string $user * @return int size of expired files @@ -755,7 +759,7 @@ class Trashbin { // if size limit for trash bin reached, delete oldest files in trash bin if ($availableSpace < 0) { $query = \OC_DB::prepare('SELECT `location`,`type`,`id`,`timestamp` FROM `*PREFIX*files_trash`' - . ' WHERE `user`=? ORDER BY `timestamp` ASC'); + . ' WHERE `user`=? ORDER BY `timestamp` ASC'); $result = $query->execute(array($user))->fetchAll(); $length = count($result); $i = 0; @@ -803,6 +807,7 @@ class Trashbin { /** * find all versions which belong to the file we want to restore + * * @param $filename name of the file which should be restored * @param $timestamp timestamp when the file was deleted */ @@ -822,10 +827,10 @@ class Trashbin { foreach ($matches as $ma) { if ($timestamp) { $parts = explode('.v', substr($ma, 0, $offset)); - $versions[] = ( end($parts) ); + $versions[] = (end($parts)); } else { $parts = explode('.v', $ma); - $versions[] = ( end($parts) ); + $versions[] = (end($parts)); } } return $versions; @@ -833,6 +838,7 @@ class Trashbin { /** * find unique extension for restored file if a file with the same name already exists + * * @param $location where the file should be restored * @param $filename name of the file * @param \OC\Files\View $view filesystem view relative to users root directory @@ -850,9 +856,9 @@ class Trashbin { if ($view->file_exists('files' . $location . '/' . $filename)) { $i = 2; - $uniqueName = $name . " (".$l->t("restored").")". $ext; + $uniqueName = $name . " (" . $l->t("restored") . ")" . $ext; while ($view->file_exists('files' . $location . '/' . $uniqueName)) { - $uniqueName = $name . " (".$l->t("restored") . " " . $i . ")" . $ext; + $uniqueName = $name . " (" . $l->t("restored") . " " . $i . ")" . $ext; $i++; } @@ -915,16 +921,19 @@ class Trashbin { public static function isEmpty($user) { $view = new \OC\Files\View('/' . $user . '/files_trashbin'); - $content = $view->getDirectoryContent('/files'); - - if ($content) { + $dh = $view->opendir('/files'); + if (!$dh) { return false; } - + while ($file = readdir($dh)) { + if ($file !== '.' and $file !== '..') { + return false; + } + } return true; } public static function preview_icon($path) { - return \OC_Helper::linkToRoute( 'core_ajax_trashbin_preview', array('x' => 36, 'y' => 36, 'file' => $path )); + return \OC_Helper::linkToRoute('core_ajax_trashbin_preview', array('x' => 36, 'y' => 36, 'file' => $path)); } } diff --git a/apps/files_versions/appinfo/info.xml b/apps/files_versions/appinfo/info.xml index 661d64aa979..a735caee945 100644 --- a/apps/files_versions/appinfo/info.xml +++ b/apps/files_versions/appinfo/info.xml @@ -9,7 +9,7 @@ <description> ownCloud supports simple version control for files. The versioning app expires old versions automatically to make sure that - the user doesn't run out of space. Following pattern is used to delete + the user doesn't run out of space. The following pattern is used to delete old versions: For the first 10 seconds ownCloud keeps one version every 2 seconds; For the first hour ownCloud keeps one version every minute; 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/appinfo/info.xml b/apps/user_ldap/appinfo/info.xml index 9cc908e8522..e4a4375a737 100644 --- a/apps/user_ldap/appinfo/info.xml +++ b/apps/user_ldap/appinfo/info.xml @@ -2,8 +2,8 @@ <info> <id>user_ldap</id> <name>LDAP user and group backend</name> - <description>Authenticate users and groups by LDAP respectively Active - Directory. + <description>Authenticate users and groups through LDAP, such as OpenLDAP + or Active Directory. This app is not compatible with the WebDAV user backend. </description> diff --git a/apps/user_ldap/l10n/am_ET.php b/apps/user_ldap/l10n/am_ET.php new file mode 100644 index 00000000000..3a1e002311c --- /dev/null +++ b/apps/user_ldap/l10n/am_ET.php @@ -0,0 +1,6 @@ +<?php +$TRANSLATIONS = array( +"_%s group found_::_%s groups found_" => array("",""), +"_%s user found_::_%s users found_" => array("","") +); +$PLURAL_FORMS = "nplurals=2; plural=(n != 1);"; diff --git a/apps/user_ldap/l10n/cs_CZ.php b/apps/user_ldap/l10n/cs_CZ.php index 930a4b10647..536834ee8b5 100644 --- a/apps/user_ldap/l10n/cs_CZ.php +++ b/apps/user_ldap/l10n/cs_CZ.php @@ -33,12 +33,14 @@ $TRANSLATIONS = array( "Save" => "Uložit", "Test Configuration" => "Vyzkoušet nastavení", "Help" => "Nápověda", +"Groups meeting these criteria are available in %s:" => "Skupiny splňující tyto podmínky jsou k dispozici v %s:", "only those object classes:" => "pouze tyto objektové třídy:", "only from those groups:" => "pouze z těchto skupin:", "Edit raw filter instead" => "Edituj filtr přímo", "Raw LDAP filter" => "Původní filtr LDAP", "The filter specifies which LDAP groups shall have access to the %s instance." => "Filtr určuje, kteří uživatelé LDAP mají mít přístup k instanci %s.", "groups found" => "nalezené skupiny", +"Users login with this attribute:" => "Uživatelé se přihlašují s tímto atributem:", "LDAP Username:" => "LDAP uživatelské jméno:", "LDAP Email Address:" => "LDAP e-mailová adresa:", "Other Attributes:" => "Další atributy:", @@ -53,6 +55,7 @@ $TRANSLATIONS = array( "For anonymous access, leave DN and Password empty." => "Pro anonymní přístup ponechte údaje DN and heslo prázdné.", "One Base DN per line" => "Jedna základní DN na řádku", "You can specify Base DN for users and groups in the Advanced tab" => "V rozšířeném nastavení můžete určit základní DN pro uživatele a skupiny", +"Limit %s access to users meeting these criteria:" => "Omezit přístup %s uživatelům splňujícím tyto podmínky:", "The filter specifies which LDAP users shall have access to the %s instance." => "Filtr určuje, kteří uživatelé LDAP mají mít přístup k instanci %s.", "users found" => "nalezení uživatelé", "Back" => "Zpět", @@ -85,6 +88,8 @@ $TRANSLATIONS = array( "One Group Base DN per line" => "Jedna skupinová základní DN na řádku", "Group Search Attributes" => "Atributy vyhledávání skupin", "Group-Member association" => "Asociace člena skupiny", +"Nested Groups" => "Vnořené skupiny", +"When switched on, groups that contain groups are supported. (Only works if the group member attribute contains DNs.)" => "Pokud zapnuto, je možno používat skupiny, které obsahují jiné skupiny. (Funguje pouze pokud atribut člena skupiny obsahuje DN.)", "Special Attributes" => "Speciální atributy", "Quota Field" => "Pole pro kvótu", "Quota Default" => "Výchozí kvóta", diff --git a/apps/user_ldap/lib/configuration.php b/apps/user_ldap/lib/configuration.php index 612a623e910..d42b1c05820 100644 --- a/apps/user_ldap/lib/configuration.php +++ b/apps/user_ldap/lib/configuration.php @@ -119,9 +119,9 @@ class Configuration { $cta = $this->getConfigTranslationArray(); foreach($config as $inputkey => $val) { - if(strpos($inputkey, '_') !== false && isset($cta[$inputkey])) { + if(strpos($inputkey, '_') !== false && array_key_exists($inputkey, $cta)) { $key = $cta[$inputkey]; - } elseif(isset($this->config[$inputkey])) { + } elseif(array_key_exists($inputkey, $this->config)) { $key = $inputkey; } else { continue; diff --git a/apps/user_ldap/lib/connection.php b/apps/user_ldap/lib/connection.php index b2075748a3b..08ac4ac626b 100644 --- a/apps/user_ldap/lib/connection.php +++ b/apps/user_ldap/lib/connection.php @@ -78,6 +78,8 @@ class Connection extends LDAPUtility { //a cloned instance inherits the connection resource. It may use it, //but it may not disconnect it $this->dontDestruct = true; + $this->configuration = new Configuration($this->configPrefix, + !is_null($this->configID)); } public function __get($name) { diff --git a/apps/user_ldap/tests/connection.php b/apps/user_ldap/tests/connection.php new file mode 100644 index 00000000000..f51b0c83017 --- /dev/null +++ b/apps/user_ldap/tests/connection.php @@ -0,0 +1,54 @@ +<?php +/** +* ownCloud +* +* @author Arthur Schiwon +* @copyright 2013 Arthur Schiwon blizzz@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 OCA\user_ldap\tests; + +class Test_Connection extends \PHPUnit_Framework_TestCase { + + public function testOriginalAgentUnchangedOnClone() { + //background: upon login a bind is done with the user credentials + //which is valid for the whole LDAP resource. It needs to be reset + //to the agent's credentials + $lw = $this->getMock('\OCA\user_ldap\lib\ILDAPWrapper'); + + $connection = new \OCA\user_ldap\lib\Connection($lw, '', null); + $agent = array( + 'ldapAgentName' => 'agent', + 'ldapAgentPassword' => '123456', + ); + $connection->setConfiguration($agent); + + $testConnection = clone $connection; + $user = array( + 'ldapAgentName' => 'user', + 'ldapAgentPassword' => 'password', + ); + $testConnection->setConfiguration($user); + + $agentName = $connection->ldapAgentName; + $agentPawd = $connection->ldapAgentPassword; + + $this->assertSame($agentName, $agent['ldapAgentName']); + $this->assertSame($agentPawd, $agent['ldapAgentPassword']); + } + +}
\ No newline at end of file diff --git a/apps/user_webdavauth/appinfo/info.xml b/apps/user_webdavauth/appinfo/info.xml index 76b314e48aa..20c5909cc12 100755 --- a/apps/user_webdavauth/appinfo/info.xml +++ b/apps/user_webdavauth/appinfo/info.xml @@ -4,7 +4,7 @@ <name>WebDAV user backend</name> <description>Authenticate users by a WebDAV call. You can use any WebDAV server, ownCloud server or other webserver to authenticate. It should return http 200 for right credentials and http 401 for wrong ones. - This app is not compatible to the LDAP user and group backend.</description> + This app is not compatible with the LDAP user and group backend.</description> <licence>AGPL</licence> <author>Frank Karlitschek</author> <require>4.93</require> |