diff --git a/apps/web/components/booking/pages/AvailabilityPage.tsx b/apps/web/components/booking/pages/AvailabilityPage.tsx index 7185cd148a..7a46d1d0c7 100644 --- a/apps/web/components/booking/pages/AvailabilityPage.tsx +++ b/apps/web/components/booking/pages/AvailabilityPage.tsx @@ -442,7 +442,7 @@ const AvailabilityPage = ({ profile, eventType, ...restProps }: Props) => { /> - {(!eventType.users[0] || !isBrandingHidden(eventType.users[0])) && !isEmbed && } + {(!restProps.isBrandingHidden || isEmbed) && } diff --git a/apps/web/lib/isBrandingHidden.tsx b/apps/web/lib/isBrandingHidden.tsx index dc96460825..b5495f377d 100644 --- a/apps/web/lib/isBrandingHidden.tsx +++ b/apps/web/lib/isBrandingHidden.tsx @@ -1,5 +1,3 @@ -import { User } from "@prisma/client"; - -export function isBrandingHidden>(user: TUser) { - return user.hideBranding && user.plan !== "FREE"; +export function isBrandingHidden(hideBrandingSetting: boolean, belongsToActiveTeam: boolean) { + return belongsToActiveTeam && hideBrandingSetting; } diff --git a/apps/web/pages/[user]/[type].tsx b/apps/web/pages/[user]/[type].tsx index 162b66ee93..6c05231379 100644 --- a/apps/web/pages/[user]/[type].tsx +++ b/apps/web/pages/[user]/[type].tsx @@ -10,8 +10,9 @@ import { useLocale } from "@calcom/lib/hooks/useLocale"; import { parseRecurringEvent } from "@calcom/lib/isRecurringEvent"; import prisma from "@calcom/prisma"; import { User } from "@calcom/prisma/client"; -import { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils"; +import { EventTypeMetaDataSchema, teamMetadataSchema } from "@calcom/prisma/zod-utils"; +import { isBrandingHidden } from "@lib/isBrandingHidden"; import { inferSSRProps } from "@lib/types/inferSSRProps"; import { EmbedProps } from "@lib/withEmbedSsr"; @@ -110,6 +111,11 @@ async function getUserPageProps(context: GetStaticPropsContext) { }, ], }, + teams: { + include: { + team: true, + }, + }, }, }); @@ -150,6 +156,13 @@ async function getUserPageProps(context: GetStaticPropsContext) { locations: privacyFilteredLocations(locations), descriptionAsSafeHTML: eventType.description ? md.render(eventType.description) : null, }); + // Check if the user you are logging into has any active teams + const hasActiveTeam = + user.teams.filter((m) => { + const metadata = teamMetadataSchema.safeParse(m.team.metadata); + if (metadata.success && metadata.data?.subscriptionId) return false; + return true; + }).length > 0; return { props: { @@ -167,6 +180,7 @@ async function getUserPageProps(context: GetStaticPropsContext) { away: user?.away, isDynamic: false, trpcState: ssg.dehydrate(), + isBrandingHidden: isBrandingHidden(user.hideBranding, hasActiveTeam), }, revalidate: 10, // seconds }; @@ -262,6 +276,7 @@ async function getDynamicGroupPageProps(context: GetStaticPropsContext) { isDynamic: true, away: false, trpcState: ssg.dehydrate(), + isBrandingHidden: false, // I think we should always show branding for dynamic groups - saves us checking every single user }, revalidate: 10, // seconds }; diff --git a/apps/web/pages/api/auth/[...nextauth].tsx b/apps/web/pages/api/auth/[...nextauth].tsx index 98a64cc779..f7716fdccc 100644 --- a/apps/web/pages/api/auth/[...nextauth].tsx +++ b/apps/web/pages/api/auth/[...nextauth].tsx @@ -20,6 +20,7 @@ import { defaultCookies } from "@calcom/lib/default-cookies"; import rateLimit from "@calcom/lib/rateLimit"; import { serverConfig } from "@calcom/lib/serverConfig"; import prisma from "@calcom/prisma"; +import { teamMetadataSchema } from "@calcom/prisma/zod-utils"; import CalComAdapter from "@lib/auth/next-auth-custom-adapter"; import { randomString } from "@lib/random"; @@ -63,6 +64,11 @@ const providers: Provider[] = [ password: true, twoFactorEnabled: true, twoFactorSecret: true, + teams: { + include: { + team: true, + }, + }, }, }); @@ -116,6 +122,13 @@ const providers: Provider[] = [ intervalInMs: 60 * 1000, // 1 minute }); await limiter.check(10, user.email); // 10 requests per minute + // Check if the user you are logging into has any active teams + const hasActiveTeams = + user.teams.filter((m) => { + const metadata = teamMetadataSchema.safeParse(m.team.metadata); + if (metadata.success && metadata.data?.subscriptionId) return false; + return true; + }).length > 0; // authentication success- but does it meet the minimum password requirements? if (user.role === "ADMIN" && !isPasswordValid(credentials.password, false, true)) { @@ -125,6 +138,7 @@ const providers: Provider[] = [ email: user.email, name: user.name, role: "USER", + belongsToActiveTeam: hasActiveTeams, }; } @@ -134,6 +148,7 @@ const providers: Provider[] = [ email: user.email, name: user.name, role: user.role, + belongsToActiveTeam: hasActiveTeams, }; }, }), @@ -272,6 +287,7 @@ export default NextAuth({ email: user.email, role: user.role, impersonatedByUID: user?.impersonatedByUID, + belongsToActiveTeam: user?.belongsToActiveTeam, }; } @@ -307,6 +323,7 @@ export default NextAuth({ email: existingUser.email, role: existingUser.role, impersonatedByUID: token.impersonatedByUID as number, + belongsToActiveTeam: token?.belongsToActiveTeam as boolean, }; } @@ -324,6 +341,7 @@ export default NextAuth({ username: token.username as string, role: token.role as UserPermissionRole, impersonatedByUID: token.impersonatedByUID as number, + belongsToActiveTeam: token?.belongsToActiveTeam as boolean, }, }; return calendsoSession; diff --git a/apps/web/pages/d/[link]/[slug].tsx b/apps/web/pages/d/[link]/[slug].tsx index bd80efe31a..b9ac03a44e 100644 --- a/apps/web/pages/d/[link]/[slug].tsx +++ b/apps/web/pages/d/[link]/[slug].tsx @@ -166,6 +166,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) => previousPage: context.req.headers.referer ?? null, booking, users: [user.username], + isBrandingHidden: user.hideBranding, }, }; }; diff --git a/apps/web/pages/settings/my-account/appearance.tsx b/apps/web/pages/settings/my-account/appearance.tsx index 3acb39548b..87b479178d 100644 --- a/apps/web/pages/settings/my-account/appearance.tsx +++ b/apps/web/pages/settings/my-account/appearance.tsx @@ -1,3 +1,4 @@ +import { useSession } from "next-auth/react"; import { Controller, useForm } from "react-hook-form"; import { APP_NAME } from "@calcom/lib/constants"; @@ -41,7 +42,7 @@ const SkeletonLoader = () => { const AppearanceView = () => { const { t } = useLocale(); - + const session = useSession(); const utils = trpc.useContext(); const { data: user, isLoading } = trpc.viewer.me.useQuery(); @@ -180,6 +181,7 @@ const AppearanceView = () => {
formMethods.setValue("hideBranding", checked, { shouldDirty: true }) } diff --git a/apps/web/pages/team/[slug]/[type].tsx b/apps/web/pages/team/[slug]/[type].tsx index d6b8fdbe6a..1df4456864 100644 --- a/apps/web/pages/team/[slug]/[type].tsx +++ b/apps/web/pages/team/[slug]/[type].tsx @@ -43,6 +43,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) => name: true, slug: true, logo: true, + hideBranding: true, eventTypes: { where: { slug: typeParam, @@ -160,6 +161,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) => previousPage: context.req.headers.referer ?? null, booking, trpcState: ssg.dehydrate(), + isBrandingHidden: team.hideBranding, }, }; }; diff --git a/packages/features/ee/impersonation/lib/ImpersonationProvider.ts b/packages/features/ee/impersonation/lib/ImpersonationProvider.ts index 11d5c753aa..407ad41311 100644 --- a/packages/features/ee/impersonation/lib/ImpersonationProvider.ts +++ b/packages/features/ee/impersonation/lib/ImpersonationProvider.ts @@ -11,7 +11,8 @@ const teamIdschema = z.object({ const auditAndReturnNextUser = async ( impersonatedUser: Pick, - impersonatedByUID: number + impersonatedByUID: number, + hasTeam?: boolean ) => { // Log impersonations for audit purposes await prisma.impersonations.create({ @@ -36,6 +37,7 @@ const auditAndReturnNextUser = async ( name: impersonatedUser.name, role: impersonatedUser.role, impersonatedByUID, + belongsToActiveTeam: hasTeam, }; return obj; @@ -103,7 +105,11 @@ const ImpersonationProvider = CredentialsProvider({ if (impersonatedUser.disableImpersonation) { throw new Error("This user has disabled Impersonation."); } - return auditAndReturnNextUser(impersonatedUser, session?.user.id as number); + return auditAndReturnNextUser( + impersonatedUser, + session?.user.id as number, + impersonatedUser.teams.length > 0 // If the user has any teams, they belong to an active team and we can set the hasActiveTeam ctx to true + ); } if (!teamId) throw new Error("You do not have permission to do this."); @@ -137,7 +143,11 @@ const ImpersonationProvider = CredentialsProvider({ throw new Error("You do not have permission to do this."); } - return auditAndReturnNextUser(impersonatedUser, session?.user.id as number); + return auditAndReturnNextUser( + impersonatedUser, + session?.user.id as number, + impersonatedUser.teams.length > 0 // If the user has any teams, they belong to an active team and we can set the hasActiveTeam ctx to true + ); }, }); diff --git a/packages/types/next-auth.d.ts b/packages/types/next-auth.d.ts index 0907804546..94c3fba101 100644 --- a/packages/types/next-auth.d.ts +++ b/packages/types/next-auth.d.ts @@ -14,6 +14,7 @@ declare module "next-auth" { emailVerified?: PrismaUser["emailVerified"]; email_verified?: boolean; impersonatedByUID?: number; + belongsToActiveTeam?: boolean; username?: PrismaUser["username"]; role?: PrismaUser["role"]; }