Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/files/js/fileinfomodel.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@
for (const i in this.attributes.shareAttributes) {
const attr = this.attributes.shareAttributes[i]
if (attr.scope === 'permissions' && attr.key === 'download') {
return attr.enabled
return attr.value === true
}
}

Expand Down
2 changes: 1 addition & 1 deletion apps/files/src/actions/downloadAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const isDownloadable = function(node: Node) {
if (node.attributes['mount-type'] === 'shared') {
const shareAttributes = JSON.parse(node.attributes['share-attributes'] ?? 'null')
const downloadAttribute = shareAttributes?.find?.((attribute: { scope: string; key: string }) => attribute.scope === 'permissions' && attribute.key === 'download')
if (downloadAttribute !== undefined && downloadAttribute.enabled === false) {
if (downloadAttribute !== undefined && downloadAttribute.value === false) {
return false
}
}
Expand Down
4 changes: 2 additions & 2 deletions apps/files/src/actions/moveOrCopyActionUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export const getQueue = () => {
}

type ShareAttribute = {
enabled: boolean
value: boolean|string|number|null|object|Array<unknown>
key: string
scope: string
}
Expand All @@ -48,7 +48,7 @@ export const canMove = (nodes: Node[]) => {
export const canDownload = (nodes: Node[]) => {
return nodes.every(node => {
const shareAttributes = JSON.parse(node.attributes?.['share-attributes'] ?? '[]') as Array<ShareAttribute>
return !shareAttributes.some(attribute => attribute.scope === 'permissions' && attribute.enabled === false && attribute.key === 'download')
return !shareAttributes.some(attribute => attribute.scope === 'permissions' && attribute.value === false && attribute.key === 'download')

})
}
Expand Down
8 changes: 4 additions & 4 deletions apps/files/src/views/FilesList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -396,14 +396,14 @@ export default defineComponent({
return { ...this.$route, query: { dir } }
},

shareAttributes(): number[] | undefined {
shareTypesAttributes(): number[] | undefined {
if (!this.currentFolder?.attributes?.['share-types']) {
return undefined
}
return Object.values(this.currentFolder?.attributes?.['share-types'] || {}).flat() as number[]
},
shareButtonLabel() {
if (!this.shareAttributes) {
if (!this.shareTypesAttributes) {
return t('files', 'Share')
}

Expand All @@ -413,12 +413,12 @@ export default defineComponent({
return t('files', 'Shared')
},
shareButtonType(): Type | null {
if (!this.shareAttributes) {
if (!this.shareTypesAttributes) {
return null
}

// If all types are links, show the link icon
if (this.shareAttributes.some(type => type === Type.SHARE_TYPE_LINK)) {
if (this.shareTypesAttributes.some(type => type === Type.SHARE_TYPE_LINK)) {
return Type.SHARE_TYPE_LINK
}

Expand Down
5 changes: 5 additions & 0 deletions apps/files_sharing/appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,11 @@
'url' => '/api/v1/shares/{id}',
'verb' => 'DELETE',
],
[
'name' => 'ShareAPI#sendShareEmail',
'url' => '/api/v1/shares/{id}/send-email',
'verb' => 'POST',
],
[
'name' => 'ShareAPI#acceptShare',
'url' => '/api/v1/shares/pending/{id}',
Expand Down
121 changes: 107 additions & 14 deletions apps/files_sharing/lib/Controller/ShareAPIController.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
use OCA\Files_Sharing\SharedStorage;
use OCP\App\IAppManager;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\Attribute\UserRateLimit;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCS\OCSBadRequestException;
use OCP\AppFramework\OCS\OCSException;
Expand All @@ -42,11 +44,14 @@
use OCP\IUserManager;
use OCP\Lock\ILockingProvider;
use OCP\Lock\LockedException;
use OCP\Mail\IMailer;
use OCP\Server;
use OCP\Share\Exceptions\GenericShareException;
use OCP\Share\Exceptions\ShareNotFound;
use OCP\Share\IManager;
use OCP\Share\IProviderFactory;
use OCP\Share\IShare;
use OCP\Share\IShareProviderWithNotification;
use OCP\UserStatus\IManager as IUserStatusManager;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\ContainerInterface;
Expand Down Expand Up @@ -81,6 +86,8 @@ public function __construct(
private IPreview $previewManager,
private IDateTimeZone $dateTimeZone,
private LoggerInterface $logger,
private IProviderFactory $factory,
private IMailer $mailer,
?string $userId = null
) {
parent::__construct($appName, $request);
Expand Down Expand Up @@ -517,6 +524,7 @@ public function deleteShare(string $id): DataResponse {
* @param string $note Note for the share
* @param string $label Label for the share (only used in link and email)
* @param string|null $attributes Additional attributes for the share
* @param 'false'|'true'|null $sendMail Send a mail to the recipient
*
* @return DataResponse<Http::STATUS_OK, Files_SharingShare, array{}>
* @throws OCSBadRequestException Unknown share type
Expand All @@ -538,7 +546,8 @@ public function createShare(
?string $expireDate = null,
string $note = '',
string $label = '',
?string $attributes = null
?string $attributes = null,
?string $sendMail = null
): DataResponse {
$share = $this->shareManager->newShare();

Expand Down Expand Up @@ -609,7 +618,7 @@ public function createShare(
$share = $this->setShareAttributes($share, $attributes);
}

//Expire date
// Expire date
if ($expireDate !== null) {
if ($expireDate !== '') {
try {
Expand All @@ -628,6 +637,11 @@ public function createShare(
$share->setSharedBy($this->currentUser);
$this->checkInheritedAttributes($share);

// Handle mail send
if ($sendMail === 'true' || $sendMail === 'false') {
$share->setMailSend($sendMail === 'true');
}

if ($shareType === IShare::TYPE_USER) {
// Valid user is required to share
if ($shareWith === null || !$this->userManager->userExists($shareWith)) {
Expand Down Expand Up @@ -685,6 +699,10 @@ public function createShare(

// Only share by mail have a recipient
if (is_string($shareWith) && $shareType === IShare::TYPE_EMAIL) {
// If sending a mail have been requested, validate the mail address
if ($share->getMailSend() && !$this->mailer->validateMailAddress($shareWith)) {
throw new OCSNotFoundException($this->l->t('Please specify a valid email address'));
}
$share->setSharedWith($shareWith);
}

Expand Down Expand Up @@ -1025,7 +1043,6 @@ private function getFormattedShares(
* 200: Shares returned
*/
public function getInheritedShares(string $path): DataResponse {

// get Node from (string) path.
$userFolder = $this->rootFolder->getUserFolder($this->currentUser);
try {
Expand All @@ -1038,7 +1055,7 @@ public function getInheritedShares(string $path): DataResponse {
}

if (!($node->getPermissions() & Constants::PERMISSION_SHARE)) {
throw new SharingRightsException('no sharing rights on this item');
throw new SharingRightsException($this->l->t('no sharing rights on this item'));
}

// The current top parent we have access to
Expand Down Expand Up @@ -1117,6 +1134,10 @@ private function hasPermission(int $permissionsSet, int $permissionsToCheck): bo
* @param string|null $label New label
* @param string|null $hideDownload New condition if the download should be hidden
* @param string|null $attributes New additional attributes
* @param string|null $sendMail if the share should be send by mail.
* Considering the share already exists, no mail will be send after the share is updated.
* You will have to use the sendMail action to send the mail.
* @param string|null $shareWith New recipient for email shares
* @return DataResponse<Http::STATUS_OK, Files_SharingShare, array{}>
* @throws OCSBadRequestException Share could not be updated because the requested changes are invalid
* @throws OCSForbiddenException Missing permissions to update the share
Expand All @@ -1134,7 +1155,8 @@ public function updateShare(
?string $note = null,
?string $label = null,
?string $hideDownload = null,
?string $attributes = null
?string $attributes = null,
?string $sendMail = null,
): DataResponse {
try {
$share = $this->getShareById($id);
Expand All @@ -1149,7 +1171,7 @@ public function updateShare(
}

if (!$this->canEditShare($share)) {
throw new OCSForbiddenException('You are not allowed to edit incoming shares');
throw new OCSForbiddenException($this->l->t('You are not allowed to edit incoming shares'));
}

if (
Expand All @@ -1161,7 +1183,8 @@ public function updateShare(
$note === null &&
$label === null &&
$hideDownload === null &&
$attributes === null
$attributes === null &&
$sendMail === null
) {
throw new OCSBadRequestException($this->l->t('Wrong or no update parameter given'));
}
Expand All @@ -1175,6 +1198,11 @@ public function updateShare(
}
$this->checkInheritedAttributes($share);

// Handle mail send
if ($sendMail === 'true' || $sendMail === 'false') {
$share->setMailSend($sendMail === 'true');
}

/**
* expirationdate, password and publicUpload only make sense for link shares
*/
Expand All @@ -1190,7 +1218,7 @@ public function updateShare(
*/

if ($share->getSharedBy() !== $this->currentUser) {
throw new OCSForbiddenException('You are not allowed to edit link shares that you don\'t own');
throw new OCSForbiddenException($this->l->t('You are not allowed to edit link shares that you don\'t own'));
}

// Update hide download state
Expand Down Expand Up @@ -1612,7 +1640,7 @@ private function parseDate(string $expireDate): \DateTime {
// Make sure it expires at midnight in owner timezone
$date->setTime(0, 0, 0);
} catch (\Exception $e) {
throw new \Exception('Invalid date. Format must be YYYY-MM-DD');
throw new \Exception($this->l->t('Invalid date. Format must be YYYY-MM-DD'));
}

return $date;
Expand Down Expand Up @@ -1817,7 +1845,7 @@ private function getSharesFromNode(string $viewer, $node, bool $reShares): array
*/
private function confirmSharingRights(Node $node): void {
if (!$this->hasResharingRights($this->currentUser, $node)) {
throw new SharingRightsException('no sharing rights on this item');
throw new SharingRightsException($this->l->t('No sharing rights on this item'));
}
}

Expand Down Expand Up @@ -1983,11 +2011,11 @@ private function setShareAttributes(IShare $share, ?string $attributesString) {
$newShareAttributes->setAttribute(
$formattedAttr['scope'],
$formattedAttr['key'],
is_string($formattedAttr['enabled']) ? (bool) \json_decode($formattedAttr['enabled']) : $formattedAttr['enabled']
$formattedAttr['value'],
);
}
} else {
throw new OCSBadRequestException('Invalid share attributes provided: \"' . $attributesString . '\"');
throw new OCSBadRequestException($this->l->t('Invalid share attributes provided: "%s"', [$attributesString]));
}
}
$share->setAttributes($newShareAttributes);
Expand All @@ -2009,10 +2037,10 @@ private function checkInheritedAttributes(IShare $share): void {
if ($storage instanceof Wrapper) {
$storage = $storage->getInstanceOfStorage(SharedStorage::class);
if ($storage === null) {
throw new \RuntimeException('Should not happen, instanceOfStorage but getInstanceOfStorage return null');
throw new \RuntimeException($this->l->t('Should not happen, instanceOfStorage but getInstanceOfStorage return null'));
}
} else {
throw new \RuntimeException('Should not happen, instanceOfStorage but not a wrapper');
throw new \RuntimeException($this->l->t('Should not happen, instanceOfStorage but not a wrapper'));
}
/** @var \OCA\Files_Sharing\SharedStorage $storage */
$inheritedAttributes = $storage->getShare()->getAttributes();
Expand All @@ -2025,6 +2053,71 @@ private function checkInheritedAttributes(IShare $share): void {
}
}
}
}

/**
* Send a mail notification again for a share.
* The mail_send option must be enabled for the given share.
* @param string $id the share ID
* @param string $password the password to check against. Necessary for password protected shares.
* @throws OCSNotFoundException Share not found
* @throws OCSForbiddenException You are not allowed to send mail notifications
* @throws OCSBadRequestException Invalid request or wrong password
* @throws OCSException Error while sending mail notification
* @return DataResponse<Http::STATUS_OK, array<empty>, array{}>
* 200: The email notification was sent successfully
*/
#[NoAdminRequired]
#[UserRateLimit(limit: 5, period: 120)]
public function sendShareEmail(string $id, $password = ''): DataResponse {
try {
$share = $this->getShareById($id);

if (!$this->canAccessShare($share, false)) {
throw new OCSNotFoundException($this->l->t('Wrong share ID, share does not exist'));
}

if (!$this->canEditShare($share)) {
throw new OCSForbiddenException($this->l->t('You are not allowed to send mail notifications'));
}

// For mail and link shares, the user must be
// the owner of the share, not only the file owner.
if ($share->getShareType() === IShare::TYPE_EMAIL
|| $share->getShareType() === IShare::TYPE_LINK) {
if ($share->getSharedBy() !== $this->currentUser) {
throw new OCSForbiddenException($this->l->t('You are not allowed to send mail notifications'));
}
}

try {
$provider = $this->factory->getProviderForType($share->getShareType());
if (!($provider instanceof IShareProviderWithNotification)) {
throw new OCSBadRequestException($this->l->t('No mail notification configured for this share type'));
}

// Circumvent the password encrypted data by
// setting the password clear. We're not storing
// the password clear, it is just a temporary
// object manipulation. The password will stay
// encrypted in the database.
if ($share->getPassword() !== null && $share->getPassword() !== $password) {
if (!$this->shareManager->checkPassword($share, $password)) {
throw new OCSBadRequestException($this->l->t('Wrong password'));
}
$share = $share->setPassword($password);
}

$provider->sendMailNotification($share);
return new DataResponse();
} catch(OCSBadRequestException $e) {
throw $e;
} catch (Exception $e) {
throw new OCSException($this->l->t('Error while sending mail notification'));
}

} catch (ShareNotFound $e) {
throw new OCSNotFoundException($this->l->t('Wrong share ID, share does not exist'));
}
}
}
2 changes: 1 addition & 1 deletion apps/files_sharing/lib/MountProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ private function buildSuperShares(array $allShares, \OCP\IUser $user) {
continue;
}
// update supershare attributes with subshare attribute
$superAttributes->setAttribute($attribute['scope'], $attribute['key'], $attribute['enabled']);
$superAttributes->setAttribute($attribute['scope'], $attribute['key'], $attribute['value']);
}
}

Expand Down
Loading