Make SmartPlaylists read-only

This commit is contained in:
Deluan 2021-10-22 21:11:44 -04:00 committed by Deluan Quintão
parent d169f54e7d
commit 2e2a647e67
9 changed files with 54 additions and 26 deletions

View File

@ -185,6 +185,12 @@ func (r *playlistRepository) refreshSmartPlaylist(pls *model.Playlist) bool {
return false
}
// Never refresh other users' playlists
usr := loggedUser(r.ctx)
if pls.Owner != usr.UserName {
return false
}
log.Debug(r.ctx, "Refreshing smart playlist", "playlist", pls.Name, "id", pls.ID)
start := time.Now()

View File

@ -12,6 +12,7 @@ type playlistTrackRepository struct {
sqlRepository
sqlRestful
playlistId string
playlist *model.Playlist
playlistRepo *playlistRepository
}
@ -32,6 +33,7 @@ func (r *playlistRepository) Tracks(playlistId string) model.PlaylistTrackReposi
if pls.IsSmartPlaylist() {
r.refreshSmartPlaylist(pls)
}
p.playlist = pls
return p
}
@ -79,8 +81,12 @@ func (r *playlistTrackRepository) NewInstance() interface{} {
return &model.PlaylistTrack{}
}
func (r *playlistTrackRepository) isTracksEditable() bool {
return r.playlistRepo.isWritable(r.playlistId) && !r.playlist.IsSmartPlaylist()
}
func (r *playlistTrackRepository) Add(mediaFileIds []string) (int, error) {
if !r.playlistRepo.isWritable(r.playlistId) {
if !r.isTracksEditable() {
return 0, rest.ErrPermissionDenied
}
@ -158,7 +164,7 @@ func (r *playlistTrackRepository) getTracks() ([]string, error) {
}
func (r *playlistTrackRepository) Delete(id string) error {
if !r.playlistRepo.isWritable(r.playlistId) {
if !r.isTracksEditable() {
return rest.ErrPermissionDenied
}
err := r.delete(And{Eq{"playlist_id": r.playlistId}, Eq{"id": id}})
@ -172,7 +178,7 @@ func (r *playlistTrackRepository) Delete(id string) error {
}
func (r *playlistTrackRepository) Reorder(pos int, newPos int) error {
if !r.playlistRepo.isWritable(r.playlistId) {
if !r.isTracksEditable() {
return rest.ErrPermissionDenied
}
ids, err := r.getTracks()

View File

@ -176,6 +176,10 @@ func reorderItem(ds model.DataStore) http.HandlerFunc {
}
tracksRepo := ds.Playlist(r.Context()).Tracks(playlistId)
err = tracksRepo.Reorder(id, newPos)
if err == rest.ErrPermissionDenied {
http.Error(w, err.Error(), http.StatusForbidden)
return
}
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return

View File

@ -20,3 +20,8 @@ export const Writable = (props) => {
}
return null
}
export const isSmartPlaylist = (pls) => !!pls.rules
export const canChangeTracks = (pls) =>
isWritable(pls.owner) && !isSmartPlaylist(pls)

View File

@ -12,7 +12,7 @@ import QueueMusicOutlinedIcon from '@material-ui/icons/QueueMusicOutlined'
import { BiCog } from 'react-icons/all'
import { useDrop } from 'react-dnd'
import SubMenu from './SubMenu'
import { isWritable } from '../common'
import { canChangeTracks } from '../common'
import { DraggableTypes, MAX_SIDEBAR_PLAYLISTS } from '../consts'
const PlaylistMenuItemLink = ({ pls, sidebarIsOpen }) => {
@ -20,7 +20,7 @@ const PlaylistMenuItemLink = ({ pls, sidebarIsOpen }) => {
const notify = useNotify()
const [, dropRef] = useDrop(() => ({
accept: isWritable(pls.owner) ? DraggableTypes.ALL : [],
accept: canChangeTracks(pls) ? DraggableTypes.ALL : [],
drop: (item) =>
dataProvider
.addToPlaylist(pls.id, item)

View File

@ -10,7 +10,7 @@ import {
required,
useTranslate,
} from 'react-admin'
import { Title } from '../common'
import { isSmartPlaylist, isWritable, Title } from '../common'
const SyncFragment = ({ formData, variant, ...rest }) => {
return (
@ -27,16 +27,26 @@ const PlaylistTitle = ({ record }) => {
return <Title subTitle={`${resourceName} "${record ? record.name : ''}"`} />
}
const PlaylistEdit = (props) => (
<Edit title={<PlaylistTitle />} {...props}>
<SimpleForm redirect="list" variant={'outlined'}>
const PlaylistEditForm = (props) => {
const { record } = props
return (
<SimpleForm redirect="list" variant={'outlined'} {...props}>
<TextInput source="name" validate={required()} />
<TextInput multiline source="comment" />
<BooleanInput source="public" />
<BooleanInput
source="public"
disabled={!isWritable(record.owner) || isSmartPlaylist(record)}
/>
<FormDataConsumer>
{(formDataProps) => <SyncFragment {...formDataProps} />}
</FormDataConsumer>
</SimpleForm>
)
}
const PlaylistEdit = (props) => (
<Edit title={<PlaylistTitle />} {...props}>
<PlaylistEditForm {...props} />
</Edit>
)

View File

@ -9,6 +9,7 @@ import {
TextField,
useUpdate,
useNotify,
useRecordContext,
} from 'react-admin'
import Switch from '@material-ui/core/Switch'
import { useMediaQuery } from '@material-ui/core'
@ -19,6 +20,7 @@ import {
isWritable,
useSelectedFields,
useResourceRefresh,
isSmartPlaylist,
} from '../common'
import PlaylistListActions from './PlaylistListActions'
@ -28,7 +30,8 @@ const PlaylistFilter = (props) => (
</Filter>
)
const TogglePublicInput = ({ permissions, resource, record = {}, source }) => {
const TogglePublicInput = ({ resource, source }) => {
const record = useRecordContext()
const notify = useNotify()
const [togglePublic] = useUpdate(
resource,
@ -51,20 +54,16 @@ const TogglePublicInput = ({ permissions, resource, record = {}, source }) => {
e.stopPropagation()
}
const canChange =
permissions === 'admin' ||
localStorage.getItem('username') === record['owner']
return (
<Switch
checked={record[source]}
onClick={handleClick}
disabled={!canChange}
disabled={!isWritable(record.owner) || isSmartPlaylist(record)}
/>
)
}
const PlaylistList = ({ permissions, ...props }) => {
const PlaylistList = (props) => {
const isXsmall = useMediaQuery((theme) => theme.breakpoints.down('xs'))
const isDesktop = useMediaQuery((theme) => theme.breakpoints.up('md'))
useResourceRefresh('playlist')
@ -78,14 +77,10 @@ const PlaylistList = ({ permissions, ...props }) => {
<DateField source="updatedAt" sortByOrder={'DESC'} />
),
public: !isXsmall && (
<TogglePublicInput
source="public"
permissions={permissions}
sortByOrder={'DESC'}
/>
<TogglePublicInput source="public" sortByOrder={'DESC'} />
),
}
}, [isDesktop, isXsmall, permissions])
}, [isDesktop, isXsmall])
const columns = useSelectedFields({
resource: 'playlist',

View File

@ -10,7 +10,8 @@ import { makeStyles } from '@material-ui/core/styles'
import PlaylistDetails from './PlaylistDetails'
import PlaylistSongs from './PlaylistSongs'
import PlaylistActions from './PlaylistActions'
import { Title, isReadOnly } from '../common'
import { Title, canChangeTracks } from '../common'
const useStyles = makeStyles(
(theme) => ({
playlistActions: {
@ -42,7 +43,7 @@ const PlaylistShowLayout = (props) => {
>
<PlaylistSongs
{...props}
readOnly={isReadOnly(record.owner)}
readOnly={!canChangeTracks(record)}
title={<Title subTitle={record.name} />}
actions={
<PlaylistActions

View File

@ -182,6 +182,7 @@ const PlaylistSongs = ({ playlistId, readOnly, actions, ...props }) => {
<PlaylistSongBulkActions
playlistId={playlistId}
onUnselectItems={onUnselectItems}
readOnly={readOnly}
/>
</BulkActionsToolbar>
<ReorderableList
@ -192,7 +193,7 @@ const PlaylistSongs = ({ playlistId, readOnly, actions, ...props }) => {
<SongDatagrid
rowClick={(id) => dispatch(playTracks(data, ids, id))}
{...listContext}
hasBulkActions={true}
hasBulkActions={!readOnly}
contextAlwaysVisible={!isDesktop}
classes={{ row: classes.row }}
>