Compare commits

...

45 Commits

Author SHA1 Message Date
C Montero-Luque
81f694d83e 9.0.0 2016-03-08 08:48:12 -05:00
Thomas Müller
f9a80a9c12 Merge pull request #22945 from owncloud/stable9-fixsharemountrecursion
[stable9] Fix share mounting recursion
2016-03-08 14:42:04 +01:00
Vincent Petry
d63f5561fb Fix share mounting recursion 2016-03-08 12:58:46 +01:00
C. Montero Luque
9232a124e2 Merge pull request #22913 from owncloud/fix-failing-migration-stable9
Fix failing migration stable9
2016-03-07 11:33:14 -05:00
Thomas Müller
b5a06ecd5c Calendar and addressbook migration commands are always available 2016-03-07 15:24:23 +01:00
Thomas Müller
79811b5806 Handle addressbook migration issue by writing the faulty event to the log and continue 2016-03-07 15:24:13 +01:00
Thomas Müller
6413fffdcb Handle calendar migration issue by writing the faulty event to the log and continue 2016-03-07 15:24:03 +01:00
Thomas Müller
b3b57621b7 Merge pull request #22897 from owncloud/backport-22896
[9.0] Correctly default to null and add type hint
2016-03-07 14:59:49 +01:00
Thomas Müller
6bfeb4595d Merge pull request #22906 from owncloud/stable9-quota-allowuploadwhenfreespaceisunlimited
[stable9] Fix uploading to fed shares where free space is unlimited
2016-03-07 14:58:31 +01:00
Vincent Petry
c1876ea51c Fix uploading when free space is unlimited
A federated share can report unlimited quota as -3, so the
ajax/upload.php code needs to be adjusted to block uploads when the free
space is unlimited.
2016-03-07 12:41:36 +01:00
Joas Schilling
9ec89b99b1 Correctly default to null and add type hint 2016-03-07 10:55:01 +01:00
C Montero-Luque
0945cb7a0e 9.0.0 RC3 2016-03-06 17:51:05 -05:00
Thomas Müller
6f4712a314 Merge pull request #22884 from owncloud/backport-cache-results
[stable9] Cache results of testRemoteUrl
2016-03-06 19:57:53 +01:00
Thomas Müller
d043b6ba91 Merge pull request #22889 from owncloud/stable9-use-custom-header
[stable9] Use custom header
2016-03-06 19:53:19 +01:00
Lukas Reschke
ef66729980 Use custom header
PHP in CGI mode eats the Authorization header => 💣
2016-03-05 23:07:11 +01:00
Lukas Reschke
71e3f7f866 Cache results of testRemoteUrl
Otherwise setting up the storage will result in a HTTP request and thus slowing down ownCloud.

Replaces https://github.com/owncloud/core/pull/22855
2016-03-05 21:09:58 +01:00
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 54cea05271 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
48 changed files with 551 additions and 123 deletions

View File

