diff --git a/.gitignore b/.gitignore index 25cb1b227f..e61ec6f035 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ /data /owncloud /config/config.php +/config/*.config.php /config/mount.php /apps/inc.php diff --git a/.jshintrc b/.jshintrc index f40dd22b5f..90cec5c596 100644 --- a/.jshintrc +++ b/.jshintrc @@ -1,5 +1,5 @@ { - "camelCase": true, + "camelcase": true, "eqeqeq": true, "immed": true, "latedef": false, diff --git a/3rdparty b/3rdparty index c7b4cdbcc1..177d3ff656 160000 --- a/3rdparty +++ b/3rdparty @@ -1 +1 @@ -Subproject commit c7b4cdbcc1faa56df2489a5753b457627f460c07 +Subproject commit 177d3ff656bcf1153b4def12403c5f2d4fc53e73 diff --git a/apps/files/ajax/rawlist.php b/apps/files/ajax/rawlist.php index 40da32b223..89c21a172f 100644 --- a/apps/files/ajax/rawlist.php +++ b/apps/files/ajax/rawlist.php @@ -1,12 +1,12 @@ getPreviewManager()->isMimeSupported($file['mimetype']); - $file["date"] = OCP\Util::formatDate($file["mtime"]); - $file['mimetype_icon'] = \OCA\Files\Helper::determineIcon($file); - $files[] = $file; - } +if ($mimetypes && !in_array('httpd/unix-directory', $mimetypes)) { + $files = array_merge($files, \OC\Files\Filesystem::getDirectoryContent($dir, 'httpd/unix-directory')); } if (is_array($mimetypes) && count($mimetypes)) { foreach ($mimetypes as $mimetype) { - foreach( \OC\Files\Filesystem::getDirectoryContent( $dir, $mimetype ) as $file ) { - $file['directory'] = $dir; - $file['isPreviewAvailable'] = \OC::$server->getPreviewManager()->isMimeSupported($file['mimetype']); - $file["date"] = OCP\Util::formatDate($file["mtime"]); - $file['mimetype_icon'] = \OCA\Files\Helper::determineIcon($file); - $files[] = $file; - } + $files = array_merge($files, \OC\Files\Filesystem::getDirectoryContent($dir, $mimetype)); } } else { - foreach( \OC\Files\Filesystem::getDirectoryContent( $dir ) as $file ) { - $file['directory'] = $dir; - $file['isPreviewAvailable'] = \OC::$server->getPreviewManager()->isMimeSupported($file['mimetype']); - $file["date"] = OCP\Util::formatDate($file["mtime"]); - $file['mimetype_icon'] = \OCA\Files\Helper::determineIcon($file); - $files[] = $file; - } + $files = array_merge($files, \OC\Files\Filesystem::getDirectoryContent($dir)); +} + +$result = array(); +foreach ($files as $file) { + $fileData = array(); + $fileData['directory'] = $dir; + $fileData['name'] = $file->getName(); + $fileData['type'] = $file->getType(); + $fileData['path'] = $file['path']; + $fileData['id'] = $file->getId(); + $fileData['size'] = $file->getSize(); + $fileData['mtime'] = $file->getMtime(); + $fileData['mimetype'] = $file->getMimetype(); + $fileData['isPreviewAvailable'] = \OC::$server->getPreviewManager()->isMimeSupported($file->getMimetype()); + $fileData["date"] = OCP\Util::formatDate($file->getMtime()); + $fileData['mimetype_icon'] = \OCA\Files\Helper::determineIcon($file); + $result[] = $fileData; } // Sort by name -usort($files, function ($a, $b) { - if ($a['name'] === $b['name']) { - return 0; - } - return ($a['name'] < $b['name']) ? -1 : 1; -}); +usort($result, array('\OCA\Files\Helper', 'fileCmp')); -OC_JSON::success(array('data' => $files)); +OC_JSON::success(array('data' => $result)); diff --git a/apps/files/css/files.css b/apps/files/css/files.css index 3ad167054c..af863aca33 100644 --- a/apps/files/css/files.css +++ b/apps/files/css/files.css @@ -20,7 +20,7 @@ padding: 10px; font-weight: normal; } -#new>a { +#new > a { padding: 14px 10px; position: relative; top: 7px; @@ -30,7 +30,7 @@ border-bottom-right-radius: 0; border-bottom: none; } -#new>ul { +#new > ul { display: none; position: fixed; min-width: 112px; @@ -39,16 +39,26 @@ padding-bottom: 0; margin-top: 14px; margin-left: -1px; - text-align:left; + text-align: left; background: #f8f8f8; border: 1px solid #ddd; border-radius: 5px; border-top-left-radius: 0; - box-shadow:0 2px 7px rgba(170,170,170,.4); + box-shadow: 0 2px 7px rgba(170,170,170,.4); +} +#new > ul > li { + height: 36px; + margin: 5px; + padding-left: 42px; + padding-bottom: 2px; + background-position: initial; + cursor: pointer; +} +#new > ul > li > p { + cursor: pointer; + padding-top: 7px; + padding-bottom: 7px; } -#new>ul>li { height:36px; margin:5px; padding-left:48px; padding-bottom:2px; - background-repeat:no-repeat; cursor:pointer; } -#new>ul>li>p { cursor:pointer; padding-top: 7px; padding-bottom: 7px;} #new .error, #fileList .error { color: #e9322d; diff --git a/apps/files/js/admin.js b/apps/files/js/admin.js index f735079fcb..842b73c0ca 100644 --- a/apps/files/js/admin.js +++ b/apps/files/js/admin.js @@ -8,19 +8,22 @@ * */ -function switchPublicFolder() -{ +function switchPublicFolder() { var publicEnable = $('#publicEnable').is(':checked'); - var sharingaimGroup = $('input:radio[name=sharingaim]'); //find all radiobuttons of that group + // find all radiobuttons of that group + var sharingaimGroup = $('input:radio[name=sharingaim]'); $.each(sharingaimGroup, function(index, sharingaimItem) { - sharingaimItem.disabled = !publicEnable; //set all buttons to the correct state + // set all buttons to the correct state + sharingaimItem.disabled = !publicEnable; }); } -$(document).ready(function(){ - switchPublicFolder(); // Execute the function after loading DOM tree - $('#publicEnable').click(function(){ - switchPublicFolder(); // To get rid of onClick() +$(document).ready(function() { + // Execute the function after loading DOM tree + switchPublicFolder(); + $('#publicEnable').click(function() { + // To get rid of onClick() + switchPublicFolder(); }); $('#allowZipDownload').bind('change', function() { diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index d6cffde05d..550c10dba3 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -11,6 +11,7 @@ /* global OC, t, n, FileList, FileActions, Files */ /* global procesSelection, dragOptions, SVGSupport, replaceSVG */ window.FileList={ + appName: t('files', 'Files'), useUndo:true, postProcessList: function() { $('#fileList tr').each(function() { @@ -18,6 +19,21 @@ window.FileList={ $(this).attr('data-file',decodeURIComponent($(this).attr('data-file'))); }); }, + /** + * Sets a new page title + */ + setPageTitle: function(title){ + if (title) { + title += ' - '; + } else { + title = ''; + } + title += FileList.appName; + // Sets the page title with the " - ownCloud" suffix as in templates + window.document.title = title + ' - ' + oc_defaults.title; + + return true; + }, /** * Returns the tr element for a given file name */ @@ -129,7 +145,7 @@ window.FileList={ if (loading) { imgurl = OC.imagePath('core', 'loading.gif'); } else { - imgurl = OC.imagePath('core', 'filetypes/file.png'); + imgurl = OC.imagePath('core', 'filetypes/file'); } var tr = this.createRow( 'file', @@ -157,7 +173,7 @@ window.FileList={ var tr = this.createRow( 'dir', name, - OC.imagePath('core', 'filetypes/folder.png'), + OC.imagePath('core', 'filetypes/folder'), OC.linkTo('files', 'index.php')+"?dir="+ encodeURIComponent($('#dir').val()+'/'+name).replace(/%2F/g, '/'), size, lastModified, @@ -204,7 +220,16 @@ window.FileList={ return OC.linkTo('files', 'index.php')+"?dir="+ encodeURIComponent(dir).replace(/%2F/g, '/'); }, setCurrentDir: function(targetDir, changeUrl) { - var url; + var url, + baseDir = OC.basename(targetDir); + + if (baseDir !== '') { + FileList.setPageTitle(baseDir); + } + else { + FileList.setPageTitle(); + } + $('#dir').val(targetDir); if (changeUrl !== false) { if (window.history.pushState && changeUrl !== false) { @@ -847,7 +872,8 @@ window.FileList={ }; $(document).ready(function() { - var isPublic = !!$('#isPublic').val(); + var baseDir, + isPublic = !!$('#isPublic').val(); // handle upload events var file_upload_start = $('#file_upload_start'); @@ -943,7 +969,7 @@ $(document).ready(function() { uploadtext.attr('currentUploads', currentUploads); var translatedText = n('files', 'Uploading %n file', 'Uploading %n files', currentUploads); if (currentUploads === 0) { - var img = OC.imagePath('core', 'filetypes/folder.png'); + var img = OC.imagePath('core', 'filetypes/folder'); data.context.find('td.filename').attr('style','background-image:url('+img+')'); uploadtext.text(translatedText); uploadtext.hide(); @@ -1003,7 +1029,7 @@ $(document).ready(function() { if (data.errorThrown === 'abort') { //cleanup uploading to a dir var uploadtext = $('tr .uploadtext'); - var img = OC.imagePath('core', 'filetypes/folder.png'); + var img = OC.imagePath('core', 'filetypes/folder'); uploadtext.parents('td.filename').attr('style','background-image:url('+img+')'); uploadtext.fadeOut(); uploadtext.attr('currentUploads', 0); @@ -1016,7 +1042,7 @@ $(document).ready(function() { if (data.errorThrown === 'abort') { //cleanup uploading to a dir var uploadtext = $('tr .uploadtext'); - var img = OC.imagePath('core', 'filetypes/folder.png'); + var img = OC.imagePath('core', 'filetypes/folder'); uploadtext.parents('td.filename').attr('style','background-image:url('+img+')'); uploadtext.fadeOut(); uploadtext.attr('currentUploads', 0); @@ -1132,5 +1158,7 @@ $(document).ready(function() { } } + FileList.setCurrentDir(parseCurrentDirFromUrl(), false); + FileList.createFileSummary(); }); diff --git a/apps/files/js/files.js b/apps/files/js/files.js index fbac601f67..f454612070 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -734,6 +734,9 @@ Files.getMimeIcon = function(mime, ready) { ready(Files.getMimeIcon.cache[mime]); } else { $.get( OC.filePath('files','ajax','mimeicon.php'), {mime: mime}, function(path) { + if(SVGSupport()){ + path = path.substr(0, path.length-4) + '.svg'; + } Files.getMimeIcon.cache[mime]=path; ready(Files.getMimeIcon.cache[mime]); }); @@ -783,13 +786,16 @@ Files.lazyLoadPreview = function(path, mime, ready, width, height, etag) { } previewURL = previewURL.replace('(', '%28'); previewURL = previewURL.replace(')', '%29'); + previewURL += '&forceIcon=0'; // preload image to prevent delay // this will make the browser cache the image var img = new Image(); img.onload = function(){ - //set preview thumbnail URL - ready(previewURL); + // if loading the preview image failed (no preview for the mimetype) then img.width will < 5 + if (img.width > 5) { + ready(previewURL); + } } img.src = previewURL; }); diff --git a/apps/files/lib/helper.php b/apps/files/lib/helper.php index ac8a2ad320..b9e41a352b 100644 --- a/apps/files/lib/helper.php +++ b/apps/files/lib/helper.php @@ -22,6 +22,7 @@ class Helper public static function determineIcon($file) { if($file['type'] === 'dir') { $dir = $file['directory']; + $icon = \OC_Helper::mimetypeIcon('dir'); $absPath = \OC\Files\Filesystem::getView()->getAbsolutePath($dir.'/'.$file['name']); $mount = \OC\Files\Filesystem::getMountManager()->find($absPath); if (!is_null($mount)) { @@ -29,21 +30,22 @@ class Helper if (!is_null($sid)) { $sid = explode(':', $sid); if ($sid[0] === 'shared') { - return \OC_Helper::mimetypeIcon('dir-shared'); + $icon = \OC_Helper::mimetypeIcon('dir-shared'); } if ($sid[0] !== 'local' and $sid[0] !== 'home') { - return \OC_Helper::mimetypeIcon('dir-external'); + $icon = \OC_Helper::mimetypeIcon('dir-external'); } } } - return \OC_Helper::mimetypeIcon('dir'); + }else{ + if($file['isPreviewAvailable']) { + $pathForPreview = $file['directory'] . '/' . $file['name']; + return \OC_Helper::previewIcon($pathForPreview) . '&c=' . $file['etag']; + } + $icon = \OC_Helper::mimetypeIcon($file['mimetype']); } - if($file['isPreviewAvailable']) { - $pathForPreview = $file['directory'] . '/' . $file['name']; - return \OC_Helper::previewIcon($pathForPreview) . '&c=' . $file['etag']; - } - return \OC_Helper::mimetypeIcon($file['mimetype']); + return substr($icon, 0, -3) . 'svg'; } /** diff --git a/apps/files/templates/index.php b/apps/files/templates/index.php index 939043b2c9..ed15e46a5a 100644 --- a/apps/files/templates/index.php +++ b/apps/files/templates/index.php @@ -5,12 +5,17 @@
t('New'));?>
diff --git a/apps/files/tests/ajax_rename.php b/apps/files/tests/ajax_rename.php index a1a5c8983b..e53c0fb3dd 100644 --- a/apps/files/tests/ajax_rename.php +++ b/apps/files/tests/ajax_rename.php @@ -110,7 +110,9 @@ class Test_OC_Files_App_Rename extends \PHPUnit_Framework_TestCase { $this->assertEquals('/test', $result['data']['directory']); $this->assertEquals(18, $result['data']['size']); $this->assertEquals('httpd/unix-directory', $result['data']['mime']); - $this->assertEquals(\OC_Helper::mimetypeIcon('dir'), $result['data']['icon']); + $icon = \OC_Helper::mimetypeIcon('dir'); + $icon = substr($icon, 0, -3) . 'svg'; + $this->assertEquals($icon, $result['data']['icon']); $this->assertFalse($result['data']['isPreviewAvailable']); } @@ -165,7 +167,9 @@ class Test_OC_Files_App_Rename extends \PHPUnit_Framework_TestCase { $this->assertEquals(18, $result['data']['size']); $this->assertEquals('httpd/unix-directory', $result['data']['mime']); $this->assertEquals('abcdef', $result['data']['etag']); - $this->assertEquals(\OC_Helper::mimetypeIcon('dir'), $result['data']['icon']); + $icon = \OC_Helper::mimetypeIcon('dir'); + $icon = substr($icon, 0, -3) . 'svg'; + $this->assertEquals($icon, $result['data']['icon']); $this->assertFalse($result['data']['isPreviewAvailable']); } diff --git a/apps/files_encryption/hooks/hooks.php b/apps/files_encryption/hooks/hooks.php index 3af43f1026..0b6c5adf3f 100644 --- a/apps/files_encryption/hooks/hooks.php +++ b/apps/files_encryption/hooks/hooks.php @@ -501,11 +501,20 @@ class Hooks { * @param array $params with the old path and the new path */ public static function preRename($params) { - $util = new Util(new \OC_FilesystemView('/'), \OCP\User::getUser()); + $user = \OCP\User::getUser(); + $view = new \OC_FilesystemView('/'); + $util = new Util($view, $user); list($ownerOld, $pathOld) = $util->getUidAndFilename($params['oldpath']); - self::$renamedFiles[$params['oldpath']] = array( - 'uid' => $ownerOld, - 'path' => $pathOld); + + // we only need to rename the keys if the rename happens on the same mountpoint + // otherwise we perform a stream copy, so we get a new set of keys + $mp1 = $view->getMountPoint('/' . $user . '/files/' . $params['oldpath']); + $mp2 = $view->getMountPoint('/' . $user . '/files/' . $params['newpath']); + if ($mp1 === $mp2) { + self::$renamedFiles[$params['oldpath']] = array( + 'uid' => $ownerOld, + 'path' => $pathOld); + } } /** diff --git a/apps/files_encryption/js/settings-admin.js b/apps/files_encryption/js/settings-admin.js index c2140a6f1e..785d02002f 100644 --- a/apps/files_encryption/js/settings-admin.js +++ b/apps/files_encryption/js/settings-admin.js @@ -7,28 +7,6 @@ * See the COPYING-README file. */ -OC.msg={ - startSaving:function(selector){ - $(selector) - .html( t('settings', 'Saving...') ) - .removeClass('success') - .removeClass('error') - .stop(true, true) - .show(); - }, - finishedSaving:function(selector, data){ - if( data.status === "success" ){ - $(selector).html( data.data.message ) - .addClass('success') - .stop(true, true) - .delay(3000) - .fadeOut(900); - }else{ - $(selector).html( data.data.message ).addClass('error'); - } - } -}; - $(document).ready(function(){ // Trigger ajax on recoveryAdmin status change var enabledStatus = $('#adminEnableRecovery').val(); diff --git a/apps/files_encryption/lib/proxy.php b/apps/files_encryption/lib/proxy.php index 9d456f6c51..a2d42c22c1 100644 --- a/apps/files_encryption/lib/proxy.php +++ b/apps/files_encryption/lib/proxy.php @@ -38,6 +38,7 @@ class Proxy extends \OC_FileProxy { private static $blackList = null; //mimetypes blacklisted from encryption private static $unencryptedSizes = array(); // remember unencrypted size + private static $fopenMode = array(); // remember the fopen mode /** * Check if a file requires encryption @@ -146,7 +147,7 @@ class Proxy extends \OC_FileProxy { if ( isset(self::$unencryptedSizes[$normalizedPath]) ) { $view = new \OC_FilesystemView('/'); $view->putFileInfo($normalizedPath, - array('encrypted' => true, 'encrypted_size' => self::$unencryptedSizes[$normalizedPath])); + array('encrypted' => true, 'unencrypted_size' => self::$unencryptedSizes[$normalizedPath])); unset(self::$unencryptedSizes[$normalizedPath]); } @@ -213,6 +214,16 @@ class Proxy extends \OC_FileProxy { return true; } + /** + * @brief remember initial fopen mode because sometimes it gets changed during the request + * @param string $path path + * @param string $mode type of access + */ + public function preFopen($path, $mode) { + self::$fopenMode[$path] = $mode; + } + + /** * @param $path * @param $result @@ -240,7 +251,15 @@ class Proxy extends \OC_FileProxy { $proxyStatus = \OC_FileProxy::$enabled; \OC_FileProxy::$enabled = false; - $meta = stream_get_meta_data($result); + // if we remember the mode from the pre proxy we re-use it + // oterwise we fall back to stream_get_meta_data() + if (isset(self::$fopenMode[$path])) { + $mode = self::$fopenMode[$path]; + unset(self::$fopenMode[$path]); + } else { + $meta = stream_get_meta_data($result); + $mode = $meta['mode']; + } $view = new \OC_FilesystemView(''); @@ -258,14 +277,15 @@ class Proxy extends \OC_FileProxy { // Open the file using the crypto stream wrapper // protocol and let it do the decryption work instead - $result = fopen('crypt://' . $path, $meta['mode']); + $result = fopen('crypt://' . $path, $mode); } elseif ( - self::shouldEncrypt($path) - and $meta['mode'] !== 'r' - and $meta['mode'] !== 'rb' + self::shouldEncrypt($path) + and $mode !== 'r' + and $mode !== 'rb' + ) { - $result = fopen('crypt://' . $path, $meta['mode']); + $result = fopen('crypt://' . $path, $mode); } // Re-enable the proxy diff --git a/apps/files_encryption/lib/session.php b/apps/files_encryption/lib/session.php index aa58e33e9d..3daaa06425 100644 --- a/apps/files_encryption/lib/session.php +++ b/apps/files_encryption/lib/session.php @@ -134,6 +134,14 @@ class Session { } + /** + * @brief remove encryption keys and init status from session + */ + public function closeSession() { + \OC::$session->remove('encryptionInitialized'); + \OC::$session->remove('privateKey'); + } + /** * @brief Gets status if we already tried to initialize the encryption app diff --git a/apps/files_encryption/lib/stream.php b/apps/files_encryption/lib/stream.php index 88eacc6f13..58ac03373a 100644 --- a/apps/files_encryption/lib/stream.php +++ b/apps/files_encryption/lib/stream.php @@ -167,6 +167,9 @@ class Stream { } else { $this->meta = stream_get_meta_data($this->handle); + // sometimes fopen changes the mode, e.g. for a url "r" convert to "r+" + // but we need to remember the original access type + $this->meta['mode'] = $mode; } diff --git a/apps/files_encryption/lib/util.php b/apps/files_encryption/lib/util.php index ec06bd52f5..6bf69cd8ee 100644 --- a/apps/files_encryption/lib/util.php +++ b/apps/files_encryption/lib/util.php @@ -1772,4 +1772,12 @@ class Util { return $session; } + /* + * @brief remove encryption related keys from the session + */ + public function closeEncryptionSession() { + $session = new \OCA\Encryption\Session($this->view); + $session->closeSession(); + } + } diff --git a/apps/files_encryption/tests/hooks.php b/apps/files_encryption/tests/hooks.php index 7d926caea1..d0e4b5f732 100644 --- a/apps/files_encryption/tests/hooks.php +++ b/apps/files_encryption/tests/hooks.php @@ -47,6 +47,7 @@ class Test_Encryption_Hooks extends \PHPUnit_Framework_TestCase { public $rootView; // view on /data/user public $data; public $filename; + public $folder; public static function setUpBeforeClass() { // reset backend @@ -89,6 +90,7 @@ class Test_Encryption_Hooks extends \PHPUnit_Framework_TestCase { // init short data $this->data = 'hats'; $this->filename = 'enc_hooks_tests-' . uniqid() . '.txt'; + $this->folder = 'enc_hooks_tests_folder-' . uniqid(); } @@ -268,4 +270,57 @@ class Test_Encryption_Hooks extends \PHPUnit_Framework_TestCase { } } + /** + * @brief test rename operation + */ + function testRenameHook() { + + // save file with content + $cryptedFile = file_put_contents('crypt:///' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files/' . $this->filename, $this->data); + + // test that data was successfully written + $this->assertTrue(is_int($cryptedFile)); + + // check if keys exists + $this->assertTrue($this->rootView->file_exists( + '/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/share-keys/' + . $this->filename . '.' . self::TEST_ENCRYPTION_HOOKS_USER1 . '.shareKey')); + + $this->assertTrue($this->rootView->file_exists( + '/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/keyfiles/' + . $this->filename . '.key')); + + // make subfolder + $this->rootView->mkdir('/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files/' . $this->folder); + + $this->assertTrue($this->rootView->is_dir('/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files/' . $this->folder)); + + // move the file out of the shared folder + $root = $this->rootView->getRoot(); + $this->rootView->chroot('/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files/'); + $this->rootView->rename($this->filename, '/' . $this->folder . '/' . $this->filename); + $this->rootView->chroot($root); + + $this->assertFalse($this->rootView->file_exists('/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files/' . $this->filename)); + $this->assertTrue($this->rootView->file_exists('/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files/' . $this->folder . '/' . $this->filename)); + + // keys should be renamed too + $this->assertFalse($this->rootView->file_exists( + '/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/share-keys/' + . $this->filename . '.' . self::TEST_ENCRYPTION_HOOKS_USER1 . '.shareKey')); + $this->assertFalse($this->rootView->file_exists( + '/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/keyfiles/' + . $this->filename . '.key')); + + $this->assertTrue($this->rootView->file_exists( + '/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/share-keys/' . $this->folder . '/' + . $this->filename . '.' . self::TEST_ENCRYPTION_HOOKS_USER1 . '.shareKey')); + $this->assertTrue($this->rootView->file_exists( + '/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/keyfiles/' . $this->folder . '/' + . $this->filename . '.key')); + + // cleanup + $this->rootView->unlink('/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files/' . $this->folder); + } + } diff --git a/apps/files_encryption/tests/share.php b/apps/files_encryption/tests/share.php index 46a21dd55c..be56968ac0 100755 --- a/apps/files_encryption/tests/share.php +++ b/apps/files_encryption/tests/share.php @@ -127,6 +127,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase { \OC_User::deleteUser(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER4); } + /** * @medium * @param bool $withTeardown @@ -498,6 +499,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase { } } + function testPublicShareFile() { // login as admin \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1); @@ -864,6 +866,13 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase { \OCA\Encryption\Helper::adminDisableRecovery('test123'); $this->assertEquals(0, \OC::$server->getAppConfig()->getValue('files_encryption', 'recoveryAdminEnabled')); + + //clean up, reset passwords + \OC_User::setPassword(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2, 'test123'); + $params = array('uid' => \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2, + 'password' => \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2, + 'recoveryPassword' => 'test123'); + \OCA\Encryption\Hooks::setPassphrase($params); } /** @@ -947,4 +956,65 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase { $this->view->chroot('/'); } + + /** + * @brief test moving a shared file out of the Shared folder + */ + function testRename() { + + // login as admin + \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1); + + // save file with content + $cryptedFile = file_put_contents('crypt:///' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files/' . $this->filename, $this->dataShort); + + // test that data was successfully written + $this->assertTrue(is_int($cryptedFile)); + + // get the file info from previous created file + $fileInfo = $this->view->getFileInfo( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files/' . $this->filename); + + // check if we have a valid file info + $this->assertTrue($fileInfo instanceof \OC\Files\FileInfo); + + // share the file + \OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2, OCP\PERMISSION_ALL); + + // check if share key for user2exists + $this->assertTrue($this->view->file_exists( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files_encryption/share-keys/' + . $this->filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '.shareKey')); + + + // login as user2 + \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2); + + $this->assertTrue($this->view->file_exists('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '/files/Shared/' . $this->filename)); + + // get file contents + $retrievedCryptedFile = $this->view->file_get_contents( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '/files/Shared/' . $this->filename); + + // check if data is the same as we previously written + $this->assertEquals($this->dataShort, $retrievedCryptedFile); + + // move the file out of the shared folder + $this->view->rename('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '/files/Shared/' . $this->filename, + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '/files/' . $this->filename); + + // check if we can read the moved file + $retrievedRenamedFile = $this->view->file_get_contents( + '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '/files/' . $this->filename); + + // check if data is the same as we previously written + $this->assertEquals($this->dataShort, $retrievedRenamedFile); + + // the owners file should be deleted + $this->assertFalse($this->view->file_exists('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files/' . $this->filename)); + + // cleanup + $this->view->unlink('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '/files/' . $this->filename); + } + } diff --git a/apps/files_encryption/tests/util.php b/apps/files_encryption/tests/util.php index f70e30c4d7..203ba55dbf 100755 --- a/apps/files_encryption/tests/util.php +++ b/apps/files_encryption/tests/util.php @@ -344,7 +344,7 @@ class Test_Encryption_Util extends \PHPUnit_Framework_TestCase { // check if mtime and etags unchanged $this->assertEquals($fileInfoEncrypted['mtime'], $fileInfoUnencrypted['mtime']); - $this->assertEquals($fileInfoEncrypted['etag'], $fileInfoUnencrypted['etag']); + $this->assertSame($fileInfoEncrypted['etag'], $fileInfoUnencrypted['etag']); $this->view->unlink($this->userId . '/files/' . $filename); } @@ -373,7 +373,7 @@ class Test_Encryption_Util extends \PHPUnit_Framework_TestCase { // check if mtime and etags unchanged $this->assertEquals($fileInfoEncrypted['mtime'], $fileInfoUnencrypted['mtime']); - $this->assertEquals($fileInfoEncrypted['etag'], $fileInfoUnencrypted['etag']); + $this->assertSame($fileInfoEncrypted['etag'], $fileInfoUnencrypted['etag']); // file should no longer be encrypted $this->assertEquals(0, $fileInfoUnencrypted['encrypted']); diff --git a/apps/files_external/lib/config.php b/apps/files_external/lib/config.php index b2109e5eac..43275d36c0 100755 --- a/apps/files_external/lib/config.php +++ b/apps/files_external/lib/config.php @@ -277,15 +277,21 @@ class OC_Mount_Config { $mountType, $applicable, $isPersonal = false) { + $backends = self::getBackends(); $mountPoint = OC\Files\Filesystem::normalizePath($mountPoint); if ($mountPoint === '' || $mountPoint === '/' || $mountPoint == '/Shared') { // can't mount at root or "Shared" folder return false; } + + if (!isset($backends[$class])) { + // invalid backend + return false; + } if ($isPersonal) { // Verify that the mount point applies for the current user // Prevent non-admin users from mounting local storage - if ($applicable != OCP\User::getUser() || $class == '\OC\Files\Storage\Local') { + if ($applicable !== OCP\User::getUser() || strtolower($class) === '\oc\files\storage\local') { return false; } $mountPoint = '/'.$applicable.'/files/'.ltrim($mountPoint, '/'); @@ -353,7 +359,8 @@ class OC_Mount_Config { $jsonFile = OC_User::getHome(OCP\User::getUser()).'/mount.json'; } else { $phpFile = OC::$SERVERROOT.'/config/mount.php'; - $jsonFile = \OC_Config::getValue("mount_file", \OC::$SERVERROOT . "/data/mount.json"); + $datadir = \OC_Config::getValue('datadirectory', \OC::$SERVERROOT . '/data/'); + $jsonFile = \OC_Config::getValue('mount_file', $datadir . '/mount.json'); } if (is_file($jsonFile)) { $mountPoints = json_decode(file_get_contents($jsonFile), true); @@ -379,7 +386,8 @@ class OC_Mount_Config { if ($isPersonal) { $file = OC_User::getHome(OCP\User::getUser()).'/mount.json'; } else { - $file = \OC_Config::getValue("mount_file", \OC::$SERVERROOT . "/data/mount.json"); + $datadir = \OC_Config::getValue('datadirectory', \OC::$SERVERROOT . '/data/'); + $file = \OC_Config::getValue('mount_file', $datadir . '/mount.json'); } $content = json_encode($data); @file_put_contents($file, $content); diff --git a/apps/files_external/tests/mountconfig.php b/apps/files_external/tests/mountconfig.php index 941aec680b..24ebcf5134 100644 --- a/apps/files_external/tests/mountconfig.php +++ b/apps/files_external/tests/mountconfig.php @@ -48,4 +48,29 @@ class Test_Mount_Config extends \PHPUnit_Framework_TestCase { $this->assertEquals(false, OC_Mount_Config::addMountPoint('/Shared', $storageClass, array(), $mountType, $applicable, $isPersonal)); } + + public function testAddMountPointSingleUser() { + \OC_User::setUserId('test'); + $mountType = 'user'; + $applicable = 'test'; + $isPersonal = true; + // local + $this->assertEquals(false, OC_Mount_Config::addMountPoint('/ext', '\OC\Files\storage\local', array(), $mountType, $applicable, $isPersonal)); + // non-local + // FIXME: can't test this yet as the class (write operation) is not mockable + // $this->assertEquals(true, OC_Mount_Config::addMountPoint('/ext', '\OC\Files\Storage\SFTP', array(), $mountType, $applicable, $isPersonal)); + + } + + public function testAddMountPointUnexistClass() { + \OC_User::setUserId('test'); + $storageClass = 'Unexist_Storage'; + $mountType = 'user'; + $applicable = 'test'; + $isPersonal = true; + // local + // non-local + $this->assertEquals(false, OC_Mount_Config::addMountPoint('/ext', $storageClass, array(), $mountType, $applicable, $isPersonal)); + + } } diff --git a/apps/files_sharing/lib/api.php b/apps/files_sharing/lib/api.php index 19a2d22b06..0ba58aa896 100644 --- a/apps/files_sharing/lib/api.php +++ b/apps/files_sharing/lib/api.php @@ -172,12 +172,12 @@ class Api { // workaround because folders are named 'dir' in this context $itemType = $file['type'] === 'file' ? 'file' : 'folder'; $share = \OCP\Share::getItemShared($itemType, $file['fileid']); - $receivedFrom = \OCP\Share::getItemSharedWithBySource($itemType, $file['fileid']); - if ($receivedFrom) { - $share['received_from'] = $receivedFrom['uid_owner']; - $share['received_from_displayname'] = \OCP\User::getDisplayName($receivedFrom['uid_owner']); - } - if ($share) { + if($share) { + $receivedFrom = \OCP\Share::getItemSharedWithBySource($itemType, $file['fileid']); + if ($receivedFrom) { + $share['received_from'] = $receivedFrom['uid_owner']; + $share['received_from_displayname'] = \OCP\User::getDisplayName($receivedFrom['uid_owner']); + } $result = array_merge($result, $share); } } diff --git a/apps/files_sharing/lib/cache.php b/apps/files_sharing/lib/cache.php index 27602f69ab..10f2182655 100644 --- a/apps/files_sharing/lib/cache.php +++ b/apps/files_sharing/lib/cache.php @@ -131,20 +131,16 @@ class Shared_Cache extends Cache { foreach ($files as &$file) { $file['mimetype'] = $this->getMimetype($file['mimetype']); $file['mimepart'] = $this->getMimetype($file['mimepart']); + $file['usersPath'] = 'files/Shared/' . ltrim($file['path'], '/'); } return $files; } else { - if ($cache = $this->getSourceCache($folder)) { + $cache = $this->getSourceCache($folder); + if ($cache) { $parent = $this->storage->getFile($folder); $sourceFolderContent = $cache->getFolderContents($this->files[$folder]); foreach ($sourceFolderContent as $key => $c) { - $ownerPathParts = explode('/', \OC_Filesystem::normalizePath($c['path'])); - $userPathParts = explode('/', \OC_Filesystem::normalizePath($folder)); - $usersPath = 'files/Shared/'.$userPathParts[1]; - foreach (array_slice($ownerPathParts, 3) as $part) { - $usersPath .= '/'.$part; - } - $sourceFolderContent[$key]['usersPath'] = $usersPath; + $sourceFolderContent[$key]['usersPath'] = 'files/Shared/' . $folder . '/' . $c['name']; $sourceFolderContent[$key]['uid_owner'] = $parent['uid_owner']; $sourceFolderContent[$key]['displayname_owner'] = $parent['uid_owner']; } diff --git a/apps/files_sharing/public.php b/apps/files_sharing/public.php index e7a5f5024b..fe61dd4d5a 100644 --- a/apps/files_sharing/public.php +++ b/apps/files_sharing/public.php @@ -32,7 +32,8 @@ function determineIcon($file, $sharingRoot, $sharingToken) { if($file['isPreviewAvailable']) { return OCP\publicPreview_icon($relativePath, $sharingToken) . '&c=' . $file['etag']; } - return OCP\mimetype_icon($file['mimetype']); + $icon = OCP\mimetype_icon($file['mimetype']); + return substr($icon, 0, -3) . 'svg'; } if (isset($_GET['t'])) { diff --git a/apps/files_sharing/templates/authenticate.php b/apps/files_sharing/templates/authenticate.php index 928be93fc9..19b1fb2763 100644 --- a/apps/files_sharing/templates/authenticate.php +++ b/apps/files_sharing/templates/authenticate.php @@ -8,7 +8,10 @@

- +

diff --git a/apps/files_sharing/tests/api.php b/apps/files_sharing/tests/api.php index 1278e0c4d1..0d3d9b98b2 100644 --- a/apps/files_sharing/tests/api.php +++ b/apps/files_sharing/tests/api.php @@ -33,13 +33,16 @@ class Test_Files_Sharing_Api extends Test_Files_Sharing_Base { parent::setUp(); $this->folder = '/folder_share_api_test'; + $this->subfolder = '/subfolder_share_api_test'; $this->filename = 'share-api-test.txt'; // save file with content $this->view->file_put_contents($this->filename, $this->data); $this->view->mkdir($this->folder); + $this->view->mkdir($this->folder . '/' . $this->subfolder); $this->view->file_put_contents($this->folder.'/'.$this->filename, $this->data); + $this->view->file_put_contents($this->folder.'/' . $this->subfolder . '/' .$this->filename, $this->data); } function tearDown() { @@ -286,6 +289,71 @@ class Test_Files_Sharing_Api extends Test_Files_Sharing_Base { } + /** + * @brief share a folder, than reshare a file within the shared folder and check if we construct the correct path + * @medium + */ + function testGetShareFromFolderReshares() { + + self::loginHelper(self::TEST_FILES_SHARING_API_USER1); + + $fileInfo1 = $this->view->getFileInfo($this->folder); + $fileInfo2 = $this->view->getFileInfo($this->folder.'/'.$this->filename); + $fileInfo3 = $this->view->getFileInfo($this->folder.'/' . $this->subfolder . '/' .$this->filename); + + // share root folder to user2 + $result = \OCP\Share::shareItem('folder', $fileInfo1['fileid'], \OCP\Share::SHARE_TYPE_USER, + \Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2, 31); + + // share was successful? + $this->assertTrue($result); + + // login as user2 + self::loginHelper(self::TEST_FILES_SHARING_API_USER2); + + // share file in root folder + $result = \OCP\Share::shareItem('file', $fileInfo2['fileid'], \OCP\Share::SHARE_TYPE_LINK, null, 1); + // share was successful? + $this->assertTrue(is_string($result)); + + // share file in subfolder + $result = \OCP\Share::shareItem('file', $fileInfo3['fileid'], \OCP\Share::SHARE_TYPE_LINK, null, 1); + // share was successful? + $this->assertTrue(is_string($result)); + + $testValues=array( + array('query' => 'Shared/' . $this->folder, + 'expectedResult' => '/Shared' . $this->folder . '/' . $this->filename), + array('query' => 'Shared/' . $this->folder . $this->subfolder, + 'expectedResult' => '/Shared' . $this->folder . $this->subfolder . '/' . $this->filename), + ); + foreach ($testValues as $value) { + + $_GET['path'] = $value['query']; + $_GET['subfiles'] = 'true'; + + $result = Share\Api::getAllShares(array()); + + $this->assertTrue($result->succeeded()); + + // test should return one share within $this->folder + $data = $result->getData(); + + $this->assertEquals($value['expectedResult'], $data[0]['path']); + } + + // cleanup + + \OCP\Share::unshare('file', $fileInfo2['fileid'], \OCP\Share::SHARE_TYPE_LINK, null); + \OCP\Share::unshare('file', $fileInfo3['fileid'], \OCP\Share::SHARE_TYPE_LINK, null); + + self::loginHelper(self::TEST_FILES_SHARING_API_USER1); + + \OCP\Share::unshare('folder', $fileInfo1['fileid'], \OCP\Share::SHARE_TYPE_USER, + \Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2); + + } + /** * @medium */ diff --git a/apps/files_sharing/tests/base.php b/apps/files_sharing/tests/base.php index 3e283271f5..d44972d01f 100644 --- a/apps/files_sharing/tests/base.php +++ b/apps/files_sharing/tests/base.php @@ -43,6 +43,7 @@ abstract class Test_Files_Sharing_Base extends \PHPUnit_Framework_TestCase { */ public $view; public $folder; + public $subfolder; public static function setUpBeforeClass() { // reset backend diff --git a/apps/files_sharing/tests/cache.php b/apps/files_sharing/tests/cache.php index 56a51c83f6..a75e186052 100644 --- a/apps/files_sharing/tests/cache.php +++ b/apps/files_sharing/tests/cache.php @@ -2,8 +2,9 @@ /** * ownCloud * - * @author Vincent Petry + * @author Vincent Petry, Bjoern Schiessle * @copyright 2014 Vincent Petry + * 2014 Bjoern Schiessle * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -23,13 +24,19 @@ require_once __DIR__ . '/base.php'; class Test_Files_Sharing_Cache extends Test_Files_Sharing_Base { + /** + * @var OC_FilesystemView + */ + public $user2View; + function setUp() { parent::setUp(); self::loginHelper(self::TEST_FILES_SHARING_API_USER1); + $this->user2View = new \OC\Files\View('/'. self::TEST_FILES_SHARING_API_USER2 . '/files'); + // prepare user1's dir structure - $textData = "dummy file data\n"; $this->view->mkdir('container'); $this->view->mkdir('container/shareddir'); $this->view->mkdir('container/shareddir/subdir'); @@ -115,20 +122,128 @@ class Test_Files_Sharing_Cache extends Test_Files_Sharing_Base { $this->verifyFiles($check, $results); } + function testGetFolderContentsInRoot() { + $results = $this->user2View->getDirectoryContent('/Shared/'); + + $this->verifyFiles( + array( + array( + 'name' => 'shareddir', + 'path' => '/shareddir', + 'mimetype' => 'httpd/unix-directory', + 'usersPath' => 'files/Shared/shareddir' + ), + array( + 'name' => 'shared single file.txt', + 'path' => '/shared single file.txt', + 'mimetype' => 'text/plain', + 'usersPath' => 'files/Shared/shared single file.txt' + ), + ), + $results + ); + } + + function testGetFolderContentsInSubdir() { + $results = $this->user2View->getDirectoryContent('/Shared/shareddir'); + + $this->verifyFiles( + array( + array( + 'name' => 'bar.txt', + 'path' => 'files/container/shareddir/bar.txt', + 'mimetype' => 'text/plain', + 'usersPath' => 'files/Shared/shareddir/bar.txt' + ), + array( + 'name' => 'emptydir', + 'path' => 'files/container/shareddir/emptydir', + 'mimetype' => 'httpd/unix-directory', + 'usersPath' => 'files/Shared/shareddir/emptydir' + ), + array( + 'name' => 'subdir', + 'path' => 'files/container/shareddir/subdir', + 'mimetype' => 'httpd/unix-directory', + 'usersPath' => 'files/Shared/shareddir/subdir' + ), + ), + $results + ); + } + + function testGetFolderContentsWhenSubSubdirShared() { + self::loginHelper(self::TEST_FILES_SHARING_API_USER1); + + $fileinfo = $this->view->getFileInfo('container/shareddir/subdir'); + \OCP\Share::shareItem('folder', $fileinfo['fileid'], \OCP\Share::SHARE_TYPE_USER, + self::TEST_FILES_SHARING_API_USER3, 31); + + self::loginHelper(self::TEST_FILES_SHARING_API_USER3); + + $thirdView = new \OC\Files\View('/' . self::TEST_FILES_SHARING_API_USER3 . '/files'); + $results = $thirdView->getDirectoryContent('/Shared/subdir'); + + $this->verifyFiles( + array( + array( + 'name' => 'another too.txt', + 'path' => 'files/container/shareddir/subdir/another too.txt', + 'mimetype' => 'text/plain', + 'usersPath' => 'files/Shared/subdir/another too.txt' + ), + array( + 'name' => 'another.txt', + 'path' => 'files/container/shareddir/subdir/another.txt', + 'mimetype' => 'text/plain', + 'usersPath' => 'files/Shared/subdir/another.txt' + ), + array( + 'name' => 'not a text file.xml', + 'path' => 'files/container/shareddir/subdir/not a text file.xml', + 'mimetype' => 'application/xml', + 'usersPath' => 'files/Shared/subdir/not a text file.xml' + ), + ), + $results + ); + + self::loginHelper(self::TEST_FILES_SHARING_API_USER1); + + \OCP\Share::unshare('folder', $fileinfo['fileid'], \OCP\Share::SHARE_TYPE_USER, + self::TEST_FILES_SHARING_API_USER3); + } + /** - * Checks that all provided attributes exist in the files list, - * only the values provided in $examples will be used to check against - * the file list. The files order also needs to be the same. + * Check if 'results' contains the expected 'examples' only. * * @param array $examples array of example files - * @param array $files array of files + * @param array $results array of files */ - private function verifyFiles($examples, $files) { - $this->assertEquals(count($examples), count($files)); - foreach ($files as $i => $file) { - foreach ($examples[$i] as $key => $value) { - $this->assertEquals($value, $file[$key]); + private function verifyFiles($examples, $results) { + $this->assertEquals(count($examples), count($results)); + + foreach ($examples as $example) { + foreach ($results as $key => $result) { + if ($result['name'] === $example['name']) { + $this->verifyKeys($example, $result); + unset($results[$key]); + break; + } } } + $this->assertTrue(empty($results)); } + + /** + * @brief verify if each value from the result matches the expected result + * @param array $example array with the expected results + * @param array $result array with the results + */ + private function verifyKeys($example, $result) { + foreach ($example as $key => $value) { + $this->assertEquals($value, $result[$key]); + } + } + } diff --git a/apps/files_trashbin/js/filelist.js b/apps/files_trashbin/js/filelist.js index f42abb6d02..a88459b0a9 100644 --- a/apps/files_trashbin/js/filelist.js +++ b/apps/files_trashbin/js/filelist.js @@ -1,3 +1,4 @@ +/* globals OC, FileList, t */ // override reload with own ajax call FileList.reload = function(){ FileList.showMask(); @@ -17,7 +18,36 @@ FileList.reload = function(){ FileList.reloadCallback(result); } }); -} +}; + +FileList.appName = t('files_trashbin', 'Deleted files'); + +FileList._deletedRegExp = new RegExp(/^(.+)\.d[0-9]+$/); + +/** + * Convert a file name in the format filename.d12345 to the real file name. + * This will use basename. + * The name will not be changed if it has no ".d12345" suffix. + * @param name file name + * @return converted file name + */ +FileList.getDeletedFileName = function(name) { + name = OC.basename(name); + var match = FileList._deletedRegExp.exec(name); + if (match && match.length > 1) { + name = match[1]; + } + return name; +}; +var oldSetCurrentDir = FileList.setCurrentDir; +FileList.setCurrentDir = function(targetDir) { + oldSetCurrentDir.apply(this, arguments); + + var baseDir = OC.basename(targetDir); + if (baseDir !== '') { + FileList.setPageTitle(FileList.getDeletedFileName(baseDir)); + } +}; FileList.linkTo = function(dir){ return OC.linkTo('files_trashbin', 'index.php')+"?dir="+ encodeURIComponent(dir).replace(/%2F/g, '/'); diff --git a/apps/user_ldap/group_ldap.php b/apps/user_ldap/group_ldap.php index 32e2cec596..cef9ca3c4c 100644 --- a/apps/user_ldap/group_ldap.php +++ b/apps/user_ldap/group_ldap.php @@ -61,8 +61,7 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface { return false; } //usually, LDAP attributes are said to be case insensitive. But there are exceptions of course. - $members = $this->access->readAttribute($dn_group, - $this->access->connection->ldapGroupMemberAssocAttr); + $members = array_keys($this->_groupMembers($dn_group)); if(!$members) { $this->access->connection->writeToCache('inGroup'.$uid.':'.$gid, false); return false; @@ -89,6 +88,39 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface { return $isInGroup; } + private function _groupMembers($dnGroup, &$seen = null) { + if ($seen === null) { + $seen = array(); + } + $allMembers = array(); + if (array_key_exists($dnGroup, $seen)) { + // avoid loops + return array(); + } + // used extensively in cron job, caching makes sense for nested groups + $cacheKey = '_groupMembers'.$dnGroup; + if($this->access->connection->isCached($cacheKey)) { + return $this->access->connection->getFromCache($cacheKey); + } + $seen[$dnGroup] = 1; + $members = $this->access->readAttribute($dnGroup, $this->access->connection->ldapGroupMemberAssocAttr, + $this->access->connection->ldapGroupFilter); + if (is_array($members)) { + foreach ($members as $memberDN) { + $allMembers[$memberDN] = 1; + $nestedGroups = $this->access->connection->ldapNestedGroups; + if (!empty($nestedGroups)) { + $subMembers = $this->_groupMembers($memberDN, $seen); + if ($subMembers) { + $allMembers = array_merge($allMembers, $subMembers); + } + } + } + } + $this->access->connection->writeToCache($cacheKey, $allMembers); + return $allMembers; + } + /** * @brief Get all groups a user belongs to * @param $uid Name of the user @@ -124,18 +156,45 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface { $uid = $userDN; } - $filter = $this->access->combineFilterWithAnd(array( - $this->access->connection->ldapGroupFilter, - $this->access->connection->ldapGroupMemberAssocAttr.'='.$uid - )); - $groups = $this->access->fetchListOfGroups($filter, - array($this->access->connection->ldapGroupDisplayName, 'dn')); + $groups = array_values($this->getGroupsByMember($uid)); $groups = array_unique($this->access->ownCloudGroupNames($groups), SORT_LOCALE_STRING); $this->access->connection->writeToCache($cacheKey, $groups); return $groups; } + private function getGroupsByMember($dn, &$seen = null) { + if ($seen === null) { + $seen = array(); + } + $allGroups = array(); + if (array_key_exists($dn, $seen)) { + // avoid loops + return array(); + } + $seen[$dn] = true; + $filter = $this->access->combineFilterWithAnd(array( + $this->access->connection->ldapGroupFilter, + $this->access->connection->ldapGroupMemberAssocAttr.'='.$dn + )); + $groups = $this->access->fetchListOfGroups($filter, + array($this->access->connection->ldapGroupDisplayName, 'dn')); + if (is_array($groups)) { + foreach ($groups as $groupobj) { + $groupDN = $groupobj['dn']; + $allGroups[$groupDN] = $groupobj; + $nestedGroups = $this->access->connection->ldapNestedGroups; + if (!empty($nestedGroups)) { + $supergroups = $this->getGroupsByMember($groupDN, $seen); + if (is_array($supergroups) && (count($supergroups)>0)) { + $allGroups = array_merge($allGroups, $supergroups); + } + } + } + } + return $allGroups; + } + /** * @brief get a list of all users in a group * @returns array with user ids @@ -172,8 +231,7 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface { return array(); } - $members = $this->access->readAttribute($groupDN, - $this->access->connection->ldapGroupMemberAssocAttr); + $members = array_keys($this->_groupMembers($groupDN)); if(!$members) { //in case users could not be retrieved, return empty resultset $this->access->connection->writeToCache($cachekey, array()); diff --git a/apps/user_ldap/lib/configuration.php b/apps/user_ldap/lib/configuration.php index 954d0501fa..612a623e91 100644 --- a/apps/user_ldap/lib/configuration.php +++ b/apps/user_ldap/lib/configuration.php @@ -76,6 +76,7 @@ class Configuration { 'ldapExpertUUIDUserAttr' => null, 'ldapExpertUUIDGroupAttr' => null, 'lastJpegPhotoLookup' => null, + 'ldapNestedGroups' => false, ); /** @@ -342,6 +343,7 @@ class Configuration { 'ldap_expert_uuid_group_attr' => '', 'has_memberof_filter_support' => 0, 'last_jpegPhoto_lookup' => 0, + 'ldap_nested_groups' => 0, ); } @@ -393,6 +395,7 @@ class Configuration { 'ldap_expert_uuid_group_attr' => 'ldapExpertUUIDGroupAttr', 'has_memberof_filter_support' => 'hasMemberOfFilterSupport', 'last_jpegPhoto_lookup' => 'lastJpegPhotoLookup', + 'ldap_nested_groups' => 'ldapNestedGroups', ); return $array; } diff --git a/apps/user_ldap/templates/settings.php b/apps/user_ldap/templates/settings.php index 3ccc7a860f..79c4ae224c 100644 --- a/apps/user_ldap/templates/settings.php +++ b/apps/user_ldap/templates/settings.php @@ -36,6 +36,7 @@

+

t('Special Attributes'));?>

diff --git a/apps/user_ldap/tests/access.php b/apps/user_ldap/tests/access.php new file mode 100644 index 0000000000..9beb2b9733 --- /dev/null +++ b/apps/user_ldap/tests/access.php @@ -0,0 +1,71 @@ +. +* +*/ + +namespace OCA\user_ldap\tests; + +use \OCA\user_ldap\lib\Access; +use \OCA\user_ldap\lib\Connection; +use \OCA\user_ldap\lib\ILDAPWrapper; + +class Test_Access extends \PHPUnit_Framework_TestCase { + private function getConnecterAndLdapMock() { + static $conMethods; + static $accMethods; + + if(is_null($conMethods) || is_null($accMethods)) { + $conMethods = get_class_methods('\OCA\user_ldap\lib\Connection'); + $accMethods = get_class_methods('\OCA\user_ldap\lib\Access'); + } + $lw = $this->getMock('\OCA\user_ldap\lib\ILDAPWrapper'); + $connector = $this->getMock('\OCA\user_ldap\lib\Connection', + $conMethods, + array($lw, null, null)); + + return array($lw, $connector); + } + + public function testEscapeFilterPartValidChars() { + list($lw, $con) = $this->getConnecterAndLdapMock(); + $access = new Access($con, $lw); + + $input = 'okay'; + $this->assertTrue($input === $access->escapeFilterPart($input)); + } + + public function testEscapeFilterPartEscapeWildcard() { + list($lw, $con) = $this->getConnecterAndLdapMock(); + $access = new Access($con, $lw); + + $input = '*'; + $expected = '\\\\*'; + $this->assertTrue($expected === $access->escapeFilterPart($input)); + } + + public function testEscapeFilterPartEscapeWildcard2() { + list($lw, $con) = $this->getConnecterAndLdapMock(); + $access = new Access($con, $lw); + + $input = 'foo*bar'; + $expected = 'foo\\\\*bar'; + $this->assertTrue($expected === $access->escapeFilterPart($input)); + } +} \ No newline at end of file diff --git a/apps/user_ldap/tests/user_ldap.php b/apps/user_ldap/tests/user_ldap.php index 9193a005ae..8c8d85b3c3 100644 --- a/apps/user_ldap/tests/user_ldap.php +++ b/apps/user_ldap/tests/user_ldap.php @@ -83,6 +83,12 @@ class Test_User_Ldap_Direct extends \PHPUnit_Framework_TestCase { * @return void */ private function prepareAccessForCheckPassword(&$access) { + $access->expects($this->once()) + ->method('escapeFilterPart') + ->will($this->returnCallback(function($uid) { + return $uid; + })); + $access->connection->expects($this->any()) ->method('__get') ->will($this->returnCallback(function($name) { @@ -116,17 +122,34 @@ class Test_User_Ldap_Direct extends \PHPUnit_Framework_TestCase { })); } - public function testCheckPassword() { + public function testCheckPasswordUidReturn() { $access = $this->getAccessMock(); + $this->prepareAccessForCheckPassword($access); $backend = new UserLDAP($access); \OC_User::useBackend($backend); $result = $backend->checkPassword('roland', 'dt19'); $this->assertEquals('gunslinger', $result); + } + + public function testCheckPasswordWrongPassword() { + $access = $this->getAccessMock(); + + $this->prepareAccessForCheckPassword($access); + $backend = new UserLDAP($access); + \OC_User::useBackend($backend); $result = $backend->checkPassword('roland', 'wrong'); $this->assertFalse($result); + } + + public function testCheckPasswordWrongUser() { + $access = $this->getAccessMock(); + + $this->prepareAccessForCheckPassword($access); + $backend = new UserLDAP($access); + \OC_User::useBackend($backend); $result = $backend->checkPassword('mallory', 'evil'); $this->assertFalse($result); @@ -140,9 +163,23 @@ class Test_User_Ldap_Direct extends \PHPUnit_Framework_TestCase { $result = \OCP\User::checkPassword('roland', 'dt19'); $this->assertEquals('gunslinger', $result); + } + + public function testCheckPasswordPublicAPIWrongPassword() { + $access = $this->getAccessMock(); + $this->prepareAccessForCheckPassword($access); + $backend = new UserLDAP($access); + \OC_User::useBackend($backend); $result = \OCP\User::checkPassword('roland', 'wrong'); $this->assertFalse($result); + } + + public function testCheckPasswordPublicAPIWrongUser() { + $access = $this->getAccessMock(); + $this->prepareAccessForCheckPassword($access); + $backend = new UserLDAP($access); + \OC_User::useBackend($backend); $result = \OCP\User::checkPassword('mallory', 'evil'); $this->assertFalse($result); @@ -154,6 +191,12 @@ class Test_User_Ldap_Direct extends \PHPUnit_Framework_TestCase { * @return void */ private function prepareAccessForGetUsers(&$access) { + $access->expects($this->once()) + ->method('escapeFilterPart') + ->will($this->returnCallback(function($search) { + return $search; + })); + $access->expects($this->any()) ->method('getFilterPartForUserSearch') ->will($this->returnCallback(function($search) { @@ -191,28 +234,52 @@ class Test_User_Ldap_Direct extends \PHPUnit_Framework_TestCase { ->will($this->returnArgument(0)); } - public function testGetUsers() { + public function testGetUsersNoParam() { $access = $this->getAccessMock(); $this->prepareAccessForGetUsers($access); $backend = new UserLDAP($access); $result = $backend->getUsers(); $this->assertEquals(3, count($result)); + } + + public function testGetUsersLimitOffset() { + $access = $this->getAccessMock(); + $this->prepareAccessForGetUsers($access); + $backend = new UserLDAP($access); $result = $backend->getUsers('', 1, 2); $this->assertEquals(1, count($result)); + } + + public function testGetUsersLimitOffset2() { + $access = $this->getAccessMock(); + $this->prepareAccessForGetUsers($access); + $backend = new UserLDAP($access); $result = $backend->getUsers('', 2, 1); $this->assertEquals(2, count($result)); + } + + public function testGetUsersSearchWithResult() { + $access = $this->getAccessMock(); + $this->prepareAccessForGetUsers($access); + $backend = new UserLDAP($access); $result = $backend->getUsers('yo'); $this->assertEquals(2, count($result)); + } + + public function testGetUsersSearchEmptyResult() { + $access = $this->getAccessMock(); + $this->prepareAccessForGetUsers($access); + $backend = new UserLDAP($access); $result = $backend->getUsers('nix'); $this->assertEquals(0, count($result)); } - public function testGetUsersViaAPI() { + public function testGetUsersViaAPINoParam() { $access = $this->getAccessMock(); $this->prepareAccessForGetUsers($access); $backend = new UserLDAP($access); @@ -220,15 +287,43 @@ class Test_User_Ldap_Direct extends \PHPUnit_Framework_TestCase { $result = \OCP\User::getUsers(); $this->assertEquals(3, count($result)); + } + + public function testGetUsersViaAPILimitOffset() { + $access = $this->getAccessMock(); + $this->prepareAccessForGetUsers($access); + $backend = new UserLDAP($access); + \OC_User::useBackend($backend); $result = \OCP\User::getUsers('', 1, 2); $this->assertEquals(1, count($result)); + } + + public function testGetUsersViaAPILimitOffset2() { + $access = $this->getAccessMock(); + $this->prepareAccessForGetUsers($access); + $backend = new UserLDAP($access); + \OC_User::useBackend($backend); $result = \OCP\User::getUsers('', 2, 1); $this->assertEquals(2, count($result)); + } + + public function testGetUsersViaAPISearchWithResult() { + $access = $this->getAccessMock(); + $this->prepareAccessForGetUsers($access); + $backend = new UserLDAP($access); + \OC_User::useBackend($backend); $result = \OCP\User::getUsers('yo'); $this->assertEquals(2, count($result)); + } + + public function testGetUsersViaAPISearchEmptyResult() { + $access = $this->getAccessMock(); + $this->prepareAccessForGetUsers($access); + $backend = new UserLDAP($access); + \OC_User::useBackend($backend); $result = \OCP\User::getUsers('nix'); $this->assertEquals(0, count($result)); diff --git a/config/config.sample.php b/config/config.sample.php index 9f47ee3294..9c5eca8a5e 100755 --- a/config/config.sample.php +++ b/config/config.sample.php @@ -53,6 +53,9 @@ $CONFIG = array( /* The optional authentication for the proxy to use to connect to the internet. The format is: [username]:[password] */ "proxyuserpwd" => "", +/* List of trusted domains, to prevent host header poisoning ownCloud is only using these Host headers */ +'trusted_domains' => array('demo.owncloud.org'), + /* Theme to use for ownCloud */ "theme" => "", @@ -264,6 +267,9 @@ $CONFIG = array( /* whether usage of the instance should be restricted to admin users only */ 'singleuser' => false, +/* 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' => '', ); diff --git a/core/ajax/preview.php b/core/ajax/preview.php index a1267d6f5c..526719e8a1 100644 --- a/core/ajax/preview.php +++ b/core/ajax/preview.php @@ -7,34 +7,39 @@ */ \OC_Util::checkLoggedIn(); -$file = array_key_exists('file', $_GET) ? (string) $_GET['file'] : ''; -$maxX = array_key_exists('x', $_GET) ? (int) $_GET['x'] : '36'; -$maxY = array_key_exists('y', $_GET) ? (int) $_GET['y'] : '36'; -$scalingUp = array_key_exists('scalingup', $_GET) ? (bool) $_GET['scalingup'] : true; +$file = array_key_exists('file', $_GET) ? (string)$_GET['file'] : ''; +$maxX = array_key_exists('x', $_GET) ? (int)$_GET['x'] : '36'; +$maxY = array_key_exists('y', $_GET) ? (int)$_GET['y'] : '36'; +$scalingUp = array_key_exists('scalingup', $_GET) ? (bool)$_GET['scalingup'] : true; +$always = array_key_exists('forceIcon', $_GET) ? (bool)$_GET['forceIcon'] : true; -if($file === '') { +if ($file === '') { //400 Bad Request \OC_Response::setStatus(400); \OC_Log::write('core-preview', 'No file parameter was passed', \OC_Log::DEBUG); exit; } -if($maxX === 0 || $maxY === 0) { +if ($maxX === 0 || $maxY === 0) { //400 Bad Request \OC_Response::setStatus(400); \OC_Log::write('core-preview', 'x and/or y set to 0', \OC_Log::DEBUG); exit; } -try{ +try { $preview = new \OC\Preview(\OC_User::getUser(), 'files'); - $preview->setFile($file); - $preview->setMaxX($maxX); - $preview->setMaxY($maxY); - $preview->setScalingUp($scalingUp); + if (!$always and !$preview->isMimeSupported(\OC\Files\Filesystem::getMimeType($file))) { + \OC_Response::setStatus(404); + } else { + $preview->setFile($file); + $preview->setMaxX($maxX); + $preview->setMaxY($maxY); + $preview->setScalingUp($scalingUp); + } $preview->show(); -}catch(\Exception $e) { +} catch (\Exception $e) { \OC_Response::setStatus(500); \OC_Log::write('core', $e->getmessage(), \OC_Log::DEBUG); -} \ No newline at end of file +} diff --git a/core/css/icons.css b/core/css/icons.css index 2dc3508412..027a3f8557 100644 --- a/core/css/icons.css +++ b/core/css/icons.css @@ -1,4 +1,4 @@ -.icon { +[class^="icon-"], [class*=" icon-"] { background-repeat: no-repeat; background-position: center; } @@ -66,7 +66,8 @@ .icon-delete { background-image: url('../img/actions/delete.svg'); } -.icon-delete-hover { +.icon-delete:hover, +.icon-delete:focus { background-image: url('../img/actions/delete-hover.svg'); } @@ -226,6 +227,12 @@ .icon-folder { background-image: url('../img/places/folder.svg'); } +.icon-filetype-text { + background-image: url('../img/filetypes/text.svg'); +} +.icon-filetype-folder { + background-image: url('../img/filetypes/folder.svg'); +} .icon-home { background-image: url('../img/places/home.svg'); diff --git a/core/css/mobile.css b/core/css/mobile.css new file mode 100644 index 0000000000..a63aa902d3 --- /dev/null +++ b/core/css/mobile.css @@ -0,0 +1,22 @@ +@media only screen and (max-width: 600px) { + +/* compress search box on mobile, expand when focused */ +.searchbox input[type="search"] { + width: 15%; + -webkit-transition: width 100ms; + -moz-transition: width 100ms; + -o-transition: width 100ms; + transition: width 100ms; +} +.searchbox input[type="search"]:focus, +.searchbox input[type="search"]:active { + width: 155px; +} + +/* do not show display name on mobile when profile picture is present */ +#header .avatardiv.avatardiv-shown + #expandDisplayName { + display: none; +} + + +} diff --git a/core/css/multiselect.css b/core/css/multiselect.css index 60f2f47e69..8d949e7cdb 100644 --- a/core/css/multiselect.css +++ b/core/css/multiselect.css @@ -48,7 +48,7 @@ ul.multiselectoptions > li input[type='checkbox']:checked+label { font-weight: bold; } -div.multiselect { +div.multiselect, select.multiselect { display: inline-block; max-width: 400px; min-width: 150px; @@ -58,6 +58,12 @@ div.multiselect { vertical-align: bottom; } +/* To make a select look like a multiselect until it's initialized */ +select.multiselect { + height: 30px; + min-width: 113px; +} + div.multiselect.active { background-color: #fff; position: relative; diff --git a/core/css/styles.css b/core/css/styles.css index 01c2f89b9c..082d2c714c 100644 --- a/core/css/styles.css +++ b/core/css/styles.css @@ -37,11 +37,12 @@ body { background:#fefefe; font:normal .8em/1.6em "Helvetica Neue",Helvetica,Ari .header-right { float:right; vertical-align:middle; padding:0.5em; } .header-right > * { vertical-align:middle; } +/* Profile picture in header */ #header .avatardiv { float: left; display: inline-block; + margin-right: 5px; } - #header .avatardiv img { opacity: 1; } @@ -218,17 +219,19 @@ textarea:disabled { color: #bbb; } - +/* Searchbox */ .searchbox input[type="search"] { + position: relative; font-size: 1.2em; - padding: .2em .5em .2em 1.5em; + padding-left: 1.5em; background: #fff url('../img/actions/search.svg') no-repeat .5em center; border: 0; - border-radius: 1em; + border-radius: 2em; -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=70)"; filter:alpha(opacity=70); opacity: .7; - margin-top: 10px; + margin-top: 6px; float: right; } + input[type="submit"].enabled { background: #66f866; border: 1px solid #5e5; @@ -402,11 +405,9 @@ input[name="adminpass-clone"] { padding-left:1.8em; width:11.7em !important; } /* General new input field look */ #body-login input[type="text"], #body-login input[type="password"], -#body-login input[type="email"] { - border: 1px solid #323233; - border-radius: 5px; -} -#body-login input[type='submit'] { +#body-login input[type="email"], +#body-login input[type="submit"] { + border: none; border-radius: 5px; } @@ -719,12 +720,11 @@ label.infield { cursor:text !important; top:1.05em; left:.85em; } /* USER MENU */ #settings { float: right; - margin-top: 7px; - margin-left: 10px; color: #bbb; } #expand { - padding: 15px 15px 15px 5px; + display: block; + padding: 7px 12px 6px 7px; cursor: pointer; font-weight: bold; } @@ -946,3 +946,20 @@ div.crumb:active { opacity:.7; } +.appear { + opacity: 1; + transition: opacity 500ms ease 0s; + -moz-transition: opacity 500ms ease 0s; + -ms-transition: opacity 500ms ease 0s; + -o-transition: opacity 500ms ease 0s; + -webkit-transition: opacity 500ms ease 0s; +} +.appear.transparent { + opacity: 0; +} + +/* for IE10 */ +@-ms-viewport { + width: device-width; +} + diff --git a/core/js/avatar.js b/core/js/avatar.js index c54c406876..67d6b9b7b9 100644 --- a/core/js/avatar.js +++ b/core/js/avatar.js @@ -1,6 +1,13 @@ $(document).ready(function(){ if (OC.currentUser) { - $('#header .avatardiv').avatar(OC.currentUser, 32, undefined, true); + var callback = function() { + // do not show display name on mobile when profile picture is present + if($('#header .avatardiv').children().length > 0) { + $('#header .avatardiv').addClass('avatardiv-shown'); + } + }; + + $('#header .avatardiv').avatar(OC.currentUser, 32, undefined, true, callback); // Personal settings $('#avatar .avatardiv').avatar(OC.currentUser, 128); } diff --git a/core/js/config.php b/core/js/config.php index b6875fb73f..7e23f3e2e4 100644 --- a/core/js/config.php +++ b/core/js/config.php @@ -10,12 +10,15 @@ header("Content-type: text/javascript"); // Disallow caching -header("Cache-Control: no-cache, must-revalidate"); -header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); +header("Cache-Control: no-cache, must-revalidate"); +header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Enable l10n support $l = OC_L10N::get('core'); +// Enable OC_Defaults support +$defaults = new OC_Defaults(); + // Get the config $apps_paths = array(); foreach(OC_App::getEnabledApps() as $app) { @@ -24,6 +27,7 @@ foreach(OC_App::getEnabledApps() as $app) { $array = array( "oc_debug" => (defined('DEBUG') && DEBUG) ? 'true' : 'false', + "oc_isadmin" => OC_User::isAdminUser(OC_User::getUser()) ? 'true' : 'false', "oc_webroot" => "\"".OC::$WEBROOT."\"", "oc_appswebroots" => str_replace('\\/', '/', json_encode($apps_paths)), // Ugly unescape slashes waiting for better solution "datepickerFormatDate" => json_encode($l->l('jsdate', 'jsdate')), @@ -57,11 +61,30 @@ $array = array( "firstDay" => json_encode($l->l('firstday', 'firstday')) , "oc_config" => json_encode( array( - 'session_lifetime' => \OCP\Config::getSystemValue('session_lifetime', ini_get('session.gc_maxlifetime')), - 'session_keepalive' => \OCP\Config::getSystemValue('session_keepalive', true) + 'session_lifetime' => \OCP\Config::getSystemValue('session_lifetime', ini_get('session.gc_maxlifetime')), + 'session_keepalive' => \OCP\Config::getSystemValue('session_keepalive', true), + 'version' => implode('.', OC_Util::getVersion()), + 'versionstring' => OC_Util::getVersionString(), + ) + ), + "oc_defaults" => json_encode( + array( + 'entity' => $defaults->getEntity(), + 'name' => $defaults->getName(), + 'title' => $defaults->getTitle(), + 'baseUrl' => $defaults->getBaseUrl(), + 'syncClientUrl' => $defaults->getSyncClientUrl(), + 'docBaseUrl' => $defaults->getDocBaseUrl(), + 'slogan' => $defaults->getSlogan(), + 'logoClaim' => $defaults->getLogoClaim(), + 'shortFooter' => $defaults->getShortFooter(), + 'longFooter' => $defaults->getLongFooter() ) ) - ); +); + +// Allow hooks to modify the output values +OC_Hook::emit('\OCP\Config', 'js', array('array' => &$array)); // Echo it foreach ($array as $setting => $value) { diff --git a/core/js/jquery.avatar.js b/core/js/jquery.avatar.js index 6012eccfad..02a40c088b 100644 --- a/core/js/jquery.avatar.js +++ b/core/js/jquery.avatar.js @@ -39,10 +39,15 @@ * This will behave like the first example, but it will hide the avatardiv, if * it will display the default placeholder. undefined is the ie8fix from * example 4 and can be either true, or false/undefined, to be ignored. + * + * 6. $('.avatardiv').avatar('jdoe', 128, undefined, true, callback); + * This will behave like the above example, but it will call the function + * defined in callback after the avatar is placed into the DOM. + * */ (function ($) { - $.fn.avatar = function(user, size, ie8fix, hidedefault) { + $.fn.avatar = function(user, size, ie8fix, hidedefault, callback) { if (typeof(size) === 'undefined') { if (this.height() > 0) { size = this.height(); @@ -91,6 +96,9 @@ $div.html(''); } } + if(typeof callback === 'function') { + callback(); + } }); }); }; diff --git a/core/js/js.js b/core/js/js.js index d4d2583f1e..21ccee0f1d 100644 --- a/core/js/js.js +++ b/core/js/js.js @@ -467,6 +467,34 @@ OC.search.lastResults={}; OC.addStyle.loaded=[]; OC.addScript.loaded=[]; +OC.msg={ + startSaving:function(selector){ + OC.msg.startAction(selector, t('core', 'Saving...')); + }, + finishedSaving:function(selector, data){ + OC.msg.finishedAction(selector, data); + }, + startAction:function(selector, message){ + $(selector) + .html( message ) + .removeClass('success') + .removeClass('error') + .stop(true, true) + .show(); + }, + finishedAction:function(selector, data){ + if( data.status === "success" ){ + $(selector).html( data.data.message ) + .addClass('success') + .stop(true, true) + .delay(3000) + .fadeOut(900); + }else{ + $(selector).html( data.data.message ).addClass('error'); + } + } +}; + OC.Notification={ queuedNotifications: [], getDefaultNotificationFunction: null, @@ -860,6 +888,7 @@ function initCore() { // checkShowCredentials(); // $('input#user, input#password').keyup(checkShowCredentials); + // user menu $('#settings #expand').keydown(function(event) { if (event.which === 13 || event.which === 32) { $('#expand').click() @@ -872,7 +901,8 @@ function initCore() { $('#settings #expanddiv').click(function(event){ event.stopPropagation(); }); - $(document).click(function(){//hide the settings menu when clicking outside it + //hide the user menu when clicking outside it + $(document).click(function(){ $('#settings #expanddiv').slideUp(200); }); @@ -987,6 +1017,17 @@ OC.set=function(name, value) { context[tail]=value; }; +// fix device width on windows phone +(function() { + if ("-ms-user-select" in document.documentElement.style && navigator.userAgent.match(/IEMobile\/10\.0/)) { + var msViewportStyle = document.createElement("style"); + msViewportStyle.appendChild( + document.createTextNode("@-ms-viewport{width:auto!important}") + ); + document.getElementsByTagName("head")[0].appendChild(msViewportStyle); + } +})(); + /** * select a range in an input field * @link http://stackoverflow.com/questions/499126/jquery-set-cursor-position-in-text-area diff --git a/core/js/oc-dialogs.js b/core/js/oc-dialogs.js index f4e3ec0144..d1bcb4659b 100644 --- a/core/js/oc-dialogs.js +++ b/core/js/oc-dialogs.js @@ -293,7 +293,7 @@ var OCdialogs = { conflict.find('.replacement .size').text(humanFileSize(replacement.size)); conflict.find('.replacement .mtime').text(formatDate(replacement.lastModifiedDate)); } - var path = getPathForPreview(original.name); + var path = original.directory + '/' +original.name; Files.lazyLoadPreview(path, original.mime, function(previewpath){ conflict.find('.original .icon').css('background-image','url('+previewpath+')'); }, 96, 96, original.etag); diff --git a/core/js/tags.js b/core/js/tags.js index 16dd3d4bf9..bc6d7b4e07 100644 --- a/core/js/tags.js +++ b/core/js/tags.js @@ -25,11 +25,11 @@ OC.Tags= { }); self.deleteButton = { text: t('core', 'Delete'), - click: function() {self._deleteTags(self, type, self._selectedIds())}, + click: function() {self._deleteTags(self, type, self._selectedIds())} }; self.addButton = { text: t('core', 'Add'), - click: function() {self._addTag(self, type, self.$taginput.val())}, + click: function() {self._addTag(self, type, self.$taginput.val())} }; self._fillTagList(type, self.$taglist); @@ -349,5 +349,5 @@ OC.Tags= { console.warn(response); }); } -} +}; diff --git a/core/js/tests/specHelper.js b/core/js/tests/specHelper.js index 1848d08354..b119324058 100644 --- a/core/js/tests/specHelper.js +++ b/core/js/tests/specHelper.js @@ -63,6 +63,7 @@ window.oc_config = { session_lifetime: 600 * 1000, session_keepalive: false }; +window.oc_defaults = {}; // global setup for all tests (function setupTests() { diff --git a/core/lostpassword/controller.php b/core/lostpassword/controller.php index fd20c6ba24..c858696885 100644 --- a/core/lostpassword/controller.php +++ b/core/lostpassword/controller.php @@ -69,7 +69,7 @@ class Controller { $defaults = new \OC_Defaults(); \OC_Mail::send($email, $_POST['user'], $l->t('%s password reset', array($defaults->getName())), $msg, $from, $defaults->getName()); } catch (Exception $e) { - \OC_Template::printErrorPage( 'A problem occurs during sending the e-mail please contact your administrator.'); + \OC_Template::printErrorPage( $l->t('A problem has occurred whilst sending the email, please contact your administrator.') ); } self::displayLostPasswordPage(false, true); } else { diff --git a/core/minimizer.php b/core/minimizer.php deleted file mode 100644 index eeeddf86a8..0000000000 --- a/core/minimizer.php +++ /dev/null @@ -1,15 +0,0 @@ -output($files, $service); -} -else if ($service == 'core.js') { - $minimizer = new OC_Minimizer_JS(); - $files = OC_TemplateLayout::findJavascriptFiles(OC_Util::$coreScripts); - $minimizer->output($files, $service); -} diff --git a/core/routes.php b/core/routes.php index f8454877e0..aea788bdc6 100644 --- a/core/routes.php +++ b/core/routes.php @@ -100,9 +100,6 @@ $this->create('core_avatar_post_cropped', '/avatar/cropped') ->action('OC\Core\Avatar\Controller', 'postCroppedAvatar'); // Not specifically routed -$this->create('app_css', '/apps/{app}/{file}') - ->requirements(array('file' => '.*.css')) - ->action('OC', 'loadCSSFile'); $this->create('app_index_script', '/apps/{app}/') ->defaults(array('file' => 'index.php')) //->requirements(array('file' => '.*.php')) diff --git a/core/templates/installation.php b/core/templates/installation.php index d3adb34f41..d9f3c38ab1 100644 --- a/core/templates/installation.php +++ b/core/templates/installation.php @@ -67,7 +67,7 @@ 0): ?>
- t( 'Advanced' )); ?> + t( 'Storage & database' )); ?>
diff --git a/core/templates/layout.user.php b/core/templates/layout.user.php index 44413f5a2a..3d89750348 100644 --- a/core/templates/layout.user.php +++ b/core/templates/layout.user.php @@ -15,7 +15,7 @@ - + @@ -51,12 +51,12 @@
+ +
+
- -
-
    diff --git a/lib/base.php b/lib/base.php index a5f064bdb4..49cbb1279d 100644 --- a/lib/base.php +++ b/lib/base.php @@ -284,10 +284,6 @@ class OC { if (self::needUpgrade()) { if ($showTemplate && !OC_Config::getValue('maintenance', false)) { OC_Config::setValue('theme', ''); - $minimizerCSS = new OC_Minimizer_CSS(); - $minimizerCSS->clearCache(); - $minimizerJS = new OC_Minimizer_JS(); - $minimizerJS->clearCache(); OC_Util::addScript('config'); // needed for web root OC_Util::addScript('update'); $tmpl = new OC_Template('', 'update.admin', 'guest'); @@ -332,6 +328,7 @@ class OC { } OC_Util::addStyle("styles"); + OC_Util::addStyle("mobile"); OC_Util::addStyle("icons"); OC_Util::addStyle("apps"); OC_Util::addStyle("fixes"); @@ -724,11 +721,6 @@ class OC { $app = OC::$REQUESTEDAPP; $file = OC::$REQUESTEDFILE; $param = array('app' => $app, 'file' => $file); - // Handle app css files - if (substr($file, -3) == 'css') { - self::loadCSSFile($param); - return; - } // Handle redirect URL for logged in users if (isset($_REQUEST['redirect_url']) && OC_User::isLoggedIn()) { @@ -760,7 +752,8 @@ class OC { OC_Preferences::deleteKey(OC_User::getUser(), 'login_token', $_COOKIE['oc_token']); } OC_User::logout(); - header("Location: " . OC::$WEBROOT . '/'); + // redirect to webroot and add slash if webroot is empty + header("Location: " . OC::$WEBROOT.(empty(OC::$WEBROOT) ? '/' : '')); } else { if (is_null($file)) { $param['file'] = 'index.php'; @@ -795,19 +788,6 @@ class OC { return false; } - public static function loadCSSFile($param) { - $app = $param['app']; - $file = $param['file']; - $app_path = OC_App::getAppPath($app); - if (file_exists($app_path . '/' . $file)) { - $app_web_path = OC_App::getAppWebPath($app); - $filepath = $app_web_path . '/' . $file; - $minimizer = new OC_Minimizer_CSS(); - $info = array($app_path, $app_web_path, $file); - $minimizer->output(array($info), $filepath); - } - } - protected static function handleLogin() { OC_App::loadApps(array('prelogin')); $error = array(); diff --git a/lib/private/app.php b/lib/private/app.php index 47f983cce3..048d4d4aeb 100644 --- a/lib/private/app.php +++ b/lib/private/app.php @@ -69,17 +69,6 @@ class OC_App{ } ob_end_clean(); - if (!defined('DEBUG') || !DEBUG) { - if (is_null($types) - && empty(OC_Util::$coreScripts) - && empty(OC_Util::$coreStyles)) { - OC_Util::$coreScripts = OC_Util::$scripts; - OC_Util::$scripts = array(); - OC_Util::$coreStyles = OC_Util::$styles; - OC_Util::$styles = array(); - } - } - // return return true; } diff --git a/lib/private/db/mdb2schemamanager.php b/lib/private/db/mdb2schemamanager.php index c050d47b49..aaf2ea543b 100644 --- a/lib/private/db/mdb2schemamanager.php +++ b/lib/private/db/mdb2schemamanager.php @@ -82,6 +82,9 @@ class MDB2SchemaManager { $platform = $this->conn->getDatabasePlatform(); foreach($schemaDiff->changedTables as $tableDiff) { $tableDiff->name = $platform->quoteIdentifier($tableDiff->name); + foreach($tableDiff->changedColumns as $column) { + $column->oldColumnName = $platform->quoteIdentifier($column->oldColumnName); + } } if ($generateSql) { diff --git a/lib/private/defaults.php b/lib/private/defaults.php index 0b97497baa..59630cda5c 100644 --- a/lib/private/defaults.php +++ b/lib/private/defaults.php @@ -174,4 +174,11 @@ class OC_Defaults { return $footer; } + public function buildDocLinkToKey($key) { + if ($this->themeExist('buildDocLinkToKey')) { + return $this->theme->buildDocLinkToKey($key); + } + return $this->getDocBaseUrl() . '/server/6.0/go.php?to=' . $key; + } + } diff --git a/lib/private/files.php b/lib/private/files.php index 656d6f044c..7e7a27f48d 100644 --- a/lib/private/files.php +++ b/lib/private/files.php @@ -21,22 +21,39 @@ * */ +// TODO: get rid of this using proper composer packages +require_once 'mcnetic/phpzipstreamer/ZipStreamer.php'; + +class GET_TYPE { + const FILE = 1; + const ZIP_FILES = 2; + const ZIP_DIR = 3; +} + /** - * Class for fileserver access + * Class for file server access * */ class OC_Files { - static $tmpFiles = array(); - - static public function getFileInfo($path, $includeMountPoints = true){ - return \OC\Files\Filesystem::getFileInfo($path, $includeMountPoints); - } /** - * @param string $path + * @param string $filename + * @param string $name + * @param bool $zip */ - static public function getDirectoryContent($path){ - return \OC\Files\Filesystem::getDirectoryContent($path); + private static function sendHeaders($filename, $name, $zip = false) { + OC_Response::setContentDispositionHeader($name, 'attachment'); + header('Content-Transfer-Encoding: binary'); + OC_Response::disableCaching(); + if ($zip) { + header('Content-Type: application/zip'); + } else { + $filesize = \OC\Files\Filesystem::filesize($filename); + header('Content-Type: '.\OC\Files\Filesystem::getMimeType($filename)); + if ($filesize > -1) { + header("Content-Length: ".$filesize); + } + } } /** @@ -54,97 +71,50 @@ class OC_Files { $xsendfile = true; } - if (is_array($files) && count($files) == 1) { + if (is_array($files) && count($files) === 1) { $files = $files[0]; } if (is_array($files)) { - self::validateZipDownload($dir, $files); - $executionTime = intval(ini_get('max_execution_time')); - set_time_limit(0); - $zip = new ZipArchive(); - $filename = OC_Helper::tmpFile('.zip'); - if ($zip->open($filename, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE)!==true) { - $l = OC_L10N::get('lib'); - throw new Exception($l->t('cannot open "%s"', array($filename))); - } - foreach ($files as $file) { - $file = $dir . '/' . $file; - if (\OC\Files\Filesystem::is_file($file)) { - $tmpFile = \OC\Files\Filesystem::toTmpFile($file); - self::$tmpFiles[] = $tmpFile; - $zip->addFile($tmpFile, basename($file)); - } elseif (\OC\Files\Filesystem::is_dir($file)) { - self::zipAddDir($file, $zip); - } - } - $zip->close(); - if ($xsendfile) { - $filename = OC_Helper::moveToNoClean($filename); - } + $get_type = GET_TYPE::ZIP_FILES; $basename = basename($dir); if ($basename) { $name = $basename . '.zip'; } else { $name = 'download.zip'; } - - set_time_limit($executionTime); - } elseif (\OC\Files\Filesystem::is_dir($dir . '/' . $files)) { - self::validateZipDownload($dir, $files); - $executionTime = intval(ini_get('max_execution_time')); - set_time_limit(0); - $zip = new ZipArchive(); - $filename = OC_Helper::tmpFile('.zip'); - if ($zip->open($filename, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE)!==true) { - $l = OC_L10N::get('lib'); - throw new Exception($l->t('cannot open "%s"', array($filename))); - } - $file = $dir . '/' . $files; - self::zipAddDir($file, $zip); - $zip->close(); - if ($xsendfile) { - $filename = OC_Helper::moveToNoClean($filename); - } - // downloading root ? - if ($files === '') { - $name = 'download.zip'; - } else { - $name = $files . '.zip'; - } - set_time_limit($executionTime); + + $filename = $dir . '/' . $name; } else { - $zip = false; $filename = $dir . '/' . $files; - $name = $files; + if (\OC\Files\Filesystem::is_dir($dir . '/' . $files)) { + $get_type = GET_TYPE::ZIP_DIR; + // downloading root ? + if ($files === '') { + $name = 'download.zip'; + } else { + $name = $files . '.zip'; + } + + } else { + $get_type = GET_TYPE::FILE; + $name = $files; + } + } + + if ($get_type === GET_TYPE::FILE) { + $zip = false; if ($xsendfile && OC_App::isEnabled('files_encryption')) { $xsendfile = false; } + } else { + self::validateZipDownload($dir, $files); + $zip = new ZipStreamer(false); } OC_Util::obEnd(); if ($zip or \OC\Files\Filesystem::isReadable($filename)) { - OC_Response::setContentDispositionHeader($name, 'attachment'); - header('Content-Transfer-Encoding: binary'); - OC_Response::disableCaching(); - if ($zip) { - ini_set('zlib.output_compression', 'off'); - header('Content-Type: application/zip'); - header('Content-Length: ' . filesize($filename)); - self::addSendfileHeader($filename); - }else{ - $filesize = \OC\Files\Filesystem::filesize($filename); - header('Content-Type: '.\OC\Files\Filesystem::getMimeType($filename)); - if ($filesize > -1) { - header("Content-Length: ".$filesize); - } - if ($xsendfile) { - list($storage) = \OC\Files\Filesystem::resolvePath(\OC\Files\Filesystem::getView()->getAbsolutePath($filename)); - if ($storage->isLocal()) { - self::addSendfileHeader(\OC\Files\Filesystem::getLocalFile($filename)); - } - } - } - } elseif ($zip or !\OC\Files\Filesystem::file_exists($filename)) { + self::sendHeaders($filename, $name, $zip); + } elseif (!\OC\Files\Filesystem::file_exists($filename)) { header("HTTP/1.0 404 Not Found"); $tmpl = new OC_Template('', '404', 'guest'); $tmpl->assign('file', $name); @@ -157,23 +127,36 @@ class OC_Files { return ; } if ($zip) { - $handle = fopen($filename, 'r'); - if ($handle) { - $chunkSize = 8 * 1024; // 1 MB chunks - while (!feof($handle)) { - echo fread($handle, $chunkSize); - flush(); + $executionTime = intval(ini_get('max_execution_time')); + set_time_limit(0); + if ($get_type === GET_TYPE::ZIP_FILES) { + foreach ($files as $file) { + $file = $dir . '/' . $file; + if (\OC\Files\Filesystem::is_file($file)) { + $fh = \OC\Files\Filesystem::fopen($file, 'r'); + $zip->addFileFromStream($fh, basename($file)); + fclose($fh); + } elseif (\OC\Files\Filesystem::is_dir($file)) { + self::zipAddDir($file, $zip); + } } + } elseif ($get_type === GET_TYPE::ZIP_DIR) { + $file = $dir . '/' . $files; + self::zipAddDir($file, $zip); } - if (!$xsendfile) { - unlink($filename); - } - }else{ - \OC\Files\Filesystem::readfile($filename); - } - foreach (self::$tmpFiles as $tmpFile) { - if (file_exists($tmpFile) and is_file($tmpFile)) { - unlink($tmpFile); + $zip->finalize(); + set_time_limit($executionTime); + } else { + if ($xsendfile) { + /** @var $storage \OC\Files\Storage\Storage */ + list($storage) = \OC\Files\Filesystem::resolvePath($filename); + if ($storage->isLocal()) { + self::addSendfileHeader(\OC\Files\Filesystem::getLocalFile($filename)); + } else { + \OC\Files\Filesystem::readfile($filename); + } + } else { + \OC\Files\Filesystem::readfile($filename); } } } @@ -186,10 +169,10 @@ class OC_Files { header("X-Sendfile: " . $filename); } if (isset($_SERVER['MOD_X_SENDFILE2_ENABLED'])) { - if (isset($_SERVER['HTTP_RANGE']) && + if (isset($_SERVER['HTTP_RANGE']) && preg_match("/^bytes=([0-9]+)-([0-9]*)$/", $_SERVER['HTTP_RANGE'], $range)) { $filelength = filesize($filename); - if ($range[2] == "") { + if ($range[2] === "") { $range[2] = $filelength - 1; } header("Content-Range: bytes $range[1]-$range[2]/" . $filelength); @@ -199,7 +182,7 @@ class OC_Files { header("X-Sendfile: " . $filename); } } - + if (isset($_SERVER['MOD_X_ACCEL_REDIRECT_ENABLED'])) { header("X-Accel-Redirect: " . $filename); } @@ -207,22 +190,27 @@ class OC_Files { /** * @param string $dir - * @param ZipArchive $zip + * @param ZipStreamer $zip + * @param string $internalDir */ public static function zipAddDir($dir, $zip, $internalDir='') { $dirname=basename($dir); - $zip->addEmptyDir($internalDir.$dirname); + $rootDir = $internalDir.$dirname; + if (!empty($rootDir)) { + $zip->addEmptyDir($rootDir); + } $internalDir.=$dirname.='/'; // prevent absolute dirs $internalDir = ltrim($internalDir, '/'); - $files=OC_Files::getDirectoryContent($dir); + + $files=\OC\Files\Filesystem::getDirectoryContent($dir); foreach($files as $file) { $filename=$file['name']; $file=$dir.'/'.$filename; if(\OC\Files\Filesystem::is_file($file)) { - $tmpFile=\OC\Files\Filesystem::toTmpFile($file); - OC_Files::$tmpFiles[]=$tmpFile; - $zip->addFile($tmpFile, $internalDir.$filename); + $fh = \OC\Files\Filesystem::fopen($file, 'r'); + $zip->addFileFromStream($fh, $internalDir.$filename); + fclose($fh); }elseif(\OC\Files\Filesystem::is_dir($file)) { self::zipAddDir($file, $zip, $internalDir); } @@ -232,8 +220,8 @@ class OC_Files { /** * checks if the selected files are within the size constraint. If not, outputs an error page. * - * @param string $dir - * @param files $files + * @param string $dir + * @param array | string $files */ static function validateZipDownload($dir, $files) { if (!OC_Config::getValue('allowZipDownload', true)) { @@ -280,8 +268,8 @@ class OC_Files { /** * set the maximum upload size limit for apache hosts using .htaccess * - * @param int size filesisze in bytes - * @return false on failure, size on success + * @param int $size file size in bytes + * @return bool false on failure, size on success */ static function setUploadLimit($size) { //don't allow user to break his config -- upper boundary @@ -297,11 +285,12 @@ class OC_Files { } //don't allow user to break his config -- broken or malicious size input - if (intval($size) == 0) { + if (intval($size) === 0) { return false; } - $htaccess = @file_get_contents(OC::$SERVERROOT . '/.htaccess'); //supress errors in case we don't have permissions for + //suppress errors in case we don't have permissions for + $htaccess = @file_get_contents(OC::$SERVERROOT . '/.htaccess'); if (!$htaccess) { return false; } @@ -319,7 +308,7 @@ class OC_Files { if ($content !== null) { $htaccess = $content; } - if ($hasReplaced == 0) { + if ($hasReplaced === 0) { $htaccess .= "\n" . $setting; } } diff --git a/lib/private/files/cache/cache.php b/lib/private/files/cache/cache.php index 4cab461914..9b18257088 100644 --- a/lib/private/files/cache/cache.php +++ b/lib/private/files/cache/cache.php @@ -166,6 +166,16 @@ class Cache { */ public function getFolderContents($folder) { $fileId = $this->getId($folder); + return $this->getFolderContentsById($fileId); + } + + /** + * get the metadata of all files stored in $folder + * + * @param int $fileId the file id of the folder + * @return array + */ + public function getFolderContentsById($fileId) { if ($fileId > -1) { $sql = 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `storage_mtime`, `encrypted`, `unencrypted_size`, `etag` diff --git a/lib/private/files/filesystem.php b/lib/private/files/filesystem.php index 952f9f9feb..6478854eae 100644 --- a/lib/private/files/filesystem.php +++ b/lib/private/files/filesystem.php @@ -320,7 +320,8 @@ class Filesystem { else { self::mount('\OC\Files\Storage\Local', array('datadir' => $root), $user); } - $mount_file = \OC_Config::getValue("mount_file", \OC::$SERVERROOT . "/data/mount.json"); + $datadir = \OC_Config::getValue("datadirectory", \OC::$SERVERROOT . "/data"); + $mount_file = \OC_Config::getValue("mount_file", $datadir . "/mount.json"); //move config file to it's new position if (is_file(\OC::$SERVERROOT . '/config/mount.json')) { @@ -760,7 +761,7 @@ class Filesystem { * * @param string $directory path under datadirectory * @param string $mimetype_filter limit returned content to this mimetype or mimepart - * @return array + * @return \OC\Files\FileInfo[] */ public static function getDirectoryContent($directory, $mimetype_filter = '') { return self::$defaultInstance->getDirectoryContent($directory, $mimetype_filter); diff --git a/lib/private/files/storage/common.php b/lib/private/files/storage/common.php index d4dca780ff..9e826dd619 100644 --- a/lib/private/files/storage/common.php +++ b/lib/private/files/storage/common.php @@ -140,43 +140,6 @@ abstract class Common implements \OC\Files\Storage\Storage { return $result; } - /** - * @brief Deletes all files and folders recursively within a directory - * @param string $directory The directory whose contents will be deleted - * @param bool $empty Flag indicating whether directory will be emptied - * @returns bool - * - * @note By default the directory specified by $directory will be - * deleted together with its contents. To avoid this set $empty to true - */ - public function deleteAll($directory, $empty = false) { - $directory = trim($directory, '/'); - if (!$this->is_dir($directory) || !$this->isReadable($directory)) { - return false; - } else { - $directoryHandle = $this->opendir($directory); - if (is_resource($directoryHandle)) { - while (($contents = readdir($directoryHandle)) !== false) { - if (!\OC\Files\Filesystem::isIgnoredDir($contents)) { - $path = $directory . '/' . $contents; - if ($this->is_dir($path)) { - $this->deleteAll($path); - } else { - $this->unlink($path); - } - } - } - } - if ($empty === false) { - if (!$this->rmdir($directory)) { - return false; - } - } - return true; - } - - } - public function getMimeType($path) { if ($this->is_dir($path)) { return 'httpd/unix-directory'; diff --git a/lib/private/files/storage/wrapper/quota.php b/lib/private/files/storage/wrapper/quota.php index 1bcdca7f47..26c952e694 100644 --- a/lib/private/files/storage/wrapper/quota.php +++ b/lib/private/files/storage/wrapper/quota.php @@ -15,12 +15,18 @@ class Quota extends Wrapper { */ protected $quota; + /** + * @var string $sizeRoot + */ + protected $sizeRoot; + /** * @param array $parameters */ public function __construct($parameters) { $this->storage = $parameters['storage']; $this->quota = $parameters['quota']; + $this->sizeRoot = isset($parameters['root']) ? $parameters['root'] : ''; } /** @@ -46,7 +52,7 @@ class Quota extends Wrapper { if ($this->quota < 0) { return $this->storage->free_space($path); } else { - $used = $this->getSize(''); + $used = $this->getSize($this->sizeRoot); if ($used < 0) { return \OC\Files\SPACE_NOT_COMPUTED; } else { diff --git a/lib/private/files/view.php b/lib/private/files/view.php index 530aa8f751..2dbbf5b88c 100644 --- a/lib/private/files/view.php +++ b/lib/private/files/view.php @@ -413,7 +413,7 @@ class View { $result = $this->copy($path1, $path2); if ($result === true) { list($storage1, $internalPath1) = Filesystem::resolvePath($absolutePath1 . $postFix1); - $result = $storage1->deleteAll($internalPath1); + $result = $storage1->unlink($internalPath1); } } else { $source = $this->fopen($path1 . $postFix1, 'r'); @@ -534,6 +534,8 @@ class View { $source = $this->fopen($path1 . $postFix1, 'r'); $target = $this->fopen($path2 . $postFix2, 'w'); list($count, $result) = \OC_Helper::streamCopy($source, $target); + fclose($source); + fclose($target); } } if ($this->shouldEmitHooks() && $result !== false) { @@ -880,12 +882,13 @@ class View { $watcher->checkUpdate($internalPath); } + $folderId = $cache->getId($internalPath); $files = array(); - $contents = $cache->getFolderContents($internalPath); //TODO: mimetype_filter + $contents = $cache->getFolderContents($internalPath, $folderId); //TODO: mimetype_filter foreach ($contents as $content) { $files[] = new FileInfo($path . '/' . $content['name'], $storage, $content['path'], $content); } - $permissions = $permissionsCache->getDirectoryPermissions($cache->getId($internalPath), $user); + $permissions = $permissionsCache->getDirectoryPermissions($folderId, $user); $ids = array(); foreach ($files as $i => $file) { diff --git a/lib/private/helper.php b/lib/private/helper.php index 1aab2f296e..d8c4650f66 100644 --- a/lib/private/helper.php +++ b/lib/private/helper.php @@ -64,7 +64,7 @@ class OC_Helper { */ public static function linkToDocs($key) { $theme = new OC_Defaults(); - return $theme->getDocBaseUrl() . '/server/6.0/go.php?to=' . $key; + return $theme->buildDocLinkToKey($key); } /** diff --git a/lib/private/image.php b/lib/private/image.php index 42685ddab5..da32aa4760 100644 --- a/lib/private/image.php +++ b/lib/private/image.php @@ -41,8 +41,7 @@ class OC_Image { // exif_imagetype throws "read error!" if file is less than 12 byte if (filesize($filePath) > 11) { $imageType = exif_imagetype($filePath); - } - else { + } else { $imageType = false; } return $imageType ? image_type_to_mime_type($imageType) : ''; @@ -50,7 +49,7 @@ class OC_Image { /** * @brief Constructor. - * @param $imageref The path to a local file, a base64 encoded string or a resource created by an imagecreate* function. + * @param string|resource $imageref The path to a local file, a base64 encoded string or a resource created by an imagecreate* function. * @returns bool False on error */ public function __construct($imageRef = null) { @@ -115,13 +114,11 @@ class OC_Image { case 3: case 4: // Not tested return $this->width(); - break; case 5: // Not tested case 6: case 7: // Not tested case 8: return $this->height(); - break; } return $this->width(); } @@ -140,13 +137,11 @@ class OC_Image { case 3: case 4: // Not tested return $this->height(); - break; case 5: // Not tested case 6: case 7: // Not tested case 8: return $this->width(); - break; } return $this->height(); } @@ -197,7 +192,6 @@ class OC_Image { return false; } - $retVal = false; switch($this->imageType) { case IMAGETYPE_GIF: $retVal = imagegif($this->resource, $filePath); @@ -231,7 +225,7 @@ class OC_Image { } /** - * @returns Returns the image resource in any. + * @returns resource Returns the image resource in any. */ public function resource() { return $this->resource; @@ -264,8 +258,8 @@ class OC_Image { } /** - * @returns Returns a base64 encoded string suitable for embedding in a VCard. - */ + * @return string - base64 encoded, which is suitable for embedding in a VCard. + */ function __toString() { return base64_encode($this->data()); } @@ -307,43 +301,33 @@ class OC_Image { $o = $this->getOrientation(); OC_Log::write('core', 'OC_Image->fixOrientation() Orientation: '.$o, OC_Log::DEBUG); $rotate = 0; - $flip = false; switch($o) { case -1: return false; //Nothing to fix - break; case 1: $rotate = 0; - $flip = false; break; case 2: // Not tested $rotate = 0; - $flip = true; break; case 3: $rotate = 180; - $flip = false; break; case 4: // Not tested $rotate = 180; - $flip = true; break; case 5: // Not tested $rotate = 90; - $flip = true; break; case 6: //$rotate = 90; $rotate = 270; - $flip = false; break; case 7: // Not tested $rotate = 270; - $flip = true; break; case 8: $rotate = 90; - $flip = false; break; } if($rotate) { @@ -367,6 +351,7 @@ class OC_Image { return false; } } + return false; } /** @@ -599,9 +584,9 @@ class OC_Image { $meta['imagesize'] = $meta['filesize'] - $meta['offset']; // in rare cases filesize is equal to offset so we need to read physical size if ($meta['imagesize'] < 1) { - $meta['imagesize'] = @filesize($filename) - $meta['offset']; + $meta['imagesize'] = @filesize($fileName) - $meta['offset']; if ($meta['imagesize'] < 1) { - trigger_error('imagecreatefrombmp: Can not obtain filesize of ' . $filename . '!', E_USER_WARNING); + trigger_error('imagecreatefrombmp: Can not obtain filesize of ' . $fileName . '!', E_USER_WARNING); return false; } } @@ -947,7 +932,7 @@ if ( ! function_exists( 'imagebmp') ) { $index = imagecolorat($im, $i, $j); if ($index !== $lastIndex || $sameNum > 255) { if ($sameNum != 0) { - $bmpData .= chr($same_num) . chr($lastIndex); + $bmpData .= chr($sameNum) . chr($lastIndex); } $lastIndex = $index; $sameNum = 1; diff --git a/lib/private/mimetypes.list.php b/lib/private/mimetypes.list.php index 9bd07b8902..a216414c9d 100644 --- a/lib/private/mimetypes.list.php +++ b/lib/private/mimetypes.list.php @@ -31,6 +31,7 @@ return array( 'bash' => 'text/x-shellscript', 'blend' => 'application/x-blender', 'bin' => 'application/x-bin', + 'bmp' => 'image/bmp', 'cb7' => 'application/x-cbr', 'cba' => 'application/x-cbr', 'cbr' => 'application/x-cbr', diff --git a/lib/private/minimizer.php b/lib/private/minimizer.php deleted file mode 100644 index db522de74d..0000000000 --- a/lib/private/minimizer.php +++ /dev/null @@ -1,64 +0,0 @@ -contentType); - OC_Response::enableCaching(); - $etag = $this->generateETag($files); - $cache_key .= '-'.$etag; - - $gzout = false; - $cache = OC_Cache::getGlobalCache(); - if (!OC_Request::isNoCache() && (!defined('DEBUG') || !DEBUG)) { - OC_Response::setETagHeader($etag); - $gzout = $cache->get($cache_key.'.gz'); - } - - if (!$gzout) { - $out = $this->minimizeFiles($files); - $gzout = gzencode($out); - $cache->set($cache_key.'.gz', $gzout); - OC_Response::setETagHeader($etag); - } - // on some systems (e.g. SLES 11, but not Ubuntu) mod_deflate and zlib compression will compress the output twice. - // This results in broken core.css and core.js. To avoid it, we switch off zlib compression. - // Since mod_deflate is still active, Apache will compress what needs to be compressed, i.e. no disadvantage. - if(function_exists('apache_get_modules') && ini_get('zlib.output_compression') && in_array('mod_deflate', apache_get_modules())) { - ini_set('zlib.output_compression', 'Off'); - } - if ($encoding = OC_Request::acceptGZip()) { - header('Content-Encoding: '.$encoding); - $out = $gzout; - } else { - $out = gzdecode($gzout); - } - header('Content-Length: '.strlen($out)); - echo $out; - } - - public function clearCache() { - $cache = OC_Cache::getGlobalCache(); - $cache->clear('core.css'); - $cache->clear('core.js'); - } -} - -if (!function_exists('gzdecode')) { - function gzdecode($data, $maxlength=null, &$filename='', &$error='') - { - if (strcmp(substr($data, 0, 9),"\x1f\x8b\x8\0\0\0\0\0\0")) { - return null; // Not the GZIP format we expect (See RFC 1952) - } - return gzinflate(substr($data, 10, -8)); - } -} diff --git a/lib/private/minimizer/css.php b/lib/private/minimizer/css.php deleted file mode 100644 index 8d130572e2..0000000000 --- a/lib/private/minimizer/css.php +++ /dev/null @@ -1,38 +0,0 @@ -getAppConfig(); $appConfig->setValue('core', 'installedat', microtime(true)); $appConfig->setValue('core', 'lastupdatedat', microtime(true)); - $appConfig->setValue('core', 'remote_core.css', '/core/minimizer.php'); - $appConfig->setValue('core', 'remote_core.js', '/core/minimizer.php'); OC_Group::createGroup('admin'); OC_Group::addToGroup($username, 'admin'); @@ -148,7 +147,7 @@ class OC_Setup { $content.= "RewriteRule ^.well-known/host-meta /public.php?service=host-meta [QSA,L]\n"; $content.= "RewriteRule ^.well-known/carddav /remote.php/carddav/ [R]\n"; $content.= "RewriteRule ^.well-known/caldav /remote.php/caldav/ [R]\n"; - $content.= "RewriteRule ^apps/([^/]*)/(.*\.(css|php))$ index.php?app=$1&getfile=$2 [QSA,L]\n"; + $content.= "RewriteRule ^apps/([^/]*)/(.*\.(php))$ index.php?app=$1&getfile=$2 [QSA,L]\n"; $content.= "RewriteRule ^remote/(.*) remote.php [QSA,L]\n"; $content.= "\n"; $content.= "\n"; diff --git a/lib/private/template/cssresourcelocator.php b/lib/private/template/cssresourcelocator.php index 8e7831ca54..e26daa2582 100644 --- a/lib/private/template/cssresourcelocator.php +++ b/lib/private/template/cssresourcelocator.php @@ -22,7 +22,7 @@ class CSSResourceLocator extends ResourceLocator { $app = substr($style, 0, strpos($style, '/')); $style = substr($style, strpos($style, '/')+1); $app_path = \OC_App::getAppPath($app); - $app_url = $this->webroot . '/index.php/apps/' . $app; + $app_url = \OC_App::getAppWebPath($app); if ($this->appendIfExist($app_path, $style.$this->form_factor.'.css', $app_url) || $this->appendIfExist($app_path, $style.'.css', $app_url) ) { diff --git a/lib/private/templatelayout.php b/lib/private/templatelayout.php index 7bca5bc483..af17adb11c 100644 --- a/lib/private/templatelayout.php +++ b/lib/private/templatelayout.php @@ -1,4 +1,11 @@ * This file is licensed under the Affero General Public License version 3 or @@ -57,35 +64,38 @@ class OC_TemplateLayout extends OC_Template { } else { parent::__construct('core', 'layout.base'); } + $versionParameter = '?v=' . md5(implode(OC_Util::getVersion())); - // Add the js files - $jsfiles = self::findJavascriptFiles(OC_Util::$scripts); - $this->assign('jsfiles', array(), false); - if (OC_Config::getValue('installed', false) && $renderas!='error') { + $useAssetPipeline = OC_Config::getValue('asset-pipeline.enabled', false); + if ($useAssetPipeline) { + $this->append( 'jsfiles', OC_Helper::linkToRoute('js_config') . $versionParameter); - } - if (!empty(OC_Util::$coreScripts)) { - $this->append( 'jsfiles', OC_Helper::linkToRemoteBase('core.js', false) . $versionParameter); - } - foreach($jsfiles as $info) { - $root = $info[0]; - $web = $info[1]; - $file = $info[2]; - $this->append( 'jsfiles', $web.'/'.$file . $versionParameter); - } - // Add the css files - $cssfiles = self::findStylesheetFiles(OC_Util::$styles); - $this->assign('cssfiles', array()); - if (!empty(OC_Util::$coreStyles)) { - $this->append( 'cssfiles', OC_Helper::linkToRemoteBase('core.css', false) . $versionParameter); - } - foreach($cssfiles as $info) { - $root = $info[0]; - $web = $info[1]; - $file = $info[2]; + $this->generateAssets(); - $this->append( 'cssfiles', $web.'/'.$file . $versionParameter); + } else { + + // Add the js files + $jsfiles = self::findJavascriptFiles(OC_Util::$scripts); + $this->assign('jsfiles', array(), false); + if (OC_Config::getValue('installed', false) && $renderas!='error') { + $this->append( 'jsfiles', OC_Helper::linkToRoute('js_config') . $versionParameter); + } + foreach($jsfiles as $info) { + $web = $info[1]; + $file = $info[2]; + $this->append( 'jsfiles', $web.'/'.$file . $versionParameter); + } + + // Add the css files + $cssfiles = self::findStylesheetFiles(OC_Util::$styles); + $this->assign('cssfiles', array()); + foreach($cssfiles as $info) { + $web = $info[1]; + $file = $info[2]; + + $this->append( 'cssfiles', $web.'/'.$file . $versionParameter); + } } } @@ -116,4 +126,57 @@ class OC_TemplateLayout extends OC_Template { $locator->find($scripts); return $locator->getResources(); } + + public function generateAssets() + { + $jsFiles = self::findJavascriptFiles(OC_Util::$scripts); + $jsHash = self::hashScriptNames($jsFiles); + + if (!file_exists("assets/$jsHash.js")) { + $jsFiles = array_map(function ($item) { + $root = $item[0]; + $file = $item[2]; + return new FileAsset($root . '/' . $file, array(), $root, $file); + }, $jsFiles); + $jsCollection = new AssetCollection($jsFiles); + $jsCollection->setTargetPath("assets/$jsHash.js"); + + $writer = new AssetWriter(\OC::$SERVERROOT); + $writer->writeAsset($jsCollection); + } + + $cssFiles = self::findStylesheetFiles(OC_Util::$styles); + $cssHash = self::hashScriptNames($cssFiles); + + if (!file_exists("assets/$cssHash.css")) { + $cssFiles = array_map(function ($item) { + $root = $item[0]; + $file = $item[2]; + $assetPath = $root . '/' . $file; + $sourceRoot = \OC::$SERVERROOT; + $sourcePath = substr($assetPath, strlen(\OC::$SERVERROOT)); + return new FileAsset($assetPath, array(new CssRewriteFilter()), $sourceRoot, $sourcePath); + }, $cssFiles); + $cssCollection = new AssetCollection($cssFiles); + $cssCollection->setTargetPath("assets/$cssHash.css"); + + $writer = new AssetWriter(\OC::$SERVERROOT); + $writer->writeAsset($cssCollection); + } + + $this->append('jsfiles', OC_Helper::linkTo('assets', "$jsHash.js")); + $this->append('cssfiles', OC_Helper::linkTo('assets', "$cssHash.css")); + } + + private static function hashScriptNames($files) + { + $files = array_map(function ($item) { + $root = $item[0]; + $file = $item[2]; + return $root . '/' . $file; + }, $files); + + sort($files); + return hash('md5', implode('', $files)); + } } diff --git a/lib/private/updater.php b/lib/private/updater.php index 764a0f1412..f05d5038b7 100644 --- a/lib/private/updater.php +++ b/lib/private/updater.php @@ -102,6 +102,20 @@ class Updater extends BasicEmitter { $this->log->debug('starting upgrade from ' . $installedVersion . ' to ' . $currentVersion, array('app' => 'core')); } $this->emit('\OC\Updater', 'maintenanceStart'); + + /* + * START CONFIG CHANGES FOR OLDER VERSIONS + */ + if (version_compare($currentVersion, '6.90.1', '<')) { + // Add the overwriteHost config if it is not existant + // This is added to prevent host header poisoning + \OC_Config::setValue('trusted_domains', \OC_Config::getValue('trusted_domains', array(\OC_Request::serverHost()))); + } + /* + * STOP CONFIG CHANGES FOR OLDER VERSIONS + */ + + try { \OC_DB::updateDbFromStructure(\OC::$SERVERROOT . '/db_structure.xml'); $this->emit('\OC\Updater', 'dbUpgrade'); @@ -162,3 +176,4 @@ class Updater extends BasicEmitter { $this->emit('\OC\Updater', 'filecacheDone'); } } + diff --git a/lib/private/user.php b/lib/private/user.php index 08ead71202..a89b7286c1 100644 --- a/lib/private/user.php +++ b/lib/private/user.php @@ -227,6 +227,7 @@ class OC_User { * Log in a user and regenerate a new session - if the password is ok */ public static function login($uid, $password) { + session_regenerate_id(true); return self::getUserSession()->login($uid, $password); } diff --git a/lib/private/user/session.php b/lib/private/user/session.php index cd03b30205..1740bad5ab 100644 --- a/lib/private/user/session.php +++ b/lib/private/user/session.php @@ -157,7 +157,6 @@ class Session implements Emitter, \OCP\IUserSession { if($user !== false) { if (!is_null($user)) { if ($user->isEnabled()) { - session_regenerate_id(true); $this->setUser($user); $this->setLoginName($uid); $this->manager->emit('\OC\User', 'postLogin', array($user, $password)); diff --git a/lib/private/util.php b/lib/private/util.php index b785643652..920161949a 100755 --- a/lib/private/util.php +++ b/lib/private/util.php @@ -11,8 +11,6 @@ class OC_Util { public static $headers=array(); private static $rootMounted=false; private static $fsSetup=false; - public static $coreStyles=array(); - public static $coreScripts=array(); /** * @brief Can be set up @@ -65,7 +63,7 @@ class OC_Util { $user = $storage->getUser()->getUID(); $quota = OC_Util::getUserQuota($user); if ($quota !== \OC\Files\SPACE_UNLIMITED) { - return new \OC\Files\Storage\Wrapper\Quota(array('storage' => $storage, 'quota' => $quota)); + return new \OC\Files\Storage\Wrapper\Quota(array('storage' => $storage, 'quota' => $quota, 'root' => 'files')); } } diff --git a/lib/public/share.php b/lib/public/share.php index ebc555dba5..2fed41488c 100644 --- a/lib/public/share.php +++ b/lib/public/share.php @@ -1250,7 +1250,21 @@ class Share { // Remove root from file source paths if retrieving own shared items if (isset($uidOwner) && isset($row['path'])) { if (isset($row['parent'])) { - $row['path'] = '/Shared/'.basename($row['path']); + $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(); + $splitPath = explode('/', $row['path']); + $tmpPath = '/Shared' . $parentRow['file_target']; + 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']); diff --git a/settings/admin.php b/settings/admin.php index c0e4570658..42477bfc1c 100755 --- a/settings/admin.php +++ b/settings/admin.php @@ -21,6 +21,16 @@ $entries=OC_Log_Owncloud::getEntries(3); $entriesremain = count(OC_Log_Owncloud::getEntries(4)) > 3; $tmpl->assign('loglevel', OC_Config::getValue( "loglevel", 2 )); +$tmpl->assign('mail_domain', OC_Config::getValue( "mail_domain", '' )); +$tmpl->assign('mail_from_address', OC_Config::getValue( "mail_from_address", '' )); +$tmpl->assign('mail_smtpmode', OC_Config::getValue( "mail_smtpmode", '' )); +$tmpl->assign('mail_smtpsecure', OC_Config::getValue( "mail_smtpsecure", '' )); +$tmpl->assign('mail_smtphost', OC_Config::getValue( "mail_smtphost", '' )); +$tmpl->assign('mail_smtpport', OC_Config::getValue( "mail_smtpport", '' )); +$tmpl->assign('mail_smtpauthtype', OC_Config::getValue( "mail_smtpauthtype", '' )); +$tmpl->assign('mail_smtpauth', OC_Config::getValue( "mail_smtpauth", false )); +$tmpl->assign('mail_smtpname', OC_Config::getValue( "mail_smtpname", '' )); +$tmpl->assign('mail_smtppassword', OC_Config::getValue( "mail_smtppassword", '' )); $tmpl->assign('entries', $entries); $tmpl->assign('entriesremain', $entriesremain); $tmpl->assign('htaccessworking', $htaccessworking); diff --git a/settings/admin/controller.php b/settings/admin/controller.php new file mode 100644 index 0000000000..a075d77436 --- /dev/null +++ b/settings/admin/controller.php @@ -0,0 +1,93 @@ +. +*/ + +namespace OC\Settings\Admin; + +class Controller { + /** + * Set mail settings + */ + public static function setMailSettings() { + \OC_Util::checkAdminUser(); + \OCP\JSON::callCheck(); + + $l = \OC_L10N::get('settings'); + + $smtp_settings = array( + 'mail_domain' => null, + 'mail_from_address' => null, + 'mail_smtpmode' => array('sendmail', 'smtp', 'qmail', 'php'), + 'mail_smtpsecure' => array('', 'ssl', 'tls'), + 'mail_smtphost' => null, + 'mail_smtpport' => null, + 'mail_smtpauthtype' => array('LOGIN', 'PLAIN', 'NTLM'), + 'mail_smtpauth' => true, + 'mail_smtpname' => null, + 'mail_smtppassword' => null, + ); + + foreach ($smtp_settings as $setting => $validate) { + if (!$validate) { + if (!isset($_POST[$setting]) || $_POST[$setting] === '') { + \OC_Config::deleteKey( $setting ); + } else { + \OC_Config::setValue( $setting, $_POST[$setting] ); + } + } + else if (is_bool($validate)) { + if (!empty($_POST[$setting])) { + \OC_Config::setValue( $setting, (bool) $_POST[$setting] ); + } else { + \OC_Config::deleteKey( $setting ); + } + } + else if (is_array($validate)) { + if (!isset($_POST[$setting]) || $_POST[$setting] === '') { + \OC_Config::deleteKey( $setting ); + } else if (in_array($_POST[$setting], $validate)) { + \OC_Config::setValue( $setting, $_POST[$setting] ); + } else { + $message = $l->t('Invalid value supplied for %s', array(self::getFieldname($setting, $l))); + \OC_JSON::error( array( "data" => array( "message" => $message)) ); + exit; + } + } + } + + \OC_JSON::success(array("data" => array( "message" => $l->t("Saved") ))); + } + + /** + * Get the field name to use it in error messages + * + * @param $setting string + * @param $l \OC_L10N + * @return string + */ + public static function getFieldname($setting, $l) { + switch ($setting) { + case 'mail_smtpmode': + return $l->t( 'Send mode' ); + case 'mail_smtpsecure': + return $l->t( 'Encryption' ); + case 'mail_smtpauthtype': + return $l->t( 'Authentification method' ); + } + } +} diff --git a/settings/ajax/decryptall.php b/settings/ajax/decryptall.php index d7c104ab15..4782a4cfc8 100644 --- a/settings/ajax/decryptall.php +++ b/settings/ajax/decryptall.php @@ -24,6 +24,8 @@ if ($result !== false) { $successful = false; } + $util->closeEncryptionSession(); + if ($successful === true) { \OCP\JSON::success(array('data' => array('message' => 'Files decrypted successfully'))); } else { diff --git a/settings/css/settings.css b/settings/css/settings.css index 8a96885b78..a47e7bf656 100644 --- a/settings/css/settings.css +++ b/settings/css/settings.css @@ -61,7 +61,12 @@ td.remove { width:1em; padding-right:1em; } tr:hover>td.password>span, tr:hover>td.displayName>span { margin:0; cursor:pointer; } tr:hover>td.remove>a, tr:hover>td.password>img,tr:hover>td.displayName>img, tr:hover>td.quota>img { visibility:visible; cursor:pointer; } tr:hover>td.remove>a { float:right; } -li.selected { background-color:#ddd; } + +li.selected, +#leftcontent li.selected { + background-color: #ddd; +} + table.grid { width:100%; } #rightcontent { padding-left: 10px; } div.quota { @@ -150,6 +155,18 @@ span.connectionwarning {color:#933; font-weight:bold; } input[type=radio] { width:1em; } table.shareAPI td { padding-bottom: 0.8em; } +#mail_settings p label:first-child { + display: inline-block; + width: 300px; + text-align: right; +} +#mail_settings p select:nth-child(2) { + width: 143px; +} +#mail_smtpport { + width: 40px; +} + /* HELP */ .pressed {background-color:#DDD;} diff --git a/settings/js/admin.js b/settings/js/admin.js index e957bd68f1..5ea6a5af2d 100644 --- a/settings/js/admin.js +++ b/settings/js/admin.js @@ -34,4 +34,38 @@ $(document).ready(function(){ $('#security').change(function(){ $.post(OC.filePath('settings','ajax','setsecurity.php'), { enforceHTTPS: $('#forcessl').val() },function(){} ); }); + + $('#mail_smtpauth').change(function() { + if (!this.checked) { + $('#mail_credentials').addClass('hidden'); + } else { + $('#mail_credentials').removeClass('hidden'); + } + }); + + $('#mail_smtpmode').change(function() { + if ($(this).val() !== 'smtp') { + $('#setting_smtpauth').addClass('hidden'); + $('#setting_smtphost').addClass('hidden'); + $('#mail_smtpsecure_label').addClass('hidden'); + $('#mail_smtpsecure').addClass('hidden'); + $('#mail_credentials').addClass('hidden'); + } else { + $('#setting_smtpauth').removeClass('hidden'); + $('#setting_smtphost').removeClass('hidden'); + $('#mail_smtpsecure_label').removeClass('hidden'); + $('#mail_smtpsecure').removeClass('hidden'); + if ($('#mail_smtpauth').attr('checked')) { + $('#mail_credentials').removeClass('hidden'); + } + } + }); + + $('#mail_settings').change(function(){ + OC.msg.startSaving('#mail_settings .msg'); + var post = $( "#mail_settings" ).serialize(); + $.post(OC.Router.generate('settings_mail_settings'), post, function(data){ + OC.msg.finishedSaving('#mail_settings .msg', data); + }); + }); }); diff --git a/settings/js/apps.js b/settings/js/apps.js index 2c6f77d931..3dbc8a2f7c 100644 --- a/settings/js/apps.js +++ b/settings/js/apps.js @@ -207,12 +207,24 @@ OC.Settings.Apps = OC.Settings.Apps || { a.prepend(filename); a.prepend(img); li.append(a); - // append the new app as last item in the list (.push is from sticky footer) + + // append the new app as last item in the list + // (.push is from sticky footer) $('#apps .wrapper .push').before(li); - // scroll the app navigation down so the newly added app is seen - $('#navigation').animate({ scrollTop: $('#navigation').height() }, 'slow'); - // draw attention to the newly added app entry by flashing it twice - container.children('li[data-id="'+entry.id+'"]').animate({opacity:.3}).animate({opacity:1}).animate({opacity:.3}).animate({opacity:1}); + + // scroll the app navigation down + // so the newly added app is seen + $('#navigation').animate({ + scrollTop: $('#navigation').height() + }, 'slow'); + + // draw attention to the newly added app entry + // by flashing it twice + container.children('li[data-id="' + entry.id + '"]') + .animate({opacity: 0.3}) + .animate({opacity: 1}) + .animate({opacity: 0.3}) + .animate({opacity: 1}); if (!SVGSupport() && entry.icon.match(/\.svg$/i)) { $(img).addClass('svg'); @@ -248,6 +260,8 @@ $(document).ready(function(){ var item = tgt.is('li') ? $(tgt) : $(tgt).parent(); var app = item.data('app'); OC.Settings.Apps.loadApp(app); + $('#leftcontent .selected').removeClass('selected'); + item.addClass('selected'); } return false; }); diff --git a/settings/js/isadmin.php b/settings/js/isadmin.php deleted file mode 100644 index 13a8ba1d31..0000000000 --- a/settings/js/isadmin.php +++ /dev/null @@ -1,20 +0,0 @@ - - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. - */ - -// Set the content type to Javascript -header("Content-type: text/javascript"); - -// Disallow caching -header("Cache-Control: no-cache, must-revalidate"); -header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); - -if (OC_User::isAdminUser(OC_User::getUser())) { - echo("var isadmin = true;"); -} else { - echo("var isadmin = false;"); -} diff --git a/settings/js/personal.js b/settings/js/personal.js index ef261b50bb..98bfe7132d 100644 --- a/settings/js/personal.js +++ b/settings/js/personal.js @@ -52,9 +52,11 @@ function updateAvatar (hidedefault) { if(hidedefault) { $headerdiv.hide(); + $('#header .avatardiv').removeClass('avatardiv-shown'); } else { $headerdiv.css({'background-color': ''}); $headerdiv.avatar(OC.currentUser, 32, true); + $('#header .avatardiv').addClass('avatardiv-shown'); } $displaydiv.css({'background-color': ''}); $displaydiv.avatar(OC.currentUser, 128, true); @@ -313,25 +315,3 @@ OC.Encryption.msg={ } } }; - -OC.msg={ - startSaving:function(selector){ - $(selector) - .html( t('settings', 'Saving...') ) - .removeClass('success') - .removeClass('error') - .stop(true, true) - .show(); - }, - finishedSaving:function(selector, data){ - if( data.status === "success" ){ - $(selector).html( data.data.message ) - .addClass('success') - .stop(true, true) - .delay(3000) - .fadeOut(900); - }else{ - $(selector).html( data.data.message ).addClass('error'); - } - } -}; diff --git a/settings/js/users.js b/settings/js/users.js index 6886db668b..160d0a8d9d 100644 --- a/settings/js/users.js +++ b/settings/js/users.js @@ -85,19 +85,24 @@ var UserList = { add: function (username, displayname, groups, subadmin, quota, sort) { var tr = $('tbody tr').first().clone(); - if (tr.find('div.avatardiv')){ + var subadminsEl; + var subadminSelect; + var groupsSelect; + if (tr.find('div.avatardiv').length){ $('div.avatardiv', tr).avatar(username, 32); } tr.attr('data-uid', username); tr.attr('data-displayName', displayname); tr.find('td.name').text(username); tr.find('td.displayName > span').text(displayname); - var groupsSelect = $('') + + // make them look like the multiselect buttons + // until they get time to really get initialized + groupsSelect = $('') .attr('data-username', username) .data('user-groups', groups); - tr.find('td.groups').empty(); if (tr.find('td.subadmins').length > 0) { - var subadminSelect = $('') .attr('data-username', username) .data('user-groups', groups) .data('subadmin', subadmin); @@ -109,11 +114,10 @@ var UserList = { subadminSelect.append($('')); } }); - tr.find('td.groups').append(groupsSelect); - UserList.applyMultiplySelect(groupsSelect); - if (tr.find('td.subadmins').length > 0) { - tr.find('td.subadmins').append(subadminSelect); - UserList.applyMultiplySelect(subadminSelect); + tr.find('td.groups').empty().append(groupsSelect); + subadminsEl = tr.find('td.subadmins'); + if (subadminsEl.length > 0) { + subadminsEl.append(subadminSelect); } if (tr.find('td.remove img').length === 0 && OC.currentUser !== username) { var rm_img = $('').attr({ @@ -139,11 +143,11 @@ var UserList = { } } $(tr).appendTo('tbody'); + if (sort) { UserList.doSort(); } - quotaSelect.singleSelect(); quotaSelect.on('change', function () { var uid = $(this).parent().parent().attr('data-uid'); var quota = $(this).val(); @@ -153,6 +157,16 @@ var UserList = { } }); }); + + // defer init so the user first sees the list appear more quickly + window.setTimeout(function(){ + quotaSelect.singleSelect(); + UserList.applyMultiplySelect(groupsSelect); + if (subadminSelect) { + UserList.applyMultiplySelect(subadminSelect); + } + }, 0); + return tr; }, // From http://my.opera.com/GreyWyvern/blog/show.dml/1671288 alphanum: function(a, b) { @@ -209,28 +223,39 @@ var UserList = { if (UserList.updating) { return; } + $('table+.loading').css('visibility', 'visible'); UserList.updating = true; $.get(OC.Router.generate('settings_ajax_userlist', { offset: UserList.offset, limit: UserList.usersToLoad }), function (result) { + var loadedUsers = 0; + var trs = []; if (result.status === 'success') { //The offset does not mirror the amount of users available, //because it is backend-dependent. For correct retrieval, //always the limit(requested amount of users) needs to be added. - UserList.offset += UserList.usersToLoad; $.each(result.data, function (index, user) { if($('tr[data-uid="' + user.name + '"]').length > 0) { return true; } var tr = UserList.add(user.name, user.displayname, user.groups, user.subadmin, user.quota, false); - if (index === 9) { - $(tr).bind('inview', function (event, isInView, visiblePartX, visiblePartY) { - $(this).unbind(event); - UserList.update(); - }); - } + tr.addClass('appear transparent'); + trs.push(tr); + loadedUsers++; }); if (result.data.length > 0) { UserList.doSort(); + $('table+.loading').css('visibility', 'hidden'); } + else { + UserList.noMoreEntries = true; + $('table+.loading').remove(); + } + UserList.offset += loadedUsers; + // animate + setTimeout(function() { + for (var i = 0; i < trs.length; i++) { + trs[i].removeClass('transparent'); + } + }, 0); } UserList.updating = false; }); @@ -239,7 +264,7 @@ var UserList = { applyMultiplySelect: function (element) { var checked = []; var user = element.attr('data-username'); - if ($(element).attr('class') === 'groupsselect') { + if ($(element).hasClass('groupsselect')) { if (element.data('userGroups')) { checked = element.data('userGroups'); } @@ -248,7 +273,7 @@ var UserList = { if (user === OC.currentUser && group === 'admin') { return false; } - if (!isadmin && checked.length === 1 && checked[0] === group) { + if (!oc_isadmin && checked.length === 1 && checked[0] === group) { return false; } $.post( @@ -280,7 +305,7 @@ var UserList = { }); }; var label; - if (isadmin) { + if (oc_isadmin) { label = t('settings', 'add group'); } else { label = null; @@ -295,7 +320,7 @@ var UserList = { minWidth: 100 }); } - if ($(element).attr('class') === 'subadminsselect') { + if ($(element).hasClass('subadminsselect')) { if (element.data('subadmin')) { checked = element.data('subadmin'); } @@ -330,18 +355,26 @@ var UserList = { minWidth: 100 }); } - } + }, + + _onScroll: function(e) { + if (!!UserList.noMoreEntries) { + return; + } + if ($(window).scrollTop() + $(window).height() > $(document).height() - 500) { + UserList.update(true); + } + }, }; $(document).ready(function () { UserList.doSort(); UserList.availableGroups = $('#content table').data('groups'); - $('tbody tr:last').bind('inview', function (event, isInView, visiblePartX, visiblePartY) { - OC.Router.registerLoadedCallback(function () { - UserList.update(); - }); + OC.Router.registerLoadedCallback(function() { + $(window).scroll(function(e) {UserList._onScroll(e);}); }); + $('table').after($('')); $('select[multiple]').each(function (index, element) { UserList.applyMultiplySelect($(element)); diff --git a/settings/routes.php b/settings/routes.php index 60f9d8e100..64f7122f0c 100644 --- a/settings/routes.php +++ b/settings/routes.php @@ -70,7 +70,8 @@ $this->create('settings_ajax_getlog', '/settings/ajax/getlog.php') ->actionInclude('settings/ajax/getlog.php'); $this->create('settings_ajax_setloglevel', '/settings/ajax/setloglevel.php') ->actionInclude('settings/ajax/setloglevel.php'); +$this->create('settings_mail_settings', '/settings/admin/mailsettings') + ->post() + ->action('OC\Settings\Admin\Controller', 'setMailSettings'); $this->create('settings_ajax_setsecurity', '/settings/ajax/setsecurity.php') ->actionInclude('settings/ajax/setsecurity.php'); -$this->create('isadmin', '/settings/js/isadmin.js') - ->actionInclude('settings/js/isadmin.php'); diff --git a/settings/templates/admin.php b/settings/templates/admin.php index 0eabffb931..139a9dd076 100644 --- a/settings/templates/admin.php +++ b/settings/templates/admin.php @@ -11,6 +11,27 @@ $levelLabels = array( $l->t( 'Errors and fatal issues' ), $l->t( 'Fatal issues only' ), ); + +$mail_smtpauthtype = array( + '' => $l->t('None'), + 'LOGIN' => $l->t('Login'), + 'PLAIN' => $l->t('Plain'), + 'NTLM' => $l->t('NT LAN Manager'), +); + +$mail_smtpsecure = array( + '' => $l->t('None'), + 'ssl' => $l->t('SSL'), + 'tls' => $l->t('TLS'), +); + +$mail_smtpmode = array( + 'sendmail', + 'smtp', + 'qmail', + 'php', +); + ?> +
    +

    t('Email Server'));?>

    + +

    t('This is used for sending out notifications.')); ?>

    + +

    + + + + + +

    + +

    + + ' /> + @ + ' /> +

    + + + + + + + +
    +

    t('Log'));?>

    t('Log level'));?> + + *dbprefix*migratekeyword + + + + + select + text + + true + 255 + + +
    + diff --git a/tests/data/db_structure2.xml b/tests/data/db_structure2.xml index ae5f22e957..568d90ab0a 100644 --- a/tests/data/db_structure2.xml +++ b/tests/data/db_structure2.xml @@ -119,4 +119,19 @@ + + *dbprefix*migratekeyword + + + + + select + text + + false + 255 + + +
    + diff --git a/tests/lib/appframework/http/ResponseTest.php b/tests/lib/appframework/http/ResponseTest.php index 4f21d77a17..063ab8b5d3 100644 --- a/tests/lib/appframework/http/ResponseTest.php +++ b/tests/lib/appframework/http/ResponseTest.php @@ -78,7 +78,7 @@ class ResponseTest extends \PHPUnit_Framework_TestCase { public function testGetEtag() { $this->childResponse->setEtag('hi'); - $this->assertEquals('hi', $this->childResponse->getEtag()); + $this->assertSame('hi', $this->childResponse->getEtag()); } diff --git a/tests/lib/connector/sabre/file.php b/tests/lib/connector/sabre/file.php index 50b8711a90..c2f0ffa12d 100644 --- a/tests/lib/connector/sabre/file.php +++ b/tests/lib/connector/sabre/file.php @@ -48,21 +48,6 @@ class Test_OC_Connector_Sabre_File extends PHPUnit_Framework_TestCase { $etag = $file->put('test data'); } - /** - * Test setting name with setName() - */ - public function testSetName() { - // setup - $file = new OC_Connector_Sabre_File('/test.txt'); - $file->fileView = $this->getMock('\OC\Files\View', array('isUpdatable'), array(), '', FALSE); - $file->fileView->expects($this->any())->method('isUpdatable')->withAnyParameters()->will($this->returnValue(true)); - $etag = $file->put('test data'); - $file->setName('/renamed.txt'); - $this->assertTrue($file->fileView->file_exists('/renamed.txt')); - // clean up - $file->delete(); - } - /** * Test setting name with setName() with invalid chars * @expectedException Sabre_DAV_Exception_BadRequest diff --git a/tests/lib/files/cache/scanner.php b/tests/lib/files/cache/scanner.php index 3f5604b4d4..5182fac8b1 100644 --- a/tests/lib/files/cache/scanner.php +++ b/tests/lib/files/cache/scanner.php @@ -150,13 +150,15 @@ class Scanner extends \PHPUnit_Framework_TestCase { $this->cache->put('folder', array('mtime' => $this->storage->filemtime('folder'), 'storage_mtime' => $this->storage->filemtime('folder'))); $this->scanner->scan('', \OC\Files\Cache\Scanner::SCAN_SHALLOW, \OC\Files\Cache\Scanner::REUSE_SIZE); $newData = $this->cache->get(''); - $this->assertNotEquals($oldData['etag'], $newData['etag']); + $this->assertInternalType('string', $oldData['etag']); + $this->assertInternalType('string', $newData['etag']); + $this->assertNotSame($oldData['etag'], $newData['etag']); $this->assertEquals($oldData['size'], $newData['size']); $oldData = $newData; $this->scanner->scan('', \OC\Files\Cache\Scanner::SCAN_SHALLOW, \OC\Files\Cache\Scanner::REUSE_ETAG); $newData = $this->cache->get(''); - $this->assertEquals($oldData['etag'], $newData['etag']); + $this->assertSame($oldData['etag'], $newData['etag']); $this->assertEquals(-1, $newData['size']); $this->scanner->scan('', \OC\Files\Cache\Scanner::SCAN_RECURSIVE); @@ -164,17 +166,17 @@ class Scanner extends \PHPUnit_Framework_TestCase { $this->assertNotEquals(-1, $oldData['size']); $this->scanner->scanFile('', \OC\Files\Cache\Scanner::REUSE_ETAG + \OC\Files\Cache\Scanner::REUSE_SIZE); $newData = $this->cache->get(''); - $this->assertEquals($oldData['etag'], $newData['etag']); + $this->assertSame($oldData['etag'], $newData['etag']); $this->assertEquals($oldData['size'], $newData['size']); $this->scanner->scan('', \OC\Files\Cache\Scanner::SCAN_RECURSIVE, \OC\Files\Cache\Scanner::REUSE_ETAG + \OC\Files\Cache\Scanner::REUSE_SIZE); $newData = $this->cache->get(''); - $this->assertEquals($oldData['etag'], $newData['etag']); + $this->assertSame($oldData['etag'], $newData['etag']); $this->assertEquals($oldData['size'], $newData['size']); $this->scanner->scan('', \OC\Files\Cache\Scanner::SCAN_SHALLOW, \OC\Files\Cache\Scanner::REUSE_ETAG + \OC\Files\Cache\Scanner::REUSE_SIZE); $newData = $this->cache->get(''); - $this->assertEquals($oldData['etag'], $newData['etag']); + $this->assertSame($oldData['etag'], $newData['etag']); $this->assertEquals($oldData['size'], $newData['size']); } @@ -217,8 +219,11 @@ class Scanner extends \PHPUnit_Framework_TestCase { // manipulate etag to simulate an empty etag $this->scanner->scan('', \OC\Files\Cache\Scanner::SCAN_SHALLOW, \OC\Files\Cache\Scanner::REUSE_ETAG); $data0 = $this->cache->get('folder/bar.txt'); + $this->assertInternalType('string', $data0['etag']); $data1 = $this->cache->get('folder'); + $this->assertInternalType('string', $data1['etag']); $data2 = $this->cache->get(''); + $this->assertInternalType('string', $data2['etag']); $data0['etag'] = ''; $this->cache->put('folder/bar.txt', $data0); @@ -227,10 +232,15 @@ class Scanner extends \PHPUnit_Framework_TestCase { // verify cache content $newData0 = $this->cache->get('folder/bar.txt'); - $newData1 = $this->cache->get('folder'); - $newData2 = $this->cache->get(''); + $this->assertInternalType('string', $newData0['etag']); $this->assertNotEmpty($newData0['etag']); - $this->assertNotEquals($data1['etag'], $newData1['etag']); - $this->assertNotEquals($data2['etag'], $newData2['etag']); + + $newData1 = $this->cache->get('folder'); + $this->assertInternalType('string', $newData1['etag']); + $this->assertNotSame($data1['etag'], $newData1['etag']); + + $newData2 = $this->cache->get(''); + $this->assertInternalType('string', $newData2['etag']); + $this->assertNotSame($data2['etag'], $newData2['etag']); } } diff --git a/tests/lib/files/cache/updater.php b/tests/lib/files/cache/updater.php index 48986149a7..a6ee8c4666 100644 --- a/tests/lib/files/cache/updater.php +++ b/tests/lib/files/cache/updater.php @@ -96,10 +96,14 @@ class Updater extends \PHPUnit_Framework_TestCase { Filesystem::file_put_contents('foo.txt', 'asd'); $cachedData = $this->cache->get('foo.txt'); $this->assertEquals(3, $cachedData['size']); - $this->assertNotEquals($fooCachedData['etag'], $cachedData['etag']); + $this->assertInternalType('string', $fooCachedData['etag']); + $this->assertInternalType('string', $cachedData['etag']); + $this->assertNotSame($fooCachedData['etag'], $cachedData['etag']); $cachedData = $this->cache->get(''); $this->assertEquals(2 * $textSize + $imageSize + 3, $cachedData['size']); - $this->assertNotEquals($rootCachedData['etag'], $cachedData['etag']); + $this->assertInternalType('string', $rootCachedData['etag']); + $this->assertInternalType('string', $cachedData['etag']); + $this->assertNotSame($rootCachedData['etag'], $cachedData['etag']); $rootCachedData = $cachedData; $this->assertFalse($this->cache->inCache('bar.txt')); @@ -110,12 +114,15 @@ class Updater extends \PHPUnit_Framework_TestCase { $mtime = $cachedData['mtime']; $cachedData = $this->cache->get(''); $this->assertEquals(2 * $textSize + $imageSize + 2 * 3, $cachedData['size']); - $this->assertNotEquals($rootCachedData['etag'], $cachedData['etag']); + $this->assertInternalType('string', $rootCachedData['etag']); + $this->assertInternalType('string', $cachedData['etag']); + $this->assertNotSame($rootCachedData['etag'], $cachedData['etag']); $this->assertGreaterThanOrEqual($rootCachedData['mtime'], $mtime); } public function testWriteWithMountPoints() { $storage2 = new \OC\Files\Storage\Temporary(array()); + $storage2->getScanner()->scan(''); //initialize etags $cache2 = $storage2->getCache(); Filesystem::mount($storage2, array(), '/' . self::$user . '/files/folder/substorage'); $folderCachedData = $this->cache->get('folder'); @@ -127,11 +134,15 @@ class Updater extends \PHPUnit_Framework_TestCase { $mtime = $cachedData['mtime']; $cachedData = $cache2->get(''); - $this->assertNotEquals($substorageCachedData['etag'], $cachedData['etag']); + $this->assertInternalType('string', $substorageCachedData['etag']); + $this->assertInternalType('string', $cachedData['etag']); + $this->assertNotSame($substorageCachedData['etag'], $cachedData['etag']); $this->assertEquals($mtime, $cachedData['mtime']); $cachedData = $this->cache->get('folder'); - $this->assertNotEquals($folderCachedData['etag'], $cachedData['etag']); + $this->assertInternalType('string', $folderCachedData['etag']); + $this->assertInternalType('string', $cachedData['etag']); + $this->assertNotSame($folderCachedData['etag'], $cachedData['etag']); $this->assertEquals($mtime, $cachedData['mtime']); } @@ -146,19 +157,25 @@ class Updater extends \PHPUnit_Framework_TestCase { $this->assertFalse($this->cache->inCache('foo.txt')); $cachedData = $this->cache->get(''); $this->assertEquals(2 * $textSize + $imageSize, $cachedData['size']); - $this->assertNotEquals($rootCachedData['etag'], $cachedData['etag']); + $this->assertInternalType('string', $rootCachedData['etag']); + $this->assertInternalType('string', $cachedData['etag']); + $this->assertNotSame($rootCachedData['etag'], $cachedData['etag']); $this->assertGreaterThanOrEqual($rootCachedData['mtime'], $cachedData['mtime']); $rootCachedData = $cachedData; Filesystem::mkdir('bar_folder'); $this->assertTrue($this->cache->inCache('bar_folder')); $cachedData = $this->cache->get(''); - $this->assertNotEquals($rootCachedData['etag'], $cachedData['etag']); + $this->assertInternalType('string', $rootCachedData['etag']); + $this->assertInternalType('string', $cachedData['etag']); + $this->assertNotSame($rootCachedData['etag'], $cachedData['etag']); $rootCachedData = $cachedData; Filesystem::rmdir('bar_folder'); $this->assertFalse($this->cache->inCache('bar_folder')); $cachedData = $this->cache->get(''); - $this->assertNotEquals($rootCachedData['etag'], $cachedData['etag']); + $this->assertInternalType('string', $rootCachedData['etag']); + $this->assertInternalType('string', $cachedData['etag']); + $this->assertNotSame($rootCachedData['etag'], $cachedData['etag']); $this->assertGreaterThanOrEqual($rootCachedData['mtime'], $cachedData['mtime']); } @@ -174,11 +191,15 @@ class Updater extends \PHPUnit_Framework_TestCase { $this->assertFalse($cache2->inCache('foo.txt')); $cachedData = $cache2->get(''); - $this->assertNotEquals($substorageCachedData['etag'], $cachedData['etag']); + $this->assertInternalType('string', $substorageCachedData['etag']); + $this->assertInternalType('string', $cachedData['etag']); + $this->assertNotSame($substorageCachedData['etag'], $cachedData['etag']); $this->assertGreaterThanOrEqual($substorageCachedData['mtime'], $cachedData['mtime']); $cachedData = $this->cache->get('folder'); - $this->assertNotEquals($folderCachedData['etag'], $cachedData['etag']); + $this->assertInternalType('string', $folderCachedData['etag']); + $this->assertInternalType('string', $cachedData['etag']); + $this->assertNotSame($folderCachedData['etag'], $cachedData['etag']); $this->assertGreaterThanOrEqual($folderCachedData['mtime'], $cachedData['mtime']); } @@ -199,7 +220,9 @@ class Updater extends \PHPUnit_Framework_TestCase { $mtime = $cachedData['mtime']; $cachedData = $this->cache->get(''); $this->assertEquals(3 * $textSize + $imageSize, $cachedData['size']); - $this->assertNotEquals($rootCachedData['etag'], $cachedData['etag']); + $this->assertInternalType('string', $rootCachedData['etag']); + $this->assertInternalType('string', $cachedData['etag']); + $this->assertNotSame($rootCachedData['etag'], $cachedData['etag']); } public function testRenameExtension() { @@ -227,12 +250,16 @@ class Updater extends \PHPUnit_Framework_TestCase { $mtime = $cachedData['mtime']; $cachedData = $cache2->get(''); - $this->assertNotEquals($substorageCachedData['etag'], $cachedData['etag']); + $this->assertInternalType('string', $substorageCachedData['etag']); + $this->assertInternalType('string', $cachedData['etag']); + $this->assertNotSame($substorageCachedData['etag'], $cachedData['etag']); // rename can cause mtime change - invalid assert // $this->assertEquals($mtime, $cachedData['mtime']); $cachedData = $this->cache->get('folder'); - $this->assertNotEquals($folderCachedData['etag'], $cachedData['etag']); + $this->assertInternalType('string', $folderCachedData['etag']); + $this->assertInternalType('string', $cachedData['etag']); + $this->assertNotSame($folderCachedData['etag'], $cachedData['etag']); // rename can cause mtime change - invalid assert // $this->assertEquals($mtime, $cachedData['mtime']); } @@ -242,11 +269,15 @@ class Updater extends \PHPUnit_Framework_TestCase { $fooCachedData = $this->cache->get('foo.txt'); Filesystem::touch('foo.txt'); $cachedData = $this->cache->get('foo.txt'); - $this->assertNotEquals($fooCachedData['etag'], $cachedData['etag']); + $this->assertInternalType('string', $fooCachedData['etag']); + $this->assertInternalType('string', $cachedData['etag']); + $this->assertNotSame($fooCachedData['etag'], $cachedData['etag']); $this->assertGreaterThanOrEqual($fooCachedData['mtime'], $cachedData['mtime']); $cachedData = $this->cache->get(''); - $this->assertNotEquals($rootCachedData['etag'], $cachedData['etag']); + $this->assertInternalType('string', $rootCachedData['etag']); + $this->assertInternalType('string', $cachedData['etag']); + $this->assertNotSame($rootCachedData['etag'], $cachedData['etag']); $this->assertGreaterThanOrEqual($rootCachedData['mtime'], $cachedData['mtime']); $rootCachedData = $cachedData; @@ -255,14 +286,20 @@ class Updater extends \PHPUnit_Framework_TestCase { $folderCachedData = $this->cache->get('folder'); Filesystem::touch('folder/bar.txt', $time); $cachedData = $this->cache->get('folder/bar.txt'); - $this->assertNotEquals($barCachedData['etag'], $cachedData['etag']); + $this->assertInternalType('string', $barCachedData['etag']); + $this->assertInternalType('string', $cachedData['etag']); + $this->assertNotSame($barCachedData['etag'], $cachedData['etag']); $this->assertEquals($time, $cachedData['mtime']); $cachedData = $this->cache->get('folder'); - $this->assertNotEquals($folderCachedData['etag'], $cachedData['etag']); + $this->assertInternalType('string', $folderCachedData['etag']); + $this->assertInternalType('string', $cachedData['etag']); + $this->assertNotSame($folderCachedData['etag'], $cachedData['etag']); $cachedData = $this->cache->get(''); - $this->assertNotEquals($rootCachedData['etag'], $cachedData['etag']); + $this->assertInternalType('string', $rootCachedData['etag']); + $this->assertInternalType('string', $cachedData['etag']); + $this->assertNotSame($rootCachedData['etag'], $cachedData['etag']); $this->assertEquals($time, $cachedData['mtime']); } @@ -279,14 +316,20 @@ class Updater extends \PHPUnit_Framework_TestCase { $time = 1371006070; Filesystem::touch('folder/substorage/foo.txt', $time); $cachedData = $cache2->get('foo.txt'); - $this->assertNotEquals($fooCachedData['etag'], $cachedData['etag']); + $this->assertInternalType('string', $fooCachedData['etag']); + $this->assertInternalType('string', $cachedData['etag']); + $this->assertNotSame($fooCachedData['etag'], $cachedData['etag']); $this->assertEquals($time, $cachedData['mtime']); $cachedData = $cache2->get(''); - $this->assertNotEquals($substorageCachedData['etag'], $cachedData['etag']); + $this->assertInternalType('string', $substorageCachedData['etag']); + $this->assertInternalType('string', $cachedData['etag']); + $this->assertNotSame($substorageCachedData['etag'], $cachedData['etag']); $cachedData = $this->cache->get('folder'); - $this->assertNotEquals($folderCachedData['etag'], $cachedData['etag']); + $this->assertInternalType('string', $folderCachedData['etag']); + $this->assertInternalType('string', $cachedData['etag']); + $this->assertNotSame($folderCachedData['etag'], $cachedData['etag']); $this->assertEquals($time, $cachedData['mtime']); } diff --git a/tests/lib/files/etagtest.php b/tests/lib/files/etagtest.php index ce05adb188..af9f66835f 100644 --- a/tests/lib/files/etagtest.php +++ b/tests/lib/files/etagtest.php @@ -65,7 +65,11 @@ class EtagTest extends \PHPUnit_Framework_TestCase { $scanner = new \OC\Files\Utils\Scanner($user1); $scanner->backgroundScan('/'); - $this->assertEquals($originalEtags, $this->getEtags($files)); + $newEtags = $this->getEtags($files); + // loop over array and use assertSame over assertEquals to prevent false positives + foreach ($originalEtags as $file => $originalEtag) { + $this->assertSame($originalEtag, $newEtags[$file]); + } } /** diff --git a/tests/lib/files/storage/wrapper/quota.php b/tests/lib/files/storage/wrapper/quota.php index e1b880255f..43eae78415 100644 --- a/tests/lib/files/storage/wrapper/quota.php +++ b/tests/lib/files/storage/wrapper/quota.php @@ -62,7 +62,7 @@ class Quota extends \Test\Files\Storage\Storage { $this->assertEquals('foobarqwe', $instance->file_get_contents('foo')); } - public function testReturnFalseWhenFopenFailed(){ + public function testReturnFalseWhenFopenFailed() { $failStorage = $this->getMock( '\OC\Files\Storage\Local', array('fopen'), @@ -76,7 +76,7 @@ class Quota extends \Test\Files\Storage\Storage { $this->assertFalse($instance->fopen('failedfopen', 'r')); } - public function testReturnRegularStreamOnRead(){ + public function testReturnRegularStreamOnRead() { $instance = $this->getLimitedStorage(9); // create test file first @@ -95,11 +95,30 @@ class Quota extends \Test\Files\Storage\Storage { fclose($stream); } - public function testReturnQuotaStreamOnWrite(){ + public function testReturnQuotaStreamOnWrite() { $instance = $this->getLimitedStorage(9); $stream = $instance->fopen('foo', 'w+'); $meta = stream_get_meta_data($stream); $this->assertEquals('user-space', $meta['wrapper_type']); fclose($stream); } + + public function testSpaceRoot() { + $storage = $this->getMockBuilder('\OC\Files\Storage\Local')->disableOriginalConstructor()->getMock(); + $cache = $this->getMockBuilder('\OC\Files\Cache\Cache')->disableOriginalConstructor()->getMock(); + $storage->expects($this->once()) + ->method('getCache') + ->will($this->returnValue($cache)); + $storage->expects($this->once()) + ->method('free_space') + ->will($this->returnValue(2048)); + $cache->expects($this->once()) + ->method('get') + ->with('files') + ->will($this->returnValue(array('size' => 50))); + + $instance = new \OC\Files\Storage\Wrapper\Quota(array('storage' => $storage, 'quota' => 1024, 'root' => 'files')); + + $this->assertEquals(1024 - 50, $instance->free_space('')); + } } diff --git a/tests/lib/files/view.php b/tests/lib/files/view.php index 371d1ed179..c85f1128db 100644 --- a/tests/lib/files/view.php +++ b/tests/lib/files/view.php @@ -563,6 +563,6 @@ class View extends \PHPUnit_Framework_TestCase { $scanner->scanFile('test', \OC\Files\Cache\Scanner::REUSE_ETAG); $info2 = $view->getFileInfo('/test/test'); - $this->assertEquals($info['etag'], $info2['etag']); + $this->assertSame($info['etag'], $info2['etag']); } } diff --git a/tests/lib/share/backend.php b/tests/lib/share/backend.php index 2f6c84678f..420bd9d88b 100644 --- a/tests/lib/share/backend.php +++ b/tests/lib/share/backend.php @@ -26,7 +26,7 @@ class Test_Share_Backend implements OCP\Share_Backend { const FORMAT_SOURCE = 0; const FORMAT_TARGET = 1; const FORMAT_PERMISSIONS = 2; - + private $testItem1 = 'test.txt'; private $testItem2 = 'share.txt'; @@ -57,11 +57,11 @@ class Test_Share_Backend implements OCP\Share_Backend { public function formatItems($items, $format, $parameters = null) { $testItems = array(); foreach ($items as $item) { - if ($format == self::FORMAT_SOURCE) { + if ($format === self::FORMAT_SOURCE) { $testItems[] = $item['item_source']; - } else if ($format == self::FORMAT_TARGET) { + } else if ($format === self::FORMAT_TARGET) { $testItems[] = $item['item_target']; - } else if ($format == self::FORMAT_PERMISSIONS) { + } else if ($format === self::FORMAT_PERMISSIONS) { $testItems[] = $item['permissions']; } } diff --git a/tests/lib/share/share.php b/tests/lib/share/share.php index a89f100d97..b5cba9430a 100644 --- a/tests/lib/share/share.php +++ b/tests/lib/share/share.php @@ -622,21 +622,21 @@ class Test_Share extends PHPUnit_Framework_TestCase { OC_User::setUserId($this->user1); $this->assertEquals( array('test.txt', 'test.txt'), - OCP\Share::getItemsShared('test', 'test.txt'), + OCP\Share::getItemsShared('test', Test_Share_Backend::FORMAT_SOURCE), 'Failed asserting that the test.txt file is shared exactly two times by user1.' ); OC_User::setUserId($this->user2); $this->assertEquals( array('test.txt'), - OCP\Share::getItemsShared('test', 'test.txt'), + OCP\Share::getItemsShared('test', Test_Share_Backend::FORMAT_SOURCE), 'Failed asserting that the test.txt file is shared exactly once by user2.' ); OC_User::setUserId($this->user3); $this->assertEquals( array('test.txt'), - OCP\Share::getItemsShared('test', 'test.txt'), + OCP\Share::getItemsShared('test', Test_Share_Backend::FORMAT_SOURCE), 'Failed asserting that the test.txt file is shared exactly once by user3.' ); diff --git a/tests/lib/urlgenerator.php b/tests/lib/urlgenerator.php new file mode 100644 index 0000000000..875a7f0658 --- /dev/null +++ b/tests/lib/urlgenerator.php @@ -0,0 +1,34 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +class Test_Urlgenerator extends PHPUnit_Framework_TestCase { + + + /** + * @small + * @brief test absolute URL construction + * @dataProvider provideURLs + */ + function testGetAbsoluteURL($url, $expectedResult) { + + $urlGenerator = new \OC\URLGenerator(null); + $result = $urlGenerator->getAbsoluteURL($url); + + $this->assertEquals($expectedResult, $result); + } + + public function provideURLs() { + return array( + array("index.php", "http://localhost/index.php"), + array("/index.php", "http://localhost/index.php"), + array("/apps/index.php", "http://localhost/apps/index.php"), + array("apps/index.php", "http://localhost/apps/index.php"), + ); + } +} +