Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: allow end user to set all url paths (WPB-4676) #16774

Merged
merged 12 commits into from
Feb 13, 2024
19 changes: 14 additions & 5 deletions server/config/client.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,37 +71,46 @@
SHOW_LOADING_INFORMATION: env.FEATURE_SHOW_LOADING_INFORMATION == 'true',
USE_CORE_CRYPTO: env.FEATURE_USE_CORE_CRYPTO == 'true',
MAX_USERS_TO_PING_WITHOUT_ALERT:
(env.FEATURE_MAX_USERS_TO_PING_WITHOUT_ALERT && Number(env.FEATURE_MAX_USERS_TO_PING_WITHOUT_ALERT)) || 4,

Check warning on line 74 in server/config/client.config.ts

View workflow job for this annotation

GitHub Actions / test

No magic number: 4
},
MAX_GROUP_PARTICIPANTS: (env.MAX_GROUP_PARTICIPANTS && Number(env.MAX_GROUP_PARTICIPANTS)) || 500,

Check warning on line 76 in server/config/client.config.ts

View workflow job for this annotation

GitHub Actions / test

No magic number: 500
MAX_VIDEO_PARTICIPANTS: (env.MAX_VIDEO_PARTICIPANTS && Number(env.MAX_VIDEO_PARTICIPANTS)) || 4,

Check warning on line 77 in server/config/client.config.ts

View workflow job for this annotation

GitHub Actions / test

No magic number: 4
NEW_PASSWORD_MINIMUM_LENGTH: (env.NEW_PASSWORD_MINIMUM_LENGTH && Number(env.NEW_PASSWORD_MINIMUM_LENGTH)) || 8,

Check warning on line 78 in server/config/client.config.ts

View workflow job for this annotation

GitHub Actions / test

No magic number: 8
URL: {
ACCOUNT_BASE: env.URL_ACCOUNT_BASE,
MOBILE_BASE: env.URL_MOBILE_BASE,
PRICING: env.URL_PRICING,
PRIVACY_POLICY: env.URL_PRIVACY_POLICY,
URL_PATH: {
CREATE_TEAM: env.URL_PATH_CREATE_TEAM,
MANAGE_SERVICES: env.URL_PATH_MANAGE_SERVICES,
MANAGE_TEAM: env.URL_PATH_MANAGE_TEAM,
PASSWORD_RESET: env.URL_PATH_PASSWORD_RESET,
},
SUPPORT: {
BUG_REPORT: env.URL_SUPPORT_BUG_REPORT,
CALLING: env.URL_SUPPORT_CALLING,
CAMERA_ACCESS_DENIED: env.URL_SUPPORT_CAMERA_ACCESS_DENIED,
CONTACT: env.URL_SUPPORT_CONTACT,
DECRYPT_ERROR: env.URL_SUPPORT_DECRYPT_ERROR,
DEVICE_ACCESS_DENIED: env.URL_SUPPORT_DEVICE_ACCESS_DENIED,
DEVICE_NOT_FOUND: env.URL_SUPPORT_DEVICE_NOT_FOUND,
E2EI_VERIFICATION: env.URL_SUPPORT_E2EI_VERIFICATION,
EMAIL_EXISTS: env.URL_SUPPORT_EMAIL_EXISTS,
FEDERATION_STOP: env.URL_SUPPORT_FEDERATION_STOP,
HISTORY: env.URL_SUPPORT_HISTORY,
INDEX: env.URL_SUPPORT_INDEX,
LEARN_MORE_ABOUT_GUEST_LINKS: env.URL_LEARN_MORE_ABOUT_GUEST_LINKS,
LEGAL_HOLD_BLOCK: env.URL_SUPPORT_LEGAL_HOLD_BLOCK,
MICROPHONE_ACCESS_DENIED: env.URL_SUPPORT_MICROPHONE_ACCESS_DENIED,
MLS_LEARN_MORE: env.URL_SUPPORT_MLS_LEARN_MORE,
PRIVACY_VERIFY_FINGERPRINT: env.URL_SUPPORT_PRIVACY_VERIFY_FINGERPRINT,
SCREEN_ACCESS_DENIED: env.URL_SUPPORT_SCREEN_ACCESS_DENIED,
LEARN_MORE_ABOUT_GUEST_LINKS: env.URL_LEARN_MORE_ABOUT_GUEST_LINKS,
NON_FEDERATING_INFO: env.URL_SUPPORT_NON_FEDERATING_INFO,
OAUTH_LEARN_MORE: env.URL_SUPPORT_OAUTH_LEARN_MORE,
OFFLINE_BACKEND: env.URL_SUPPORT_OFFLINE_BACKEND,
FEDERATION_STOP: env.URL_SUPPORT_FEDERATION_STOP,
E2EI_VERIFICATION: env.URL_SUPPORT_E2EI_VERIFICATION,
PRIVACY_UNVERIFIED_USERS: env.URL_SUPPORT_PRIVACY_UNVERIFIED_USERS,
PRIVACY_VERIFY_FINGERPRINT: env.URL_SUPPORT_PRIVACY_VERIFY_FINGERPRINT,
PRIVACY_WHY: env.URL_SUPPORT_PRIVACY_WHY,
SCREEN_ACCESS_DENIED: env.URL_SUPPORT_SCREEN_ACCESS_DENIED,
},
TEAMS_BASE: env.URL_TEAMS_BASE,
TEAMS_CREATE: env.URL_TEAMS_CREATE,
Expand Down
17 changes: 17 additions & 0 deletions server/config/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,16 @@ export type Env = {
/** Sets the host URL for the website, e.g. https://wire.com */
URL_WEBSITE_BASE: string;

/** Sets paths to append to a base URL */
URL_PATH_CREATE_TEAM: string;

URL_PATH_MANAGE_SERVICES: string;

URL_PATH_MANAGE_TEAM: string;

URL_PATH_PASSWORD_RESET: string;

/** Sets Support URLs to specific pages */
URL_SUPPORT_INDEX: string;

URL_SUPPORT_BUG_REPORT: string;
Expand Down Expand Up @@ -257,8 +267,15 @@ export type Env = {
URL_SUPPORT_OFFLINE_BACKEND: string;

URL_SUPPORT_FEDERATION_STOP: string;

URL_SUPPORT_E2EI_VERIFICATION: string;

URL_SUPPORT_DECRYPT_ERROR: string;

URL_SUPPORT_PRIVACY_UNVERIFIED_USERS: string;

URL_SUPPORT_PRIVACY_WHY: string;

URL_WHATS_NEW: string;

/** Content Security Policy
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import React, {useState} from 'react';

import {Icon} from 'Components/Icon';
import {getDecryptErrorUrl} from 'src/script/externalRoute';
import {Config} from 'src/script/Config';
import {MotionDuration} from 'src/script/motion/MotionDuration';
import {t} from 'Util/LocalizerUtil';
import {splitFingerprint} from 'Util/StringUtil';
Expand All @@ -36,7 +36,7 @@ export interface DecryptErrorMessageProps {
const DecryptErrorMessage: React.FC<DecryptErrorMessageProps> = ({message, onClickResetSession}) => {
const [isResettingSession, setIsResettingSession] = useState(false);

const link = getDecryptErrorUrl();
const link = Config.getConfig().URL.SUPPORT.DECRYPT_ERROR;
const caption = message.isIdentityChanged
? t('conversationUnableToDecrypt2', message.user().name(), {
'/highlight': '</span>',
Expand Down
3 changes: 1 addition & 2 deletions src/script/components/Modals/UserModal/UserModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ import {ModalComponent} from 'Components/ModalComponent';
import {EnrichedFields} from 'Components/panel/EnrichedFields';
import {UserActions} from 'Components/panel/UserActions';
import {UserDetails} from 'Components/panel/UserDetails';
import {getPrivacyUnverifiedUsersUrl} from 'src/script/externalRoute';
import {useKoSubscribableChildren} from 'Util/ComponentUtil';
import {handleKeyDown} from 'Util/KeyboardUtil';
import {replaceLink, t} from 'Util/LocalizerUtil';
Expand Down Expand Up @@ -117,7 +116,7 @@ export const UnverifiedUserWarning: React.FC<UnverifiedUserWarningProps> = ({use
css={{fontSize: 'var(--font-size-medium)', margin: '0 0.2em'}}
variant={LinkVariant.PRIMARY}
targetBlank
href={getPrivacyUnverifiedUsersUrl()}
href={Config.getConfig().URL.SUPPORT.PRIVACY_UNVERIFIED_USERS}
>
{t('modalUserLearnMore')}
</Link>
Expand Down
3 changes: 1 addition & 2 deletions src/script/components/userDevices/DeviceList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ import {DeviceCard} from './DeviceCard';
import type {ClientEntity} from '../../client/ClientEntity';
import {Config} from '../../Config';
import type {User} from '../../entity/User';
import {getPrivacyWhyUrl} from '../../externalRoute';

interface DeviceListProps {
clickOnDevice: (client: ClientEntity) => void;
Expand All @@ -52,7 +51,7 @@ const DeviceList: React.FC<DeviceListProps> = ({user, getDeviceIdentity, noPaddi

<a
className="participant-devices__link accent-text"
href={getPrivacyWhyUrl()}
href={Config.getConfig().URL.SUPPORT.PRIVACY_WHY}
rel="nofollow noopener noreferrer"
target="_blank"
>
Expand Down
4 changes: 2 additions & 2 deletions src/script/components/userDevices/NoDevicesFound.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import {t} from 'Util/LocalizerUtil';

import {Config} from '../../Config';
import type {User} from '../../entity/User';
import {getPrivacyPolicyUrl} from '../../externalRoute';
import {externalUrl} from '../../externalRoute';

interface NoDevicesFoundProps {
noPadding: boolean;
Expand All @@ -48,7 +48,7 @@ const NoDevicesFound: React.FC<NoDevicesFoundProps> = ({user, noPadding}) => {
</p>
<a
className="participant-devices__link accent-text"
href={getPrivacyPolicyUrl()}
href={externalUrl.privacyPolicy}
rel="nofollow noopener noreferrer"
target="_blank"
>
Expand Down
107 changes: 40 additions & 67 deletions src/script/externalRoute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,79 +20,55 @@
import {currentLanguage} from './auth/localeConfig';
import {Config} from './Config';

const env = window.wire.env;

export const URL = {
ACCOUNT: env.URL?.ACCOUNT_BASE,
PRIVACY_POLICY: env.URL?.PRIVACY_POLICY,
SUPPORT: env.URL?.SUPPORT.INDEX,
TEAM_SETTINGS: env.URL?.TEAMS_BASE,
TERMS_OF_USE_PERSONAL: env.URL?.TERMS_OF_USE_PERSONAL,
TERMS_OF_USE_TEAMS: env.URL?.TERMS_OF_USE_TEAMS,
WEBAPP: {
INTERNAL: 'https://wire-webapp-staging.wire.com',
PRODUCTION: env.APP_BASE || 'https://app.wire.com',
STAGING: 'https://wire-webapp-staging.zinfra.io',
},
WEBSITE: env.URL?.WEBSITE_BASE,
};

export const URL_PATH = {
CREATE_TEAM: '/create-team/',
DECRYPT_ERROR_1: '/articles/207948115',
DECRYPT_ERROR_2: '/privacy/error-2/',
MANAGE_SERVICES: '/services/',
MANAGE_TEAM: '/login/',
PASSWORD_RESET: '/forgot/',
PRIVACY_HOW: '/privacy/how/',
PRIVACY_UNVERIFIED_USERS: '/articles/202857164',
PRIVACY_WHY: '/articles/207859815',
SUPPORT_USERNAME: '/support/username/',
} as const;
Comment on lines -40 to -51
Copy link
Contributor Author

@V-Gira V-Gira Feb 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • some of those are removed as deprecated
  • support links still in use have been moved with existing ones as environment variables instead of being hardcoded
  • url paths intended to be dynamically appended have been made into the URL_SUBPATH env variables as to be end user configurable

const URL = Config.getConfig().URL;

const getTeamSettingsUrl = (path: string = '', utmSource?: string): string | undefined => {
const query = utmSource ? `?utm_source=${utmSource}&utm_term=desktop` : '';
const teamSettingsUrl = `${URL.TEAM_SETTINGS}${path}${query}`;
return URL.TEAM_SETTINGS ? teamSettingsUrl : undefined;
const teamSettingsUrl = `${URL.TEAMS_BASE}${path}${query}`;
return URL.TEAMS_BASE ? teamSettingsUrl : undefined;
};

export const getWebsiteUrl = (path: string = '', pkCampaign?: string): string | undefined => {
if (URL.WEBSITE) {
const getWebsiteUrl = (path: string = '', pkCampaign?: string): string | undefined => {
if (URL.WEBSITE_BASE) {
const query = pkCampaign ? `?pk_campaign=${pkCampaign}&pk_kwd=desktop` : '';
const websiteUrl = `${URL.WEBSITE}${path}${query}`;
return addLocaleToUrl(URL.WEBSITE ? websiteUrl : undefined);
const websiteUrl = `${URL.WEBSITE_BASE}${path}${query}`;
return addLocaleToUrl(websiteUrl);
}
return undefined;
};

const getHelpCenterUrl = (path: (typeof URL_PATH)[keyof typeof URL_PATH]) => {
if (URL.SUPPORT) {
const helpcenterUrl = `${URL.SUPPORT}${path}`;
return addLocaleToHelpCenterUrl(URL.SUPPORT ? helpcenterUrl : undefined);
}
return undefined;
const getAccountPagesUrl = (path: string = ''): string | undefined => {
return URL.ACCOUNT_BASE ? `${URL.ACCOUNT_BASE}${path}` : undefined;
};

export const getAccountPagesUrl = (path: string = ''): string | undefined => {
const accountPagesUrl = `${URL.ACCOUNT}${path}`;
return URL.ACCOUNT ? accountPagesUrl : undefined;
};
const getPrivacyPolicyUrl = (): string | undefined => addLocaleToUrl(URL.PRIVACY_POLICY || undefined);
const getTermsOfUsePersonalUrl = (): string | undefined => addLocaleToUrl(URL.TERMS_OF_USE_PERSONAL || undefined);
const getTermsOfUseTeamUrl = (): string | undefined => addLocaleToUrl(URL.TERMS_OF_USE_TEAMS || undefined);

export const getPrivacyPolicyUrl = (): string => addLocaleToUrl(URL.PRIVACY_POLICY || undefined);
export const getTermsOfUsePersonalUrl = (): string => addLocaleToUrl(URL.TERMS_OF_USE_PERSONAL || undefined);
export const getTermsOfUseTeamUrl = (): string => addLocaleToUrl(URL.TERMS_OF_USE_TEAMS || undefined);
/**
* Retrieves the URL for managing services with optional UTM parameters.
* UTM parameters are used in online marketing to track the effectiveness of campaigns.
*
* @param utmSource - Optional. The source of the UTM parameters.
* @returns The URL for managing services with optional UTM parameters.
*/
export const getManageServicesUrl = (utmSource?: string): string | undefined =>
getTeamSettingsUrl(URL.URL_PATH.MANAGE_SERVICES, utmSource);
Comment on lines +55 to +56
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hard to find doc on those UTM sources
we use 2 variations of those in the webapp:

  • client_landing when navigating from the main portion of the app
  • client_settings when navigating from the settings

Do we want to keep that granularity @atomrc ?
Should I had those to the externalUrl object and stop exporting those methods?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah!! I do not know about those utms, honestly. But, since we will be tracking team settings usages soonish, I'm guessing those will be used at some point (if they are not currently).
We should probably keep a way to add utm to some urls I guess

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll keep the export of the method and add some JSDoc to make the utmSource thing more transparent, how does that sound?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds good to me :)


export const getManageServicesUrl = (utmSource?: string): string =>
getTeamSettingsUrl(URL_PATH.MANAGE_SERVICES, utmSource);
export const getManageTeamUrl = (utmSource?: string): string => getTeamSettingsUrl(URL_PATH.MANAGE_TEAM, utmSource);
/**
* Retrieves the URL for managing team settings with optional UTM parameters.
* UTM parameters are used in online marketing to track the effectiveness of campaigns.
*
* @param utmSource - Optional. The source of the UTM parameters.
* @returns The URL for managing team settings with optional UTM parameters.
*/
export const getManageTeamUrl = (utmSource?: string): string | undefined =>
getTeamSettingsUrl(URL.URL_PATH.MANAGE_TEAM, utmSource);

export const getCreateTeamUrl = (): string =>
Config.getConfig().FEATURE.ENABLE_ACCOUNT_REGISTRATION && `${Config.getConfig().URL.TEAMS_BASE}/register/email`;
export const getDecryptErrorUrl = (): string => getHelpCenterUrl(URL_PATH.DECRYPT_ERROR_1);
export const getPrivacyUnverifiedUsersUrl = (): string => getHelpCenterUrl(URL_PATH.PRIVACY_UNVERIFIED_USERS);
export const getPrivacyWhyUrl = (): string => getHelpCenterUrl(URL_PATH.PRIVACY_WHY);
const getCreateTeamUrl = (): string | undefined =>
Config.getConfig().FEATURE.ENABLE_ACCOUNT_REGISTRATION ? `${URL.TEAMS_BASE}${URL.URL_PATH.CREATE_TEAM}` : undefined;

export const addLocaleToUrl = (url?: string): string => {
export const addLocaleToUrl = (url?: string): string | undefined => {
if (!url) {
return undefined;
}
Expand All @@ -101,14 +77,11 @@ export const addLocaleToUrl = (url?: string): string => {
return url.replace(Config.getConfig().URL.WEBSITE_BASE, `${Config.getConfig().URL.WEBSITE_BASE}/${websiteLanguage}`);
};

const addLocaleToHelpCenterUrl = (url?: string): string => {
if (!url) {
return undefined;
}
const language = currentLanguage().slice(0, 2);
const websiteLanguage = language == 'de' ? language : 'en-us';
return url.replace(
`${Config.getConfig().URL.SUPPORT.INDEX}`,
`${Config.getConfig().URL.SUPPORT.INDEX}/hc/${websiteLanguage}`,
);
export const externalUrl = {
createTeam: getCreateTeamUrl(),
passwordReset: getAccountPagesUrl(URL.URL_PATH?.PASSWORD_RESET),
privacyPolicy: getPrivacyPolicyUrl(),
termsOfUsePersonnal: getTermsOfUsePersonalUrl(),
termsOfUseTeam: getTermsOfUseTeamUrl(),
website: getWebsiteUrl(),
};
4 changes: 2 additions & 2 deletions src/script/main/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ import {ServiceMiddleware} from '../event/preprocessor/ServiceMiddleware';
import {FederationEventProcessor} from '../event/processor/FederationEventProcessor';
import {GiphyRepository} from '../extension/GiphyRepository';
import {GiphyService} from '../extension/GiphyService';
import {getWebsiteUrl} from '../externalRoute';
import {externalUrl} from '../externalRoute';
import {IntegrationRepository} from '../integration/IntegrationRepository';
import {IntegrationService} from '../integration/IntegrationService';
import {startNewVersionPolling} from '../lifecycle/newVersionHandler';
Expand Down Expand Up @@ -829,7 +829,7 @@ export class App {
const isLeavingGuestRoom = isTemporaryGuestReason && this.repository.user['userState'].self()?.isTemporaryGuest();

if (isLeavingGuestRoom) {
const websiteUrl = getWebsiteUrl();
const websiteUrl = externalUrl.website;

if (websiteUrl) {
return window.location.replace(websiteUrl);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import {PreferencesSection} from './components/PreferencesSection';

import {Config} from '../../../../Config';
import {User} from '../../../../entity/User';
import {getPrivacyPolicyUrl, getTermsOfUsePersonalUrl, getTermsOfUseTeamUrl, URL} from '../../../../externalRoute';
import {externalUrl} from '../../../../externalRoute';

interface AboutPreferencesProps {
selfUser: User;
Expand All @@ -41,12 +41,12 @@ interface AboutPreferencesProps {
const AboutPreferences: React.FC<AboutPreferencesProps> = ({selfUser, teamState = container.resolve(TeamState)}) => {
const inTeam = teamState.isInTeam(selfUser);
const config = Config.getConfig();
const websiteUrl = URL.WEBSITE;
const privacyPolicyUrl = getPrivacyPolicyUrl();
const websiteUrl = externalUrl.website;
const privacyPolicyUrl = externalUrl.privacyPolicy;

const termsOfUseUrl = useMemo(() => {
if (selfUser) {
return inTeam ? getTermsOfUseTeamUrl() : getTermsOfUsePersonalUrl();
return inTeam ? externalUrl.termsOfUseTeam : externalUrl.termsOfUsePersonnal;
}
return '';
}, [selfUser, inTeam]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,9 @@ import {WebAppEvents} from '@wireapp/webapp-events';
import {PrimaryModal} from 'Components/Modals/PrimaryModal';
import {useKoSubscribableChildren} from 'Util/ComponentUtil';
import {t} from 'Util/LocalizerUtil';
import {safeWindowOpen} from 'Util/SanitizationUtil';

import {User} from '../../../../../entity/User';
import {getAccountPagesUrl, getCreateTeamUrl, getManageTeamUrl, URL_PATH} from '../../../../../externalRoute';
import {externalUrl, getManageTeamUrl} from '../../../../../externalRoute';
import {TeamState} from '../../../../../team/TeamState';
import {AppLockState} from '../../../../../user/AppLockState';
import {FEATURES, hasAccessToFeature} from '../../../../../user/UserPermission';
Expand All @@ -53,7 +52,7 @@ const AccountSecuritySection: React.FC<AccountSecuritySectionProps> = ({
appLockState = container.resolve(AppLockState),
teamState = container.resolve(TeamState),
}) => {
const createTeamUrl = getCreateTeamUrl();
const createTeamUrl = externalUrl.createTeam;
const manageTeamUrl = getManageTeamUrl('client_settings');
const {teamRole} = useKoSubscribableChildren(selfUser, ['teamRole']);
const {isAppLockActivated} = useKoSubscribableChildren(appLockState, ['isAppLockActivated']);
Expand Down Expand Up @@ -82,7 +81,8 @@ const AccountSecuritySection: React.FC<AccountSecuritySectionProps> = ({
<Link
tabIndex={TabIndex.FOCUSABLE}
variant={LinkVariant.PRIMARY}
onClick={() => safeWindowOpen(manageTeamUrl)}
href={manageTeamUrl}
targetBlank
data-uie-name="do-manage-team"
type="button"
>
Expand Down Expand Up @@ -110,8 +110,8 @@ const AccountSecuritySection: React.FC<AccountSecuritySectionProps> = ({
<Link
tabIndex={TabIndex.FOCUSABLE}
variant={LinkVariant.PRIMARY}
href="#"
onClick={() => safeWindowOpen(getAccountPagesUrl(URL_PATH.PASSWORD_RESET))}
href={externalUrl.passwordReset}
targetBlank
title={t('tooltipPreferencesPassword')}
data-uie-name="do-reset-password"
type="button"
Expand Down
Loading