Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
90 commits
Select commit Hold shift + click to select a range
63712df
fix: disable cal branding
dhairyashiil Oct 8, 2025
9ab52db
fix: disable branding in cancelation email
dhairyashiil Oct 8, 2025
83188b9
fix: disable branding for confirmation booking email
dhairyashiil Oct 8, 2025
80aa753
disable branding in seated bookings
dhairyashiil Oct 9, 2025
fff5d0c
disable branding for declined bookings
dhairyashiil Oct 9, 2025
cbfd22d
avoid unnecessary db call when emails disabled
dhairyashiil Oct 9, 2025
de2ce3b
made hideBranding mandatory for email functions
dhairyashiil Oct 11, 2025
24f61b6
Remove unused code
dhairyashiil Oct 12, 2025
1eb7eb3
resolve merge conflicts
dhairyashiil Oct 12, 2025
383a72e
Addressed coderabits comments
dhairyashiil Oct 13, 2025
865178d
Addressed coderabits comments
dhairyashiil Oct 13, 2025
d0d93ba
Addressed coderabits comments
dhairyashiil Oct 13, 2025
08b2dd3
Addressed coderabits comments
dhairyashiil Oct 13, 2025
714f725
no primsa outside repositories structure, better error handling, no d…
dhairyashiil Oct 16, 2025
e5ecbe7
Resolve merge conflicts
dhairyashiil Oct 16, 2025
fb9d9a4
fix repository pattern violation
dhairyashiil Oct 16, 2025
11d6d83
type export and JSDoc
dhairyashiil Oct 16, 2025
4f88da2
fix: compute hideBranding dynamically in payment handlers
dhairyashiil Oct 17, 2025
e721528
Resolve merge conflicts
dhairyashiil Oct 24, 2025
305774f
fix failing round robin test
dhairyashiil Oct 24, 2025
00c2c63
fix violation of no prisma outside of repositories rule
dhairyashiil Oct 24, 2025
b49393d
Merge remote-tracking branch 'upstream/main' into fix/disable-cal-bra…
dhairyashiil Oct 26, 2025
c411cc7
update orgId passing logic and logger
dhairyashiil Oct 26, 2025
1d301ba
address cubics comments
dhairyashiil Oct 26, 2025
a108677
use proper types instead of any
dhairyashiil Oct 26, 2025
89b9f2b
remove comments
dhairyashiil Oct 26, 2025
cee90fc
resolve merge conflicts
dhairyashiil Nov 2, 2025
036caf8
Merge remote-tracking branch 'upstream/main' into fix/disable-cal-bra…
dhairyashiil Nov 5, 2025
3c6b237
use prisma from repository
dhairyashiil Nov 5, 2025
5b68ef4
address cubics comment
dhairyashiil Nov 5, 2025
1ad1fbf
address cubics comment
dhairyashiil Nov 5, 2025
0707e91
Merge remote-tracking branch 'upstream/main' into fix/disable-cal-bra…
dhairyashiil Nov 6, 2025
4686bf6
chore: trigger again
Udit-takkar Nov 7, 2025
790410d
fix: type errro
Udit-takkar Nov 7, 2025
cdcac99
Merge remote-tracking branch 'upstream/main' into fix/disable-cal-bra…
dhairyashiil Nov 10, 2025
8b9f34f
added new method findByIdIncludeDestinationCalendarAndBranding and re…
dhairyashiil Nov 10, 2025
52edc3f
address cubics comments
dhairyashiil Nov 10, 2025
a7379f7
Merge remote-tracking branch 'upstream/main' into fix/disable-cal-bra…
dhairyashiil Nov 10, 2025
3270f07
Merge remote-tracking branch 'upstream/main' into fix/disable-cal-bra…
dhairyashiil Nov 11, 2025
8ac9884
use correct organization resolution for both team and user events
dhairyashiil Nov 11, 2025
ada2238
Merge remote-tracking branch 'upstream/main' into fix/disable-cal-bra…
dhairyashiil Nov 11, 2025
ac55d97
use getOrgIdFromMemberOrTeamId to compute orgId in other files
dhairyashiil Nov 11, 2025
2665fe0
use helped function instead of manually computing hidebranding
dhairyashiil Nov 11, 2025
1fc9525
use select instead of include, correctly compute orgId in reminder em…
dhairyashiil Nov 11, 2025
25edc37
resolved merge conflicts
dhairyashiil Nov 13, 2025
e1ddfa4
resolved merge conflicts
dhairyashiil Nov 13, 2025
982ca53
fix failing type check
dhairyashiil Nov 13, 2025
739a25a
Merge branch 'main' into fix/disable-cal-branding-user-team-organisation
dhairyashiil Nov 14, 2025
a55a321
repository pattern and update method name
dhairyashiil Nov 17, 2025
13ab76c
Merge upstream-fix-temp: Complete repository pattern refactoring
dhairyashiil Nov 17, 2025
a1ea8b8
update one missed file change
dhairyashiil Nov 17, 2025
aee5c72
Merge remote-tracking branch 'upstream/main' into fix/disable-cal-bra…
dhairyashiil Nov 19, 2025
5cc0343
Merge upstream/main into fix/disable-cal-branding-user-team-organisation
dhairyashiil Nov 21, 2025
aeaa5fb
fix: use dynamic hideBranding flag instead of hardcoded true in email…
dhairyashiil Nov 21, 2025
e126e0e
perf: remove unused eventType.parent select in connectAndJoin handler
dhairyashiil Nov 21, 2025
8cc3a5e
refactor: replace include with select in findByIdIncludeDestinationCa…
dhairyashiil Nov 21, 2025
dc8de6a
perf: remove unused eventType.parent select in handleDeleteCredential
dhairyashiil Nov 21, 2025
a3f5ce7
Merge remote-tracking branch 'upstream/main' into fix/disable-cal-bra…
dhairyashiil Nov 21, 2025
280e869
fix: add missing userPrimaryEmail field to BookingRepository select
dhairyashiil Nov 21, 2025
2e61d74
resolve merge conflicts
dhairyashiil Nov 21, 2025
08eb8a5
Merge upstream/main into fix/disable-cal-branding-user-team-organisation
dhairyashiil Dec 18, 2025
b63f1ed
fix failing tests and type check
dhairyashiil Dec 18, 2025
5b32395
fix failing tests
dhairyashiil Dec 18, 2025
ad490ca
fix failing tests
dhairyashiil Dec 18, 2025
bdf944e
Merge branch 'main' into fix/disable-cal-branding-user-team-organisation
Ryukemeister Jan 2, 2026
86c02d6
chore: update locales
Ryukemeister Jan 4, 2026
59b4cb6
fix: hide branding should be respected when sending verify email subject
Ryukemeister Jan 4, 2026
8f7b771
Merge branch 'main' into fix/disable-cal-branding-user-team-organisation
Ryukemeister Jan 4, 2026
e1e38d6
fix: bad imports
Ryukemeister Jan 4, 2026
15c57a1
chore: implement cubic feedback
Ryukemeister Jan 4, 2026
69adeb5
Merge branch 'main' into fix/disable-cal-branding-user-team-organisation
Ryukemeister Jan 4, 2026
68680ed
Merge branch 'main' into fix/disable-cal-branding-user-team-organisation
keithwillcode Jan 4, 2026
87df808
fixup: implement correct logic for booker email verification for hide…
Ryukemeister Jan 5, 2026
9f69a14
resolve merge conflicts
dhairyashiil Jan 6, 2026
5017d73
Merge branch 'main' into fix/disable-cal-branding-user-team-organisation
Ryukemeister Jan 7, 2026
bc20a74
Merge upstream/main into fix/disable-cal-branding-user-team-organisation
devin-ai-integration[bot] Jan 16, 2026
b36bab4
chore: implement PR feedback
Ryukemeister Jan 16, 2026
92ef99f
Merge branch 'main' into fix/disable-cal-branding-user-team-organisation
Ryukemeister Jan 17, 2026
cd32d98
fix: type check
Ryukemeister Jan 17, 2026
f6a1268
fix: unit tests
Ryukemeister Jan 17, 2026
de67265
chore: shift withHideBranding fn from email-manager to hideBranding
Ryukemeister Jan 18, 2026
6d21283
chore: implement cubic feedback
Ryukemeister Jan 18, 2026
e63f49c
Merge branch 'main' into fix/disable-cal-branding-user-team-organisation
Ryukemeister Jan 18, 2026
6006c55
Merge branch 'main' into fix/disable-cal-branding-user-team-organisation
Ryukemeister Jan 19, 2026
08bfdc7
Merge upstream/main into fix/disable-cal-branding-user-team-organisation
devin-ai-integration[bot] Jan 21, 2026
f5cd5f8
fix: revert formatting changes to improve PR readability
devin-ai-integration[bot] Jan 21, 2026
489f077
refactor: remove unused findByIdIncludeDestinationCalendar method
devin-ai-integration[bot] Jan 21, 2026
a506fe6
chore: implement cubic feedback
Ryukemeister Jan 21, 2026
5fa9998
Merge branch 'main' into fix/disable-cal-branding-user-team-organisation
Ryukemeister Jan 21, 2026
c0b8663
Merge branch 'main' into fix/disable-cal-branding-user-team-organisation
Ryukemeister Jan 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ import {
Get,
Query,
} from "@nestjs/common";
import { ApiTags as DocsTags, ApiExcludeController as DocsExcludeController } from "@nestjs/swagger";
import {
ApiTags as DocsTags,
ApiExcludeController as DocsExcludeController,
} from "@nestjs/swagger";

