Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 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 |
@@ -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/>
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
<dependencies>
|
||||
<owncloud min-version="9.0" max-version="9.0" />
|
||||
</dependencies>
|
||||
<default_enable/>
|
||||
<types>
|
||||
<authentication/>
|
||||
</types>
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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')
|
||||
@@ -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.
|
||||
|
||||
+8
-3
@@ -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)) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -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]);
|
||||
|
||||
+2
-2
@@ -26,10 +26,10 @@
|
||||
// We only can count up. The 4. digit is only for the internal patchlevel to trigger DB upgrades
|
||||
// between betas, final and RCs. This is _not_ the public version number. Reset minor/patchlevel
|
||||
// when updating major/minor version number.
|
||||
$OC_Version = array(9, 0, 0, 15);
|
||||
$OC_Version = array(9, 0, 0, 17);
|
||||
|
||||
// The human readable string
|
||||
$OC_VersionString = '9.0.0 beta 2';
|
||||
$OC_VersionString = '9.0.0 RC2';
|
||||
|
||||
$OC_VersionCanBeUpgradedFrom = array(8, 2);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user