Skip to content

Commit d589054

Browse files
committed
edit wiki pages
1 parent 2aa7ee1 commit d589054

4 files changed

Lines changed: 176 additions & 19 deletions

File tree

api/src/controllers/wiki.js

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import Wiki from "../schema/wiki";
2+
import { re } from "@babel/core/lib/vendor/import-meta-resolve";
23

34
const slugRegex = /^\/([a-z0-9-_\/])*/i;
45

@@ -107,3 +108,66 @@ export const deleteWiki = async (req, res, next) => {
107108
next(e);
108109
}
109110
};
111+
112+
export const updateWiki = async (req, res, next) => {
113+
if (req.body.slug && req.body.title && req.body.body) {
114+
try {
115+
if (req.userRole !== "admin") {
116+
res
117+
.status(401)
118+
.send("You do not have permission to create a wiki page");
119+
return;
120+
}
121+
122+
const existing = await Wiki.findOne({ _id: req.params.wikiId }).lean();
123+
124+
if (!existing) {
125+
res.status(404).send("That wiki page does not exist");
126+
return;
127+
}
128+
129+
if (existing.slug === "/" && req.body.slug !== "/") {
130+
res.status(400).send("Root page cannot be moved to a different path");
131+
return;
132+
}
133+
134+
const validSlug = slugRegex.test(req.body.slug);
135+
136+
if (!validSlug) {
137+
res.status(400).send("That is not a valid path");
138+
return;
139+
}
140+
141+
if (req.body.slug !== existing.slug) {
142+
const existingSlug = await Wiki.findOne({ slug: req.body.slug }).lean();
143+
144+
if (existingSlug) {
145+
res
146+
.status(409)
147+
.send(
148+
"Wiki page with this slug already exists. Please choose something unique."
149+
);
150+
return;
151+
}
152+
}
153+
154+
await Wiki.findOneAndUpdate(
155+
{ _id: req.params.wikiId },
156+
{
157+
$set: {
158+
slug: req.body.slug,
159+
title: req.body.title,
160+
body: req.body.body,
161+
updated: Date.now(),
162+
},
163+
}
164+
);
165+
166+
res.sendStatus(200);
167+
} catch (e) {
168+
next(e);
169+
}
170+
} else {
171+
res.status(400).send("Request must include slug, title and body");
172+
}
173+
};

api/src/routes/wiki.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
import express from "express";
2-
import { createWiki, getWiki, deleteWiki } from "../controllers/wiki";
2+
import {
3+
createWiki,
4+
getWiki,
5+
deleteWiki,
6+
updateWiki,
7+
} from "../controllers/wiki";
38

49
const router = express.Router();
510

611
export default () => {
712
router.post("/new", createWiki);
13+
router.post("/update/:wikiId", updateWiki);
814
router.get("*", getWiki);
915
router.delete("*", deleteWiki);
1016
return router;

client/pages/wiki/[[...slug]].js

Lines changed: 74 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useState, useContext } from "react";
1+
import React, { useState, useContext, useEffect } from "react";
22
import getConfig from "next/config";
33
import Link from "next/link";
44
import { useRouter } from "next/router";
@@ -15,8 +15,10 @@ import { withAuthServerSideProps } from "../../utils/withAuth";
1515
import { NotificationContext } from "../../components/Notifications";
1616
import LoadingContext from "../../utils/LoadingContext";
1717
import Modal from "../../components/Modal";
18+
import { WikiFields } from "./new";
1819

