2022-02-18 14:27:39 +03:00
|
|
|
import { UserDto } from '@thornbill/jellyfin-sdk/dist/generated-client';
|
2021-11-13 22:10:34 +03:00
|
|
|
import React, { FunctionComponent, useCallback, useEffect, useState, useRef } from 'react';
|
2021-10-16 00:26:31 +03:00
|
|
|
|
|
|
|
import loading from '../loading/loading';
|
|
|
|
import libraryMenu from '../../scripts/libraryMenu';
|
|
|
|
import globalize from '../../scripts/globalize';
|
|
|
|
import toast from '../toast/toast';
|
|
|
|
import { appRouter } from '../appRouter';
|
|
|
|
import SectionTitleLinkElement from '../dashboard/users/SectionTitleLinkElement';
|
2022-01-02 05:04:08 +03:00
|
|
|
import SectionTabs from '../dashboard/users/SectionTabs';
|
2021-10-16 00:26:31 +03:00
|
|
|
import CheckBoxElement from '../dashboard/users/CheckBoxElement';
|
|
|
|
import CheckBoxListItem from '../dashboard/users/CheckBoxListItem';
|
|
|
|
import ButtonElement from '../dashboard/users/ButtonElement';
|
|
|
|
|
|
|
|
type ItemsArr = {
|
|
|
|
Name?: string;
|
|
|
|
Id?: string;
|
|
|
|
AppName?: string;
|
|
|
|
checkedAttribute?: string
|
|
|
|
}
|
|
|
|
|
|
|
|
const UserLibraryAccessPage: FunctionComponent = () => {
|
|
|
|
const [ userName, setUserName ] = useState('');
|
|
|
|
const [channelsItems, setChannelsItems] = useState([]);
|
|
|
|
const [mediaFoldersItems, setMediaFoldersItems] = useState([]);
|
|
|
|
const [devicesItems, setDevicesItems] = useState([]);
|
|
|
|
|
2022-02-15 23:47:59 +03:00
|
|
|
const element = useRef<HTMLDivElement>(null);
|
2021-10-16 00:26:31 +03:00
|
|
|
|
2022-02-18 14:27:39 +03:00
|
|
|
const triggerChange = (select: HTMLInputElement) => {
|
2021-11-13 22:10:34 +03:00
|
|
|
const evt = document.createEvent('HTMLEvents');
|
|
|
|
evt.initEvent('change', false, true);
|
|
|
|
select.dispatchEvent(evt);
|
|
|
|
};
|
|
|
|
|
|
|
|
const loadMediaFolders = useCallback((user, mediaFolders) => {
|
|
|
|
const itemsArr: ItemsArr[] = [];
|
|
|
|
|
|
|
|
for (const folder of mediaFolders) {
|
|
|
|
const isChecked = user.Policy.EnableAllFolders || user.Policy.EnabledFolders.indexOf(folder.Id) != -1;
|
|
|
|
const checkedAttribute = isChecked ? ' checked="checked"' : '';
|
|
|
|
itemsArr.push({
|
|
|
|
Id: folder.Id,
|
|
|
|
Name: folder.Name,
|
|
|
|
checkedAttribute: checkedAttribute
|
2021-10-16 00:26:31 +03:00
|
|
|
});
|
2021-11-13 22:10:34 +03:00
|
|
|
}
|
2021-10-16 00:26:31 +03:00
|
|
|
|
2021-11-13 22:10:34 +03:00
|
|
|
setMediaFoldersItems(itemsArr);
|
2021-10-16 00:26:31 +03:00
|
|
|
|
2021-11-13 22:10:34 +03:00
|
|
|
const chkEnableAllFolders = element.current.querySelector('.chkEnableAllFolders');
|
|
|
|
chkEnableAllFolders.checked = user.Policy.EnableAllFolders;
|
|
|
|
triggerChange(chkEnableAllFolders);
|
|
|
|
}, []);
|
2021-10-16 00:26:31 +03:00
|
|
|
|
2021-11-13 22:10:34 +03:00
|
|
|
const loadChannels = useCallback((user, channels) => {
|
|
|
|
const itemsArr: ItemsArr[] = [];
|
|
|
|
|
|
|
|
for (const folder of channels) {
|
|
|
|
const isChecked = user.Policy.EnableAllChannels || user.Policy.EnabledChannels.indexOf(folder.Id) != -1;
|
|
|
|
const checkedAttribute = isChecked ? ' checked="checked"' : '';
|
|
|
|
itemsArr.push({
|
|
|
|
Id: folder.Id,
|
|
|
|
Name: folder.Name,
|
|
|
|
checkedAttribute: checkedAttribute
|
|
|
|
});
|
|
|
|
}
|
2021-10-16 00:26:31 +03:00
|
|
|
|
2021-11-13 22:10:34 +03:00
|
|
|
setChannelsItems(itemsArr);
|
2021-10-16 00:26:31 +03:00
|
|
|
|
2021-11-13 22:10:34 +03:00
|
|
|
if (channels.length) {
|
|
|
|
element?.current?.querySelector('.channelAccessContainer').classList.remove('hide');
|
|
|
|
} else {
|
|
|
|
element?.current?.querySelector('.channelAccessContainer').classList.add('hide');
|
|
|
|
}
|
2021-10-16 00:26:31 +03:00
|
|
|
|
2021-11-13 22:10:34 +03:00
|
|
|
const chkEnableAllChannels = element.current.querySelector('.chkEnableAllChannels');
|
|
|
|
chkEnableAllChannels.checked = user.Policy.EnableAllChannels;
|
|
|
|
triggerChange(chkEnableAllChannels);
|
|
|
|
}, []);
|
2021-10-16 00:26:31 +03:00
|
|
|
|
2021-11-13 22:10:34 +03:00
|
|
|
const loadDevices = useCallback((user, devices) => {
|
|
|
|
const itemsArr: ItemsArr[] = [];
|
|
|
|
|
|
|
|
for (const device of devices) {
|
|
|
|
const isChecked = user.Policy.EnableAllDevices || user.Policy.EnabledDevices.indexOf(device.Id) != -1;
|
|
|
|
const checkedAttribute = isChecked ? ' checked="checked"' : '';
|
|
|
|
itemsArr.push({
|
|
|
|
Id: device.Id,
|
|
|
|
Name: device.Name,
|
|
|
|
AppName : device.AppName,
|
|
|
|
checkedAttribute: checkedAttribute
|
|
|
|
});
|
|
|
|
}
|
2021-10-16 00:26:31 +03:00
|
|
|
|
2021-11-13 22:10:34 +03:00
|
|
|
setDevicesItems(itemsArr);
|
2021-10-16 00:26:31 +03:00
|
|
|
|
2021-11-13 22:10:34 +03:00
|
|
|
const chkEnableAllDevices = element.current.querySelector('.chkEnableAllDevices');
|
|
|
|
chkEnableAllDevices.checked = user.Policy.EnableAllDevices;
|
|
|
|
triggerChange(chkEnableAllDevices);
|
2021-10-16 00:26:31 +03:00
|
|
|
|
2021-11-13 22:10:34 +03:00
|
|
|
if (user.Policy.IsAdministrator) {
|
|
|
|
element?.current?.querySelector('.deviceAccessContainer').classList.add('hide');
|
|
|
|
} else {
|
|
|
|
element?.current?.querySelector('.deviceAccessContainer').classList.remove('hide');
|
|
|
|
}
|
|
|
|
}, []);
|
2021-10-16 00:26:31 +03:00
|
|
|
|
2021-11-13 22:10:34 +03:00
|
|
|
const loadUser = useCallback((user, mediaFolders, channels, devices) => {
|
|
|
|
setUserName(user.Name);
|
|
|
|
libraryMenu.setTitle(user.Name);
|
|
|
|
loadChannels(user, channels);
|
|
|
|
loadMediaFolders(user, mediaFolders);
|
|
|
|
loadDevices(user, devices);
|
|
|
|
loading.hide();
|
|
|
|
}, [loadChannels, loadDevices, loadMediaFolders]);
|
|
|
|
|
|
|
|
const loadData = useCallback(() => {
|
|
|
|
loading.show();
|
|
|
|
const userId = appRouter.param('userId');
|
|
|
|
const promise1 = userId ? window.ApiClient.getUser(userId) : Promise.resolve({ Configuration: {} });
|
|
|
|
const promise2 = window.ApiClient.getJSON(window.ApiClient.getUrl('Library/MediaFolders', {
|
|
|
|
IsHidden: false
|
|
|
|
}));
|
|
|
|
const promise3 = window.ApiClient.getJSON(window.ApiClient.getUrl('Channels'));
|
|
|
|
const promise4 = window.ApiClient.getJSON(window.ApiClient.getUrl('Devices'));
|
|
|
|
Promise.all([promise1, promise2, promise3, promise4]).then(function (responses) {
|
|
|
|
loadUser(responses[0], responses[1].Items, responses[2].Items, responses[3].Items);
|
|
|
|
});
|
|
|
|
}, [loadUser]);
|
2021-10-16 00:26:31 +03:00
|
|
|
|
2021-11-13 22:10:34 +03:00
|
|
|
useEffect(() => {
|
|
|
|
loadData();
|
2021-10-16 00:26:31 +03:00
|
|
|
|
2022-02-18 14:27:39 +03:00
|
|
|
const onSubmit = (e: Event) => {
|
2021-10-16 00:26:31 +03:00
|
|
|
loading.show();
|
|
|
|
const userId = appRouter.param('userId');
|
|
|
|
window.ApiClient.getUser(userId).then(function (result) {
|
|
|
|
saveUser(result);
|
|
|
|
});
|
|
|
|
e.preventDefault();
|
|
|
|
e.stopPropagation();
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
|
2022-02-18 14:27:39 +03:00
|
|
|
const saveUser = (user: UserDto) => {
|
|
|
|
if (!user.Id) {
|
|
|
|
throw new Error('Unexpected null user.Id');
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!user.Policy) {
|
|
|
|
throw new Error('Unexpected null user.Policy');
|
|
|
|
}
|
|
|
|
|
2021-10-16 00:26:31 +03:00
|
|
|
user.Policy.EnableAllFolders = element?.current?.querySelector('.chkEnableAllFolders').checked;
|
|
|
|
user.Policy.EnabledFolders = user.Policy.EnableAllFolders ? [] : Array.prototype.filter.call(element?.current?.querySelectorAll('.chkFolder'), function (c) {
|
|
|
|
return c.checked;
|
|
|
|
}).map(function (c) {
|
|
|
|
return c.getAttribute('data-id');
|
|
|
|
});
|
|
|
|
user.Policy.EnableAllChannels = element?.current?.querySelector('.chkEnableAllChannels').checked;
|
|
|
|
user.Policy.EnabledChannels = user.Policy.EnableAllChannels ? [] : Array.prototype.filter.call(element?.current?.querySelectorAll('.chkChannel'), function (c) {
|
|
|
|
return c.checked;
|
|
|
|
}).map(function (c) {
|
|
|
|
return c.getAttribute('data-id');
|
|
|
|
});
|
|
|
|
user.Policy.EnableAllDevices = element?.current?.querySelector('.chkEnableAllDevices').checked;
|
|
|
|
user.Policy.EnabledDevices = user.Policy.EnableAllDevices ? [] : Array.prototype.filter.call(element?.current?.querySelectorAll('.chkDevice'), function (c) {
|
|
|
|
return c.checked;
|
|
|
|
}).map(function (c) {
|
|
|
|
return c.getAttribute('data-id');
|
|
|
|
});
|
|
|
|
user.Policy.BlockedChannels = null;
|
|
|
|
user.Policy.BlockedMediaFolders = null;
|
|
|
|
window.ApiClient.updateUserPolicy(user.Id, user.Policy).then(function () {
|
|
|
|
onSaveComplete();
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
const onSaveComplete = () => {
|
|
|
|
loading.hide();
|
|
|
|
toast(globalize.translate('SettingsSaved'));
|
|
|
|
};
|
|
|
|
|
|
|
|
element?.current?.querySelector('.chkEnableAllDevices').addEventListener('change', function (this: HTMLInputElement) {
|
2022-01-07 03:33:25 +03:00
|
|
|
element?.current?.querySelector('.deviceAccessListContainer').classList.toggle('hide', this.checked);
|
2021-10-16 00:26:31 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
element?.current?.querySelector('.chkEnableAllChannels').addEventListener('change', function (this: HTMLInputElement) {
|
2022-01-07 03:33:25 +03:00
|
|
|
element?.current?.querySelector('.channelAccessListContainer').classList.toggle('hide', this.checked);
|
2021-10-16 00:26:31 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
element?.current?.querySelector('.chkEnableAllFolders').addEventListener('change', function (this: HTMLInputElement) {
|
2022-01-07 03:33:25 +03:00
|
|
|
element?.current?.querySelector('.folderAccessListContainer').classList.toggle('hide', this.checked);
|
2021-10-16 00:26:31 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
element?.current?.querySelector('.userLibraryAccessForm').addEventListener('submit', onSubmit);
|
2021-11-13 22:10:34 +03:00
|
|
|
}, [loadData]);
|
2021-10-16 00:26:31 +03:00
|
|
|
|
|
|
|
return (
|
|
|
|
<div ref={element}>
|
|
|
|
<div className='content-primary'>
|
|
|
|
<div className='verticalSection'>
|
|
|
|
<div className='sectionTitleContainer flex align-items-center'>
|
|
|
|
<h2 className='sectionTitle username'>
|
|
|
|
{userName}
|
|
|
|
</h2>
|
|
|
|
<SectionTitleLinkElement
|
|
|
|
className='raised button-alt headerHelpButton'
|
|
|
|
title='Help'
|
|
|
|
url='https://docs.jellyfin.org/general/server/users/'
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
</div>
|
2022-01-02 05:04:08 +03:00
|
|
|
<SectionTabs activeTab='userlibraryaccess'/>
|
2021-10-16 00:26:31 +03:00
|
|
|
<form className='userLibraryAccessForm'>
|
|
|
|
<div className='folderAccessContainer'>
|
|
|
|
<h2>{globalize.translate('HeaderLibraryAccess')}</h2>
|
|
|
|
<CheckBoxElement
|
2022-01-02 05:04:08 +03:00
|
|
|
labelClassName='checkboxContainer'
|
2021-10-16 00:26:31 +03:00
|
|
|
type='checkbox'
|
|
|
|
className='chkEnableAllFolders'
|
|
|
|
title='OptionEnableAccessToAllLibraries'
|
|
|
|
/>
|
|
|
|
<div className='folderAccessListContainer'>
|
|
|
|
<div className='folderAccess'>
|
|
|
|
<h3 className='checkboxListLabel'>
|
|
|
|
{globalize.translate('HeaderLibraries')}
|
|
|
|
</h3>
|
2022-01-02 05:04:08 +03:00
|
|
|
<div className='checkboxList paperList checkboxList-paperList'>
|
2021-10-16 00:26:31 +03:00
|
|
|
{mediaFoldersItems.map(Item => {
|
|
|
|
return (
|
|
|
|
<CheckBoxListItem
|
|
|
|
key={Item.Id}
|
|
|
|
className='chkFolder'
|
|
|
|
Id={Item.Id}
|
|
|
|
Name={Item.Name}
|
|
|
|
checkedAttribute={Item.checkedAttribute}
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
})}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div className='fieldDescription'>
|
|
|
|
{globalize.translate('LibraryAccessHelp')}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div className='channelAccessContainer hide'>
|
|
|
|
<h2>{globalize.translate('HeaderChannelAccess')}</h2>
|
|
|
|
<CheckBoxElement
|
2022-01-02 05:04:08 +03:00
|
|
|
labelClassName='checkboxContainer'
|
2021-10-16 00:26:31 +03:00
|
|
|
type='checkbox'
|
|
|
|
className='chkEnableAllChannels'
|
|
|
|
title='OptionEnableAccessToAllChannels'
|
|
|
|
/>
|
|
|
|
<div className='channelAccessListContainer'>
|
|
|
|
<div className='channelAccess'>
|
|
|
|
<h3 className='checkboxListLabel'>
|
|
|
|
{globalize.translate('Channels')}
|
|
|
|
</h3>
|
|
|
|
<div className='checkboxList paperList' style={{padding: '.5em 1em'}}>
|
|
|
|
{channelsItems.map(Item => (
|
|
|
|
<CheckBoxListItem
|
|
|
|
key={Item.Id}
|
|
|
|
className='chkChannel'
|
|
|
|
Id={Item.Id}
|
|
|
|
Name={Item.Name}
|
|
|
|
checkedAttribute={Item.checkedAttribute}
|
|
|
|
/>
|
|
|
|
))}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div className='fieldDescription'>
|
|
|
|
{globalize.translate('ChannelAccessHelp')}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<br />
|
|
|
|
<div className='deviceAccessContainer hide'>
|
|
|
|
<h2>{globalize.translate('HeaderDeviceAccess')}</h2>
|
|
|
|
<CheckBoxElement
|
2022-01-02 05:04:08 +03:00
|
|
|
labelClassName='checkboxContainer'
|
2021-10-16 00:26:31 +03:00
|
|
|
type='checkbox'
|
|
|
|
className='chkEnableAllDevices'
|
|
|
|
title='OptionEnableAccessFromAllDevices'
|
|
|
|
/>
|
|
|
|
<div className='deviceAccessListContainer'>
|
|
|
|
<div className='deviceAccess'>
|
|
|
|
<h3 className='checkboxListLabel'>
|
|
|
|
{globalize.translate('HeaderDevices')}
|
|
|
|
</h3>
|
|
|
|
<div className='checkboxList paperList' style={{padding: '.5em 1em'}}>
|
|
|
|
{devicesItems.map(Item => (
|
|
|
|
<CheckBoxListItem
|
|
|
|
key={Item.Id}
|
|
|
|
className='chkDevice'
|
|
|
|
Id={Item.Id}
|
|
|
|
Name={Item.Name}
|
|
|
|
AppName={Item.AppName}
|
|
|
|
checkedAttribute={Item.checkedAttribute}
|
|
|
|
/>
|
|
|
|
))}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div className='fieldDescription'>
|
|
|
|
{globalize.translate('DeviceAccessHelp')}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<br />
|
|
|
|
</div>
|
|
|
|
<br />
|
|
|
|
<div>
|
|
|
|
<ButtonElement
|
|
|
|
type='submit'
|
|
|
|
className='raised button-submit block'
|
|
|
|
title='Save'
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
</form>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
export default UserLibraryAccessPage;
|