diff --git a/app/[locale]/contacts/page.tsx b/app/[locale]/contacts/page.tsx
index 3fc1f59f..1785759e 100644
--- a/app/[locale]/contacts/page.tsx
+++ b/app/[locale]/contacts/page.tsx
@@ -2,7 +2,7 @@
import { useState, useEffect, useCallback, useRef, useMemo } from "react";
import { useTranslations } from "next-intl";
-import { ArrowLeft, Users } from "lucide-react";
+import { ArrowLeft, Users, AlertTriangle } from "lucide-react";
import { Button } from "@/components/ui/button";
import { ConfirmDialog } from "@/components/ui/confirm-dialog";
import { useConfirmDialog } from "@/hooks/use-confirm-dialog";
@@ -19,6 +19,7 @@ import { AppTopBannerSlot } from "@/components/plugins/app-top-banner-slot";
import { useContactStore, getContactDisplayName } from "@/stores/contact-store";
import { useAuthStore, redirectToLogin } from "@/stores/auth-store";
import { useEmailStore } from "@/stores/email-store";
+import { usePolicyStore } from "@/stores/policy-store";
import { toast } from "@/stores/toast-store";
import { cn, generateUUID } from "@/lib/utils";
import { NavigationRail } from "@/components/layout/navigation-rail";
@@ -43,6 +44,7 @@ type View =
export default function ContactsPage() {
const t = useTranslations("contacts");
+ const contactsEnabled = usePolicyStore((s) => s.isFeatureEnabled('contactsEnabled'));
const { client, isAuthenticated, logout, checkAuth, isLoading: authLoading } = useAuthStore();
const { showAppsModal, inlineApp, loadedApps, handleManageApps, handleInlineApp, closeInlineApp, closeAppsModal } = useSidebarApps();
const [initialCheckDone, setInitialCheckDone] = useState(() => useAuthStore.getState().isAuthenticated && !!useAuthStore.getState().client);
@@ -641,6 +643,18 @@ export default function ContactsPage() {
}
};
+ if (!contactsEnabled) {
+ return (
+
+
+
+
Contacts feature is disabled by your administrator
+
Please contact your administrator if you need access.
+
+
+ );
+ }
+
const showListPanel = !isMobile || view === "list";
const showRightPanel = !isMobile || view !== "list";
diff --git a/app/[locale]/settings/page.tsx b/app/[locale]/settings/page.tsx
index d349d383..32aad8d7 100644
--- a/app/[locale]/settings/page.tsx
+++ b/app/[locale]/settings/page.tsx
@@ -590,7 +590,7 @@ export default function SettingsPage() {
// Apps
...(supportsCalendar ? [{ id: 'calendar' as Tab, label: t('tabs.calendar'), icon: tabIcons.calendar, group: 'apps' as TabGroup }] : []),
- { id: 'contacts', label: t('tabs.contacts'), icon: tabIcons.contacts, group: 'apps' },
+ ...(isFeatureEnabled('contactsEnabled') ? [{ id: 'contacts' as Tab, label: t('tabs.contacts'), icon: tabIcons.contacts, group: 'apps' as TabGroup }] : []),
...(supportsFiles && isFeatureEnabled('filesEnabled') ? [{ id: 'files' as Tab, label: t('tabs.files'), icon: tabIcons.files, group: 'apps' as TabGroup }] : []),
...(isFeatureEnabled('sidebarAppsEnabled') ? [{ id: 'sidebar_apps' as Tab, label: t('tabs.sidebar_apps'), icon: tabIcons.sidebar_apps, group: 'apps' as TabGroup }] : []),
diff --git a/app/admin/_tabs/auth.tsx b/app/admin/_tabs/auth.tsx
index 95aca959..b56a66f6 100644
--- a/app/admin/_tabs/auth.tsx
+++ b/app/admin/_tabs/auth.tsx
@@ -273,6 +273,8 @@ export function AuthTab() {
+
+
diff --git a/components/layout/navigation-rail.tsx b/components/layout/navigation-rail.tsx
index d4a7d3a8..d7a08cf1 100644
--- a/components/layout/navigation-rail.tsx
+++ b/components/layout/navigation-rail.tsx
@@ -175,6 +175,7 @@ export function NavigationRail({
const showRailAccountList = useSettingsStore((s) => s.showRailAccountList);
const sidebarAppsEnabled = usePolicyStore((s) => s.isFeatureEnabled('sidebarAppsEnabled'));
const filesEnabled = usePolicyStore((s) => s.isFeatureEnabled('filesEnabled'));
+ const contactsEnabled = usePolicyStore((s) => s.isFeatureEnabled('contactsEnabled'));
const visibleSidebarApps = sidebarAppsEnabled ? sidebarApps : [];
const inboxUnread = mailboxes.find(m => m.role === "inbox")?.unreadEmails || 0;
const [isStalwartAdmin, setIsStalwartAdmin] = useState(false);
@@ -254,7 +255,7 @@ export function NavigationRail({
const navItems: NavItem[] = [
{ id: "mail", icon: Mail, labelKey: "mail", href: "/", badge: inboxUnread },
{ id: "calendar", icon: Calendar, labelKey: "calendar", href: "/calendar", hidden: !supportsCalendar },
- { id: "contacts", icon: BookUser, labelKey: "contacts", href: "/contacts", hidden: !supportsContacts },
+ { id: "contacts", icon: BookUser, labelKey: "contacts", href: "/contacts", hidden: !supportsContacts || !contactsEnabled },
{ id: "files", icon: HardDrive, labelKey: "files", href: "/files", hidden: !supportsFiles || !filesEnabled },
];
diff --git a/components/settings/content-senders-settings.tsx b/components/settings/content-senders-settings.tsx
index 7fd02d79..bd72b083 100644
--- a/components/settings/content-senders-settings.tsx
+++ b/components/settings/content-senders-settings.tsx
@@ -12,7 +12,7 @@ import { useContactStore } from '@/stores/contact-store';
export function ContentSendersSettings() {
const t = useTranslations('settings.email_behavior');
const [showTrustedModal, setShowTrustedModal] = useState(false);
- const { isSettingLocked, isSettingHidden } = usePolicyStore();
+ const { isSettingLocked, isSettingHidden, isFeatureEnabled } = usePolicyStore();
const {
externalContentPolicy,
@@ -32,7 +32,7 @@ export function ContentSendersSettings() {
return (
- {!isSettingHidden('externalContentPolicy') && (
+ {isFeatureEnabled('externalContentEnabled') && !isSettingHidden('externalContentPolicy') && (