diff --git a/src/components/syncplay/groupSelectionMenu.js b/src/components/syncplay/groupSelectionMenu.js index 671b76d5e..046900d8e 100644 --- a/src/components/syncplay/groupSelectionMenu.js +++ b/src/components/syncplay/groupSelectionMenu.js @@ -27,18 +27,17 @@ function emptyCallback() { /** * Used when user needs to join a group. * @param {HTMLElement} button - Element where to place the menu. + * @param {Object} user - Current user. + * @param {Object} apiClient - ApiClient. */ -function showNewJoinGroupSelection(button) { - var apiClient = connectionManager.currentApiClient(); - var sessionId = getActivePlayerId(); +function showNewJoinGroupSelection(button, user, apiClient) { + let sessionId = getActivePlayerId(); sessionId = sessionId ? sessionId : "none"; - - loading.show(); + const inSession = sessionId !== "none"; + const policy = user.localUser ? user.localUser.Policy : {}; apiClient.sendSyncplayCommand(sessionId, "ListGroups").then(function (response) { - response.json().then(function (groups) { - var inSession = sessionId !== "none"; - + response.json().then(function (groups) { var menuItems = groups.map(function (group) { var name = datetime.getDisplayRunningTime(group.PositionTicks); if (!inSession) { @@ -53,7 +52,7 @@ function showNewJoinGroupSelection(button) { }; }); - if (inSession) { + if (inSession && policy.SyncplayAccess === "CreateAndJoinGroups") { menuItems.push({ name: globalize.translate('LabelSyncplayNewGroup'), icon: "add", @@ -64,9 +63,15 @@ function showNewJoinGroupSelection(button) { } if (menuItems.length === 0) { + if (inSession && policy.SyncplayAccess === "JoinGroups") { + toast({ + text: globalize.translate('MessageSyncplayPermissionRequired') + }); + } else { toast({ text: globalize.translate('MessageSyncplayNoGroupsAvailable') }); + } loading.hide(); return; } @@ -92,20 +97,23 @@ function showNewJoinGroupSelection(button) { loading.hide(); }); }).catch(function (error) { - loading.hide(); console.error(error); + loading.hide(); + toast({ + text: globalize.translate('MessageSyncplayNoGroupsAvailable') + }); }); } /** * Used when user has joined a group. * @param {HTMLElement} button - Element where to place the menu. + * @param {Object} user - Current user. + * @param {Object} apiClient - ApiClient. */ -function showLeaveGroupSelection(button) { - const apiClient = connectionManager.currentApiClient(); +function showLeaveGroupSelection(button, user, apiClient) { const sessionId = getActivePlayerId(); - loading.show(); const menuItems = [{ name: globalize.translate('LabelSyncplayLeaveGroup'), @@ -143,9 +151,20 @@ events.on(syncplayManager, 'SyncplayEnabled', function (e, enabled) { * @param {HTMLElement} button - Element where to place the menu. */ export function show(button) { - if (syncplayEnabled) { - showLeaveGroupSelection(button); - } else { - showNewJoinGroupSelection(button); - } + loading.show(); + + const apiClient = connectionManager.currentApiClient(); + connectionManager.user(apiClient).then((user) => { + if (syncplayEnabled) { + showLeaveGroupSelection(button, user, apiClient); + } else { + showNewJoinGroupSelection(button, user, apiClient); + } + }).catch((error) => { + console.error(error); + loading.hide(); + toast({ + text: globalize.translate('MessageSyncplayNoGroupsAvailable') + }); + }); } diff --git a/src/components/syncplay/syncplayManager.js b/src/components/syncplay/syncplayManager.js index bc0fe7bc6..8619508e2 100644 --- a/src/components/syncplay/syncplayManager.js +++ b/src/components/syncplay/syncplayManager.js @@ -645,7 +645,7 @@ class SyncplayManager { /** * Converts server time to local time. - * @param {Date} server + * @param {Date} server The time to convert. * @returns {Date} Local time. */ serverDateToLocal (server) { @@ -655,7 +655,7 @@ class SyncplayManager { /** * Converts local time to server time. - * @param {Date} local + * @param {Date} local The time to convert. * @returns {Date} Server time. */ localDateToServer (local) { @@ -665,7 +665,7 @@ class SyncplayManager { /** * Gets Syncplay status. - * @returns {boolean} _true_ is user joined a group, _false_ otherwise. + * @returns {boolean} _true_ if user joined a group, _false_ otherwise. */ isSyncplayEnabled () { return this.syncplayEnabledAt !== null ? true : false; diff --git a/src/controllers/useredit.js b/src/controllers/useredit.js index 21ed60cfa..cbe5acd36 100644 --- a/src/controllers/useredit.js +++ b/src/controllers/useredit.js @@ -104,6 +104,7 @@ define(['jQuery', 'loading', 'libraryMenu', 'globalize', 'fnchecked'], function $('#chkEnableSharing', page).checked(user.Policy.EnablePublicSharing); $('#txtRemoteClientBitrateLimit', page).val(user.Policy.RemoteClientBitrateLimit / 1e6 || ''); $('#txtLoginAttemptsBeforeLockout', page).val(user.Policy.LoginAttemptsBeforeLockout || '0'); + $('#selectSyncplayAccess').val(user.Policy.SyncplayAccess); loading.hide(); } @@ -145,6 +146,7 @@ define(['jQuery', 'loading', 'libraryMenu', 'globalize', 'fnchecked'], function }).map(function (c) { return c.getAttribute('data-id'); }); + user.Policy.SyncplayAccess = page.querySelector('#selectSyncplayAccess').value; ApiClient.updateUser(user).then(function () { ApiClient.updateUserPolicy(user.Id, user.Policy).then(function () { onSaveComplete(page, user); diff --git a/src/scripts/librarymenu.js b/src/scripts/librarymenu.js index 8afe5f10f..1daa200b8 100644 --- a/src/scripts/librarymenu.js +++ b/src/scripts/librarymenu.js @@ -86,6 +86,12 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', ' if (!layoutManager.tv) { headerCastButton.classList.remove('hide'); } + + var policy = user.Policy ? user.Policy : user.localUser.Policy; + + if (headerSyncButton && policy && policy.SyncplayAccess !== "None") { + headerSyncButton.classList.remove("hide"); + } } else { headerHomeButton.classList.add('hide'); headerCastButton.classList.add('hide'); @@ -95,8 +101,6 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', ' } } - headerSyncButton.classList.remove("hide"); - requiresUserRefresh = false; } diff --git a/src/strings/en-us.json b/src/strings/en-us.json index c7611333f..83890426c 100644 --- a/src/strings/en-us.json +++ b/src/strings/en-us.json @@ -862,6 +862,10 @@ "LabelSyncplayNewGroupDescription": "Create a new group", "LabelSyncplayLeaveGroup": "Leave group", "LabelSyncplayLeaveGroupDescription": "Disable Syncplay", + "LabelSyncplayAccessCreateAndJoinGroups": "Allow user to create and join groups", + "LabelSyncplayAccessJoinGroups": "Allow user to join groups", + "LabelSyncplayAccessNone": "Disabled for this user", + "LabelSyncplayAccess": "Syncplay access", "LabelTVHomeScreen": "TV mode home screen:", "LabelTag": "Tag:", "LabelTagline": "Tagline:", @@ -1031,6 +1035,7 @@ "MessageSyncplayUserLeft": "{0} left group.", "MessageSyncplayGroupWait": "{0} is buffering...", "MessageSyncplayNoGroupsAvailable": "No groups available.", + "MessageSyncplayPermissionRequired": "Permission required to create a group.", "Metadata": "Metadata", "MetadataManager": "Metadata Manager", "MetadataSettingChangeHelp": "Changing metadata settings will affect new content that is added going forward. To refresh existing content, open the detail screen and click the refresh button, or perform bulk refreshes using the metadata manager.", @@ -1379,6 +1384,7 @@ "Suggestions": "Suggestions", "Sunday": "Sunday", "Sync": "Sync", + "SyncplayAccessHelp": "Select the level of access this user has to the Syncplay feature. Syncplay enables to sync playback with other users.", "SystemDlnaProfilesHelp": "System profiles are read-only. Changes to a system profile will be saved to a new custom profile.", "TV": "TV", "TabAccess": "Access", diff --git a/src/useredit.html b/src/useredit.html index 0de3069dc..5f759c973 100644 --- a/src/useredit.html +++ b/src/useredit.html @@ -104,6 +104,16 @@
${LabelUserRemoteClientBitrateLimitHelp}
+
+
+ +
${SyncplayAccessHelp}
+
+

${HeaderAllowMediaDeletionFrom}