Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cipp",
"version": "10.2.0",
"version": "10.2.1",
"author": "CIPP Contributors",
"homepage": "https://cipp.app/",
"bugs": {
Expand Down
2 changes: 1 addition & 1 deletion public/version.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"version": "10.2.0"
"version": "10.2.1"
}
3 changes: 1 addition & 2 deletions src/components/CippComponents/CippAddTenantGroupDrawer.jsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import React, { useState, useEffect } from "react";
import { useState, useEffect } from "react";
import { Button, Box } from "@mui/material";
import { useForm, useFormState } from "react-hook-form";
import { GroupAdd } from "@mui/icons-material";
import { CippOffCanvas } from "./CippOffCanvas";
import { CippApiResults } from "./CippApiResults";
import { ApiPostCall } from "../../api/ApiCall";
import CippAddEditTenantGroups from "./CippAddEditTenantGroups";
import { getCippValidator } from "../../utils/get-cipp-validator";

export const CippAddTenantGroupDrawer = ({
buttonText = "Add Tenant Group",
Expand Down
21 changes: 17 additions & 4 deletions src/components/CippFormPages/CippAddEditUser.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,21 @@ const CippAddEditUser = (props) => {
return username.toLowerCase();
};

const validateOtherMails = (value) => {
if (!value || (Array.isArray(value) && value.length === 0)) {
return true;
}

const emailList = (Array.isArray(value) ? value.join(",") : value)
.split(",")
.map((email) => email.trim())
.filter(Boolean);

const invalidEmail = emailList.find((email) => getCippValidator(email, "email") !== true);

return !invalidEmail || `This is not a valid email: ${invalidEmail}`;
};

useEffect(() => {
//if watch.firstname changes, and watch.lastname changes, set displayname to firstname + lastname
if (watcher.givenName && watcher.surname && formType === "add") {
Expand Down Expand Up @@ -331,7 +346,6 @@ const CippAddEditUser = (props) => {
setDisplayNameManuallySet(true);
}}
required={true}
validators={{ required: "Display Name is required" }}
/>
</Grid>
<Grid size={{ md: 6, xs: 12 }}>
Expand All @@ -356,7 +370,6 @@ const CippAddEditUser = (props) => {
setUsernameManuallySet(true);
}}
required={true}
validators={{ required: "Username is required" }}
/>
</Grid>
<Grid size={{ md: 6, xs: 12 }}>
Expand Down Expand Up @@ -603,10 +616,10 @@ const CippAddEditUser = (props) => {
<CippFormComponent
type="textField"
fullWidth
label="Alternate Email Address"
label="Alternate Email Addresses (comma separated)"
name="otherMails"
formControl={formControl}
validators={{ validate: (value) => !value || getCippValidator(value, "email") }}
validators={{ validate: validateOtherMails }}
/>
</Grid>
{userSettingsDefaults?.userAttributes
Expand Down
5 changes: 5 additions & 0 deletions src/layouts/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -741,6 +741,11 @@ export const nativeMenuItems = [
path: "/email/reports/calendar-permissions",
permissions: ["Exchange.Mailbox.*"],
},
{
title: "Mailbox Forwarding",
path: "/email/reports/mailbox-forwarding",
permissions: ["Exchange.Mailbox.*"],
},
{
title: "Anti-Phishing Filters",
path: "/email/reports/antiphishing-filters",
Expand Down
150 changes: 106 additions & 44 deletions src/layouts/top-nav.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ export const TopNav = (props) => {
const [flashLock, setFlashLock] = useState(false);
const itemRefs = useRef({});
const touchDragRef = useRef({ startIdx: null, overIdx: null });
const tenantSelectorRef = useRef(null);

const handleBookmarkClick = (event) => {
setAnchorEl(event.currentTarget);
Expand Down Expand Up @@ -148,7 +149,10 @@ export const TopNav = (props) => {
if (index <= 0 || animatingPair) return;
const el1 = itemRefs.current[index];
const el2 = itemRefs.current[index - 1];
if (!el1 || !el2) { moveBookmarkUp(index); return; }
if (!el1 || !el2) {
moveBookmarkUp(index);
return;
}
const distance = el1.getBoundingClientRect().top - el2.getBoundingClientRect().top;
setAnimatingPair({ idx1: index, idx2: index - 1, offset1: -distance, offset2: distance });
setTimeout(() => {
Expand All @@ -162,7 +166,10 @@ export const TopNav = (props) => {
if (index >= bookmarks.length - 1 || animatingPair) return;
const el1 = itemRefs.current[index];
const el2 = itemRefs.current[index + 1];
if (!el1 || !el2) { moveBookmarkDown(index); return; }
if (!el1 || !el2) {
moveBookmarkDown(index);
return;
}
const distance = el2.getBoundingClientRect().top - el1.getBoundingClientRect().top;
setAnimatingPair({ idx1: index, idx2: index + 1, offset1: distance, offset2: -distance });
setTimeout(() => {
Expand All @@ -188,9 +195,16 @@ export const TopNav = (props) => {
const popoverOpen = Boolean(anchorEl);
const popoverId = popoverOpen ? "bookmark-popover" : undefined;

const openSearch = useCallback(() => {
searchDialog.handleOpen();
}, [searchDialog.handleOpen]);

useEffect(() => {
const handleKeyDown = (event) => {
if ((event.metaKey || event.ctrlKey) && event.key === "k") {
if ((event.metaKey || event.ctrlKey) && event.altKey && event.key === "k") {
event.preventDefault();
tenantSelectorRef.current?.focus();
} else if ((event.metaKey || event.ctrlKey) && event.key === "k") {
event.preventDefault();
openSearch();
}
Expand All @@ -199,11 +213,7 @@ export const TopNav = (props) => {
return () => {
window.removeEventListener("keydown", handleKeyDown);
};
}, []);

const openSearch = () => {
searchDialog.handleOpen();
};
}, [openSearch]);

return (
<Box
Expand Down Expand Up @@ -249,7 +259,9 @@ export const TopNav = (props) => {
>
<Logo />
</Box>
{!mdDown && <CippTenantSelector refreshButton={true} tenantButton={true} />}
{!mdDown && (
<CippTenantSelector ref={tenantSelectorRef} refreshButton={true} tenantButton={true} />
)}
{mdDown && (
<IconButton color="inherit" onClick={onNavOpen}>
<SvgIcon color="action" fontSize="small">
Expand Down Expand Up @@ -331,7 +343,13 @@ export const TopNav = (props) => {
},
}),
}}
title={sortOrder === "custom" ? "Custom order" : sortOrder === "asc" ? "A > Z" : "Z > A"}
title={
sortOrder === "custom"
? "Custom order"
: sortOrder === "asc"
? "A > Z"
: "Z > A"
}
>
{sortOrder === "custom" && <SwapVertIcon fontSize="small" />}
{sortOrder === "asc" && <ArrowUpwardIcon fontSize="small" />}
Expand All @@ -341,7 +359,11 @@ export const TopNav = (props) => {
variant="body2"
sx={{ ml: 0.5, color: "text.secondary", fontSize: 12 }}
>
{sortOrder === "custom" ? "Custom order" : sortOrder === "asc" ? "A > Z" : "Z > A"}
{sortOrder === "custom"
? "Custom order"
: sortOrder === "asc"
? "A > Z"
: "Z > A"}
</Typography>
</ListItem>
<Divider />
Expand All @@ -355,47 +377,70 @@ export const TopNav = (props) => {
displayBookmarks.map((bookmark, idx) => (
<ListItem
key={bookmark.path}
ref={(el) => { itemRefs.current[idx] = el; }}
ref={(el) => {
itemRefs.current[idx] = el;
}}
data-bookmark-index={idx}
draggable={reorderMode === "drag" && sortOrder === "custom" && !locked}
{...(reorderMode === "drag" ? {
onDragStart: (e) => {
if (locked) { e.preventDefault(); triggerLockFlash(); return; }
if (sortOrder !== "custom") { e.preventDefault(); triggerSortFlash(); return; }
handleDragStart(idx);
},
onDragEnd: handleDragEnd,
...(sortOrder === "custom" && !locked ? {
onDragOver: (e) => handleDragOver(e, idx),
onDrop: (e) => handleDrop(e, idx),
} : {}),
} : {})}
{...(reorderMode === "drag"
? {
onDragStart: (e) => {
if (locked) {
e.preventDefault();
triggerLockFlash();
return;
}
if (sortOrder !== "custom") {
e.preventDefault();
triggerSortFlash();
return;
}
handleDragStart(idx);
},
onDragEnd: handleDragEnd,
...(sortOrder === "custom" && !locked
? {
onDragOver: (e) => handleDragOver(e, idx),
onDrop: (e) => handleDrop(e, idx),
}
: {}),
}
: {})}
sx={{
color: "inherit",
display: "flex",
justifyContent: "space-between",
"&:hover .bookmark-controls": {
opacity: 1,
},
...(sortOrder === "custom" && reorderMode === "drag" && dragIndex === idx && {
opacity: 0.4,
}),
...(sortOrder === "custom" && reorderMode === "drag" && dragOverIndex === idx && dragIndex !== idx && {
borderTop: "2px solid",
borderColor: "primary.main",
}),
...(animatingPair && (animatingPair.idx1 === idx || animatingPair.idx2 === idx) && {
transform: `translateY(${animatingPair.idx1 === idx ? animatingPair.offset1 : animatingPair.offset2}px)`,
transition: 'transform 250ms ease-in-out',
position: 'relative',
zIndex: animatingPair.idx1 === idx ? 1 : 0,
}),
...(sortOrder === "custom" &&
reorderMode === "drag" &&
dragIndex === idx && {
opacity: 0.4,
}),
...(sortOrder === "custom" &&
reorderMode === "drag" &&
dragOverIndex === idx &&
dragIndex !== idx && {
borderTop: "2px solid",
borderColor: "primary.main",
}),
...(animatingPair &&
(animatingPair.idx1 === idx || animatingPair.idx2 === idx) && {
transform: `translateY(${animatingPair.idx1 === idx ? animatingPair.offset1 : animatingPair.offset2}px)`,
transition: "transform 250ms ease-in-out",
position: "relative",
zIndex: animatingPair.idx1 === idx ? 1 : 0,
}),
}}
>
{reorderMode === "drag" && !locked && (
<Box
onTouchStart={() => {
if (sortOrder !== "custom") { triggerSortFlash(); return; }
if (sortOrder !== "custom") {
triggerSortFlash();
return;
}
touchDragRef.current.startIdx = idx;
setDragIndex(idx);
}}
Expand All @@ -409,7 +454,11 @@ export const TopNav = (props) => {
const li = el?.closest("[data-bookmark-index]");
if (li) {
const overIdx = parseInt(li.dataset.bookmarkIndex, 10);
if (!isNaN(overIdx) && overIdx >= 0 && overIdx < (settings.bookmarks || []).length) {
if (
!isNaN(overIdx) &&
overIdx >= 0 &&
overIdx < (settings.bookmarks || []).length
) {
touchDragRef.current.overIdx = overIdx;
setDragOverIndex(overIdx);
}
Expand Down Expand Up @@ -470,7 +519,10 @@ export const TopNav = (props) => {
size="small"
onClick={(e) => {
e.preventDefault();
if (locked) { triggerLockFlash(); return; }
if (locked) {
triggerLockFlash();
return;
}
sortOrder === "custom" ? animatedMoveUp(idx) : triggerSortFlash();
}}
disabled={sortOrder === "custom" && idx === 0}
Expand All @@ -482,10 +534,17 @@ export const TopNav = (props) => {
size="small"
onClick={(e) => {
e.preventDefault();
if (locked) { triggerLockFlash(); return; }
sortOrder === "custom" ? animatedMoveDown(idx) : triggerSortFlash();
if (locked) {
triggerLockFlash();
return;
}
sortOrder === "custom"
? animatedMoveDown(idx)
: triggerSortFlash();
}}
disabled={sortOrder === "custom" && idx === displayBookmarks.length - 1}
disabled={
sortOrder === "custom" && idx === displayBookmarks.length - 1
}
sx={{ opacity: sortOrder !== "custom" || locked ? 0.4 : 1 }}
>
<KeyboardArrowDownIcon fontSize="small" />
Expand All @@ -497,7 +556,10 @@ export const TopNav = (props) => {
size="small"
onClick={(e) => {
e.preventDefault();
if (locked) { triggerLockFlash(); return; }
if (locked) {
triggerLockFlash();
return;
}
removeBookmark(bookmark.path);
}}
sx={{ ...(locked && { opacity: 0.4 }) }}
Expand Down
Loading