1920
const Wiki = ({ page, token, userRole, slug }) => {
21+
const [editing, setEditing] = useState(false);
2022
const [showDeleteModal, setShowDeleteModal] = useState(false);
2123

2224
const { addNotification } = useContext(NotificationContext);
@@ -28,6 +30,10 @@ const Wiki = ({ page, token, userRole, slug }) => {
2830
publicRuntimeConfig: { SQ_SITE_NAME, SQ_API_URL },
2931
} = getConfig();
3032

33+
useEffect(() => {
34+
setEditing(false);
35+
}, [router.asPath]);
36+
3137
const handleDelete = async () => {
3238
setLoading(true);
3339

@@ -57,6 +63,45 @@ const Wiki = ({ page, token, userRole, slug }) => {
5763
setLoading(false);
5864
};
5965

66+
const handleEdit = async (e) => {
67+
e.preventDefault();
68+
setLoading(true);
69+
const form = new FormData(e.target);
70+
71+
try {
72+
const updateWikiRes = await fetch(
73+
`${SQ_API_URL}/wiki/update/${page._id}`,
74+
{
75+
method: "POST",
76+
headers: {
77+
"Content-Type": "application/json",
78+
Authorization: `Bearer ${token}`,
79+
},
80+
body: JSON.stringify({
81+
slug: form.get("slug"),
82+
title: form.get("title"),
83+
body: form.get("body"),
84+
}),
85+
}
86+
);
87+
88+
if (updateWikiRes.status !== 200) {
89+
const reason = await updateWikiRes.text();
90+
throw new Error(reason);
91+
}
92+
93+
addNotification("success", "Wiki page updated successfully");
94+
95+
if (form.get("slug") === page.slug) window.location.reload();
96+
else window.location.href = "/wiki" + form.get("slug");
97+
} catch (e) {
98+
addNotification("error", `Could not update wiki page: ${e.message}`);
99+
console.error(e);
100+
}
101+
102+
setLoading(false);
103+
};
104+
60105
return (
61106
<>
62107
<SEO title={page?.title ? `${page.title} | Wiki` : "Wiki"} />
@@ -67,7 +112,7 @@ const Wiki = ({ page, token, userRole, slug }) => {
67112
mb={3}
68113
>
69114
<Text as="h1">{page?.title ?? `${SQ_SITE_NAME} wiki`}</Text>
70-
{userRole === "admin" && (
115+
{userRole === "admin" && !editing && (
71116
<Box display="flex" alignItems="center">
72117
<Link href="/wiki/new" passHref>
73118
<Button as="a" variant="secondary">
@@ -76,7 +121,11 @@ const Wiki = ({ page, token, userRole, slug }) => {
76121
</Link>
77122
{!!page && (
78123
<>
79-
<Button variant="secondary" ml={3}>
124+
<Button
125+
onClick={() => setEditing(true)}
126+
variant="secondary"
127+
ml={3}
128+
>
80129
Edit
81130
</Button>
82131
{!!slug && (
@@ -109,11 +158,28 @@ const Wiki = ({ page, token, userRole, slug }) => {
109158
)}
110159
</Text>
111160
</Box>
112-
<MarkdownBody>
113-
<ReactMarkdown remarkPlugins={[remarkGfm]}>
114-
{page.body}
115-
</ReactMarkdown>
116-
</MarkdownBody>
161+
{!editing ? (
162+
<MarkdownBody>
163+
<ReactMarkdown remarkPlugins={[remarkGfm]}>
164+
{page.body}
165+
</ReactMarkdown>
166+
</MarkdownBody>
167+
) : (
168+
<form onSubmit={handleEdit}>
169+
<WikiFields values={page} />
170+
<Box display="flex" justifyContent="flex-end">
171+
<Button
172+
onClick={() => setEditing(false)}
173+
type="button"
174+
variant="secondary"
175+
mr={3}
176+
>
177+
Cancel
178+
</Button>
179+
<Button>Save changes</Button>
180+
</Box>
181+
</form>
182+
)}
117183
</>
118184
) : (
119185
<Text color="grey">There is nothing here yet.</Text>

client/pages/wiki/new.js

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,36 @@ import { withAuthServerSideProps } from "../../utils/withAuth";
1010
import { NotificationContext } from "../../components/Notifications";
1111
import LoadingContext from "../../utils/LoadingContext";
1212

13+
export const WikiFields = ({ values }) => {
14+
return (
15+
<>
16+
<Input
17+
name="slug"
18+
label="Path"
19+
defaultValue={values?.slug}
20+
mb={4}
21+
required
22+
/>
23+
<Input
24+
name="title"
25+
label="Title"
26+
defaultValue={values?.title}
27+
mb={4}
28+
required
29+
/>
30+
<Input
31+
name="body"
32+
label="Body"
33+
placeholder="Markdown supported"
34+
defaultValue={values?.body}
35+
rows={20}
36+
mb={4}
37+
required
38+
/>
39+
</>
40+
);
41+
};
42+
1343
const NewWiki = ({ token, userRole }) => {
1444
if (userRole !== "admin") {
1545
return <Text>You do not have permission to do that.</Text>;
@@ -67,16 +97,7 @@ const NewWiki = ({ token, userRole }) => {
6797
New wiki page
6898
</Text>
6999
<form onSubmit={handleCreate}>
70-
<Input name="slug" label="Path" mb={4} required />
71-
<Input name="title" label="Title" mb={4} required />
72-
<Input
73-
name="body"
74-
label="Body"
75-
placeholder="Markdown supported"
76-
rows={20}
77-
mb={4}
78-
required
79-
/>
100+
<WikiFields />
80101
<Button display="block" ml="auto">
81102
Create wiki page
82103
</Button>

0 commit comments

Comments
 (0)