Compare commits

...

29 Commits

Author SHA1 Message Date
C Montero-Luque 0a5f34ab80 9.0.0 RC2 2016-03-04 18:08:02 -05:00
C. Montero Luque 5488bb74fe Merge pull request #22879 from owncloud/stable9-backport-use-clob-for-timezone
[stable9] Use CLOB for timezone
2016-03-04 18:02:45 -05:00
C. Montero Luque 4b85660984 Merge pull request #22871 from owncloud/enable-federation-app-stable9
[stable9] Automatically enabled federation app
2016-03-04 18:02:32 -05:00
C. Montero Luque 5080c34d78 Merge pull request #22867 from owncloud/stable9-backport-22865
[stable9] Run cleanup of expired DB file locks to background job
2016-03-04 17:45:07 -05:00
C. Montero Luque 5d402fc817 Merge pull request #22848 from owncloud/stable9-fileactions-downloadspinnerfix
[stable9] Fix download spinner to work with CSS styles
2016-03-04 16:41:26 -05:00
Lukas Reschke fb62043cc1 [stable9] Use CLOB for timezone
TEXT defaults to a length of 255 which is going to fail in some cases as the timezone can be rather long.

This changes it back to a CLOB as it has been before as well: owncloudarchive/calendar@8d8bb68. I'm not super convinced that CLOB is the best choice here but at least it seems to work.

Fixes #22876

Backport of https://github.com/owncloud/core/pull/22878 to stable9
2016-03-04 22:12:54 +01:00
C. Montero Luque b3c9ed8d5c Merge pull request #22782 from owncloud/backport-22776-doc-fix
Add Versions app header to config.sample.php
2016-03-04 16:10:35 -05:00
Morris Jobke 9737290e39 Run cleanup of expired DB file locks to background job
* fixes #22819

The old way fired a DELETE statement on each destruction of the
DBLockingProvider. Which could cause a lot of queries. It's enough
to run this every 5 minutes in a background job, which in the end
could result in file locks that exists 5 minutes longer - in the
worst case and for not properly released locks.

This makes the DB based locking a lot more performant and could
result in a similar performance to the Redis based locking provider.
2016-03-04 20:13:05 +01:00
C. Montero Luque 121ff350d4 Merge branch 'stable9' into enable-federation-app-stable9 2016-03-04 13:38:02 -05:00
C. Montero Luque bb0c304482 Merge pull request #22861 from owncloud/stable9-trashbin-checkpath
[stable9] Properly check path validity before deleting to trash
2016-03-04 13:33:23 -05:00
Lukas Reschke c9c85b8d4a Adjust OCS test 2016-03-04 17:59:57 +01:00
Lukas Reschke eb59aa8be4 Automatically enabled federation app 2016-03-04 17:40:45 +01:00
Vincent Petry 96d45e90dc Properly check path validity before deleting to trash
This prevents deleting the whole "files" folder of the user whenever
$ownerPath is empty. This can happen in concurrency situations.
2016-03-04 15:33:02 +01:00
Thomas Müller 0655f25406 Merge pull request #22858 from owncloud/stable9-release-channel
[stable9] Add release channel selection back
2016-03-04 15:29:37 +01:00
Thomas Müller 434747f450 Merge pull request #22832 from owncloud/external-unavailable-recheck9
[9.0] allow availability recheck for external storages
2016-03-04 15:29:27 +01:00
Lukas Reschke 7ff2b9232b Add release channel selection back
Allows to select the release channels again and also shows the last check date
2016-03-04 14:39:14 +01:00
Thomas Müller 3d28f364c5 Merge pull request #22852 from owncloud/backport-22851-php54-for-comments
[9.0] ucwords does not support delimiter on 5.4
2016-03-04 14:05:45 +01:00
Joas Schilling 62399f7852 ucwords does not support delimiter on 5.4 2016-03-04 12:20:50 +01:00
Thomas Müller 4fc6deaaf0 Merge pull request #22822 from owncloud/stable9-exclude-assets
[stable9] Exclude the assets folder from integrity check
2016-03-04 11:50:24 +01:00
Vincent Petry e6c6ee8d2a Fix download spinner to work with CSS styles
A recent change replaced img elements with CSS icons for file actions.
This fix adjusts the logic to work properly with CSS icons instead of
images.
2016-03-04 10:50:41 +01:00
Thomas Müller 4da858b3b7 Merge pull request #22829 from owncloud/stable9-revert-22264-read-version-from-info.xml-only
[stable9] Revert "No longer evaluate appinfo/version"
2016-03-04 08:55:52 +01:00
Vincent Petry 8e8f5cdddf Properly set exception in FailedStorage 2016-03-03 20:07:22 +01:00
Robin Appelman f603c57751 allow availability recheck for external storages 2016-03-03 20:07:18 +01:00
Vincent Petry e155f28f5e Revert "No longer evaluate appinfo/version" 2016-03-03 18:42:23 +01:00
Lukas Reschke 750fc9ae26 Merge pull request #22818 from owncloud/backport-fix-encryption-on-version-restore
[stable9] Keep "encryptedVersion" when calling `\OC\Files\View::copy`
2016-03-03 16:18:51 +01:00
Lukas Reschke 4186bcbdf2 Exclude the assets folder from integrity check
We should not scan the assets folder as this can contain user specific content. Partially addresses https://github.com/owncloud/core/issues/22803
2016-03-03 16:00:34 +01:00
Lukas Reschke 98f79173ed Keep "encryptedVersion" when calling \OC\Files\View::copy
When calling `\OC\Files\View::copy` we should also keep the version to ensure that the file will always have the correct version attached and can be successfully decrypted.

