Skip to content

Commit

Permalink
Add DAV endpoint for location grouping
Browse files Browse the repository at this point in the history
Signed-off-by: Louis Chemineau <louis@chmn.me>
  • Loading branch information
artonge committed Oct 26, 2022
1 parent c5c257d commit 619e08d
Show file tree
Hide file tree
Showing 7 changed files with 510 additions and 8 deletions.
2 changes: 1 addition & 1 deletion appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
<collection>OCA\Photos\Sabre\PublicRootCollection</collection>
</collections>
<plugins>
<plugin>OCA\Photos\Sabre\Album\PropFindPlugin</plugin>
<plugin>OCA\Photos\Sabre\PropFindPlugin</plugin>
</plugins>
</sabre>
</info>
181 changes: 181 additions & 0 deletions lib/Sabre/Location/LocationPhoto.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
<?php

declare(strict_types=1);
/**
* @copyright Copyright (c) 2022 Louis Chemineau <louis@chmn.me>
*
* @author Louis Chemineau <louis@chmn.me>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

namespace OCA\Photos\Sabre\Location;

use OC\Metadata\FileMetadata;
use OCA\Photos\DB\Location\LocationFile;
use OCA\Photos\DB\Location\LocationInfo;
use OCP\Files\IRootFolder;
use OCP\Files\Node;
use OCP\Files\File;
use OCP\Files\NotFoundException;
use Sabre\DAV\Exception\Forbidden;
use Sabre\DAV\IFile;

class LocationPhoto implements IFile {
private LocationInfo $locationInfo;
private LocationFile $locationFile;
private IRootFolder $rootFolder;

public const TAG_FAVORITE = '_$!<Favorite>!$_';

public function __construct(
LocationInfo $locationInfo,
LocationFile $locationFile,
IRootFolder $rootFolder
) {
$this->locationInfo = $locationInfo;
$this->locationFile = $locationFile;
$this->rootFolder = $rootFolder;
}

/**
* @return void
*/
public function delete() {
throw new Forbidden('Cannot remove from a location');
}

public function getName() {
return $this->locationFile->getFileId() . "-" . $this->locationFile->getName();
}

/**
* @return never
*/
public function setName($name) {
throw new Forbidden('Cannot rename from a location');
}

public function getLastModified() {
return $this->locationFile->getMTime();
}

public function put($data) {
$nodes = $this->userFolder->getById($this->file->getFileId());
$node = current($nodes);
if ($node) {
/** @var Node $node */
if ($node instanceof File) {
return $node->putContent($data);
} else {
throw new NotFoundException("Photo is a folder");
}
} else {
throw new NotFoundException("Photo not found for user");
}
}

public function get() {
$nodes = $this->rootFolder
->getUserFolder($this->locationInfo->getUserId())
->getById($this->locationFile->getFileId());
$node = current($nodes);
if ($node) {
/** @var Node $node */
if ($node instanceof File) {
return $node->fopen('r');
} else {
throw new NotFoundException("Photo is a folder");
}
} else {
throw new NotFoundException("Photo not found for user");
}
}

public function getFileId(): int {
return $this->locationFile->getFileId();
}

public function getFileInfo(): Node {
$nodes = $this->rootFolder
->getUserFolder($this->locationInfo->getUserId())
->getById($this->locationFile->getFileId());
$node = current($nodes);
if ($node) {
return $node;
} else {
throw new NotFoundException("Photo not found for user");
}
}

public function getContentType() {
return $this->locationFile->getMimeType();
}

public function getETag() {
return $this->locationFile->getEtag();
}

public function getSize() {
return $this->locationFile->getSize();
}

public function getFile(): LocationFile {
return $this->locationFile;
}

public function isFavorite(): bool {
$tagManager = \OCP\Server::get(\OCP\ITagManager::class);
$tagger = $tagManager->load('files');
if ($tagger === null) {
return false;
}
$tags = $tagger->getTagsForObjects([$this->getFileId()]);

if ($tags === false || empty($tags)) {
return false;
}

return array_search(self::TAG_FAVORITE, current($tags)) !== false;
}

