mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-17 08:50:15 +01:00
🤝 refactor: Clarify labels for Sharing Permissions
This commit is contained in:
parent
6af7efd0f4
commit
d711fc7852
6 changed files with 27 additions and 366 deletions
|
|
@ -109,7 +109,7 @@ const AdminSettings = () => {
|
||||||
const labelControllerData = [
|
const labelControllerData = [
|
||||||
{
|
{
|
||||||
promptPerm: Permissions.SHARED_GLOBAL,
|
promptPerm: Permissions.SHARED_GLOBAL,
|
||||||
label: localize('com_ui_prompts_allow_share_global'),
|
label: localize('com_ui_prompts_allow_share'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
promptPerm: Permissions.CREATE,
|
promptPerm: Permissions.CREATE,
|
||||||
|
|
|
||||||
|
|
@ -1,350 +0,0 @@
|
||||||
import React, { useState, useEffect } from 'react';
|
|
||||||
import { AccessRoleIds, ResourceType } from 'librechat-data-provider';
|
|
||||||
import { Settings, Users, UserCheck, Trash2, Shield } from 'lucide-react';
|
|
||||||
import {
|
|
||||||
useGetResourcePermissionsQuery,
|
|
||||||
useUpdateResourcePermissionsMutation,
|
|
||||||
} from 'librechat-data-provider/react-query';
|
|
||||||
import type { TPrincipal } from 'librechat-data-provider';
|
|
||||||
import {
|
|
||||||
Button,
|
|
||||||
Spinner,
|
|
||||||
OGDialog,
|
|
||||||
OGDialogTitle,
|
|
||||||
OGDialogClose,
|
|
||||||
OGDialogContent,
|
|
||||||
OGDialogTrigger,
|
|
||||||
useToastContext,
|
|
||||||
} from '@librechat/client';
|
|
||||||
import { SelectedPrincipalsList } from './PeoplePicker';
|
|
||||||
import PublicSharingToggle from './PublicSharingToggle';
|
|
||||||
import { cn, removeFocusOutlines } from '~/utils';
|
|
||||||
import { useLocalize } from '~/hooks';
|
|
||||||
|
|
||||||
export default function ManagePermissionsDialog({
|
|
||||||
agentName,
|
|
||||||
resourceType = ResourceType.AGENT,
|
|
||||||
agentDbId,
|
|
||||||
onUpdatePermissions,
|
|
||||||
}: {
|
|
||||||
agentDbId: string;
|
|
||||||
agentName?: string;
|
|
||||||
resourceType?: ResourceType;
|
|
||||||
onUpdatePermissions?: (
|
|
||||||
shares: TPrincipal[],
|
|
||||||
isPublic: boolean,
|
|
||||||
publicRole: AccessRoleIds,
|
|
||||||
) => void;
|
|
||||||
}) {
|
|
||||||
const localize = useLocalize();
|
|
||||||
const { showToast } = useToastContext();
|
|
||||||
|
|
||||||
const {
|
|
||||||
data: permissionsData,
|
|
||||||
isLoading: isLoadingPermissions,
|
|
||||||
error: permissionsError,
|
|
||||||
} = useGetResourcePermissionsQuery(resourceType, agentDbId, {
|
|
||||||
enabled: !!agentDbId,
|
|
||||||
});
|
|
||||||
|
|
||||||
const updatePermissionsMutation = useUpdateResourcePermissionsMutation();
|
|
||||||
|
|
||||||
const [managedShares, setManagedShares] = useState<TPrincipal[]>([]);
|
|
||||||
const [managedIsPublic, setManagedIsPublic] = useState(false);
|
|
||||||
const [managedPublicRole, setManagedPublicRole] = useState<AccessRoleIds>(
|
|
||||||
AccessRoleIds.AGENT_VIEWER,
|
|
||||||
);
|
|
||||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
||||||
const [hasChanges, setHasChanges] = useState(false);
|
|
||||||
|
|
||||||
const currentShares: TPrincipal[] = permissionsData?.principals || [];
|
|
||||||
|
|
||||||
const isPublic = permissionsData?.public || false;
|
|
||||||
const publicRole = permissionsData?.publicAccessRoleId || AccessRoleIds.AGENT_VIEWER;
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (permissionsData) {
|
|
||||||
const shares = permissionsData.principals || [];
|
|
||||||
const isPublicValue = permissionsData.public || false;
|
|
||||||
const publicRoleValue = permissionsData.publicAccessRoleId || AccessRoleIds.AGENT_VIEWER;
|
|
||||||
|
|
||||||
setManagedShares(shares);
|
|
||||||
setManagedIsPublic(isPublicValue);
|
|
||||||
setManagedPublicRole(publicRoleValue);
|
|
||||||
setHasChanges(false);
|
|
||||||
}
|
|
||||||
}, [permissionsData, isModalOpen]);
|
|
||||||
|
|
||||||
if (!agentDbId) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (permissionsError) {
|
|
||||||
return <div className="text-sm text-red-600">{localize('com_ui_permissions_failed_load')}</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleRemoveShare = (idOnTheSource: string) => {
|
|
||||||
setManagedShares(managedShares.filter((s) => s.idOnTheSource !== idOnTheSource));
|
|
||||||
setHasChanges(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleRoleChange = (idOnTheSource: string, newRole: AccessRoleIds) => {
|
|
||||||
setManagedShares(
|
|
||||||
managedShares.map((s) =>
|
|
||||||
s.idOnTheSource === idOnTheSource ? { ...s, accessRoleId: newRole } : s,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
setHasChanges(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSaveChanges = async () => {
|
|
||||||
try {
|
|
||||||
const originalSharesMap = new Map(
|
|
||||||
currentShares.map((share) => [`${share.type}-${share.idOnTheSource}`, share]),
|
|
||||||
);
|
|
||||||
const managedSharesMap = new Map(
|
|
||||||
managedShares.map((share) => [`${share.type}-${share.idOnTheSource}`, share]),
|
|
||||||
);
|
|
||||||
|
|
||||||
const updated = managedShares.filter((share) => {
|
|
||||||
const key = `${share.type}-${share.idOnTheSource}`;
|
|
||||||
const original = originalSharesMap.get(key);
|
|
||||||
return !original || original.accessRoleId !== share.accessRoleId;
|
|
||||||
});
|
|
||||||
|
|
||||||
const removed = currentShares.filter((share) => {
|
|
||||||
const key = `${share.type}-${share.idOnTheSource}`;
|
|
||||||
return !managedSharesMap.has(key);
|
|
||||||
});
|
|
||||||
|
|
||||||
await updatePermissionsMutation.mutateAsync({
|
|
||||||
resourceType,
|
|
||||||
resourceId: agentDbId,
|
|
||||||
data: {
|
|
||||||
updated,
|
|
||||||
removed,
|
|
||||||
public: managedIsPublic,
|
|
||||||
publicAccessRoleId: managedIsPublic ? managedPublicRole : undefined,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (onUpdatePermissions) {
|
|
||||||
onUpdatePermissions(managedShares, managedIsPublic, managedPublicRole);
|
|
||||||
}
|
|
||||||
|
|
||||||
showToast({
|
|
||||||
message: localize('com_ui_permissions_updated_success'),
|
|
||||||
status: 'success',
|
|
||||||
});
|
|
||||||
|
|
||||||
setIsModalOpen(false);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error updating permissions:', error);
|
|
||||||
showToast({
|
|
||||||
message: localize('com_ui_permissions_failed_update'),
|
|
||||||
status: 'error',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleCancel = () => {
|
|
||||||
setManagedShares(currentShares);
|
|
||||||
setManagedIsPublic(isPublic);
|
|
||||||
setManagedPublicRole(publicRole);
|
|
||||||
setIsModalOpen(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleRevokeAll = () => {
|
|
||||||
setManagedShares([]);
|
|
||||||
setManagedIsPublic(false);
|
|
||||||
setHasChanges(true);
|
|
||||||
};
|
|
||||||
const handlePublicToggle = (isPublic: boolean) => {
|
|
||||||
setManagedIsPublic(isPublic);
|
|
||||||
setHasChanges(true);
|
|
||||||
if (!isPublic) {
|
|
||||||
setManagedPublicRole(AccessRoleIds.AGENT_VIEWER);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const handlePublicRoleChange = (role: AccessRoleIds) => {
|
|
||||||
setManagedPublicRole(role);
|
|
||||||
setHasChanges(true);
|
|
||||||
};
|
|
||||||
const totalShares = managedShares.length + (managedIsPublic ? 1 : 0);
|
|
||||||
const originalTotalShares = currentShares.length + (isPublic ? 1 : 0);
|
|
||||||
|
|
||||||
/** Check if there's at least one owner (user, group, or public with owner role) */
|
|
||||||
const hasAtLeastOneOwner =
|
|
||||||
managedShares.some((share) => share.accessRoleId === AccessRoleIds.AGENT_OWNER) ||
|
|
||||||
(managedIsPublic && managedPublicRole === AccessRoleIds.AGENT_OWNER);
|
|
||||||
|
|
||||||
let peopleLabel = localize('com_ui_people');
|
|
||||||
if (managedShares.length === 1) {
|
|
||||||
peopleLabel = localize('com_ui_person');
|
|
||||||
}
|
|
||||||
|
|
||||||
let buttonAriaLabel = localize('com_ui_manage_permissions_for') + ' agent';
|
|
||||||
if (agentName != null && agentName !== '') {
|
|
||||||
buttonAriaLabel = localize('com_ui_manage_permissions_for') + ` "${agentName}"`;
|
|
||||||
}
|
|
||||||
|
|
||||||
let dialogTitle = localize('com_ui_manage_permissions_for') + ' Agent';
|
|
||||||
if (agentName != null && agentName !== '') {
|
|
||||||
dialogTitle = localize('com_ui_manage_permissions_for') + ` "${agentName}"`;
|
|
||||||
}
|
|
||||||
|
|
||||||
let publicSuffix = '';
|
|
||||||
if (managedIsPublic) {
|
|
||||||
publicSuffix = localize('com_ui_and_public');
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<OGDialog open={isModalOpen} onOpenChange={setIsModalOpen}>
|
|
||||||
<OGDialogTrigger asChild>
|
|
||||||
<button
|
|
||||||
className={cn(
|
|
||||||
'btn btn-neutral border-token-border-light relative h-9 rounded-lg font-medium',
|
|
||||||
removeFocusOutlines,
|
|
||||||
)}
|
|
||||||
aria-label={buttonAriaLabel}
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
<div className="flex items-center justify-center gap-2 text-blue-500">
|
|
||||||
<Settings className="icon-md h-4 w-4" />
|
|
||||||
<span className="hidden sm:inline">{localize('com_ui_manage')}</span>
|
|
||||||
{originalTotalShares > 0 && `(${originalTotalShares})`}
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
</OGDialogTrigger>
|
|
||||||
|
|
||||||
<OGDialogContent className="max-h-[90vh] w-11/12 overflow-y-auto md:max-w-3xl">
|
|
||||||
<OGDialogTitle>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<Shield className="h-5 w-5 text-blue-500" />
|
|
||||||
{dialogTitle}
|
|
||||||
</div>
|
|
||||||
</OGDialogTitle>
|
|
||||||
|
|
||||||
<div className="space-y-6 p-2">
|
|
||||||
<div className="rounded-lg bg-surface-tertiary p-4">
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<div>
|
|
||||||
<h3 className="text-sm font-medium text-text-primary">
|
|
||||||
{localize('com_ui_current_access')}
|
|
||||||
</h3>
|
|
||||||
<p className="text-xs text-text-secondary">
|
|
||||||
{(() => {
|
|
||||||
if (totalShares === 0) {
|
|
||||||
return localize('com_ui_no_users_groups_access');
|
|
||||||
}
|
|
||||||
return localize('com_ui_shared_with_count', {
|
|
||||||
0: managedShares.length,
|
|
||||||
1: peopleLabel,
|
|
||||||
2: publicSuffix,
|
|
||||||
});
|
|
||||||
})()}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
{(managedShares.length > 0 || managedIsPublic) && (
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
size="sm"
|
|
||||||
onClick={handleRevokeAll}
|
|
||||||
className="text-red-600 hover:text-red-700"
|
|
||||||
>
|
|
||||||
<Trash2 className="mr-2 h-4 w-4" />
|
|
||||||
{localize('com_ui_revoke_all')}
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{(() => {
|
|
||||||
if (isLoadingPermissions) {
|
|
||||||
return (
|
|
||||||
<div className="flex items-center justify-center p-8">
|
|
||||||
<Spinner className="h-6 w-6" />
|
|
||||||
<span className="ml-2 text-sm text-text-secondary">
|
|
||||||
{localize('com_ui_loading_permissions')}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (managedShares.length > 0) {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<h3 className="mb-3 flex items-center gap-2 text-sm font-medium text-text-primary">
|
|
||||||
<UserCheck className="h-4 w-4" />
|
|
||||||
{localize('com_ui_user_group_permissions')} ({managedShares.length})
|
|
||||||
</h3>
|
|
||||||
<SelectedPrincipalsList
|
|
||||||
principles={managedShares}
|
|
||||||
onRemoveHandler={handleRemoveShare}
|
|
||||||
resourceType={resourceType}
|
|
||||||
onRoleChange={(id, newRole) => handleRoleChange(id, newRole)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="rounded-lg border-2 border-dashed border-border-light p-8 text-center">
|
|
||||||
<Users className="mx-auto h-8 w-8 text-text-secondary" />
|
|
||||||
<p className="mt-2 text-sm text-text-secondary">
|
|
||||||
{localize('com_ui_no_individual_access')}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})()}
|
|
||||||
|
|
||||||
<PublicSharingToggle
|
|
||||||
isPublic={managedIsPublic}
|
|
||||||
publicRole={managedPublicRole}
|
|
||||||
onPublicToggle={handlePublicToggle}
|
|
||||||
onPublicRoleChange={handlePublicRoleChange}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div className="flex justify-end gap-3 border-t pt-4">
|
|
||||||
<OGDialogClose asChild>
|
|
||||||
<Button variant="outline" onClick={handleCancel}>
|
|
||||||
{localize('com_ui_cancel')}
|
|
||||||
</Button>
|
|
||||||
</OGDialogClose>
|
|
||||||
<Button
|
|
||||||
onClick={handleSaveChanges}
|
|
||||||
disabled={
|
|
||||||
updatePermissionsMutation.isLoading ||
|
|
||||||
!hasChanges ||
|
|
||||||
isLoadingPermissions ||
|
|
||||||
!hasAtLeastOneOwner
|
|
||||||
}
|
|
||||||
className="min-w-[120px]"
|
|
||||||
>
|
|
||||||
{updatePermissionsMutation.isLoading ? (
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<Spinner className="h-4 w-4" />
|
|
||||||
{localize('com_ui_saving')}
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
localize('com_ui_save_changes')
|
|
||||||
)}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{hasChanges && (
|
|
||||||
<div className="text-xs text-orange-600 dark:text-orange-400">
|
|
||||||
* {localize('com_ui_unsaved_changes')}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{!hasAtLeastOneOwner && hasChanges && (
|
|
||||||
<div className="text-xs text-red-600 dark:text-red-400">
|
|
||||||
* {localize('com_ui_at_least_one_owner_required')}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</OGDialogContent>
|
|
||||||
</OGDialog>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -16,6 +16,11 @@ interface PublicSharingToggleProps {
|
||||||
className?: string;
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const accessDescriptions: Record<ResourceType, 'com_ui_agent' | 'com_ui_prompt'> = {
|
||||||
|
[ResourceType.AGENT]: 'com_ui_agent',
|
||||||
|
[ResourceType.PROMPTGROUP]: 'com_ui_prompt',
|
||||||
|
};
|
||||||
|
|
||||||
export default function PublicSharingToggle({
|
export default function PublicSharingToggle({
|
||||||
isPublic,
|
isPublic,
|
||||||
publicRole,
|
publicRole,
|
||||||
|
|
@ -49,19 +54,25 @@ export default function PublicSharingToggle({
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Label
|
<Label
|
||||||
htmlFor="public-access-toggle"
|
htmlFor="share-everyone-toggle"
|
||||||
className="cursor-pointer text-sm font-medium text-text-primary"
|
className="cursor-pointer text-sm font-medium text-text-primary"
|
||||||
>
|
>
|
||||||
{localize('com_ui_public_access')}
|
{localize('com_ui_share_everyone')}
|
||||||
</Label>
|
</Label>
|
||||||
<InfoHoverCard side={ESide.Top} text={localize('com_ui_public_access_description')} />
|
<InfoHoverCard
|
||||||
|
side={ESide.Top}
|
||||||
|
text={localize('com_ui_share_everyone_description_var', {
|
||||||
|
resource:
|
||||||
|
localize(accessDescriptions[resourceType]) || localize('com_ui_resource'),
|
||||||
|
})}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Switch
|
<Switch
|
||||||
id="public-access-toggle"
|
id="share-everyone-toggle"
|
||||||
checked={isPublic}
|
checked={isPublic}
|
||||||
onCheckedChange={handleToggle}
|
onCheckedChange={handleToggle}
|
||||||
aria-label={localize('com_ui_public_access')}
|
aria-label={localize('com_ui_share_everyone')}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -94,7 +105,7 @@ export default function PublicSharingToggle({
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-0.5">
|
<div className="flex flex-col gap-0.5">
|
||||||
<Label htmlFor="permission-level" className="text-sm font-medium text-text-primary">
|
<Label htmlFor="permission-level" className="text-sm font-medium text-text-primary">
|
||||||
{localize('com_ui_public_permission_level')}
|
{localize('com_ui_everyone_permission_level')}
|
||||||
</Label>
|
</Label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
export { default as AccessRolesPicker } from './AccessRolesPicker';
|
|
||||||
export { default as GenericGrantAccessDialog } from './GenericGrantAccessDialog';
|
|
||||||
export { default as ManagePermissionsDialog } from './ManagePermissionsDialog';
|
|
||||||
export { default as PrincipalAvatar } from './PrincipalAvatar';
|
export { default as PrincipalAvatar } from './PrincipalAvatar';
|
||||||
|
export { default as AccessRolesPicker } from './AccessRolesPicker';
|
||||||
export { default as PublicSharingToggle } from './PublicSharingToggle';
|
export { default as PublicSharingToggle } from './PublicSharingToggle';
|
||||||
|
export { default as GenericGrantAccessDialog } from './GenericGrantAccessDialog';
|
||||||
|
|
|
||||||
|
|
@ -114,7 +114,7 @@ const AdminSettings = () => {
|
||||||
const labelControllerData = [
|
const labelControllerData = [
|
||||||
{
|
{
|
||||||
agentPerm: Permissions.SHARED_GLOBAL,
|
agentPerm: Permissions.SHARED_GLOBAL,
|
||||||
label: localize('com_ui_agents_allow_share_global'),
|
label: localize('com_ui_agents_allow_share'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
agentPerm: Permissions.CREATE,
|
agentPerm: Permissions.CREATE,
|
||||||
|
|
|
||||||
|
|
@ -586,7 +586,7 @@
|
||||||
"com_ui_agent_version_unknown_date": "Unknown date",
|
"com_ui_agent_version_unknown_date": "Unknown date",
|
||||||
"com_ui_agents": "Agents",
|
"com_ui_agents": "Agents",
|
||||||
"com_ui_agents_allow_create": "Allow creating Agents",
|
"com_ui_agents_allow_create": "Allow creating Agents",
|
||||||
"com_ui_agents_allow_share_global": "Allow sharing Agents to all users",
|
"com_ui_agents_allow_share": "Allow sharing Agents",
|
||||||
"com_ui_agents_allow_use": "Allow using Agents",
|
"com_ui_agents_allow_use": "Allow using Agents",
|
||||||
"com_ui_people_picker": "People Picker",
|
"com_ui_people_picker": "People Picker",
|
||||||
"com_ui_people_picker_allow_view_users": "Allow viewing users",
|
"com_ui_people_picker_allow_view_users": "Allow viewing users",
|
||||||
|
|
@ -981,7 +981,7 @@
|
||||||
"com_ui_prompt_update_error": "There was an error updating the prompt",
|
"com_ui_prompt_update_error": "There was an error updating the prompt",
|
||||||
"com_ui_prompts": "Prompts",
|
"com_ui_prompts": "Prompts",
|
||||||
"com_ui_prompts_allow_create": "Allow creating Prompts",
|
"com_ui_prompts_allow_create": "Allow creating Prompts",
|
||||||
"com_ui_prompts_allow_share_global": "Allow sharing Prompts to all users",
|
"com_ui_prompts_allow_share": "Allow sharing Prompts",
|
||||||
"com_ui_prompts_allow_use": "Allow using Prompts",
|
"com_ui_prompts_allow_use": "Allow using Prompts",
|
||||||
"com_ui_provider": "Provider",
|
"com_ui_provider": "Provider",
|
||||||
"com_ui_quality": "Quality",
|
"com_ui_quality": "Quality",
|
||||||
|
|
@ -1167,6 +1167,7 @@
|
||||||
"com_ui_select_options": "Select options...",
|
"com_ui_select_options": "Select options...",
|
||||||
"com_ui_no_results_found": "No results found",
|
"com_ui_no_results_found": "No results found",
|
||||||
"com_ui_try_adjusting_search": "Try adjusting your search terms",
|
"com_ui_try_adjusting_search": "Try adjusting your search terms",
|
||||||
|
"com_ui_resource": "resource",
|
||||||
"com_ui_role_viewer": "Viewer",
|
"com_ui_role_viewer": "Viewer",
|
||||||
"com_ui_role_editor": "Editor",
|
"com_ui_role_editor": "Editor",
|
||||||
"com_ui_role_manager": "Manager",
|
"com_ui_role_manager": "Manager",
|
||||||
|
|
@ -1189,11 +1190,11 @@
|
||||||
"com_ui_loading_permissions": "Loading permissions...",
|
"com_ui_loading_permissions": "Loading permissions...",
|
||||||
"com_ui_user_group_permissions": "User & Group Permissions",
|
"com_ui_user_group_permissions": "User & Group Permissions",
|
||||||
"com_ui_no_individual_access": "No individual users or groups have access to this agent",
|
"com_ui_no_individual_access": "No individual users or groups have access to this agent",
|
||||||
"com_ui_public_access": "Public Access",
|
"com_ui_share_everyone": "Share with everyone",
|
||||||
"com_ui_public_access_description": "Anyone can access this resource publicly",
|
"com_ui_share_everyone_description_var": "This {{resource}} will be available to everyone. Please make sure the {{resource}} is really meant to be shared with everyone. Be careful with your data.",
|
||||||
"com_ui_save_changes": "Save Changes",
|
"com_ui_save_changes": "Save Changes",
|
||||||
"com_ui_unsaved_changes": "You have unsaved changes",
|
"com_ui_unsaved_changes": "You have unsaved changes",
|
||||||
"com_ui_public_permission_level": "Public permission level",
|
"com_ui_everyone_permission_level": "Everyone's permission level",
|
||||||
"com_ui_at_least_one_owner_required": "At least one owner is required",
|
"com_ui_at_least_one_owner_required": "At least one owner is required",
|
||||||
"com_agents_marketplace": "Agent Marketplace",
|
"com_agents_marketplace": "Agent Marketplace",
|
||||||
"com_agents_all": "All Agents",
|
"com_agents_all": "All Agents",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue