diff --git a/appinfo/info.xml b/appinfo/info.xml
index 8b3e63f2..d8948710 100644
--- a/appinfo/info.xml
+++ b/appinfo/info.xml
@@ -9,7 +9,7 @@
Recommendations
Shows recommended files
Shows recommended files for quick access of files and folders with recent activity
- 5.0.0-dev.0
+ 5.0.0-dev.1
agpl
Christoph Wurst
Jan-Christoph Borchardt
@@ -19,7 +19,19 @@
+
+
+
+
OCA\Recommendations\Command\GetRecommendations
+
+
+ OCA\Recommendations\Sabre\RootCollection
+
+
+ OCA\Recommendations\Sabre\PropFindPlugin
+
+
diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php
index 909dd4c8..16bd7c98 100644
--- a/lib/AppInfo/Application.php
+++ b/lib/AppInfo/Application.php
@@ -9,6 +9,7 @@
namespace OCA\Recommendations\AppInfo;
+use OCA\DAV\Connector\Sabre\Principal;
use OCA\Files\Event\LoadAdditionalScriptsEvent;
use OCA\Recommendations\Capabilities;
use OCA\Recommendations\Dashboard\RecommendationWidget;
@@ -29,6 +30,7 @@ public function register(IRegistrationContext $context): void {
$context->registerEventListener(LoadAdditionalScriptsEvent::class, FilesLoadAdditionalScriptsListener::class);
$context->registerDashboardWidget(RecommendationWidget::class);
$context->registerCapability(Capabilities::class);
+ $context->registerServiceAlias('principalBackend', Principal::class);
}
public function boot(IBootContext $context): void {
diff --git a/lib/Command/GetRecommendations.php b/lib/Command/GetRecommendations.php
index 2ab7523e..5bc17b5d 100644
--- a/lib/Command/GetRecommendations.php
+++ b/lib/Command/GetRecommendations.php
@@ -49,12 +49,12 @@ public function execute(InputInterface $input, OutputInterface $output) {
);
if (is_null($user)) {
- $output->writeln("user does not exist");
+ $output->writeln('user does not exist');
return 1;
}
if ($input->getArgument('max')) {
- $recommendations = $this->recommendationService->getRecommendations($user, (int) $input->getArgument('max'));
+ $recommendations = $this->recommendationService->getRecommendations($user, (int)$input->getArgument('max'));
} else {
$recommendations = $this->recommendationService->getRecommendations($user);
}
diff --git a/lib/Controller/RecommendationController.php b/lib/Controller/RecommendationController.php
index 3d23701e..0aef9969 100644
--- a/lib/Controller/RecommendationController.php
+++ b/lib/Controller/RecommendationController.php
@@ -50,7 +50,7 @@ public function __construct(IRequest $request,
public function index(): DataResponse {
$user = $this->userSession->getUser();
if (is_null($user)) {
- throw new Exception("Not logged in");
+ throw new Exception('Not logged in');
}
$response = [];
$response['enabled'] = $this->config->getUserValue($user->getUID(), Application::APP_ID, 'enabled', 'true') === 'true';
@@ -73,7 +73,7 @@ public function index(): DataResponse {
public function always(): DataResponse {
$user = $this->userSession->getUser();
if (is_null($user)) {
- throw new Exception("Not logged in");
+ throw new Exception('Not logged in');
}
$response = [
'enabled' => $this->config->getUserValue($user->getUID(), Application::APP_ID, 'enabled', 'true') === 'true',
diff --git a/lib/Controller/SettingsController.php b/lib/Controller/SettingsController.php
index ad9836fb..b1e6837d 100644
--- a/lib/Controller/SettingsController.php
+++ b/lib/Controller/SettingsController.php
@@ -40,7 +40,7 @@ public function __construct($appName,
public function getSettings(): JSONResponse {
$user = $this->userSession->getUser();
if (!$user instanceof IUser) {
- throw new Exception("Not logged in");
+ throw new Exception('Not logged in');
}
return new JSONResponse([
'enabled' => $this->config->getUserValue($user->getUID(), Application::APP_ID, 'enabled', 'true') === 'true',
@@ -55,7 +55,7 @@ public function getSettings(): JSONResponse {
public function setSetting(string $key, string $value): JSONResponse {
$user = $this->userSession->getUser();
if (!$user instanceof IUser) {
- throw new Exception("Not logged in");
+ throw new Exception('Not logged in');
}
$availableSettings = ['enabled'];
if (!in_array($key, $availableSettings)) {
diff --git a/lib/Dashboard/RecommendationWidget.php b/lib/Dashboard/RecommendationWidget.php
index 3d0de16c..9575d4dd 100644
--- a/lib/Dashboard/RecommendationWidget.php
+++ b/lib/Dashboard/RecommendationWidget.php
@@ -37,7 +37,7 @@ public function __construct(
IURLGenerator $urlGenerator,
IMimeTypeDetector $mimeTypeDetector,
RecommendationService $recommendationService,
- IUserManager $userManager
+ IUserManager $userManager,
) {
$this->userSession = $userSession;
$this->l10n = $l10n;
diff --git a/lib/ResponseDefinitions.php b/lib/ResponseDefinitions.php
index b240498c..140bda83 100644
--- a/lib/ResponseDefinitions.php
+++ b/lib/ResponseDefinitions.php
@@ -17,6 +17,7 @@
* mimeType: string,
* hasPreview: bool,
* reason: string,
+ * reasonLabel: string,
* }
*
* @psalm-suppress UnusedClass
diff --git a/lib/Sabre/IRecommendationNode.php b/lib/Sabre/IRecommendationNode.php
new file mode 100644
index 00000000..5f6fbd81
--- /dev/null
+++ b/lib/Sabre/IRecommendationNode.php
@@ -0,0 +1,14 @@
+on('propFind', $this->propFind(...));
+ }
+
+ public function propFind(PropFind $propFind, INode $node): void {
+ if ($node instanceof RecommendationDirectory || $node instanceof RecommendationFile) {
+ $propFind->handle(
+ self::RECOMMENDATION_REASON,
+ /** @psalm-suppress PossiblyNullReference Null already checked above */
+ fn () => $node->getRecommendationReason(),
+ );
+ $propFind->handle(
+ self::RECOMMENDATION_REASON_LABEL,
+ /** @psalm-suppress PossiblyNullReference Null already checked above */
+ fn () => $node->getRecommendationReasonLabel(),
+ );
+ $propFind->handle(
+ self::RECOMMENDATION_ORIGINAL_LOCATION,
+ /** @psalm-suppress PossiblyNullReference Null already checked above */
+ fn () => $node->getPath(),
+ );
+ }
+ }
+}
diff --git a/lib/Sabre/RecommendationDirectory.php b/lib/Sabre/RecommendationDirectory.php
new file mode 100644
index 00000000..6ca5f687
--- /dev/null
+++ b/lib/Sabre/RecommendationDirectory.php
@@ -0,0 +1,37 @@
+recommendationReason;
+ }
+
+ public function getRecommendationReasonLabel(): string {
+ return $this->recommendationReasonLabel;
+ }
+}
diff --git a/lib/Sabre/RecommendationFile.php b/lib/Sabre/RecommendationFile.php
new file mode 100644
index 00000000..8e5f50f6
--- /dev/null
+++ b/lib/Sabre/RecommendationFile.php
@@ -0,0 +1,38 @@
+recommendationReason;
+ }
+
+ public function getRecommendationReasonLabel(): string {
+ return $this->recommendationReasonLabel;
+ }
+}
diff --git a/lib/Sabre/RecommendationsHome.php b/lib/Sabre/RecommendationsHome.php
new file mode 100644
index 00000000..59022f5c
--- /dev/null
+++ b/lib/Sabre/RecommendationsHome.php
@@ -0,0 +1,132 @@
+ */
+ private ?array $cachedRecommendations = null;
+
+ private View $fileView;
+
+ public function __construct(
+ private array $principalInfo,
+ private IUser $user,
+ private IConfig $config,
+ private RecommendationService $recommendationService,
+ private IManager $shareManager,
+ private IRequest $request,
+ private IL10N $l10n,
+ ) {
+ $this->fileView = Filesystem::getView();
+ }
+
+ public function delete() {
+ throw new Forbidden();
+ }
+
+ public function getName(): string {
+ [, $name] = \Sabre\Uri\split($this->principalInfo['uri']);
+ return $name;
+ }
+
+ public function setName($name) {
+ throw new Forbidden();
+ }
+
+ public function createFile($name, $data = null) {
+ throw new Forbidden();
+ }
+
+ public function createDirectory($name) {
+ throw new Forbidden();
+ }
+
+ public function getChild($name) {
+ if (!$this->isEnabled()) {
+ throw new NotFound('Recommendations are disabled');
+ }
+
+ $recommendations = $this->getChildren();
+ foreach ($recommendations as $child) {
+ if ($child->getName() === $name) {
+ return $child;
+ }
+ }
+
+ throw new NotFound("Child '$name' not found in recommendations");
+ }
+
+ public function getChildren(): array {
+ if (!$this->isEnabled()) {
+ return [];
+ }
+
+ if ($this->cachedRecommendations === null) {
+ $this->cachedRecommendations = $this->recommendationService->getRecommendations($this->user);
+ }
+
+ return array_map(
+ function (IRecommendation $recommendation) {
+ $fileInfo = $recommendation->getNode()->getFileInfo();
+ if ($recommendation->getNode()->getType() === FileInfo::TYPE_FOLDER) {
+ return new RecommendationDirectory(
+ $recommendation->getReason(),
+ $recommendation->getReasonLabel(),
+ $this->fileView,
+ $fileInfo,
+ null,
+ $this->shareManager,
+ );
+ }
+ return new RecommendationFile(
+ $recommendation->getReason(),
+ $recommendation->getReasonLabel(),
+ $this->fileView,
+ $fileInfo,
+ $this->shareManager,
+ $this->request,
+ $this->l10n
+ );
+ },
+ $this->cachedRecommendations
+ );
+ }
+
+ public function childExists($name): bool {
+ if (!$this->isEnabled()) {
+ return false;
+ }
+ // TODO: map the recommendations to a Sabre node type
+ return true;
+ }
+
+ public function getLastModified(): int {
+ return 0;
+ }
+
+ private function isEnabled(): bool {
+ return $this->config->getUserValue($this->user->getUID(), Application::APP_ID, 'enabled', 'true') === 'true';
+ }
+}
diff --git a/lib/Sabre/RootCollection.php b/lib/Sabre/RootCollection.php
new file mode 100644
index 00000000..031533fa
--- /dev/null
+++ b/lib/Sabre/RootCollection.php
@@ -0,0 +1,67 @@
+disableListing = !$config->getSystemValue('debug', false);
+ }
+
+ /**
+ * This method returns a node for a principal.
+ *
+ * The passed array contains principal information, and is guaranteed to
+ * at least contain a uri item. Other properties may or may not be
+ * supplied by the authentication backend.
+ *
+ * @param array $principalInfo
+ * @return INode
+ */
+ public function getChildForPrincipal(array $principalInfo): RecommendationsHome {
+ [, $name] = \Sabre\Uri\split($principalInfo['uri']);
+ $user = $this->userSession->getUser();
+ if (is_null($user) || $name !== $user->getUID()) {
+ throw new Forbidden();
+ }
+ return new RecommendationsHome(
+ $principalInfo,
+ $user,
+ $this->config,
+ $this->recommendationService,
+ $this->shareManager,
+ $this->request,
+ $this->l10n,
+ );
+ }
+
+ public function getName(): string {
+ return Application::APP_ID;
+ }
+}
diff --git a/lib/Service/IRecommendation.php b/lib/Service/IRecommendation.php
index 080fd600..491ddea8 100644
--- a/lib/Service/IRecommendation.php
+++ b/lib/Service/IRecommendation.php
@@ -24,4 +24,6 @@ public function hasPreview(): bool;
public function setHasPreview(bool $state);
public function getReason(): string;
+
+ public function getReasonLabel(): string;
}
diff --git a/lib/Service/RecentlyCommentedFilesSource.php b/lib/Service/RecentlyCommentedFilesSource.php
index 9f912d1b..75ad049e 100644
--- a/lib/Service/RecentlyCommentedFilesSource.php
+++ b/lib/Service/RecentlyCommentedFilesSource.php
@@ -1,4 +1,5 @@
getNode(),
$file->getComment()->getCreationDateTime()->getTimestamp(),
self::REASON,
+ $this->l10n->t('Recently commented')
);
}, $this->getNMostRecentlyCommenedFiles($all, $max));
}
diff --git a/lib/Service/RecentlyEditedFilesSource.php b/lib/Service/RecentlyEditedFilesSource.php
index 71afae05..9a3dc140 100644
--- a/lib/Service/RecentlyEditedFilesSource.php
+++ b/lib/Service/RecentlyEditedFilesSource.php
@@ -48,6 +48,7 @@ public function getMostRecentRecommendation(IUser $user, int $max): array {
$node,
$node->getMTime(),
self::REASON,
+ $this->l10n->t('Recently edited'),
);
} catch (StorageNotAvailableException $e) {
return null;
diff --git a/lib/Service/RecentlySharedFilesSource.php b/lib/Service/RecentlySharedFilesSource.php
index 2b9aa1b5..0b4d72b6 100644
--- a/lib/Service/RecentlySharedFilesSource.php
+++ b/lib/Service/RecentlySharedFilesSource.php
@@ -99,11 +99,13 @@ public function getMostRecentRecommendation(IUser $user, int $max): array {
return array_filter(array_map(function (IShare $share) use ($userFolder): ?RecommendedFile {
try {
+ $node = $userFolder->get($share->getTarget());
return new RecommendedFile(
- $userFolder->getRelativePath($userFolder->get($share->getTarget())->getParent()->getPath()),
- $share->getNode(),
+ $userFolder->getRelativePath($node->getParent()->getPath()),
+ $node,
$share->getShareTime()->getTimestamp(),
self::REASON,
+ $this->l10n->t('Recently shared'),
);
} catch (NotFoundException $ex) {
return null;
diff --git a/lib/Service/RecommendedFile.php b/lib/Service/RecommendedFile.php
index c8c3d30b..2bd32806 100644
--- a/lib/Service/RecommendedFile.php
+++ b/lib/Service/RecommendedFile.php
@@ -16,21 +16,14 @@
* @psalm-import-type RecommendationsRecommendedFile from ResponseDefinitions
*/
class RecommendedFile implements IRecommendation {
- private string $directory;
- private Node $node;
- private int $timestamp;
- private string $reason;
- private bool $hasPreview;
-
- public function __construct(string $directory,
- Node $node,
- int $timestamp,
- string $reason) {
- $this->directory = $directory;
- $this->node = $node;
- $this->reason = $reason;
- $this->timestamp = $timestamp;
- $this->hasPreview = false;
+ public function __construct(
+ private string $directory,
+ private Node $node,
+ private int $timestamp,
+ private string $reason,
+ private string $reasonLabel,
+ private bool $hasPreview = false,
+ ) {
}
public function getId(): string {
@@ -61,6 +54,10 @@ public function setHasPreview(bool $state) {
$this->hasPreview = $state;
}
+ public function getReasonLabel(): string {
+ return $this->reasonLabel;
+ }
+
/**
* @return RecommendationsRecommendedFile
*/
@@ -75,6 +72,7 @@ public function jsonSerialize() {
'mimeType' => $this->node->getMimetype(),
'hasPreview' => $this->hasPreview(),
'reason' => $this->getReason(),
+ 'reasonLabel' => $this->getReasonLabel(),
];
}
}
diff --git a/openapi.json b/openapi.json
index c72e5f6c..ea0a485c 100644
--- a/openapi.json
+++ b/openapi.json
@@ -70,7 +70,8 @@
"extension",
"mimeType",
"hasPreview",
- "reason"
+ "reason",
+ "reasonLabel"
],
"properties": {
"id": {
@@ -97,6 +98,9 @@
},
"reason": {
"type": "string"
+ },
+ "reasonLabel": {
+ "type": "string"
}
}
}