public function setFavoriteState($favoriteState): bool {
$tagManager = \OCP\Server::get(\OCP\ITagManager::class);
$tagger = $tagManager->load('files');

switch ($favoriteState) {
case "0":
return $tagger->removeFromFavorites($this->locationFile->getFileId());
case "1":
return $tagger->addToFavorites($this->locationFile->getFileId());
default:
new \Exception('Favorite state is invalide, should be 0 or 1.');
}
}

public function setMetadata(string $key, FileMetadata $value): void {
$this->metaData[$key] = $value;
}

public function hasMetadata(string $key): bool {
return isset($this->metaData[$key]);
}

public function getMetadata(string $key): FileMetadata {
return $this->metaData[$key];
}
}
155 changes: 155 additions & 0 deletions lib/Sabre/Location/LocationRoot.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
<?php

declare(strict_types=1);
/**
* @copyright Copyright (c) 2022 Louis Chemineau <louis@chmn.me>
*
* @author Louis Chemineau <louis@chmn.me>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

namespace OCA\Photos\Sabre\Location;

use OCA\Photos\DB\Location\LocationFile;
use OCA\Photos\DB\Location\LocationInfo;
use OCA\Photos\DB\Location\LocationMapper;
use OCA\Photos\Service\ReverseGeoCoderService;
use OCP\Files\IRootFolder;
use Sabre\DAV\Exception\Forbidden;
use Sabre\DAV\Exception\NotFound;
use Sabre\DAV\ICollection;

class LocationRoot implements ICollection {
protected LocationMapper $locationMapper;
protected ReverseGeoCoderService $reverseGeoCoderService;
protected LocationInfo $locationInfo;
protected IRootFolder $rootFolder;
/** @var LocationFile[] */
protected ?array $children = null;

public function __construct(
LocationMapper $locationMapper,
ReverseGeoCoderService $reverseGeoCoderService,
LocationInfo $locationInfo,
IRootFolder $rootFolder,
) {
$this->locationMapper = $locationMapper;
$this->reverseGeoCoderService = $reverseGeoCoderService;
$this->locationInfo = $locationInfo;
$this->rootFolder = $rootFolder;
}

/**
* @return never
*/
public function delete() {
throw new Forbidden('Not allowed to delete a location collection');
}

public function getName(): string {
return $this->reverseGeoCoderService->getLocationNameForLocationId(
$this->locationInfo->getLocationId()
);
}

/**
* @return never
*/
public function setName($name) {
throw new Forbidden('Cannot change the location collection name');
}

/**
* @param string $name
* @param null|resource|string $data
* @return never
*/
public function createFile($name, $data = null) {
throw new Forbidden('Cannot create a file in a location collection');
}

/**
* @return never
*/
public function createDirectory($name) {
throw new Forbidden('Not allowed to create directories in this folder');
}

/**
* @return LocationPhoto[]
*/
public function getChildren(): array {
if ($this->children === null) {
$this->children = array_map(
fn (LocationFile $file) => new LocationPhoto($this->locationInfo, $file, $this->rootFolder),
$this->locationMapper->findFilesForUserAndLocation($this->locationInfo->getUserId(), $this->locationInfo->getLocationId())
);
}

return $this->children;
}

public function getChild($name): LocationPhoto {
foreach ($this->getChildren() as $child) {
if ($child->getName() === $name) {
return $child;
}
}

throw new NotFound("$name not found");
}

public function childExists($name): bool {
try {
$this->getChild($name);
return true;
} catch (NotFound $e) {
return false;
}
}

public function getLastModified(): int {
return 0;
}

public function getFirstPhoto(): int {
return $this->getChildren()[0]->getFileId();
}

/**
* @return int[]
*/
public function getFileIds(): array {
return array_map(function (LocationPhoto $file) {
return $file->getFileId();
}, $this->getChildren());
}

/**
* @return int|null
*/
public function getCover() {
$children = $this->getChildren();

if (count($children) > 0) {
return $children[0]->getFileId();
} else {
return null;
}
}
}
Loading

0 comments on commit 619e08d

Please sign in to comment.