Tab -> Space
parent
f0c0e03f2c
commit
af38c24198
|
@ -35,8 +35,8 @@
|
|||
<nextcloud min-version="22" max-version="24"/>
|
||||
</dependencies>
|
||||
<commands>
|
||||
<command>OCA\Memories\Command\Index</command>
|
||||
</commands>
|
||||
<command>OCA\Memories\Command\Index</command>
|
||||
</commands>
|
||||
<navigations>
|
||||
<navigation>
|
||||
<name>Memories</name>
|
||||
|
|
|
@ -4,13 +4,13 @@ return [
|
|||
// Days and folder API
|
||||
['name' => 'page#main', 'url' => '/', 'verb' => 'GET'],
|
||||
['name' => 'page#folder', 'url' => '/folders/{path}', 'verb' => 'GET',
|
||||
'requirements' => [
|
||||
'path' => '.*',
|
||||
],
|
||||
'defaults' => [
|
||||
'path' => '',
|
||||
]
|
||||
],
|
||||
'requirements' => [
|
||||
'path' => '.*',
|
||||
],
|
||||
'defaults' => [
|
||||
'path' => '',
|
||||
]
|
||||
],
|
||||
|
||||
// API
|
||||
['name' => 'api#days', 'url' => '/api/days', 'verb' => 'GET'],
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
*/
|
||||
|
||||
.icon-folder.icon-dark {
|
||||
@include icon-color('folder', 'filetypes', $color-black, 1, true);
|
||||
@include icon-color('folder', 'filetypes', $color-black, 1, true);
|
||||
}
|
||||
|
||||
@include icon-black-white('yourmemories', 'memories', 1);
|
|
@ -37,40 +37,40 @@ use OCP\Files\Events\Node\NodeDeletedEvent;
|
|||
use OCP\Files\Events\Node\NodeTouchedEvent;
|
||||
|
||||
class Application extends App implements IBootstrap {
|
||||
public const APPNAME = 'memories';
|
||||
public const APPNAME = 'memories';
|
||||
|
||||
public const IMAGE_MIMES = [
|
||||
'image/png',
|
||||
'image/jpeg',
|
||||
'image/heic',
|
||||
'image/png',
|
||||
'image/tiff',
|
||||
// 'image/gif', // too rarely used for photos
|
||||
// 'image/x-xbitmap', // too rarely used for photos
|
||||
// 'image/bmp', // too rarely used for photos
|
||||
// 'image/svg+xml', // too rarely used for photos
|
||||
];
|
||||
public const IMAGE_MIMES = [
|
||||
'image/png',
|
||||
'image/jpeg',
|
||||
'image/heic',
|
||||
'image/png',
|
||||
'image/tiff',
|
||||
// 'image/gif', // too rarely used for photos
|
||||
// 'image/x-xbitmap', // too rarely used for photos
|
||||
// 'image/bmp', // too rarely used for photos
|
||||
// 'image/svg+xml', // too rarely used for photos
|
||||
];
|
||||
|
||||
public const VIDEO_MIMES = [
|
||||
'video/mpeg',
|
||||
// 'video/ogg', // too rarely used for photos
|
||||
// 'video/webm', // too rarely used for photos
|
||||
'video/mp4',
|
||||
// 'video/x-m4v', // too rarely used for photos
|
||||
'video/quicktime',
|
||||
'video/x-matroska',
|
||||
];
|
||||
public const VIDEO_MIMES = [
|
||||
'video/mpeg',
|
||||
// 'video/ogg', // too rarely used for photos
|
||||
// 'video/webm', // too rarely used for photos
|
||||
'video/mp4',
|
||||
// 'video/x-m4v', // too rarely used for photos
|
||||
'video/quicktime',
|
||||
'video/x-matroska',
|
||||
];
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct(self::APPNAME);
|
||||
}
|
||||
public function __construct() {
|
||||
parent::__construct(self::APPNAME);
|
||||
}
|
||||
|
||||
public function register(IRegistrationContext $context): void {
|
||||
$context->registerEventListener(NodeWrittenEvent::class, PostWriteListener::class);
|
||||
public function register(IRegistrationContext $context): void {
|
||||
$context->registerEventListener(NodeWrittenEvent::class, PostWriteListener::class);
|
||||
$context->registerEventListener(NodeTouchedEvent::class, PostWriteListener::class);
|
||||
$context->registerEventListener(NodeDeletedEvent::class, PostDeleteListener::class);
|
||||
}
|
||||
}
|
||||
|
||||
public function boot(IBootContext $context): void {
|
||||
}
|
||||
public function boot(IBootContext $context): void {
|
||||
}
|
||||
}
|
|
@ -46,149 +46,149 @@ use Symfony\Component\Console\Output\OutputInterface;
|
|||
|
||||
class Index extends Command {
|
||||
|
||||
/** @var ?GlobalStoragesService */
|
||||
protected $globalService;
|
||||
/** @var ?GlobalStoragesService */
|
||||
protected $globalService;
|
||||
|
||||
/** @var int[][] */
|
||||
protected array $sizes;
|
||||
/** @var int[][] */
|
||||
protected array $sizes;
|
||||
|
||||
protected IUserManager $userManager;
|
||||
protected IRootFolder $rootFolder;
|
||||
protected IPreview $previewGenerator;
|
||||
protected IConfig $config;
|
||||
protected OutputInterface $output;
|
||||
protected IManager $encryptionManager;
|
||||
protected IDBConnection $connection;
|
||||
protected TimelineWrite $timelineWrite;
|
||||
protected IUserManager $userManager;
|
||||
protected IRootFolder $rootFolder;
|
||||
protected IPreview $previewGenerator;
|
||||
protected IConfig $config;
|
||||
protected OutputInterface $output;
|
||||
protected IManager $encryptionManager;
|
||||
protected IDBConnection $connection;
|
||||
protected TimelineWrite $timelineWrite;
|
||||
|
||||
// Stats
|
||||
private int $nProcessed = 0;
|
||||
private int $nSkipped = 0;
|
||||
private int $nInvalid = 0;
|
||||
// Stats
|
||||
private int $nProcessed = 0;
|
||||
private int $nSkipped = 0;
|
||||
private int $nInvalid = 0;
|
||||
|
||||
public function __construct(IRootFolder $rootFolder,
|
||||
IUserManager $userManager,
|
||||
IPreview $previewGenerator,
|
||||
IConfig $config,
|
||||
IManager $encryptionManager,
|
||||
IDBConnection $connection,
|
||||
ContainerInterface $container) {
|
||||
parent::__construct();
|
||||
public function __construct(IRootFolder $rootFolder,
|
||||
IUserManager $userManager,
|
||||
IPreview $previewGenerator,
|
||||
IConfig $config,
|
||||
IManager $encryptionManager,
|
||||
IDBConnection $connection,
|
||||
ContainerInterface $container) {
|
||||
parent::__construct();
|
||||
|
||||
$this->userManager = $userManager;
|
||||
$this->rootFolder = $rootFolder;
|
||||
$this->previewGenerator = $previewGenerator;
|
||||
$this->config = $config;
|
||||
$this->encryptionManager = $encryptionManager;
|
||||
$this->connection = $connection;
|
||||
$this->timelineWrite = new TimelineWrite($this->connection);
|
||||
$this->userManager = $userManager;
|
||||
$this->rootFolder = $rootFolder;
|
||||
$this->previewGenerator = $previewGenerator;
|
||||
$this->config = $config;
|
||||
$this->encryptionManager = $encryptionManager;
|
||||
$this->connection = $connection;
|
||||
$this->timelineWrite = new TimelineWrite($this->connection);
|
||||
|
||||
try {
|
||||
$this->globalService = $container->get(GlobalStoragesService::class);
|
||||
} catch (ContainerExceptionInterface $e) {
|
||||
$this->globalService = null;
|
||||
}
|
||||
}
|
||||
try {
|
||||
$this->globalService = $container->get(GlobalStoragesService::class);
|
||||
} catch (ContainerExceptionInterface $e) {
|
||||
$this->globalService = null;
|
||||
}
|
||||
}
|
||||
|
||||
/** Make sure exiftool is available */
|
||||
private function testExif() {
|
||||
$testfile = dirname(__FILE__). '/../../exiftest.jpg';
|
||||
$stream = fopen($testfile, 'rb');
|
||||
if (!$stream) {
|
||||
return false;
|
||||
}
|
||||
/** Make sure exiftool is available */
|
||||
private function testExif() {
|
||||
$testfile = dirname(__FILE__). '/../../exiftest.jpg';
|
||||
$stream = fopen($testfile, 'rb');
|
||||
if (!$stream) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$exif = \OCA\Memories\Exif::getExifFromStream($stream);
|
||||
fclose($stream);
|
||||
$exif = \OCA\Memories\Exif::getExifFromStream($stream);
|
||||
fclose($stream);
|
||||
|
||||
if (!$exif || $exif["DateTimeOriginal"] !== "2004:08:31 19:52:58") {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (!$exif || $exif["DateTimeOriginal"] !== "2004:08:31 19:52:58") {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function configure(): void {
|
||||
$this
|
||||
->setName('memories:index')
|
||||
->setDescription('Generate entries');
|
||||
}
|
||||
protected function configure(): void {
|
||||
$this
|
||||
->setName('memories:index')
|
||||
->setDescription('Generate entries');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int {
|
||||
// Refuse to run without exiftool
|
||||
\OCA\Memories\Exif::ensureStaticExiftoolProc();
|
||||
if (!$this->testExif()) {
|
||||
error_log('FATAL: exiftool could not be found or test failed');
|
||||
exit(1);
|
||||
}
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int {
|
||||
// Refuse to run without exiftool
|
||||
\OCA\Memories\Exif::ensureStaticExiftoolProc();
|
||||
if (!$this->testExif()) {
|
||||
error_log('FATAL: exiftool could not be found or test failed');
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Time measurement
|
||||
$startTime = microtime(true);
|
||||
// Time measurement
|
||||
$startTime = microtime(true);
|
||||
|
||||
if ($this->encryptionManager->isEnabled()) {
|
||||
$output->writeln('Encryption is enabled. Aborted.');
|
||||
return 1;
|
||||
}
|
||||
$this->output = $output;
|
||||
if ($this->encryptionManager->isEnabled()) {
|
||||
$output->writeln('Encryption is enabled. Aborted.');
|
||||
return 1;
|
||||
}
|
||||
$this->output = $output;
|
||||
|
||||
$this->userManager->callForSeenUsers(function (IUser $user) {
|
||||
$this->generateUserEntries($user);
|
||||
});
|
||||
|
||||
// Close the exiftool process
|
||||
\OCA\Memories\Exif::closeStaticExiftoolProc();
|
||||
// Close the exiftool process
|
||||
\OCA\Memories\Exif::closeStaticExiftoolProc();
|
||||
|
||||
// Show some stats
|
||||
$endTime = microtime(true);
|
||||
$execTime = intval(($endTime - $startTime)*1000)/1000 ;
|
||||
$nTotal = $this->nInvalid + $this->nSkipped + $this->nProcessed;
|
||||
$this->output->writeln("==========================================");
|
||||
$this->output->writeln("Checked $nTotal files in $execTime sec");
|
||||
$this->output->writeln($this->nInvalid . " not valid media items");
|
||||
$this->output->writeln($this->nSkipped . " skipped because unmodified");
|
||||
$this->output->writeln($this->nProcessed . " (re-)processed");
|
||||
$this->output->writeln("==========================================");
|
||||
// Show some stats
|
||||
$endTime = microtime(true);
|
||||
$execTime = intval(($endTime - $startTime)*1000)/1000 ;
|
||||
$nTotal = $this->nInvalid + $this->nSkipped + $this->nProcessed;
|
||||
$this->output->writeln("==========================================");
|
||||
$this->output->writeln("Checked $nTotal files in $execTime sec");
|
||||
$this->output->writeln($this->nInvalid . " not valid media items");
|
||||
$this->output->writeln($this->nSkipped . " skipped because unmodified");
|
||||
$this->output->writeln($this->nProcessed . " (re-)processed");
|
||||
$this->output->writeln("==========================================");
|
||||
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function generateUserEntries(IUser &$user): void {
|
||||
\OC_Util::tearDownFS();
|
||||
\OC_Util::setupFS($user->getUID());
|
||||
private function generateUserEntries(IUser &$user): void {
|
||||
\OC_Util::tearDownFS();
|
||||
\OC_Util::setupFS($user->getUID());
|
||||
|
||||
$userFolder = $this->rootFolder->getUserFolder($user->getUID());
|
||||
$this->parseFolder($userFolder);
|
||||
}
|
||||
$userFolder = $this->rootFolder->getUserFolder($user->getUID());
|
||||
$this->parseFolder($userFolder);
|
||||
}
|
||||
|
||||
private function parseFolder(Folder &$folder): void {
|
||||
try {
|
||||
$folderPath = $folder->getPath();
|
||||
$this->output->writeln('Scanning folder ' . $folderPath);
|
||||
private function parseFolder(Folder &$folder): void {
|
||||
try {
|
||||
$folderPath = $folder->getPath();
|
||||
$this->output->writeln('Scanning folder ' . $folderPath);
|
||||
|
||||
$nodes = $folder->getDirectoryListing();
|
||||
$nodes = $folder->getDirectoryListing();
|
||||
|
||||
foreach ($nodes as &$node) {
|
||||
if ($node instanceof Folder) {
|
||||
$this->parseFolder($node);
|
||||
} elseif ($node instanceof File) {
|
||||
$this->parseFile($node);
|
||||
}
|
||||
}
|
||||
} catch (StorageNotAvailableException $e) {
|
||||
$this->output->writeln(sprintf('<error>Storage for folder folder %s is not available: %s</error>',
|
||||
$folder->getPath(),
|
||||
$e->getHint()
|
||||
));
|
||||
}
|
||||
}
|
||||
foreach ($nodes as &$node) {
|
||||
if ($node instanceof Folder) {
|
||||
$this->parseFolder($node);
|
||||
} elseif ($node instanceof File) {
|
||||
$this->parseFile($node);
|
||||
}
|
||||
}
|
||||
} catch (StorageNotAvailableException $e) {
|
||||
$this->output->writeln(sprintf('<error>Storage for folder folder %s is not available: %s</error>',
|
||||
$folder->getPath(),
|
||||
$e->getHint()
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
private function parseFile(File &$file): void {
|
||||
$res = $this->timelineWrite->processFile($file);
|
||||
if ($res === 2) {
|
||||
$this->nProcessed++;
|
||||
} else if ($res === 1) {
|
||||
$this->nSkipped++;
|
||||
} else {
|
||||
$this->nInvalid++;
|
||||
}
|
||||
}
|
||||
private function parseFile(File &$file): void {
|
||||
$res = $this->timelineWrite->processFile($file);
|
||||
if ($res === 2) {
|
||||
$this->nProcessed++;
|
||||
} else if ($res === 1) {
|
||||
$this->nSkipped++;
|
||||
} else {
|
||||
$this->nInvalid++;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -41,172 +41,172 @@ use OCP\Files\FileInfo;
|
|||
use OCP\Files\Search\ISearchComparison;
|
||||
|
||||
class ApiController extends Controller {
|
||||
private IConfig $config;
|
||||
private IUserSession $userSession;
|
||||
private IConfig $config;
|
||||
private IUserSession $userSession;
|
||||
private IDBConnection $connection;
|
||||
private IRootFolder $rootFolder;
|
||||
private TimelineQuery $timelineQuery;
|
||||
private IRootFolder $rootFolder;
|
||||
private TimelineQuery $timelineQuery;
|
||||
|
||||
public function __construct(
|
||||
IRequest $request,
|
||||
IConfig $config,
|
||||
IUserSession $userSession,
|
||||
public function __construct(
|
||||
IRequest $request,
|
||||
IConfig $config,
|
||||
IUserSession $userSession,
|
||||
IDBConnection $connection,
|
||||
IRootFolder $rootFolder) {
|
||||
IRootFolder $rootFolder) {
|
||||
|
||||
parent::__construct(Application::APPNAME, $request);
|
||||
parent::__construct(Application::APPNAME, $request);
|
||||
|
||||
$this->config = $config;
|
||||
$this->userSession = $userSession;
|
||||
$this->config = $config;
|
||||
$this->userSession = $userSession;
|
||||
$this->connection = $connection;
|
||||
$this->timelineQuery = new TimelineQuery($this->connection);
|
||||
$this->rootFolder = $rootFolder;
|
||||
}
|
||||
$this->timelineQuery = new TimelineQuery($this->connection);
|
||||
$this->rootFolder = $rootFolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* @NoAdminRequired
|
||||
*
|
||||
* @return JSONResponse
|
||||
*/
|
||||
public function days(): JSONResponse {
|
||||
/**
|
||||
* @NoAdminRequired
|
||||
*
|
||||
* @return JSONResponse
|
||||
*/
|
||||
public function days(): JSONResponse {
|
||||
$user = $this->userSession->getUser();
|
||||
if (is_null($user)) {
|
||||
return new JSONResponse([], Http::STATUS_PRECONDITION_FAILED);
|
||||
}
|
||||
if (is_null($user)) {
|
||||
return new JSONResponse([], Http::STATUS_PRECONDITION_FAILED);
|
||||
}
|
||||
|
||||
$list = $this->timelineQuery->getDays($this->config, $user->getUID());
|
||||
return new JSONResponse($list, Http::STATUS_OK);
|
||||
}
|
||||
return new JSONResponse($list, Http::STATUS_OK);
|
||||
}
|
||||
|
||||
/**
|
||||
* @NoAdminRequired
|
||||
*
|
||||
* @return JSONResponse
|
||||
*/
|
||||
public function day(string $id): JSONResponse {
|
||||
/**
|
||||
* @NoAdminRequired
|
||||
*
|
||||
* @return JSONResponse
|
||||
*/
|
||||
public function day(string $id): JSONResponse {
|
||||
$user = $this->userSession->getUser();
|
||||
if (is_null($user) || !is_numeric($id)) {
|
||||
return new JSONResponse([], Http::STATUS_PRECONDITION_FAILED);
|
||||
}
|
||||
if (is_null($user) || !is_numeric($id)) {
|
||||
return new JSONResponse([], Http::STATUS_PRECONDITION_FAILED);
|
||||
}
|
||||
|
||||
$list = $this->timelineQuery->getDay($this->config, $user->getUID(), intval($id));
|
||||
return new JSONResponse($list, Http::STATUS_OK);
|
||||
}
|
||||
return new JSONResponse($list, Http::STATUS_OK);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if folder is allowed and get it if yes
|
||||
*/
|
||||
private function getAllowedFolder(int $folder, $user) {
|
||||
// Get root if folder not specified
|
||||
$root = $this->rootFolder->getUserFolder($user->getUID());
|
||||
if ($folder === 0) {
|
||||
$folder = $root->getId();
|
||||
}
|
||||
/**
|
||||
* Check if folder is allowed and get it if yes
|
||||
*/
|
||||
private function getAllowedFolder(int $folder, $user) {
|
||||
// Get root if folder not specified
|
||||
$root = $this->rootFolder->getUserFolder($user->getUID());
|
||||
if ($folder === 0) {
|
||||
$folder = $root->getId();
|
||||
}
|
||||
|
||||
// Check access to folder
|
||||
$nodes = $root->getById($folder);
|
||||
if (empty($nodes)) {
|
||||
return NULL;
|
||||
}
|
||||
// Check access to folder
|
||||
$nodes = $root->getById($folder);
|
||||
if (empty($nodes)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Check it is a folder
|
||||
$node = $nodes[0];
|
||||
if (!$node instanceof \OCP\Files\Folder) {
|
||||
return NULL;
|
||||
}
|
||||
// Check it is a folder
|
||||
$node = $nodes[0];
|
||||
if (!$node instanceof \OCP\Files\Folder) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @NoAdminRequired
|
||||
*
|
||||
* @return JSONResponse
|
||||
*/
|
||||
public function folder(string $folder): JSONResponse {
|
||||
/**
|
||||
* @NoAdminRequired
|
||||
*
|
||||
* @return JSONResponse
|
||||
*/
|
||||
public function folder(string $folder): JSONResponse {
|
||||
$user = $this->userSession->getUser();
|
||||
if (is_null($user) || !is_numeric($folder)) {
|
||||
return new JSONResponse([], Http::STATUS_PRECONDITION_FAILED);
|
||||
}
|
||||
if (is_null($user) || !is_numeric($folder)) {
|
||||
return new JSONResponse([], Http::STATUS_PRECONDITION_FAILED);
|
||||
}
|
||||
|
||||
// Check permissions
|
||||
$node = $this->getAllowedFolder(intval($folder), $user);
|
||||
if (is_null($node)) {
|
||||
return new JSONResponse([], Http::STATUS_FORBIDDEN);
|
||||
}
|
||||
// Check permissions
|
||||
$node = $this->getAllowedFolder(intval($folder), $user);
|
||||
if (is_null($node)) {
|
||||
return new JSONResponse([], Http::STATUS_FORBIDDEN);
|
||||
}
|
||||
|
||||
// Get response from db
|
||||
// Get response from db
|
||||
$list = $this->timelineQuery->getDaysFolder($node->getId());
|
||||
|
||||
// Get subdirectories
|
||||
$sub = $node->search(new SearchQuery(
|
||||
new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'mimetype', FileInfo::MIMETYPE_FOLDER),
|
||||
0, 0, [], $user));
|
||||
$sub = array_filter($sub, function ($item) use ($node) {
|
||||
return $item->getParent()->getId() === $node->getId();
|
||||
});
|
||||
// Get subdirectories
|
||||
$sub = $node->search(new SearchQuery(
|
||||
new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'mimetype', FileInfo::MIMETYPE_FOLDER),
|
||||
0, 0, [], $user));
|
||||
$sub = array_filter($sub, function ($item) use ($node) {
|
||||
return $item->getParent()->getId() === $node->getId();
|
||||
});
|
||||
|
||||
// Sort by name
|
||||
usort($sub, function($a, $b) {
|
||||
return strnatcmp($a->getName(), $b->getName());
|
||||
});
|
||||
// Sort by name
|
||||
usort($sub, function($a, $b) {
|
||||
return strnatcmp($a->getName(), $b->getName());
|
||||
});
|
||||
|
||||
// Map sub to JSON array
|
||||
$subdirArray = [
|
||||
"dayid" => -0.1,
|
||||
"detail" => array_map(function ($node) {
|
||||
return [
|
||||
"fileid" => $node->getId(),
|
||||
"name" => $node->getName(),
|
||||
"is_folder" => 1,
|
||||
"path" => $node->getPath(),
|
||||
];
|
||||
}, $sub, []),
|
||||
];
|
||||
$subdirArray["count"] = count($subdirArray["detail"]);
|
||||
array_unshift($list, $subdirArray);
|
||||
// Map sub to JSON array
|
||||
$subdirArray = [
|
||||
"dayid" => -0.1,
|
||||
"detail" => array_map(function ($node) {
|
||||
return [
|
||||
"fileid" => $node->getId(),
|
||||
"name" => $node->getName(),
|
||||
"is_folder" => 1,
|
||||
"path" => $node->getPath(),
|
||||
];
|
||||
}, $sub, []),
|
||||
];
|
||||
$subdirArray["count"] = count($subdirArray["detail"]);
|
||||
array_unshift($list, $subdirArray);
|
||||
|
||||
return new JSONResponse($list, Http::STATUS_OK);
|
||||
}
|
||||
return new JSONResponse($list, Http::STATUS_OK);
|
||||
}
|
||||
|
||||
/**
|
||||
* @NoAdminRequired
|
||||
*
|
||||
* @return JSONResponse
|
||||
*/
|
||||
public function folderDay(string $folder, string $dayId): JSONResponse {
|
||||
/**
|
||||
* @NoAdminRequired
|
||||
*
|
||||
* @return JSONResponse
|
||||
*/
|
||||
public function folderDay(string $folder, string $dayId): JSONResponse {
|
||||
$user = $this->userSession->getUser();
|
||||
if (is_null($user) || !is_numeric($folder) || !is_numeric($dayId)) {
|
||||
return new JSONResponse([], Http::STATUS_PRECONDITION_FAILED);
|
||||
}
|
||||
if (is_null($user) || !is_numeric($folder) || !is_numeric($dayId)) {
|
||||
return new JSONResponse([], Http::STATUS_PRECONDITION_FAILED);
|
||||
}
|
||||
|
||||
$node = $this->getAllowedFolder(intval($folder), $user);
|
||||
if ($node === NULL) {
|
||||
return new JSONResponse([], Http::STATUS_FORBIDDEN);
|
||||
}
|
||||
$node = $this->getAllowedFolder(intval($folder), $user);
|
||||
if ($node === NULL) {
|
||||
return new JSONResponse([], Http::STATUS_FORBIDDEN);
|
||||
}
|
||||
|
||||
$list = $this->timelineQuery->getDayFolder($node->getId(), intval($dayId));
|
||||
return new JSONResponse($list, Http::STATUS_OK);
|
||||
}
|
||||
return new JSONResponse($list, Http::STATUS_OK);
|
||||
}
|
||||
|
||||
/**
|
||||
* @NoAdminRequired
|
||||
*
|
||||
* update preferences (user setting)
|
||||
*
|
||||
* @param string key the identifier to change
|
||||
* @param string value the value to set
|
||||
*
|
||||
* @return JSONResponse an empty JSONResponse with respective http status code
|
||||
*/
|
||||
public function setUserConfig(string $key, string $value): JSONResponse {
|
||||
$user = $this->userSession->getUser();
|
||||
if (is_null($user)) {
|
||||
return new JSONResponse([], Http::STATUS_PRECONDITION_FAILED);
|
||||
}
|
||||
/**
|
||||
* @NoAdminRequired
|
||||
*
|
||||
* update preferences (user setting)
|
||||
*
|
||||
* @param string key the identifier to change
|
||||
* @param string value the value to set
|
||||
*
|
||||
* @return JSONResponse an empty JSONResponse with respective http status code
|
||||
*/
|
||||
public function setUserConfig(string $key, string $value): JSONResponse {
|
||||
$user = $this->userSession->getUser();
|
||||
if (is_null($user)) {
|
||||
return new JSONResponse([], Http::STATUS_PRECONDITION_FAILED);
|
||||
}
|
||||
|
||||
$userId = $user->getUid();
|
||||
$this->config->setUserValue($userId, Application::APPNAME, $key, $value);
|
||||
return new JSONResponse([], Http::STATUS_OK);
|
||||
}
|
||||
$userId = $user->getUid();
|
||||
$this->config->setUserValue($userId, Application::APPNAME, $key, $value);
|
||||
return new JSONResponse([], Http::STATUS_OK);
|
||||
}
|
||||
}
|
|
@ -13,60 +13,60 @@ use OCP\IUserSession;
|
|||
use OCP\Util;
|
||||
|
||||
class PageController extends Controller {
|
||||
protected string $userId;
|
||||
protected $appName;
|
||||
protected IEventDispatcher $eventDispatcher;
|
||||
private IInitialState $initialState;
|
||||
private IUserSession $userSession;
|
||||
private IConfig $config;
|
||||
protected string $userId;
|
||||
protected $appName;
|
||||
protected IEventDispatcher $eventDispatcher;
|
||||
private IInitialState $initialState;
|
||||
private IUserSession $userSession;
|
||||
private IConfig $config;
|
||||
|
||||
public function __construct(
|
||||
string $AppName,
|
||||
IRequest $request,
|
||||
string $UserId,
|
||||
IEventDispatcher $eventDispatcher,
|
||||
IInitialState $initialState,
|
||||
IUserSession $userSession,
|
||||
IConfig $config) {
|
||||
public function __construct(
|
||||
string $AppName,
|
||||
IRequest $request,
|
||||
string $UserId,
|
||||
IEventDispatcher $eventDispatcher,
|
||||
IInitialState $initialState,
|
||||
IUserSession $userSession,
|
||||
IConfig $config) {
|
||||
|
||||
parent::__construct($AppName, $request);
|
||||
$this->userId = $UserId;
|
||||
$this->appName = $AppName;
|
||||
$this->eventDispatcher = $eventDispatcher;
|
||||
$this->initialState = $initialState;
|
||||
$this->userSession = $userSession;
|
||||
$this->config = $config;
|
||||
}
|
||||
parent::__construct($AppName, $request);
|
||||
$this->userId = $UserId;
|
||||
$this->appName = $AppName;
|
||||
$this->eventDispatcher = $eventDispatcher;
|
||||
$this->initialState = $initialState;
|
||||
$this->userSession = $userSession;
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* @NoAdminRequired
|
||||
* @NoCSRFRequired
|
||||
*/
|
||||
public function main() {
|
||||
$user = $this->userSession->getUser();
|
||||
if (is_null($user)) {
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* @NoAdminRequired
|
||||
* @NoCSRFRequired
|
||||
*/
|
||||
public function main() {
|
||||
$user = $this->userSession->getUser();
|
||||
if (is_null($user)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Util::addScript($this->appName, 'memories-main');
|
||||
Util::addStyle($this->appName, 'custom-icons');
|
||||
Util::addScript($this->appName, 'memories-main');
|
||||
Util::addStyle($this->appName, 'custom-icons');
|
||||
|
||||
$this->eventDispatcher->dispatchTyped(new LoadSidebar());
|
||||
$this->eventDispatcher->dispatchTyped(new LoadViewer());
|
||||
$this->eventDispatcher->dispatchTyped(new LoadSidebar());
|
||||
$this->eventDispatcher->dispatchTyped(new LoadViewer());
|
||||
|
||||
|
||||
$timelinePath = \OCA\Memories\Util::getPhotosPath($this->config, $user->getUid());
|
||||
$this->initialState->provideInitialState('timelinePath', $timelinePath);
|
||||
$timelinePath = \OCA\Memories\Util::getPhotosPath($this->config, $user->getUid());
|
||||
$this->initialState->provideInitialState('timelinePath', $timelinePath);
|
||||
|
||||
$response = new TemplateResponse($this->appName, 'main');
|
||||
return $response;
|
||||
}
|
||||
$response = new TemplateResponse($this->appName, 'main');
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* @NoAdminRequired
|
||||
* @NoCSRFRequired
|
||||
*/
|
||||
public function folder() {
|
||||
return $this->main();
|
||||
}
|
||||
/**
|
||||
* @NoAdminRequired
|
||||
* @NoCSRFRequired
|
||||
*/
|
||||
public function folder() {
|
||||
return $this->main();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,11 +8,11 @@ use OCP\IConfig;
|
|||
use OCP\IDBConnection;
|
||||
|
||||
class TimelineQuery {
|
||||
protected IDBConnection $connection;
|
||||
protected IDBConnection $connection;
|
||||
|
||||
public function __construct(IDBConnection $connection) {
|
||||
$this->connection = $connection;
|
||||
}
|
||||
public function __construct(IDBConnection $connection) {
|
||||
$this->connection = $connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the days response
|
||||
|
@ -104,7 +104,7 @@ class TimelineQuery {
|
|||
ORDER BY `*PREFIX*memories`.`datetaken` DESC';
|
||||
|
||||
$path = "files" . Exif::getPhotosPath($config, $user) . "%";
|
||||
$rows = $this->connection->executeQuery($sql, [$path, $user, $dayId], [
|
||||
$rows = $this->connection->executeQuery($sql, [$path, $user, $dayId], [
|
||||
\PDO::PARAM_STR, \PDO::PARAM_STR, \PDO::PARAM_INT,
|
||||
])->fetchAll();
|
||||
return $this->processDay($rows);
|
||||
|
@ -126,7 +126,7 @@ class TimelineQuery {
|
|||
AND (`*PREFIX*filecache`.`parent`=? OR `*PREFIX*filecache`.`fileid`=?)
|
||||
WHERE `*PREFIX*memories`.`dayid`=?
|
||||
ORDER BY `*PREFIX*memories`.`datetaken` DESC';
|
||||
$rows = $this->connection->executeQuery($sql, [$folderId, $folderId, $dayId], [
|
||||
$rows = $this->connection->executeQuery($sql, [$folderId, $folderId, $dayId], [
|
||||
\PDO::PARAM_INT, \PDO::PARAM_INT, \PDO::PARAM_INT,
|
||||
])->fetchAll();
|
||||
return $this->processDay($rows);
|
||||
|
|
|
@ -9,11 +9,11 @@ use OCP\Files\File;
|
|||
use OCP\IDBConnection;
|
||||
|
||||
class TimelineWrite {
|
||||
protected IDBConnection $connection;
|
||||
protected IDBConnection $connection;
|
||||
|
||||
public function __construct(IDBConnection $connection) {
|
||||
$this->connection = $connection;
|
||||
}
|
||||
public function __construct(IDBConnection $connection) {
|
||||
$this->connection = $connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a file to insert Exif data into the database
|
||||
|
|
|
@ -34,20 +34,20 @@ use OCP\IDBConnection;
|
|||
class PostDeleteListener implements IEventListener {
|
||||
private TimelineWrite $util;
|
||||
|
||||
public function __construct(IDBConnection $connection) {
|
||||
public function __construct(IDBConnection $connection) {
|
||||
$this->util = new TimelineWrite($connection);
|
||||
}
|
||||
}
|
||||
|
||||
public function handle(Event $event): void {
|
||||
if (!($event instanceof NodeDeletedEvent)) {
|
||||
return;
|
||||
}
|
||||
public function handle(Event $event): void {
|
||||
if (!($event instanceof NodeDeletedEvent)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$node = $event->getNode();
|
||||
if ($node instanceof Folder) {
|
||||
return;
|
||||
}
|
||||
$node = $event->getNode();
|
||||
if ($node instanceof Folder) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->util->deleteFile($node);
|
||||
}
|
||||
$this->util->deleteFile($node);
|
||||
}
|
||||
}
|
|
@ -36,23 +36,23 @@ use OCP\IUserManager;
|
|||
class PostWriteListener implements IEventListener {
|
||||
private TimelineWrite $util;
|
||||
|
||||
public function __construct(IDBConnection $connection,
|
||||
IUserManager $userManager) {
|
||||
$this->userManager = $userManager;
|
||||
public function __construct(IDBConnection $connection,
|
||||
IUserManager $userManager) {
|
||||
$this->userManager = $userManager;
|
||||
$this->util = new TimelineWrite($connection);
|
||||
}
|
||||
}
|
||||
|
||||
public function handle(Event $event): void {
|
||||
if (!($event instanceof NodeWrittenEvent) &&
|
||||
!($event instanceof NodeTouchedEvent)) {
|
||||
return;
|
||||
}
|
||||
public function handle(Event $event): void {
|
||||
if (!($event instanceof NodeWrittenEvent) &&
|
||||
!($event instanceof NodeTouchedEvent)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$node = $event->getNode();
|
||||
if ($node instanceof Folder) {
|
||||
return;
|
||||
}
|
||||
$node = $event->getNode();
|
||||
if ($node instanceof Folder) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->util->processFile($node);
|
||||
}
|
||||
$this->util->processFile($node);
|
||||
}
|
||||
}
|
|
@ -58,19 +58,19 @@ class Version000000Date20220812163631 extends SimpleMigrationStep {
|
|||
'notnull' => false,
|
||||
]);
|
||||
$table->addColumn('fileid', Types::BIGINT, [
|
||||
'notnull' => true,
|
||||
'length' => 20,
|
||||
]);
|
||||
'notnull' => true,
|
||||
'length' => 20,
|
||||
]);
|
||||
$table->addColumn('dayid', Types::INTEGER, [
|
||||
'notnull' => true,
|
||||
]);
|
||||
'notnull' => true,
|
||||
]);
|
||||
$table->addColumn('isvideo', Types::BOOLEAN, [
|
||||
'notnull' => false,
|
||||
'notnull' => false,
|
||||
'default' => false
|
||||
]);
|
||||
]);
|
||||
$table->addColumn('mtime', Types::INTEGER, [
|
||||
'notnull' => true,
|
||||
]);
|
||||
'notnull' => true,
|
||||
]);
|
||||
|
||||
$table->setPrimaryKey(['id']);
|
||||
$table->addIndex(['uid'], 'memories_uid_index');
|
||||
|
|
118
src/App.vue
118
src/App.vue
|
@ -1,43 +1,43 @@
|
|||
<template>
|
||||
<NcContent app-name="memories">
|
||||
<NcAppNavigation>
|
||||
<template id="app-memories-navigation" #list>
|
||||
<NcAppNavigationItem :to="{name: 'timeline'}"
|
||||
:title="t('timeline', 'Timeline')"
|
||||
icon="icon-yourmemories"
|
||||
exact>
|
||||
</NcAppNavigationItem>
|
||||
<NcAppNavigationItem :to="{name: 'folders'}"
|
||||
:title="t('folders', 'Folders')"
|
||||
icon="icon-files-dark">
|
||||
</NcAppNavigationItem>
|
||||
</template>
|
||||
<template #footer>
|
||||
<NcAppNavigationSettings :title="t('memories', 'Settings')">
|
||||
<Settings />
|
||||
</NcAppNavigationSettings>
|
||||
</template>
|
||||
</NcAppNavigation>
|
||||
<NcContent app-name="memories">
|
||||
<NcAppNavigation>
|
||||
<template id="app-memories-navigation" #list>
|
||||
<NcAppNavigationItem :to="{name: 'timeline'}"
|
||||
:title="t('timeline', 'Timeline')"
|
||||
icon="icon-yourmemories"
|
||||
exact>
|
||||
</NcAppNavigationItem>
|
||||
<NcAppNavigationItem :to="{name: 'folders'}"
|
||||
:title="t('folders', 'Folders')"
|
||||
icon="icon-files-dark">
|
||||
</NcAppNavigationItem>
|
||||
</template>
|
||||
<template #footer>
|
||||
<NcAppNavigationSettings :title="t('memories', 'Settings')">
|
||||
<Settings />
|
||||
</NcAppNavigationSettings>
|
||||
</template>
|
||||
</NcAppNavigation>
|
||||
|
||||
<NcAppContent>
|
||||
<div class="outer">
|
||||
<router-view />
|
||||
</div>
|
||||
</NcAppContent>
|
||||
</NcContent>
|
||||
<NcAppContent>
|
||||
<div class="outer">
|
||||
<router-view />
|
||||
</div>
|
||||
</NcAppContent>
|
||||
</NcContent>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.outer {
|
||||
padding: 0 0 0 44px;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.outer {
|
||||
padding-left: 5px;
|
||||
}
|
||||
.outer {
|
||||
padding-left: 5px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
@ -48,35 +48,35 @@ import Timeline from './components/Timeline.vue'
|
|||
import Settings from './components/Settings.vue'
|
||||
|
||||
export default {
|
||||
name: 'App',
|
||||
components: {
|
||||
NcContent,
|
||||
NcAppContent,
|
||||
NcAppNavigation,
|
||||
NcAppNavigationItem,
|
||||
NcAppNavigationSettings,
|
||||
name: 'App',
|
||||
components: {
|
||||
NcContent,
|
||||
NcAppContent,
|
||||
NcAppNavigation,
|
||||
NcAppNavigationItem,
|
||||
NcAppNavigationSettings,
|
||||
|
||||
Timeline,
|
||||
Settings,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
show: true,
|
||||
starred: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
close() {
|
||||
this.show = false
|
||||
console.debug(arguments)
|
||||
},
|
||||
newButtonAction() {
|
||||
console.debug(arguments)
|
||||
},
|
||||
log() {
|
||||
console.debug(arguments)
|
||||
},
|
||||
},
|
||||
Timeline,
|
||||
Settings,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
show: true,
|
||||
starred: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
close() {
|
||||
this.show = false
|
||||
console.debug(arguments)
|
||||
},
|
||||
newButtonAction() {
|
||||
console.debug(arguments)
|
||||
},
|
||||
log() {
|
||||
console.debug(arguments)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
-->
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div>
|
||||
<label for="timeline-path">{{ t('memories', 'Timeline Path') }}</label>
|
||||
<input id="timeline-path"
|
||||
v-model="timelinePath"
|
||||
|
@ -30,7 +30,7 @@
|
|||
<button @click="updateAll()">
|
||||
{{ t('memories', 'Update') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
@ -43,10 +43,10 @@ input[type=text] {
|
|||
import UserConfig from '../mixins/UserConfig'
|
||||
|
||||
export default {
|
||||
name: 'Settings',
|
||||
mixins: [
|
||||
UserConfig,
|
||||
],
|
||||
name: 'Settings',
|
||||
mixins: [
|
||||
UserConfig,
|
||||
],
|
||||
|
||||
methods: {
|
||||
async updateAll() {
|
||||
|
|
|
@ -176,16 +176,16 @@ export default {
|
|||
},
|
||||
|
||||
watch: {
|
||||
$route(from, to) {
|
||||
console.log('route changed', from, to)
|
||||
this.resetState();
|
||||
$route(from, to) {
|
||||
console.log('route changed', from, to)
|
||||
this.resetState();
|
||||
this.fetchDays();
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
this.resetState();
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
/** Reset all state */
|
||||
|
|
24
src/main.js
24
src/main.js
|
@ -29,10 +29,10 @@ import router from './router'
|
|||
|
||||
// Adding translations to the whole app
|
||||
Vue.mixin({
|
||||
methods: {
|
||||
t,
|
||||
n,
|
||||
},
|
||||
methods: {
|
||||
t,
|
||||
n,
|
||||
},
|
||||
})
|
||||
|
||||
Vue.use(VueVirtualScroller)
|
||||
|
@ -42,15 +42,15 @@ Vue.use(VueVirtualScroller)
|
|||
// original scripts are loaded from
|
||||
// https://github.com/nextcloud/server/blob/5bf3d1bb384da56adbf205752be8f840aac3b0c5/lib/private/legacy/template.php#L120-L122
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
if (!window.OCA.Files) {
|
||||
window.OCA.Files = {}
|
||||
}
|
||||
// register unused client for the sidebar to have access to its parser methods
|
||||
Object.assign(window.OCA.Files, { App: { fileList: { filesClient: OC.Files.getClient() } } }, window.OCA.Files)
|
||||
if (!window.OCA.Files) {
|
||||
window.OCA.Files = {}
|
||||
}
|
||||
// register unused client for the sidebar to have access to its parser methods
|
||||
Object.assign(window.OCA.Files, { App: { fileList: { filesClient: OC.Files.getClient() } } }, window.OCA.Files)
|
||||
})
|
||||
|
||||
export default new Vue({
|
||||
el: '#content',
|
||||
router,
|
||||
render: h => h(App),
|
||||
el: '#content',
|
||||
router,
|
||||
render: h => h(App),
|
||||
})
|
||||
|
|
|
@ -4,21 +4,21 @@ import { genFileInfo } from './FileUtils'
|
|||
import client from './DavClient';
|
||||
|
||||
const props = `
|
||||
<oc:fileid />
|
||||
<oc:permissions />
|
||||
<d:getlastmodified />
|
||||
<d:getetag />
|
||||
<d:getcontenttype />
|
||||
<d:getcontentlength />
|
||||
<nc:has-preview />
|
||||
<oc:favorite />
|
||||
<d:resourcetype />`;
|
||||
<oc:fileid />
|
||||
<oc:permissions />
|
||||
<d:getlastmodified />
|
||||
<d:getetag />
|
||||
<d:getcontenttype />
|
||||
<d:getcontentlength />
|
||||
<nc:has-preview />
|
||||
<oc:favorite />
|
||||
<d:resourcetype />`;
|
||||
|
||||
const IMAGE_MIME_TYPES = [
|
||||
'image/jpeg',
|
||||
'image/png',
|
||||
'image/tiff',
|
||||
'image/heic',
|
||||
'image/jpeg',
|
||||
'image/png',
|
||||
'image/tiff',
|
||||
'image/heic',
|
||||
];
|
||||
|
||||
export async function getFiles(fileIds) {
|
||||
|
@ -34,102 +34,102 @@ export async function getFiles(fileIds) {
|
|||
`).join('');
|
||||
|
||||
const options = {
|
||||
method: 'SEARCH',
|
||||
headers: {
|
||||
'content-Type': 'text/xml',
|
||||
},
|
||||
data: `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<d:searchrequest xmlns:d="DAV:"
|
||||
xmlns:oc="http://owncloud.org/ns"
|
||||
xmlns:nc="http://nextcloud.org/ns"
|
||||
xmlns:ns="https://github.com/icewind1991/SearchDAV/ns"
|
||||
xmlns:ocs="http://open-collaboration-services.org/ns">
|
||||
<d:basicsearch>
|
||||
<d:select>
|
||||
<d:prop>
|
||||
${props}
|
||||
</d:prop>
|
||||
</d:select>
|
||||
<d:from>
|
||||
<d:scope>
|
||||
method: 'SEARCH',
|
||||
headers: {
|
||||
'content-Type': 'text/xml',
|
||||
},
|
||||
data: `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<d:searchrequest xmlns:d="DAV:"
|
||||
xmlns:oc="http://owncloud.org/ns"
|
||||
xmlns:nc="http://nextcloud.org/ns"
|
||||
xmlns:ns="https://github.com/icewind1991/SearchDAV/ns"
|
||||
xmlns:ocs="http://open-collaboration-services.org/ns">
|
||||
<d:basicsearch>
|
||||
<d:select>
|
||||
<d:prop>
|
||||
${props}
|
||||
</d:prop>
|
||||
</d:select>
|
||||
<d:from>
|
||||
<d:scope>
|
||||
<d:href>${prefixPath}</d:href>
|
||||
<d:depth>0</d:depth>
|
||||
</d:scope>
|
||||
</d:from>
|
||||
<d:where>
|
||||
<d:depth>0</d:depth>
|
||||
</d:scope>
|
||||
</d:from>
|
||||
<d:where>
|
||||
<d:or>
|
||||
${filter}
|
||||
</d:or>
|
||||
</d:where>
|
||||
</d:basicsearch>
|
||||
</d:searchrequest>`,
|
||||
deep: true,
|
||||
details: true,
|
||||
</d:where>
|
||||
</d:basicsearch>
|
||||
</d:searchrequest>`,
|
||||
deep: true,
|
||||
details: true,
|
||||
responseType: 'text',
|
||||
};
|
||||
|
||||
let response = await client.getDirectoryContents('', options);
|
||||
return response.data
|
||||
.map(data => genFileInfo(data))
|
||||
.map(data => Object.assign({}, data, { filename: data.filename.replace(prefixPath, '') }));
|
||||
.map(data => genFileInfo(data))
|
||||
.map(data => Object.assign({}, data, { filename: data.filename.replace(prefixPath, '') }));
|
||||
}
|
||||
|
||||
export async function getFolderPreviewFileIds(folderPath, limit) {
|
||||
const prefixPath = `/files/${getCurrentUser().uid}`;
|
||||
|
||||
const filter = IMAGE_MIME_TYPES.map(mime => `
|
||||
<d:like>
|
||||
<d:prop>
|
||||
<d:getcontenttype/>
|
||||
</d:prop>
|
||||
<d:literal>${mime}</d:literal>
|
||||
</d:like>
|
||||
`).join('');
|
||||
const filter = IMAGE_MIME_TYPES.map(mime => `
|
||||
<d:like>
|
||||
<d:prop>
|
||||
<d:getcontenttype/>
|
||||
</d:prop>
|
||||
<d:literal>${mime}</d:literal>
|
||||
</d:like>
|
||||
`).join('');
|
||||
|
||||
const options = {
|
||||
method: 'SEARCH',
|
||||
headers: {
|
||||
'content-Type': 'text/xml',
|
||||
},
|
||||
data: `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<d:searchrequest xmlns:d="DAV:"
|
||||
xmlns:oc="http://owncloud.org/ns"
|
||||
xmlns:nc="http://nextcloud.org/ns"
|
||||
xmlns:ns="https://github.com/icewind1991/SearchDAV/ns"
|
||||
xmlns:ocs="http://open-collaboration-services.org/ns">
|
||||
<d:basicsearch>
|
||||
<d:select>
|
||||
<d:prop>
|
||||
${props}
|
||||
</d:prop>
|
||||
</d:select>
|
||||
<d:from>
|
||||
<d:scope>
|
||||
method: 'SEARCH',
|
||||
headers: {
|
||||
'content-Type': 'text/xml',
|
||||
},
|
||||
data: `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<d:searchrequest xmlns:d="DAV:"
|
||||
xmlns:oc="http://owncloud.org/ns"
|
||||
xmlns:nc="http://nextcloud.org/ns"
|
||||
xmlns:ns="https://github.com/icewind1991/SearchDAV/ns"
|
||||
xmlns:ocs="http://open-collaboration-services.org/ns">
|
||||
<d:basicsearch>
|
||||
<d:select>
|
||||
<d:prop>
|
||||
${props}
|
||||
</d:prop>
|
||||
</d:select>
|
||||
<d:from>
|
||||
<d:scope>
|
||||
<d:href>${prefixPath}/${folderPath}</d:href>
|
||||
<d:depth>0</d:depth>
|
||||
</d:scope>
|
||||
</d:from>
|
||||
<d:where>
|
||||
<d:or>
|
||||
${filter}
|
||||
</d:or>
|
||||
</d:where>
|
||||
<d:limit>
|
||||
<d:nresults>${limit}</d:nresults>
|
||||
</d:limit>
|
||||
</d:basicsearch>
|
||||
</d:searchrequest>`,
|
||||
deep: true,
|
||||
details: true,
|
||||
<d:depth>0</d:depth>
|
||||
</d:scope>
|
||||
</d:from>
|
||||
<d:where>
|
||||
<d:or>
|
||||
${filter}
|
||||
</d:or>
|
||||
</d:where>
|
||||
<d:limit>
|
||||
<d:nresults>${limit}</d:nresults>
|
||||
</d:limit>
|
||||
</d:basicsearch>
|
||||
</d:searchrequest>`,
|
||||
deep: true,
|
||||
details: true,
|
||||
responseType: 'text',
|
||||
};
|
||||
|
||||
let response = await client.getDirectoryContents('', options);
|
||||
return response.data
|
||||
.map(data => genFileInfo(data))
|
||||
.map(data => Object.assign({}, data, {
|
||||
filename: data.filename.replace(prefixPath, '')
|
||||
}));
|
||||
.map(data => genFileInfo(data))
|
||||
.map(data => Object.assign({}, data, {
|
||||
filename: data.filename.replace(prefixPath, '')
|
||||
}));
|
||||
}
|
||||
|
||||
export async function deleteFile(path) {
|
||||
|
@ -143,30 +143,30 @@ export async function deleteFile(path) {
|
|||
* @param {string[]} fileNames - The file's names
|
||||
*/
|
||||
export async function downloadFiles(fileNames) {
|
||||
const randomToken = Math.random().toString(36).substring(2)
|
||||
const randomToken = Math.random().toString(36).substring(2)
|
||||
|
||||
const params = new URLSearchParams()
|
||||
params.append('files', JSON.stringify(fileNames))
|
||||
params.append('downloadStartSecret', randomToken)
|
||||
const params = new URLSearchParams()
|
||||
params.append('files', JSON.stringify(fileNames))
|
||||
params.append('downloadStartSecret', randomToken)
|
||||
|
||||
const downloadURL = generateUrl(`/apps/files/ajax/download.php?${params}`)
|
||||
const downloadURL = generateUrl(`/apps/files/ajax/download.php?${params}`)
|
||||
|
||||
window.location = `${downloadURL}downloadStartSecret=${randomToken}`
|
||||
window.location = `${downloadURL}downloadStartSecret=${randomToken}`
|
||||
|
||||
return new Promise((resolve) => {
|
||||
const waitForCookieInterval = setInterval(
|
||||
() => {
|
||||
const cookieIsSet = document.cookie
|
||||
.split(';')
|
||||
.map(cookie => cookie.split('='))
|
||||
.findIndex(([cookieName, cookieValue]) => cookieName === 'ocDownloadStarted' && cookieValue === randomToken)
|
||||
return new Promise((resolve) => {
|
||||
const waitForCookieInterval = setInterval(
|
||||
() => {
|
||||
const cookieIsSet = document.cookie
|
||||
.split(';')
|
||||
.map(cookie => cookie.split('='))
|
||||
.findIndex(([cookieName, cookieValue]) => cookieName === 'ocDownloadStarted' && cookieValue === randomToken)
|
||||
|
||||
if (cookieIsSet) {
|
||||
clearInterval(waitForCookieInterval)
|
||||
resolve(true)
|
||||
}
|
||||
},
|
||||
50
|
||||
)
|
||||
})
|
||||
if (cookieIsSet) {
|
||||
clearInterval(waitForCookieInterval)
|
||||
resolve(true)
|
||||
}
|
||||
},
|
||||
50
|
||||
)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -21,10 +21,10 @@
|
|||
*/
|
||||
|
||||
const isNumber = function(num) {
|
||||
if (!num) {
|
||||
return false
|
||||
}
|
||||
return Number(num).toString() === num.toString()
|
||||
if (!num) {
|
||||
return false
|
||||
}
|
||||
return Number(num).toString() === num.toString()
|
||||
}
|
||||
|
||||
export { isNumber }
|
Loading…
Reference in New Issue