Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8d27561d96 | |||
| c3354e9c84 | |||
| 5d744456f6 | |||
| 4e840fb17a | |||
| ae4a64b724 | |||
| 61df8fa3ea | |||
| 250bb12572 | |||
| ea9f2361ae | |||
| d270561ef8 | |||
| ead63a067e | |||
| a5ba81a250 | |||
| e5ec9d6a7b |
@@ -22,6 +22,7 @@ use OCP\Files\NotFoundException;
|
||||
use OCP\IPreview;
|
||||
use OCP\IRequest;
|
||||
use OCP\Preview\IMimeIconProvider;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class PreviewController extends Controller {
|
||||
public function __construct(
|
||||
@@ -141,6 +142,11 @@ class PreviewController extends Controller {
|
||||
return new DataResponse([], Http::STATUS_FORBIDDEN);
|
||||
}
|
||||
|
||||
if ($node->getId() <= 0) {
|
||||
\OCP\Server::get(LoggerInterface::class)->error('Requested preview with invalid file id: ' . $node->getId());
|
||||
return new DataResponse([], Http::STATUS_FORBIDDEN);
|
||||
}
|
||||
|
||||
$storage = $node->getStorage();
|
||||
if ($storage->instanceOfStorage(SharedStorage::class)) {
|
||||
/** @var SharedStorage $storage */
|
||||
|
||||
@@ -13,3 +13,5 @@ declare(strict_types=1);
|
||||
// Routing
|
||||
$this->create('core_ajax_update', '/core/ajax/update.php')
|
||||
->actionInclude('core/ajax/update.php');
|
||||
|
||||
$this->create('heartbeat', '/heartbeat')->get();
|
||||
|
||||
+4
-1
@@ -94,7 +94,10 @@ export default defineConfig({
|
||||
config.baseUrl = `http://${ip}/index.php`
|
||||
await waitOnNextcloud(ip)
|
||||
await configureNextcloud()
|
||||
await applyChangesToNextcloud()
|
||||
|
||||
if (!process.env.CI) {
|
||||
await applyChangesToNextcloud()
|
||||
}
|
||||
|
||||
// IMPORTANT: return the config otherwise cypress-split will not work
|
||||
return config
|
||||
|
||||
+20
-3
@@ -9,7 +9,9 @@
|
||||
import Docker from 'dockerode'
|
||||
import waitOn from 'wait-on'
|
||||
import tar from 'tar'
|
||||
import path from 'path'
|
||||
import { execSync } from 'child_process'
|
||||
import { existsSync } from 'fs'
|
||||
|
||||
export const docker = new Docker()
|
||||
|
||||
@@ -129,7 +131,6 @@ export const configureNextcloud = async function() {
|
||||
*/
|
||||
export const applyChangesToNextcloud = async function() {
|
||||
console.log('\nApply local changes to nextcloud...')
|
||||
const container = docker.getContainer(CONTAINER_NAME)
|
||||
|
||||
const htmlPath = '/var/www/html'
|
||||
const folderPaths = [
|
||||
@@ -151,10 +152,26 @@ export const applyChangesToNextcloud = async function() {
|
||||
'./version.php',
|
||||
]
|
||||
|
||||
folderPaths.forEach((path) => {
|
||||
console.log(`├─ Copying ${path}`)
|
||||
let needToApplyChanges = false
|
||||
|
||||
folderPaths.forEach((folderPath) => {
|
||||
const fullPath = path.join(htmlPath, folderPath)
|
||||
|
||||
if (existsSync(fullPath)) {
|
||||
needToApplyChanges = true
|
||||
console.log(`├─ Copying ${folderPath}`)
|
||||
}
|
||||
})
|
||||
|
||||
// Don't try to apply changes, when there are none. Otherwise we
|
||||
// still execute the 'chown' command, which is not needed.
|
||||
if (!needToApplyChanges) {
|
||||
console.log('└─ No local changes found to apply')
|
||||
return
|
||||
}
|
||||
|
||||
const container = docker.getContainer(CONTAINER_NAME)
|
||||
|
||||
// Tar-streaming the above folders into the container
|
||||
const serverTar = tar.c({ gzip: false }, folderPaths)
|
||||
await container.putArchive(serverTar, {
|
||||
|
||||
@@ -110,7 +110,7 @@ class CacheEntry implements ICacheEntry {
|
||||
return $this->data['upload_time'] ?? null;
|
||||
}
|
||||
|
||||
public function getData(): array {
|
||||
public function getData() {
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2024 Robin Appelman <robin@icewind.nl>
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OC\Files\Cache;
|
||||
|
||||
use OC\FilesMetadata\FilesMetadataManager;
|
||||
use OC\SystemConfig;
|
||||
use OCP\Files\Cache\ICacheEntry;
|
||||
use OCP\Files\IMimeTypeLoader;
|
||||
use OCP\ICache;
|
||||
use OCP\ICacheFactory;
|
||||
use OCP\IDBConnection;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* Provides cached access to file metadata.
|
||||
*
|
||||
* Note that this does not include any logic for invalidation and should only be
|
||||
* used in places where up to 5 minute outdated metadata is accepted
|
||||
*/
|
||||
class CachedFileAccess extends FileAccess {
|
||||
const TTL = 5 * 60;
|
||||
|
||||
private ICache $cache;
|
||||
|
||||
public function __construct(
|
||||
IDBConnection $connection,
|
||||
SystemConfig $systemConfig,
|
||||
LoggerInterface $logger,
|
||||
FilesMetadataManager $metadataManager,
|
||||
IMimeTypeLoader $mimeTypeLoader,
|
||||
ICacheFactory $cacheFactory,
|
||||
) {
|
||||
parent::__construct($connection, $systemConfig, $logger, $metadataManager, $mimeTypeLoader);
|
||||
$this->cache = $cacheFactory->createLocal('file_access::');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int[] $fileIds
|
||||
* @return string
|
||||
*/
|
||||
private function getCacheKey(array $fileIds): string {
|
||||
return md5(implode(',', $fileIds));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int[] $fileIds
|
||||
* @return null|ICacheEntry[]
|
||||
*/
|
||||
private function getCachedByFileIds(array $fileIds): ?array {
|
||||
$cached = $this->cache->get($this->getCacheKey($fileIds));
|
||||
if (is_array($cached)) {
|
||||
return array_map(function ($data) {
|
||||
return new CacheEntry($data);
|
||||
}, $cached);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ICacheEntry[] $results
|
||||
* @return void
|
||||
*/
|
||||
private function cacheEntries(array $results): void {
|
||||
$resultFileIds = array_map(function(ICacheEntry $result) {
|
||||
return $result->getId();
|
||||
}, $results);
|
||||
$value = array_map(function(ICacheEntry $entry) {
|
||||
return $entry->getData();
|
||||
}, $results);
|
||||
$this->cache->set($this->getCacheKey($resultFileIds), $value, self::TTL);
|
||||
}
|
||||
|
||||
public function getByFileIdInStorage(int $fileId, int $storageId): ?CacheEntry {
|
||||
$items = array_values($this->getByFileIdsInStorage([$fileId], $storageId));
|
||||
return $items[0] ?? null;
|
||||
}
|
||||
|
||||
public function getByFileId(int $fileId): ?CacheEntry {
|
||||
$items = array_values($this->getByFileIds([$fileId]));
|
||||
return $items[0] ?? null;
|
||||
}
|
||||
|
||||
public function getByFileIds(array $fileIds): array {
|
||||
$cached = $this->getCachedByFileIds($fileIds);
|
||||
if ($cached) {
|
||||
return $cached;
|
||||
}
|
||||
$result = parent::getByFileIds($fileIds);
|
||||
$this->cacheEntries($result);
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function getByFileIdsInStorage(array $fileIds, int $storageId): array {
|
||||
$cached = $this->getCachedByFileIds($fileIds);
|
||||
if ($cached) {
|
||||
return $cached;
|
||||
}
|
||||
$result = parent::getByFileIdsInStorage($fileIds, $storageId);
|
||||
$this->cacheEntries($result);
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -493,7 +493,9 @@ class ObjectStoreStorage extends \OC\Files\Storage\Common implements IChunkedFil
|
||||
$stat['checksum'] = '';
|
||||
|
||||
$exists = $this->getCache()->inCache($path);
|
||||
// if ($this->needsPartFile()) {
|
||||
$uploadPath = $exists ? $path : $path . '.part';
|
||||
// }
|
||||
|
||||
if ($exists) {
|
||||
$fileId = $stat['fileid'];
|
||||
@@ -554,7 +556,8 @@ class ObjectStoreStorage extends \OC\Files\Storage\Common implements IChunkedFil
|
||||
$this->getCache()->update($fileId, $stat);
|
||||
} else {
|
||||
if (!$this->validateWrites || $this->objectStore->objectExists($urn)) {
|
||||
$this->getCache()->move($uploadPath, $path);
|
||||
// Here we rename
|
||||
// $this->getCache()->move($uploadPath, $path);
|
||||
} else {
|
||||
$this->getCache()->remove($uploadPath);
|
||||
throw new \Exception("Object not found after writing (urn: $urn, path: $path)", 404);
|
||||
|
||||
@@ -1791,6 +1791,8 @@ class View {
|
||||
* Get a fileinfo object for files that are ignored in the cache (part files)
|
||||
*/
|
||||
private function getPartFileInfo(string $path): \OC\Files\FileInfo {
|
||||
// For S3 we could actually get the file info here already
|
||||
// $storage->getCache()->get($internalPath);
|
||||
$mount = $this->getMount($path);
|
||||
$storage = $mount->getStorage();
|
||||
$internalPath = $mount->getInternalPath($this->getAbsolutePath($path));
|
||||
|
||||
@@ -50,6 +50,7 @@ class PreviewManager implements IPreview {
|
||||
private IServerContainer $container;
|
||||
private IBinaryFinder $binaryFinder;
|
||||
private IMagickSupport $imagickSupport;
|
||||
private bool $enablePreviews;
|
||||
|
||||
public function __construct(
|
||||
IConfig $config,
|
||||
@@ -73,6 +74,7 @@ class PreviewManager implements IPreview {
|
||||
$this->container = $container;
|
||||
$this->binaryFinder = $binaryFinder;
|
||||
$this->imagickSupport = $imagickSupport;
|
||||
$this->enablePreviews = $config->getSystemValueBool('enable_previews', true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -86,7 +88,7 @@ class PreviewManager implements IPreview {
|
||||
* @return void
|
||||
*/
|
||||
public function registerProvider($mimeTypeRegex, \Closure $callable): void {
|
||||
if (!$this->config->getSystemValueBool('enable_previews', true)) {
|
||||
if (!$this->enablePreviews) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -101,7 +103,7 @@ class PreviewManager implements IPreview {
|
||||
* Get all providers
|
||||
*/
|
||||
public function getProviders(): array {
|
||||
if (!$this->config->getSystemValueBool('enable_previews', true)) {
|
||||
if (!$this->enablePreviews) {
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -158,6 +160,7 @@ class PreviewManager implements IPreview {
|
||||
* @since 11.0.0 - \InvalidArgumentException was added in 12.0.0
|
||||
*/
|
||||
public function getPreview(File $file, $width = -1, $height = -1, $crop = false, $mode = IPreview::MODE_FILL, $mimeType = null) {
|
||||
$this->throwIfPreviewsDisabled();
|
||||
$previewConcurrency = $this->getGenerator()->getNumConcurrentPreviews('preview_concurrency_all');
|
||||
$sem = Generator::guardWithSemaphore(Generator::SEMAPHORE_ID_ALL, $previewConcurrency);
|
||||
try {
|
||||
@@ -181,6 +184,7 @@ class PreviewManager implements IPreview {
|
||||
* @since 19.0.0
|
||||
*/
|
||||
public function generatePreviews(File $file, array $specifications, $mimeType = null) {
|
||||
$this->throwIfPreviewsDisabled();
|
||||
return $this->getGenerator()->generatePreviews($file, $specifications, $mimeType);
|
||||
}
|
||||
|
||||
@@ -191,7 +195,7 @@ class PreviewManager implements IPreview {
|
||||
* @return boolean
|
||||
*/
|
||||
public function isMimeSupported($mimeType = '*') {
|
||||
if (!$this->config->getSystemValueBool('enable_previews', true)) {
|
||||
if (!$this->enablePreviews) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -216,7 +220,7 @@ class PreviewManager implements IPreview {
|
||||
* Check if a preview can be generated for a file
|
||||
*/
|
||||
public function isAvailable(\OCP\Files\FileInfo $file): bool {
|
||||
if (!$this->config->getSystemValueBool('enable_previews', true)) {
|
||||
if (!$this->enablePreviews) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -452,4 +456,13 @@ class PreviewManager implements IPreview {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws NotFoundException if preview generation is disabled
|
||||
*/
|
||||
private function throwIfPreviewsDisabled(): void {
|
||||
if (!$this->enablePreviews) {
|
||||
throw new NotFoundException('Previews disabled');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,6 +102,8 @@ class Repair implements IOutput {
|
||||
$this->dispatcher->dispatchTyped(new RepairErrorEvent($e->getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
$this->repairSteps = [];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,13 +5,9 @@
|
||||
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
namespace OC\Share20;
|
||||
|
||||
use OC\Files\Cache\Cache;
|
||||
use OC\Files\Cache\CachedFileAccess;
|
||||
use OC\Files\Cache\CacheEntry;
|
||||
use OC\Files\Cache\FileAccess;
|
||||
use OC\Share20\Exception\BackendError;
|
||||
use OC\Share20\Exception\InvalidShare;
|
||||
use OC\Share20\Exception\ProviderException;
|
||||
@@ -45,21 +41,52 @@ class DefaultShareProvider implements IShareProvider {
|
||||
// Special share type for user modified group shares
|
||||
public const SHARE_TYPE_USERGROUP = 2;
|
||||
|
||||
private IDBConnection $dbConn;
|
||||
/** @var IDBConnection */
|
||||
private $dbConn;
|
||||
|
||||
/** @var IUserManager */
|
||||
private $userManager;
|
||||
|
||||
/** @var IGroupManager */
|
||||
private $groupManager;
|
||||
|
||||
/** @var IRootFolder */
|
||||
private $rootFolder;
|
||||
|
||||
/** @var IMailer */
|
||||
private $mailer;
|
||||
|
||||
/** @var Defaults */
|
||||
private $defaults;
|
||||
|
||||
/** @var IFactory */
|
||||
private $l10nFactory;
|
||||
|
||||
/** @var IURLGenerator */
|
||||
private $urlGenerator;
|
||||
|
||||
private ITimeFactory $timeFactory;
|
||||
|
||||
public function __construct(
|
||||
IDBConnection $connection,
|
||||
private IUserManager $userManager,
|
||||
private IGroupManager $groupManager,
|
||||
private IRootFolder $rootFolder,
|
||||
private IMailer $mailer,
|
||||
private Defaults $defaults,
|
||||
private IFactory $l10nFactory,
|
||||
private IURLGenerator $urlGenerator,
|
||||
private ITimeFactory $timeFactory,
|
||||
private CachedFileAccess $cacheAccess,
|
||||
IDBConnection $connection,
|
||||
IUserManager $userManager,
|
||||
IGroupManager $groupManager,
|
||||
IRootFolder $rootFolder,
|
||||
IMailer $mailer,
|
||||
Defaults $defaults,
|
||||
IFactory $l10nFactory,
|
||||
IURLGenerator $urlGenerator,
|
||||
ITimeFactory $timeFactory,
|
||||
) {
|
||||
$this->dbConn = $connection;
|
||||
$this->userManager = $userManager;
|
||||
$this->groupManager = $groupManager;
|
||||
$this->rootFolder = $rootFolder;
|
||||
$this->mailer = $mailer;
|
||||
$this->defaults = $defaults;
|
||||
$this->l10nFactory = $l10nFactory;
|
||||
$this->urlGenerator = $urlGenerator;
|
||||
$this->timeFactory = $timeFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -604,7 +631,10 @@ class DefaultShareProvider implements IShareProvider {
|
||||
}
|
||||
|
||||
$qb = $this->dbConn->getQueryBuilder();
|
||||
$qb->select('s.*')
|
||||
$qb->select('s.*',
|
||||
'f.fileid', 'f.path', 'f.permissions AS f_permissions', 'f.storage', 'f.path_hash',
|
||||
'f.parent AS f_parent', 'f.name', 'f.mimetype', 'f.mimepart', 'f.size', 'f.mtime', 'f.storage_mtime',
|
||||
'f.encrypted', 'f.unencrypted_size', 'f.etag', 'f.checksum')
|
||||
->from('share', 's')
|
||||
->andWhere($qb->expr()->orX(
|
||||
$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
|
||||
@@ -631,17 +661,27 @@ class DefaultShareProvider implements IShareProvider {
|
||||
);
|
||||
}
|
||||
|
||||
$childFileIds = array_map(function (Node $node): int {
|
||||
// todo? maybe get these from the oc_mounts table
|
||||
$childMountNodes = array_filter($node->getDirectoryListing(), function (Node $node): bool {
|
||||
return $node->getInternalPath() === '';
|
||||
});
|
||||
$childMountRootIds = array_map(function (Node $node): int {
|
||||
return $node->getId();
|
||||
}, $node->getDirectoryListing());
|
||||
}, $childMountNodes);
|
||||
|
||||
$qb->andWhere($qb->expr()->in('s.file_source', $qb->createParameter('chunk')));
|
||||
$qb->innerJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid'));
|
||||
$qb->andWhere(
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId())),
|
||||
$qb->expr()->in('f.fileid', $qb->createParameter('chunk'))
|
||||
)
|
||||
);
|
||||
|
||||
$qb->orderBy('id');
|
||||
|
||||
$shares = [];
|
||||
|
||||
$chunks = array_chunk($childFileIds, 1000);
|
||||
$chunks = array_chunk($childMountRootIds, 1000);
|
||||
|
||||
// Force the request to be run when there is 0 mount.
|
||||
if (count($chunks) === 0) {
|
||||
@@ -652,7 +692,7 @@ class DefaultShareProvider implements IShareProvider {
|
||||
$qb->setParameter('chunk', $chunk, IQueryBuilder::PARAM_INT_ARRAY);
|
||||
$cursor = $qb->executeQuery();
|
||||
while ($data = $cursor->fetch()) {
|
||||
$shares[$data['file_source']][] = $this->createShare($data);
|
||||
$shares[$data['fileid']][] = $this->createShare($data);
|
||||
}
|
||||
$cursor->closeCursor();
|
||||
}
|
||||
@@ -751,7 +791,7 @@ class DefaultShareProvider implements IShareProvider {
|
||||
|
||||
// If the recipient is set for a group share resolve to that user
|
||||
if ($recipientId !== null && $share->getShareType() === IShare::TYPE_GROUP) {
|
||||
$share = $this->resolveGroupShares([(int)$share->getId() => $share], $recipientId)[0];
|
||||
$share = $this->resolveGroupShares([(int) $share->getId() => $share], $recipientId)[0];
|
||||
}
|
||||
|
||||
return $share;
|
||||
@@ -794,9 +834,25 @@ class DefaultShareProvider implements IShareProvider {
|
||||
* Returns whether the given database result can be interpreted as
|
||||
* a share with accessible file (not trashed, not deleted)
|
||||
*/
|
||||
private function isAccessibleResult(CacheEntry $data) {
|
||||
$path = $data->getPath();
|
||||
return !(str_starts_with($path, 'files_trashbin/') || str_starts_with($path, '__groupfolders/trash/'));
|
||||
private function isAccessibleResult($data) {
|
||||
// exclude shares leading to deleted file entries
|
||||
if ($data['fileid'] === null || $data['path'] === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// exclude shares leading to trashbin on home storages
|
||||
$pathSections = explode('/', $data['path'], 2);
|
||||
// FIXME: would not detect rare md5'd home storage case properly
|
||||
if ($pathSections[0] !== 'files'
|
||||
&& (str_starts_with($data['storage_string_id'], 'home::') || str_starts_with($data['storage_string_id'], 'object::user'))) {
|
||||
return false;
|
||||
} elseif ($pathSections[0] === '__groupfolders'
|
||||
&& str_starts_with($pathSections[1], 'trash/')
|
||||
) {
|
||||
// exclude shares leading to trashbin on group folders storages
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -809,8 +865,15 @@ class DefaultShareProvider implements IShareProvider {
|
||||
if ($shareType === IShare::TYPE_USER) {
|
||||
//Get shares directly with this user
|
||||
$qb = $this->dbConn->getQueryBuilder();
|
||||
$qb->select('s.*')
|
||||
->from('share', 's');
|
||||
$qb->select('s.*',
|
||||
'f.fileid', 'f.path', 'f.permissions AS f_permissions', 'f.storage', 'f.path_hash',
|
||||
'f.parent AS f_parent', 'f.name', 'f.mimetype', 'f.mimepart', 'f.size', 'f.mtime', 'f.storage_mtime',
|
||||
'f.encrypted', 'f.unencrypted_size', 'f.etag', 'f.checksum'
|
||||
)
|
||||
->selectAlias('st.id', 'storage_string_id')
|
||||
->from('share', 's')
|
||||
->leftJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid'))
|
||||
->leftJoin('f', 'storages', 'st', $qb->expr()->eq('f.storage', 'st.numeric_id'));
|
||||
|
||||
// Order by id
|
||||
$qb->orderBy('s.id');
|
||||
@@ -836,7 +899,14 @@ class DefaultShareProvider implements IShareProvider {
|
||||
$cursor = $qb->execute();
|
||||
|
||||
while ($data = $cursor->fetch()) {
|
||||
$shares[] = $this->createShare($data);
|
||||
if ($data['fileid'] && $data['path'] === null) {
|
||||
$data['path'] = (string) $data['path'];
|
||||
$data['name'] = (string) $data['name'];
|
||||
$data['checksum'] = (string) $data['checksum'];
|
||||
}
|
||||
if ($this->isAccessibleResult($data)) {
|
||||
$shares[] = $this->createShare($data);
|
||||
}
|
||||
}
|
||||
$cursor->closeCursor();
|
||||
} elseif ($shareType === IShare::TYPE_GROUP) {
|
||||
@@ -856,8 +926,15 @@ class DefaultShareProvider implements IShareProvider {
|
||||
}
|
||||
|
||||
$qb = $this->dbConn->getQueryBuilder();
|
||||
$qb->select('s.*')
|
||||
$qb->select('s.*',
|
||||
'f.fileid', 'f.path', 'f.permissions AS f_permissions', 'f.storage', 'f.path_hash',
|
||||
'f.parent AS f_parent', 'f.name', 'f.mimetype', 'f.mimepart', 'f.size', 'f.mtime', 'f.storage_mtime',
|
||||
'f.encrypted', 'f.unencrypted_size', 'f.etag', 'f.checksum'
|
||||
)
|
||||
->selectAlias('st.id', 'storage_string_id')
|
||||
->from('share', 's')
|
||||
->leftJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid'))
|
||||
->leftJoin('f', 'storages', 'st', $qb->expr()->eq('f.storage', 'st.numeric_id'))
|
||||
->orderBy('s.id')
|
||||
->setFirstResult(0);
|
||||
|
||||
@@ -890,8 +967,10 @@ class DefaultShareProvider implements IShareProvider {
|
||||
continue;
|
||||
}
|
||||
|
||||
$share = $this->createShare($data);
|
||||
$shares2[$share->getId()] = $share;
|
||||
if ($this->isAccessibleResult($data)) {
|
||||
$share = $this->createShare($data);
|
||||
$shares2[$share->getId()] = $share;
|
||||
}
|
||||
}
|
||||
$cursor->closeCursor();
|
||||
}
|
||||
@@ -905,31 +984,7 @@ class DefaultShareProvider implements IShareProvider {
|
||||
}
|
||||
|
||||
|
||||
return $this->setNodes($shares);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param IShare[] $shares
|
||||
* @return IShare[]
|
||||
*/
|
||||
private function setNodes(array $shares): array {
|
||||
$fileIds = array_map(function (IShare $share): int {
|
||||
return $share->getNodeId();
|
||||
}, $shares);
|
||||
$files = $this->cacheAccess->getByFileIds($fileIds);
|
||||
|
||||
$sharesWithFiles = [];
|
||||
foreach ($shares as $share) {
|
||||
if (isset($files[$share->getNodeId()])) {
|
||||
$cacheItem = $files[$share->getNodeId()];
|
||||
if ($this->isAccessibleResult($cacheItem)) {
|
||||
$share->setNodeCacheEntry($cacheItem);
|
||||
$sharesWithFiles[] = $share;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $sharesWithFiles;
|
||||
return $shares;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1298,15 +1353,14 @@ class DefaultShareProvider implements IShareProvider {
|
||||
|
||||
/**
|
||||
* For each user the path with the fewest slashes is returned
|
||||
*
|
||||
* @param array $shares
|
||||
* @return array
|
||||
*/
|
||||
protected function filterSharesOfUser(array $shares) {
|
||||
// Group shares when the user has a share exception
|
||||
foreach ($shares as $id => $share) {
|
||||
$type = (int)$share['share_type'];
|
||||
$permissions = (int)$share['permissions'];
|
||||
$type = (int) $share['share_type'];
|
||||
$permissions = (int) $share['permissions'];
|
||||
|
||||
if ($type === IShare::TYPE_USERGROUP) {
|
||||
unset($shares[$share['parent']]);
|
||||
|
||||
@@ -15,12 +15,14 @@ use OCA\FederatedFileSharing\TokenHandler;
|
||||
use OCA\ShareByMail\Settings\SettingsManager;
|
||||
use OCA\ShareByMail\ShareByMailProvider;
|
||||
use OCA\Talk\Share\RoomShareProvider;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\Defaults;
|
||||
use OCP\EventDispatcher\IEventDispatcher;
|
||||
use OCP\Federation\ICloudFederationFactory;
|
||||
use OCP\Files\IRootFolder;
|
||||
use OCP\Http\Client\IClientService;
|
||||
use OCP\IServerContainer;
|
||||
use OCP\L10N\IFactory;
|
||||
use OCP\Mail\IMailer;
|
||||
use OCP\Security\IHasher;
|
||||
use OCP\Security\ISecureRandom;
|
||||
@@ -75,7 +77,17 @@ class ProviderFactory implements IProviderFactory {
|
||||
*/
|
||||
protected function defaultShareProvider() {
|
||||
if ($this->defaultProvider === null) {
|
||||
$this->defaultProvider = $this->serverContainer->get(DefaultShareProvider::class);
|
||||
$this->defaultProvider = new DefaultShareProvider(
|
||||
$this->serverContainer->getDatabaseConnection(),
|
||||
$this->serverContainer->getUserManager(),
|
||||
$this->serverContainer->getGroupManager(),
|
||||
$this->serverContainer->get(IRootFolder::class),
|
||||
$this->serverContainer->get(IMailer::class),
|
||||
$this->serverContainer->query(Defaults::class),
|
||||
$this->serverContainer->get(IFactory::class),
|
||||
$this->serverContainer->getURLGenerator(),
|
||||
$this->serverContainer->query(ITimeFactory::class),
|
||||
);
|
||||
}
|
||||
|
||||
return $this->defaultProvider;
|
||||
|
||||
@@ -19,8 +19,10 @@ use OCP\App\ManagerEvent;
|
||||
use OCP\Authentication\IAlternativeLogin;
|
||||
use OCP\EventDispatcher\IEventDispatcher;
|
||||
use OCP\IAppConfig;
|
||||
use OCP\Server;
|
||||
use Psr\Container\ContainerExceptionInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use function OCP\Log\logger;
|
||||
|
||||
/**
|
||||
* This class manages the apps. It allows them to register and integrate in the
|
||||
@@ -777,16 +779,16 @@ class OC_App {
|
||||
// load the app
|
||||
self::loadApp($appId);
|
||||
|
||||
$dispatcher = \OC::$server->get(IEventDispatcher::class);
|
||||
$dispatcher = Server::get(IEventDispatcher::class);
|
||||
|
||||
// load the steps
|
||||
$r = \OCP\Server::get(Repair::class);
|
||||
$r = Server::get(Repair::class);
|
||||
foreach ($steps as $step) {
|
||||
try {
|
||||
$r->addStep($step);
|
||||
} catch (Exception $ex) {
|
||||
$dispatcher->dispatchTyped(new RepairErrorEvent($ex->getMessage()));
|
||||
\OC::$server->getLogger()->logException($ex);
|
||||
logger('core')->error('Failed to add app migration step ' . $step, ['exception' => $ex]);
|
||||
}
|
||||
}
|
||||
// run the steps
|
||||
|
||||
@@ -161,12 +161,4 @@ interface ICacheEntry extends ArrayAccess {
|
||||
* @since 25.0.0
|
||||
*/
|
||||
public function getUnencryptedSize(): int;
|
||||
|
||||
/**
|
||||
* Get the cache data as array
|
||||
*
|
||||
* @return array
|
||||
* @since 30.0.0
|
||||
*/
|
||||
public function getData(): array;
|
||||
}
|
||||
|
||||
@@ -25,7 +25,9 @@ use Test\TestCase;
|
||||
* @package Test\Route
|
||||
*/
|
||||
class RouterTest extends TestCase {
|
||||
public function testGenerateConsecutively(): void {
|
||||
private Router $router;
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
/** @var LoggerInterface $logger */
|
||||
$logger = $this->createMock(LoggerInterface::class);
|
||||
$logger->method('info')
|
||||
@@ -34,7 +36,7 @@ class RouterTest extends TestCase {
|
||||
$this->fail('Unexpected info log: '.(string)($data['exception'] ?? $message));
|
||||
}
|
||||
);
|
||||
$router = new Router(
|
||||
$this->router = new Router(
|
||||
$logger,
|
||||
$this->createMock(IRequest::class),
|
||||
$this->createMock(IConfig::class),
|
||||
@@ -42,13 +44,20 @@ class RouterTest extends TestCase {
|
||||
$this->createMock(ContainerInterface::class),
|
||||
$this->createMock(IAppManager::class),
|
||||
);
|
||||
}
|
||||
|
||||
$this->assertEquals('/index.php/apps/files/', $router->generate('files.view.index'));
|
||||
public function testHeartbeat(): void {
|
||||
$this->assertEquals('/index.php/heartbeat', $this->router->generate('heartbeat'));
|
||||
}
|
||||
|
||||
public function testGenerateConsecutively(): void {
|
||||
|
||||
$this->assertEquals('/index.php/apps/files/', $this->router->generate('files.view.index'));
|
||||
|
||||
// the OCS route is the prefixed one for the AppFramework - see /ocs/v1.php for routing details
|
||||
$this->assertEquals('/index.php/ocsapp/apps/dav/api/v1/direct', $router->generate('ocs.dav.direct.getUrl'));
|
||||
$this->assertEquals('/index.php/ocsapp/apps/dav/api/v1/direct', $this->router->generate('ocs.dav.direct.getUrl'));
|
||||
|
||||
// test caching
|
||||
$this->assertEquals('/index.php/apps/files/', $router->generate('files.view.index'));
|
||||
$this->assertEquals('/index.php/apps/files/', $this->router->generate('files.view.index'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,8 +7,6 @@
|
||||
|
||||
namespace Test\Share20;
|
||||
|
||||
use OC\Files\Cache\CacheEntry;
|
||||
use OC\Files\Cache\FileAccess;
|
||||
use OC\Share20\DefaultShareProvider;
|
||||
use OC\Share20\ShareAttributes;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
@@ -70,11 +68,6 @@ class DefaultShareProviderTest extends \Test\TestCase {
|
||||
/** @var ITimeFactory|MockObject */
|
||||
protected $timeFactory;
|
||||
|
||||
/** @var FileAccess|MockObject */
|
||||
protected $cacheAccess;
|
||||
/** @var array<int, CacheEntry> */
|
||||
protected $cacheItems = [];
|
||||
|
||||
protected function setUp(): void {
|
||||
$this->dbConn = \OC::$server->getDatabaseConnection();
|
||||
$this->userManager = $this->createMock(IUserManager::class);
|
||||
@@ -86,26 +79,6 @@ class DefaultShareProviderTest extends \Test\TestCase {
|
||||
$this->defaults = $this->getMockBuilder(Defaults::class)->disableOriginalConstructor()->getMock();
|
||||
$this->urlGenerator = $this->createMock(IURLGenerator::class);
|
||||
$this->timeFactory = $this->createMock(ITimeFactory::class);
|
||||
$this->cacheAccess = $this->createMock(FileAccess::class);
|
||||
$this->cacheItems = [];
|
||||
$this->cacheAccess->method('getByFileIds')->willReturnCallback(function (array $fileIds) {
|
||||
$result = [];
|
||||
foreach ($fileIds as $fileId) {
|
||||
if (isset($this->cacheItems[$fileId])) {
|
||||
$result[$fileId] = $this->cacheItems[$fileId];
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
});
|
||||
$this->cacheAccess->method('getByFileIdsInStorage')->willReturnCallback(function (array $fileIds, int $storageId) {
|
||||
$result = [];
|
||||
foreach ($fileIds as $fileId) {
|
||||
if (isset($this->cacheItems[$fileId]) && $this->cacheItems[$fileId]->getStorageId() === $storageId) {
|
||||
$result[$fileId] = $this->cacheItems[$fileId];
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
});
|
||||
|
||||
$this->userManager->expects($this->any())->method('userExists')->willReturn(true);
|
||||
$this->timeFactory->expects($this->any())->method('now')->willReturn(new \DateTimeImmutable("2023-05-04 00:00 Europe/Berlin"));
|
||||
@@ -122,13 +95,13 @@ class DefaultShareProviderTest extends \Test\TestCase {
|
||||
$this->defaults,
|
||||
$this->l10nFactory,
|
||||
$this->urlGenerator,
|
||||
$this->timeFactory,
|
||||
$this->cacheAccess,
|
||||
$this->timeFactory
|
||||
);
|
||||
}
|
||||
|
||||
protected function tearDown(): void {
|
||||
$this->dbConn->getQueryBuilder()->delete('share')->execute();
|
||||
$this->dbConn->getQueryBuilder()->delete('filecache')->execute();
|
||||
$this->dbConn->getQueryBuilder()->delete('storages')->execute();
|
||||
}
|
||||
|
||||
@@ -483,8 +456,7 @@ class DefaultShareProviderTest extends \Test\TestCase {
|
||||
$this->defaults,
|
||||
$this->l10nFactory,
|
||||
$this->urlGenerator,
|
||||
$this->timeFactory,
|
||||
$this->cacheAccess,
|
||||
$this->timeFactory
|
||||
])
|
||||
->setMethods(['getShareById'])
|
||||
->getMock();
|
||||
@@ -579,8 +551,7 @@ class DefaultShareProviderTest extends \Test\TestCase {
|
||||
$this->defaults,
|
||||
$this->l10nFactory,
|
||||
$this->urlGenerator,
|
||||
$this->timeFactory,
|
||||
$this->cacheAccess,
|
||||
$this->timeFactory
|
||||
])
|
||||
->setMethods(['getShareById'])
|
||||
->getMock();
|
||||
@@ -935,15 +906,16 @@ class DefaultShareProviderTest extends \Test\TestCase {
|
||||
}
|
||||
|
||||
private function createTestFileEntry($path, $storage = 1) {
|
||||
$id = count($this->cacheItems);
|
||||
$this->cacheItems[$id] = new CacheEntry([
|
||||
'fileid' => $id,
|
||||
'storage' => $storage,
|
||||
'path' => $path,
|
||||
'path_hash' => md5($path),
|
||||
'name' => basename($path),
|
||||
]);
|
||||
return $id;
|
||||
$qb = $this->dbConn->getQueryBuilder();
|
||||
$qb->insert('filecache')
|
||||
->values([
|
||||
'storage' => $qb->expr()->literal($storage),
|
||||
'path' => $qb->expr()->literal($path),
|
||||
'path_hash' => $qb->expr()->literal(md5($path)),
|
||||
'name' => $qb->expr()->literal(basename($path)),
|
||||
]);
|
||||
$this->assertEquals(1, $qb->execute());
|
||||
return $qb->getLastInsertId();
|
||||
}
|
||||
|
||||
public function storageAndFileNameProvider() {
|
||||
@@ -952,6 +924,8 @@ class DefaultShareProviderTest extends \Test\TestCase {
|
||||
['home::shareOwner', 'files/test.txt', 'files/test2.txt'],
|
||||
// regular file on external storage
|
||||
['smb::whatever', 'files/test.txt', 'files/test2.txt'],
|
||||
// regular file on external storage in trashbin-like folder,
|
||||
['smb::whatever', 'files_trashbin/files/test.txt', 'files_trashbin/files/test2.txt'],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -1305,7 +1279,6 @@ class DefaultShareProviderTest extends \Test\TestCase {
|
||||
|
||||
$user = $this->createMock(IUser::class);
|
||||
$user->method('getUID')->willReturn('sharedWith');
|
||||
$user->method('getDisplayName')->willReturn('sharedWith');
|
||||
$owner = $this->createMock(IUser::class);
|
||||
$owner->method('getUID')->willReturn('shareOwner');
|
||||
$initiator = $this->createMock(IUser::class);
|
||||
@@ -2546,8 +2519,7 @@ class DefaultShareProviderTest extends \Test\TestCase {
|
||||
$this->defaults,
|
||||
$this->l10nFactory,
|
||||
$this->urlGenerator,
|
||||
$this->timeFactory,
|
||||
$this->cacheAccess,
|
||||
$this->timeFactory
|
||||
);
|
||||
|
||||
$password = md5(time());
|
||||
@@ -2645,8 +2617,7 @@ class DefaultShareProviderTest extends \Test\TestCase {
|
||||
$this->defaults,
|
||||
$this->l10nFactory,
|
||||
$this->urlGenerator,
|
||||
$this->timeFactory,
|
||||
$this->cacheAccess,
|
||||
$this->timeFactory
|
||||
);
|
||||
|
||||
$u1 = $userManager->createUser('testShare1', 'test');
|
||||
@@ -2742,8 +2713,7 @@ class DefaultShareProviderTest extends \Test\TestCase {
|
||||
$this->defaults,
|
||||
$this->l10nFactory,
|
||||
$this->urlGenerator,
|
||||
$this->timeFactory,
|
||||
$this->cacheAccess,
|
||||
$this->timeFactory
|
||||
);
|
||||
|
||||
$u1 = $userManager->createUser('testShare1', 'test');
|
||||
|
||||
Reference in New Issue
Block a user