diff options
author | Roeland Douma <rullzer@users.noreply.github.com> | 2015-09-06 16:56:35 +0200 |
---|---|---|
committer | Roeland Douma <rullzer@users.noreply.github.com> | 2015-09-06 16:56:35 +0200 |
commit | 24f5f50b20c4f49bcd602a3c322f2ee2deb0f95b (patch) | |
tree | b5ba1876566dfa1cc39341e8f58e419bc137d722 | |
parent | 3642fb701a7fc0caba779de9f5d53bf12c27f5aa (diff) | |
parent | c6314fc699c7316973fa79f75b7d585e620323e9 (diff) | |
download | nextcloud-server-24f5f50b20c4f49bcd602a3c322f2ee2deb0f95b.tar.gz nextcloud-server-24f5f50b20c4f49bcd602a3c322f2ee2deb0f95b.zip |
Merge pull request #18742 from owncloud/mimetype-updatedb
Introduce mimetype DB update occ command
-rw-r--r-- | apps/files_sharing/lib/cache.php | 6 | ||||
-rw-r--r-- | core/command/maintenance/mimetype/updatedb.php | 96 | ||||
-rw-r--r-- | core/command/maintenance/mimetype/updatejs.php (renamed from core/command/maintenance/mimetypesjs.php) | 43 | ||||
-rw-r--r-- | core/register_command.php | 3 | ||||
-rw-r--r-- | lib/private/files/cache/cache.php | 96 | ||||
-rw-r--r-- | lib/private/files/type/detection.php | 30 | ||||
-rw-r--r-- | lib/private/files/type/loader.php | 165 | ||||
-rw-r--r-- | lib/private/server.php | 14 | ||||
-rw-r--r-- | lib/public/files/imimetypeloader.php | 59 | ||||
-rw-r--r-- | lib/public/iservercontainer.php | 8 | ||||
-rw-r--r-- | tests/core/command/maintenance/mimetype/updatedbtest.php | 184 | ||||
-rw-r--r-- | tests/lib/files/type/loadertest.php | 93 | ||||
-rw-r--r-- | tests/lib/repair/repairmimetypes.php | 29 |
13 files changed, 702 insertions, 124 deletions
diff --git a/apps/files_sharing/lib/cache.php b/apps/files_sharing/lib/cache.php index c25dc92409f..377c9f02253 100644 --- a/apps/files_sharing/lib/cache.php +++ b/apps/files_sharing/lib/cache.php @@ -47,6 +47,7 @@ class Shared_Cache extends Cache { * @param \OC\Files\Storage\Shared $storage */ public function __construct($storage) { + parent::__construct($storage); $this->storage = $storage; } @@ -94,6 +95,7 @@ class Shared_Cache extends Cache { * @return array|false */ public function get($file) { + $mimetypeLoader = \OC::$server->getMimeTypeLoader(); if (is_string($file)) { $cache = $this->getSourceCache($file); if ($cache) { @@ -130,8 +132,8 @@ class Shared_Cache extends Cache { $data['mtime'] = (int)$data['mtime']; $data['storage_mtime'] = (int)$data['storage_mtime']; $data['encrypted'] = (bool)$data['encrypted']; - $data['mimetype'] = $this->getMimetype($data['mimetype']); - $data['mimepart'] = $this->getMimetype($data['mimepart']); + $data['mimetype'] = $mimetypeLoader->getMimetypeById($data['mimetype']); + $data['mimepart'] = $mimetypeLoader->getMimetypeById($data['mimepart']); if ($data['storage_mtime'] === 0) { $data['storage_mtime'] = $data['mtime']; } diff --git a/core/command/maintenance/mimetype/updatedb.php b/core/command/maintenance/mimetype/updatedb.php new file mode 100644 index 00000000000..37c401c0338 --- /dev/null +++ b/core/command/maintenance/mimetype/updatedb.php @@ -0,0 +1,96 @@ +<?php +/** + * @author Robin McCorkell <rmccorkell@owncloud.com> + * + * @copyright Copyright (c) 2015, 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 OC\Core\Command\Maintenance\Mimetype; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputOption; + +use OCP\Files\IMimeTypeDetector; +use OCP\Files\IMimeTypeLoader; + +class UpdateDB extends Command { + + const DEFAULT_MIMETYPE = 'application/octet-stream'; + + /** @var IMimeTypeDetector */ + protected $mimetypeDetector; + + /** @var IMimeTypeLoader */ + protected $mimetypeLoader; + + public function __construct( + IMimeTypeDetector $mimetypeDetector, + IMimeTypeLoader $mimetypeLoader + ) { + parent::__construct(); + $this->mimetypeDetector = $mimetypeDetector; + $this->mimetypeLoader = $mimetypeLoader; + } + + protected function configure() { + $this + ->setName('maintenance:mimetype:update-db') + ->setDescription('Update database mimetypes and update filecache') + ->addOption( + 'repair-filecache', + null, + InputOption::VALUE_NONE, + 'Repair filecache for all mimetypes, not just new ones' + ) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) { + $mappings = $this->mimetypeDetector->getAllMappings(); + + $totalFilecacheUpdates = 0; + $totalNewMimetypes = 0; + + foreach ($mappings as $ext => $mimetypes) { + if ($ext[0] === '_') { + // comment + continue; + } + $mimetype = $mimetypes[0]; + $existing = $this->mimetypeLoader->exists($mimetype); + // this will add the mimetype if it didn't exist + $mimetypeId = $this->mimetypeLoader->getId($mimetype); + + if (!$existing) { + $output->writeln('Added mimetype "'.$mimetype.'" to database'); + $totalNewMimetypes++; + } + + if (!$existing || $input->getOption('repair-filecache')) { + $touchedFilecacheRows = $this->mimetypeLoader->updateFilecache($ext, $mimetypeId); + if ($touchedFilecacheRows > 0) { + $output->writeln('Updated '.$touchedFilecacheRows.' filecache rows for mimetype "'.$mimetype.'"'); + } + $totalFilecacheUpdates += $touchedFilecacheRows; + } + } + + $output->writeln('Added '.$totalNewMimetypes.' new mimetypes'); + $output->writeln('Updated '.$totalFilecacheUpdates.' filecache rows'); + } +} diff --git a/core/command/maintenance/mimetypesjs.php b/core/command/maintenance/mimetype/updatejs.php index 8b01f0acf79..5de75d53a3f 100644 --- a/core/command/maintenance/mimetypesjs.php +++ b/core/command/maintenance/mimetype/updatejs.php @@ -18,27 +18,35 @@ * */ -namespace OC\Core\Command\Maintenance; +namespace OC\Core\Command\Maintenance\Mimetype; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; -class MimeTypesJS extends Command { +use OCP\Files\IMimeTypeDetector; + +class UpdateJS extends Command { + + /** @var IMimeTypeDetector */ + protected $mimetypeDetector; + + public function __construct( + IMimeTypeDetector $mimetypeDetector + ) { + parent::__construct(); + $this->mimetypeDetector = $mimetypeDetector; + } + protected function configure() { $this - ->setName('maintenance:mimetypesjs') + ->setName('maintenance:mimetype:update-js') ->setDescription('Update mimetypelist.js'); } protected function execute(InputInterface $input, OutputInterface $output) { // Fetch all the aliases - $aliases = json_decode(file_get_contents(\OC::$SERVERROOT . '/config/mimetypealiases.dist.json'), true); - - if (file_exists(\OC::$SERVERROOT . '/config/mimetypealiases.json')) { - $custom = get_object_vars(json_decode(file_get_contents(\OC::$SERVERROOT . '/config/mimetypealiases.json'))); - $aliases = array_merge($aliases, $custom); - } + $aliases = $this->mimetypeDetector->getAllAliases(); // Remove comments $keys = array_filter(array_keys($aliases), function($k) { @@ -49,23 +57,22 @@ class MimeTypesJS extends Command { } // Fetch all files - $dir = new \DirectoryIterator(dirname(__DIR__) . '/../img/filetypes'); + $dir = new \DirectoryIterator(\OC::$SERVERROOT.'/core/img/filetypes'); $files = []; foreach($dir as $fileInfo) { - if ($fileInfo->isFile()) { - $file = preg_replace('/.[^.]*$/', '', $fileInfo->getFilename()); - $files[] = $file; - } + if ($fileInfo->isFile()) { + $file = preg_replace('/.[^.]*$/', '', $fileInfo->getFilename()); + $files[] = $file; + } } //Remove duplicates $files = array_values(array_unique($files)); - // Fetch all themes! $themes = []; - $dirs = new \DirectoryIterator(dirname(__DIR__) . '/../../themes/'); + $dirs = new \DirectoryIterator(\OC::$SERVERROOT.'/themes/'); foreach($dirs as $dir) { //Valid theme dir if ($dir->isFile() || $dir->isDot()) { @@ -84,7 +91,7 @@ class MimeTypesJS extends Command { $themeIt = new \DirectoryIterator($themeDir); foreach ($themeIt as $fileInfo) { if ($fileInfo->isFile()) { - $file = preg_replace('/.[^.]*$/', '', $fileInfo->getFilename()); + $file = preg_replace('/.[^.]*$/', '', $fileInfo->getFilename()); $themes[$theme][] = $file; } } @@ -110,7 +117,7 @@ OC.MimeTypeList={ '; //Output the JS - file_put_contents(dirname(__DIR__) . '/../js/mimetypelist.js', $js); + file_put_contents(\OC::$SERVERROOT.'/core/js/mimetypelist.js', $js); $output->writeln('<info>mimetypelist.js is updated'); } diff --git a/core/register_command.php b/core/register_command.php index 72c7b28e9ae..d3c04ad0671 100644 --- a/core/register_command.php +++ b/core/register_command.php @@ -79,7 +79,8 @@ if (\OC::$server->getConfig()->getSystemValue('installed', false)) { ); $application->add(new OC\Core\Command\Encryption\ShowKeyStorageRoot($util)); - $application->add(new OC\Core\Command\Maintenance\MimeTypesJS()); + $application->add(new OC\Core\Command\Maintenance\Mimetype\UpdateDB(\OC::$server->getMimeTypeDetector(), \OC::$server->getMimeTypeLoader())); + $application->add(new OC\Core\Command\Maintenance\Mimetype\UpdateJS(\OC::$server->getMimeTypeDetector())); $application->add(new OC\Core\Command\Maintenance\Mode(\OC::$server->getConfig())); $application->add(new OC\Core\Command\Maintenance\Repair(new \OC\Repair(\OC\Repair::getRepairSteps()), \OC::$server->getConfig())); $application->add(new OC\Core\Command\Maintenance\SingleUser(\OC::$server->getConfig())); diff --git a/lib/private/files/cache/cache.php b/lib/private/files/cache/cache.php index 8cf097421d4..5c04da1f0d5 100644 --- a/lib/private/files/cache/cache.php +++ b/lib/private/files/cache/cache.php @@ -35,6 +35,8 @@ namespace OC\Files\Cache; +use \OCP\Files\IMimeTypeLoader; + /** * Metadata cache for a storage * @@ -66,8 +68,8 @@ class Cache { */ protected $storageCache; - protected static $mimetypeIds = array(); - protected static $mimetypes = array(); + /** @var IMimeTypeLoader */ + protected $mimetypeLoader; /** * @param \OC\Files\Storage\Storage|string $storage @@ -83,6 +85,7 @@ class Cache { } $this->storageCache = new Storage($storage); + $this->mimetypeLoader = \OC::$server->getMimeTypeLoader(); } /** @@ -95,72 +98,6 @@ class Cache { } /** - * Get the numeric id for a mimetype - * - * Mimetypes are stored as integers in the cache to prevent duplicated data of the (usually) fairly limited amount of unique mimetypes - * If the supplied mimetype does not yet have a numeric id a new one will be generated - * - * @param string $mime - * @return int - */ - public function getMimetypeId($mime) { - if (empty($mime)) { - // Can not insert empty string into Oracle NOT NULL column. - $mime = 'application/octet-stream'; - } - if (empty(self::$mimetypeIds)) { - $this->loadMimetypes(); - } - - if (!isset(self::$mimetypeIds[$mime])) { - try { - $connection = \OC_DB::getConnection(); - $connection->insertIfNotExist('*PREFIX*mimetypes', [ - 'mimetype' => $mime, - ]); - $this->loadMimetypes(); - } catch (\Doctrine\DBAL\DBALException $e) { - \OCP\Util::writeLog('core', 'Exception during mimetype insertion: ' . $e->getmessage(), \OCP\Util::DEBUG); - return -1; - } - } - - return self::$mimetypeIds[$mime]; - } - - - /** - * Get the mimetype (as string) from a mimetype id - * - * @param int $id - * @return string | null the mimetype for the id or null if the id is not known - */ - public function getMimetype($id) { - if (empty(self::$mimetypes)) { - $this->loadMimetypes(); - } - - return isset(self::$mimetypes[$id]) ? self::$mimetypes[$id] : null; - } - - /** - * Load all known mimetypes and mimetype ids from the database - * - * @throws \OC\DatabaseException - */ - public function loadMimetypes() { - self::$mimetypeIds = self::$mimetypes = array(); - - $result = \OC_DB::executeAudited('SELECT `id`, `mimetype` FROM `*PREFIX*mimetypes`', array()); - if ($result) { - while ($row = $result->fetchRow()) { - self::$mimetypeIds[$row['mimetype']] = $row['id']; - self::$mimetypes[$row['id']] = $row['mimetype']; - } - } - } - - /** * get the stored metadata of a file or folder * * the returned cache entry contains at least the following values: @@ -222,8 +159,8 @@ class Cache { $data['storage_mtime'] = (int)$data['storage_mtime']; $data['encrypted'] = (bool)$data['encrypted']; $data['storage'] = $this->storageId; - $data['mimetype'] = $this->getMimetype($data['mimetype']); - $data['mimepart'] = $this->getMimetype($data['mimepart']); + $data['mimetype'] = $this->mimetypeLoader->getMimetypeById($data['mimetype']); + $data['mimepart'] = $this->mimetypeLoader->getMimetypeById($data['mimepart']); if ($data['storage_mtime'] == 0) { $data['storage_mtime'] = $data['mtime']; } @@ -258,8 +195,8 @@ class Cache { $result = \OC_DB::executeAudited($sql, array($fileId)); $files = $result->fetchAll(); foreach ($files as &$file) { - $file['mimetype'] = $this->getMimetype($file['mimetype']); - $file['mimepart'] = $this->getMimetype($file['mimepart']); + $file['mimetype'] = $this->mimetypeLoader->getMimetypeById($file['mimetype']); + $file['mimepart'] = $this->mimetypeLoader->getMimetypeById($file['mimepart']); if ($file['storage_mtime'] == 0) { $file['storage_mtime'] = $file['mtime']; } @@ -385,9 +322,9 @@ class Cache { $params[] = md5($value); $queryParts[] = '`path_hash`'; } elseif ($name === 'mimetype') { - $params[] = $this->getMimetypeId(substr($value, 0, strpos($value, '/'))); + $params[] = $this->mimetypeLoader->getId(substr($value, 0, strpos($value, '/'))); $queryParts[] = '`mimepart`'; - $value = $this->getMimetypeId($value); + $value = $this->mimetypeLoader->getId($value); } elseif ($name === 'storage_mtime') { if (!isset($data['mtime'])) { $params[] = $value; @@ -613,7 +550,6 @@ class Cache { * @return array an array of cache entries where the name matches the search pattern */ public function search($pattern) { - // normalize pattern $pattern = $this->normalize($pattern); @@ -630,8 +566,8 @@ class Cache { $files = array(); while ($row = $result->fetchRow()) { - $row['mimetype'] = $this->getMimetype($row['mimetype']); - $row['mimepart'] = $this->getMimetype($row['mimepart']); + $row['mimetype'] = $this->mimetypeLoader->getMimetypeById($row['mimetype']); + $row['mimepart'] = $this->mimetypeLoader->getMimetypeById($row['mimepart']); $files[] = $row; } return $files; @@ -652,12 +588,12 @@ class Cache { } $sql = 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted`, `etag`, `permissions` FROM `*PREFIX*filecache` WHERE ' . $where . ' AND `storage` = ?'; - $mimetype = $this->getMimetypeId($mimetype); + $mimetype = $this->mimetypeLoader->getId($mimetype); $result = \OC_DB::executeAudited($sql, array($mimetype, $this->getNumericStorageId())); $files = array(); while ($row = $result->fetchRow()) { - $row['mimetype'] = $this->getMimetype($row['mimetype']); - $row['mimepart'] = $this->getMimetype($row['mimepart']); + $row['mimetype'] = $this->mimetypeLoader->getMimetypeById($row['mimetype']); + $row['mimepart'] = $this->mimetypeLoader->getMimetypeById($row['mimepart']); $files[] = $row; } return $files; diff --git a/lib/private/files/type/detection.php b/lib/private/files/type/detection.php index ba286637df3..3dc3975fb2a 100644 --- a/lib/private/files/type/detection.php +++ b/lib/private/files/type/detection.php @@ -6,6 +6,7 @@ * @author Robin Appelman <icewind@owncloud.com> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Tanghus <thomas@tanghus.net> + * @author Robin McCorkell <rmccorkell@owncloud.com> * * @copyright Copyright (c) 2015, ownCloud, Inc. * @license AGPL-3.0 @@ -101,16 +102,23 @@ class Detection implements IMimeTypeDetector { return; } - $file = file_get_contents($this->configDir . '/mimetypealiases.dist.json'); - $this->mimeTypeAlias = get_object_vars(json_decode($file)); + $this->mimeTypeAlias = json_decode(file_get_contents($this->configDir . '/mimetypealiases.dist.json'), true); if (file_exists($this->configDir . '/mimetypealiases.json')) { - $custom = get_object_vars(json_decode(file_get_contents($this->configDir . '/mimetypealiases.json'))); + $custom = json_decode(file_get_contents($this->configDir . '/mimetypealiases.json'), true); $this->mimeTypeAlias = array_merge($this->mimeTypeAlias, $custom); } } /** + * @return array + */ + public function getAllAliases() { + $this->loadAliases(); + return $this->mimeTypeAlias; + } + + /** * Add mimetype mappings if they are not yet present */ private function loadMappings() { @@ -118,20 +126,26 @@ class Detection implements IMimeTypeDetector { return; } - $dist = file_get_contents($this->configDir . '/mimetypemapping.dist.json'); - $mimetypemapping = get_object_vars(json_decode($dist)); + $mimetypemapping = json_decode(file_get_contents($this->configDir . '/mimetypemapping.dist.json'), true); //Check if need to load custom mappings if (file_exists($this->configDir . '/mimetypemapping.json')) { - $custom = file_get_contents($this->configDir . '/mimetypemapping.json'); - $custom_mapping = get_object_vars(json_decode($custom)); - $mimetypemapping = array_merge($mimetypemapping, $custom_mapping); + $custom = json_decode(file_get_contents($this->configDir . '/mimetypemapping.json'), true); + $mimetypemapping = array_merge($mimetypemapping, $custom); } $this->registerTypeArray($mimetypemapping); } /** + * @return array + */ + public function getAllMappings() { + $this->loadMappings(); + return $this->mimetypes; + } + + /** * detect mimetype only based on filename, content of file is not used * * @param string $path diff --git a/lib/private/files/type/loader.php b/lib/private/files/type/loader.php new file mode 100644 index 00000000000..df893306615 --- /dev/null +++ b/lib/private/files/type/loader.php @@ -0,0 +1,165 @@ +<?php +/** + * @author Robin McCorkell <rmccorkell@owncloud.com> + * + * @copyright Copyright (c) 2015, 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 OC\Files\Type; + +use OCP\Files\IMimeTypeLoader; +use OCP\IDBConnection; + +use Doctrine\DBAL\Exception\UniqueConstraintViolationException; + +/** + * Mimetype database loader + * + * @package OC\Files\Type + */ +class Loader implements IMimeTypeLoader { + + /** @var IDBConnection */ + private $dbConnection; + + /** @var array [id => mimetype] */ + protected $mimetypes; + + /** @var array [mimetype => id] */ + protected $mimetypeIds; + + /** + * @param IDBConnection $dbConnection + */ + public function __construct(IDBConnection $dbConnection) { + $this->dbConnection = $dbConnection; + $this->mimetypes = []; + $this->mimetypeIds = []; + } + + /** + * Get a mimetype from its ID + * + * @param int $id + * @return string|null + */ + public function getMimetypeById($id) { + if (!$this->mimetypes) { + $this->loadMimetypes(); + } + if (isset($this->mimetypes[$id])) { + return $this->mimetypes[$id]; + } + return null; + } + + /** + * Get a mimetype ID, adding the mimetype to the DB if it does not exist + * + * @param string $mimetype + * @return int + */ + public function getId($mimetype) { + if (!$this->mimetypeIds) { + $this->loadMimetypes(); + } + if (isset($this->mimetypeIds[$mimetype])) { + return $this->mimetypeIds[$mimetype]; + } + return $this->store($mimetype); + } + + /** + * Test if a mimetype exists in the database + * + * @param string $mimetype + * @return bool + */ + public function exists($mimetype) { + if (!$this->mimetypeIds) { + $this->loadMimetypes(); + } + return isset($this->mimetypeIds[$mimetype]); + } + + /** + * Store a mimetype in the DB + * + * @param string $mimetype + * @param int inserted ID + */ + protected function store($mimetype) { + try { + $qb = $this->dbConnection->getQueryBuilder(); + $qb->insert('mimetypes') + ->values([ + 'mimetype' => $qb->createNamedParameter($mimetype) + ]); + $qb->execute(); + } catch (UniqueConstraintViolationException $e) { + // something inserted it before us + } + + $fetch = $this->dbConnection->getQueryBuilder(); + $fetch->select('id') + ->from('mimetypes') + ->where( + $fetch->expr()->eq('mimetype', $fetch->createNamedParameter($mimetype) + )); + $row = $fetch->execute()->fetch(); + + $this->mimetypes[$row['id']] = $mimetype; + $this->mimetypeIds[$mimetype] = $row['id']; + return $row['id']; + } + + /** + * Load all mimetypes from DB + */ + private function loadMimetypes() { + $qb = $this->dbConnection->getQueryBuilder(); + $qb->select('id', 'mimetype') + ->from('mimetypes'); + $results = $qb->execute()->fetchAll(); + + foreach ($results as $row) { + $this->mimetypes[$row['id']] = $row['mimetype']; + $this->mimetypeIds[$row['mimetype']] = $row['id']; + } + } + + /** + * Update filecache mimetype based on file extension + * + * @param string $ext file extension + * @param int $mimetypeId + * @return int number of changed rows + */ + public function updateFilecache($ext, $mimetypeId) { + $update = $this->dbConnection->getQueryBuilder(); + $update->update('filecache') + ->set('mimetype', $update->createNamedParameter($mimetypeId)) + ->where($update->expr()->neq( + 'mimetype', $update->createNamedParameter($mimetypeId) + )) + ->andWhere($update->expr()->like( + $update->createFunction('LOWER(`name`)'), $update->createNamedParameter($ext) + )); + return $update->execute(); + } + +} diff --git a/lib/private/server.php b/lib/private/server.php index 24674d2e3c7..393c1840973 100644 --- a/lib/private/server.php +++ b/lib/private/server.php @@ -470,6 +470,11 @@ class Server extends SimpleContainer implements IServerContainer { $c->getURLGenerator(), \OC::$configDir); }); + $this->registerService('MimeTypeLoader', function(Server $c) { + return new \OC\Files\Type\Loader( + $c->getDatabaseConnection() + ); + }); $this->registerService('CapabilitiesManager', function (Server $c) { $manager = new \OC\CapabilitiesManager(); $manager->registerCapability(function() use ($c) { @@ -1011,6 +1016,15 @@ class Server extends SimpleContainer implements IServerContainer { } /** + * Get the MimeTypeLoader + * + * @return \OCP\Files\IMimeTypeLoader + */ + public function getMimeTypeLoader() { + return $this->query('MimeTypeLoader'); + } + + /** * Get the manager of all the capabilities * * @return \OC\CapabilitiesManager diff --git a/lib/public/files/imimetypeloader.php b/lib/public/files/imimetypeloader.php new file mode 100644 index 00000000000..24937ea9b86 --- /dev/null +++ b/lib/public/files/imimetypeloader.php @@ -0,0 +1,59 @@ +<?php +/** + * @author Robin McCorkell <rmccorkell@owncloud.com> + * + * @copyright Copyright (c) 2015, 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 OCP\Files; + +/** + * Interface IMimeTypeLoader + * @package OCP\Files + * @since 8.2.0 + * + * Interface to load mimetypes + **/ +interface IMimeTypeLoader { + + /** + * Get a mimetype from its ID + * + * @param int $id + * @return string|null + * @since 8.2.0 + */ + public function getMimetypeById($id); + + /** + * Get a mimetype ID, adding the mimetype to the DB if it does not exist + * + * @param string $mimetype + * @return int + * @since 8.2.0 + */ + public function getId($mimetype); + + /** + * Test if a mimetype exists in the database + * + * @param string $mimetype + * @return bool + * @since 8.2.0 + */ + public function exists($mimetype); +} diff --git a/lib/public/iservercontainer.php b/lib/public/iservercontainer.php index a6d83156de3..81724cb4967 100644 --- a/lib/public/iservercontainer.php +++ b/lib/public/iservercontainer.php @@ -440,6 +440,14 @@ interface IServerContainer { */ public function getMimeTypeDetector(); + /** + * Get the MimeTypeLoader + * + * @return \OCP\Files\IMimeTypeLoader + * @since 8.2.0 + */ + public function getMimeTypeLoader(); + /** * Get the EventDispatcher diff --git a/tests/core/command/maintenance/mimetype/updatedbtest.php b/tests/core/command/maintenance/mimetype/updatedbtest.php new file mode 100644 index 00000000000..217301102c5 --- /dev/null +++ b/tests/core/command/maintenance/mimetype/updatedbtest.php @@ -0,0 +1,184 @@ +<?php +/** + * @author Robin McCorkell <rmccorkell@owncloud.com> + * + * @copyright Copyright (c) 2015, 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 Tests\Core\Command\Maintenance\Mimetype; + +use OC\Core\Command\Maintenance\Mimetype\UpdateDB; +use Test\TestCase; +use OCP\Files\IMimeTypeDetector; +use OCP\Files\IMimeTypeLoader; + +class UpdateDBTest extends TestCase { + /** @var IMimeTypeDetector */ + protected $detector; + /** @var IMimeTypeLoader */ + protected $loader; + + /** @var \PHPUnit_Framework_MockObject_MockObject */ + protected $consoleInput; + /** @var \PHPUnit_Framework_MockObject_MockObject */ + protected $consoleOutput; + + /** @var \Symfony\Component\Console\Command\Command */ + protected $command; + + protected function setUp() { + parent::setUp(); + + $this->detector = $this->getMockBuilder('OC\Files\Type\Detection') + ->disableOriginalConstructor() + ->getMock(); + $this->loader = $this->getMockBuilder('OC\Files\Type\Loader') + ->disableOriginalConstructor() + ->getMock(); + + $this->consoleInput = $this->getMock('Symfony\Component\Console\Input\InputInterface'); + $this->consoleOutput = $this->getMock('Symfony\Component\Console\Output\OutputInterface'); + + $this->command = new UpdateDB($this->detector, $this->loader); + } + + public function testNoop() { + $this->consoleInput->method('getOption') + ->with('repair-filecache') + ->willReturn(false); + + $this->detector->expects($this->once()) + ->method('getAllMappings') + ->willReturn([ + 'ext' => ['testing/existingmimetype'] + ]); + $this->loader->expects($this->once()) + ->method('exists') + ->with('testing/existingmimetype') + ->willReturn(true); + + $this->loader->expects($this->never()) + ->method('updateFilecache'); + + $this->consoleOutput->expects($this->at(0)) + ->method('writeln') + ->with('Added 0 new mimetypes'); + $this->consoleOutput->expects($this->at(1)) + ->method('writeln') + ->with('Updated 0 filecache rows'); + + self::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]); + } + + public function testAddMimetype() { + $this->consoleInput->method('getOption') + ->with('repair-filecache') + ->willReturn(false); + + $this->detector->expects($this->once()) + ->method('getAllMappings') + ->willReturn([ + 'ext' => ['testing/existingmimetype'], + 'new' => ['testing/newmimetype'] + ]); + $this->loader->expects($this->exactly(2)) + ->method('exists') + ->will($this->returnValueMap([ + ['testing/existingmimetype', true], + ['testing/newmimetype', false], + ])); + $this->loader->expects($this->exactly(2)) + ->method('getId') + ->will($this->returnValueMap([ + ['testing/existingmimetype', 1], + ['testing/newmimetype', 2], + ])); + + $this->loader->expects($this->once()) + ->method('updateFilecache') + ->with('new', 2) + ->willReturn(3); + + $this->consoleOutput->expects($this->at(0)) + ->method('writeln') + ->with('Added mimetype "testing/newmimetype" to database'); + $this->consoleOutput->expects($this->at(1)) + ->method('writeln') + ->with('Updated 3 filecache rows for mimetype "testing/newmimetype"'); + + $this->consoleOutput->expects($this->at(2)) + ->method('writeln') + ->with('Added 1 new mimetypes'); + $this->consoleOutput->expects($this->at(3)) + ->method('writeln') + ->with('Updated 3 filecache rows'); + + self::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]); + } + + public function testSkipComments() { + $this->detector->expects($this->once()) + ->method('getAllMappings') + ->willReturn([ + '_comment' => 'some comment in the JSON' + ]); + $this->loader->expects($this->never()) + ->method('exists'); + + self::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]); + } + + public function testRepairFilecache() { + $this->consoleInput->method('getOption') + ->with('repair-filecache') + ->willReturn(true); + + $this->detector->expects($this->once()) + ->method('getAllMappings') + ->willReturn([ + 'ext' => ['testing/existingmimetype'], + ]); + $this->loader->expects($this->exactly(1)) + ->method('exists') + ->will($this->returnValueMap([ + ['testing/existingmimetype', true], + ])); + $this->loader->expects($this->exactly(1)) + ->method('getId') + ->will($this->returnValueMap([ + ['testing/existingmimetype', 1], + ])); + + $this->loader->expects($this->once()) + ->method('updateFilecache') + ->with('ext', 1) + ->willReturn(3); + + $this->consoleOutput->expects($this->at(0)) + ->method('writeln') + ->with('Updated 3 filecache rows for mimetype "testing/existingmimetype"'); + + $this->consoleOutput->expects($this->at(1)) + ->method('writeln') + ->with('Added 0 new mimetypes'); + $this->consoleOutput->expects($this->at(2)) + ->method('writeln') + ->with('Updated 3 filecache rows'); + + self::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]); + } +} diff --git a/tests/lib/files/type/loadertest.php b/tests/lib/files/type/loadertest.php new file mode 100644 index 00000000000..7f87afd2f4d --- /dev/null +++ b/tests/lib/files/type/loadertest.php @@ -0,0 +1,93 @@ +<?php +/** + * @author Robin McCorkell <rmccorkell@owncloud.com> + * + * @copyright Copyright (c) 2015, 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 OC\Files\Type; + +use \OC\Files\Type\Loader; +use \OCP\IDBConnection; + +class LoaderTest extends \Test\TestCase { + /** @var IDBConnection */ + protected $db; + /** @var Loader */ + protected $loader; + + protected function setUp() { + $this->db = \OC::$server->getDatabaseConnection(); + $this->loader = new Loader($this->db); + } + + protected function tearDown() { + $deleteMimetypes = $this->db->getQueryBuilder(); + $deleteMimetypes->delete('mimetypes') + ->where($deleteMimetypes->expr()->like( + 'mimetype', $deleteMimetypes->createPositionalParameter('testing/%') + )); + $deleteMimetypes->execute(); + } + + + public function testGetMimetype() { + $qb = $this->db->getQueryBuilder(); + $qb->insert('mimetypes') + ->values([ + 'mimetype' => $qb->createPositionalParameter('testing/mymimetype') + ]); + $qb->execute(); + + $this->assertTrue($this->loader->exists('testing/mymimetype')); + $mimetypeId = $this->loader->getId('testing/mymimetype'); + $this->assertNotNull($mimetypeId); + + $mimetype = $this->loader->getMimetypeById($mimetypeId); + $this->assertEquals('testing/mymimetype', $mimetype); + } + + public function testGetNonexistentMimetype() { + $this->assertFalse($this->loader->exists('testing/nonexistent')); + // hopefully this ID doesn't exist + $this->assertNull($this->loader->getMimetypeById(12345)); + } + + public function testStore() { + $this->assertFalse($this->loader->exists('testing/mymimetype')); + $mimetypeId = $this->loader->getId('testing/mymimetype'); + + $qb = $this->db->getQueryBuilder(); + $qb->select('mimetype') + ->from('mimetypes') + ->where($qb->expr()->eq('id', $qb->createPositionalParameter($mimetypeId))); + + $mimetype = $qb->execute()->fetch(); + $this->assertEquals('testing/mymimetype', $mimetype['mimetype']); + + $this->assertEquals('testing/mymimetype', $this->loader->getMimetypeById($mimetypeId)); + $this->assertEquals($mimetypeId, $this->loader->getId('testing/mymimetype')); + } + + public function testStoreExists() { + $mimetypeId = $this->loader->getId('testing/mymimetype'); + $mimetypeId2 = $this->loader->getId('testing/mymimetype'); + + $this->assertEquals($mimetypeId, $mimetypeId2); + } + +} diff --git a/tests/lib/repair/repairmimetypes.php b/tests/lib/repair/repairmimetypes.php index 40ffc14612b..76d4d7cc656 100644 --- a/tests/lib/repair/repairmimetypes.php +++ b/tests/lib/repair/repairmimetypes.php @@ -22,8 +22,17 @@ class RepairMimeTypes extends \Test\TestCase { protected function setUp() { parent::setUp(); - $this->storage = new \OC\Files\Storage\Temporary([]); + $this->savedMimetypeLoader = \OC::$server->getMimeTypeLoader(); + $this->mimetypeLoader = $this->getMockBuilder('\OC\Files\Type\Loader') + ->setConstructorArgs([\OC::$server->getDatabaseConnection()]) + ->setMethods(null) + ->getMock(); + \OC::$server->registerService('MimeTypeLoader', function ($c) { + return $this->mimetypeLoader; + }); + + $this->storage = new \OC\Files\Storage\Temporary([]); $this->repair = new \OC\Repair\RepairMimeTypes(); } @@ -33,7 +42,9 @@ class RepairMimeTypes extends \Test\TestCase { \OC_DB::executeAudited($sql, [$this->storage->getId()]); $this->clearMimeTypes(); - DummyFileCache::clearCachedMimeTypes(); + \OC::$server->registerService('MimeTypeLoader', function($c) { + return $this->savedMimetypeLoader; + }); parent::tearDown(); } @@ -86,8 +97,7 @@ class RepairMimeTypes extends \Test\TestCase { $this->repair->run(); // force mimetype reload - DummyFileCache::clearCachedMimeTypes(); - $this->storage->getCache()->loadMimeTypes(); + self::invokePrivate($this->mimetypeLoader, 'loadMimetypes'); $this->checkEntries($fixedMimeTypes); } @@ -434,14 +444,3 @@ class RepairMimeTypes extends \Test\TestCase { } } -/** - * Dummy class to access protected members - */ -class DummyFileCache extends \OC\Files\Cache\Cache { - - public static function clearCachedMimeTypes() { - self::$mimetypeIds = []; - self::$mimetypes = []; - } -} - |