Skip to content

Commit

Permalink
feat: let user manage allowing and blocking addresses
Browse files Browse the repository at this point in the history
* refactor: renamed domainWhitelistSubsection into trustedAddressesSubSection

* feat: added Allowed senders list item to mail subsections

* feat: added senders-list component

* test: add tests for settings subsections

* feat: add blocked senders subsection

* feat: added allowed and blocked address lists to settings-view

* chore: moved tooltip under the title in senders-list

* chore: extracted getMessage function

* chore: extracted type ListType

* test: added some tests for senders-list

* feat: added widget for adding email to senders-list

* test: added add widget test for blocked list

* feat: added behaviour for add button in senders-list

* feat: added settings update on add sender in senders-list

* feat: added settings read in senders-list

* test: added test for updateSettings with list not empty for senders-list

* test: clean input after add in senders-list

* feat: added email validation on add in senders-list

* refactor: inverted logic of isInvalid to isAddEnabled in senders-list

* test: add tests for getSettingsSubSections

* chore: review of input error messages in senders-list

* feat: added list of addresses in senders-list

* feat: implemented address removal in senders-list

* fix: test on item removal in senders-list

* chore: removed extra dividers

* refactor(trustee-list-item): rename file and component, add data-testid, fix typization, change export

refactor(trustee-addresses): change DS list component

* feat: use SendersListItem component and tests

* fix: remove useless component

* chore: remove unused store

* chore: inline method and code reformat

* chore: remove duplicated test

* test: parametrize test

* chore: remove useless async keyword

* feat: switched reading amavis attributes from get-info attrs

* chore: renamed settingsObj into currentPrefs

* feat: extended array support in differenceObject to amavis related attributes

* feat: added support for attributes saving / reading in settings-view

* fix: senders list can arrive as a single string from the backend

* feat: introduced saveSettings to avoid using the shell-ui editSettings functionality

* chore: aligned code to work with shell-ui 7.0.x

* chore: completed save settings with account store update

* fix: disable save button after successful save

* chore(idea): load prettier configuration

* refactor: extract updateAccountStore function

* refactor: use more specific name for amavis attributes request function

* feat: update settings.attrs property after save

* refactor: make updateAccountStore void

* refactor: properly handle promise status after save with then and catch

* wip: updateSettings from shell

* wip: updateAccount from shell

* refactor(settings): update Shell settings/account store

* refactor: update import method for updateSettings and updateAccount

- Changed the import method for better readability and maintainability
- Ensured compatibility with the existing codebase

* refactor: remove unused code when generating API request to modify the identities

* refactor(settings): update saveSettings type

* refactor(settings): remove no more useful MailMods type

* refactor(type): change Shell types import

* refactor: copied some types from shell that are not exported anymore and updated UpdateAccount type

* feat: added labels for empty table and for address conflict to senders-list

* chore: improved style of conflict label in senders-list

* build(dep): update Shell dependency

* refactor: align tests with the new SoapFault type

* refactor: replace board url with context

* chore: fixed alignment of text and buttons in senders-list

* feat: added label with items vs max in senders-list

* chore: fixed alignment of add button in trusted addresses

* feat: added check to avoid adding more items than max

* refactor: review logic to enable / disable inputs and buttons in senders-list

* fix: uncommented code

* fix: failing tests due to shell changes

* fix: removed duplicated declaration

---------

Co-authored-by: Luca Stauble <luca.stauble@zextras.com>
Co-authored-by: Matteo Baglini <matteo.baglini@gmail.com>
Co-authored-by: gabriele <gabriele.marino@zextras.com>
Co-authored-by: Luca Stauble <luca.stauble@gmail.com>
  • Loading branch information
5 people authored Jul 10, 2024
1 parent 3b1a09e commit b777212
Show file tree
Hide file tree
Showing 17 changed files with 849 additions and 89 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
/junit.xml
/coverage
.DS_Store
.tool-versions
.vscode/
coverage
zextras-carbonio-*.tgz
1 change: 1 addition & 0 deletions .idea/prettier.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 8 additions & 2 deletions src/app-utils/test/add-shell-components.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,10 @@ describe('addShellComponents', () => {
{ id: 'recover_messages', label: 'label.recover_messages' },
{ id: 'signatures', label: 'signatures.signature_heading' },
{ id: 'using_signatures', label: 'label.using_signatures' },
{ id: 'filters', label: 'filters.filters' }
{ id: 'filters', label: 'filters.filters' },
{ id: 'trusted_addresses', label: 'label.trusted_addresses' },
{ id: 'allowed_addresses', label: 'label.allowed_addresses' },
{ id: 'blocked_addresses', label: 'label.blocked_addresses' }
]
})
);
Expand All @@ -80,7 +83,10 @@ describe('addShellComponents', () => {
{ id: 'receiving_messages', label: 'label.receive_message' },
{ id: 'signatures', label: 'signatures.signature_heading' },
{ id: 'using_signatures', label: 'label.using_signatures' },
{ id: 'filters', label: 'filters.filters' }
{ id: 'filters', label: 'filters.filters' },
{ id: 'trusted_addresses', label: 'label.trusted_addresses' },
{ id: 'allowed_addresses', label: 'label.allowed_addresses' },
{ id: 'blocked_addresses', label: 'label.blocked_addresses' }
]
})
);
Expand Down
2 changes: 1 addition & 1 deletion src/carbonio-ui-commons
5 changes: 3 additions & 2 deletions src/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ import { FOLDERS } from '@zextras/carbonio-shell-ui';
import { TFunction } from 'i18next';