To test this the following steps are necessary (from https://github.com/owncloud/core/issues/22781#issuecomment-191328982):

1. setup a new ownCloud 9.0 beta2
2. enable encryption
2. upload a docx (5.7MB large)
3. upload the same file again and overwrite the existing file
4. I can download the original file and the first version
5. I restore the first version
6. restored version can no longer be downloaded with the error described above

The manual cache operation in `\OCA\Files_Versions\Storage` is unfortunately necessary since `\OCA\Files_Versions\Storage::copyFileContents` is not using `\OCP\Files\Storage::moveFromStorage` in the case when an object storage is used. Due to the workaround added in https://github.com/owncloud/core/commit/54cea05271b887f1c8062c034741df869bc0f055 the stream is directly copied and thus bypassing the FS.
2016-03-03 14:41:53 +01:00
RealRancor 8d07cb4d85 Add Versions app header to config.sample.php 2016-03-02 15:33:09 +01:00
C Montero-Luque 445957a0e2 9.0.0 RC1 2016-03-01 16:59:42 -05:00
36 changed files with 451 additions and 87 deletions
+2 -2
View File
@@ -283,7 +283,7 @@ CREATE TABLE calendarobjects (
description TEXT,
calendarorder INT(11) UNSIGNED NOT NULL DEFAULT '0',
calendarcolor VARBINARY(10),
timezone TEXT,
timezone CLOB,
components VARBINARY(20),
transparent TINYINT(1) NOT NULL DEFAULT '0',
UNIQUE(principaluri, uri)
@@ -337,7 +337,7 @@ CREATE TABLE calendarobjects (
</field>
<field>
<name>timezone</name>
<type>text</type>
<type>clob</type>
</field>
<field>
<name>components</name>
+1 -1
View File
@@ -5,7 +5,7 @@
<description>ownCloud WebDAV endpoint</description>
<licence>AGPL</licence>
<author>owncloud.org</author>
<version>0.1.4</version>
<version>0.1.5</version>
<default_enable/>
<types>
<filesystem/>
+1
View File
@@ -11,6 +11,7 @@
<dependencies>
<owncloud min-version="9.0" max-version="9.0" />
</dependencies>
<default_enable/>
<types>
<authentication/>
</types>
+1 -1
View File
@@ -6,7 +6,7 @@
<licence>AGPL</licence>
<author>Robin Appelman, Vincent Petry</author>
<default_enable/>
<version>1.4.3</version>
<version>1.4.4</version>
<types>
<filesystem/>
</types>
+1
View File
@@ -24,3 +24,4 @@
// Cron job for scanning user storages
\OC::$server->getJobList()->add('OCA\Files\BackgroundJob\ScanFiles');
\OC::$server->getJobList()->add('OCA\Files\BackgroundJob\DeleteOrphanedItems');
\OC::$server->getJobList()->add('OCA\Files\BackgroundJob\CleanupFileLocks');
+1
View File
@@ -102,3 +102,4 @@ if ($installedVersion === '1.1.9' && (
// Add cron job for scanning user storages
\OC::$server->getJobList()->add('OCA\Files\BackgroundJob\ScanFiles');
\OC::$server->getJobList()->add('OCA\Files\BackgroundJob\DeleteOrphanedItems');
\OC::$server->getJobList()->add('OCA\Files\BackgroundJob\CleanupFileLocks');
+13
View File
@@ -793,6 +793,19 @@ html.ie8 #controls .button.new {
background-size: 16px 16px;
}
#filestable .filename .action .icon.hidden,
#filestable .selectedActions a .icon.hidden,
#controls .actions .button .icon.hidden {
display: none;
}
#filestable .filename .action .icon.loading,
#filestable .selectedActions a .icon.loading,
#controls .actions .button .icon.loading {
width: 15px;
height: 15px;
}
.app-files .actions .button.new .icon {
margin-bottom: 2px;
}
+9 -10
View File
@@ -659,19 +659,18 @@
* Replaces the download icon with a loading spinner and vice versa
* - also adds the class disabled to the passed in element
*
* @param downloadButtonElement download fileaction
* @param {jQuery} $downloadButtonElement download fileaction
* @param {boolean} showIt whether to show the spinner(true) or to hide it(false)
*/
OCA.Files.FileActions.updateFileActionSpinner = function(downloadButtonElement, showIt) {
var icon = downloadButtonElement.find('img'),
sourceImage = icon.attr('src');
if(showIt) {
downloadButtonElement.addClass('disabled');
icon.attr('src', sourceImage.replace('actions/download.svg', 'loading-small.gif'));
OCA.Files.FileActions.updateFileActionSpinner = function($downloadButtonElement, showIt) {
var $icon = $downloadButtonElement.find('.icon');
if (showIt) {
var $loadingIcon = $('<span class="icon loading"></span>');
$icon.after($loadingIcon);
$icon.addClass('hidden');
} else {
downloadButtonElement.removeClass('disabled');
icon.attr('src', sourceImage.replace('loading-small.gif', 'actions/download.svg'));
$downloadButtonElement.find('.loading').remove();
$downloadButtonElement.find('.icon').removeClass('hidden');
}
};
@@ -0,0 +1,57 @@
<?php
/**
* @author Morris Jobke <hey@morrisjobke.de>
*
* @copyright Copyright (c) 2016, ownCloud, Inc.
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OCA\Files\BackgroundJob;
use OC\BackgroundJob\TimedJob;
use OC\Lock\DBLockingProvider;
/**
* Clean up all file locks that are expired for the DB file locking provider
*/
class CleanupFileLocks extends TimedJob {
/**
* Default interval in minutes
*
* @var int $defaultIntervalMin
**/
protected $defaultIntervalMin = 5;
/**
* sets the correct interval for this timed job
*/
public function __construct() {
$this->interval = $this->defaultIntervalMin * 60;
}
/**
* Makes the background job do its work
*
* @param array $argument unused argument
*/
public function run($argument) {
$lockingProvider = \OC::$server->getLockingProvider();
if($lockingProvider instanceof DBLockingProvider) {
$lockingProvider->cleanExpiredLocks();
}
}
}
+20
View File
@@ -656,4 +656,24 @@ describe('OCA.Files.FileActions tests', function() {
});
});
});
describe('download spinner', function() {
var FileActions = OCA.Files.FileActions;
var $el;
beforeEach(function() {
$el = $('<a href="#"><span class="icon icon-download"></span><span>Download</span></a>');
});
it('replaces download icon with spinner', function() {
FileActions.updateFileActionSpinner($el, true);
expect($el.find('.icon.loading').length).toEqual(1);
expect($el.find('.icon.icon-download').hasClass('hidden')).toEqual(true);
});
it('replaces spinner back with download icon with spinner', function() {
FileActions.updateFileActionSpinner($el, true);
FileActions.updateFileActionSpinner($el, false);
expect($el.find('.icon.loading').length).toEqual(0);
expect($el.find('.icon.icon-download').hasClass('hidden')).toEqual(false);
});
});
});
@@ -23,6 +23,7 @@
namespace OCA\Files_External\Config;
use OC\Files\Storage\Wrapper\Availability;
use OCA\Files_external\Migration\StorageMigrator;
use OCP\Files\Storage;
use OC\Files\Mount\MountPoint;
@@ -34,6 +35,7 @@ use OCA\Files_external\Service\UserStoragesService;
use OCA\Files_External\Service\UserGlobalStoragesService;
use OCA\Files_External\Lib\StorageConfig;
use OCA\Files_External\Lib\FailedStorage;
use OCP\Files\StorageNotAvailableException;
/**
* Make the old files_external config work with the new public mount config api
@@ -132,8 +134,10 @@ class ConfigAdapter implements IMountProvider {
try {
$availability = $impl->getAvailability();
if (!$availability['available']) {
$impl = new FailedStorage(['exception' => null]);
if (!$availability['available'] && !Availability::shouldRecheck($availability)) {
$impl = new FailedStorage([
'exception' => new StorageNotAvailableException('Storage with mount id ' . $storage->getId() . ' is not available')
]);
}
} catch (\Exception $e) {
// propagate exception into filesystem
@@ -39,6 +39,9 @@ class FailedStorage extends Common {
*/
public function __construct($params) {
$this->e = $params['exception'];
if (!$this->e) {
throw new \InvalidArgumentException('Missing "exception" argument in FailedStorage constructor');
}
}
public function getId() {
+1 -1
View File
@@ -204,7 +204,7 @@ class Trashbin {
$ownerView = new View('/' . $owner);
// file has been deleted in between
if (!$ownerView->file_exists('/files/' . $ownerPath)) {
if (is_null($ownerPath) || $ownerPath === '' || !$ownerView->file_exists('/files/' . $ownerPath)) {
return true;
}
+12 -10
View File
@@ -191,12 +191,7 @@ class Storage {
$mtime = $users_view->filemtime('files/' . $filename);
$users_view->copy('files/' . $filename, 'files_versions/' . $filename . '.v' . $mtime);
// call getFileInfo to enforce a file cache entry for the new version
$newFileInfo = $users_view->getFileInfo('files_versions/' . $filename . '.v' . $mtime);
// Keep the "encrypted" value of the original file
$oldVersion = $files_view->getFileInfo($filename)->getEncryptedVersion();
$cache = $newFileInfo->getStorage()->getCache();
$cache->update($newFileInfo->getId(), ['encrypted' => $oldVersion, 'encryptedVersion' => $oldVersion]);
$users_view->getFileInfo('files_versions/' . $filename . '.v' . $mtime);
}
}
@@ -331,15 +326,22 @@ class Storage {
//first create a new version
$version = 'files_versions'.$filename.'.v'.$users_view->filemtime('files'.$filename);
if ( !$users_view->file_exists($version)) {
if (!$users_view->file_exists($version)) {
$users_view->copy('files'.$filename, 'files_versions'.$filename.'.v'.$users_view->filemtime('files'.$filename));
$versionCreated = true;
}
$fileToRestore = 'files_versions' . $filename . '.v' . $revision;
// Restore encrypted version of the old file for the newly restored file
// This has to happen manually here since the file is manually copied below
$oldVersion = $users_view->getFileInfo($fileToRestore)->getEncryptedVersion();
$newFileInfo = $files_view->getFileInfo($filename);
$cache = $newFileInfo->getStorage()->getCache();
$cache->update($newFileInfo->getId(), ['encrypted' => $oldVersion, 'encryptedVersion' => $oldVersion]);
// rollback
if (self::copyFileContents($users_view, 'files_versions' . $filename . '.v' . $revision, 'files' . $filename)) {
if (self::copyFileContents($users_view, $fileToRestore, 'files' . $filename)) {
$files_view->touch($file, $revision);
Storage::scheduleExpire($uid, $file);
\OC_Hook::emit('\OCP\Versions', 'rollback', array(
+5 -3
View File
@@ -31,9 +31,11 @@ if(\OC::$server->getConfig()->getSystemValue('updatechecker', true) === true) {
$userObject = \OC::$server->getUserSession()->getUser();
if($userObject !== null) {
if(\OC::$server->getGroupManager()->isAdmin($userObject->getUID()) && $updateChecker->getUpdateState() !== []) {
\OCP\Util::addScript('updatenotification', 'notification');
OC_Hook::connect('\OCP\Config', 'js', $updateChecker, 'getJavaScript');
if(\OC::$server->getGroupManager()->isAdmin($userObject->getUID())) {
if($updateChecker->getUpdateState() !== []) {
\OCP\Util::addScript('updatenotification', 'notification');
OC_Hook::connect('\OCP\Config', 'js', $updateChecker, 'getJavaScript');
}
\OC_App::registerAdmin('updatenotification', 'admin');
}
}
@@ -22,7 +22,9 @@
namespace OCA\UpdateNotification\AppInfo;
use OC\AppFramework\Utility\TimeFactory;
use OC\Updater;
use OCA\UpdateNotification\Controller\AdminController;
use OCA\UpdateNotification\UpdateChecker;
use OCP\AppFramework\App;
use OCP\AppFramework\IAppContainer;
@@ -32,13 +34,21 @@ class Application extends App {
$container = $this->getContainer();
$container->registerService('AdminController', function(IAppContainer $c) {
$updater = new \OC\Updater(
\OC::$server->getHTTPHelper(),
\OC::$server->getConfig(),
\OC::$server->getIntegrityCodeChecker()
);
return new AdminController(
$c->query('AppName'),
$c->query('Request'),
$c->getServer()->getJobList(),
$c->getServer()->getSecureRandom(),
$c->getServer()->getConfig(),
new TimeFactory()
new TimeFactory(),
$c->getServer()->getL10N($c->query('AppName')),
new UpdateChecker($updater),
$c->getServer()->getDateTimeFormatter()
);
});
}
@@ -24,4 +24,5 @@ namespace OCA\UpdateNotification\AppInfo;
$application = new Application();
$application->registerRoutes($this, ['routes' => [
['name' => 'Admin#createCredentials', 'url' => '/credentials', 'verb' => 'GET'],
['name' => 'Admin#setChannel', 'url' => '/channel', 'verb' => 'POST'],
]]);
@@ -21,12 +21,15 @@
namespace OCA\UpdateNotification\Controller;
use OCA\UpdateNotification\UpdateChecker;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\BackgroundJob\IJobList;
use OCP\IConfig;
use OCP\IDateTimeFormatter;
use OCP\IL10N;
use OCP\IRequest;
use OCP\Security\ISecureRandom;
@@ -39,6 +42,12 @@ class AdminController extends Controller {
private $config;
/** @var ITimeFactory */
private $timeFactory;
/** @var UpdateChecker */
private $updateChecker;
/** @var IL10N */
private $l10n;
/** @var IDateTimeFormatter */
private $dateTimeFormatter;
/**
* @param string $appName
@@ -47,25 +56,70 @@ class AdminController extends Controller {
* @param ISecureRandom $secureRandom
* @param IConfig $config
* @param ITimeFactory $timeFactory
* @param IL10N $l10n
* @param UpdateChecker $updateChecker
* @param IDateTimeFormatter $dateTimeFormatter
*/
public function __construct($appName,
IRequest $request,
IJobList $jobList,
ISecureRandom $secureRandom,
IConfig $config,
ITimeFactory $timeFactory) {
ITimeFactory $timeFactory,
IL10N $l10n,
UpdateChecker $updateChecker,
IDateTimeFormatter $dateTimeFormatter) {
parent::__construct($appName, $request);
$this->jobList = $jobList;
$this->secureRandom = $secureRandom;
$this->config = $config;
$this->timeFactory = $timeFactory;
$this->l10n = $l10n;
$this->updateChecker = $updateChecker;
$this->dateTimeFormatter = $dateTimeFormatter;
}
/**
* @return TemplateResponse
*/
public function displayPanel() {
return new TemplateResponse($this->appName, 'admin', [], '');
$lastUpdateCheck = $this->dateTimeFormatter->formatDateTime(
$this->config->getAppValue('core', 'lastupdatedat')
);
$channels = [
'daily',
'beta',
'stable',
'production',
];
$currentChannel = \OCP\Util::getChannel();
// Remove the currently used channel from the channels list
if(($key = array_search($currentChannel, $channels)) !== false) {
unset($channels[$key]);
}
$params = [
'isNewVersionAvailable' => ($this->updateChecker->getUpdateState() === []) ? false : true,
'lastChecked' => $lastUpdateCheck,
'currentChannel' => $currentChannel,
'channels' => $channels,
];
return new TemplateResponse($this->appName, 'admin', $params, '');
}
/**
* @UseSession
*
* @param string $channel
* @return DataResponse
*/
public function setChannel($channel) {
\OCP\Util::setChannel($channel);
$this->config->setAppValue('core', 'lastupdatedat', 0);
return new DataResponse(['status' => 'success', 'data' => ['message' => $this->l10n->t('Updated channel')]]);
}
/**
+13 -1
View File
@@ -15,7 +15,7 @@
*/
var loginToken = '';
$(document).ready(function(){
$('#oca_updatenotification').click(function() {
$('#oca_updatenotification_button').click(function() {
// Load the new token
$.ajax({
url: OC.generateUrl('/apps/updatenotification/credentials')
@@ -39,4 +39,16 @@ $(document).ready(function(){
});
});
});
$('#release-channel').change(function() {
var newChannel = $('#release-channel').find(":selected").val();
$.post(
OC.generateUrl('/apps/updatenotification/channel'),
{
'channel': newChannel
},
function(data){
OC.msg.finishedAction('#channel_save_msg', data);
}
);
});
});
+38 -4
View File
@@ -1,8 +1,42 @@
<?php script('updatenotification', 'admin') ?>
<form id="oca_updatenotification" class="section">
<?php
script('updatenotification', 'admin');
/** @var array $_ */
/** @var bool $isNewVersionAvailable */
$isNewVersionAvailable = $_['isNewVersionAvailable'];
/** @var string $newVersionString */
$newVersionString = $_['newVersionString'];
/** @var string $lastCheckedDate */
$lastCheckedDate = $_['lastChecked'];
/** @var array $channels */
$channels = $_['channels'];
/** @var string $currentChannel */
$currentChannel = $_['currentChannel'];
?>
<form id="oca_updatenotification_section" class="section">
<h2><?php p($l->t('Updater')); ?></h2>
<?php if($isNewVersionAvailable === true): ?>
<strong><?php p($l->t('A new version is available: %s', [$newVersionString])); ?></strong>
<input type="button" id="oca_updatenotification_button" value="<?php p($l->t('Open updater')) ?>">
<?php else: ?>
<strong><?php print_unescaped($l->t('Your version is up to date.')); ?></strong>
<span class="icon-info svg" title="<?php p($l->t('Checked on %s', [$lastCheckedDate])) ?>"></span>
<?php endif; ?>
<p>
<?php p($l->t('For security reasons the built-in ownCloud updater is using additional credentials. To visit the updater page please click the following button.')) ?>
<label for="release-channel"><?php p($l->t('Update channel:')) ?></label>
<select id="release-channel">
<option value="<?php p($currentChannel); ?>"><?php p($currentChannel); ?></option>
<?php foreach ($channels as $channel => $channelTitle){ ?>
<option value="<?php p($channelTitle) ?>">
<?php p($channelTitle) ?>
</option>
<?php } ?>
</select>
<span id="channel_save_msg"></span>
</p>
<p>
<em><?php p($l->t('You can always update to a newer version / experimental channel. But you can never downgrade to a more stable channel.')); ?></em>
</p>
<input type="button" id="oca_updatenotification" value="<?php p($l->t('Open updater')) ?>">
</form>
@@ -22,11 +22,14 @@
namespace OCA\UpdateNotification\Tests\Controller;
use OCA\UpdateNotification\Controller\AdminController;
use OCA\UpdateNotification\UpdateChecker;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\BackgroundJob\IJobList;
use OCP\IConfig;
use OCP\IDateTimeFormatter;
use OCP\IL10N;
use OCP\IRequest;
use OCP\Security\ISecureRandom;
use Test\TestCase;
@@ -44,6 +47,12 @@ class AdminControllerTest extends TestCase {
private $adminController;
/** @var ITimeFactory */
private $timeFactory;
/** @var IL10N */
private $l10n;
/** @var UpdateChecker */
private $updateChecker;
/** @var IDateTimeFormatter */
private $dateTimeFormatter;
public function setUp() {
parent::setUp();
@@ -53,6 +62,10 @@ class AdminControllerTest extends TestCase {
$this->secureRandom = $this->getMock('\\OCP\\Security\\ISecureRandom');
$this->config = $this->getMock('\\OCP\\IConfig');
$this->timeFactory = $this->getMock('\\OCP\\AppFramework\\Utility\\ITimeFactory');
$this->l10n = $this->getMock('\\OCP\\IL10N');
$this->updateChecker = $this->getMockBuilder('\\OCA\\UpdateNotification\\UpdateChecker')
->disableOriginalConstructor()->getMock();
$this->dateTimeFormatter = $this->getMock('\\OCP\\IDateTimeFormatter');
$this->adminController = new AdminController(
'updatenotification',
@@ -60,15 +73,94 @@ class AdminControllerTest extends TestCase {
$this->jobList,
$this->secureRandom,
$this->config,
$this->timeFactory
$this->timeFactory,
$this->l10n,
$this->updateChecker,
$this->dateTimeFormatter
);
}
public function testDisplayPanel() {
$expected = new TemplateResponse('updatenotification', 'admin', [], '');
public function testDisplayPanelWithUpdate() {
$channels = [
'daily',
'beta',
'stable',
'production',
];
$currentChannel = \OCP\Util::getChannel();
// Remove the currently used channel from the channels list
if(($key = array_search($currentChannel, $channels)) !== false) {
unset($channels[$key]);
}
$this->config
->expects($this->once())
->method('getAppValue')
->with('core', 'lastupdatedat')
->willReturn('12345');
$this->dateTimeFormatter
->expects($this->once())
->method('formatDateTime')
->with('12345')
->willReturn('LastCheckedReturnValue');
$this->updateChecker
->expects($this->once())
->method('getUpdateState')
->willReturn(['foo' => 'bar']);
$params = [
'isNewVersionAvailable' => true,
'lastChecked' => 'LastCheckedReturnValue',
'currentChannel' => \OCP\Util::getChannel(),
'channels' => $channels,
];
$expected = new TemplateResponse('updatenotification', 'admin', $params, '');
$this->assertEquals($expected, $this->adminController->displayPanel());
}
public function testDisplayPanelWithoutUpdate() {
$channels = [
'daily',
'beta',
'stable',
'production',
];
$currentChannel = \OCP\Util::getChannel();
// Remove the currently used channel from the channels list
if(($key = array_search($currentChannel, $channels)) !== false) {
unset($channels[$key]);
}
$this->config
->expects($this->once())
->method('getAppValue')
->with('core', 'lastupdatedat')
->willReturn('12345');
$this->dateTimeFormatter
->expects($this->once())
->method('formatDateTime')
->with('12345')
->willReturn('LastCheckedReturnValue');
$this->updateChecker
->expects($this->once())
->method('getUpdateState')
->willReturn([]);
$params = [
'isNewVersionAvailable' => false,
'lastChecked' => 'LastCheckedReturnValue',
'currentChannel' => \OCP\Util::getChannel(),
'channels' => $channels,
];
$expected = new TemplateResponse('updatenotification', 'admin', $params, '');
$this->assertEquals($expected, $this->adminController->displayPanel());
}
public function testCreateCredentials() {
$this->jobList
->expects($this->once())
@@ -285,6 +285,7 @@ Feature: provisioning
| comments |
| dav |
| federatedfilesharing |
| federation |
| files |
| files_sharing |
| files_trashbin |
+6
View File
@@ -413,6 +413,12 @@ $CONFIG = array(
'trashbin_retention_obligation' => 'auto',
/**
* File versions
*
* These parameters control the Versions app.
*/
/**
* If the versions app is enabled (default), this setting defines the policy
* for when versions will be permanently deleted.
+8 -3
View File
@@ -567,7 +567,7 @@ class OC_App {
}
/**
* get the last version of the app from appinfo/info.xml
* get the last version of the app, either from appinfo/version or from appinfo/info.xml
*
* @param string $appId
* @return string
@@ -587,9 +587,14 @@ class OC_App {
* @return string
*/
public static function getAppVersionByPath($path) {
$versionFile = $path . '/appinfo/version';
$infoFile = $path . '/appinfo/info.xml';
$appData = self::getAppInfo($infoFile, true);
return isset($appData['version']) ? $appData['version'] : '';
if (is_file($versionFile)) {
return trim(file_get_contents($versionFile));
} else {
$appData = self::getAppInfo($infoFile, true);
return isset($appData['version']) ? $appData['version'] : '';
}
}
+3
View File
@@ -299,6 +299,9 @@ class AppManager implements IAppManager {
/**
* Returns the app information from "appinfo/info.xml".
*
* If no version was present in "appinfo/info.xml", reads it
* from the external "appinfo/version" file instead.
*
* @param string $appId app id
*
* @return array app iinfo
+1 -1
View File
@@ -362,7 +362,7 @@ class Comment implements IComment {
protected function fromArray($data) {
foreach(array_keys($data) as $key) {
// translate DB keys to internal setter names
$setter = 'set' . str_replace('_', '', ucwords($key,'_'));
$setter = 'set' . implode('', array_map('ucfirst', explode('_', $key)));
$setter = str_replace('Timestamp', 'DateTime', $setter);
if(method_exists($this, $setter)) {
@@ -29,6 +29,16 @@ namespace OC\Files\Storage\Wrapper;
class Availability extends Wrapper {
const RECHECK_TTL_SEC = 600; // 10 minutes
public static function shouldRecheck($availability) {
if (!$availability['available']) {
// trigger a recheck if TTL reached
if ((time() - $availability['last_checked']) > self::RECHECK_TTL_SEC) {
return true;
}
}
return false;
}
/**
* @return bool
*/
@@ -47,11 +57,8 @@ class Availability extends Wrapper {
*/
private function isAvailable() {
$availability = $this->getAvailability();
if (!$availability['available']) {
// trigger a recheck if TTL reached
if ((time() - $availability['last_checked']) > self::RECHECK_TTL_SEC) {
return $this->updateAvailability();
}
if (self::shouldRecheck($availability)) {
return $this->updateAvailability();
}
return $availability['available'];
}
@@ -620,6 +620,32 @@ class Encryption extends Wrapper {
return $this->copyBetweenStorage($sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime, false);
}
/**
* Update the encrypted cache version in the database
*
* @param Storage $sourceStorage
* @param string $sourceInternalPath
* @param string $targetInternalPath
* @param bool $isRename
*/
private function updateEncryptedVersion(Storage $sourceStorage, $sourceInternalPath, $targetInternalPath, $isRename) {
$isEncrypted = $this->encryptionManager->isEnabled() && $this->mount->getOption('encrypt', true) ? 1 : 0;
$cacheInformation = [
'encrypted' => (bool)$isEncrypted,
];
if($isEncrypted === 1) {
$cacheInformation['encryptedVersion'] = $sourceStorage->getCache()->get($sourceInternalPath)['encryptedVersion'];
}
// in case of a rename we need to manipulate the source cache because
// this information will be kept for the new target
if ($isRename) {
$sourceStorage->getCache()->put($sourceInternalPath, $cacheInformation);
} else {
$this->getCache()->put($targetInternalPath, $cacheInformation);
}
}
/**
* copy file between two storages
*
@@ -647,6 +673,7 @@ class Encryption extends Wrapper {
$info['size']
);
}
$this->updateEncryptedVersion($sourceStorage, $sourceInternalPath, $targetInternalPath, $isRename);
}
return $result;
}
@@ -689,15 +716,7 @@ class Encryption extends Wrapper {
if ($preserveMtime) {
$this->touch($targetInternalPath, $sourceStorage->filemtime($sourceInternalPath));
}
$isEncrypted = $this->encryptionManager->isEnabled() && $this->mount->getOption('encrypt', true) ? 1 : 0;
// in case of a rename we need to manipulate the source cache because
// this information will be kept for the new target
if ($isRename) {
$sourceStorage->getCache()->put($sourceInternalPath, ['encrypted' => $isEncrypted]);
} else {
$this->getCache()->put($targetInternalPath, ['encrypted' => $isEncrypted]);
}
$this->updateEncryptedVersion($sourceStorage, $sourceInternalPath, $targetInternalPath, $isRename);
} else {
// delete partially written target file
$this->unlink($targetInternalPath);
+7 -2
View File
@@ -389,11 +389,16 @@ class OC_Installer{
}
// check if the ocs version is the same as the version in info.xml/version
$version = trim($info['version']);
$versionFile= $extractDir.'/appinfo/version';
if(is_file($versionFile)) {
$version = trim(file_get_contents($versionFile));
}else{
$version = trim($info['version']);
}
if(isset($data['appdata']['version']) && $version<>trim($data['appdata']['version'])) {
OC_Helper::rmdirr($extractDir);
throw new \Exception($l->t("App can't be installed because the version in info.xml is not the same as the version reported from the app store"));
throw new \Exception($l->t("App can't be installed because the version in info.xml/version is not the same as the version reported from the app store"));
}
return $info;
@@ -35,8 +35,9 @@ class ExcludeFoldersByPathFilterIterator extends \RecursiveFilterIterator {
$excludedFolders = [
rtrim($root . '/data', '/'),
rtrim($root .'/themes', '/'),
rtrim($root.'/config', '/'),
rtrim($root.'/apps', '/'),
rtrim($root . '/config', '/'),
rtrim($root . '/apps', '/'),
rtrim($root . '/assets', '/'),
];
$customDataDir = \OC::$server->getConfig()->getSystemValue('datadirectory', '');
if($customDataDir !== '') {
+11 -15
View File
@@ -235,10 +235,17 @@ class DBLockingProvider extends AbstractLockingProvider {
*/
public function cleanExpiredLocks() {
$expire = $this->timeFactory->getTime();
$this->connection->executeUpdate(
'DELETE FROM `*PREFIX*file_locks` WHERE `ttl` < ?',
[$expire]
);
try {
$this->connection->executeUpdate(
'DELETE FROM `*PREFIX*file_locks` WHERE `ttl` < ?',
[$expire]
);
} catch (\Exception $e) {
// If the table is missing, the clean up was successful
if ($this->connection->tableExists('file_locks')) {
throw $e;
}
}
}
/**
@@ -257,15 +264,4 @@ class DBLockingProvider extends AbstractLockingProvider {
}
}
}
public function __destruct() {
try {
$this->cleanExpiredLocks();
} catch (\Exception $e) {
// If the table is missing, the clean up was successful
if ($this->connection->tableExists('file_locks')) {
throw $e;
}
}
}
}
+1 -1
View File
@@ -142,7 +142,7 @@ class App {
}
/**
* Get the last version of the app from appinfo/info.xml
* Get the last version of the app, either from appinfo/version or from appinfo/info.xml
* @param string $app
* @return string
* @since 4.0.0
Binary file not shown.
Binary file not shown.
+21 -6
View File
@@ -693,11 +693,19 @@ class Encryption extends Storage {
$temp = \OC::$server->getTempManager();
return fopen($temp->getTemporaryFile(), $mode);
});
if($expectedEncrypted) {
$cache = $this->getMock('\OCP\Files\Cache\ICache');
$cache->expects($this->once())
->method('get')
->with($sourceInternalPath)
->willReturn(['encryptedVersion' => 12345]);
$storage2->expects($this->once())
->method('getCache')
->willReturn($cache);
}
$this->encryptionManager->expects($this->any())
->method('isEnabled')
->willReturn($encryptionEnabled);
// FIXME can not overwrite the return after definition
// $this->mount->expects($this->at(0))
// ->method('getOption')
@@ -706,9 +714,16 @@ class Encryption extends Storage {
global $mockedMountPointEncryptionEnabled;
$mockedMountPointEncryptionEnabled = $mountPointEncryptionEnabled;
$expectedCachePut = [
'encrypted' => $expectedEncrypted,
];
if($expectedEncrypted === true) {
$expectedCachePut['encryptedVersion'] = 12345;
}
$this->cache->expects($this->once())
->method('put')
->with($sourceInternalPath, ['encrypted' => $expectedEncrypted]);
->with($sourceInternalPath, $expectedCachePut);
$this->invokePrivate($this->instance, 'copyBetweenStorage', [$storage2, $sourceInternalPath, $targetInternalPath, $preserveMtime, $isRename]);
@@ -765,10 +780,10 @@ class Encryption extends Storage {
->with($sourceStorage, $sourceInternalPath, $targetInternalPath)
->willReturn($copyResult);
$instance->expects($this->any())->method('getCache')
->willReturn($cache);
if ($copyResult) {
$instance->expects($this->once())->method('getCache')
->with('', $sourceStorage)
->willReturn($cache);
$cache->expects($this->once())->method('get')
->with($sourceInternalPath)
->willReturn(['encrypted' => $encrypted, 'size' => 42]);
+2 -2
View File
@@ -26,10 +26,10 @@
// We only can count up. The 4. digit is only for the internal patchlevel to trigger DB upgrades
// between betas, final and RCs. This is _not_ the public version number. Reset minor/patchlevel
// when updating major/minor version number.
$OC_Version = array(9, 0, 0, 15);
$OC_Version = array(9, 0, 0, 17);
// The human readable string
$OC_VersionString = '9.0.0 beta 2';
$OC_VersionString = '9.0.0 RC2';
$OC_VersionCanBeUpgradedFrom = array(8, 2);