@@ -99,18 +99,24 @@ class Application extends App {
$container->registerService('MigrateAddressbooks', function($c) {
/** @var IAppContainer $c */
$db = $c->getServer()->getDatabaseConnection();
$logger = $c->getServer()->getLogger();
return new MigrateAddressbooks(
new AddressBookAdapter($db),
$c->query('CardDavBackend')
$c->query('CardDavBackend'),
$logger,
null
);
});
$container->registerService('MigrateCalendars', function($c) {
/** @var IAppContainer $c */
$db = $c->getServer()->getDatabaseConnection();
$logger = $c->getServer()->getLogger();
return new MigrateCalendars(
new CalendarAdapter($db),
$c->query('CalDavBackend')
$c->query('CalDavBackend'),
$logger,
null
);
});

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>

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/>

View File

@@ -29,7 +29,6 @@ use OCA\DAV\Command\SyncSystemAddressBook;
$dbConnection = \OC::$server->getDatabaseConnection();
$userManager = OC::$server->getUserManager();
$groupManager = OC::$server->getGroupManager();
$config = \OC::$server->getConfig();
$app = new Application();
@@ -38,12 +37,5 @@ $application->add(new CreateCalendar($userManager, $groupManager, $dbConnection)
$application->add(new CreateAddressBook($userManager, $app->getContainer()->query('CardDavBackend')));
$application->add(new SyncSystemAddressBook($app->getSyncService()));
$application->add(new SyncBirthdayCalendar($userManager, $app->getContainer()->query('BirthdayService')));
// the occ tool is *for now* only available in debug mode for developers to test
if ($config->getSystemValue('debug', false)){
$app = new \OCA\Dav\AppInfo\Application();
$migration = $app->getContainer()->query('MigrateAddressbooks');
$application->add(new MigrateAddressbooks($userManager, $migration));
$migration = $app->getContainer()->query('MigrateCalendars');
$application->add(new MigrateCalendars($userManager, $migration));
}
$application->add(new MigrateAddressbooks($userManager, $app->getContainer()->query('MigrateAddressbooks')));
$application->add(new MigrateCalendars($userManager, $app->getContainer()->query('MigrateCalendars')));

View File

@@ -78,7 +78,7 @@ class CardDavBackend implements BackendInterface, SyncSupport {
*/
public function __construct(IDBConnection $db,
Principal $principalBackend,
$dispatcher ) {
EventDispatcherInterface $dispatcher = null) {
$this->db = $db;
$this->principalBackend = $principalBackend;
$this->dispatcher = $dispatcher;

View File

@@ -23,10 +23,8 @@ namespace OCA\Dav\Migration;
use OCA\DAV\CardDAV\AddressBook;
use OCA\DAV\CardDAV\CardDavBackend;
use OCP\ILogger;
use Sabre\CardDAV\Plugin;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class MigrateAddressbooks {
@@ -37,15 +35,26 @@ class MigrateAddressbooks {
/** @var CardDavBackend */
private $backend;
/** @var ILogger */
private $logger;
/** @var OutputInterface */
private $consoleOutput;
/**
* @param AddressBookAdapter $adapter
* @param CardDavBackend $backend
*/
function __construct(AddressBookAdapter $adapter,
CardDavBackend $backend
CardDavBackend $backend,
ILogger $logger,
OutputInterface $consoleOutput = null
) {
$this->adapter = $adapter;
$this->backend = $backend;
$this->logger = $logger;
$this->consoleOutput = $consoleOutput;
}
/**
@@ -80,7 +89,17 @@ class MigrateAddressbooks {
*/
private function migrateBook($addressBookId, $newAddressBookId) {
$this->adapter->foreachCard($addressBookId, function($card) use ($newAddressBookId) {
$this->backend->createCard($newAddressBookId, $card['uri'], $card['carddata']);
try {
$this->backend->createCard($newAddressBookId, $card['uri'], $card['carddata']);
} catch (\Exception $ex) {
$eventId = $card['id'];
$addressBookId = $card['addressbookid'];
$msg = "One event could not be migrated. (id: $eventId, addressbookid: $addressBookId)";
$this->logger->logException($ex, ['app' => 'dav', 'message' => $msg]);
if (!is_null($this->consoleOutput)) {
$this->consoleOutput->writeln($msg);
}
}
});
}

View File

@@ -23,9 +23,7 @@ namespace OCA\Dav\Migration;
use OCA\DAV\CalDAV\CalDavBackend;
use OCA\DAV\CalDAV\Calendar;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use OCP\ILogger;
use Symfony\Component\Console\Output\OutputInterface;
class MigrateCalendars {
@@ -36,15 +34,25 @@ class MigrateCalendars {
/** @var CalDavBackend */
private $backend;
/** @var ILogger */
private $logger;
/** @var OutputInterface */
private $consoleOutput;
/**
* @param CalendarAdapter $adapter
* @param CalDavBackend $backend
*/
function __construct(CalendarAdapter $adapter,
CalDavBackend $backend
CalDavBackend $backend,
ILogger $logger,
OutputInterface $consoleOutput = null
) {
$this->adapter = $adapter;
$this->backend = $backend;
$this->logger = $logger;
$this->consoleOutput = $consoleOutput;
}
/**
@@ -82,7 +90,17 @@ class MigrateCalendars {
*/
private function migrateCalendar($calendarId, $newCalendarId) {
$this->adapter->foreachCalendarObject($calendarId, function($calObject) use ($newCalendarId) {
$this->backend->createCalendarObject($newCalendarId, $calObject['uri'], $calObject['calendardata']);
try {
$this->backend->createCalendarObject($newCalendarId, $calObject['uri'], $calObject['calendardata']);
} catch (\Exception $ex) {
$eventId = $calObject['id'];
$calendarId = $calObject['calendarId'];
$msg = "One event could not be migrated. (id: $eventId, calendarid: $calendarId)";
$this->logger->logException($ex, ['app' => 'dav', 'message' => $msg]);
if (!is_null($this->consoleOutput)) {
$this->consoleOutput->writeln($msg);
}
}
});
}

View File

@@ -22,6 +22,7 @@ namespace OCA\DAV\Tests\Unit\Migration;
use OCA\DAV\CardDAV\CardDavBackend;
use OCA\Dav\Migration\AddressBookAdapter;
use OCP\ILogger;
use Test\TestCase;
class MigrateAddressbookTest extends TestCase {
@@ -35,8 +36,10 @@ class MigrateAddressbookTest extends TestCase {
$cardDav->method('createAddressBook')->willReturn(666);
$cardDav->expects($this->once())->method('createAddressBook')->with('principals/users/test01', 'test_contacts');
$cardDav->expects($this->once())->method('createCard')->with(666, '63f0dd6c-39d5-44be-9d34-34e7a7441fc2.vcf', 'BEGIN:VCARD');
/** @var ILogger $logger */
$logger = $this->getMockBuilder('\OCP\ILogger')->disableOriginalConstructor()->getMock();
$m = new \OCA\Dav\Migration\MigrateAddressbooks($adapter, $cardDav);
$m = new \OCA\Dav\Migration\MigrateAddressbooks($adapter, $cardDav, $logger, null);
$m->migrateForUser('test01');
}

View File

@@ -22,6 +22,7 @@ namespace OCA\DAV\Tests\Unit\Migration;
use OCA\DAV\CalDAV\CalDavBackend;
use OCA\Dav\Migration\CalendarAdapter;
use OCP\ILogger;
use Test\TestCase;
class MigrateCalendarTest extends TestCase {
@@ -35,15 +36,17 @@ class MigrateCalendarTest extends TestCase {
$cardDav->method('createCalendar')->willReturn(666);
$cardDav->expects($this->once())->method('createCalendar')->with('principals/users/test01', 'test_contacts');
$cardDav->expects($this->once())->method('createCalendarObject')->with(666, '63f0dd6c-39d5-44be-9d34-34e7a7441fc2.ics', 'BEGIN:VCARD');
/** @var ILogger $logger */
$logger = $this->getMockBuilder('\OCP\ILogger')->disableOriginalConstructor()->getMock();
$m = new \OCA\Dav\Migration\MigrateCalendars($adapter, $cardDav);
$m = new \OCA\Dav\Migration\MigrateCalendars($adapter, $cardDav, $logger, null);
$m->migrateForUser('test01');
}
/**
* @return \PHPUnit_Framework_MockObject_MockObject
*/
private function mockAdapter($shares = []) {
private function mockAdapter($shares = [], $calData = 'BEGIN:VCARD') {
$adapter = $this->getMockBuilder('\OCA\Dav\Migration\CalendarAdapter')
->disableOriginalConstructor()
->getMock();
@@ -62,15 +65,14 @@ class MigrateCalendarTest extends TestCase {
'components' => 'VEVENT,VTODO,VJOURNAL'
]);
});
$adapter->method('foreachCalendarObject')->willReturnCallback(function ($addressBookId, \Closure $callBack) {
$adapter->method('foreachCalendarObject')->willReturnCallback(function ($addressBookId, \Closure $callBack) use ($calData) {
$callBack([
'userid' => $addressBookId,
'uri' => '63f0dd6c-39d5-44be-9d34-34e7a7441fc2.ics',
'calendardata' => 'BEGIN:VCARD'
'calendardata' => $calData
]);
});
$adapter->method('getShares')->willReturn($shares);
return $adapter;
}
}

View File

@@ -11,6 +11,7 @@
<dependencies>
<owncloud min-version="9.0" max-version="9.0" />
</dependencies>
<default_enable/>
<types>
<authentication/>
</types>

View File

@@ -138,7 +138,7 @@ $maxHumanFileSize = OCP\Util::humanFileSize($maxUploadFileSize);
$totalSize = 0;
$isReceivedShare = \OC::$server->getRequest()->getParam('isReceivedShare', false) === 'true';
// defer quota check for received shares
if (!$isReceivedShare) {
if (!$isReceivedShare && $storageStats['freeSpace'] >= 0) {
foreach ($files['size'] as $size) {
$totalSize += $size;
}

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>

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');

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');

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;
}

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');
}
};

View File

@@ -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();
}
}
}

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);
});
});
});