export const MAILS_ROUTE = 'mails';

export const MAILS_BOARD_VIEW_ID = 'mails_editor_board_view';

export const BACKUP_SEARCH_ROUTE = 'backup-search';

export const MAIL_APP_ID = 'carbonio-mails-ui';

export const MAILS_BOARD_VIEW_ID = 'mails_editor_board_view';

export const NO_ACCOUNT_NAME = 'No account';

export const RECOVER_MESSAGES_INTERVAL = 3;
Expand Down
4 changes: 2 additions & 2 deletions src/store/actions/share-folder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { createAsyncThunk } from '@reduxjs/toolkit';
import { Account, BatchRequest, soapFetch } from '@zextras/carbonio-shell-ui';
import { BatchResponse } from '@zextras/carbonio-shell-ui/lib/types/network';
import { soapFetch } from '@zextras/carbonio-shell-ui';
import type { Account, BatchRequest, BatchResponse } from '@zextras/carbonio-shell-ui';
import { trim } from 'lodash';

import { Folder } from '../../carbonio-ui-commons/types/folder';
Expand Down
2 changes: 1 addition & 1 deletion src/types/editor/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import type { Folder } from '../../carbonio-ui-commons/types/folder';
import { EDIT_VIEW_CLOSING_REASONS } from '../../constants';
import { EDIT_VIEW_CLOSING_REASONS, EditViewActions } from '../../constants';
import { type AppDispatch } from '../../store/redux';
import { SavedAttachment, UnsavedAttachment } from '../attachments';
import type { MailMessage } from '../messages';
Expand Down
8 changes: 8 additions & 0 deletions src/views/app/detail-panel/edit/tests/edit-view.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ import { setupTest } from '../../../../../carbonio-ui-commons/test/test-setup';
import { EditViewActions, MAILS_ROUTE } from '../../../../../constants';
import * as useQueryParam from '../../../../../hooks/use-query-param';
import * as saveDraftAction from '../../../../../store/actions/save-draft';
import {
GetSignaturesRequest,
GetSignaturesResponse
} from '../../../../../store/actions/signatures';
import { addEditor } from '../../../../../store/zustand/editor';
import {
generateEditAsNewEditor,
Expand Down Expand Up @@ -374,6 +378,10 @@ describe('Edit view', () => {

await firstSaveDraftInterceptor;
const draftSavingInterceptor = aSuccessfullSaveDraft();
createSoapAPIInterceptor<GetSignaturesRequest, GetSignaturesResponse>('GetSignatures', {
signature: [],
_jsns: 'urn:zimbraAccount'
});

const subject = faker.lorem.sentence(5);
// Get the default identity address
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,27 +21,32 @@ const ListItem = styled(Row)`
}
`;

// TODO remove the any after the DS
const TrusteeListItem: FC<any> = ({ item, onRemove }): ReactElement => {
export type SendersListItemProps = {
value: string;
onRemove: (sender: string) => void;
};

export const SendersListItem: FC<SendersListItemProps> = ({ value, onRemove }): ReactElement => {
const [hovered, setHovered] = useState(false);

const onMouseEnter = useCallback(() => setHovered(true), []);
const onMouseLeave = useCallback(() => setHovered(false), []);

const onClick = useCallback(() => {
onRemove(item.value);
}, [item, onRemove]);
onRemove(value);
}, [onRemove, value]);

return (
<ListItem
data-testid="senders-list-item"
height="fit"
orientation="horizontal"
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
>
<Row height="2.5rem" padding={{ all: 'small' }}>
<Container width="80%" crossAlignment="flex-start">
<Text size="small">{item.value}</Text>
<Text size="small">{value}</Text>
</Container>

<Container width="20%" orientation="horizontal" mainAlignment="flex-end">
Expand All @@ -59,5 +64,3 @@ const TrusteeListItem: FC<any> = ({ item, onRemove }): ReactElement => {
</ListItem>
);
};

export default TrusteeListItem;
8 changes: 7 additions & 1 deletion src/views/settings/components/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,18 @@ import { filter, find, isEqual, isObject, map, reduce, transform } from 'lodash'

import { NO_SIGNATURE_ID } from '../../../helpers/signatures';

const arraysProps = [
'zimbraPrefMailTrustedSenderList',
'amavisWhitelistSender',
'amavisBlacklistSender'
];

export const differenceObject = (object, base) => {
// eslint-disable-next-line no-shadow
function changes(object, base) {
return transform(object, (result, value, key) => {
if (!isEqual(value, base[key])) {
if (key === 'zimbraPrefMailTrustedSenderList') {
if (arraysProps.includes(key)) {
// eslint-disable-next-line no-param-reassign
result[key] = value;
} else {
Expand Down
109 changes: 109 additions & 0 deletions src/views/settings/save-settings.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
* SPDX-FileCopyrightText: 2024 Zextras <https://www.zextras.com>
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { Identity, updateAccount, updateSettings, xmlSoapFetch } from '@zextras/carbonio-shell-ui';
import { isArray, map } from 'lodash';

import { MAIL_APP_ID } from '../../constants';

type AccountSettings = {
[key: string]: string | number | Array<string | number> | undefined;
};

type AccountSettingsPrefs = AccountSettings;
type AccountSettingsAttrs = AccountSettings;
type IdentityAttrs = AccountSettings;

type PropsMods = Record<string, { app: string; value: unknown }>;
type PrefsMods = Record<string, unknown> & AccountSettingsPrefs;
type AttrsMods = Record<string, unknown> & AccountSettingsAttrs;

type IdentityMods = {
modifyList?: Record<string, { id: string; prefs: Partial<IdentityAttrs> }>;
deleteList?: string[];
createList?: { prefs: Partial<IdentityAttrs> }[];
};

interface Mods extends Record<string, Record<string, unknown> | undefined> {
props?: PropsMods;
prefs?: PrefsMods;
attrs?: AttrsMods;
identity?: IdentityMods;
}

export type SaveSettingsResponse = {
CreateIdentityResponse?: {
identity: [Identity];
}[];
};

function getRequestForProps(props: PropsMods | undefined, appId: string): string {
return props
? `<ModifyPropertiesRequest xmlns="urn:zimbraAccount">${map(
props,
(prop, key) => `<prop name="${key}" zimlet="${prop.app ?? appId}">${prop.value}</prop>`
)}</ModifyPropertiesRequest>`
: '';
}

function getRequestForAmavisSendersListAttrs(attrs: AttrsMods | undefined): string {
return attrs?.amavisWhitelistSender || attrs?.amavisBlacklistSender
? `<ModifyWhiteBlackListRequest xmlns="urn:zimbraAccount">${
attrs?.amavisWhitelistSender && isArray(attrs?.amavisWhitelistSender)
? `<whiteList>${attrs?.amavisWhitelistSender
.map((email) => `<addr>${email}</addr>`)
.join('')}</whiteList>`
: ''
}${
attrs?.amavisBlacklistSender && isArray(attrs?.amavisBlacklistSender)
? `<blackList>${attrs?.amavisBlacklistSender
.map((email) => `<addr>${email}</addr>`)
.join('')}</blackList>`
: ''
}</ModifyWhiteBlackListRequest>`
: '';
}

function getRequestForIdentities(identity: IdentityMods | undefined): string {
return `${
identity?.modifyList
? map(
identity.modifyList,
(item) =>
`<ModifyIdentityRequest xmlns="urn:zimbraAccount" requestId="0"><identity id="${
item.id
}">${map(item.prefs, (value, key) => `<a name="${key}">${value}</a>`).join(
''
)}</identity></ModifyIdentityRequest>`
).join('')
: ''
}`;
}

export const saveSettings = (
mods: Mods,
appId = MAIL_APP_ID
): Promise<{
CreateIdentityResponse?: {
identity: [Identity];
}[];
}> =>
xmlSoapFetch<string, SaveSettingsResponse>(
'Batch',
`<BatchRequest xmlns="urn:zimbra" onerror="stop">
${getRequestForProps(mods.props, appId)}
${getRequestForAmavisSendersListAttrs(mods.attrs)}
${getRequestForIdentities(mods.identity)}
</BatchRequest>`
).then((resp) => {
updateSettings(mods);
if (mods.identity) {
updateAccount(
mods.identity,
resp.CreateIdentityResponse?.map((item) => item?.identity[0]) ?? []
);
}
return resp;
});
Loading

0 comments on commit b777212

Please sign in to comment.