From 55ec4c9dbd9fa1b98cab03f046c98d33125fb484 Mon Sep 17 00:00:00 2001 From: realtradam Date: Sat, 27 Jul 2024 23:24:57 -0400 Subject: make edit buttons conditional on user --- .../blog/web/controllers/ArticleController.java | 2 +- .../src/main/java/com/blog/web/dto/ArticleDto.java | 6 +- .../java/com/blog/web/dto/ArticlePublicDto.java | 2 +- .../src/main/java/com/blog/web/models/Article.java | 2 +- .../blog/web/services/impl/ArticleServiceImpl.java | 22 +++- frontend/src/pages/Home.tsx | 53 +++++--- frontend/src/pages/articles/Article.tsx | 23 +++- frontend/src/pages/articles/Edit.tsx | 142 +++++++++++++++++++++ frontend/src/routes/index.tsx | 12 +- 9 files changed, 229 insertions(+), 35 deletions(-) create mode 100644 frontend/src/pages/articles/Edit.tsx diff --git a/backend/src/main/java/com/blog/web/controllers/ArticleController.java b/backend/src/main/java/com/blog/web/controllers/ArticleController.java index b321cd2..e7890b0 100644 --- a/backend/src/main/java/com/blog/web/controllers/ArticleController.java +++ b/backend/src/main/java/com/blog/web/controllers/ArticleController.java @@ -78,7 +78,7 @@ public class ArticleController { } @PostMapping("/articles/edit/{articleId}") - public String updateArticle(@PathVariable("articleId") Long articleId, @Valid @ModelAttribute("article") ArticleDto article, BindingResult result) { + public String updateArticle(@PathVariable("articleId") long articleId, @Valid @ModelAttribute("article") ArticleDto article, BindingResult result) { if (result.hasErrors()) { return "articles/edit"; } diff --git a/backend/src/main/java/com/blog/web/dto/ArticleDto.java b/backend/src/main/java/com/blog/web/dto/ArticleDto.java index 755b1f6..9e82c40 100644 --- a/backend/src/main/java/com/blog/web/dto/ArticleDto.java +++ b/backend/src/main/java/com/blog/web/dto/ArticleDto.java @@ -29,7 +29,7 @@ public class ArticleDto { @JoinColumn(name = "created_by", nullable = false) private UserEntity createdBy; - public ArticleDto(long id, String title, String photoUrl, String content, UserEntity createdBy, LocalDateTime createdOn, LocalDateTime updatedOn) { + public ArticleDto(Long id, String title, String photoUrl, String content, UserEntity createdBy, LocalDateTime createdOn, LocalDateTime updatedOn) { this.id = id; this.title = title; this.photoUrl = photoUrl; @@ -111,4 +111,8 @@ public class ArticleDto { public String getUsername() { return createdBy.getUsername(); } + + public Long getUserId() { + return createdBy.getId(); + } } diff --git a/backend/src/main/java/com/blog/web/dto/ArticlePublicDto.java b/backend/src/main/java/com/blog/web/dto/ArticlePublicDto.java index 5dac4fe..50dda43 100644 --- a/backend/src/main/java/com/blog/web/dto/ArticlePublicDto.java +++ b/backend/src/main/java/com/blog/web/dto/ArticlePublicDto.java @@ -13,7 +13,7 @@ public class ArticlePublicDto { private LocalDateTime updatedOn; private String createdBy; - public ArticlePublicDto(long id, String title, String photoUrl, String content, String createdBy, LocalDateTime createdOn, LocalDateTime updatedOn) { + public ArticlePublicDto(Long id, String title, String photoUrl, String content, String createdBy, LocalDateTime createdOn, LocalDateTime updatedOn) { this.id = id; this.title = title; this.photoUrl = photoUrl; diff --git a/backend/src/main/java/com/blog/web/models/Article.java b/backend/src/main/java/com/blog/web/models/Article.java index 78ad668..ed4ac1c 100644 --- a/backend/src/main/java/com/blog/web/models/Article.java +++ b/backend/src/main/java/com/blog/web/models/Article.java @@ -46,7 +46,7 @@ public class Article { this.updatedOn = articleDto.getUpdatedOn(); } - public long getId() { + public Long getId() { return id; } diff --git a/backend/src/main/java/com/blog/web/services/impl/ArticleServiceImpl.java b/backend/src/main/java/com/blog/web/services/impl/ArticleServiceImpl.java index 2f9de6c..04cc8be 100644 --- a/backend/src/main/java/com/blog/web/services/impl/ArticleServiceImpl.java +++ b/backend/src/main/java/com/blog/web/services/impl/ArticleServiceImpl.java @@ -61,13 +61,21 @@ public class ArticleServiceImpl implements ArticleService { } @Override - public void updateArticle(ArticleDto articleDto) { - final String username = SecurityUtil.getSessionUser(); - final UserEntity user = userRepository.findByUsername(username).orElse(null); - if (user == null) { - return; - } - final Article article = mapToArticle(articleDto); + public void updateArticle(ArticleDto newArticle) { + if(newArticle == null) { return; } + final Optional optExistingArticle = this.findArticleById(newArticle.getId()); + if(optExistingArticle.isEmpty()) { return; } // cant find article, give up + final ArticleDto existingArticle = optExistingArticle.get(); + Long ownerId = existingArticle.getUserId(); + + final Optional optUser = userService.getLoggedInUser(); + if (optUser.isEmpty()) { return; } // not logged in, not allowed to edit + final UserEntity user = optUser.get(); + Long userId = user.getId(); + + if (!ownerId.equals(userId)) { return; } // logged in a different user, not allowed to edit + + final Article article = mapToArticle(newArticle); article.setCreatedBy(user); articleRepository.save(article); } diff --git a/frontend/src/pages/Home.tsx b/frontend/src/pages/Home.tsx index 71683e7..df9b4c1 100644 --- a/frontend/src/pages/Home.tsx +++ b/frontend/src/pages/Home.tsx @@ -5,6 +5,7 @@ type article = { title: string; photoUrl: string; content: string; + createdBy: string; createdOn: string; updateOn: string; }; @@ -14,11 +15,12 @@ type articleSearch = { value: string | null; }; - export default function Home({ articleSearch, + username, }: { articleSearch: articleSearch; + username: string | null; }) { const [articles, setArticles] = useState([]); const [allArticles, setAllArticles] = useState([]); @@ -40,12 +42,30 @@ export default function Home({ if (!response.ok) { console.log(response); alert("check console for error"); + } else { + fetchArticles(); } - else { - fetchArticles(); - } }; + function renderButtons(article: article) { + return ( +
+ + Edit + +
+ + +
+
+ ); + } + const fetchArticles = useCallback(() => { let url; if (articleSearch.value === null) { @@ -68,7 +88,9 @@ export default function Home({ }, [articleSearch.value]); // pull data when new search is given - useEffect(() => { fetchArticles(); } , [articleSearch.value, fetchArticles]); + useEffect(() => { + fetchArticles(); + }, [articleSearch.value, fetchArticles]); // when new data is pulled update the articles shown useEffect(() => { @@ -80,7 +102,10 @@ export default function Home({ >
{/*th:href="@{/articles/{articleId}(articleId=${article.id})}"*/} - +

- {/*th:if="${user.id} == ${article.createdBy.id}"*/} -
- {/*th:href="@{/articles/edit/{articleId}(articleId=${article.id})}"*/} - - Edit - - {/*th:href="@{/articles/delete/{articleId}(articleId=${article.id})}"*/} -
- - -
+ {username == article?.createdBy && renderButtons(article)}
@@ -112,7 +125,7 @@ export default function Home({
)), ); - }, [articles]); + }, [articles, username]); return ( <> diff --git a/frontend/src/pages/articles/Article.tsx b/frontend/src/pages/articles/Article.tsx index 1a96ada..633e418 100644 --- a/frontend/src/pages/articles/Article.tsx +++ b/frontend/src/pages/articles/Article.tsx @@ -7,7 +7,7 @@ type article = { photoUrl: string; content: string; createdOn: string; - updateOn: string; + updatedOn: string; }; export default function Article() { @@ -28,8 +28,25 @@ export default function Article() { return ( <> -

{articleData?.title}

-
{articleData?.content}
+
+

+ {articleData?.createdOn} +

+

+ {articleData?.title} +

+
+
+
+
+
+
{articleData?.content}
+
+
+
); } diff --git a/frontend/src/pages/articles/Edit.tsx b/frontend/src/pages/articles/Edit.tsx new file mode 100644 index 0000000..4d8e2d8 --- /dev/null +++ b/frontend/src/pages/articles/Edit.tsx @@ -0,0 +1,142 @@ +import { useState, useEffect, FormEvent, ChangeEvent } from "react"; +import { useParams, useNavigate } from "react-router-dom"; + +type article = { + id: number; + title: string; + photoUrl: string; + content: string; + createdOn: string; + updatedOn: string; +}; + +export default function EditArticle() { + const navigate = useNavigate(); + const { id } = useParams(); + const [articleData, setArticleData] = useState
({ + id: 0, + title: "", + photoUrl: "", + content: "", + createdOn: "", + updatedOn: "", + }); + + useEffect(() => { + const url = `${import.meta.env.VITE_API_TITLE}/api/v1/article/${id}`; + fetch(url) + .then((response) => { + if (response.ok) { + return response.json(); + } + throw new Error("Network response was not ok."); + }) + .then((response) => setArticleData(response)); + }, [id]); + + const handleChange = (e: ChangeEvent) => { + setArticleData({ ...articleData, [e.target.name]: e.target.value }); + }; + + const handleSubmit = async (e: FormEvent) => { + e.preventDefault(); //stops submit from happening + + const target = e.target as typeof e.target & { + title: { value: string }; + photoUrl: { value: string }; + content: { value: string }; + }; + + const formData = new FormData(); + formData.append("title", target.title.value); + formData.append("photoUrl", target.photoUrl.value); + formData.append("content", target.content.value); + + const response = await fetch( + `${import.meta.env.VITE_API_TITLE}/api/v1/articles/edit/${articleData.id}`, + { + credentials: "include", + method: "post", + body: formData, + }, + ); + if (response.ok) { + navigate("/"); + } else { + console.log(response); + alert("check console for error"); + } + }; + + return ( + <> +
+
+ +
+
+ + +

+ Please fill out this field. +

+
+
+ + +

+ Please fill out this field. +

+
+
+
+
+ + +

+ Please fill out this field. +

+
+
+
+ +
+
+ + ); +} diff --git a/frontend/src/routes/index.tsx b/frontend/src/routes/index.tsx index cee3529..df38459 100644 --- a/frontend/src/routes/index.tsx +++ b/frontend/src/routes/index.tsx @@ -4,6 +4,7 @@ import Home from "../pages/Home"; import Layout from "../components/Layout"; import Article from "../pages/articles/Article"; import NewArticle from "../pages/articles/New"; +import EditArticle from "../pages/articles/Edit"; import Register from "../pages/auth/Register"; import Login from "../pages/auth/Login"; @@ -33,9 +34,18 @@ export default function Index() { } > - } /> + + } + /> } /> } /> + } /> } /> } /> -- cgit v1.2.3