Compare commits
45 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
81f694d83e | ||
|
|
f9a80a9c12 | ||
|
|
d63f5561fb | ||
|
|
9232a124e2 | ||
|
|
b5a06ecd5c | ||
|
|
79811b5806 | ||
|
|
6413fffdcb | ||
|
|
b3b57621b7 | ||
|
|
6bfeb4595d | ||
|
|
c1876ea51c | ||
|
|
9ec89b99b1 | ||
|
|
0945cb7a0e | ||
|
|
6f4712a314 | ||
|
|
d043b6ba91 | ||
|
|
ef66729980 | ||
|
|
71e3f7f866 | ||
|
|
0a5f34ab80 | ||
|
|
5488bb74fe | ||
|
|
4b85660984 | ||
|
|
5080c34d78 | ||
|
|
5d402fc817 | ||
|
|
fb62043cc1 | ||
|
|
b3c9ed8d5c | ||
|
|
9737290e39 | ||
|
|
121ff350d4 | ||
|
|
bb0c304482 | ||
|
|
c9c85b8d4a | ||
|
|
eb59aa8be4 | ||
|
|
96d45e90dc | ||
|
|
0655f25406 | ||
|
|
434747f450 | ||
|
|
7ff2b9232b | ||
|
|
3d28f364c5 | ||
|
|
62399f7852 | ||
|
|
4fc6deaaf0 | ||
|
|
e6c6ee8d2a | ||
|
|
4da858b3b7 | ||
|
|
8e8f5cdddf | ||
|
|
f603c57751 | ||
|
|
e155f28f5e | ||
|
|
750fc9ae26 | ||
|
|
4186bcbdf2 | ||
|
|
98f79173ed | ||
|
|
8d07cb4d85 | ||
|
|
445957a0e2 |
@@ -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
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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/>
|
||||
|
||||
@@ -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')));
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
<dependencies>
|
||||
<owncloud min-version="9.0" max-version="9.0" />
|
||||
</dependencies>
|
||||
<default_enable/>
|
||||
<types>
|
||||
<authentication/>
|
||||
</types>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
57
apps/files/lib/backgroundjob/cleanupfilelocks.php
Normal file
57
apps/files/lib/backgroundjob/cleanupfilelocks.php
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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() {
|
||||
|
||||
21
apps/files_sharing/lib/external/storage.php
vendored
21
apps/files_sharing/lib/external/storage.php
vendored
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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')]]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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'] : '';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 !== '') {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
@@ -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]);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user