Changeset 9bf1f8d
- Timestamp:
- 01/20/23 22:57:18 (2 years ago)
- Branches:
- main
- Children:
- 8dffe02
- Parents:
- 4abf55a
- Files:
-
- 5 added
- 10 deleted
- 48 edited
- 2 moved
Legend:
- Unmodified
- Added
- Removed
-
reactapp/public/index.html
r4abf55a r9bf1f8d 3 3 <head> 4 4 <meta charset="utf-8" /> 5 <link rel="icon" href="%PUBLIC_URL%/favicon.ico " />5 <link rel="icon" href="%PUBLIC_URL%/favicon.ico?v=2" /> 6 6 <meta name="viewport" content="width=device-width, initial-scale=1" /> 7 7 <meta name="theme-color" content="#000000" /> -
reactapp/src/App.js
r4abf55a r9bf1f8d 4 4 import Registration from "./Pages/Registration"; 5 5 import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom"; 6 import Home from "./Pages/Home";6 import Root from "./Components/Root"; 7 7 import UserDashboard from "./Pages/UserDashboard"; 8 8 import Subject from "./Pages/Subject"; … … 15 15 import Topic from "./Pages/Topic"; 16 16 import LoadingSpinner from "./Components/Styled/LoadingSpinner.style"; 17 import Home from "./Pages/Home"; 18 import PublicUserProfile from "./Pages/PublicUserProfile"; 17 19 18 20 export default function App() { … … 51 53 <BrowserRouter> 52 54 <Routes> 53 <Route path="/" element={<Home />}> 55 <Route path="/" element={<Root />}> 56 <Route path="/" element={<Home/>}/> 54 57 <Route path="login" element={<Login />}></Route> 55 58 <Route path="registration" element={<Registration />}></Route> … … 58 61 </Route> 59 62 <Route path="university/:universityId" element={<University />} /> 63 <Route path="user/:userId" element={<PublicUserProfile />} /> 60 64 <Route path="faculty/:facultyId" element={<Faculty />} /> 61 65 <Route path="subject/:subjectId" element={<Subject />} /> -
reactapp/src/Components/Logout.js
r4abf55a r9bf1f8d 3 3 import Cookies from "js-cookie"; 4 4 import { LogoutButton } from "./Styled/UserDetails.style"; 5 import { Navigate } from "react-router-dom";6 5 7 6 function Logout() { … … 13 12 }; 14 13 15 return <LogoutButton onClick={handleLogout} >Одјави се</LogoutButton>;14 return <LogoutButton onClick={handleLogout} style={{color:"black"}}>Одјави се</LogoutButton>; 16 15 } 17 16 -
reactapp/src/Components/OpinionTree.js
r4abf55a r9bf1f8d 26 26 import axios from "../api/axios"; 27 27 28 function OpinionTree({ professor }){28 const OpinionTree = ({professor, relatedOpinions}) => { 29 29 var renderedOpinionIds = []; 30 30 var postCount; // za da ne go pokazuva ispod postot … … 35 35 const [replyModalDisplay, setReplyModalDisplay] = useState("none"); 36 36 const [replyContent, setReplyContent] = useState(""); 37 const [postForModal, setPostForModal] = useState(null); 37 const [postForReplyModal, setPostForReplyModal] = useState(null); 38 const [reportModalDisplay, setReportModalDisplay] = useState("none"); 39 const [reportContent, setReportContent] = useState("") 40 const [postForReportModal, setPostForReportModal] = useState(null); 41 38 42 const [user, setUser] = useState(null); 39 43 const [loadedUser, setLoadedUser] = useState(false); … … 42 46 43 47 useEffect(() => { 44 const url = `http://192.168. 0.29:8080/secure/currentUser`;48 const url = `http://192.168.1.254:8080/secure/currentUser`; 45 49 46 50 const fetchUser = async () => { … … 68 72 ) { 69 73 const response = await axios( 70 `http://192.168. 0.29:8080/secure/upvoteOpinion/${post.postId}`,74 `http://192.168.1.254:8080/secure/upvoteOpinion/${post.postId}`, 71 75 { 72 76 method: "get", … … 91 95 ) { 92 96 const response = await axios( 93 `http://192.168. 0.29:8080/secure/downvoteOpinion/${post.postId}`,97 `http://192.168.1.254:8080/secure/downvoteOpinion/${post.postId}`, 94 98 { 95 99 method: "get", … … 110 114 if (auth) { 111 115 setReplyModalDisplay("block"); 112 setPostFor Modal(opinion);116 setPostForReplyModal(opinion); 113 117 document.body.style.overflowY = "hidden"; 114 118 } else { … … 116 120 } 117 121 }; 122 123 const handleReport = (opinion) => { 124 if (auth) { 125 setReportModalDisplay("block"); 126 setPostForReportModal(opinion); 127 document.body.style.overflowY = "hidden"; 128 } else { 129 navigate("/login"); 130 } 131 } 118 132 119 133 const handleModalCloseClick = () => { 120 134 setReplyModalDisplay("none"); 135 setReportModalDisplay("none"); 121 136 document.body.style.overflowY = "auto"; 122 137 }; 123 138 124 const handle ContentChange = (e) => {139 const handleReplyContentChange = (e) => { 125 140 setReplyContent(e.target.value); 141 }; 142 143 const handleReportContentChange = (e) => { 144 setReportContent(e.target.value); 126 145 }; 127 146 … … 131 150 if (!replyContent.length < 1) { 132 151 const response = await axios( 133 `http://192.168. 0.29:8080/secure/professor/${professor.professorId}/replyToOpinion/${postId}`,152 `http://192.168.1.254:8080/secure/professor/${professor.professorId}/replyToOpinion/${postId}`, 134 153 { 135 154 method: "post", … … 147 166 }; 148 167 168 const handleReportSubmit = async (e, postId) => { 169 e.preventDefault(); 170 171 if (!reportContent.length < 1) { 172 const response = await axios( 173 `http://192.168.1.254:8080/secure/reportOpinion/${postId}`, 174 { 175 method: "post", 176 data: { 177 description: reportContent, 178 }, 179 withCredentials: true, 180 } 181 ); 182 setErrorMessage(""); 183 window.location.reload(); 184 } else { 185 setErrorMessage("Полето за содржина не смее да биде празно"); 186 } 187 }; 188 149 189 function displayChildPosts(child, parentPostAuthorUsername, replyIndent) { 150 190 if (child == null) return; 151 postCount = renderedOpinionIds.push(child.postId);191 if (!renderedOpinionIds.includes(child.postId)) {postCount = renderedOpinionIds.push(child.postId);} 152 192 return ( 153 193 <div key={child.postId}> … … 158 198 му реплицирал на {parentPostAuthorUsername} 159 199 </p> 160 <p style={{ marginBottom: "10px", maxWidth: " 90%" }}>200 <p style={{ marginBottom: "10px", maxWidth: "85%" }}> 161 201 {child.content} 162 202 </p> … … 228 268 onClick={() => handleReply(child)} 229 269 /> 270 271 <StyledFontAwesomeIcon 272 icon={solid("flag")} 273 right={130 + "px"} 274 color="darkgrey" 275 onClick={() => handleReport(child)} 276 /> 230 277 </div> 231 278 </OpinionReplyCardContent> … … 244 291 return ( 245 292 <div className="opinionTree"> 246 { professor.relatedOpinions.map((opinion) => {247 if (!renderedOpinionIds.includes(opinion.postId) ) {293 {relatedOpinions.map((opinion) => { 294 if (!renderedOpinionIds.includes(opinion.postId) && opinion.parent === null) { 248 295 postCount = renderedOpinionIds.push(opinion.postId); 249 296 return ( … … 257 304 напишал 258 305 </p> 259 <p style={{ marginBottom: "10px", maxWidth: " 90%" }}>306 <p style={{ marginBottom: "10px", maxWidth: "85%" }}> 260 307 {opinion.content} 261 308 </p> … … 335 382 onClick={() => handleReply(opinion)} 336 383 /> 384 385 <StyledFontAwesomeIcon 386 icon={solid("flag")} 387 right={130 + "px"} 388 color="darkgrey" 389 onClick={() => handleReport(opinion)} 390 /> 337 391 </div> 338 392 </OpinionCardContent> … … 346 400 return null; 347 401 })} 348 {postFor Modal && (402 {postForReplyModal && ( 349 403 <Modal display={replyModalDisplay}> 350 404 <ModalContent> … … 352 406 <ModalClose onClick={handleModalCloseClick}>×</ModalClose> 353 407 <h3 style={{ marginTop: "5px" }}> 354 Реплика на {postFor Modal.author.username}408 Реплика на {postForReplyModal.author.username} 355 409 </h3> 356 410 </ModalHeader> 357 <form onSubmit={(e) => handleReplySubmit(e, postFor Modal.postId)}>411 <form onSubmit={(e) => handleReplySubmit(e, postForReplyModal.postId)}> 358 412 <ModalBody> 359 413 <label htmlFor="content"> … … 364 418 cols="100" 365 419 value={replyContent} 366 onChange={handle ContentChange}420 onChange={handleReplyContentChange} 367 421 spellCheck={false} 368 422 /> … … 379 433 </Modal> 380 434 )} 435 {postForReportModal && ( 436 <Modal display={reportModalDisplay}> 437 <ModalContent> 438 <ModalHeader> 439 <ModalClose onClick={handleModalCloseClick}>×</ModalClose> 440 <h3 style={{ marginTop: "5px" }}> 441 Пријава за мислење #{postForReportModal.postId} 442 </h3> 443 </ModalHeader> 444 <form onSubmit={(e) => handleReportSubmit(e, postForReportModal.postId)}> 445 <ModalBody> 446 <label htmlFor="content"> 447 <b>Наведете причина</b>: 448 <ModalTextarea 449 id="content" 450 rows="8" 451 cols="100" 452 value={reportContent} 453 onChange={handleReportContentChange} 454 spellCheck={false} 455 /> 456 </label> 457 </ModalBody> 458 <p 459 style={{ color: "red", marginLeft: "15px", marginTop: "10px" }} 460 > 461 {errorMessage} 462 </p> 463 <ModalFooter type="submit">ПРИЈАВИ</ModalFooter> 464 </form> 465 </ModalContent> 466 </Modal> 467 )} 381 468 </div> 382 469 ); -
reactapp/src/Components/Search.js
r4abf55a r9bf1f8d 13 13 const [query, setQuery] = useState(""); 14 14 const [professors, setProfessors] = useState([]); 15 const [subjects, setSubjects] = useState([]); 15 16 16 17 useEffect(() => { 17 const url = `http://192.168.0.29:8080/public/professors/nameContains/${transliterate(18 query19 )}`;20 21 18 const fetchData = async () => { 22 19 try { 23 const response = await fetch(url); 24 var cyclicGraph = await response.json(); 25 var jsogStructure = JSOG.encode(cyclicGraph); 26 cyclicGraph = JSOG.decode(jsogStructure); 27 setProfessors(cyclicGraph); 20 Promise.all([fetch(`http://192.168.1.254:8080/public/professors/nameContains/${transliterate(query)}`), 21 fetch(`http://192.168.1.254:8080/public/subjects/nameContains/${transliterate(query)}`)]) 22 .then(([resProfessors, resSubjects]) => Promise.all([resProfessors.json(), resSubjects.json()])) 23 .then(([dataProfessors, dataSubjects]) => { 24 let cyclicGraph1 = dataProfessors; 25 let jsogStructure1 = JSOG.encode(cyclicGraph1); 26 cyclicGraph1 = JSOG.decode(jsogStructure1); 27 setProfessors(cyclicGraph1); 28 29 let cyclicGraph2 = dataSubjects; 30 let jsogStructure2 = JSOG.encode(cyclicGraph2); 31 cyclicGraph2 = JSOG.decode(jsogStructure2); 32 setSubjects(cyclicGraph2); 33 }) 28 34 } catch (error) { 29 35 console.log("Fetching error", error); … … 31 37 }; 32 38 33 if (query.length > 2) fetchData();39 if (query.length > 3) fetchData(); 34 40 }, [query]); 35 41 … … 39 45 if (event.key === "Enter") { 40 46 setExpanded(false); 41 navigate("/search", { state: professors });47 navigate("/search", { state: professors.concat(subjects) }); 42 48 } 43 49 }; … … 71 77 <SearchDropdown 72 78 display={ 73 query.length > 2 && professors.length > 0&& expanded79 query.length > 3 && (professors.length > 0 || subjects.length > 0) && expanded 74 80 ? "block" 75 81 : "none" 76 82 } 77 83 > 78 {query.length > 2&&79 professors. slice(0, 7).map((professor) => (84 {query.length > 3 && 85 professors.concat(subjects).slice(0, 7).map((match) => ( 80 86 <SearchResult 81 key={ professor.professorId}87 key={match.professorId !== undefined ? match.professorId : match.subjectId} 82 88 onMouseDown={(event) => { 83 89 event.preventDefault(); … … 86 92 margin="0" 87 93 > 88 <SearchResultLink href={ "/professor/" + professor.professorId}>94 <SearchResultLink href={`/${match.professorId !== undefined ? 'professor': 'subject'}/` + `${match.professorId !== undefined ? match.professorId : match.subjectId}`}> 89 95 <SearchResultText weight="bold" size="medium"> 90 { professor.professorName}96 {match.professorId !== undefined ? match.professorName : match.subjectName} 91 97 </SearchResultText> 92 98 <SearchResultText weight="normal" size="er"> 93 { professor.faculty.facultyName}99 {match.professorId !== undefined ? match.faculty.facultyName : match.studyProgramme.faculty.facultyName} 94 100 </SearchResultText> 95 101 </SearchResultLink> -
reactapp/src/Components/Styled/Main.style.js
r4abf55a r9bf1f8d 25 25 export const CurrentPageNav = styled.div` 26 26 font-style: italic; 27 font-size: 14px; 27 font-size: 16px; 28 font-weight: bold; 28 29 `; -
reactapp/src/Components/Styled/Search.style.js
r4abf55a r9bf1f8d 3 3 4 4 export const SearchBox = styled.input` 5 width: 350px;5 width: 450px; 6 6 box-sizing: border-box; 7 7 border: 2px solid #ccc; … … 23 23 padding: 12px 16px; 24 24 z-index: 1; 25 width: 350px;25 width: 450px; 26 26 padding: 0; 27 27 `; -
reactapp/src/Components/SubjectsAccordion.js
r4abf55a r9bf1f8d 6 6 } from "./Styled/SubjectsAccordion.style."; 7 7 import { EntityLi, EntityUl, EntityParam } from "./Styled/EntityList.style"; 8 import JSOG from "jsog"; 8 9 9 10 const SubjectsAccordion = (props) => { 10 11 const [height, setHeight] = useState("0"); 11 12 const [opacity, setOpacity] = useState(0); 13 14 const [subjects, setSubjects] = useState(null); 15 const [loadedSubjects, setLoadedSubjects] = useState(false); 16 const [counts, setCounts] = useState(null); 17 const [loadedCounts, setLoadedCounts] = useState(false); 18 19 const [fetchError, setFetchError] = useState(false); 12 20 const handleClick = () => { 13 21 if (height === "0") { 14 22 setHeight("auto"); 15 23 setOpacity(1); 24 try { 25 Promise.all([fetch(`http://192.168.1.254:8080/public/subjects?studyProgrammeId=${props.title.studyProgrammeId}`), 26 fetch(`http://192.168.1.254:8080/public/study_programme/${props.title.studyProgrammeId}/threadCountForEachSubject`)]) 27 .then(([resSubjects, counts]) => Promise.all([resSubjects.json(), counts.json()])) 28 .then(([dataSubjects, dataCounts]) => { 29 let cyclicGraph1 = dataSubjects; 30 let jsogStructure1 = JSOG.encode(cyclicGraph1); 31 cyclicGraph1 = JSOG.decode(jsogStructure1); 32 setSubjects(cyclicGraph1); 33 setLoadedSubjects(true); 34 35 let cyclicGraph2 = dataCounts; 36 let jsogStructure2 = JSOG.encode(cyclicGraph2); 37 cyclicGraph2 = JSOG.decode(jsogStructure2); 38 setCounts(cyclicGraph2); 39 setLoadedCounts(true); 40 }) 41 42 } catch (error) { 43 setFetchError(true); 44 } 16 45 } else { 17 46 setHeight("0"); … … 27 56 <SubjectsAccordionDiv height={height} opacity={opacity}> 28 57 <EntityUl> 29 { props.content&&30 props.content.map((item) => {31 let totalPosts = item.threads.length;58 {loadedSubjects && 59 subjects.map((item, idx) => { 60 let totalPosts = parseInt(counts[idx].split(",")[1]); 32 61 return ( 33 62 <EntityLi key={item.subjectName} bgcolor="cornsilk"> -
reactapp/src/Components/UserHeader.js
r4abf55a r9bf1f8d 11 11 12 12 useEffect(() => { 13 const url = `http://192.168. 0.29:8080/secure/currentUser`;13 const url = `http://192.168.1.254:8080/secure/currentUser`; 14 14 15 15 const fetchUser = async () => { … … 30 30 31 31 return loadedUser ? ( 32 <div style={{ float: "left", marginTop: 20, marginLeft: 40 }}>32 <div style={{ float: "left", marginTop: 20, marginLeft: 10 }}> 33 33 Најавен/а: <a href="/user_dashboard">{user.username}</a> <Logout />{" "} 34 34 </div> 35 35 ) : ( 36 <div style={{ float: "left", marginTop: 25, marginLeft: 60 }}>36 <div style={{ float: "left", marginTop: 25, marginLeft: 10 }}> 37 37 <LoadingSpinner/> 38 38 </div> -
reactapp/src/Pages/Faculty.js
r4abf55a r9bf1f8d 20 20 const Faculty = () => { 21 21 let params = useParams(); 22 23 const [professors, setProfessors] = useState(null); 24 const [studyProgrammes, setStudyProgrammes] = useState(null); 25 const [professorOpinionCount, setProfessorOpinionCount] = useState(null); 22 26 const [loadedProfessors, setLoadedProfessors] = useState(false); 23 27 const [loadedStudyProgrammes, setLoadedStudyProgrammes] = useState(false); 24 const [ professors, setProfessors] = useState(null);25 const [studyProgrammes, setStudyProgrammes] = useState(null); 28 const [loadedProfessorOpinionCount, setLoadedProfessorOpinionCount] = useState(false); 29 26 30 const [fetchError, setFetchError] = useState(false); 27 31 const [entityType, setEntityType] = useState(0); 28 32 29 33 useEffect(() => { 30 const urlProfessors = `http://192.168.0.29:8080/public/professors?facultyId=${params.facultyId}`; 31 const urlStudyProgrammes = `http://192.168.0.29:8080/public/study_programmes?facultyId=${params.facultyId}`; 32 33 const fetchDataProfessors = async () => { 34 const fetchProfessors = async () => { 34 35 try { 35 const response = await fetch( urlProfessors);36 const response = await fetch(`http://192.168.1.254:8080/public/professors?facultyId=${params.facultyId}`); 36 37 let cyclicGraph = await response.json(); 37 38 let jsogStructure = JSOG.encode(cyclicGraph); … … 44 45 }; 45 46 46 const fetch DataStudyProgrammes = async () => {47 const fetchStudyProgrammes = async () => { 47 48 try { 48 const response2 = await fetch( urlStudyProgrammes);49 const response2 = await fetch(`http://192.168.1.254:8080/public/study_programmes?facultyId=${params.facultyId}`); 49 50 let cyclicGraph2 = await response2.json(); 50 51 let jsogStructure2 = JSOG.encode(cyclicGraph2); … … 57 58 }; 58 59 59 fetchDataProfessors(); 60 fetchDataStudyProgrammes(); 60 const fetchProfessorOpinionCount = async () => { 61 try { 62 const response3 = await fetch(`http://192.168.1.254:8080/public/faculty/${params.facultyId}/opinionCountForEachProfessor`); 63 let cyclicGraph3 = await response3.json(); 64 let jsogStructure3 = JSOG.encode(cyclicGraph3); 65 cyclicGraph3 = JSOG.decode(jsogStructure3); 66 setProfessorOpinionCount(cyclicGraph3); 67 setLoadedProfessorOpinionCount(true); 68 } catch (error) { 69 setFetchError(true); 70 } 71 } 72 73 fetchProfessors(); 74 fetchStudyProgrammes(); 75 fetchProfessorOpinionCount(); 61 76 }, [params.facultyId]); 62 77 63 return loadedProfessors && professors.length != 0 ? (78 return loadedProfessors && professors.length !== 0 ? ( 64 79 entityType === 0 ? ( 65 80 <> … … 106 121 </div> 107 122 <div key={params.facultyId}> 108 {professors.map((professor) => { 109 let totalPosts = professor.relatedOpinions.length; 110 123 {professors.map((professor, idx) => { 124 let totalPosts = loadedProfessorOpinionCount ? parseInt(professorOpinionCount[idx].split(",")[1]) : 0; 111 125 return ( 112 126 <EntityUl key={professor.professorId}> … … 215 229 key={studyProgramme.studyProgrammeId} 216 230 title={studyProgramme} 217 content={studyProgramme.subjects}218 231 ></SubjectsAccordion> 219 232 ); -
reactapp/src/Pages/Home.js
r4abf55a r9bf1f8d 1 import React, { useContext } from "react"; 2 import { MainWrapper, MainTitle } from "../Components/Styled/Main.style"; 3 import { Outlet } from "react-router-dom"; 4 import Search from "../Components/Search"; 5 import UserHeader from "../Components/UserHeader"; 6 import AuthApi from "../api/AuthApi"; 1 import React, {useEffect, useState} from 'react'; 2 import JSOG from "jsog"; 3 import {OpinionCard, OpinionCardContent, OpinionCardContentTime} from "../Components/Styled/OpinionCard.style"; 4 import LoadingSpinner from "../Components/Styled/LoadingSpinner.style"; 5 import {dateConverter} from "../Util/dateConverter"; 6 import {findParentThread} from "../Util/findParentThread"; 7 import {CurrentPageNav} from "../Components/Styled/Main.style"; 7 8 8 function Home({ user, userLoaded }) { 9 const { auth, setAuth } = useContext(AuthApi); 9 const Home = () => { 10 const [latestOpinions, setLatestOpinions] = useState(null); 11 const [loadedLatestOpinions, setLoadedLatestOpinions] = useState(false); 12 const [latestThreads, setLatestThreads] = useState(null); 13 const [loadedLatestThreads, setLoadedLatestThreads] = useState(false); 10 14 11 return ( 12 <MainWrapper> 13 <style> 14 @import 15 url('https://fonts.googleapis.com/css2?family=Roboto+Mono:ital,wght@0,400;0,500;0,700;1,400;1,500;1,700&display=swap'); 16 </style> 17 <style> 18 { 19 "body { background-color: papayawhip;} * {margin: 0; padding: 0; box-sizing: border-box;}" 20 } 21 </style> 22 <a href="/"> 23 <MainTitle>profesori.mk</MainTitle> 24 </a>{" "} 25 <Search /> 26 {auth && <UserHeader />} 27 <div style={{ marginTop: "140px" }}></div> 28 <Outlet /> 29 </MainWrapper> 30 ); 31 } 15 useEffect(() => { 16 Promise.all([fetch(`http://192.168.1.254:8080/public/latest10opinions`), 17 fetch(`http://192.168.1.254:8080/public/latest10threads`)]) 18 .then(([resOpinions, resThreads]) => Promise.all([resOpinions.json(), resThreads.json()])) 19 .then(([dataOpinions, dataThreads]) => { 20 let cyclicGraph1 = dataOpinions; 21 let jsogStructure1 = JSOG.encode(cyclicGraph1); 22 cyclicGraph1 = JSOG.decode(jsogStructure1); 23 setLatestOpinions(cyclicGraph1); 24 setLoadedLatestOpinions(true); 25 26 let cyclicGraph2 = dataThreads; 27 let jsogStructure2 = JSOG.encode(cyclicGraph2); 28 cyclicGraph2 = JSOG.decode(jsogStructure2); 29 setLatestThreads(cyclicGraph2); 30 setLoadedLatestThreads(true); 31 }) 32 33 }, []); 34 35 return ( 36 <> 37 <CurrentPageNav> 38 »{" "} 39 <a href={"/university/1"}> 40 Универзитет „Св. Кирил и Методиј“ 41 </a> 42 </CurrentPageNav> 43 <div style={{display:"grid", gridTemplateColumns:"1fr 1fr", gap:"40px", marginTop:"60px"}}> 44 45 <div id="latestOpinions" style={{gridColumn:"1", paddingLeft:"20px"}}> 46 <h2 style={{fontWeight:"normal", marginBottom:"30px"}}>Последни мислења за <span style={{fontWeight:"bold"}}>професори</span></h2> 47 {loadedLatestOpinions ? latestOpinions.slice(5).map(opinion => { 48 opinion.timePosted = undefined; 49 return <OpinionCard key={opinion.postId}> 50 <OpinionCardContent> 51 <p style={{marginBottom:"10px"}}> 52 во дискусија за{" "} 53 <a href={"/professor/" + opinion.targetProfessor.professorId}> 54 {opinion.targetProfessor.professorName} 55 </a> 56 </p> 57 <p style={{ fontStyle: "italic", marginBottom: "10px" }}> 58 <a href={"/user/" + opinion.author.id}> 59 {opinion.author.username} 60 </a>{" "} 61 напишал 62 </p> 63 <p style={{ marginBottom: "10px", maxWidth: "90%" }}> 64 {opinion.content} 65 </p> 66 {new Date(opinion.timePosted).setMilliseconds(0) === new Date(opinion.timeLastEdited).setMilliseconds(0) ? ( 67 <OpinionCardContentTime> 68 {dateConverter( 69 new Date(opinion.timePosted).toString().slice(4, -43) 70 )} <span style={{fontStyle:"normal",color:"blue"}}>#{opinion.postId}</span> 71 </OpinionCardContentTime> 72 ) : ( 73 <OpinionCardContentTime> 74 {dateConverter( 75 new Date(opinion.timeLastEdited) 76 .toString() 77 .slice(4, -43) 78 )}{" "} <span style={{fontStyle:"normal",color:"blue"}}>#{opinion.postId}</span>{" "} 79 (едитирано од модератор) 80 </OpinionCardContentTime> 81 )} 82 </OpinionCardContent> 83 </OpinionCard> 84 }) : <LoadingSpinner/>} 85 </div> 86 87 <div id="latestThreads" style={{gridColumn:"2", paddingRight:"20px"}}> 88 <h2 style={{fontWeight:"normal", marginBottom:"30px"}}>Последни мислења за <span style={{fontWeight:"bold"}}>предмети</span></h2> 89 {loadedLatestThreads ? latestThreads.slice(5).map(thread => { 90 return <OpinionCard key={thread.postId}> 91 <OpinionCardContent> 92 <p style={{marginBottom:"10px"}}> 93 во дискусија за{" "} 94 <a href={ 95 thread.parent === null 96 ? "/topic/" + thread.postId 97 : "/topic/" + findParentThread(thread).postId 98 } 99 > {thread.targetSubject.subjectName} 100 </a> 101 </p> 102 <p style={{ fontStyle: "italic", marginBottom: "10px" }}> 103 <a href={"/user/" + thread.author.id}> 104 {thread.author.username} 105 </a>{" "} 106 напишал 107 </p> 108 <p style={{ marginBottom: "10px", maxWidth: "90%" }}> 109 {thread.content} 110 </p> 111 {new Date(thread.timePosted).setMilliseconds(0) === new Date(thread.timeLastEdited).setMilliseconds(0) ? ( 112 <OpinionCardContentTime> 113 {dateConverter( 114 new Date(thread.timePosted).toString().slice(4, -43) 115 )} <span style={{fontStyle:"normal",color:"blue"}}>#{thread.postId}</span> 116 </OpinionCardContentTime> 117 ) : ( 118 <OpinionCardContentTime> 119 {dateConverter( 120 new Date(thread.timeLastEdited) 121 .toString() 122 .slice(4, -43) 123 )}{" "} <span style={{fontStyle:"normal",color:"blue"}}>#{thread.postId}</span>{" "} 124 (едитирано од модератор) 125 </OpinionCardContentTime> 126 )} 127 </OpinionCardContent> 128 </OpinionCard> 129 }) : null} 130 </div> 131 </div> 132 </> 133 ); 134 }; 32 135 33 136 export default Home; -
reactapp/src/Pages/Professor.js
r4abf55a r9bf1f8d 32 32 const [professor, setProfessor] = useState(null); 33 33 const [loadedProfessor, setLoadedProfessor] = useState(false); 34 const [relatedOpinions, setRelatedOpinions] = useState(null); 35 const [loadedRelatedOpinions, setLoadedRelatedOpinions] = useState(false); 34 36 35 37 const [postModalDisplay, setPostModalDisplay] = useState("none"); … … 40 42 41 43 useEffect(() => { 42 const url = `http://192.168.0.29:8080/public/professor/${params.professorId}`; 44 Promise.all([fetch(`http://192.168.1.254:8080/public/professor/${params.professorId}`), 45 fetch(`http://192.168.1.254:8080/public/professor/${params.professorId}/relatedOpinions`)]) 46 .then(([resProfessor, resRelatedOpinions]) => Promise.all([resProfessor.json(), resRelatedOpinions.json()])) 47 .then(([dataProfessor, dataRelatedOpinions]) => { 48 let cyclicGraph1 = dataProfessor; 49 let jsogStructure1 = JSOG.encode(cyclicGraph1); 50 cyclicGraph1 = JSOG.decode(jsogStructure1); 51 setProfessor(cyclicGraph1); 52 setLoadedProfessor(true); 43 53 44 const fetchProfessor = async () => { 45 try { 46 const response = await fetch(url); 47 var cyclicGraph = await response.json(); 48 var jsogStructure = JSOG.encode(cyclicGraph); 49 cyclicGraph = JSOG.decode(jsogStructure); 50 setProfessor(cyclicGraph); 51 setLoadedProfessor(true); 52 } catch (error) { 53 setFetchError(true); 54 } 55 }; 54 let cyclicGraph2 = dataRelatedOpinions; 55 let jsogStructure2 = JSOG.encode(cyclicGraph2); 56 cyclicGraph2 = JSOG.decode(jsogStructure2); 57 setRelatedOpinions(cyclicGraph2); 58 setLoadedRelatedOpinions(true); 59 }) 56 60 57 fetchProfessor(); 58 }, [params.professorId]); 61 }, []); 59 62 60 63 const handleAddOpinionButtonClick = () => { … … 77 80 if (!postContent.length < 1) { 78 81 const response = await axios( 79 `http://192.168. 0.29:8080/secure/professor/${params.professorId}/addOpinion`,82 `http://192.168.1.254:8080/secure/professor/${params.professorId}/addOpinion`, 80 83 { 81 84 method: "post", … … 129 132 }} 130 133 > 131 { professor.relatedOpinions.length}{" "}132 { professor.relatedOpinions.length !== 1 ? "мислења" : "мислење"}134 {relatedOpinions.length}{" "} 135 {relatedOpinions.length !== 1 ? "мислења" : "мислење"} 133 136 </h3> 134 137 {auth && ( … … 172 175 173 176 <div className="opinionTree"> 174 <OpinionTree professor={professor} />177 <OpinionTree professor={professor} relatedOpinions={relatedOpinions}/> 175 178 </div> 176 179 <Outlet /> -
reactapp/src/Pages/SearchResults.js
r4abf55a r9bf1f8d 14 14 <SearchResultsWrapper> 15 15 <h3 style={{ marginBottom: "30px" }}>Резултати од пребарувањето:</h3> 16 {location.state.map(( professor) => (17 <SearchResult key={ professor.professorId} margin="10px">18 <SearchResultLink href={ "/professor/" + professor.professorId}>16 {location.state.map((match) => ( 17 <SearchResult key={match.professorId !== undefined ? match.professorId : match.subjectId} margin="10px"> 18 <SearchResultLink href={`/${match.professorId !== undefined ? 'professor': 'subject'}/` + `${match.professorId !== undefined ? match.professorId : match.subjectId}`}> 19 19 <SearchResultText weight="bold" size="medium"> 20 { professor.professorName}20 {match.professorId !== undefined ? match.professorName : match.subjectName} 21 21 </SearchResultText> 22 22 <SearchResultText weight="normal" size="er"> 23 { professor.faculty.facultyName}23 {match.professorId !== undefined ? match.faculty.facultyName : match.studyProgramme.faculty.facultyName} 24 24 </SearchResultText> 25 25 </SearchResultLink> -
reactapp/src/Pages/Subject.js
r4abf55a r9bf1f8d 33 33 let params = useParams(); 34 34 let navigate = useNavigate(); 35 36 35 const { auth, setAuth } = useContext(AuthApi); 36 37 37 const [subject, setSubject] = useState(null); 38 const [loaded, setLoaded] = useState(false); 38 const [loadedSubject, setLoadedSubject] = useState(false); 39 const [threads, setThreads] = useState(null); 40 const [loadedThreads, setLoadedThreads] = useState(false); 39 41 const [fetchError, setFetchError] = useState(false); 42 40 43 var totalTopics = 0; 41 44 var topics = []; … … 47 50 48 51 useEffect(() => { 49 const url = `http://192.168.0.29:8080/public/subject/${params.subjectId}`; 50 51 const fetchData = async () => { 52 try { 53 const response = await fetch(url); 54 let cyclicGraph = await response.json(); 55 let jsogStructure = JSOG.encode(cyclicGraph); 56 cyclicGraph = JSOG.decode(jsogStructure); 57 setSubject(cyclicGraph); 58 setLoaded(true); 59 } catch (error) { 60 setFetchError(true); 61 } 62 }; 63 64 fetchData(); 65 }, [params.subjectId]); 52 Promise.all([fetch(`http://192.168.1.254:8080/public/subject/${params.subjectId}`), 53 fetch(`http://192.168.1.254:8080/public/subject/${params.subjectId}/threads`)]) 54 .then(([resSubject, resThreads]) => Promise.all([resSubject.json(), resThreads.json()])) 55 .then(([dataSubject, dataThreads]) => { 56 let cyclicGraph1 = dataSubject; 57 let jsogStructure1 = JSOG.encode(cyclicGraph1); 58 cyclicGraph1 = JSOG.decode(jsogStructure1); 59 setSubject(cyclicGraph1); 60 setLoadedSubject(true); 61 62 let cyclicGraph2 = dataThreads; 63 let jsogStructure2 = JSOG.encode(cyclicGraph2); 64 cyclicGraph2 = JSOG.decode(jsogStructure2); 65 setThreads(cyclicGraph2); 66 setLoadedThreads(true); 67 }) 68 69 }, []); 66 70 67 71 const handleAddTopicButtonClick = () => { … … 84 88 if (!topicTitle.length < 1 && !topicContent.length < 1) { 85 89 const response = await axios( 86 `http://192.168. 0.29:8080/secure/subject/${params.subjectId}/addThread`,90 `http://192.168.1.254:8080/secure/subject/${params.subjectId}/addThread`, 87 91 { 88 92 method: "post", … … 109 113 }; 110 114 111 return loaded ? (115 return loadedSubject ? ( 112 116 <> 113 117 <CurrentPageNav> … … 151 155 }} 152 156 > 153 { subject.threads.map((thread) => {157 {threads.map((thread) => { 154 158 if (thread.parent === null) { 155 159 totalTopics++; … … 210 214 <EntityUl key={topic.postId}> 211 215 <EntityLi bgcolor="cornsilk"> 212 <a href={"/topic/" + topic.postId}>{topic.title }</a>216 <a href={"/topic/" + topic.postId}>{topic.title.length >= 99 ? topic.title.slice(0,98) + "..." : topic.title}</a> 213 217 <EntityParam right="30px"> 214 218 <span style={{ fontWeight: "normal" }}> -
reactapp/src/Pages/Topic.js
r4abf55a r9bf1f8d 43 43 const [postModalDisplay, setPostModalDisplay] = useState("none"); 44 44 const [postContent, setPostContent] = useState(""); 45 45 46 const [replyModalDisplay, setReplyModalDisplay] = useState("none"); 46 47 const [replyContent, setReplyContent] = useState(""); 47 const [postForModal, setPostForModal] = useState(null); 48 const [postForReplyModal, setPostForReplyModal] = useState(null); 49 50 const [reportModalDisplay, setReportModalDisplay] = useState("none"); 51 const [reportContent, setReportContent] = useState(""); 52 const [postForReportModal, setPostForReportModal] = useState(null); 53 48 54 const [errorMessage, setErrorMessage] = useState(""); 49 55 50 56 useEffect(() => { 51 const url1 = `http://192.168. 0.29:8080/public/thread/${params.topicId}`;52 const url2 = `http://192.168. 0.29:8080/secure/currentUser`;57 const url1 = `http://192.168.1.254:8080/public/thread/${params.topicId}`; 58 const url2 = `http://192.168.1.254:8080/secure/currentUser`; 53 59 54 60 const fetchTopic = async () => { … … 79 85 80 86 fetchTopic().then(fetchUser); 81 }, [ ]);87 }, [loadedThread]); 82 88 83 89 const handleReply = (post) => { 84 90 if (auth) { 85 91 setReplyModalDisplay("block"); 86 setPostForModal(post); 92 setPostForReplyModal(post); 93 document.body.style.overflowY = "hidden"; 94 } else { 95 navigate("/login"); 96 } 97 }; 98 99 const handleReport = (post) => { 100 if (auth) { 101 setReportModalDisplay("block"); 102 setPostForReportModal(post); 87 103 document.body.style.overflowY = "hidden"; 88 104 } else { … … 95 111 }; 96 112 113 const handleReportContentChange = (e) => { 114 e.preventDefault(); 115 setReportContent(e.target.value); 116 }; 117 97 118 const handleReplySubmit = async (e, postId) => { 98 119 e.preventDefault(); … … 100 121 if (!replyContent.length < 1) { 101 122 const response = await axios( 102 `http://192.168. 0.29:8080/secure/subject/${thread.targetSubject.subjectId}/replyToThread/${postId}`,123 `http://192.168.1.254:8080/secure/subject/${thread.targetSubject.subjectId}/replyToThread/${postId}`, 103 124 { 104 125 method: "post", … … 116 137 }; 117 138 139 const handleReportSubmit = async (e, postId) => { 140 e.preventDefault(); 141 142 if (!reportContent.length < 1) { 143 const response = await axios( 144 `http://192.168.1.254:8080/secure/reportThread/${postId}`, 145 { 146 method: "post", 147 data: { 148 description: reportContent, 149 }, 150 withCredentials: true, 151 } 152 ); 153 setErrorMessage(""); 154 window.location.reload(); 155 } else { 156 setErrorMessage("Полето за содржина не смее да биде празно"); 157 } 158 }; 159 118 160 const handleAddOpinionButtonClick = () => { 119 161 if (auth) { … … 129 171 if (!postContent.length < 1) { 130 172 const response = await axios( 131 `http://192.168. 0.29:8080/secure/subject/${thread.targetSubject.subjectId}/replyToThread/${params.topicId}`,173 `http://192.168.1.254:8080/secure/subject/${thread.targetSubject.subjectId}/replyToThread/${params.topicId}`, 132 174 { 133 175 method: "post", … … 147 189 setPostModalDisplay("none"); 148 190 setReplyModalDisplay("none"); 191 setReportModalDisplay("none"); 149 192 document.body.style.overflowY = "auto"; 150 193 }; … … 161 204 ) { 162 205 const response = await axios( 163 `http://192.168. 0.29:8080/secure/upvoteThread/${post.postId}`,206 `http://192.168.1.254:8080/secure/upvoteThread/${post.postId}`, 164 207 { 165 208 method: "get", … … 184 227 ) { 185 228 const response = await axios( 186 `http://192.168. 0.29:8080/secure/downvoteThread/${post.postId}`,229 `http://192.168.1.254:8080/secure/downvoteThread/${post.postId}`, 187 230 { 188 231 method: "get", … … 280 323 color="darkgrey" 281 324 onClick={() => handleReply(child)} 325 /> 326 327 <StyledFontAwesomeIcon 328 icon={solid("flag")} 329 right={130 + "px"} 330 color="darkgrey" 331 onClick={() => handleReport(child)} 282 332 /> 283 333 </div> … … 393 443 > 394 444 <StyledFontAwesomeIcon 445 icon={solid("flag")} 446 right={90 + "px"} 447 color="darkgrey" 448 onClick={() => handleReport(thread)} 449 /> 450 <StyledFontAwesomeIcon 395 451 icon={solid("thumbs-up")} 396 452 right={50 + "px"} … … 517 573 onClick={() => handleReply(directChild)} 518 574 /> 575 576 <StyledFontAwesomeIcon 577 icon={solid("flag")} 578 right={130 + "px"} 579 color="darkgrey" 580 onClick={() => handleReport(directChild)} 581 /> 519 582 </div> 520 583 </OpinionCardContent> … … 525 588 ); 526 589 })} 527 {postFor Modal && (590 {postForReplyModal && ( 528 591 <Modal display={replyModalDisplay}> 529 592 <ModalContent> … … 531 594 <ModalClose onClick={handleModalCloseClick}>×</ModalClose> 532 595 <h3 style={{ marginTop: "5px" }}> 533 Реплика на {postFor Modal.author.username}596 Реплика на {postForReplyModal.author.username} 534 597 </h3> 535 598 </ModalHeader> 536 <form onSubmit={(e) => handleReplySubmit(e, postFor Modal.postId)}>599 <form onSubmit={(e) => handleReplySubmit(e, postForReplyModal.postId)}> 537 600 <ModalBody> 538 601 <label htmlFor="content"> … … 557 620 </Modal> 558 621 )} 622 {postForReportModal && ( 623 <Modal display={reportModalDisplay}> 624 <ModalContent> 625 <ModalHeader> 626 <ModalClose onClick={handleModalCloseClick}>×</ModalClose> 627 <h3 style={{ marginTop: "5px" }}> 628 Пријава за мислење #{postForReportModal.postId} 629 </h3> 630 </ModalHeader> 631 <form onSubmit={(e) => handleReportSubmit(e, postForReportModal.postId)}> 632 <ModalBody> 633 <label htmlFor="content"> 634 <b>Наведете причина</b>: 635 <ModalTextarea 636 id="content" 637 rows="8" 638 cols="100" 639 value={reportContent} 640 onChange={handleReportContentChange} 641 /> 642 </label> 643 </ModalBody> 644 <p 645 style={{ color: "red", marginLeft: "15px", marginTop: "10px" }} 646 > 647 {errorMessage} 648 </p> 649 <ModalFooter type="submit">ПРИЈАВИ</ModalFooter> 650 </form> 651 </ModalContent> 652 </Modal> 653 )} 559 654 </> 560 655 ) : !fetchError && !loadedThread ? ( -
reactapp/src/Pages/University.js
r4abf55a r9bf1f8d 18 18 const University = () => { 19 19 let params = useParams(); 20 const [loaded, setLoaded] = useState(false); 20 21 21 const [faculties, setFaculties] = useState(null); 22 const [loadedFaculties, setLoadedFaculties] = useState(false); 23 24 const [counts, setCounts] = useState(null); 25 const [loadedCounts, setLoadedCounts] = useState(false); 26 22 27 const [fetchError, setFetchError] = useState(false); 23 28 24 29 useEffect(() => { 25 const url = `http://192.168.0.29:8080/public/faculties?universityId=${params.universityId}`; 30 Promise.all([fetch(`http://192.168.1.254:8080/public/faculties?universityId=${params.universityId}`), 31 fetch(`http://192.168.1.254:8080/public/university/${params.universityId}/sectionAndPostCount`)]) 32 .then(([resFaculties, counts]) => Promise.all([resFaculties.json(), counts.json()])) 33 .then(([dataFaculties, dataCounts]) => { 34 let cyclicGraph1 = dataFaculties; 35 let jsogStructure1 = JSOG.encode(cyclicGraph1); 36 cyclicGraph1 = JSOG.decode(jsogStructure1); 37 setFaculties(cyclicGraph1); 38 setLoadedFaculties(true); 26 39 27 const fetchData = async () => { 28 try { 29 const response = await fetch(url); 30 var cyclicGraph = await response.json(); 31 var jsogStructure = JSOG.encode(cyclicGraph); 32 cyclicGraph = JSOG.decode(jsogStructure); 33 setFaculties(cyclicGraph); 34 setLoaded(true); 35 } catch (error) { 36 setFetchError(true); 37 } 38 }; 39 fetchData(); 40 }, [params.universityId]); 40 let cyclicGraph2 = dataCounts; 41 let jsogStructure2 = JSOG.encode(cyclicGraph2); 42 cyclicGraph2 = JSOG.decode(jsogStructure2); 43 setCounts(cyclicGraph2); 44 setLoadedCounts(true); 45 }) 41 46 42 return loaded && !fetchError && faculties.length !== 0 ? ( 47 }, []); 48 49 return loadedFaculties && !fetchError && faculties.length !== 0 ? ( 43 50 <> 44 51 <CurrentPageNav> … … 55 62 </ProfessorCard> 56 63 <div key={params.universityId}> 57 {faculties.map((faculty) => { 58 let totalPosts = 0; 59 let totalSections = 0; 60 faculty.professors.map((professor) => { 61 totalPosts += professor.relatedOpinions.length; 62 totalSections++; 63 }); 64 faculty.studyProgrammes.map((studyProgramme) => { 65 studyProgramme.subjects.map((subject) => { 66 totalPosts += subject.threads.length; 67 totalSections++; 68 }); 69 }); 70 64 {faculties.map((faculty, idx) => { 65 let totalPosts = parseInt(counts[idx].split(",")[2]); 66 let totalSections = parseInt(counts[idx].split(",")[1]); 67 console.log(counts) 71 68 return ( 72 69 <EntityUl key={faculty.facultyId}> … … 104 101 </div> 105 102 </> 106 ) : !fetchError && !loaded ? (103 ) : !fetchError && !loadedFaculties ? ( 107 104 <div> 108 105 <LoadingSpinner style={{ marginTop: "140px" }}/> -
reactapp/src/Pages/UserDashboard.js
r4abf55a r9bf1f8d 27 27 } from "../Components/Styled/Modal.style"; 28 28 import LoadingSpinner from "../Components/Styled/LoadingSpinner.style"; 29 import {findParentThread} from "../Util/findParentThread"; 29 30 30 31 function UserDashboard() { … … 33 34 const [user, setUser] = useState(null); 34 35 const [loadedUser, setLoadedUser] = useState(false); 36 const [authoredPosts, setAuthoredPosts] = useState(null); 37 const [loadedAuthoredPosts, setLoadedAuthoredPosts] = useState(false); 38 35 39 const [fetchError, setFetchError] = useState(false); 36 40 … … 45 49 const [newPostContent, setNewPostContent] = useState(""); 46 50 const [newOpinionTargetProfessorId, setNewOpinionTargetProfessorId] = useState(""); 47 const [newOpinionTargetProfessor, setNewOpinionTargetProfessor] = useState(null);48 const [loadedNewProfessor,setLoadedNewProfessor] = useState(false);49 51 const [newParentPostId, setNewParentPostId] = useState("-1"); 50 52 … … 52 54 const [newParentThreadId,setNewParentThreadId] = useState("-1"); 53 55 const [newTargetSubjectId, setNewTargetSubjectId] = useState(""); 54 const [newTargetSubject, setNewTargetSubject] = useState(null); 55 const [loadedNewSubject, setLoadedNewSubject] = useState(null); 56 56 57 57 58 const [markResolved, setMarkResolved] = useState(false); … … 83 84 }, [reportForModal]); 84 85 85 const[loadingProf, setLoadingProf] = useState(false); 86 const [newOpinionTargetProfessor, setNewOpinionTargetProfessor] = useState(null); 87 const [loadedNewProfessor,setLoadedNewProfessor] = useState(false); 88 const [newProfessorRelatedOpinions, setNewProfessorRelatedOpinions] = useState(null); 89 const [loadedNewProfessorRelatedOpinions, setLoadedNewProfessorRelatedOpinions] = useState(false); 90 const [loadingProf, setLoadingProf] = useState(false); 86 91 87 92 const handleNewTargetProfessorChange = async (e) => { … … 90 95 if (newOpinionTargetProfessorId!=="") { 91 96 try { 92 const response = await axios.get(`http://192.168.0.29:8080/public/professor/${newOpinionTargetProfessorId}`, {withCredentials: true}); 93 let cyclicGraph = await response.data; 94 var jsogStructure = JSOG.encode(cyclicGraph); 95 cyclicGraph = JSOG.decode(jsogStructure); 96 setNewOpinionTargetProfessor(cyclicGraph); 97 setLoadedNewProfessor(true); 98 setLoadingProf(false); 97 Promise.all([fetch(`http://192.168.1.254:8080/public/professor/${newOpinionTargetProfessorId}`), 98 fetch(`http://192.168.1.254:8080/public/professor/${newOpinionTargetProfessorId}/relatedOpinions`)]) 99 .then(([resNewOpinionTargetProfessor, resNewProfessorRelatedOpinions]) => Promise.all([resNewOpinionTargetProfessor.json(), resNewProfessorRelatedOpinions.json()])) 100 .then(([dataNewOpinionTargetProfessor, dataNewProfessorRelatedOpinions]) => { 101 let cyclicGraph1 = dataNewOpinionTargetProfessor; 102 var jsogStructure1 = JSOG.encode(cyclicGraph1); 103 cyclicGraph1 = JSOG.decode(jsogStructure1); 104 setNewOpinionTargetProfessor(cyclicGraph1); 105 setLoadedNewProfessor(true); 106 107 let cyclicGraph2 = dataNewProfessorRelatedOpinions; 108 var jsogStructure2 = JSOG.encode(cyclicGraph2); 109 cyclicGraph2 = JSOG.decode(jsogStructure2); 110 setNewProfessorRelatedOpinions(cyclicGraph2); 111 setLoadedNewProfessorRelatedOpinions(true); 112 113 setLoadingProf(false); 114 }) 99 115 } catch (error) { 100 116 setFetchError(true); … … 103 119 } 104 120 105 const[loadingSubj, setLoadingSubj] = useState(false); 121 const [newTargetSubject, setNewTargetSubject] = useState(null); 122 const [loadedNewSubject, setLoadedNewSubject] = useState(false); 123 const [newTargetSubjectThreads, setNewTargetSubjectThreads] = useState(null); 124 const [loadedNewSubjectThreads, setLoadedNewSubjectThreads] = useState(false); 125 const [loadingSubj, setLoadingSubj] = useState(false); 106 126 107 127 const handleNewTargetSubjectChange = async (e) => { … … 110 130 if (newTargetSubjectId!=="") { 111 131 try { 112 const response = await axios.get(`http://192.168.0.29:8080/public/subject/${newTargetSubjectId}`, {withCredentials: true}); 113 let cyclicGraph = await response.data; 114 var jsogStructure = JSOG.encode(cyclicGraph); 115 cyclicGraph = JSOG.decode(jsogStructure); 116 setNewTargetSubject(cyclicGraph); 117 setLoadedNewSubject(true); 118 setLoadingSubj(false); 119 } catch (error) { 120 setFetchError(true); 121 } 132 Promise.all([fetch(`http://192.168.1.254:8080/public/subject/${newTargetSubjectId}`), 133 fetch(`http://192.168.1.254:8080/public/subject/${newTargetSubjectId}/threads`)]) 134 .then(([resNewTargetSubject, resNewTargetSubjectThreads]) => Promise.all([resNewTargetSubject.json(), resNewTargetSubjectThreads.json()])) 135 .then(([dataNewTargetSubject, dataNewTargetSubjectThreads]) => { 136 let cyclicGraph1 = dataNewTargetSubject; 137 var jsogStructure1 = JSOG.encode(cyclicGraph1); 138 cyclicGraph1 = JSOG.decode(jsogStructure1); 139 setNewTargetSubject(cyclicGraph1); 140 setLoadedNewSubject(true); 141 142 let cyclicGraph2 = dataNewTargetSubjectThreads; 143 var jsogStructure2 = JSOG.encode(cyclicGraph2); 144 cyclicGraph2 = JSOG.decode(jsogStructure2); 145 setNewTargetSubjectThreads(cyclicGraph2); 146 setLoadedNewSubjectThreads(true); 147 148 setLoadingSubj(false); 149 }) 150 } catch (error) { 151 setFetchError(true); 152 } 122 153 } 123 154 } 124 155 125 156 useEffect(() => { 126 const url1 = `http://192.168.0.29:8080/secure/currentUser`; 127 const url2 = `http://192.168.0.29:8080/secure/getAllPostReports`; 128 129 const fetchUser = async () => { 157 const fetchUser = () => { 130 158 try { 131 159 if(!loadedUser) { 132 const response = await axios.get(url1, {withCredentials: true}); 133 let cyclicGraph = await response.data; 134 var jsogStructure = JSOG.encode(cyclicGraph); 135 cyclicGraph = JSOG.decode(jsogStructure); 136 setUser(cyclicGraph); 137 setLoadedUser(true); 160 Promise.all([axios.get(`http://192.168.1.254:8080/secure/currentUser`, {withCredentials:true}), 161 axios.get(`http://192.168.1.254:8080/secure/currentUser/posts`, {withCredentials:true})]) 162 .then(([resUser, resAuthoredPosts]) => Promise.all([resUser.data, resAuthoredPosts.data])) 163 .then(([dataUser, dataAuthoredPosts]) => { 164 let cyclicGraph1 = dataUser; 165 let jsogStructure1 = JSOG.encode(cyclicGraph1); 166 cyclicGraph1 = JSOG.decode(jsogStructure1); 167 setUser(cyclicGraph1); 168 setLoadedUser(true); 169 170 let cyclicGraph2 = dataAuthoredPosts; 171 let jsogStructure2 = JSOG.encode(cyclicGraph2); 172 cyclicGraph2 = JSOG.decode(jsogStructure2); 173 setAuthoredPosts(cyclicGraph2); 174 setLoadedAuthoredPosts(true); 175 }) 138 176 } 139 if(user.userRole==='MODERATOR') fetchPostReports();177 if(user.userRole==='MODERATOR') fetchPostReports(); 140 178 } catch (error) { 141 179 setFetchError(true); … … 145 183 const fetchPostReports = async () => { 146 184 try { 147 const response = await axios.get( url2, {withCredentials: true});185 const response = await axios.get(`http://192.168.1.254:8080/secure/getAllPostReports`, {withCredentials: true}); 148 186 var cyclicGraph = await response.data; 149 187 var jsogStructure = JSOG.encode(cyclicGraph); … … 160 198 }, [user]); 161 199 162 // useEffect(() => {163 // const timer = setTimeout(() => {164 // if (user === null) window.location.reload(); <---- :-)165 // }, 3000);166 // return () => clearTimeout(timer);167 // }, []);168 169 function findParentThread(post) {170 if (post.parent === null) return post;171 return findParentThread(post.parent);172 }173 174 200 const handleEdit = async (e) => { 175 201 e.preventDefault(); 176 202 try { 177 203 if(reportForModal.post !== null && reportForModal.post.targetProfessor !== undefined) { 178 await axios(`http://192.168. 0.29:8080/secure/updateOpinion/${reportForModal.post.postId}`,204 await axios(`http://192.168.1.254:8080/secure/updateOpinion/${reportForModal.post.postId}`, 179 205 { 180 206 method: "put", … … 186 212 withCredentials: true, 187 213 }) 214 await axios(`http://192.168.1.254:8080/secure/markReportResolved/${reportForModal.postReportId}/${markResolved ? `resolve` : `open`}`,{ 215 method: "get", 216 withCredentials: true 217 }) 188 218 } else if(reportForModal.post !== null && reportForModal.post.targetProfessor === undefined) { 189 await axios(`http://192.168. 0.29:8080/secure/updateThread/${reportForModal.post.postId}`,219 await axios(`http://192.168.1.254:8080/secure/updateThread/${reportForModal.post.postId}`, 190 220 { 191 221 method: "put", … … 199 229 }) 200 230 } 201 await axios(`http://192.168.0.29:8080/secure/markReportResolved/${reportForModal.postReportId}/${markResolved ? `resolve` : `open`}`,{231 await axios(`http://192.168.1.254:8080/secure/markReportResolved/${reportForModal.postReportId}/${markResolved ? `resolve` : `open`}`,{ 202 232 method: "get", 203 233 withCredentials: true 204 234 }) 205 235 } catch (error) { 206 236 setFetchError(true); … … 213 243 try { 214 244 if(reportForModal.post !== null && reportForModal.post.targetProfessor !== undefined) { 215 var response = await axios(`http://192.168. 0.29:8080/secure/updateOpinion/${reportForModal.post.postId}`,245 var response = await axios(`http://192.168.1.254:8080/secure/updateOpinion/${reportForModal.post.postId}`, 216 246 { 217 247 method: "put", … … 223 253 withCredentials: true, 224 254 }) 255 await axios(`http://192.168.1.254:8080/secure/markReportResolved/${reportForModal.postReportId}/${markResolved ? `resolve` : `open`}`,{ 256 method: "get", 257 withCredentials: true 258 }) 225 259 } else if(reportForModal.post !== null && reportForModal.post.targetProfessor === undefined) { 226 var response = await axios(`http://192.168. 0.29:8080/secure/updateThread/${reportForModal.post.postId}`,260 var response = await axios(`http://192.168.1.254:8080/secure/updateThread/${reportForModal.post.postId}`, 227 261 { 228 262 method: "put", … … 236 270 }) 237 271 } 238 await axios(`http://192.168. 0.29:8080/secure/markReportResolved/${reportForModal.postReportId}/${markResolved ? `resolve` : `open`}`,{272 await axios(`http://192.168.1.254:8080/secure/markReportResolved/${reportForModal.postReportId}/${markResolved ? `resolve` : `open`}`,{ 239 273 method: "get", 240 274 withCredentials: true … … 251 285 try { 252 286 if(reportForModal.post !== null && reportForModal.post.targetProfessor !== undefined) { 253 await axios(`http://192.168. 0.29:8080/secure/deleteOpinion/${reportForModal.post.postId}`,287 await axios(`http://192.168.1.254:8080/secure/deleteOpinion/${reportForModal.post.postId}`, 254 288 { 255 289 method: "delete", … … 258 292 window.location.reload(); 259 293 } else if(reportForModal.post !== null && reportForModal.post.targetProfessor === undefined) { 260 await axios(`http://192.168. 0.29:8080/secure/deleteThread/${reportForModal.post.postId}`,294 await axios(`http://192.168.1.254:8080/secure/deleteThread/${reportForModal.post.postId}`, 261 295 { 262 296 method: "delete", … … 312 346 })} 313 347 </EntityUl> 314 { user.authoredPosts.length > 0 ? (348 {authoredPosts.length > 0 ? ( 315 349 <h3 style={{ marginBottom: "10px", marginTop:"30px" }}>Ваши мислења:</h3> 316 350 ) : ( 317 351 <h3 style={{ marginBottom: "10px" }}>Немате објавени мислења</h3> 318 352 )} 319 { user.authoredPosts.map((post) => {353 {authoredPosts.map((post) => { 320 354 return ( 321 355 <div key={post.postId}> … … 527 561 во која треба да биде преместено мислењето:</p> 528 562 <div style={{marginTop:"15px"}}> 529 <label>530 563 <ModalInput 531 564 value={newOpinionTargetProfessorId} … … 542 575 <select value={newParentPostId} onChange={e => setNewParentPostId(e.target.value)} style={{width:"280px", display:"block", padding:"5px",marginBottom:"5px", fontFamily: "Roboto Mono, monospace"}}> 543 576 <option value="-1">Постави како самостојно мислење</option> 544 {new OpinionTargetProfessor.relatedOpinions.filter((opinion)=>opinion.postId!==reportForModal.post.postId).map((opinion) => {577 {newProfessorRelatedOpinions.filter((opinion)=>opinion.postId!==reportForModal.post.postId).map((opinion) => { 545 578 return <option key={opinion.postId} value={opinion.postId}>{opinion.postId}</option>}) 546 579 } 547 580 </select>} 548 581 <br/> 549 < input550 type="checkbox"551 defaultChecked={reportForModal.resolved}552 onChange={handleMarkResolved}553 />554 <span style={{marginLeft:"10px", fontWeight:"bold"}}>Означи како разрешено</span>555 </label>582 <label> 583 <input 584 type="checkbox" 585 onChange={handleMarkResolved} 586 /> 587 <span style={{marginLeft:"10px", fontWeight:"bold"}}>Означи како разрешено</span> 588 </label> 556 589 </div> 557 590 {errMsg!=="" && <p style={{color:"red", display:"flex", justifyContent:"space-around"}}>{errMsg}</p>} … … 578 611 <select value={newParentThreadId} onChange={e => setNewParentThreadId(e.target.value)} style={{width:"370px", display:"block", padding:"5px",marginBottom:"5px", fontFamily: "Roboto Mono, monospace"}}> 579 612 <option value="-1">Постави како самостојно мислење (нова тема)</option> 580 {newTargetSubject .threads.filter((thread)=>thread.postId!==reportForModal.post.postId).map((thread) => {613 {newTargetSubjectThreads.filter((thread)=>thread.postId!==reportForModal.post.postId).map((thread) => { 581 614 return <option key={thread.postId} value={thread.postId}>{thread.postId}</option>}) 582 615 } -
reactapp/src/api/axios.js
r4abf55a r9bf1f8d 2 2 3 3 export default axios.create({ 4 baseURL: "http://192.168. 0.29:8080",4 baseURL: "http://192.168.1.254:8080", 5 5 }); -
springapp/src/main/java/mk/profesori/springapp/Controller/PublicController.java
r4abf55a r9bf1f8d 1 1 package mk.profesori.springapp.Controller; 2 3 import mk.profesori.springapp.Model.*; 4 import mk.profesori.springapp.Service.MainService; 5 import org.springframework.beans.factory.annotation.Autowired; 6 import org.springframework.web.bind.annotation.*; 2 7 3 8 import java.util.Collections; … … 6 11 import java.util.Optional; 7 12 8 import org.springframework.beans.factory.annotation.Autowired;9 import org.springframework.web.bind.annotation.CrossOrigin;10 import org.springframework.web.bind.annotation.PathVariable;11 import org.springframework.web.bind.annotation.RequestMapping;12 import org.springframework.web.bind.annotation.RequestMethod;13 import org.springframework.web.bind.annotation.RequestParam;14 import org.springframework.web.bind.annotation.RestController;15 16 import mk.profesori.springapp.Model.City;17 import mk.profesori.springapp.Model.Faculty;18 import mk.profesori.springapp.Model.Professor;19 import mk.profesori.springapp.Model.StudyProgramme;20 import mk.profesori.springapp.Model.Subject;21 import mk.profesori.springapp.Model.University;22 import mk.profesori.springapp.Model._Thread;23 import mk.profesori.springapp.Service.MainService;24 25 13 @RestController 26 14 @RequestMapping("/public") 27 @CrossOrigin(origins = { "http://192.168. 0.29:3000", "http://192.168.0.28:3000" })15 @CrossOrigin(origins = { "http://192.168.1.254:3000", "http://192.168.0.28:3000" }) 28 16 public class PublicController { 29 17 … … 42 30 @RequestMapping(value = "/professors/nameContains/{contained}", method = RequestMethod.GET) 43 31 public List<Professor> getProfessorsByNameContains(@PathVariable String contained) { 44 return mainService.getProfessorsByNameContains(contained); // vrakja profesori sto sodrzat "contained" vo 45 // professorName 32 return mainService.getProfessorsByNameContains(contained); // vrakja profesori sto sodrzat "contained" vo imeto 33 34 } 35 36 @RequestMapping(value = "/subjects/nameContains/{contained}", method = RequestMethod.GET) 37 public List<Subject> getSubjectsByNameContains(@PathVariable String contained) { 38 return mainService.getSubjectsByNameContains(contained); // vrakja predmeti sto sodrzat "contained" vo imeto 46 39 } 47 40 … … 49 42 public Professor getProfessorById(@PathVariable Long professorId) { 50 43 return mainService.getProfessorById(professorId); // vrakja profesor spored id 44 } 45 46 @RequestMapping(value="/professor/{professorId}/relatedOpinions", method = RequestMethod.GET) 47 public List<Opinion> getRelatedOpinions(@PathVariable Long professorId) { 48 return mainService.getRelatedOpinions(professorId); 51 49 } 52 50 … … 105 103 } 106 104 105 @RequestMapping(value = "/subjects", method = RequestMethod.GET) 106 public List<Subject> getSubjectsByStudyProgramme(@RequestParam Long studyProgrammeId) { 107 return mainService.getSubjectsByStudyProgramme(studyProgrammeId); 108 } 109 107 110 @RequestMapping(value = "/thread/{postId}", method = RequestMethod.GET) 108 111 public _Thread getThreadById(@PathVariable Long postId) { … … 119 122 return Collections.singletonMap("sessionId", sessionId); 120 123 } 124 125 @RequestMapping(value = "/latest10opinions", method = RequestMethod.GET) 126 public List<Opinion> latest10opinions() { 127 return mainService.getLatest10Opinions(); 128 } 129 130 @RequestMapping(value = "/latest10threads", method = RequestMethod.GET) 131 public List<_Thread> latest10threads() { 132 return mainService.getLatest10Threads(); 133 } 134 135 @RequestMapping(value = "/subject/{id}/threads") 136 public List<_Thread> getThreadsBySubject(@PathVariable Long id) { 137 return mainService.getThreadsBySubject(id); 138 } 139 140 @RequestMapping(value = "/university/{id}/sectionAndPostCount") 141 public List<String> getUniversitySectionCount(@PathVariable Long id) { 142 return mainService.getUniversitySectionAndPostCount(); 143 } 144 145 @RequestMapping(value = "/faculty/{id}/opinionCountForEachProfessor") 146 public List<String> getOpinionCountForEachProfessorInFaculty(@PathVariable Long id) { 147 return mainService.getOpinionCountForEachProfessorInFaculty(id); 148 } 149 150 @RequestMapping(value = "/study_programme/{id}/threadCountForEachSubject") 151 public List<String> getThreadCountForEachSubjectInStudyProgramme(@PathVariable Long id) { 152 return mainService.getThreadCountForEachSubjectInStudyProgramme(id); 153 } 154 155 @RequestMapping(value = "/user/{id}") 156 public CustomUserDetails getPublicUserProfile(@PathVariable Long id) { 157 return mainService.getPublicUserProfile(id); 158 } 159 121 160 } -
springapp/src/main/java/mk/profesori/springapp/Controller/SecureController.java
r4abf55a r9bf1f8d 3 3 import com.fasterxml.jackson.databind.node.ObjectNode; 4 4 import mk.profesori.springapp.Model.CustomUserDetails; 5 import mk.profesori.springapp.Model.Post; 5 6 import mk.profesori.springapp.Model.PostReport; 6 7 import mk.profesori.springapp.Model.UserRole; 7 8 import mk.profesori.springapp.Service.CustomUserDetailsService; 8 import mk.profesori.springapp.Service. DisallowedOperationException;9 import mk.profesori.springapp.Service. IncompatiblePostId;9 import mk.profesori.springapp.Service.Exception.DisallowedOperationException; 10 import mk.profesori.springapp.Service.Exception.IncompatiblePostId; 10 11 import mk.profesori.springapp.Service.MainService; 11 12 import org.apache.tomcat.websocket.AuthenticationException; … … 17 18 18 19 import java.util.List; 20 import java.util.Set; 19 21 20 22 @RestController 21 23 @RequestMapping("/secure") 22 @CrossOrigin(origins = { "http://192.168. 0.29:3000", "http://192.168.0.28:3000" })24 @CrossOrigin(origins = { "http://192.168.1.254:3000", "http://192.168.0.28:3000" }) 23 25 public class SecureController { 24 26 … … 77 79 if (authentication != null && authentication.getPrincipal() instanceof CustomUserDetails currentUser) { 78 80 return customUserDetailsService.loadUserByUsername(currentUser.getEmail()); 81 } 82 return null; 83 } 84 85 @RequestMapping(value = "/currentUser/posts", method = RequestMethod.GET) 86 public Set<Post> getPostsByUser(@CurrentSecurityContext SecurityContext context) { 87 Authentication authentication = context.getAuthentication(); 88 if (authentication != null && authentication.getPrincipal() instanceof CustomUserDetails currentUser) { 89 return mainService.getPostsByUser(currentUser.getEmail()); 79 90 } 80 91 return null; -
springapp/src/main/java/mk/profesori/springapp/Model/City.java
r4abf55a r9bf1f8d 1 1 package mk.profesori.springapp.Model; 2 2 3 import com.fasterxml.jackson.annotation.JsonIdentityInfo; 4 import com.voodoodyne.jackson.jsog.JSOGGenerator; 5 import lombok.Data; 6 7 import javax.persistence.*; 3 8 import java.util.HashSet; 4 9 import java.util.Set; 5 10 6 import javax.persistence.Column;7 import javax.persistence.Entity;8 import javax.persistence.GeneratedValue;9 import javax.persistence.GenerationType;10 import javax.persistence.Id;11 import javax.persistence.OneToMany;12 import javax.persistence.Table;13 14 import com.fasterxml.jackson.annotation.JsonIdentityInfo;15 import com.fasterxml.jackson.annotation.ObjectIdGenerators;16 17 11 @Entity 12 @Data 18 13 @Table(name = "city") 19 @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "cityId")14 @JsonIdentityInfo(generator = JSOGGenerator.class) 20 15 public class City { 21 16 … … 28 23 private String cityName; 29 24 25 @Transient 30 26 @OneToMany(mappedBy = "city") 31 27 private Set<University> universities = new HashSet<>(); 32 28 33 // getters34 public Long getCityId() {35 return cityId;36 }37 38 public String getCityName() {39 return cityName;40 }41 42 public Set<University> getUniversities() {43 return universities;44 }45 46 29 } -
springapp/src/main/java/mk/profesori/springapp/Model/CustomUserDetails.java
r4abf55a r9bf1f8d 2 2 3 3 import com.fasterxml.jackson.annotation.JsonIdentityInfo; 4 import com.fasterxml.jackson.annotation.JsonIgnore; 4 5 import com.voodoodyne.jackson.jsog.JSOGGenerator; 5 6 import lombok.EqualsAndHashCode; … … 31 32 private String fullName; 32 33 private String username; 34 @JsonIgnore 33 35 private String email; 34 private String password; // TODO 36 @JsonIgnore 37 private String password; 38 /* UseCases 39 го уредува својот кориснички профил 40 го верификува својот кориснички профил todo... 41 */ 35 42 @Enumerated(EnumType.STRING) 36 43 private UserRole userRole; 37 44 private Boolean locked = false; 38 45 private Boolean enabled = false; 39 @OneToMany(mappedBy = "customUserDetails", fetch = FetchType.EAGER, orphanRemoval = true) 46 @Transient 47 @OneToMany(mappedBy = "customUserDetails", orphanRemoval = true) 40 48 private Set<ConfirmationToken> confirmationTokens = new HashSet<>(); 41 @OneToMany(mappedBy = "author", fetch = FetchType.EAGER, orphanRemoval = true) 49 @Transient 50 @OneToMany(mappedBy = "author", orphanRemoval = true) 42 51 private Set<Post> authoredPosts = new HashSet<>(); 43 52 private Integer karma = 0; 44 53 54 @Transient 45 55 @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true) 46 56 private Set<PostVote> votes = new HashSet<>(); 47 57 58 @Transient 48 59 @OneToMany(mappedBy = "user", cascade = {CascadeType.PERSIST}) 49 60 private Set<PostReport> reportsSubmitted = new HashSet<>(); … … 99 110 } 100 111 101 public Set<Post> getAuthoredPosts() {102 return this.authoredPosts;103 }104 105 public Integer getKarma() {106 return this.karma;107 }108 109 public void setKarma(Integer karma) {110 this.karma = karma;111 }112 113 112 @Override 114 113 public String toString() { 115 114 return this.id.toString(); 116 115 } 117 118 public void setLocked(Boolean locked) {119 this.locked = locked;120 }121 122 public Set<PostVote> getVotes() {123 return votes;124 }125 126 public void setVotes(Set<PostVote> votes) {127 this.votes = votes;128 }129 116 } -
springapp/src/main/java/mk/profesori/springapp/Model/Faculty.java
r4abf55a r9bf1f8d 1 1 package mk.profesori.springapp.Model; 2 2 3 import com.fasterxml.jackson.annotation.JsonIdentityInfo; 4 import com.voodoodyne.jackson.jsog.JSOGGenerator; 5 import lombok.Data; 6 7 import javax.persistence.*; 3 8 import java.util.HashSet; 4 9 import java.util.Set; 5 10 6 import javax.persistence.Column;7 import javax.persistence.Entity;8 import javax.persistence.GeneratedValue;9 import javax.persistence.GenerationType;10 import javax.persistence.Id;11 import javax.persistence.JoinColumn;12 import javax.persistence.ManyToOne;13 import javax.persistence.OneToMany;14 import javax.persistence.Table;15 16 import com.fasterxml.jackson.annotation.JsonIdentityInfo;17 import com.voodoodyne.jackson.jsog.JSOGGenerator;18 19 11 @Entity 12 @Data 20 13 @Table(name = "faculty") 21 14 @JsonIdentityInfo(generator = JSOGGenerator.class) … … 34 27 private University university; 35 28 29 @Transient 36 30 @OneToMany(mappedBy = "faculty") 37 31 private Set<Professor> professors = new HashSet<>(); 38 32 33 @Transient 39 34 @OneToMany(mappedBy = "faculty") 40 35 private Set<StudyProgramme> studyProgrammes = new HashSet<>(); 41 36 42 // getters43 public Long getFacultyId() {44 return facultyId;45 }46 47 public String getFacultyName() {48 return facultyName;49 }50 51 public University getUniversity() {52 return university;53 }54 55 public Set<Professor> getProfessors() {56 return professors;57 }58 59 public Set<StudyProgramme> getStudyProgrammes() {60 return studyProgrammes;61 }62 63 37 } -
springapp/src/main/java/mk/profesori/springapp/Model/Opinion.java
r4abf55a r9bf1f8d 1 1 package mk.profesori.springapp.Model; 2 2 3 import java.time.LocalDateTime;4 import java.util.List;3 import lombok.Data; 4 import lombok.NoArgsConstructor; 5 5 6 6 import javax.persistence.DiscriminatorValue; … … 8 8 import javax.persistence.JoinColumn; 9 9 import javax.persistence.ManyToOne; 10 11 import lombok.NoArgsConstructor;10 import java.time.LocalDateTime; 11 import java.util.List; 12 12 13 13 @Entity 14 @Data 14 15 @DiscriminatorValue("opinion") 15 16 @NoArgsConstructor … … 35 36 this.targetProfessor = targetProfessor; 36 37 } 37 38 // getters and setters39 public Professor getTargetProfessor() {40 return targetProfessor;41 }42 public void setTargetProfessor(Professor targetProfessor) {43 this.targetProfessor = targetProfessor;44 }45 46 38 } -
springapp/src/main/java/mk/profesori/springapp/Model/Post.java
r4abf55a r9bf1f8d 3 3 import com.fasterxml.jackson.annotation.JsonIdentityInfo; 4 4 import com.voodoodyne.jackson.jsog.JSOGGenerator; 5 import lombok.Data; 5 6 import lombok.NoArgsConstructor; 6 7 … … 13 14 14 15 @Entity(name = "post") 16 @Data 15 17 @Inheritance(strategy = InheritanceType.SINGLE_TABLE) 16 18 @DiscriminatorColumn(name = "post_type", discriminatorType = DiscriminatorType.STRING) … … 44 46 private Post parent; 45 47 46 @OneToMany(mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true )48 @OneToMany(mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER) 47 49 private Set<PostVote> votes = new HashSet<>(); 48 50 49 @OneToMany(mappedBy = "post", cascade={CascadeType.PERSIST} )51 @OneToMany(mappedBy = "post", cascade={CascadeType.PERSIST}, fetch = FetchType.EAGER) 50 52 private Set<PostReport> reports = new HashSet<>(); 53 51 54 @PreRemove 52 55 public void preRemove() { … … 56 59 }); 57 60 } 58 59 60 @OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, orphanRemoval = true) 61 @OneToMany(mappedBy = "parent", orphanRemoval = true, fetch = FetchType.EAGER) 61 62 private List<Post> children = new ArrayList<>(); 62 63 // getters and setters64 public Set<PostVote> getVotes() {65 return votes;66 }67 68 public void setVotes(Set<PostVote> votes) {69 this.votes = votes;70 }71 public Long getPostId() {72 return postId;73 }74 75 public void setPostId(Long postId) {76 this.postId = postId;77 }78 79 public String getTitle() {80 return title;81 }82 83 public void setTitle(String title) {84 this.title = title;85 }86 87 public String getContent() {88 return content;89 }90 91 public void setContent(String content) {92 this.content = content;93 }94 95 public CustomUserDetails getAuthor() {96 return author;97 }98 99 public void setAuthor(CustomUserDetails author) {100 this.author = author;101 }102 103 public LocalDateTime getTimePosted() {104 return timePosted;105 }106 107 public void setTimePosted(LocalDateTime timePosted) {108 this.timePosted = timePosted;109 }110 111 public LocalDateTime getTimeLastEdited() {112 return timeLastEdited;113 }114 115 public void setTimeLastEdited(LocalDateTime timeLastEdited) {116 this.timeLastEdited = timeLastEdited;117 }118 119 public Post getParent() {120 return parent;121 }122 123 public void setParent(Post parent) {124 this.parent = parent;125 }126 127 public List<Post> getChildren() {128 return children;129 }130 131 public void setChildren(List<Post> children) {132 this.children = children;133 }134 63 135 64 // konstruktor so parent (koga e reply) -
springapp/src/main/java/mk/profesori/springapp/Model/PostReport.java
r4abf55a r9bf1f8d 1 1 package mk.profesori.springapp.Model; 2 2 3 import lombok.Data; 3 4 import lombok.NoArgsConstructor; 4 5 … … 7 8 8 9 @Entity 10 @Data 9 11 @NoArgsConstructor 10 12 public class PostReport { … … 38 40 this.resolved=false; 39 41 } 40 41 public boolean isResolved() {42 return resolved;43 }44 45 public void setResolved(boolean resolved) {46 this.resolved = resolved;47 }48 49 public Long getPostReportId() {50 return postReportId;51 }52 53 public void setPostReportId(Long postReportId) {54 this.postReportId = postReportId;55 }56 57 public CustomUserDetails getUser() {58 return user;59 }60 61 public void setUser(CustomUserDetails user) {62 this.user = user;63 }64 65 public Post getPost() {66 return post;67 }68 69 public void setPost(Post post) {70 this.post = post;71 }72 73 public LocalDateTime getTime() {74 return time;75 }76 77 public void setTime(LocalDateTime time) {78 this.time = time;79 }80 81 public String getDescription() {82 return description;83 }84 85 public void setDescription(String description) {86 this.description = description;87 }88 42 } -
springapp/src/main/java/mk/profesori/springapp/Model/PostVote.java
r4abf55a r9bf1f8d 1 1 package mk.profesori.springapp.Model; 2 2 3 import lombok.Data; 3 4 import lombok.NoArgsConstructor; 4 5 … … 7 8 8 9 @Entity 10 @Data 9 11 @NoArgsConstructor 10 12 public class PostVote { … … 34 36 this.vote = vote; 35 37 } 36 37 public Long getPostVoteId() {38 return postVoteId;39 }40 41 public void setPostVoteId(Long id) {42 this.postVoteId = id;43 }44 45 public CustomUserDetails getUser() {46 return user;47 }48 49 public void setUser(CustomUserDetails user) {50 this.user = user;51 }52 53 public Post getPost() {54 return post;55 }56 57 public void setPost(Post post) {58 this.post = post;59 }60 61 public LocalDateTime getTime() {62 return time;63 }64 65 public void setTime(LocalDateTime time) {66 this.time = time;67 }68 69 public VoteType getVote() {70 return vote;71 }72 73 public void setVote(VoteType vote) {74 this.vote = vote;75 }76 38 } -
springapp/src/main/java/mk/profesori/springapp/Model/Professor.java
r4abf55a r9bf1f8d 1 1 package mk.profesori.springapp.Model; 2 3 import java.util.ArrayList;4 import java.util.List;5 import javax.persistence.CascadeType;6 import javax.persistence.Column;7 import javax.persistence.Entity;8 import javax.persistence.GeneratedValue;9 import javax.persistence.GenerationType;10 import javax.persistence.Id;11 import javax.persistence.JoinColumn;12 import javax.persistence.ManyToOne;13 import javax.persistence.OneToMany;14 import javax.persistence.Table;15 2 16 3 import com.fasterxml.jackson.annotation.JsonIdentityInfo; 17 4 import com.voodoodyne.jackson.jsog.JSOGGenerator; 5 import lombok.Data; 6 7 import javax.persistence.*; 8 import java.util.ArrayList; 9 import java.util.List; 18 10 19 11 @Entity 12 @Data 20 13 @Table(name = "professor") 21 14 @JsonIdentityInfo(generator = JSOGGenerator.class) … … 34 27 private Faculty faculty; 35 28 29 @Transient 36 30 @OneToMany(mappedBy = "targetProfessor", cascade = CascadeType.ALL) 37 private List<Opinion> relatedOpinions = new ArrayList<Opinion>(); 38 39 // getters 40 public Long getProfessorId() { 41 return professorId; 42 } 43 44 public String getProfessorName() { 45 return professorName; 46 } 47 48 public Faculty getFaculty() { 49 return faculty; 50 } 51 52 public List<Opinion> getRelatedOpinions() { 53 return relatedOpinions; 54 } 31 private List<Opinion> relatedOpinions = new ArrayList<>(); 55 32 } -
springapp/src/main/java/mk/profesori/springapp/Model/StudyProgramme.java
r4abf55a r9bf1f8d 1 1 package mk.profesori.springapp.Model; 2 2 3 import com.fasterxml.jackson.annotation.JsonIdentityInfo; 4 import com.voodoodyne.jackson.jsog.JSOGGenerator; 5 import lombok.Data; 6 7 import javax.persistence.*; 3 8 import java.util.HashSet; 4 9 import java.util.Set; 5 10 6 import javax.persistence.Column;7 import javax.persistence.Entity;8 import javax.persistence.GeneratedValue;9 import javax.persistence.GenerationType;10 import javax.persistence.Id;11 import javax.persistence.JoinColumn;12 import javax.persistence.ManyToOne;13 import javax.persistence.OneToMany;14 import javax.persistence.Table;15 16 import com.fasterxml.jackson.annotation.JsonIdentityInfo;17 import com.voodoodyne.jackson.jsog.JSOGGenerator;18 19 11 @Entity 12 @Data 20 13 @Table(name = "study_programme") 21 14 @JsonIdentityInfo(generator = JSOGGenerator.class) … … 37 30 private Faculty faculty; 38 31 32 @Transient 39 33 @OneToMany(mappedBy = "studyProgramme") 40 34 private Set<Subject> subjects = new HashSet<>(); 41 35 42 // getters43 public Long getStudyProgrammeId() {44 return studyProgrammeId;45 }46 47 public String getStudyProgrammeName() {48 return studyProgrammeName;49 }50 51 public int getCycle() {52 return cycle;53 }54 55 public Faculty getFaculty() {56 return faculty;57 }58 59 public Set<Subject> getSubjects() {60 return subjects;61 }62 63 36 } -
springapp/src/main/java/mk/profesori/springapp/Model/Subject.java
r4abf55a r9bf1f8d 1 1 package mk.profesori.springapp.Model; 2 3 import java.util.ArrayList;4 import java.util.List;5 import javax.persistence.CascadeType;6 import javax.persistence.Column;7 import javax.persistence.Entity;8 import javax.persistence.GeneratedValue;9 import javax.persistence.GenerationType;10 import javax.persistence.Id;11 import javax.persistence.JoinColumn;12 import javax.persistence.ManyToOne;13 import javax.persistence.OneToMany;14 import javax.persistence.Table;15 2 16 3 import com.fasterxml.jackson.annotation.JsonIdentityInfo; 17 4 import com.voodoodyne.jackson.jsog.JSOGGenerator; 5 import lombok.Data; 6 7 import javax.persistence.*; 8 import java.util.ArrayList; 9 import java.util.List; 18 10 19 11 @Entity 12 @Data 20 13 @Table(name = "subject") 21 14 @JsonIdentityInfo(generator = JSOGGenerator.class) … … 34 27 private StudyProgramme studyProgramme; 35 28 29 @Transient 36 30 @OneToMany(mappedBy = "targetSubject", cascade = CascadeType.ALL) 37 31 private List<_Thread> threads = new ArrayList<>(); 38 32 39 // getters40 public Long getSubjectId() {41 return subjectId;42 }43 44 public String getSubjectName() {45 return subjectName;46 }47 48 public StudyProgramme getStudyProgramme() {49 return studyProgramme;50 }51 52 public List<_Thread> getThreads() {53 return threads;54 }55 56 33 } -
springapp/src/main/java/mk/profesori/springapp/Model/University.java
r4abf55a r9bf1f8d 1 1 package mk.profesori.springapp.Model; 2 2 3 import com.fasterxml.jackson.annotation.JsonIdentityInfo; 4 import com.voodoodyne.jackson.jsog.JSOGGenerator; 5 import lombok.Data; 6 7 import javax.persistence.*; 3 8 import java.util.HashSet; 4 9 import java.util.Set; 5 10 6 import javax.persistence.Column;7 import javax.persistence.Entity;8 import javax.persistence.GeneratedValue;9 import javax.persistence.GenerationType;10 import javax.persistence.Id;11 import javax.persistence.JoinColumn;12 import javax.persistence.ManyToOne;13 import javax.persistence.OneToMany;14 import javax.persistence.Table;15 16 import com.fasterxml.jackson.annotation.JsonIdentityInfo;17 import com.voodoodyne.jackson.jsog.JSOGGenerator;18 19 11 @Entity 12 @Data 20 13 @Table(name = "university") 21 14 @JsonIdentityInfo(generator = JSOGGenerator.class) … … 34 27 private City city; 35 28 29 @Transient 36 30 @OneToMany(mappedBy = "university") 37 31 private Set<Faculty> faculties = new HashSet<>(); 38 39 // getters40 public Long getUniversityId() {41 return universityId;42 }43 44 public String getUniversityName() {45 return universityName;46 }47 48 public City getCity() {49 return city;50 }51 52 public Set<Faculty> getFaculties() {53 return faculties;54 }55 56 32 } -
springapp/src/main/java/mk/profesori/springapp/Model/_Thread.java
r4abf55a r9bf1f8d 1 1 package mk.profesori.springapp.Model; 2 2 3 import java.time.LocalDateTime; 4 5 import java.util.List; 3 import lombok.Data; 4 import lombok.NoArgsConstructor; 6 5 7 6 import javax.persistence.DiscriminatorValue; 8 9 7 import javax.persistence.Entity; 10 8 import javax.persistence.JoinColumn; 11 9 import javax.persistence.ManyToOne; 12 13 import lombok.NoArgsConstructor;10 import java.time.LocalDateTime; 11 import java.util.List; 14 12 15 13 @Entity 14 @Data 16 15 @DiscriminatorValue("thread") 17 16 @NoArgsConstructor … … 36 35 this.targetSubject = targetSubject; 37 36 } 38 39 public Subject getTargetSubject() {40 return targetSubject;41 }42 43 public void setTargetSubject(Subject targetSubject) {44 this.targetSubject = targetSubject;45 }46 37 } -
springapp/src/main/java/mk/profesori/springapp/Repository/CityRepository.java
r4abf55a r9bf1f8d 1 1 package mk.profesori.springapp.Repository; 2 3 import mk.profesori.springapp.Model.City; 4 import org.springframework.data.repository.CrudRepository; 5 import org.springframework.stereotype.Repository; 2 6 3 7 import java.util.List; 4 8 5 import org.springframework.data.repository.CrudRepository;6 import org.springframework.stereotype.Repository;7 8 import mk.profesori.springapp.Model.City;9 10 9 @Repository 11 10 public interface CityRepository extends CrudRepository<City, Long>{ 12 13 11 List<City> findAll(); 14 12 City findByCityId(Long id); -
springapp/src/main/java/mk/profesori/springapp/Repository/FacultyRepository.java
r4abf55a r9bf1f8d 1 1 package mk.profesori.springapp.Repository; 2 3 import mk.profesori.springapp.Model.Faculty; 4 import mk.profesori.springapp.Model.University; 5 import org.springframework.data.jpa.repository.Query; 6 import org.springframework.data.repository.CrudRepository; 7 import org.springframework.data.repository.query.Param; 8 import org.springframework.stereotype.Repository; 2 9 3 10 import java.util.List; 4 11 5 import org.springframework.data.repository.CrudRepository;6 import org.springframework.stereotype.Repository;7 8 import mk.profesori.springapp.Model.Faculty;9 import mk.profesori.springapp.Model.University;10 11 12 @Repository 12 13 public interface FacultyRepository extends CrudRepository<Faculty, Long>{ 13 14 14 List<Faculty> findAll(); 15 15 Faculty findByFacultyId(Long id); 16 16 List<Faculty> findByUniversity(University university); 17 @Query(value = "select professor.id, count(p.*)\n" + 18 "from professor left join post p on professor.id = p.professor_id\n" + 19 "where professor.faculty_id = :facultyId\n" + 20 "group by professor.id\n" + 21 "order by professor.id;", nativeQuery = true) 22 List<String> getOpinionCountForEachProfessorInFaculty(@Param("facultyId") Long id); 17 23 } -
springapp/src/main/java/mk/profesori/springapp/Repository/OpinionRepository.java
r4abf55a r9bf1f8d 2 2 3 3 import mk.profesori.springapp.Model.Opinion; 4 import mk.profesori.springapp.Model.Professor; 4 5 import org.springframework.data.repository.CrudRepository; 5 6 import org.springframework.stereotype.Repository; 7 8 import java.util.List; 6 9 7 10 @Repository 8 11 public interface OpinionRepository extends CrudRepository<Opinion,Long> { 9 12 Opinion findByPostId(Long postId); 13 List<Opinion> findByTargetProfessor(Professor p); 14 List<Opinion> findTop10ByOrderByTimePosted(); 10 15 } -
springapp/src/main/java/mk/profesori/springapp/Repository/PostVoteRepository.java
r4abf55a r9bf1f8d 1 1 package mk.profesori.springapp.Repository; 2 2 3 import mk.profesori.springapp.Model.Post; 3 4 import mk.profesori.springapp.Model.PostVote; 4 5 import org.springframework.data.repository.CrudRepository; 5 6 import org.springframework.stereotype.Repository; 6 7 8 import java.util.List; 9 7 10 @Repository 8 11 public interface PostVoteRepository extends CrudRepository<PostVote,Long> { 9 12 PostVote findByPostVoteId(Long id); 13 List<PostVote> findByPost(Post post); 10 14 } -
springapp/src/main/java/mk/profesori/springapp/Repository/ProfessorRepository.java
r4abf55a r9bf1f8d 1 1 package mk.profesori.springapp.Repository; 2 2 3 import java.util.List;4 3 import mk.profesori.springapp.Model.Faculty; 4 import mk.profesori.springapp.Model.Professor; 5 5 import org.springframework.data.jpa.repository.JpaSpecificationExecutor; 6 6 import org.springframework.data.repository.CrudRepository; 7 7 import org.springframework.stereotype.Repository; 8 8 9 import mk.profesori.springapp.Model.Faculty; 10 import mk.profesori.springapp.Model.Professor; 9 import java.util.List; 11 10 12 11 @Repository … … 14 13 15 14 List<Professor> findAll(); 16 17 15 Professor findByProfessorId(Long id); 18 19 16 List<Professor> findByFaculty(Faculty faculty); 20 21 17 List<Professor> findByProfessorNameContainingIgnoreCase(String name); 22 18 } -
springapp/src/main/java/mk/profesori/springapp/Repository/StudyProgrammeRepository.java
r4abf55a r9bf1f8d 1 1 package mk.profesori.springapp.Repository; 2 3 import java.util.List;4 5 import org.springframework.data.repository.CrudRepository;6 import org.springframework.stereotype.Repository;7 2 8 3 import mk.profesori.springapp.Model.Faculty; 9 4 import mk.profesori.springapp.Model.StudyProgramme; 5 import org.springframework.data.jpa.repository.Query; 6 import org.springframework.data.repository.CrudRepository; 7 import org.springframework.data.repository.query.Param; 8 import org.springframework.stereotype.Repository; 9 10 import java.util.List; 10 11 11 12 12 13 @Repository 13 14 public interface StudyProgrammeRepository extends CrudRepository<StudyProgramme, Long>{ 14 15 15 List<StudyProgramme> findAll(); 16 16 StudyProgramme findByStudyProgrammeId(Long id); 17 17 List<StudyProgramme> findByFaculty(Faculty faculty); 18 @Query(value = "select subject.id, count(p.*)\n" + 19 "from subject left join post p on subject.id = p.subject_id\n" + 20 "where subject.study_programme_id = :studyProgrammeId\n" + 21 "group by subject.id\n" + 22 "order by subject.id;", nativeQuery = true) 23 List<String> getThreadCountForEachSubjectInStudyProgramme(@Param("studyProgrammeId") Long id); 18 24 } -
springapp/src/main/java/mk/profesori/springapp/Repository/SubjectRepository.java
r4abf55a r9bf1f8d 1 1 package mk.profesori.springapp.Repository; 2 2 3 import mk.profesori.springapp.Model.StudyProgramme; 4 import mk.profesori.springapp.Model.Subject; 3 5 import org.springframework.data.repository.CrudRepository; 4 6 import org.springframework.stereotype.Repository; 5 7 6 import mk.profesori.springapp.Model.Subject;8 import java.util.List; 7 9 8 10 @Repository 9 11 public interface SubjectRepository extends CrudRepository<Subject, Long> { 10 12 Subject findBySubjectId(Long id); 13 List<Subject> findByStudyProgramme(StudyProgramme studyProgramme); 14 List<Subject> findBySubjectNameContainingIgnoreCase(String name); 11 15 } -
springapp/src/main/java/mk/profesori/springapp/Repository/UniversityRepository.java
r4abf55a r9bf1f8d 1 1 package mk.profesori.springapp.Repository; 2 3 import mk.profesori.springapp.Model.City; 4 import mk.profesori.springapp.Model.University; 5 import org.springframework.data.jpa.repository.Query; 6 import org.springframework.data.repository.CrudRepository; 7 import org.springframework.stereotype.Repository; 2 8 3 9 import java.util.List; 4 10 5 import org.springframework.data.repository.CrudRepository;6 import org.springframework.stereotype.Repository;7 8 import mk.profesori.springapp.Model.City;9 import mk.profesori.springapp.Model.University;10 11 11 @Repository 12 12 public interface UniversityRepository extends CrudRepository<University, Long> { 13 14 13 List<University> findAll(); 15 14 16 15 University findByUniversityId(Long id); 17 18 16 List<University> findByCity(City city); 17 18 @Query(value = "select facultyid, sectioncount, opinioncount from\n" + 19 "(select faculty_id as facultyid, t1.subject_count+t2.professor_count as sectioncount\n" + 20 "from (select sp.faculty_id, count(*) as subject_count\n" + 21 " from subject s join study_programme sp on s.study_programme_id = sp.id\n" + 22 " group by sp.faculty_id) as t1\n" + 23 " natural join\n" + 24 " (select count(*) as professor_count, f.id as faculty_id\n" + 25 " from professor p join faculty f on f.id = p.faculty_id\n" + 26 " group by f.id) as t2) as q1\n" + 27 "natural join\n" + 28 "(select facultyid, count1+count2 as opinioncount from\n" + 29 " (select pr.faculty_id as facultyid, count(*) as count1\n" + 30 " from post po join professor pr on po.professor_id = pr.id\n" + 31 " where professor_id is not null\n" + 32 " group by pr.faculty_id) as t3\n" + 33 " natural join\n" + 34 " (select sp.faculty_id as facultyid, count(*) as count2\n" + 35 " from post po join subject s on po.subject_id = s.id join study_programme sp on s.study_programme_id = sp.id\n" + 36 " where po.subject_id is not null\n" + 37 " group by sp.faculty_id) as t4) as q2;", nativeQuery = true) 38 List<String> findSectionAndPostCount(); 19 39 } -
springapp/src/main/java/mk/profesori/springapp/Repository/UserRepository.java
r4abf55a r9bf1f8d 1 1 package mk.profesori.springapp.Repository; 2 2 3 import java.util.Optional; 4 3 import mk.profesori.springapp.Model.CustomUserDetails; 5 4 import org.springframework.data.jpa.repository.JpaRepository; 6 5 import org.springframework.data.jpa.repository.Modifying; … … 9 8 import org.springframework.transaction.annotation.Transactional; 10 9 11 import mk.profesori.springapp.Model.CustomUserDetails;10 import java.util.Optional; 12 11 13 12 @Repository … … 16 15 Optional<CustomUserDetails> findByEmail(String email); 17 16 Optional<CustomUserDetails> findByUsername(String username); 17 Optional<CustomUserDetails> findById(Long id); 18 18 19 19 @Transactional -
springapp/src/main/java/mk/profesori/springapp/Repository/_ThreadRepository.java
r4abf55a r9bf1f8d 1 1 package mk.profesori.springapp.Repository; 2 2 3 import mk.profesori.springapp.Model.Subject; 3 4 import mk.profesori.springapp.Model._Thread; 4 5 import org.springframework.data.repository.CrudRepository; 5 6 import org.springframework.stereotype.Repository; 6 7 8 import java.util.List; 9 7 10 @Repository 8 11 public interface _ThreadRepository extends CrudRepository<_Thread,Long> { 9 12 _Thread findByPostId(Long postId); 13 List<_Thread> findTop10ByOrderByTimePosted(); 14 List<_Thread> findByTargetSubject(Subject s); 10 15 } -
springapp/src/main/java/mk/profesori/springapp/Security/SecurityConfiguration.java
r4abf55a r9bf1f8d 37 37 @Override 38 38 public void addCorsMappings(CorsRegistry registry) { 39 registry.addMapping("/**").allowedOrigins("http://192.168. 0.29:3000", "http://192.168.0.28:3000")39 registry.addMapping("/**").allowedOrigins("http://192.168.1.254:3000", "http://192.168.0.28:3000") 40 40 .allowCredentials(true); 41 41 } -
springapp/src/main/java/mk/profesori/springapp/Service/CustomUserDetailsService.java
r4abf55a r9bf1f8d 1 1 package mk.profesori.springapp.Service; 2 2 3 import java.time.LocalDateTime; 4 import java.util.UUID; 5 3 import lombok.AllArgsConstructor; 4 import mk.profesori.springapp.Model.ConfirmationToken; 5 import mk.profesori.springapp.Model.CustomUserDetails; 6 import mk.profesori.springapp.Repository.UserRepository; 6 7 import org.springframework.beans.factory.annotation.Autowired; 7 8 import org.springframework.security.core.userdetails.UserDetails; … … 12 13 import org.springframework.stereotype.Service; 13 14 14 import lombok.AllArgsConstructor; 15 import mk.profesori.springapp.Model.ConfirmationToken; 16 import mk.profesori.springapp.Model.CustomUserDetails; 17 import mk.profesori.springapp.Repository.UserRepository; 15 import java.time.LocalDateTime; 16 import java.util.UUID; 18 17 19 18 @Service -
springapp/src/main/java/mk/profesori/springapp/Service/Exception/DisallowedOperationException.java
r4abf55a r9bf1f8d 1 package mk.profesori.springapp.Service ;1 package mk.profesori.springapp.Service.Exception; 2 2 3 3 public class DisallowedOperationException extends RuntimeException{ -
springapp/src/main/java/mk/profesori/springapp/Service/Exception/IncompatiblePostId.java
r4abf55a r9bf1f8d 1 package mk.profesori.springapp.Service ;1 package mk.profesori.springapp.Service.Exception; 2 2 3 3 public class IncompatiblePostId extends RuntimeException{ -
springapp/src/main/java/mk/profesori/springapp/Service/MainService.java
r4abf55a r9bf1f8d 3 3 import mk.profesori.springapp.Model.*; 4 4 import mk.profesori.springapp.Repository.*; 5 import mk.profesori.springapp.Service.Exception.DisallowedOperationException; 6 import mk.profesori.springapp.Service.Exception.IncompatiblePostId; 5 7 import org.springframework.security.core.userdetails.UsernameNotFoundException; 6 8 import org.springframework.stereotype.Service; … … 9 11 import java.util.ArrayList; 10 12 import java.util.List; 13 import java.util.Set; 11 14 12 15 @Service … … 24 27 private final UserRepository userRepository; 25 28 private final PostReportRepository postReportRepository; 26 27 public MainService(ProfessorRepository professorRepository, StudyProgrammeRepository studyProgrammeRepository, FacultyRepository facultyRepository, UniversityRepository universityRepository, CityRepository cityRepository, OpinionRepository opinionRepository, _ThreadRepository _threadRepository, SubjectRepository subjectRepository, PostVoteRepository postVoteRepository, UserRepository userRepository, PostReportRepository postReportRepository) { 29 private final PostRepository postRepository; 30 31 public MainService(ProfessorRepository professorRepository, StudyProgrammeRepository studyProgrammeRepository, FacultyRepository facultyRepository, UniversityRepository universityRepository, CityRepository cityRepository, OpinionRepository opinionRepository, _ThreadRepository _threadRepository, SubjectRepository subjectRepository, PostVoteRepository postVoteRepository, UserRepository userRepository, PostReportRepository postReportRepository, PostRepository postRepository) { 28 32 this.professorRepository = professorRepository; 29 33 this.studyProgrammeRepository = studyProgrammeRepository; … … 37 41 this.userRepository = userRepository; 38 42 this.postReportRepository = postReportRepository; 43 this.postRepository = postRepository; 39 44 } 40 45 … … 58 63 public List<Professor> getProfessorsByNameContains(String contained) { 59 64 return new ArrayList<>(professorRepository.findByProfessorNameContainingIgnoreCase(contained)); 65 } 66 67 public List<Subject> getSubjectsByNameContains(String contained) { 68 return new ArrayList<>(subjectRepository.findBySubjectNameContainingIgnoreCase(contained)); 60 69 } 61 70 … … 200 209 public void delete_Thread(Long postId) {_threadRepository.deleteById(postId);} 201 210 202 public StringupdateOpinion(String newContent, Long newTargetProfessorId, Long newParentPostId, Long postId) {211 public void updateOpinion(String newContent, Long newTargetProfessorId, Long newParentPostId, Long postId) { 203 212 Opinion opinionToUpdate = opinionRepository.findByPostId(postId); 204 213 … … 218 227 opinionToUpdate.setParent(newParentOpinion); 219 228 229 propagateNewTargetProfessorToChildren(opinionToUpdate, newTargetProfessor); 230 231 opinionToUpdate.setTimeLastEdited(LocalDateTime.now()); 232 opinionRepository.save(opinionToUpdate); 233 } 234 235 public void propagateNewTargetProfessorToChildren(Opinion opinionToUpdate, Professor newTargetProfessor) { 220 236 for(Post p : opinionToUpdate.getChildren()) { 237 // direct children 221 238 Opinion o = (Opinion) p; 222 239 o.setTargetProfessor(newTargetProfessor); 240 // ancestors 241 if(o.getChildren().isEmpty()) return; 242 propagateNewTargetProfessorToChildren(o, newTargetProfessor); 223 243 } 224 opinionToUpdate.setTimeLastEdited(LocalDateTime.now()); 225 opinionRepository.save(opinionToUpdate); 226 return null; 227 } 228 229 public String update_Thread(String newTitle, String newContent, Long newTargetSubjectId, Long newParentThreadId, Long postId) { 244 } 245 246 public void update_Thread(String newTitle, String newContent, Long newTargetSubjectId, Long newParentThreadId, Long postId) { 230 247 _Thread _threadToUpdate = _threadRepository.findByPostId(postId); 231 248 … … 251 268 } 252 269 253 for(Post p : _threadToUpdate.getChildren()) { 254 _Thread t = (_Thread) p; 255 t.setTargetSubject(newTargetSubject); 256 } 270 propagateNewTargetSubjectToChildren(_threadToUpdate, newTargetSubject); 271 257 272 _threadToUpdate.setTimeLastEdited(LocalDateTime.now()); 258 273 _threadRepository.save(_threadToUpdate); 259 return null; 274 } 275 276 public void propagateNewTargetSubjectToChildren(_Thread _threadToUpdate, Subject newTargetSubject) { 277 for(Post p : _threadToUpdate.getChildren()) { 278 // direct children 279 _Thread t = (_Thread) p; 280 t.setTargetSubject(newTargetSubject); 281 // ancestors 282 if (t.getChildren().isEmpty()) return; 283 propagateNewTargetSubjectToChildren(t, newTargetSubject); 284 } 260 285 } 261 286 … … 306 331 postReportRepository.save(reportToAdd); 307 332 } 333 334 public List<Opinion> getRelatedOpinions(Long professorId) { 335 Professor p = professorRepository.findByProfessorId(professorId); 336 return opinionRepository.findByTargetProfessor(p); 337 } 338 339 public List<Opinion> getLatest10Opinions() { 340 return opinionRepository.findTop10ByOrderByTimePosted(); 341 } 342 public List<_Thread> getLatest10Threads() { 343 return _threadRepository.findTop10ByOrderByTimePosted(); 344 } 345 346 public List<String> getUniversitySectionAndPostCount() { 347 return universityRepository.findSectionAndPostCount(); 348 } 349 350 public List<Subject> getSubjectsByStudyProgramme(Long studyProgrammeId) { 351 StudyProgramme sp = studyProgrammeRepository.findByStudyProgrammeId(studyProgrammeId); 352 return subjectRepository.findByStudyProgramme(sp); 353 } 354 355 public List<String> getOpinionCountForEachProfessorInFaculty(Long id) { 356 return facultyRepository.getOpinionCountForEachProfessorInFaculty(id); 357 } 358 359 public List<_Thread> getThreadsBySubject(Long id) { 360 Subject s = subjectRepository.findBySubjectId(id); 361 return _threadRepository.findByTargetSubject(s); 362 } 363 364 public List<String> getThreadCountForEachSubjectInStudyProgramme(Long id) { 365 return studyProgrammeRepository.getThreadCountForEachSubjectInStudyProgramme(id); 366 } 367 368 public Set<Post> getPostsByUser(String email) { 369 CustomUserDetails u = userRepository.findByEmail(email).orElseThrow( 370 () -> new UsernameNotFoundException(String.format("User with email %s not found", email))); 371 return postRepository.findByAuthor(u); 372 } 373 374 public CustomUserDetails getPublicUserProfile(Long id) { 375 return userRepository.findById(id).orElseThrow( 376 () -> new UsernameNotFoundException(String.format("User with id %d not found", id))); 377 } 308 378 } -
springapp/src/main/java/mk/profesori/springapp/Service/RegistrationService.java
r4abf55a r9bf1f8d 45 45 String tokenToResend = customUserDetailsService 46 46 .createToken(userRepository.findByEmail(request.getEmail()).get()); 47 String link = "http://192.168. 0.29:8080/registration/confirm?token=" + tokenToResend;47 String link = "http://192.168.1.254:8080/registration/confirm?token=" + tokenToResend; 48 48 emailSender.send(request.getEmail(), emailSender.buildEmail(request.getUsername(), link)); 49 49 return tokenToResend; … … 66 66 UserRole.REGULAR)); 67 67 68 String link = "http://192.168. 0.29:8080/registration/confirm?token=" + token;68 String link = "http://192.168.1.254:8080/registration/confirm?token=" + token; 69 69 70 70 emailSender.send(request.getEmail(), emailSender.buildEmail(request.getUsername(), link)); -
springapp/src/main/resources/application.properties
r4abf55a r9bf1f8d 6 6 spring.jpa.show-sql=false 7 7 spring.jpa.properties.hibernate.format_sql=true 8 server.address=192.168. 0.299 spring.mail.host= localhost8 server.address=192.168.1.254 9 spring.mail.host=192.168.1.254 10 10 spring.mail.username=mailuser 11 11 spring.mail.password=mailpass 12 12 spring.mail.port=1025 13 spring.jpa.open-in-view=false
Note:
See TracChangeset
for help on using the changeset viewer.