View File

@@ -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

View File

@@ -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() {

View File

@@ -54,6 +54,11 @@ class Storage extends DAV implements ISharedStorage {
*/
private $token;
/**
* @var \OCP\ICacheFactory
*/
private $memcacheFactory;
/**
* @var \OCP\ICertificateManager
*/
@@ -67,8 +72,9 @@ class Storage extends DAV implements ISharedStorage {
private $manager;
public function __construct($options) {
$this->memcacheFactory = \OC::$server->getMemCacheFactory();
$discoveryManager = new DiscoveryManager(
\OC::$server->getMemCacheFactory(),
$this->memcacheFactory,
\OC::$server->getHTTPClientService()
);
@@ -241,10 +247,21 @@ class Storage extends DAV implements ISharedStorage {
}
}
/**
* @param string $url
* @return bool
*/
private function testRemoteUrl($url) {
$cache = $this->memcacheFactory->create('files_sharing_remote_url');
if($result = $cache->get($url)) {
return (bool)$result;
}
$result = file_get_contents($url);
$data = json_decode($result);
return (is_object($data) and !empty($data->version));
$returnValue = (is_object($data) and !empty($data->version));
$cache->set($url, $returnValue);
return $returnValue;
}
/**

View File

@@ -85,6 +85,7 @@ class Shared extends \OC\Files\Storage\Common implements ISharedStorage {
}
private function isValid() {
$this->init();
return ($this->sourceRootInfo->getPermissions() & Constants::PERMISSION_SHARE) === Constants::PERMISSION_SHARE;
}
@@ -566,6 +567,7 @@ class Shared extends \OC\Files\Storage\Common implements ISharedStorage {
}
public function getCache($path = '', $storage = null) {
$this->init();
if (!$storage) {
$storage = $this;
}

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;
}

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(

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');
}
}

View File

@@ -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()
);
});
}

View File

@@ -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'],
]]);

View File

@@ -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')]]);
}
/**

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')
@@ -24,7 +24,7 @@ $(document).ready(function(){
$.ajax({
url: OC.webroot+'/updater/',
headers: {
'Authorization': loginToken
'X-Updater-Auth': loginToken
},
method: 'POST',
success: function(data){
@@ -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);
}
);
});
});

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>

View File

@@ -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())

View File

@@ -285,6 +285,7 @@ Feature: provisioning
| comments |
| dav |
| federatedfilesharing |
| federation |
| files |
| files_sharing |
| files_trashbin |

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.

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'] : '';
}
}

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

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)) {

View File

@@ -24,6 +24,7 @@ namespace OC\Files\Config;
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use OC\Files\Filesystem;
use OCA\Files_Sharing\SharedMount;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\Files\Config\ICachedMountInfo;
use OCP\Files\Config\IUserMountCache;
@@ -75,12 +76,16 @@ class UserMountCache implements IUserMountCache {
public function registerMounts(IUser $user, array $mounts) {
// filter out non-proper storages coming from unit tests
$mounts = array_filter($mounts, function (IMountPoint $mount) {
return $mount->getStorage() && $mount->getStorage()->getCache();
return $mount instanceof SharedMount || $mount->getStorage() && $mount->getStorage()->getCache();
});
/** @var ICachedMountInfo[] $newMounts */
$newMounts = array_map(function (IMountPoint $mount) use ($user) {
$storage = $mount->getStorage();
$rootId = (int)$storage->getCache()->getId('');
if ($storage->instanceOfStorage('\OC\Files\Storage\Shared')) {
$rootId = (int)$storage->getShare()['file_source'];
} else {
$rootId = (int)$storage->getCache()->getId('');
}
$storageId = (int)$storage->getStorageCache()->getNumericId();
// filter out any storages which aren't scanned yet since we aren't interested in files from those storages (yet)
if ($rootId === -1) {

View File

@@ -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'];
}

View File

@@ -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);

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;

View File

@@ -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 !== '') {

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;
}
}
}
}

View File

@@ -165,7 +165,7 @@ class OC_Util {
// install storage availability wrapper, before most other wrappers
\OC\Files\Filesystem::addStorageWrapper('oc_availability', function ($mountPoint, $storage) {
if (!$storage->isLocal()) {
if (!$storage->instanceOfStorage('\OC\Files\Storage\Shared') && !$storage->isLocal()) {
return new \OC\Files\Storage\Wrapper\Availability(['storage' => $storage]);
}
return $storage;

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.

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]);

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, 19);
// The human readable string
$OC_VersionString = '9.0.0 beta 2';
$OC_VersionString = '9.0.0';
$OC_VersionCanBeUpgradedFrom = array(8, 2);