mirror of
https://github.com/documenso/documenso.git
synced 2026-07-04 18:14:56 +10:00
0b86ece1d5
Platform-plan organisations and teams can now customise non-embed signing pages with six brand colour tokens, a border-radius, and a free-text custom CSS block (up to 256 KB). - Stored on OrganisationGlobalSettings / TeamGlobalSettings; teams inherit from the org via brandingEnabled === null. - CSS is sanitised on save (PostCSS) so we can inline it at SSR with no per-render parsing. - Rendered via a nonce'd <style> scoped under .documenso-branded, using native CSS nesting so user selectors don't need scoping. - Gated on the existing embedSigningWhiteLabel claim (or self-hosted) — reuses the embed white-label decision.
144 lines
4.5 KiB
TypeScript
144 lines
4.5 KiB
TypeScript
import type { ORGANISATION_MEMBER_ROLE_MAP } from '@documenso/lib/constants/organisations-translations';
|
|
import type { Organisation, OrganisationGlobalSettings, Prisma } from '@prisma/client';
|
|
import { DocumentVisibility, type OrganisationGroup, type OrganisationMemberRole } from '@prisma/client';
|
|
|
|
import { DEFAULT_DOCUMENT_DATE_FORMAT } from '../constants/date-formats';
|
|
import { DEFAULT_ENVELOPE_EXPIRATION_PERIOD } from '../constants/envelope-expiration';
|
|
import { DEFAULT_ENVELOPE_REMINDER_SETTINGS } from '../constants/envelope-reminder';
|
|
import {
|
|
LOWEST_ORGANISATION_ROLE,
|
|
ORGANISATION_MEMBER_ROLE_HIERARCHY,
|
|
ORGANISATION_MEMBER_ROLE_PERMISSIONS_MAP,
|
|
} from '../constants/organisations';
|
|
import { DEFAULT_DOCUMENT_EMAIL_SETTINGS } from '../types/document-email';
|
|
|
|
export const isPersonalLayout = (organisations: Pick<Organisation, 'type'>[]) => {
|
|
return organisations.length === 1 && organisations[0].type === 'PERSONAL';
|
|
};
|
|
|
|
/**
|
|
* Determines whether a team member can execute a given action.
|
|
*
|
|
* @param action The action the user is trying to execute.
|
|
* @param role The current role of the user.
|
|
* @returns Whether the user can execute the action.
|
|
*/
|
|
export const canExecuteOrganisationAction = (
|
|
action: keyof typeof ORGANISATION_MEMBER_ROLE_PERMISSIONS_MAP,
|
|
role: keyof typeof ORGANISATION_MEMBER_ROLE_MAP,
|
|
) => {
|
|
return ORGANISATION_MEMBER_ROLE_PERMISSIONS_MAP[action].some((i) => i === role);
|
|
};
|
|
|
|
/**
|
|
* Compares the provided `currentUserRole` with the provided `roleToCheck` to determine
|
|
* whether the `currentUserRole` has permission to modify the `roleToCheck`.
|
|
*
|
|
* @param currentUserRole Role of the current user
|
|
* @param roleToCheck Role of another user to see if the current user can modify
|
|
* @returns True if the current user can modify the other user, false otherwise
|
|
*/
|
|
export const isOrganisationRoleWithinUserHierarchy = (
|
|
currentUserRole: keyof typeof ORGANISATION_MEMBER_ROLE_MAP,
|
|
roleToCheck: keyof typeof ORGANISATION_MEMBER_ROLE_MAP,
|
|
) => {
|
|
return ORGANISATION_MEMBER_ROLE_HIERARCHY[currentUserRole].some((i) => i === roleToCheck);
|
|
};
|
|
|
|
export const getHighestOrganisationRoleInGroup = (
|
|
groups: Pick<OrganisationGroup, 'type' | 'organisationRole'>[],
|
|
): OrganisationMemberRole => {
|
|
let highestOrganisationRole: OrganisationMemberRole = LOWEST_ORGANISATION_ROLE;
|
|
|
|
groups.forEach((group) => {
|
|
const currentRolePriority = ORGANISATION_MEMBER_ROLE_HIERARCHY[group.organisationRole].length;
|
|
const highestOrganisationRolePriority = ORGANISATION_MEMBER_ROLE_HIERARCHY[highestOrganisationRole].length;
|
|
|
|
if (currentRolePriority > highestOrganisationRolePriority) {
|
|
highestOrganisationRole = group.organisationRole;
|
|
}
|
|
});
|
|
|
|
return highestOrganisationRole;
|
|
};
|
|
|
|
type BuildOrganisationWhereQueryOptions = {
|
|
organisationId: string | undefined;
|
|
userId: number;
|
|
roles?: OrganisationMemberRole[];
|
|
};
|
|
|
|
export const buildOrganisationWhereQuery = ({
|
|
organisationId,
|
|
userId,
|
|
roles,
|
|
}: BuildOrganisationWhereQueryOptions): Prisma.OrganisationWhereInput => {
|
|
// Note: Not using inline ternary since typesafety breaks for some reason.
|
|
if (!roles) {
|
|
return {
|
|
id: organisationId,
|
|
members: {
|
|
some: {
|
|
userId,
|
|
},
|
|
},
|
|
};
|
|
}
|
|
|
|
return {
|
|
id: organisationId,
|
|
members: {
|
|
some: {
|
|
userId,
|
|
organisationGroupMembers: {
|
|
some: {
|
|
group: {
|
|
organisationRole: {
|
|
in: roles,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
};
|
|
};
|
|
|
|
export const generateDefaultOrganisationSettings = (): Omit<OrganisationGlobalSettings, 'id' | 'organisation'> => {
|
|
return {
|
|
documentVisibility: DocumentVisibility.EVERYONE,
|
|
documentLanguage: 'en',
|
|
documentTimezone: null, // Null means local timezone.
|
|
documentDateFormat: DEFAULT_DOCUMENT_DATE_FORMAT,
|
|
delegateDocumentOwnership: false,
|
|
|
|
includeSenderDetails: true,
|
|
includeSigningCertificate: true,
|
|
includeAuditLog: false,
|
|
|
|
typedSignatureEnabled: true,
|
|
uploadSignatureEnabled: true,
|
|
drawSignatureEnabled: true,
|
|
|
|
brandingEnabled: false,
|
|
brandingLogo: '',
|
|
brandingUrl: '',
|
|
brandingCompanyDetails: '',
|
|
brandingColors: null,
|
|
brandingCss: '',
|
|
|
|
emailId: null,
|
|
emailReplyTo: null,
|
|
// emailReplyToName: null,
|
|
emailDocumentSettings: DEFAULT_DOCUMENT_EMAIL_SETTINGS,
|
|
|
|
defaultRecipients: null,
|
|
|
|
envelopeExpirationPeriod: DEFAULT_ENVELOPE_EXPIRATION_PERIOD,
|
|
|
|
reminderSettings: DEFAULT_ENVELOPE_REMINDER_SETTINGS,
|
|
|
|
aiFeaturesEnabled: false,
|
|
};
|
|
};
|