import { SUCCESS_STATUS } from "@calcom/platform-constants";
import { ApiResponse } from "@calcom/platform-types";
Expand All @@ -41,7 +44,12 @@ export class AtomsVerificationController {
@Post("/verification/email/send-code")
@Version(VERSION_NEUTRAL)
@HttpCode(HttpStatus.OK)
@Throttle({ limit: 3, ttl: 60000, blockDuration: 60000, name: "atoms_verification_email_send_code" })
@Throttle({
limit: 3,
ttl: 60000,
blockDuration: 60000,
name: "atoms_verification_email_send_code",
})
async sendEmailVerificationCode(
@Body() body: SendVerificationEmailInput
): Promise<SendVerificationEmailOutput> {
Expand All @@ -50,6 +58,7 @@ export class AtomsVerificationController {
username: body.username,
language: body.language,
isVerifyingEmail: body.isVerifyingEmail,
eventTypeId: body.eventTypeId,
});

return {
Expand All @@ -64,10 +73,11 @@ export class AtomsVerificationController {
async checkEmailVerificationRequired(
@Query() query: CheckEmailVerificationRequiredParams
): Promise<ApiResponse<boolean>> {
const required = await this.verificationService.checkEmailVerificationRequired({
email: query.email,
userSessionEmail: query.userSessionEmail,
});
const required =
await this.verificationService.checkEmailVerificationRequired({
email: query.email,
userSessionEmail: query.userSessionEmail,
});

return {
data: required,
Expand All @@ -78,7 +88,9 @@ export class AtomsVerificationController {
@Post("/verification/email/verify-code")
@Version(VERSION_NEUTRAL)
@HttpCode(HttpStatus.OK)
async verifyEmailCode(@Body() body: VerifyEmailCodeInput): Promise<VerifyEmailCodeOutput> {
async verifyEmailCode(
@Body() body: VerifyEmailCodeInput
): Promise<VerifyEmailCodeOutput> {
await this.verificationService.verifyEmailCodeUnAuthenticated({
email: body.email,
code: body.code,
Expand All @@ -98,10 +110,11 @@ export class AtomsVerificationController {
@Body() body: VerifyEmailCodeInput,
@GetUser() user: UserWithProfile
): Promise<VerifyEmailCodeOutput> {
const verified = await this.verificationService.verifyEmailCodeAuthenticated(user, {
email: body.email,
code: body.code,
});
const verified =
await this.verificationService.verifyEmailCodeAuthenticated(user, {
email: body.email,
code: body.code,
});

return {
data: { verified },
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
import { IsEmail, IsOptional, IsString, IsBoolean } from "class-validator";
import { IsBoolean, IsEmail, IsInt, IsOptional, IsString } from "class-validator";

export class SendVerificationEmailInput {
@ApiProperty({ example: "user@example.com" })
Expand All @@ -20,4 +20,9 @@ export class SendVerificationEmailInput {
@IsOptional()
@IsBoolean()
isVerifyingEmail?: boolean;

@ApiPropertyOptional({ example: 123 })
@IsOptional()
@IsInt()
eventTypeId?: number;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ import { SendVerificationEmailInput } from "@/modules/atoms/inputs/send-verifica
import { VerifyEmailCodeInput } from "@/modules/atoms/inputs/verify-email-code.input";
import { UserWithProfile } from "@/modules/users/users.repository";
import { UsersRepository } from "@/modules/users/users.repository";
import { Injectable, BadRequestException, UnauthorizedException } from "@nestjs/common";
import {
Injectable,
BadRequestException,
UnauthorizedException,
} from "@nestjs/common";

import {
verifyCodeUnAuthenticated,
Expand All @@ -21,7 +25,9 @@ export class VerificationAtomsService {
private readonly usersRepository: UsersRepository
) {}

async checkEmailVerificationRequired(input: CheckEmailVerificationRequiredParams) {
async checkEmailVerificationRequired(
input: CheckEmailVerificationRequiredParams
) {
return await checkEmailVerificationRequired(input);
}

Expand All @@ -41,7 +47,10 @@ export class VerificationAtomsService {
}
}

async verifyEmailCodeAuthenticated(user: UserWithProfile, input: VerifyEmailCodeInput) {
async verifyEmailCodeAuthenticated(
user: UserWithProfile,
input: VerifyEmailCodeInput
) {
try {
return await verifyCodeAuthenticated({
user,
Expand All @@ -54,7 +63,9 @@ export class VerificationAtomsService {
throw new BadRequestException("Invalid verification code");
}
if (error.message === "BAD_REQUEST") {
throw new BadRequestException("Email, code, and user ID are required");
throw new BadRequestException(
"Email, code, and user ID are required"
);
}
}
throw new UnauthorizedException("Verification failed");
Expand All @@ -67,28 +78,35 @@ export class VerificationAtomsService {
username: input.username,
language: input.language,
isVerifyingEmail: input.isVerifyingEmail,
eventTypeId: input.eventTypeId,
});
}

async getVerifiedEmails(input: GetVerifiedEmailsInput): Promise<string[]> {
const { userId, userEmail, teamId } = input;
const userEmailWithoutOauthClientId = this.removeClientIdFromEmail(userEmail);
const userEmailWithoutOauthClientId =
this.removeClientIdFromEmail(userEmail);

if (teamId) {
const verifiedEmails: string[] = [];

const teamMembers = await this.usersRepository.getUserEmailsVerifiedForTeam(teamId);
const teamMembers =
await this.usersRepository.getUserEmailsVerifiedForTeam(teamId);

if (teamMembers.length === 0) {
return verifiedEmails;
}

teamMembers.forEach((member) => {
const memberEmailWithoutOauthClientId = this.removeClientIdFromEmail(member.email);
const memberEmailWithoutOauthClientId = this.removeClientIdFromEmail(
member.email
);

verifiedEmails.push(memberEmailWithoutOauthClientId);
member.secondaryEmails.forEach((secondaryEmail) => {
verifiedEmails.push(this.removeClientIdFromEmail(secondaryEmail.email));
verifiedEmails.push(
this.removeClientIdFromEmail(secondaryEmail.email)
);
});
});

Expand All @@ -97,9 +115,14 @@ export class VerificationAtomsService {

let verifiedEmails = [userEmailWithoutOauthClientId];

const secondaryEmails = await this.atomsSecondaryEmailsRepository.getSecondaryEmailsVerified(userId);
const secondaryEmails =
await this.atomsSecondaryEmailsRepository.getSecondaryEmailsVerified(
userId
);
verifiedEmails = verifiedEmails.concat(
secondaryEmails.map((secondaryEmail) => this.removeClientIdFromEmail(secondaryEmail.email))
secondaryEmails.map((secondaryEmail) =>
this.removeClientIdFromEmail(secondaryEmail.email)
)
);

return verifiedEmails;
Expand All @@ -115,8 +138,14 @@ export class VerificationAtomsService {
email: string;
}): Promise<boolean> {
const existingSecondaryEmail =
await this.atomsSecondaryEmailsRepository.getExistingSecondaryEmailByUserAndEmail(userId, email);
const alreadyExistingEmail = await this.atomsSecondaryEmailsRepository.getExistingSecondaryEmail(email);
await this.atomsSecondaryEmailsRepository.getExistingSecondaryEmailByUserAndEmail(
userId,
email
);
const alreadyExistingEmail =
await this.atomsSecondaryEmailsRepository.getExistingSecondaryEmail(
email
);

if (alreadyExistingEmail) {
throw new BadRequestException("Email already exists");
Expand All @@ -126,7 +155,10 @@ export class VerificationAtomsService {
return true;
}

await this.atomsSecondaryEmailsRepository.addSecondaryEmailVerified(userId, email);
await this.atomsSecondaryEmailsRepository.addSecondaryEmailVerified(
userId,
email
);

return true;
}
Expand Down
43 changes: 42 additions & 1 deletion apps/web/app/api/cron/bookingReminder/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import { NextResponse } from "next/server";
import dayjs from "@calcom/dayjs";
import { sendOrganizerRequestReminderEmail } from "@calcom/emails/email-manager";
import { getCalEventResponses } from "@calcom/features/bookings/lib/getCalEventResponses";
import { shouldHideBrandingForEvent, withHideBranding } from "@calcom/features/profile/lib/hideBranding";
import getOrgIdFromMemberOrTeamId from "@calcom/lib/getOrgIdFromMemberOrTeamId";
import { getTeamIdFromEventType } from "@calcom/lib/getTeamIdFromEventType";
import { isPrismaObjOrUndefined } from "@calcom/lib/isPrismaObj";
import { parseRecurringEvent } from "@calcom/lib/isRecurringEvent";
import { getTranslation } from "@calcom/lib/server/i18n";
Expand Down Expand Up @@ -57,14 +60,30 @@ async function postHandler(request: NextRequest) {
timeZone: true,
destinationCalendar: true,
isPlatformManaged: true,
hideBranding: true,
organizationId: true,
platformOAuthClients: { select: { id: true, areEmailsEnabled: true } },
},
},
eventType: {
select: {
id: true,
parentId: true,
recurringEvent: true,
bookingFields: true,
metadata: true,
team: {
select: {
id: true,
parentId: true,
hideBranding: true,
parent: {
select: {
hideBranding: true,
},
},
},
},
},
},
responses: true,
Expand Down Expand Up @@ -116,6 +135,24 @@ async function postHandler(request: NextRequest) {

const attendeesList = await Promise.all(attendeesListPromises);
const selectedDestinationCalendar = booking.destinationCalendar || user.destinationCalendar;

const teamId = await getTeamIdFromEventType({
eventType: {
team: { id: booking.eventType?.team?.id ?? null },
parentId: booking.eventType?.parentId ?? null,
},
});
const orgId = await getOrgIdFromMemberOrTeamId({
memberId: booking.user?.id ?? null,
teamId,
});
const hideBranding = await shouldHideBrandingForEvent({
eventTypeId: booking.eventType?.id ?? 0,
team: booking.eventType?.team ?? null,
owner: booking.user ?? null,
organizationId: orgId ?? null,
});

const evt: CalendarEvent = {
type: booking.title,
title: booking.title,
Expand All @@ -139,9 +176,13 @@ async function postHandler(request: NextRequest) {
uid: booking.uid,
recurringEvent: parseRecurringEvent(booking.eventType?.recurringEvent),
destinationCalendar: selectedDestinationCalendar ? [selectedDestinationCalendar] : [],
hideBranding,
};

await sendOrganizerRequestReminderEmail(evt, booking?.eventType?.metadata as EventTypeMetadata);
await sendOrganizerRequestReminderEmail(
withHideBranding(evt),
booking?.eventType?.metadata as EventTypeMetadata
);

await prisma.reminderMail.create({
data: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -251,11 +251,11 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
hideBranding: isPlatformBooking
? true
: await shouldHideBrandingForEvent({
eventTypeId: eventType.id,
team: eventType?.parent?.team ?? eventType?.team,
owner: eventType.users[0] ?? null,
organizationId: session?.user?.profile?.organizationId ?? session?.user?.org?.id ?? null,
}),
eventTypeId: eventType.id,
team: eventType?.parent?.team ?? eventType?.team,
owner: eventType.users[0] ?? null,
organizationId: session?.user?.profile?.organizationId ?? session?.user?.org?.id ?? null,
}),
profile,
eventType,
recurringBookings: await getRecurringBookings(bookingInfo.recurringEventId),
Expand Down
1 change: 1 addition & 0 deletions apps/web/public/static/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"reset_password_subject": "{{appName}}: Reset password instructions",
"verify_email_subject": "{{appName}}: Verify your account",
"verify_email_subject_verifying_email": "{{appName}}: Verify your email",
"hide_branding_verify_email_subject": "Action Required: Verify your email",
"check_your_email": "Check your email",
"old_email_address": "Old email",
"new_email_address": "New email",
Expand Down
25 changes: 23 additions & 2 deletions packages/app-store/_utils/payments/handlePaymentSuccess.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,14 @@ import { getAllWorkflowsFromEventType } from "@calcom/features/ee/workflows/lib/
import { WorkflowService } from "@calcom/features/ee/workflows/lib/service/WorkflowService";
import { getPlatformParams } from "@calcom/features/platform-oauth-client/get-platform-params";
import { PlatformOAuthClientRepository } from "@calcom/features/platform-oauth-client/platform-oauth-client.repository";
import { shouldHideBrandingForEvent } from "@calcom/features/profile/lib/hideBranding";
import tasker from "@calcom/features/tasker";
import getWebhooks from "@calcom/features/webhooks/lib/getWebhooks";
import sendPayload from "@calcom/features/webhooks/lib/sendOrSchedulePayload";
import type { EventPayloadType, EventTypeInfo } from "@calcom/features/webhooks/lib/sendPayload";
import { getVideoCallUrlFromCalEvent } from "@calcom/lib/CalEventParser";
import getOrgIdFromMemberOrTeamId from "@calcom/lib/getOrgIdFromMemberOrTeamId";
import { getTeamIdFromEventType } from "@calcom/lib/getTeamIdFromEventType";
import tasker from "@calcom/features/tasker";
import { HttpError as HttpCode } from "@calcom/lib/http-error";
import logger from "@calcom/lib/logger";
import { safeStringify } from "@calcom/lib/safeStringify";
Expand Down Expand Up @@ -260,7 +261,27 @@ export async function handlePaymentSuccess(params: {
log.debug(`handling booking request for eventId ${eventType.id}`);
}
} else if (areEmailsEnabled) {
await sendScheduledEmailsAndSMS({ ...evt }, undefined, undefined, undefined, eventType.metadata);
const teamId = await getTeamIdFromEventType({
eventType: {
team: { id: booking.eventType?.teamId ?? null },
parentId: booking?.eventType?.parentId ?? null,
},
});
const orgId = await getOrgIdFromMemberOrTeamId({ memberId: booking.userId, teamId });
const hideBranding = await shouldHideBrandingForEvent({
eventTypeId: booking.eventType?.id ?? 0,
team: booking.eventType?.team ?? null,
owner: userWithCredentials ?? null,
organizationId: orgId ?? null,
});

await sendScheduledEmailsAndSMS(
{ ...evt, hideBranding },
undefined,
undefined,
undefined,
booking.eventType?.metadata as EventTypeMetadata
);
}

throw new HttpCode({
Expand Down
Loading
Loading