Fixes formatted description in email + sanitize html everywhere (#7928)
* use sanitize-html instead
* add list formatting
* add lost changes from merging main
* fixes caused by merge
* remove dompurify
* Revert "remove dompurify"
This reverts commit 583bb623c7
.
* sanitize already done in editor
* Update yarn.lock
---------
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
pull/7986/head
parent
02d32321aa
commit
3aaa1cde0d
|
@ -1,11 +1,11 @@
|
|||
export type EventTypeDescriptionSafeProps = {
|
||||
eventType: { description: string | null };
|
||||
eventType: { description: string | null; descriptionAsSafeHTML: string | null };
|
||||
};
|
||||
|
||||
export const EventTypeDescriptionSafeHTML = ({ eventType }: EventTypeDescriptionSafeProps) => {
|
||||
const props: JSX.IntrinsicElements["div"] = { suppressHydrationWarning: true };
|
||||
// @ts-expect-error: @see packages/prisma/middleware/eventTypeDescriptionParseAndSanitize.ts
|
||||
if (eventType.description) props.dangerouslySetInnerHTML = { __html: eventType.descriptionAsSafeHTML };
|
||||
if (eventType.description)
|
||||
props.dangerouslySetInnerHTML = { __html: eventType.descriptionAsSafeHTML || "" };
|
||||
return <div {...props} />;
|
||||
};
|
||||
|
||||
|
|
|
@ -2,13 +2,14 @@ import Link from "next/link";
|
|||
|
||||
import { WEBAPP_URL } from "@calcom/lib/constants";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { md } from "@calcom/lib/markdownIt";
|
||||
import type { TeamWithMembers } from "@calcom/lib/server/queries/teams";
|
||||
import { Avatar } from "@calcom/ui";
|
||||
|
||||
type TeamType = NonNullable<TeamWithMembers>;
|
||||
type MembersType = TeamType["members"];
|
||||
type MemberType = MembersType[number];
|
||||
type MemberType = MembersType[number] & { safeBio: string | null };
|
||||
|
||||
type TeamTypeWithSafeHtml = Omit<TeamType, "members"> & { members: MemberType[] };
|
||||
|
||||
const Member = ({ member, teamName }: { member: MemberType; teamName: string | null }) => {
|
||||
const { t } = useLocale();
|
||||
|
@ -30,7 +31,7 @@ const Member = ({ member, teamName }: { member: MemberType; teamName: string | n
|
|||
<>
|
||||
<div
|
||||
className="dark:text-darkgray-600 text-sm text-gray-500 [&_a]:text-blue-500 [&_a]:underline [&_a]:hover:text-blue-600"
|
||||
dangerouslySetInnerHTML={{ __html: md.render(member.bio || "") }}
|
||||
dangerouslySetInnerHTML={{ __html: member.safeBio || "" }}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
|
@ -43,7 +44,7 @@ const Member = ({ member, teamName }: { member: MemberType; teamName: string | n
|
|||
);
|
||||
};
|
||||
|
||||
const Members = ({ members, teamName }: { members: MembersType; teamName: string | null }) => {
|
||||
const Members = ({ members, teamName }: { members: MemberType[]; teamName: string | null }) => {
|
||||
if (!members || members.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
@ -57,7 +58,7 @@ const Members = ({ members, teamName }: { members: MembersType; teamName: string
|
|||
);
|
||||
};
|
||||
|
||||
const Team = ({ team }: { team: TeamType }) => {
|
||||
const Team = ({ team }: { team: TeamTypeWithSafeHtml }) => {
|
||||
return (
|
||||
<div>
|
||||
<Members members={team.members} teamName={team.name} />
|
||||
|
|
|
@ -119,6 +119,7 @@
|
|||
"react-window": "^1.8.7",
|
||||
"remark": "^14.0.2",
|
||||
"rrule": "^2.7.1",
|
||||
"sanitize-html": "^2.10.0",
|
||||
"schema-dts": "^1.1.0",
|
||||
"short-uuid": "^4.2.0",
|
||||
"strip-markdown": "^5.0.0",
|
||||
|
@ -156,6 +157,7 @@
|
|||
"@types/react-phone-number-input": "^3.0.14",
|
||||
"@types/react-virtualized-auto-sizer": "^1.0.1",
|
||||
"@types/react-window": "^1.8.5",
|
||||
"@types/sanitize-html": "^2.9.0",
|
||||
"@types/stripe": "^8.0.417",
|
||||
"@types/uuid": "8.3.1",
|
||||
"autoprefixer": "^10.4.12",
|
||||
|
|
|
@ -24,7 +24,7 @@ import defaultEvents, {
|
|||
} from "@calcom/lib/defaultEvents";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import useTheme from "@calcom/lib/hooks/useTheme";
|
||||
import { md } from "@calcom/lib/markdownIt";
|
||||
import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML";
|
||||
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
|
||||
import prisma from "@calcom/prisma";
|
||||
import { baseEventTypeSelect } from "@calcom/prisma/selects";
|
||||
|
@ -147,7 +147,7 @@ export default function User(props: inferSSRProps<typeof getServerSideProps> & E
|
|||
<>
|
||||
<div
|
||||
className=" dark:text-darkgray-600 text-sm text-gray-500 [&_a]:text-blue-500 [&_a]:underline [&_a]:hover:text-blue-600"
|
||||
dangerouslySetInnerHTML={{ __html: md.render(user.bio || "") }}
|
||||
dangerouslySetInnerHTML={{ __html: props.safeBio }}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
@ -343,6 +343,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
|
|||
const eventTypes = eventTypesRaw.map((eventType) => ({
|
||||
...eventType,
|
||||
metadata: EventTypeMetaDataSchema.parse(eventType.metadata || {}),
|
||||
descriptionAsSafeHTML: markdownToSafeHTML(eventType.description),
|
||||
}));
|
||||
|
||||
const isSingleUser = users.length === 1;
|
||||
|
@ -352,9 +353,12 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
|
|||
})
|
||||
: [];
|
||||
|
||||
const safeBio = markdownToSafeHTML(user.bio) || "";
|
||||
|
||||
return {
|
||||
props: {
|
||||
users,
|
||||
safeBio,
|
||||
profile,
|
||||
user: {
|
||||
emailMd5: crypto.createHash("md5").update(user.email).digest("hex"),
|
||||
|
|
|
@ -5,7 +5,7 @@ import type { LocationObject } from "@calcom/app-store/locations";
|
|||
import { IS_TEAM_BILLING_ENABLED, WEBAPP_URL } from "@calcom/lib/constants";
|
||||
import hasKeyInMetadata from "@calcom/lib/hasKeyInMetadata";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { addListFormatting } from "@calcom/lib/markdownIt";
|
||||
import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML";
|
||||
import type { User } from "@calcom/prisma/client";
|
||||
|
||||
import { isBrandingHidden } from "@lib/isBrandingHidden";
|
||||
|
@ -59,7 +59,6 @@ Type.isThemeSupported = true;
|
|||
const paramsSchema = z.object({ type: z.string(), user: z.string() });
|
||||
async function getUserPageProps(context: GetStaticPropsContext) {
|
||||
// load server side dependencies
|
||||
const MarkdownIt = await import("markdown-it").then((mod) => mod.default);
|
||||
const prisma = await import("@calcom/prisma").then((mod) => mod.default);
|
||||
const { privacyFilteredLocations } = await import("@calcom/app-store/locations");
|
||||
const { parseRecurringEvent } = await import("@calcom/lib/isRecurringEvent");
|
||||
|
@ -124,9 +123,6 @@ async function getUserPageProps(context: GetStaticPropsContext) {
|
|||
},
|
||||
},
|
||||
});
|
||||
|
||||
const md = new MarkdownIt("default", { html: true, breaks: true, linkify: true });
|
||||
|
||||
if (!user || !user.eventTypes.length) return { notFound: true };
|
||||
|
||||
const [eventType]: ((typeof user.eventTypes)[number] & {
|
||||
|
@ -153,7 +149,7 @@ async function getUserPageProps(context: GetStaticPropsContext) {
|
|||
metadata: EventTypeMetaDataSchema.parse(eventType.metadata || {}),
|
||||
recurringEvent: parseRecurringEvent(eventType.recurringEvent),
|
||||
locations: privacyFilteredLocations(locations),
|
||||
descriptionAsSafeHTML: eventType.description ? addListFormatting(md.render(eventType.description)) : null,
|
||||
descriptionAsSafeHTML: markdownToSafeHTML(eventType.description),
|
||||
});
|
||||
// Check if the user you are logging into has any active teams or premium user name
|
||||
const hasActiveTeam =
|
||||
|
|
|
@ -14,6 +14,7 @@ import {
|
|||
getUsernameList,
|
||||
} from "@calcom/lib/defaultEvents";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML";
|
||||
import prisma, { bookEventTypeSelect } from "@calcom/prisma";
|
||||
import {
|
||||
customInputSchema,
|
||||
|
@ -189,6 +190,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
|
|||
slug: u.username,
|
||||
theme: u.theme,
|
||||
})),
|
||||
descriptionAsSafeHTML: markdownToSafeHTML(eventType.description),
|
||||
};
|
||||
})[0];
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import type { LocationObject } from "@calcom/core/location";
|
|||
import { privacyFilteredLocations } from "@calcom/core/location";
|
||||
import { parseRecurringEvent } from "@calcom/lib";
|
||||
import { getWorkingHours } from "@calcom/lib/availability";
|
||||
import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML";
|
||||
import { availiblityPageEventTypeSelect } from "@calcom/prisma";
|
||||
import prisma from "@calcom/prisma";
|
||||
import { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
|
||||
|
@ -119,6 +120,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
|
|||
hideBranding: u.hideBranding,
|
||||
timeZone: u.timeZone,
|
||||
})),
|
||||
descriptionAsSafeHTML: markdownToSafeHTML(hashedLink.eventType.description),
|
||||
});
|
||||
|
||||
const [user] = users;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import type { GetServerSidePropsContext } from "next";
|
||||
|
||||
import { parseRecurringEvent } from "@calcom/lib";
|
||||
import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML";
|
||||
import prisma from "@calcom/prisma";
|
||||
import { bookEventTypeSelect } from "@calcom/prisma/selects";
|
||||
import { customInputSchema, eventTypeBookingFields, EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
|
||||
|
@ -93,6 +94,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
|
|||
brandColor: u.brandColor,
|
||||
darkBrandColor: u.darkBrandColor,
|
||||
})),
|
||||
descriptionAsSafeHTML: markdownToSafeHTML(eventType.description),
|
||||
};
|
||||
})[0];
|
||||
|
||||
|
|
|
@ -141,7 +141,7 @@ const Item = ({ type, group, readOnly }: { type: EventType; group: EventTypeGrou
|
|||
</div>
|
||||
<EventTypeDescription
|
||||
// @ts-expect-error FIXME: We have a type mismatch here @hariombalhara @sean-brydon
|
||||
eventType={type}
|
||||
eventType={{ ...type, descriptionAsSafeHTML: type.safeDescription }}
|
||||
shortenDescription
|
||||
/>
|
||||
</Link>
|
||||
|
|
|
@ -10,7 +10,7 @@ import { CAL_URL } from "@calcom/lib/constants";
|
|||
import { getPlaceholderAvatar } from "@calcom/lib/defaultAvatarImage";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import useTheme from "@calcom/lib/hooks/useTheme";
|
||||
import { md } from "@calcom/lib/markdownIt";
|
||||
import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML";
|
||||
import { getTeamWithMembers } from "@calcom/lib/server/queries/teams";
|
||||
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
|
||||
import prisma from "@calcom/prisma";
|
||||
|
@ -113,7 +113,7 @@ function TeamPage({ team, isUnpublished }: TeamPageProps) {
|
|||
<>
|
||||
<div
|
||||
className="dark:text-darkgray-600 text-sm text-gray-500 [&_a]:text-blue-500 [&_a]:underline [&_a]:hover:text-blue-600"
|
||||
dangerouslySetInnerHTML={{ __html: md.render(team.bio || "") }}
|
||||
dangerouslySetInnerHTML={{ __html: team.safeBio }}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
@ -187,11 +187,18 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
|
|||
...user,
|
||||
avatar: CAL_URL + "/" + user.username + "/avatar.png",
|
||||
})),
|
||||
descriptionAsSafeHTML: markdownToSafeHTML(type.description),
|
||||
}));
|
||||
|
||||
const safeBio = markdownToSafeHTML(team.bio) || "";
|
||||
|
||||
const members = team.members.map((member) => {
|
||||
return { ...member, safeBio: markdownToSafeHTML(member.bio || "") };
|
||||
});
|
||||
|
||||
return {
|
||||
props: {
|
||||
team,
|
||||
team: { ...team, safeBio, members },
|
||||
trpcState: ssr.dehydrate(),
|
||||
},
|
||||
} as const;
|
||||
|
|
|
@ -4,6 +4,7 @@ import type { LocationObject } from "@calcom/core/location";
|
|||
import { privacyFilteredLocations } from "@calcom/core/location";
|
||||
import { parseRecurringEvent } from "@calcom/lib";
|
||||
import { getWorkingHours } from "@calcom/lib/availability";
|
||||
import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML";
|
||||
import prisma from "@calcom/prisma";
|
||||
import { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
|
||||
|
||||
|
@ -170,6 +171,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
|
|||
hideBranding,
|
||||
timeZone,
|
||||
})),
|
||||
descriptionAsSafeHTML: markdownToSafeHTML(eventType.description),
|
||||
});
|
||||
|
||||
eventTypeObject.availability = [];
|
||||
|
|
|
@ -5,6 +5,7 @@ import type { LocationObject } from "@calcom/app-store/locations";
|
|||
import { privacyFilteredLocations } from "@calcom/app-store/locations";
|
||||
import { getBookingFieldsWithSystemFields } from "@calcom/features/bookings/lib/getBookingFields";
|
||||
import { parseRecurringEvent } from "@calcom/lib";
|
||||
import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML";
|
||||
import prisma from "@calcom/prisma";
|
||||
import { customInputSchema, eventTypeBookingFields, EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
|
||||
|
||||
|
@ -121,6 +122,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
|
|||
image: u.avatar,
|
||||
slug: u.username,
|
||||
})),
|
||||
descriptionAsSafeHTML: markdownToSafeHTML(eventType.description),
|
||||
};
|
||||
})[0];
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML";
|
||||
|
||||
const Spacer = () => <p style={{ height: 6 }} />;
|
||||
|
||||
export const Info = (props: {
|
||||
|
@ -6,8 +8,14 @@ export const Info = (props: {
|
|||
extraInfo?: React.ReactNode;
|
||||
withSpacer?: boolean;
|
||||
lineThrough?: boolean;
|
||||
formatted?: boolean;
|
||||
}) => {
|
||||
if (!props.description || props.description === "") return null;
|
||||
|
||||
const descriptionCSS = "color: '#101010'; font-weight: 400; line-height: 24px; margin: 0;";
|
||||
|
||||
const safeDescription = markdownToSafeHTML(props.description.toString()) || "";
|
||||
|
||||
return (
|
||||
<>
|
||||
{props.withSpacer && <Spacer />}
|
||||
|
@ -21,7 +29,18 @@ export const Info = (props: {
|
|||
whiteSpace: "pre-wrap",
|
||||
textDecoration: props.lineThrough ? "line-through" : undefined,
|
||||
}}>
|
||||
{props.description}
|
||||
{props.formatted ? (
|
||||
<p
|
||||
className="dark:text-darkgray-600 mt-2 text-sm text-gray-500 [&_a]:text-blue-500 [&_a]:underline [&_a]:hover:text-blue-600"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: safeDescription
|
||||
.replaceAll("<p>", `<p style="${descriptionCSS}">`)
|
||||
.replaceAll("<li>", `<li style="${descriptionCSS}">`),
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
props.description
|
||||
)}
|
||||
</p>
|
||||
{props.extraInfo}
|
||||
</div>
|
||||
|
|
|
@ -76,7 +76,7 @@ export const BaseScheduledEmail = (
|
|||
<WhenInfo calEvent={props.calEvent} t={t} timeZone={timeZone} />
|
||||
<WhoInfo calEvent={props.calEvent} t={t} />
|
||||
<LocationInfo calEvent={props.calEvent} t={t} />
|
||||
<Info label={t("description")} description={props.calEvent.description} withSpacer />
|
||||
<Info label={t("description")} description={props.calEvent.description} withSpacer formatted />
|
||||
<Info label={t("additional_notes")} description={props.calEvent.additionalNotes} withSpacer />
|
||||
{props.includeAppsStatus && <AppsStatus calEvent={props.calEvent} t={t} />}
|
||||
<UserFieldsResponses calEvent={props.calEvent} />
|
||||
|
|
|
@ -256,7 +256,7 @@ const ProfileView = () => {
|
|||
<Label className="mt-5 text-black">{t("about")}</Label>
|
||||
<div
|
||||
className="dark:text-darkgray-600 text-sm text-gray-500 [&_a]:text-blue-500 [&_a]:underline [&_a]:hover:text-blue-600"
|
||||
dangerouslySetInnerHTML={{ __html: md.render(team.bio || "") }}
|
||||
dangerouslySetInnerHTML={{ __html: team.safeBio || "" }}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
|
|
@ -7,7 +7,6 @@ import type { z } from "zod";
|
|||
import { classNames, parseRecurringEvent } from "@calcom/lib";
|
||||
import getPaymentAppData from "@calcom/lib/getPaymentAppData";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { addListFormatting, md } from "@calcom/lib/markdownIt";
|
||||
import type { baseEventTypeSelect } from "@calcom/prisma";
|
||||
import type { EventTypeModel } from "@calcom/prisma/zod";
|
||||
import { Badge } from "@calcom/ui";
|
||||
|
@ -26,6 +25,7 @@ export type EventTypeDescriptionProps = {
|
|||
z.infer<typeof EventTypeModel>,
|
||||
Exclude<keyof typeof baseEventTypeSelect, "recurringEvent"> | "metadata"
|
||||
> & {
|
||||
descriptionAsSafeHTML?: string | null;
|
||||
recurringEvent: Prisma.JsonValue;
|
||||
seatsPerTimeSlot?: number;
|
||||
};
|
||||
|
@ -57,7 +57,7 @@ export const EventTypeDescription = ({
|
|||
shortenDescription ? "line-clamp-4" : ""
|
||||
)}
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: addListFormatting(md.render(eventType.description)),
|
||||
__html: eventType.descriptionAsSafeHTML || "",
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
|
|
@ -1,12 +1,3 @@
|
|||
import MarkdownIt from "markdown-it";
|
||||
|
||||
export const md = new MarkdownIt("default", { html: true, breaks: true, linkify: true });
|
||||
|
||||
export function addListFormatting(html: string) {
|
||||
return html
|
||||
.replaceAll("<ul>", "<ul style='list-style-type: disc; list-style-position: inside; margin-left: 12px'>")
|
||||
.replaceAll(
|
||||
"<ol>",
|
||||
"<ol style='list-style-type: decimal; list-style-position: inside; margin-left: 12px'>"
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
import sanitizeHtml from "sanitize-html";
|
||||
|
||||
import { md } from "@calcom/lib/markdownIt";
|
||||
|
||||
export function markdownToSafeHTML(markdown: string | null) {
|
||||
if (!markdown) return null;
|
||||
|
||||
const html = md.render(markdown);
|
||||
|
||||
const safeHTML = sanitizeHtml(html);
|
||||
|
||||
const safeHTMLWithListFormatting = safeHTML
|
||||
.replace(
|
||||
/<ul>/g,
|
||||
"<ul style='list-style-type: disc; list-style-position: inside; margin-left: 12px; margin-bottom: 4px'>"
|
||||
)
|
||||
.replace(
|
||||
/<ol>/g,
|
||||
"<ol style='list-style-type: decimal; list-style-position: inside; margin-left: 12px; margin-bottom: 4px'>"
|
||||
);
|
||||
|
||||
return safeHTMLWithListFormatting;
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
import { Prisma, PrismaClient } from "@prisma/client";
|
||||
import type { Prisma } from "@prisma/client";
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
|
||||
import { bookingReferenceMiddleware, eventTypeDescriptionParseAndSanitizeMiddleware } from "./middleware";
|
||||
import { bookingReferenceMiddleware } from "./middleware";
|
||||
|
||||
declare global {
|
||||
// eslint-disable-next-line no-var
|
||||
|
@ -21,7 +22,6 @@ if (process.env.NODE_ENV !== "production") {
|
|||
}
|
||||
// If any changed on middleware server restart is required
|
||||
bookingReferenceMiddleware(prisma);
|
||||
eventTypeDescriptionParseAndSanitizeMiddleware(prisma);
|
||||
|
||||
export default prisma;
|
||||
|
||||
|
|
|
@ -1,43 +0,0 @@
|
|||
import type { PrismaClient, EventType } from "@prisma/client";
|
||||
|
||||
import { md } from "@calcom/lib/markdownIt";
|
||||
|
||||
function parseAndSanitize(description: string) {
|
||||
const parsedMarkdown = md.render(description);
|
||||
return parsedMarkdown;
|
||||
}
|
||||
|
||||
function getParsedResults(eventTypes: EventType | EventType[]) {
|
||||
const results = Array.isArray(eventTypes) ? eventTypes : [eventTypes];
|
||||
const parsedResults = results.map((record) => ({
|
||||
...record,
|
||||
descriptionAsSafeHTML: record.description ? parseAndSanitize(record.description) : null,
|
||||
}));
|
||||
/* If the original result was an array, return the parsed array, otherwise is a single record */
|
||||
return Array.isArray(eventTypes) ? parsedResults : parsedResults[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses event type descriptions and treat them as markdown,
|
||||
* then sanitizes the resulting HTML and adds a new `descriptionAsSafeHTML` property.
|
||||
*/
|
||||
function eventTypeDescriptionParseAndSanitize(prisma: PrismaClient) {
|
||||
prisma.$use(async (params, next) => {
|
||||
if (params.model === "Team") {
|
||||
const result = await next(params);
|
||||
if (result?.eventTypes) {
|
||||
result.eventTypes = getParsedResults(result.eventTypes);
|
||||
}
|
||||
return result;
|
||||
} else if (params.model === "EventType") {
|
||||
const result = await next(params);
|
||||
if (result) {
|
||||
return getParsedResults(result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return next(params);
|
||||
});
|
||||
}
|
||||
|
||||
export default eventTypeDescriptionParseAndSanitize;
|
|
@ -1,2 +1 @@
|
|||
export { default as bookingReferenceMiddleware } from "./bookingReference";
|
||||
export { default as eventTypeDescriptionParseAndSanitizeMiddleware } from "./eventTypeDescriptionParseAndSanitize";
|
||||
|
|
|
@ -203,7 +203,8 @@
|
|||
"categories": ["video"],
|
||||
"slug": "facetime",
|
||||
"type": "facetime_video",
|
||||
"isTemplate": false},
|
||||
"isTemplate": false
|
||||
},
|
||||
{
|
||||
"dirName": "zohocrm",
|
||||
"categories": ["other"],
|
||||
|
|
|
@ -12,6 +12,7 @@ import getApps, { getAppFromLocationValue, getAppFromSlug } from "@calcom/app-st
|
|||
import { validateIntervalLimitOrder } from "@calcom/lib";
|
||||
import { CAL_URL } from "@calcom/lib/constants";
|
||||
import getEventTypeById from "@calcom/lib/getEventTypeById";
|
||||
import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML";
|
||||
import { baseEventTypeSelect, baseUserSelect } from "@calcom/prisma";
|
||||
import { _DestinationCalendarModel, _EventTypeModel } from "@calcom/prisma/zod";
|
||||
import type { CustomInputSchema } from "@calcom/prisma/zod-utils";
|
||||
|
@ -283,6 +284,7 @@ export const eventTypesRouter = router({
|
|||
|
||||
const mapEventType = (eventType: (typeof user.eventTypes)[number]) => ({
|
||||
...eventType,
|
||||
safeDescription: markdownToSafeHTML(eventType.description),
|
||||
users: !!eventType.hosts?.length ? eventType.hosts.map((host) => host.user) : eventType.users,
|
||||
// @FIXME: cc @hariombalhara This is failing with production data
|
||||
// metadata: EventTypeMetaDataSchema.parse(eventType.metadata),
|
||||
|
|
|
@ -11,6 +11,7 @@ import {
|
|||
updateQuantitySubscriptionFromStripe,
|
||||
} from "@calcom/features/ee/teams/lib/payments";
|
||||
import { IS_TEAM_BILLING_ENABLED, WEBAPP_URL } from "@calcom/lib/constants";
|
||||
import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML";
|
||||
import { getTranslation } from "@calcom/lib/server/i18n";
|
||||
import { getTeamWithMembers, isTeamAdmin, isTeamMember, isTeamOwner } from "@calcom/lib/server/queries/teams";
|
||||
import slugify from "@calcom/lib/slugify";
|
||||
|
@ -42,9 +43,9 @@ export const viewerTeamsRouter = router({
|
|||
throw new TRPCError({ code: "NOT_FOUND", message: "Team not found." });
|
||||
}
|
||||
const membership = team?.members.find((membership) => membership.id === ctx.user.id);
|
||||
|
||||
return {
|
||||
...team,
|
||||
safeBio: markdownToSafeHTML(team.bio),
|
||||
membership: {
|
||||
role: membership?.role as MembershipRole,
|
||||
accepted: membership?.accepted,
|
||||
|
|
|
@ -351,7 +351,7 @@ export default function ToolbarPlugin(props: TextEditorProps) {
|
|||
|
||||
editor.registerUpdateListener(({ editorState, prevEditorState }) => {
|
||||
editorState.read(() => {
|
||||
const textInHtml = $generateHtmlFromNodes(editor);
|
||||
const textInHtml = $generateHtmlFromNodes(editor).replace(/</g, "<").replace(/>/g, ">");
|
||||
props.setText(textInHtml);
|
||||
});
|
||||
if (!prevEditorState._selection) editor.blur();
|
||||
|
|
81
yarn.lock
81
yarn.lock
|
@ -4883,6 +4883,7 @@ __metadata:
|
|||
"@types/react-phone-number-input": ^3.0.14
|
||||
"@types/react-virtualized-auto-sizer": ^1.0.1
|
||||
"@types/react-window": ^1.8.5
|
||||
"@types/sanitize-html": ^2.9.0
|
||||
"@types/stripe": ^8.0.417
|
||||
"@types/turndown": ^5.0.1
|
||||
"@types/uuid": 8.3.1
|
||||
|
@ -4952,6 +4953,7 @@ __metadata:
|
|||
react-window: ^1.8.7
|
||||
remark: ^14.0.2
|
||||
rrule: ^2.7.1
|
||||
sanitize-html: ^2.10.0
|
||||
schema-dts: ^1.1.0
|
||||
short-uuid: ^4.2.0
|
||||
strip-markdown: ^5.0.0
|
||||
|
@ -13028,6 +13030,15 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/sanitize-html@npm:^2.9.0":
|
||||
version: 2.9.0
|
||||
resolution: "@types/sanitize-html@npm:2.9.0"
|
||||
dependencies:
|
||||
htmlparser2: ^8.0.0
|
||||
checksum: b60f42b740bbfb1b1434ce8b43925a38ecc608b60aa654fd009d2e22e33f324b61d370768c55bd2fd98e03de08518ffa8911d61606c483526fb931bb8b59d1b0
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/scheduler@npm:*":
|
||||
version: 0.16.2
|
||||
resolution: "@types/scheduler@npm:0.16.2"
|
||||
|
@ -19131,6 +19142,17 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"dom-serializer@npm:^2.0.0":
|
||||
version: 2.0.0
|
||||
resolution: "dom-serializer@npm:2.0.0"
|
||||
dependencies:
|
||||
domelementtype: ^2.3.0
|
||||
domhandler: ^5.0.2
|
||||
entities: ^4.2.0
|
||||
checksum: cd1810544fd8cdfbd51fa2c0c1128ec3a13ba92f14e61b7650b5de421b88205fd2e3f0cc6ace82f13334114addb90ed1c2f23074a51770a8e9c1273acbc7f3e6
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"dom-walk@npm:^0.1.0":
|
||||
version: 0.1.2
|
||||
resolution: "dom-walk@npm:0.1.2"
|
||||
|
@ -19145,7 +19167,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"domelementtype@npm:^2.0.1, domelementtype@npm:^2.2.0":
|
||||
"domelementtype@npm:^2.0.1, domelementtype@npm:^2.2.0, domelementtype@npm:^2.3.0":
|
||||
version: 2.3.0
|
||||
resolution: "domelementtype@npm:2.3.0"
|
||||
checksum: ee837a318ff702622f383409d1f5b25dd1024b692ef64d3096ff702e26339f8e345820f29a68bcdcea8cfee3531776b3382651232fbeae95612d6f0a75efb4f6
|
||||
|
@ -19179,6 +19201,15 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"domhandler@npm:^5.0.1, domhandler@npm:^5.0.2, domhandler@npm:^5.0.3":
|
||||
version: 5.0.3
|
||||
resolution: "domhandler@npm:5.0.3"
|
||||
dependencies:
|
||||
domelementtype: ^2.3.0
|
||||
checksum: 0f58f4a6af63e6f3a4320aa446d28b5790a009018707bce2859dcb1d21144c7876482b5188395a188dfa974238c019e0a1e610d2fc269a12b2c192ea2b0b131c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"domino@npm:^2.1.6":
|
||||
version: 2.1.6
|
||||
resolution: "domino@npm:2.1.6"
|
||||
|
@ -19211,6 +19242,17 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"domutils@npm:^3.0.1":
|
||||
version: 3.0.1
|
||||
resolution: "domutils@npm:3.0.1"
|
||||
dependencies:
|
||||
dom-serializer: ^2.0.0
|
||||
domelementtype: ^2.3.0
|
||||
domhandler: ^5.0.1
|
||||
checksum: 23aa7a840572d395220e173cb6263b0d028596e3950100520870a125af33ff819e6f609e1606d6f7d73bd9e7feb03bb404286e57a39063b5384c62b724d987b3
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"dot-case@npm:^1.1.0":
|
||||
version: 1.1.2
|
||||
resolution: "dot-case@npm:1.1.2"
|
||||
|
@ -19602,7 +19644,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"entities@npm:^4.4.0":
|
||||
"entities@npm:^4.2.0, entities@npm:^4.4.0":
|
||||
version: 4.4.0
|
||||
resolution: "entities@npm:4.4.0"
|
||||
checksum: 84d250329f4b56b40fa93ed067b194db21e8815e4eb9b59f43a086f0ecd342814f6bc483de8a77da5d64e0f626033192b1b4f1792232a7ea6b970ebe0f3187c2
|
||||
|
@ -23760,6 +23802,18 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"htmlparser2@npm:^8.0.0":
|
||||
version: 8.0.2
|
||||
resolution: "htmlparser2@npm:8.0.2"
|
||||
dependencies:
|
||||
domelementtype: ^2.3.0
|
||||
domhandler: ^5.0.3
|
||||
domutils: ^3.0.1
|
||||
entities: ^4.4.0
|
||||
checksum: 29167a0f9282f181da8a6d0311b76820c8a59bc9e3c87009e21968264c2987d2723d6fde5a964d4b7b6cba663fca96ffb373c06d8223a85f52a6089ced942700
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"http-cache-semantics@npm:3.8.1":
|
||||
version: 3.8.1
|
||||
resolution: "http-cache-semantics@npm:3.8.1"
|
||||
|
@ -30736,6 +30790,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"parse-srcset@npm:^1.0.2":
|
||||
version: 1.0.2
|
||||
resolution: "parse-srcset@npm:1.0.2"
|
||||
checksum: 3a0380380c6082021fcce982f0b89fb8a493ce9dfd7d308e5e6d855201e80db8b90438649b31fdd82a3d6089a8ca17dccddaa2b730a718389af4c037b8539ebf
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"parse5-htmlparser2-tree-adapter@npm:^6.0.0":
|
||||
version: 6.0.1
|
||||
resolution: "parse5-htmlparser2-tree-adapter@npm:6.0.1"
|
||||
|
@ -31515,7 +31576,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"postcss@npm:^8.4.13, postcss@npm:^8.4.21":
|
||||
"postcss@npm:^8.3.11, postcss@npm:^8.4.13, postcss@npm:^8.4.21":
|
||||
version: 8.4.21
|
||||
resolution: "postcss@npm:8.4.21"
|
||||
dependencies:
|
||||
|
@ -34466,6 +34527,20 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"sanitize-html@npm:^2.10.0":
|
||||
version: 2.10.0
|
||||
resolution: "sanitize-html@npm:2.10.0"
|
||||
dependencies:
|
||||
deepmerge: ^4.2.2
|
||||
escape-string-regexp: ^4.0.0
|
||||
htmlparser2: ^8.0.0
|
||||
is-plain-object: ^5.0.0
|
||||
parse-srcset: ^1.0.2
|
||||
postcss: ^8.3.11
|
||||
checksum: 0cb2bb330ed966a4d667b1890322dd868a67f527f87c04d7e3be1688fcfda20f7452a9a7744870751f51e255742e7264a287d9bcfcd64d4cd74a3c99f99c73d2
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"saslprep@npm:^1.0.3":
|
||||
version: 1.0.3
|
||||
resolution: "saslprep@npm:1.0.3"
|
||||
|
|
Loading…
Reference in New Issue