import type { SyncPlayUserAccessType, UserDto } from '@jellyfin/sdk/lib/generated-client'; import React, { FunctionComponent, useCallback, useEffect, useState, useRef } from 'react'; import escapeHTML from 'escape-html'; import Dashboard from '../../../../utils/dashboard'; import globalize from '../../../../scripts/globalize'; import LibraryMenu from '../../../../scripts/libraryMenu'; import ButtonElement from '../../../../elements/ButtonElement'; import CheckBoxElement from '../../../../elements/CheckBoxElement'; import InputElement from '../../../../elements/InputElement'; import LinkEditUserPreferences from '../../../../components/dashboard/users/LinkEditUserPreferences'; import SectionTitleContainer from '../../../../elements/SectionTitleContainer'; import SectionTabs from '../../../../components/dashboard/users/SectionTabs'; import loading from '../../../../components/loading/loading'; import toast from '../../../../components/toast/toast'; import { getParameterByName } from '../../../../utils/url'; import SelectElement from '../../../../elements/SelectElement'; import Page from '../../../../components/Page'; type ResetProvider = AuthProvider & { checkedAttribute: string }; type AuthProvider = { Name?: string; Id?: string; }; const getCheckedElementDataIds = (elements: NodeListOf) => ( Array.prototype.filter.call(elements, e => e.checked) .map(e => e.getAttribute('data-id')) ); function onSaveComplete() { Dashboard.navigate('userprofiles.html'); loading.hide(); toast(globalize.translate('SettingsSaved')); } const UserEdit: FunctionComponent = () => { const [ userName, setUserName ] = useState(''); const [ deleteFoldersAccess, setDeleteFoldersAccess ] = useState([]); const [ authProviders, setAuthProviders ] = useState([]); const [ passwordResetProviders, setPasswordResetProviders ] = useState([]); const [ authenticationProviderId, setAuthenticationProviderId ] = useState(''); const [ passwordResetProviderId, setPasswordResetProviderId ] = useState(''); const element = useRef(null); const triggerChange = (select: HTMLInputElement) => { const evt = document.createEvent('HTMLEvents'); evt.initEvent('change', false, true); select.dispatchEvent(evt); }; const getUser = () => { const userId = getParameterByName('userId'); return window.ApiClient.getUser(userId); }; const loadAuthProviders = useCallback((user, providers) => { const page = element.current; if (!page) { console.error('Unexpected null reference'); return; } const fldSelectLoginProvider = page.querySelector('.fldSelectLoginProvider') as HTMLDivElement; fldSelectLoginProvider.classList.toggle('hide', providers.length <= 1); setAuthProviders(providers); const currentProviderId = user.Policy.AuthenticationProviderId; setAuthenticationProviderId(currentProviderId); }, []); const loadPasswordResetProviders = useCallback((user, providers) => { const page = element.current; if (!page) { console.error('Unexpected null reference'); return; } const fldSelectPasswordResetProvider = page.querySelector('.fldSelectPasswordResetProvider') as HTMLDivElement; fldSelectPasswordResetProvider.classList.toggle('hide', providers.length <= 1); setPasswordResetProviders(providers); const currentProviderId = user.Policy.PasswordResetProviderId; setPasswordResetProviderId(currentProviderId); }, []); const loadDeleteFolders = useCallback((user, mediaFolders) => { const page = element.current; if (!page) { console.error('Unexpected null reference'); return; } window.ApiClient.getJSON(window.ApiClient.getUrl('Channels', { SupportsMediaDeletion: true })).then(function (channelsResult) { let isChecked; let checkedAttribute; const itemsArr: ResetProvider[] = []; for (const folder of mediaFolders) { isChecked = user.Policy.EnableContentDeletion || user.Policy.EnableContentDeletionFromFolders.indexOf(folder.Id) != -1; checkedAttribute = isChecked ? ' checked="checked"' : ''; itemsArr.push({ Id: folder.Id, Name: folder.Name, checkedAttribute: checkedAttribute }); } for (const folder of channelsResult.Items) { isChecked = user.Policy.EnableContentDeletion || user.Policy.EnableContentDeletionFromFolders.indexOf(folder.Id) != -1; checkedAttribute = isChecked ? ' checked="checked"' : ''; itemsArr.push({ Id: folder.Id, Name: folder.Name, checkedAttribute: checkedAttribute }); } setDeleteFoldersAccess(itemsArr); const chkEnableDeleteAllFolders = page.querySelector('.chkEnableDeleteAllFolders') as HTMLInputElement; chkEnableDeleteAllFolders.checked = user.Policy.EnableContentDeletion; triggerChange(chkEnableDeleteAllFolders); }); }, []); const loadUser = useCallback((user) => { const page = element.current; if (!page) { console.error('Unexpected null reference'); return; } window.ApiClient.getJSON(window.ApiClient.getUrl('Auth/Providers')).then(function (providers) { loadAuthProviders(user, providers); }); window.ApiClient.getJSON(window.ApiClient.getUrl('Auth/PasswordResetProviders')).then(function (providers) { loadPasswordResetProviders(user, providers); }); window.ApiClient.getJSON(window.ApiClient.getUrl('Library/MediaFolders', { IsHidden: false })).then(function (folders) { loadDeleteFolders(user, folders.Items); }); const disabledUserBanner = page.querySelector('.disabledUserBanner') as HTMLDivElement; disabledUserBanner.classList.toggle('hide', !user.Policy.IsDisabled); const txtUserName = page.querySelector('#txtUserName') as HTMLInputElement; txtUserName.disabled = false; txtUserName.removeAttribute('disabled'); const lnkEditUserPreferences = page.querySelector('.lnkEditUserPreferences') as HTMLDivElement; lnkEditUserPreferences.setAttribute('href', 'mypreferencesmenu.html?userId=' + user.Id); LibraryMenu.setTitle(user.Name); setUserName(user.Name); (page.querySelector('#txtUserName') as HTMLInputElement).value = user.Name; (page.querySelector('.chkIsAdmin') as HTMLInputElement).checked = user.Policy.IsAdministrator; (page.querySelector('.chkDisabled') as HTMLInputElement).checked = user.Policy.IsDisabled; (page.querySelector('.chkIsHidden') as HTMLInputElement).checked = user.Policy.IsHidden; (page.querySelector('.chkEnableCollectionManagement') as HTMLInputElement).checked = user.Policy.EnableCollectionManagement; (page.querySelector('.chkRemoteControlSharedDevices') as HTMLInputElement).checked = user.Policy.EnableSharedDeviceControl; (page.querySelector('.chkEnableRemoteControlOtherUsers') as HTMLInputElement).checked = user.Policy.EnableRemoteControlOfOtherUsers; (page.querySelector('.chkEnableDownloading') as HTMLInputElement).checked = user.Policy.EnableContentDownloading; (page.querySelector('.chkManageLiveTv') as HTMLInputElement).checked = user.Policy.EnableLiveTvManagement; (page.querySelector('.chkEnableLiveTvAccess') as HTMLInputElement).checked = user.Policy.EnableLiveTvAccess; (page.querySelector('.chkEnableMediaPlayback') as HTMLInputElement).checked = user.Policy.EnableMediaPlayback; (page.querySelector('.chkEnableAudioPlaybackTranscoding') as HTMLInputElement).checked = user.Policy.EnableAudioPlaybackTranscoding; (page.querySelector('.chkEnableVideoPlaybackTranscoding') as HTMLInputElement).checked = user.Policy.EnableVideoPlaybackTranscoding; (page.querySelector('.chkEnableVideoPlaybackRemuxing') as HTMLInputElement).checked = user.Policy.EnablePlaybackRemuxing; (page.querySelector('.chkForceRemoteSourceTranscoding') as HTMLInputElement).checked = user.Policy.ForceRemoteSourceTranscoding; (page.querySelector('.chkRemoteAccess') as HTMLInputElement).checked = user.Policy.EnableRemoteAccess == null || user.Policy.EnableRemoteAccess; (page.querySelector('#txtRemoteClientBitrateLimit') as HTMLInputElement).value = user.Policy.RemoteClientBitrateLimit > 0 ? (user.Policy.RemoteClientBitrateLimit / 1e6).toLocaleString(undefined, { maximumFractionDigits: 6 }) : ''; (page.querySelector('#txtLoginAttemptsBeforeLockout') as HTMLInputElement).value = user.Policy.LoginAttemptsBeforeLockout || '0'; (page.querySelector('#txtMaxActiveSessions') as HTMLInputElement).value = user.Policy.MaxActiveSessions || '0'; if (window.ApiClient.isMinServerVersion('10.6.0')) { (page.querySelector('#selectSyncPlayAccess') as HTMLSelectElement).value = user.Policy.SyncPlayAccess; } loading.hide(); }, [loadAuthProviders, loadPasswordResetProviders, loadDeleteFolders ]); const loadData = useCallback(() => { loading.show(); getUser().then(function (user) { loadUser(user); }); }, [loadUser]); useEffect(() => { const page = element.current; if (!page) { console.error('Unexpected null reference'); return; } loadData(); const saveUser = (user: UserDto) => { if (!user.Id || !user.Policy) { throw new Error('Unexpected null user id or policy'); } user.Name = (page.querySelector('#txtUserName') as HTMLInputElement).value; user.Policy.IsAdministrator = (page.querySelector('.chkIsAdmin') as HTMLInputElement).checked; user.Policy.IsHidden = (page.querySelector('.chkIsHidden') as HTMLInputElement).checked; user.Policy.IsDisabled = (page.querySelector('.chkDisabled') as HTMLInputElement).checked; user.Policy.EnableRemoteControlOfOtherUsers = (page.querySelector('.chkEnableRemoteControlOtherUsers') as HTMLInputElement).checked; user.Policy.EnableLiveTvManagement = (page.querySelector('.chkManageLiveTv') as HTMLInputElement).checked; user.Policy.EnableLiveTvAccess = (page.querySelector('.chkEnableLiveTvAccess') as HTMLInputElement).checked; user.Policy.EnableSharedDeviceControl = (page.querySelector('.chkRemoteControlSharedDevices') as HTMLInputElement).checked; user.Policy.EnableMediaPlayback = (page.querySelector('.chkEnableMediaPlayback') as HTMLInputElement).checked; user.Policy.EnableAudioPlaybackTranscoding = (page.querySelector('.chkEnableAudioPlaybackTranscoding') as HTMLInputElement).checked; user.Policy.EnableVideoPlaybackTranscoding = (page.querySelector('.chkEnableVideoPlaybackTranscoding') as HTMLInputElement).checked; user.Policy.EnablePlaybackRemuxing = (page.querySelector('.chkEnableVideoPlaybackRemuxing') as HTMLInputElement).checked; user.Policy.EnableCollectionManagement = (page.querySelector('.chkEnableCollectionManagement') as HTMLInputElement).checked; user.Policy.ForceRemoteSourceTranscoding = (page.querySelector('.chkForceRemoteSourceTranscoding') as HTMLInputElement).checked; user.Policy.EnableContentDownloading = (page.querySelector('.chkEnableDownloading') as HTMLInputElement).checked; user.Policy.EnableRemoteAccess = (page.querySelector('.chkRemoteAccess') as HTMLInputElement).checked; user.Policy.RemoteClientBitrateLimit = Math.floor(1e6 * parseFloat((page.querySelector('#txtRemoteClientBitrateLimit') as HTMLInputElement).value || '0')); user.Policy.LoginAttemptsBeforeLockout = parseInt((page.querySelector('#txtLoginAttemptsBeforeLockout') as HTMLInputElement).value || '0', 10); user.Policy.MaxActiveSessions = parseInt((page.querySelector('#txtMaxActiveSessions') as HTMLInputElement).value || '0', 10); user.Policy.AuthenticationProviderId = (page.querySelector('#selectLoginProvider') as HTMLSelectElement).value; user.Policy.PasswordResetProviderId = (page.querySelector('#selectPasswordResetProvider') as HTMLSelectElement).value; user.Policy.EnableContentDeletion = (page.querySelector('.chkEnableDeleteAllFolders') as HTMLInputElement).checked; user.Policy.EnableContentDeletionFromFolders = user.Policy.EnableContentDeletion ? [] : getCheckedElementDataIds(page.querySelectorAll('.chkFolder')); user.Policy.SyncPlayAccess = (page.querySelector('#selectSyncPlayAccess') as HTMLSelectElement).value as SyncPlayUserAccessType; window.ApiClient.updateUser(user) .then(() => ( window.ApiClient.updateUserPolicy(user.Id || '', user.Policy || {}) )).then(() => { onSaveComplete(); }); }; const onSubmit = (e: Event) => { loading.show(); getUser().then(function (result) { saveUser(result); }); e.preventDefault(); e.stopPropagation(); return false; }; (page.querySelector('.chkEnableDeleteAllFolders') as HTMLInputElement).addEventListener('change', function (this: HTMLInputElement) { (page.querySelector('.deleteAccess') as HTMLDivElement).classList.toggle('hide', this.checked); }); window.ApiClient.getNamedConfiguration('network').then(function (config) { (page.querySelector('.fldRemoteAccess') as HTMLDivElement).classList.toggle('hide', !config.EnableRemoteAccess); }); (page.querySelector('.editUserProfileForm') as HTMLFormElement).addEventListener('submit', onSubmit); (page.querySelector('#btnCancel') as HTMLButtonElement).addEventListener('click', function() { window.history.back(); }); }, [loadData]); const optionLoginProvider = authProviders.map((provider) => { const selected = provider.Id === authenticationProviderId || authProviders.length < 2 ? ' selected' : ''; return ``; }); const optionPasswordResetProvider = passwordResetProviders.map((provider) => { const selected = provider.Id === passwordResetProviderId || passwordResetProviders.length < 2 ? ' selected' : ''; return ``; }); const optionSyncPlayAccess = () => { let content = ''; content += ``; content += ``; content += ``; return content; }; return (
{globalize.translate('HeaderThisUserIsCurrentlyDisabled')}
{globalize.translate('MessageReenableUser')}
{optionLoginProvider}
{globalize.translate('AuthProviderHelp')}
{optionPasswordResetProvider}
{globalize.translate('PasswordResetProviderHelp')}
{globalize.translate('AllowRemoteAccessHelp')}

{globalize.translate('HeaderFeatureAccess')}

{globalize.translate('HeaderPlayback')}

{globalize.translate('OptionAllowMediaPlaybackTranscodingHelp')}

{globalize.translate('LabelRemoteClientBitrateLimitHelp')}
{globalize.translate('LabelUserRemoteClientBitrateLimitHelp')}
{optionSyncPlayAccess()}
{globalize.translate('SyncPlayAccessHelp')}

{globalize.translate('HeaderAllowMediaDeletionFrom')}

{deleteFoldersAccess.map(Item => ( ))}

{globalize.translate('HeaderRemoteControl')}

{globalize.translate('OptionAllowRemoteSharedDevicesHelp')}

{globalize.translate('Other')}

{globalize.translate('OptionAllowContentDownloadHelp')}
{globalize.translate('OptionDisableUserHelp')}
{globalize.translate('OptionHideUserFromLoginHelp')}

{globalize.translate('OptionLoginAttemptsBeforeLockout')}
{globalize.translate('OptionLoginAttemptsBeforeLockoutHelp')}

{globalize.translate('OptionMaxActiveSessions')}
{globalize.translate('OptionMaxActiveSessionsHelp')}

); }; export default UserEdit;