Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Generate token in the backend
Signed-off-by: Louis Chemineau <[email protected]>
  • Loading branch information
artonge committed Oct 20, 2022
commit bfaab638d7274a6fb55428f20b96e279ba380002
18 changes: 14 additions & 4 deletions lib/Album/AlbumMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\Files\IMimeTypeLoader;
use OCP\Security\ISecureRandom;
use OCP\IDBConnection;
use OCP\IGroup;
use OCP\IUser;
Expand All @@ -42,6 +43,7 @@ class AlbumMapper {
private IUserManager $userManager;
private IGroupManager $groupManager;
protected IL10N $l;
protected ISecureRandom $random;

// Same mapping as IShare.
public const TYPE_USER = 0;
Expand All @@ -54,14 +56,16 @@ public function __construct(
ITimeFactory $timeFactory,
IUserManager $userManager,
IGroupManager $groupManager,
IL10N $l
IL10N $l,
ISecureRandom $random
) {
$this->connection = $connection;
$this->mimeTypeLoader = $mimeTypeLoader;
$this->timeFactory = $timeFactory;
$this->userManager = $userManager;
$this->groupManager = $groupManager;
$this->l = $l;
$this->random = $random;
}

public function create(string $userId, string $name, string $location = ""): AlbumInfo {
Expand Down Expand Up @@ -321,7 +325,7 @@ public function getCollaborators(int $albumId): array {
return [
'id' => $row['collaborator_id'],
'label' => $displayName,
'type' => $row['collaborator_type'],
'type' => (int)$row['collaborator_type'],
];
}, $rows);

Expand All @@ -335,8 +339,13 @@ public function getCollaborators(int $albumId): array {
public function setCollaborators(int $albumId, array $collaborators): void {
$existingCollaborators = $this->getCollaborators($albumId);

$collaboratorsToAdd = array_udiff($collaborators, $existingCollaborators, fn ($a, $b) => strcmp($a['id'].$a['type'], $b['id'].$b['type']));
$collaboratorsToRemove = array_udiff($existingCollaborators, $collaborators, fn ($a, $b) => strcmp($a['id'].$a['type'], $b['id'].$b['type']));
// Different behavior if type is link to prevent creating multiple link.
function computeKey($c) {
return ($c['type'] === AlbumMapper::TYPE_LINK ? '' : $c['id']).$c['type'];
}

$collaboratorsToAdd = array_udiff($collaborators, $existingCollaborators, fn ($a, $b) => strcmp(computeKey($a), computeKey($b)));
$collaboratorsToRemove = array_udiff($existingCollaborators, $collaborators, fn ($a, $b) => strcmp(computeKey($a), computeKey($b)));

$this->connection->beginTransaction();

Expand All @@ -353,6 +362,7 @@ public function setCollaborators(int $albumId, array $collaborators): void {
}
break;
case self::TYPE_LINK:
$collaborator['id'] = $this->random->generate(15, ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_DIGITS);
break;
default:
throw new \Exception('Invalid collaborator type: ' . $collaborator['type']);
Expand Down
4 changes: 4 additions & 0 deletions lib/Sabre/Album/PublicAlbumRoot.php
Original file line number Diff line number Diff line change
Expand Up @@ -98,4 +98,8 @@ public function getCollaborators(): array {
/** @var array{array{'nc:collaborator': array{'id': string, 'label': string, 'type': int}}} */
return [];
}

public function setCollaborators($collaborators): array {
throw new Forbidden('Not allowed to collaborators a public album');
}
}
4 changes: 4 additions & 0 deletions lib/Sabre/Album/SharedAlbumRoot.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,8 @@ public function getCollaborators(): array {
/** @var array{array{'nc:collaborator': array{'id': string, 'label': string, 'type': int}}} */
return [];
}

public function setCollaborators($collaborators): array {
throw new Forbidden('Not allowed to collaborators a public album');
}
}
2 changes: 1 addition & 1 deletion src/PhotosPublic.vue
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export default {
window.addEventListener('load', async () => {
try {
const url = generateUrl('/apps/photos/service-worker.js', {}, { noRewrite: true })
const registration = await navigator.serviceWorker.register(url, { scope: '/' })
const registration = await navigator.serviceWorker.register(url, { scope: generateUrl('/apps/photos') })
logger.debug('SW registered: ', registration)
} catch (error) {
logger.error('SW registration failed: ', error)
Expand Down
133 changes: 91 additions & 42 deletions src/components/Albums/CollaboratorsSelectionForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@
<template v-if="isPublicLinkSelected">
<NcButton class="manage-collaborators__public-link-button"
:aria-label="t('photos', 'Copy the public link')"
:disabled="publicLink.id === ''"
@click="copyPublicLink">
<template v-if="publicLinkCopied">
{{ t('photos', 'Public link copied!') }}
Expand All @@ -102,8 +103,12 @@
<ContentCopy v-else />
</template>
</NcButton>
<NcButton type="tertiary" :aria-label="t('photos', 'Delete the public link')" @click="deletePublicLink">
<Close slot="icon" />
<NcButton type="tertiary"
:aria-label="t('photos', 'Delete the public link')"
:disabled="publicLink.id === ''"
@click="deletePublicLink">
<NcLoadingIcon v-if="publicLink.id === ''" slot="icon" />
<Close v-else slot="icon" />
</NcButton>
</template>
<NcButton v-else
Expand Down Expand Up @@ -137,6 +142,8 @@ import { generateOcsUrl, generateUrl } from '@nextcloud/router'
import { NcButton, NcListItemIcon, NcLoadingIcon, NcPopover, NcTextField, NcEmptyContent } from '@nextcloud/vue'

import logger from '../../services/logger.js'
import AbortControllerMixin from '../../mixins/AbortControllerMixin.js'
import { fetchAlbum } from '../../services/Albums.js'

/**
* @typedef {object} Collaborator
Expand All @@ -163,6 +170,8 @@ export default {
NcEmptyContent,
},

mixins: [AbortControllerMixin],

props: {
albumName: {
type: String,
Expand Down Expand Up @@ -190,18 +199,14 @@ export default {
selectedCollaboratorsKeys: [],
/** @type {Collaborator[]} */
currentSearchResults: [],
loadingAlbum: false,
errorFetchingAlbum: null,
loadingCollaborators: false,
randomId: Math.random().toString().substring(2, 10),
publicLinkCopied: false,
config: {
minSearchStringLength: parseInt(OC.config['sharing.minSearchStringLength'], 10) || 0,
},
/** @type {Collaborator} */
publicLink: {
id: (Math.random().toString(36).substring(2) + Math.random().toString(36).substring(2)).substring(0, 15),
label: t('photos', 'Public link'),
type: OC.Share.SHARE_TYPE_LINK,
},
}
},

Expand Down Expand Up @@ -236,32 +241,28 @@ export default {
* @return {boolean}
*/
isPublicLinkSelected() {
return this.selectedCollaborators
.some(collaborator => collaborator.type === OC.Share.SHARE_TYPE_LINK)
return this.selectedCollaboratorsKeys.includes(OC.Share.SHARE_TYPE_LINK.toString())
},

/** @return {Collaborator} */
publicLink() {
return this.availableCollaborators[OC.Share.SHARE_TYPE_LINK]
},
},

watch: {
collaborators(collaborators) {
this.populateCollaborators(collaborators)
},
},

mounted() {
this.searchCollaborators()

const initialCollaborators = this.collaborators.reduce(this.indexCollaborators, {})
const publicLink = this.collaborators.find(collaborator => collaborator.type === OC.Share.SHARE_TYPE_LINK)

if (publicLink !== undefined) {
this.publicLink = publicLink
}

this.selectedCollaboratorsKeys = Object.keys(initialCollaborators)
this.availableCollaborators = {
[`${this.publicLink.type}:${this.publicLink.id}`]: this.publicLink,
...this.availableCollaborators,
...initialCollaborators,
}
this.populateCollaborators(this.collaborators)
},

methods: {
...mapActions(['updateAlbum']),
...mapActions(['updateAlbum', 'addAlbums']),

/**
* Fetch possible collaborators.
Expand Down Expand Up @@ -309,42 +310,85 @@ export default {
}
},

/**
* Populate selectedCollaboratorsKeys and availableCollaborators.
*
* @param {Collaborator[]} collaborators
*/
populateCollaborators(collaborators) {
const initialCollaborators = collaborators.reduce(this.indexCollaborators, {})
this.selectedCollaboratorsKeys = Object.keys(initialCollaborators)
this.availableCollaborators = {
3: {
id: '',
label: t('photos', 'Public link'),
type: OC.Share.SHARE_TYPE_LINK,
},
...this.availableCollaborators,
...initialCollaborators,
}
},

/**
* @param {Object<string, Collaborator>} collaborators - Index of collaborators
* @param {Collaborator} collaborator - A collaborator
*/
indexCollaborators(collaborators, collaborator) {
return { ...collaborators, [`${collaborator.type}:${collaborator.id}`]: collaborator }
return { ...collaborators, [`${collaborator.type}${collaborator.type === OC.Share.SHARE_TYPE_LINK ? '' : ':'}${collaborator.type === OC.Share.SHARE_TYPE_LINK ? '' : collaborator.id}`]: collaborator }
},

async createPublicLinkForAlbum() {
this.selectEntity(`${this.publicLink.type}:${this.publicLink.id}`)
this.selectEntity(OC.Share.SHARE_TYPE_LINK.toString())
await this.updateAlbumCollaborators()
},
try {
this.loadingAlbum = true
this.errorFetchingAlbum = null

async deletePublicLink() {
this.unselectEntity(`${this.publicLink.type}:${this.publicLink.id}`)
const album = await fetchAlbum(
`/photos/${getCurrentUser().uid}/albums/${this.albumName}`,
{ signal: this.abortController.signal }
)

this.publicLinkCopied = false
this.addAlbums({ albums: [album] })
} catch (error) {
if (error.response?.status === 404) {
this.errorFetchingAlbum = 404
} else {
this.errorFetchingAlbum = error
}

delete this.availableCollaborators[`${this.publicLink.type}:${this.publicLink.id}`]
this.publicLink = {
id: (Math.random().toString(36).substring(2) + Math.random().toString(36).substring(2)).substring(0, 15),
logger.error('[PublicAlbumContent] Error fetching album', error)
showError(this.t('photos', 'Failed to fetch album.'))
} finally {
this.loadingAlbum = false
}
},

async deletePublicLink() {
this.unselectEntity(OC.Share.SHARE_TYPE_LINK.toString())
this.availableCollaborators[3] = {
id: '',
label: t('photos', 'Public link'),
type: OC.Share.SHARE_TYPE_LINK,
}
this.availableCollaborators[`${this.publicLink.type}:${this.publicLink.id}`] = this.publicLink

this.publicLinkCopied = false
await this.updateAlbumCollaborators()
},

async updateAlbumCollaborators() {
await this.updateAlbum({
albumName: this.albumName,
properties: {
collaborators: this.selectedCollaborators,
},
})
try {
await this.updateAlbum({
albumName: this.albumName,
properties: {
collaborators: this.selectedCollaborators,
},
})
} catch (error) {
logger.error('[PublicAlbumContent] Error updating album', error)
showError(this.t('photos', 'Failed to update album.'))
} finally {
this.loadingAlbum = false
}
},

async copyPublicLink() {
Expand All @@ -366,6 +410,11 @@ export default {

unselectEntity(collaboratorKey) {
const index = this.selectedCollaboratorsKeys.indexOf(collaboratorKey)

if (index === -1) {
return
}

this.selectedCollaboratorsKeys.splice(index, 1)
},
},
Expand Down
2 changes: 1 addition & 1 deletion src/components/Collection/CollectionContent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
-->
<template>
<!-- Errors handlers-->
<NcEmptyContent v-if="collection === undefined && !loading"
<NcEmptyContent v-if="(collection === undefined && !loading) || error === 404"
class="empty-content-with-illustration"
:title="t('photos', 'This collection does not exist')">
<FolderMultipleImage slot="icon" />
Expand Down
15 changes: 6 additions & 9 deletions src/services/Albums.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,11 @@ import { genFileInfo } from '../utils/fileUtils.js'
*
* @param {string} path - Albums' root path.
* @param {import('webdav').StatOptions} options - Options to forward to the webdav client.
* @param {import('webdav').WebDAVClient} customClient
* @return {Promise<Album|null>}
*/
export async function fetchAlbum(path, options, customClient) {
export async function fetchAlbum(path, options) {
try {
const response = await (customClient || client).stat(path, {
const response = await client.stat(path, {
data: `<?xml version="1.0"?>
<d:propfind xmlns:d="DAV:"
xmlns:oc="http://owncloud.org/ns"
Expand Down Expand Up @@ -82,12 +81,11 @@ export async function fetchAlbum(path, options, customClient) {
*
* @param {string} path - Albums' root path.
* @param {import('webdav').StatOptions} options - Options to forward to the webdav client.
* @param {import('webdav').WebDAVClient} customClient
* @return {Promise<Album[]>}
*/
export async function fetchAlbums(path, options, customClient) {
export async function fetchAlbums(path, options) {
try {
const response = await (customClient || client).getDirectoryContents(path, {
const response = await client.getDirectoryContents(path, {
data: `<?xml version="1.0"?>
<d:propfind xmlns:d="DAV:"
xmlns:oc="http://owncloud.org/ns"
Expand Down Expand Up @@ -162,12 +160,11 @@ function formatAlbum(album) {
*
* @param {string} path - Albums' root path.
* @param {import('webdav').StatOptions} options - Options to forward to the webdav client.
* @param {import('webdav').WebDAVClient} customClient
* @return {Promise<Array>}
*/
export async function fetchAlbumContent(path, options, customClient) {
export async function fetchAlbumContent(path, options) {
try {
const response = await (customClient || client).getDirectoryContents(path, {
const response = await client.getDirectoryContents(path, {
data: DavRequest,
details: true,
...options,
Expand Down
6 changes: 4 additions & 2 deletions src/views/AlbumContent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -288,8 +288,10 @@ export default {
},

watch: {
album() {
this.fetchAlbumContent()
album(newAlbum, oldAlbum) {
if (newAlbum.filename !== oldAlbum.filename) {
this.fetchAlbumContent()
}
},
},

Expand Down
Loading