Changeset c68150f


Ignore:
Timestamp:
10/27/22 17:35:03 (2 years ago)
Author:
unknown <mlviktor23@…>
Branches:
main
Children:
3b6962d
Parents:
8d83180
Message:

left: moderation, oAuth, messaging

Files:
6 added
1 deleted
30 edited

Legend:

Unmodified
Added
Removed
  • reactapp/src/App.js

    r8d83180 rc68150f  
    1212import AuthApi from "./api/AuthApi";
    1313import Cookies from "js-cookie";
    14 import axios from "./api/axios";
    15 import JSOG from "jsog";
    1614import NotFound from "./Pages/NotFound";
     15import Topic from "./Pages/Topic";
    1716
    1817export default function App() {
    1918  const [auth, setAuth] = useState(false);
    20   const [user, setUser] = useState(null);
    21   const [userLoaded, setUserLoaded] = useState(false);
    2219  const variableAuth = useMemo(() => ({ auth, setAuth }), [auth]);
    2320  const [authLoaded, setAuthLoaded] = useState(false);
    24 
    25   const fetchUser = async () => {
    26     try {
    27       const response = await axios.get(
    28         "http://192.168.0.17:8080/secure/currentUser",
    29         { withCredentials: true }
    30       );
    31       var cyclicGraph = await response.data;
    32       var jsogStructure = JSOG.encode(cyclicGraph);
    33       cyclicGraph = JSOG.decode(jsogStructure);
    34       setUser(cyclicGraph);
    35       setUserLoaded(true);
    36     } catch (error) {
    37       console.log("Fetching error", error);
    38     }
    39   };
    4021
    4122  const readCookie = async () => {
     
    4324    if (session) {
    4425      setAuth(true);
    45       fetchUser();
    4626    } else {
    4727      setAuth(false);
     
    5131
    5232  useEffect(() => {
     33    document.title = "profesori.mk";
    5334    readCookie();
    5435  }, []);
     
    6950      <BrowserRouter>
    7051        <Routes>
    71           <Route
    72             path="/"
    73             element={<Home user={user} userLoaded={userLoaded} />}
    74           >
     52          <Route path="/" element={<Home />}>
    7553            <Route path="login" element={<Login />}></Route>
    7654            <Route path="registration" element={<Registration />}></Route>
    7755            <Route path="professor">
    78               <Route
    79                 path=":professorId"
    80                 element={<Professor user={user} userLoaded={userLoaded} />}
    81               />
     56              <Route path=":professorId" element={<Professor />} />
    8257            </Route>
    8358            <Route path="university/:universityId" element={<University />} />
    8459            <Route path="faculty/:facultyId" element={<Faculty />} />
    8560            <Route path="subject/:subjectId" element={<Subject />} />
     61            <Route path="topic/:topicId" element={<Topic />} />
    8662            <Route path="search" element={<SearchResults />}></Route>
    8763            <Route
    8864              path="user_dashboard"
    8965              element={
    90                 <ProtectedRoute auth={auth}>
    91                   {<UserDashboard user={user} userLoaded={userLoaded} />}
    92                 </ProtectedRoute>
     66                <ProtectedRoute auth={auth}>{<UserDashboard />}</ProtectedRoute>
    9367              }
    9468            ></Route>
  • reactapp/src/Components/OpinionTree.js

    r8d83180 rc68150f  
    33  OpinionCardContent,
    44  OpinionCardContentTime,
    5   OpinionCardContentTitle,
    65  OpinionReplyCard,
    76  OpinionReplyCardContent,
     
    1413import AuthApi from "../api/AuthApi";
    1514import { useNavigate } from "react-router-dom";
    16 import { useContext, useState } from "react";
     15import { useContext, useState, useEffect } from "react";
     16import JSOG from "jsog";
    1717import {
    1818  Modal,
     
    2626import axios from "../api/axios";
    2727
    28 function OpinionTree({ professor, user, userLoaded }) {
     28function OpinionTree({ professor }) {
    2929  var renderedOpinionIds = [];
    3030  var postCount; // za da ne go pokazuva ispod postot
    3131
     32  const { auth, setAuth } = useContext(AuthApi);
    3233  let navigate = useNavigate();
    33   const { auth, setAuth } = useContext(AuthApi);
    34 
    35   let [replyModalDisplay, setReplyModalDisplay] = useState("none");
     34
     35  const [replyModalDisplay, setReplyModalDisplay] = useState("none");
    3636  const [replyContent, setReplyContent] = useState("");
    37 
    3837  const [postForModal, setPostForModal] = useState(null);
     38  const [user, setUser] = useState(null);
     39  const [loadedUser, setLoadedUser] = useState(false);
     40  const [fetchError, setFetchError] = useState(false);
     41  const [errorMessage, setErrorMessage] = useState("");
     42
     43  useEffect(() => {
     44    const url = `http://192.168.0.17:8080/secure/currentUser`;
     45
     46    const fetchUser = async () => {
     47      try {
     48        const response = await axios.get(url, { withCredentials: true });
     49        var cyclicGraph = await response.data;
     50        var jsogStructure = JSOG.encode(cyclicGraph);
     51        cyclicGraph = JSOG.decode(jsogStructure);
     52        setUser(cyclicGraph);
     53        setLoadedUser(true);
     54      } catch (error) {
     55        setFetchError(true);
     56      }
     57    };
     58
     59    if (auth) fetchUser();
     60  }, []);
    3961
    4062  const handleLike = async (post) => {
    41     if (
    42       auth &&
    43       userLoaded &&
    44       !post.likes.some((e) => e.id === user.user.id) &&
    45       !post.dislikes.some((e) => e.id === user.user.id)
    46     ) {
    47       const response = await axios(
    48         `http://192.168.0.17:8080/secure/professor/${professor.professorId}/upvoteOpinion/${post.postId}`,
    49         {
    50           method: "get",
    51           withCredentials: true,
    52         }
    53       );
    54 
    55       window.location.reload(false);
     63    if (auth) {
     64      if (
     65        loadedUser &&
     66        user &&
     67        !post.votes.some((e) => e.user.id === user.id)
     68      ) {
     69        const response = await axios(
     70          `http://192.168.0.17:8080/secure/upvoteOpinion/${post.postId}`,
     71          {
     72            method: "get",
     73            withCredentials: true,
     74          }
     75        );
     76        window.location.reload(false);
     77      } else {
     78        return;
     79      }
    5680    } else {
    57       return;
     81      navigate("/login");
    5882    }
    5983  };
    6084
    6185  const handleDislike = async (post) => {
    62     if (
    63       auth &&
    64       auth &&
    65       userLoaded &&
    66       !post.likes.some((e) => e.id === user.user.id) &&
    67       !post.dislikes.some((e) => e.id === user.user.id)
    68     ) {
    69       const response = await axios(
    70         `http://192.168.0.17:8080/secure/professor/${professor.professorId}/downvoteOpinion/${post.postId}`,
    71         {
    72           method: "get",
    73           withCredentials: true,
    74         }
    75       );
    76 
    77       window.location.reload(false);
     86    if (auth) {
     87      if (
     88        loadedUser &&
     89        user &&
     90        !post.votes.some((e) => e.user.id === user.id)
     91      ) {
     92        const response = await axios(
     93          `http://192.168.0.17:8080/secure/downvoteOpinion/${post.postId}`,
     94          {
     95            method: "get",
     96            withCredentials: true,
     97          }
     98        );
     99
     100        window.location.reload(false);
     101      } else {
     102        return;
     103      }
    78104    } else {
    79       return;
     105      navigate("/login");
    80106    }
    81107  };
     
    101127    e.preventDefault();
    102128
    103     const response = await axios(
    104       `http://192.168.0.17:8080/secure/professor/${professor.professorId}/replyToOpinion/${postId}`,
    105       {
    106         method: "post",
    107         body: {
    108           content: replyContent,
    109         },
    110         withCredentials: true,
    111       }
    112     );
    113 
    114     window.location.reload(false);
    115     //console.log(response);
     129    if (!replyContent.length < 1) {
     130      const response = await axios(
     131        `http://192.168.0.17:8080/secure/professor/${professor.professorId}/replyToOpinion/${postId}`,
     132        {
     133          method: "post",
     134          data: {
     135            content: replyContent,
     136          },
     137          withCredentials: true,
     138        }
     139      );
     140      setErrorMessage("");
     141      window.location.reload(false);
     142    } else {
     143      setErrorMessage("Полето за содржина не смее да биде празно");
     144    }
    116145  };
    117146
     
    123152        <OpinionReplyCard indent={replyIndent + "px"}>
    124153          <OpinionReplyCardContent>
    125             <p>
    126               <a href="#">{child.author.username}</a> му реплицирал на{" "}
    127               {parentPostAuthorUsername}
     154            <p style={{ fontStyle: "italic", marginBottom: "10px" }}>
     155              <a href={"/user/" + child.author.id}>{child.author.username}</a>{" "}
     156              му реплицирал на {parentPostAuthorUsername}
    128157            </p>
    129             <p>{child.content}</p>
     158            <p style={{ marginBottom: "10px", maxWidth: "90%" }}>
     159              {child.content}
     160            </p>
    130161            <OpinionReplyCardContentTime>
    131162              {dateConverter(
     
    133164              )}
    134165            </OpinionReplyCardContentTime>
    135             {auth && userLoaded && user.user.id !== child.author.id && (
    136               <>
    137                 <StyledFontAwesomeIcon
    138                   icon={solid("thumbs-up")}
    139                   right={50 + "px"}
    140                   color={
    141                     child.likes.some((e) => e.id === user.user.id)
     166
     167            <div
     168              style={{
     169                display:
     170                  !auth || (auth && loadedUser && user.id !== child.author.id)
     171                    ? "block"
     172                    : "none",
     173              }}
     174            >
     175              <StyledFontAwesomeIcon
     176                icon={solid("thumbs-up")}
     177                right={50 + "px"}
     178                color={
     179                  auth && loadedUser && user
     180                    ? child.votes.some(
     181                        (e) => e.vote === "UPVOTE" && e.user.id === user.id
     182                      )
    142183                      ? "greenyellow"
    143184                      : "darkgrey"
    144                   }
    145                   onClick={() => handleLike(child)}
    146                 />
    147                 <VoteCount right={50 + "px"}>{child.likes.length}</VoteCount>
    148                 <StyledFontAwesomeIcon
    149                   icon={solid("thumbs-down")}
    150                   right={10 + "px"}
    151                   color={
    152                     child.dislikes.some((e) => e.id === user.user.id)
     185                    : "darkgrey"
     186                }
     187                onClick={() => handleLike(child)}
     188              />
     189
     190              <VoteCount right={50 + "px"}>
     191                {child.votes.filter((v) => v.vote === "UPVOTE").length}
     192              </VoteCount>
     193
     194              <StyledFontAwesomeIcon
     195                icon={solid("thumbs-down")}
     196                right={10 + "px"}
     197                color={
     198                  auth && loadedUser && user
     199                    ? child.votes.some(
     200                        (e) => e.vote === "DOWNVOTE" && e.user.id === user.id
     201                      )
    153202                      ? "indianred"
    154203                      : "darkgrey"
    155                   }
    156                   onClick={() => handleDislike(child)}
    157                 />
    158                 <VoteCount right={10 + "px"}>{child.dislikes.length}</VoteCount>
    159                 <StyledFontAwesomeIcon
    160                   icon={solid("reply")}
    161                   right={90 + "px"}
    162                   color="darkgrey"
    163                   onClick={() => handleReply(child)}
    164                 />
    165               </>
    166             )}
     204                    : "darkgrey"
     205                }
     206                onClick={() => handleDislike(child)}
     207              />
     208
     209              <VoteCount right={10 + "px"}>
     210                {child.votes.filter((v) => v.vote === "DOWNVOTE").length}
     211              </VoteCount>
     212
     213              <StyledFontAwesomeIcon
     214                icon={solid("reply")}
     215                right={90 + "px"}
     216                color="darkgrey"
     217                onClick={() => handleReply(child)}
     218              />
     219            </div>
    167220          </OpinionReplyCardContent>
    168221          {child.children.map((childOfChild) =>
     
    187240              <OpinionCard>
    188241                <OpinionCardContent>
    189                   <p>
    190                     <a href="#">{opinion.author.username}</a> напишал
     242                  <p style={{ fontStyle: "italic", marginBottom: "10px" }}>
     243                    <a href={"/user/" + opinion.author.id}>
     244                      {opinion.author.username}
     245                    </a>{" "}
     246                    напишал
    191247                  </p>
    192                   <OpinionCardContentTitle>
    193                     {opinion.title}
    194                   </OpinionCardContentTitle>
    195                   <p>{opinion.content}</p>
     248                  <p style={{ marginBottom: "10px", maxWidth: "90%" }}>
     249                    {opinion.content}
     250                  </p>
    196251                  <OpinionCardContentTime>
    197252                    {dateConverter(
     
    199254                    )}
    200255                  </OpinionCardContentTime>
    201                   {auth && userLoaded && user.user.id !== opinion.author.id && (
    202                     <>
    203                       <StyledFontAwesomeIcon
    204                         icon={solid("thumbs-up")}
    205                         right={50 + "px"}
    206                         color={
    207                           opinion.likes.some((e) => e.id === user.user.id)
     256
     257                  <div
     258                    style={{
     259                      display:
     260                        !auth ||
     261                        (auth && loadedUser && user.id !== opinion.author.id)
     262                          ? "block"
     263                          : "none",
     264                    }}
     265                  >
     266                    <StyledFontAwesomeIcon
     267                      icon={solid("thumbs-up")}
     268                      right={50 + "px"}
     269                      color={
     270                        auth && loadedUser && user
     271                          ? opinion.votes.some(
     272                              (e) =>
     273                                e.vote === "UPVOTE" && e.user.id === user.id
     274                            )
    208275                            ? "greenyellow"
    209276                            : "darkgrey"
    210                         }
    211                         onClick={() => handleLike(opinion)}
    212                       />
    213                       <VoteCount right={50 + "px"}>
    214                         {opinion.likes.length}
    215                       </VoteCount>
    216                       <StyledFontAwesomeIcon
    217                         icon={solid("thumbs-down")}
    218                         right={10 + "px"}
    219                         color={
    220                           opinion.dislikes.some((e) => e.id === user.user.id)
     277                          : "darkgrey"
     278                      }
     279                      onClick={() => handleLike(opinion)}
     280                    />
     281
     282                    <VoteCount right={50 + "px"}>
     283                      {opinion.votes.filter((v) => v.vote === "UPVOTE").length}
     284                    </VoteCount>
     285
     286                    <StyledFontAwesomeIcon
     287                      icon={solid("thumbs-down")}
     288                      right={10 + "px"}
     289                      color={
     290                        auth && loadedUser && user
     291                          ? opinion.votes.some(
     292                              (e) =>
     293                                e.vote === "DOWNVOTE" && e.user.id === user.id
     294                            )
    221295                            ? "indianred"
    222296                            : "darkgrey"
    223                         }
    224                         onClick={() => handleDislike(opinion)}
    225                       />
    226                       <VoteCount right={10 + "px"}>
    227                         {opinion.dislikes.length}
    228                       </VoteCount>
    229                       <StyledFontAwesomeIcon
    230                         icon={solid("reply")}
    231                         right={90 + "px"}
    232                         color="darkgrey"
    233                         onClick={() => handleReply(opinion)}
    234                       />
    235                     </>
    236                   )}
     297                          : "darkgrey"
     298                      }
     299                      onClick={() => handleDislike(opinion)}
     300                    />
     301
     302                    <VoteCount right={10 + "px"}>
     303                      {
     304                        opinion.votes.filter((v) => v.vote === "DOWNVOTE")
     305                          .length
     306                      }
     307                    </VoteCount>
     308
     309                    <StyledFontAwesomeIcon
     310                      icon={solid("reply")}
     311                      right={90 + "px"}
     312                      color="darkgrey"
     313                      onClick={() => handleReply(opinion)}
     314                    />
     315                  </div>
    237316                </OpinionCardContent>
    238317                {opinion.children.map((child) =>
     
    243322          );
    244323        }
     324        return null;
    245325      })}
    246326      {postForModal && (
     
    266346                </label>
    267347              </ModalBody>
     348              <p
     349                style={{ color: "red", marginLeft: "15px", marginTop: "10px" }}
     350              >
     351                {errorMessage}
     352              </p>
    268353              <ModalFooter type="submit">РЕПЛИЦИРАЈ</ModalFooter>
    269354            </form>
  • reactapp/src/Components/Styled/EntityList.style.js

    r8d83180 rc68150f  
    1818export const EntityParam = styled.p`
    1919  position: absolute;
    20   right: 30px;
     20  right: ${(props) => props.right};
    2121  top: 10px;
    2222  font-style: italic;
  • reactapp/src/Components/Styled/Modal.style.js

    r8d83180 rc68150f  
    1818  float: right;
    1919  font-weight: bold;
     20  box-shadow: 2px 1px 10px #aaaaaa;
    2021`;
    2122
  • reactapp/src/Components/Styled/OpinionCard.style.js

    r8d83180 rc68150f  
    1818  }
    1919  position: relative;
     20  overflow-wrap: break-word;
    2021`;
    2122
     
    5556  display: block;
    5657  position: absolute;
     58  height: 20px;
    5759  top: 50%;
    5860  transform: translateY(-50%);
     
    6062  transition: 0.5s;
    6163  &:hover {
    62     color: ${(props) => props.color};
     64    opacity: 0.5;
    6365    cursor: pointer;
    6466  }
  • reactapp/src/Components/SubjectsAccordion.js

    r8d83180 rc68150f  
    2727      <SubjectsAccordionDiv height={height} opacity={opacity}>
    2828        <EntityUl>
    29           {props.content.map((item) => {
    30             let totalPosts = item.threads.length;
    31             return (
    32               <EntityLi key={item.subjectName} bgcolor="cornsilk">
    33                 <a href={"/subject/" + item.subjectId}>{item.subjectName}</a>
    34                 <EntityParam>
    35                   {totalPosts !== 1 ? (
    36                     totalPosts !== 0 ? (
    37                       <span
    38                         style={{
    39                           fontWeight: "normal",
    40                           opacity: totalPosts === 0 ? "0.5" : "1",
    41                         }}
    42                       >
     29          {props.content &&
     30            props.content.map((item) => {
     31              let totalPosts = item.threads.length;
     32              return (
     33                <EntityLi key={item.subjectName} bgcolor="cornsilk">
     34                  <a href={"/subject/" + item.subjectId}>{item.subjectName}</a>
     35                  <EntityParam right="30px">
     36                    {totalPosts !== 1 ? (
     37                      totalPosts !== 0 ? (
    4338                        <span
    4439                          style={{
    45                             fontWeight: "bold",
     40                            fontWeight: "normal",
    4641                            opacity: totalPosts === 0 ? "0.5" : "1",
    4742                          }}
    4843                        >
    49                           {totalPosts}
    50                         </span>{" "}
    51                         мислења
    52                       </span>
    53                     ) : (
    54                       <span
    55                         style={{
    56                           fontWeight: "normal",
    57                           opacity: totalPosts === 0 ? "0.5" : "1",
    58                         }}
    59                       >
     44                          <span
     45                            style={{
     46                              fontWeight: "bold",
     47                              opacity: totalPosts === 0 ? "0.5" : "1",
     48                            }}
     49                          >
     50                            {totalPosts}
     51                          </span>{" "}
     52                          мислења
     53                        </span>
     54                      ) : (
    6055                        <span
    6156                          style={{
    62                             fontWeight: "bold",
     57                            fontWeight: "normal",
    6358                            opacity: totalPosts === 0 ? "0.5" : "1",
    6459                          }}
    6560                        >
    66                           {totalPosts}
    67                         </span>{" "}
    68                         мислења
     61                          <span
     62                            style={{
     63                              fontWeight: "bold",
     64                              opacity: totalPosts === 0 ? "0.5" : "1",
     65                            }}
     66                          >
     67                            {totalPosts}
     68                          </span>{" "}
     69                          мислења
     70                        </span>
     71                      )
     72                    ) : (
     73                      <span style={{ fontWeight: "normal" }}>
     74                        <span style={{ fontWeight: "bold" }}>{totalPosts}</span>{" "}
     75                        мислење
    6976                      </span>
    70                     )
    71                   ) : (
    72                     <span style={{ fontWeight: "normal" }}>
    73                       <span style={{ fontWeight: "bold" }}>{totalPosts}</span>{" "}
    74                       мислење
    75                     </span>
    76                   )}
    77                 </EntityParam>
    78               </EntityLi>
    79             );
    80           })}
     77                    )}
     78                  </EntityParam>
     79                </EntityLi>
     80              );
     81            })}
    8182        </EntityUl>
    8283      </SubjectsAccordionDiv>
  • reactapp/src/Components/UserHeader.js

    r8d83180 rc68150f  
    44import Logout from "./Logout";
    55
    6 function UserHeader({ user, userLoaded }) {
    7   return userLoaded ? (
     6function UserHeader({}) {
     7  const [user, setUser] = useState(null);
     8  const [loadedUser, setLoadedUser] = useState(false);
     9  const [fetchError, setFetchError] = useState(false);
     10
     11  useEffect(() => {
     12    const url = `http://192.168.0.17:8080/secure/currentUser`;
     13
     14    const fetchUser = async () => {
     15      try {
     16        const response = await axios.get(url, { withCredentials: true });
     17        var cyclicGraph = await response.data;
     18        var jsogStructure = JSOG.encode(cyclicGraph);
     19        cyclicGraph = JSOG.decode(jsogStructure);
     20        setUser(cyclicGraph);
     21        setLoadedUser(true);
     22      } catch (error) {
     23        setFetchError(true);
     24      }
     25    };
     26
     27    fetchUser();
     28  }, []);
     29
     30  return loadedUser ? (
    831    <div style={{ float: "left", marginTop: 20, marginLeft: 40 }}>
    932      Најавен/а: <a href="/user_dashboard">{user.username}</a> <Logout />{" "}
  • reactapp/src/Pages/Faculty.js

    r8d83180 rc68150f  
    6060  }, [params.facultyId]);
    6161
    62   return loadedProfessors ? (
     62  return loadedProfessors && professors.length != 0 ? (
    6363    entityType === 0 ? (
    6464      <>
     
    114114                    {professor.professorName}
    115115                  </a>
    116                   <EntityParam>
     116                  <EntityParam right="30px">
    117117                    {totalPosts !== 1 ? (
    118118                      totalPosts !== 0 ? (
     
    168168        <>
    169169          <CurrentPageNav>
     170            &#187;{" "}
    170171            <a
    171172              href={
     
    175176              {professors[0].faculty.university.universityName}
    176177            </a>{" "}
    177             / <a href="#">{professors[0].faculty.facultyName}</a>
     178            &#187; <a href="#">{professors[0].faculty.facultyName}</a>
    178179          </CurrentPageNav>
    179180          <ProfessorCard>
     
    221222      )
    222223    )
    223   ) : !fetchError ? (
     224  ) : !fetchError && !loadedProfessors ? (
    224225    <div>
    225226      <p style={{ marginTop: "140px" }}>се вчитува...</p>
  • reactapp/src/Pages/Home.js

    r8d83180 rc68150f  
    2424      </a>{" "}
    2525      <Search />
    26       {auth && <UserHeader user={user} userLoaded={userLoaded} />}
     26      {auth && <UserHeader />}
    2727      <div style={{ marginTop: "140px" }}></div>
    2828      <Outlet />
  • reactapp/src/Pages/Login.js

    r8d83180 rc68150f  
    3535    if (!response.request.responseURL.includes("error")) {
    3636      // ako NE redirektira na /login?error
    37       Cookies.set("JSESSIONID", response.data.sessionId);
     37      var in30Minutes = 1 / 48;
     38      Cookies.set("JSESSIONID", response.data.sessionId, {
     39        expires: in30Minutes,
     40      });
    3841      setAuth(true);
    3942      setErrMsg("");
  • reactapp/src/Pages/Professor.js

    r8d83180 rc68150f  
    2525import { CurrentPageNav } from "../Components/Styled/Main.style";
    2626
    27 function Professor(user, userLoaded) {
     27function Professor() {
    2828  let params = useParams();
     29  let navigate = useNavigate();
    2930
    30   let [professor, setProfessor] = useState(null);
    31   let [loaded, setLoaded] = useState(null);
    32   let [postModalDisplay, setPostModalDisplay] = useState("none");
    33   let navigate = useNavigate();
     31  const [professor, setProfessor] = useState(null);
     32  const [loadedProfessor, setLoadedProfessor] = useState(false);
     33
     34  const [postModalDisplay, setPostModalDisplay] = useState("none");
    3435  const { auth, setAuth } = useContext(AuthApi);
    35   const [postTitle, setPostTitle] = useState("");
    3636  const [postContent, setPostContent] = useState("");
    3737  const [fetchError, setFetchError] = useState(false);
     38  const [errorMessage, setErrorMessage] = useState("");
    3839
    3940  useEffect(() => {
    4041    const url = `http://192.168.0.17:8080/public/professor/${params.professorId}`;
    4142
    42     const fetchData = async () => {
     43    const fetchProfessor = async () => {
    4344      try {
    4445        const response = await fetch(url);
     
    4748        cyclicGraph = JSOG.decode(jsogStructure);
    4849        setProfessor(cyclicGraph);
    49         setLoaded(true);
     50        setLoadedProfessor(true);
    5051      } catch (error) {
    5152        setFetchError(true);
     
    5354    };
    5455
    55     fetchData();
     56    fetchProfessor();
    5657  }, [params.professorId]);
    5758
     
    7172    e.preventDefault();
    7273
    73     const response = await axios(
    74       `http://192.168.0.17:8080/secure/professor/${professor.professorId}/addOpinion`,
    75       {
    76         method: "post",
    77         data: {
    78           title: postTitle,
    79           content: postContent,
    80         },
    81         withCredentials: true,
    82       }
    83     );
    84 
    85     window.location.reload(false);
    86   };
    87 
    88   const handleTitleChange = (e) => {
    89     setPostTitle(e.target.value);
     74    if (!postContent.length < 1) {
     75      const response = await axios(
     76        `http://192.168.0.17:8080/secure/professor/${params.professorId}/addOpinion`,
     77        {
     78          method: "post",
     79          data: {
     80            content: postContent,
     81          },
     82          withCredentials: true,
     83        }
     84      );
     85      setErrorMessage("");
     86      window.location.reload(false);
     87    } else {
     88      setErrorMessage("Полето за содржина не смее да биде празно");
     89    }
    9090  };
    9191
     
    9494  };
    9595
    96   if (loaded) {
     96  if (loadedProfessor) {
    9797    return (
    9898      <div>
     
    120120          </div>
    121121        </ProfessorCard>
    122         <div style={{ height: "20px", marginBottom: "30px" }}>
     122        <div style={{ height: "20px", marginBottom: "50px" }}>
    123123          <h3
    124124            style={{
     
    146146            <form onSubmit={handlePostSubmit}>
    147147              <ModalBody>
    148                 <label htmlFor="title">
    149                   <b>Наслов</b>:
    150                   <ModalInput
    151                     id="title"
    152                     type="text"
    153                     value={postTitle}
    154                     onChange={handleTitleChange}
    155                   />
    156                 </label>
    157148                <label htmlFor="content">
    158149                  <b>Содржина</b>:
     
    166157                </label>
    167158              </ModalBody>
     159              <p
     160                style={{ color: "red", marginLeft: "15px", marginTop: "10px" }}
     161              >
     162                {errorMessage}
     163              </p>
    168164              <ModalFooter type="submit">ОБЈАВИ</ModalFooter>
    169165            </form>
     
    172168
    173169        <div className="opinionTree">
    174           <OpinionTree
    175             professor={professor}
    176             user={user}
    177             userLoaded={userLoaded}
    178           />
     170          <OpinionTree professor={professor} />
    179171        </div>
    180172        <Outlet />
  • reactapp/src/Pages/Registration.js

    r8d83180 rc68150f  
    1 import React, { useRef, useState, useEffect } from "react";
     1import React, { useRef, useState, useEffect, useContext } from "react";
    22import axios from "../api/axios";
    33import {
     
    77  RequiredAsterisk,
    88} from "../Components/Styled/Login.style";
     9import { Navigate } from "react-router-dom";
     10import AuthApi from "../api/AuthApi";
    911const REGISTRATION_URL = "/registration";
    1012
    1113const Registration = () => {
     14  const { auth, setAuth } = useContext(AuthApi);
    1215  const userRef = useRef();
    1316  const errRef = useRef();
     
    5053    useState(0);
    5154
    52   return registrationSuccessful === false ? (
     55  return auth ? (
     56    <Navigate to="/user_dashboard" />
     57  ) : registrationSuccessful === false ? (
    5358    <div
    5459      style={{
  • reactapp/src/Pages/Subject.js

    r8d83180 rc68150f  
    1 import React from "react";
    2 import { useParams } from "react-router-dom";
     1import React, { useState, useEffect, useContext } from "react";
     2import { useNavigate, useParams } from "react-router-dom";
     3import JSOG from "jsog";
     4import { Outlet } from "react-router-dom";
     5import { CurrentPageNav } from "../Components/Styled/Main.style";
     6import {
     7  ProfessorCard,
     8  ProfessorCardDetails,
     9  ProfessorCardName,
     10  ProfessorCardSeparator,
     11} from "../Components/Styled/ProfessorCard.style";
     12import AuthApi from "../api/AuthApi";
     13import { AddOpinionButton } from "../Components/Styled/Modal.style";
     14import {
     15  EntityLi,
     16  EntityUl,
     17  EntityParam,
     18} from "../Components/Styled/EntityList.style";
     19import {
     20  Modal,
     21  ModalBody,
     22  ModalClose,
     23  ModalContent,
     24  ModalFooter,
     25  ModalHeader,
     26  ModalInput,
     27  ModalTextarea,
     28} from "../Components/Styled/Modal.style";
     29import axios from "../api/axios";
    330
    431const Subject = () => {
    532  let params = useParams();
    6   return <div>Subject {params.subjectId}</div>;
     33  let navigate = useNavigate();
     34
     35  const { auth, setAuth } = useContext(AuthApi);
     36  const [subject, setSubject] = useState(null);
     37  const [loaded, setLoaded] = useState(false);
     38  const [fetchError, setFetchError] = useState(false);
     39  var totalTopics = 0;
     40  var topics = [];
     41
     42  const [topicModalDisplay, setTopicModalDisplay] = useState("none");
     43  const [topicTitle, setTopicTitle] = useState("");
     44  const [topicContent, setTopicContent] = useState("");
     45  const [errorMessage, setErrorMessage] = useState("");
     46
     47  useEffect(() => {
     48    const url = `http://192.168.0.17:8080/public/subject/${params.subjectId}`;
     49
     50    const fetchData = async () => {
     51      try {
     52        const response = await fetch(url);
     53        let cyclicGraph = await response.json();
     54        let jsogStructure = JSOG.encode(cyclicGraph);
     55        cyclicGraph = JSOG.decode(jsogStructure);
     56        setSubject(cyclicGraph);
     57        setLoaded(true);
     58      } catch (error) {
     59        setFetchError(true);
     60      }
     61    };
     62
     63    fetchData();
     64  }, [params.subjectId]);
     65
     66  const handleAddTopicButtonClick = () => {
     67    if (auth) {
     68      setTopicModalDisplay("block");
     69    } else {
     70      navigate("/login");
     71    }
     72  };
     73
     74  const handleModalCloseClick = () => {
     75    setTopicModalDisplay("none");
     76  };
     77
     78  const handleTopicSubmit = async (e) => {
     79    e.preventDefault();
     80
     81    if (!topicTitle.length < 1 && !topicContent.length < 1) {
     82      const response = await axios(
     83        `http://192.168.0.17:8080/secure/subject/${params.subjectId}/addThread`,
     84        {
     85          method: "post",
     86          data: {
     87            title: topicTitle,
     88            content: topicContent,
     89          },
     90          withCredentials: true,
     91        }
     92      );
     93      setErrorMessage("");
     94      window.location.reload(false);
     95    } else {
     96      setErrorMessage("Полињата за наслов и содржина не смеат да бидат празни");
     97    }
     98  };
     99
     100  const handleContentChange = (e) => {
     101    setTopicContent(e.target.value);
     102  };
     103
     104  const handleTitleChange = (e) => {
     105    setTopicTitle(e.target.value);
     106  };
     107
     108  return loaded ? (
     109    <>
     110      <CurrentPageNav>
     111        &#187;{" "}
     112        <a
     113          href={
     114            "/university/" +
     115            subject.studyProgramme.faculty.university.universityId
     116          }
     117        >
     118          {subject.studyProgramme.faculty.university.universityName}
     119        </a>{" "}
     120        &#187;{" "}
     121        <a href={"/faculty/" + subject.studyProgramme.faculty.facultyId}>
     122          {subject.studyProgramme.faculty.facultyName}
     123        </a>{" "}
     124        &#187; <a href="#">{subject.subjectName}</a>
     125      </CurrentPageNav>
     126      <ProfessorCard>
     127        <ProfessorCardName>{subject.subjectName}</ProfessorCardName>
     128        <ProfessorCardSeparator />
     129        <div style={{ marginTop: "10px" }}>
     130          <ProfessorCardDetails fontSize="20px">
     131            {subject.studyProgramme.studyProgrammeName} (
     132            {subject.studyProgramme.cycle}
     133            {"."}
     134            {"циклус"})
     135          </ProfessorCardDetails>
     136          <ProfessorCardDetails fontSize="20px">
     137            {subject.studyProgramme.faculty.facultyName}
     138          </ProfessorCardDetails>
     139          <ProfessorCardDetails fontSize="15px">
     140            {subject.studyProgramme.faculty.university.universityName}
     141          </ProfessorCardDetails>
     142        </div>
     143      </ProfessorCard>
     144      <div style={{ height: "20px", marginBottom: "50px" }}>
     145        <h3
     146          style={{
     147            float: "left",
     148          }}
     149        >
     150          {subject.threads.map((thread) => {
     151            if (thread.parent === null) {
     152              totalTopics++;
     153              topics.push(thread);
     154            }
     155          })}
     156          {totalTopics} {totalTopics !== 1 ? "теми" : "тема"}
     157        </h3>
     158        {auth && (
     159          <AddOpinionButton onClick={handleAddTopicButtonClick}>
     160            Отвори тема
     161          </AddOpinionButton>
     162        )}
     163      </div>
     164      <Modal display={topicModalDisplay}>
     165        <ModalContent>
     166          <ModalHeader>
     167            <ModalClose onClick={handleModalCloseClick}>&times;</ModalClose>
     168            <h3 style={{ marginTop: "5px" }}>
     169              Тема во врска со {subject.subjectName}
     170            </h3>
     171          </ModalHeader>
     172          <form onSubmit={handleTopicSubmit}>
     173            <ModalBody>
     174              <label htmlFor="title">
     175                <b>Наслов</b>:
     176                <ModalInput
     177                  id="title"
     178                  value={topicTitle}
     179                  onChange={handleTitleChange}
     180                />
     181              </label>
     182              <label htmlFor="content">
     183                <b>Содржина</b>:
     184                <ModalTextarea
     185                  id="content"
     186                  rows="8"
     187                  cols="100"
     188                  value={topicContent}
     189                  onChange={handleContentChange}
     190                />
     191              </label>
     192            </ModalBody>
     193            <p style={{ color: "red", marginLeft: "15px", marginTop: "10px" }}>
     194              {errorMessage}
     195            </p>
     196            <ModalFooter type="submit">ОБЈАВИ</ModalFooter>
     197          </form>
     198        </ModalContent>
     199      </Modal>
     200      <div key={subject.subjectId}>
     201        {topics.map((topic) => {
     202          var numReplies = topic.children.length;
     203          return (
     204            <EntityUl key={topic.postId}>
     205              <EntityLi bgcolor="cornsilk">
     206                <a href={"/topic/" + topic.postId}>{topic.title}</a>
     207                <EntityParam right="30px">
     208                  <span style={{ fontWeight: "normal" }}>
     209                    отворил:{" "}
     210                    <a href={"/user/" + topic.author.id}>
     211                      {topic.author.username}
     212                    </a>
     213                  </span>
     214                  <span style={{ fontStyle: "normal" }}>,</span>{" "}
     215                  {numReplies !== 1 ? (
     216                    numReplies !== 0 ? (
     217                      <span
     218                        style={{
     219                          fontWeight: "normal",
     220                          opacity: numReplies === 0 ? "0.5" : "1",
     221                        }}
     222                      >
     223                        <span
     224                          style={{
     225                            fontWeight: "bold",
     226                            opacity: numReplies === 0 ? "0.5" : "1",
     227                          }}
     228                        >
     229                          {numReplies}
     230                        </span>{" "}
     231                        реплики
     232                      </span>
     233                    ) : (
     234                      <span
     235                        style={{
     236                          fontWeight: "normal",
     237                          opacity: numReplies === 0 ? "0.5" : "1",
     238                        }}
     239                      >
     240                        <span
     241                          style={{
     242                            fontWeight: "bold",
     243                            opacity: numReplies === 0 ? "0.5" : "1",
     244                          }}
     245                        >
     246                          {numReplies}
     247                        </span>{" "}
     248                        реплики
     249                      </span>
     250                    )
     251                  ) : (
     252                    <span style={{ fontWeight: "normal" }}>
     253                      <span style={{ fontWeight: "bold" }}>{numReplies}</span>{" "}
     254                      реплика
     255                    </span>
     256                  )}
     257                </EntityParam>
     258              </EntityLi>
     259            </EntityUl>
     260          );
     261        })}
     262      </div>
     263    </>
     264  ) : !fetchError ? (
     265    <div>
     266      <p style={{ marginTop: "140px" }}>се вчитува...</p>
     267      <Outlet />
     268    </div>
     269  ) : (
     270    <div style={{ marginTop: "140px" }}>
     271      <h1 style={{ textAlign: "center" }}>Страницата не е пронајдена.</h1>
     272    </div>
     273  );
    7274};
    8275
  • reactapp/src/Pages/University.js

    r8d83180 rc68150f  
    1818  let params = useParams();
    1919  const [loaded, setLoaded] = useState(false);
    20   const [faculties, setFaculties] = useState(false);
     20  const [faculties, setFaculties] = useState(null);
    2121  const [fetchError, setFetchError] = useState(false);
    2222
     
    3939  }, [params.universityId]);
    4040
    41   return loaded ? (
     41  return loaded && !fetchError && faculties.length !== 0 ? (
    4242    <>
    4343      <CurrentPageNav>
     
    7474                  {faculty.facultyName}
    7575                </a>
    76                 <EntityParam>
     76                <EntityParam right="30px">
    7777                  {totalSections}{" "}
    7878                  {totalSections !== 1 ? (
     
    103103      </div>
    104104    </>
    105   ) : !fetchError ? (
     105  ) : !fetchError && !loaded ? (
    106106    <div>
    107107      <p style={{ marginTop: "140px" }}>се вчитува...</p>
  • reactapp/src/Pages/UserDashboard.js

    r8d83180 rc68150f  
    1 import React, { useEffect } from "react";
     1import React, { useEffect, useState, useContext } from "react";
    22import {
    33  OpinionCard,
    44  OpinionCardContent,
    55  OpinionCardContentTime,
    6   OpinionCardContentTitle,
    76} from "../Components/Styled/OpinionCard.style";
    87import {
     
    1110} from "../Components/Styled/UserDetails.style";
    1211import { dateConverter } from "../Util/dateConverter";
     12import axios from "../api/axios";
     13import JSOG from "jsog";
     14import AuthApi from "../api/AuthApi";
    1315
    14 function UserDashboard({ user, userLoaded }) {
     16function UserDashboard() {
     17  const { auth, setAuth } = useContext(AuthApi);
     18
     19  const [user, setUser] = useState(null);
     20  const [loadedUser, setLoadedUser] = useState(false);
     21  const [fetchError, setFetchError] = useState(false);
     22
    1523  useEffect(() => {
    16     const timer = setTimeout(() => {
    17       if (user === null) window.location.reload(false);
    18     }, 3000);
    19     return () => clearTimeout(timer);
     24    const url = `http://192.168.0.17:8080/secure/currentUser`;
     25
     26    const fetchUser = async () => {
     27      try {
     28        const response = await axios.get(url, { withCredentials: true });
     29        var cyclicGraph = await response.data;
     30        var jsogStructure = JSOG.encode(cyclicGraph);
     31        cyclicGraph = JSOG.decode(jsogStructure);
     32        setUser(cyclicGraph);
     33        setLoadedUser(true);
     34      } catch (error) {
     35        setFetchError(true);
     36      }
     37    };
     38
     39    if (auth) fetchUser();
    2040  }, []);
    2141
    22   return userLoaded ? (
     42  // useEffect(() => {
     43  //   const timer = setTimeout(() => {
     44  //     if (user === null) window.location.reload(false); <---- :-)
     45  //   }, 3000);
     46  //   return () => clearTimeout(timer);
     47  // }, []);
     48
     49  function findParentThread(post) {
     50    if (post.parent === null) return post;
     51    return findParentThread(post.parent);
     52  }
     53
     54  return loadedUser ? (
    2355    <>
    2456      <h3>Кориснички податоци:</h3>
     
    4981            <OpinionCard>
    5082              <OpinionCardContent>
    51                 <p>
    52                   Во дискусија за{" "}
    53                   <a href={"/professor/" + post.targetProfessor.professorId}>
    54                     {post.targetProfessor.professorName}
    55                   </a>
     83                <p style={{ fontStyle: "italic", marginBottom: "10px" }}>
     84                  во дискусија за{" "}
     85                  {post.targetProfessor !== undefined ? (
     86                    <a href={"/professor/" + post.targetProfessor.professorId}>
     87                      {post.targetProfessor.professorName}
     88                    </a>
     89                  ) : (
     90                    <a
     91                      href={
     92                        post.parent === null
     93                          ? "/topic/" + post.postId
     94                          : "/topic/" + findParentThread(post).postId
     95                      }
     96                    >
     97                      {post.targetSubject.subjectName}
     98                    </a>
     99                  )}
    56100                </p>
    57                 <OpinionCardContentTitle>{post.title}</OpinionCardContentTitle>
    58                 <p>{post.content}</p>
     101                <p style={{ marginBottom: "10px" }}>{post.content}</p>
    59102                <OpinionCardContentTime>
    60103                  {dateConverter(
  • springapp/pom.xml

    r8d83180 rc68150f  
    3030                        <artifactId>spring-boot-starter-web</artifactId>
    3131                </dependency>
    32 
    3332                <dependency>
    3433                        <groupId>org.postgresql</groupId>
     
    6362                        <scope>compile</scope>
    6463                </dependency>
     64               
    6565        </dependencies>
    6666
  • springapp/src/main/java/mk/profesori/springapp/Controller/PublicController.java

    r8d83180 rc68150f  
    1818import mk.profesori.springapp.Model.Professor;
    1919import mk.profesori.springapp.Model.StudyProgramme;
     20import mk.profesori.springapp.Model.Subject;
    2021import mk.profesori.springapp.Model.University;
     22import mk.profesori.springapp.Model._Thread;
    2123import mk.profesori.springapp.Service.MainService;
    2224
    2325@RestController
    2426@RequestMapping("/public")
    25 @CrossOrigin(origins = { "http://192.168.0.17:3000", "http://192.168.0.28:3000" })
     27@CrossOrigin(origins = { "http://192.168.0.17:3000", "http://192.168.0.39:3000" })
    2628public class PublicController {
    2729
     
    98100    }
    99101
     102    @RequestMapping(value = "/subject/{subjectId}", method = RequestMethod.GET)
     103    public Subject getSubjectById(@PathVariable Long subjectId) {
     104        return mainService.getSubjectById(subjectId); // vrakja predmet spored id
     105    }
     106
     107    @RequestMapping(value = "/thread/{postId}", method = RequestMethod.GET)
     108    public _Thread getThreadById(@PathVariable Long postId) {
     109        return mainService.get_ThreadById(postId); // vrakja thread (tema) spored id
     110    }
     111
    100112    @RequestMapping(value = "/loginSuccessRegular", method = RequestMethod.GET)
    101113    public Map<String, String> loginSuccessRegular(@RequestParam String sessionId) {
  • springapp/src/main/java/mk/profesori/springapp/Controller/SecureController.java

    r8d83180 rc68150f  
    11package mk.profesori.springapp.Controller;
    22
    3 import java.util.Collections;
    4 import java.util.Map;
    5 
    6 import org.springframework.beans.factory.annotation.Autowired;
     3import com.fasterxml.jackson.databind.node.ObjectNode;
     4import mk.profesori.springapp.Model.CustomUserDetails;
     5import mk.profesori.springapp.Service.CustomUserDetailsService;
     6import mk.profesori.springapp.Service.MainService;
    77import org.springframework.security.core.Authentication;
    88import org.springframework.security.core.annotation.CurrentSecurityContext;
    99import org.springframework.security.core.context.SecurityContext;
    1010import org.springframework.security.core.userdetails.UserDetails;
    11 import org.springframework.web.bind.annotation.CrossOrigin;
    12 import org.springframework.web.bind.annotation.PathVariable;
    13 import org.springframework.web.bind.annotation.RequestBody;
    14 import org.springframework.web.bind.annotation.RequestMapping;
    15 import org.springframework.web.bind.annotation.RequestMethod;
    16 import org.springframework.web.bind.annotation.RequestParam;
    17 import org.springframework.web.bind.annotation.RestController;
    18 
    19 import com.fasterxml.jackson.databind.node.ObjectNode;
    20 
    21 import mk.profesori.springapp.Model.CustomUserDetails;
    22 import mk.profesori.springapp.Service.CustomUserDetailsService;
    23 import mk.profesori.springapp.Service.MainService;
     11import org.springframework.web.bind.annotation.*;
    2412
    2513@RestController
    2614@RequestMapping("/secure")
    27 @CrossOrigin(origins = { "http://192.168.0.17:3000", "http://192.168.0.28:3000" })
     15@CrossOrigin(origins = { "http://192.168.0.17:3000", "http://192.168.0.39:3000" })
    2816public class SecureController {
    2917
    30     @Autowired
    31     private MainService mainService;
    32     @Autowired
     18    private final MainService mainService;
     19    final
    3320    CustomUserDetailsService customUserDetailsService;
     21
     22    public SecureController(MainService mainService, CustomUserDetailsService customUserDetailsService) {
     23        this.mainService = mainService;
     24        this.customUserDetailsService = customUserDetailsService;
     25    }
    3426
    3527    @RequestMapping(value = "/professor/{professorId}/addOpinion", method = RequestMethod.POST)
     
    3931        Authentication authentication = context.getAuthentication();
    4032
    41         if (authentication != null && authentication.getPrincipal() instanceof CustomUserDetails) {
    42             CustomUserDetails currentUser = (CustomUserDetails) authentication.getPrincipal();
    43             String title = objectNode.get("title").asText();
     33        if (authentication != null && authentication.getPrincipal() instanceof CustomUserDetails currentUser) {
    4434            String content = objectNode.get("content").asText();
    45             mainService.addOpinion(title, content, professorId, currentUser);
     35            mainService.addOpinion(content, professorId, currentUser);
    4636        }
    4737    }
     
    5343        Authentication authentication = context.getAuthentication();
    5444
    55         if (authentication != null && authentication.getPrincipal() instanceof CustomUserDetails) {
    56             CustomUserDetails currentUser = (CustomUserDetails) authentication.getPrincipal();
     45        if (authentication != null && authentication.getPrincipal() instanceof CustomUserDetails currentUser) {
    5746            String content = objectNode.get("content").asText();
    5847            mainService.replyToOpinion(content, professorId, postId, currentUser);
     48        }
     49    }
     50
     51    @RequestMapping(value = "/subject/{subjectId}/addThread", method = RequestMethod.POST)
     52    public void addThread(@RequestBody ObjectNode objectNode, @PathVariable Long subjectId,
     53            @CurrentSecurityContext SecurityContext context) {
     54
     55        Authentication authentication = context.getAuthentication();
     56
     57        if (authentication != null && authentication.getPrincipal() instanceof CustomUserDetails currentUser) {
     58            String title = objectNode.get("title").asText();
     59            String content = objectNode.get("content").asText();
     60            mainService.addThread(title, content, subjectId, currentUser);
     61        }
     62    }
     63
     64    @RequestMapping(value = "/subject/{subjectId}/replyToThread/{postId}", method = RequestMethod.POST)
     65    public void replyToThread(@RequestBody ObjectNode objectNode, @PathVariable Long subjectId,
     66            @PathVariable Long postId, @CurrentSecurityContext SecurityContext context) {
     67
     68        Authentication authentication = context.getAuthentication();
     69
     70        if (authentication != null && authentication.getPrincipal() instanceof CustomUserDetails currentUser) {
     71            String content = objectNode.get("content").asText();
     72            mainService.replyToThread(content, subjectId, postId, currentUser);
    5973        }
    6074    }
     
    6478
    6579        Authentication authentication = context.getAuthentication();
    66         if (authentication != null && authentication.getPrincipal() instanceof CustomUserDetails) {
    67             CustomUserDetails currentUser = (CustomUserDetails) authentication.getPrincipal();
     80        if (authentication != null && authentication.getPrincipal() instanceof CustomUserDetails currentUser) {
    6881            return customUserDetailsService.loadUserByUsername(currentUser.getEmail());
    6982        }
     
    7285    }
    7386
    74     @RequestMapping(value = "/professor/{professorId}/upvoteOpinion/{postId}", method = RequestMethod.GET)
    75     public void upvoteOpinion(@PathVariable Long professorId,
    76             @PathVariable Long postId, @CurrentSecurityContext SecurityContext context) {
     87    @RequestMapping(value = "/upvoteOpinion/{postId}", method = RequestMethod.GET)
     88    public void upvoteOpinion(@PathVariable Long postId, @CurrentSecurityContext SecurityContext context) {
    7789
    7890        Authentication authentication = context.getAuthentication();
    7991
    80         if (authentication != null && authentication.getPrincipal() instanceof CustomUserDetails) {
    81             CustomUserDetails currentUser = (CustomUserDetails) authentication.getPrincipal();
     92        if (authentication != null && authentication.getPrincipal() instanceof CustomUserDetails currentUser) {
    8293            mainService.upvoteOpinion(postId, currentUser);
    8394        }
    8495    }
    8596
    86     @RequestMapping(value = "/professor/{professorId}/downvoteOpinion/{postId}", method = RequestMethod.GET)
    87     public void downvoteOpinion(@PathVariable Long professorId,
    88             @PathVariable Long postId, @CurrentSecurityContext SecurityContext context) {
     97    @RequestMapping(value = "/downvoteOpinion/{postId}", method = RequestMethod.GET)
     98    public void downvoteOpinion(@PathVariable Long postId, @CurrentSecurityContext SecurityContext context) {
    8999
    90100        Authentication authentication = context.getAuthentication();
    91101
    92         if (authentication != null && authentication.getPrincipal() instanceof CustomUserDetails) {
    93             CustomUserDetails currentUser = (CustomUserDetails) authentication.getPrincipal();
     102        if (authentication != null && authentication.getPrincipal() instanceof CustomUserDetails currentUser) {
    94103            mainService.downvoteOpinion(postId, currentUser);
    95104        }
    96105    }
    97106
     107    @RequestMapping(value = "/upvoteThread/{postId}", method = RequestMethod.GET)
     108    public void upvoteThread(@PathVariable Long postId, @CurrentSecurityContext SecurityContext context) {
     109
     110        Authentication authentication = context.getAuthentication();
     111
     112        if (authentication != null && authentication.getPrincipal() instanceof CustomUserDetails currentUser) {
     113            mainService.upvote_Thread(postId, currentUser);
     114        }
     115    }
     116
     117    @RequestMapping(value = "/downvoteThread/{postId}", method = RequestMethod.GET)
     118    public void downvoteThread(@PathVariable Long postId, @CurrentSecurityContext SecurityContext context) {
     119
     120        Authentication authentication = context.getAuthentication();
     121
     122        if (authentication != null && authentication.getPrincipal() instanceof CustomUserDetails currentUser) {
     123            mainService.downvote_Thread(postId, currentUser);
     124        }
     125    }
     126
    98127}
  • springapp/src/main/java/mk/profesori/springapp/Model/ConfirmationToken.java

    r8d83180 rc68150f  
    1212import javax.persistence.SequenceGenerator;
    1313
     14import com.fasterxml.jackson.annotation.JsonIdentityInfo;
     15import com.voodoodyne.jackson.jsog.JSOGGenerator;
     16
    1417import lombok.Getter;
    1518import lombok.NoArgsConstructor;
     
    2023@NoArgsConstructor
    2124@Entity
     25@JsonIdentityInfo(generator = JSOGGenerator.class)
    2226public class ConfirmationToken {
    23    
     27
    2428    @Id
    2529    @SequenceGenerator(name = "confirmation_token_sequence", sequenceName = "confirmation_token_sequence", allocationSize = 1)
     
    4246    private CustomUserDetails customUserDetails;
    4347
    44     public ConfirmationToken(String token, LocalDateTime createdAt, LocalDateTime expiredAt, 
    45     CustomUserDetails customUserDetails) {
    46        
     48    public ConfirmationToken(String token, LocalDateTime createdAt, LocalDateTime expiredAt,
     49            CustomUserDetails customUserDetails) {
     50
    4751        this.token = token;
    4852        this.createdAt = createdAt;
  • springapp/src/main/java/mk/profesori/springapp/Model/CustomUserDetails.java

    r8d83180 rc68150f  
    11package mk.profesori.springapp.Model;
    22
    3 import java.util.ArrayList;
    4 import java.util.Collection;
    5 import java.util.Collections;
    6 import java.util.HashSet;
    7 import java.util.List;
    8 import java.util.Set;
    9 
    10 import javax.persistence.CascadeType;
    11 import javax.persistence.Entity;
    12 import javax.persistence.EnumType;
    13 import javax.persistence.Enumerated;
    14 import javax.persistence.FetchType;
    15 import javax.persistence.GeneratedValue;
    16 import javax.persistence.GenerationType;
    17 import javax.persistence.Id;
    18 import javax.persistence.JoinTable;
    19 import javax.persistence.JoinColumn;
    20 import javax.persistence.ManyToMany;
    21 import javax.persistence.OneToMany;
    22 import javax.persistence.SequenceGenerator;
    23 
     3import com.fasterxml.jackson.annotation.JsonIdentityInfo;
     4import com.voodoodyne.jackson.jsog.JSOGGenerator;
     5import lombok.EqualsAndHashCode;
     6import lombok.Getter;
     7import lombok.NoArgsConstructor;
     8import lombok.Setter;
    249import org.springframework.security.core.GrantedAuthority;
    2510import org.springframework.security.core.authority.SimpleGrantedAuthority;
    2611import org.springframework.security.core.userdetails.UserDetails;
    2712
    28 import com.fasterxml.jackson.annotation.JsonIdentityInfo;
    29 import com.voodoodyne.jackson.jsog.JSOGGenerator;
    30 
    31 import lombok.EqualsAndHashCode;
    32 import lombok.Getter;
    33 import lombok.NoArgsConstructor;
    34 import lombok.Setter;
     13import javax.persistence.*;
     14import java.util.Collection;
     15import java.util.Collections;
     16import java.util.HashSet;
     17import java.util.Set;
    3518
    3619@Getter
     
    5942    private Set<Post> authoredPosts = new HashSet<>();
    6043    private Integer karma = 0;
    61     @ManyToMany(fetch = FetchType.EAGER)
    62     @JoinTable(name = "post_like", joinColumns = @JoinColumn(name = "custom_user_details_id"), inverseJoinColumns = @JoinColumn(name = "post_id"))
    63     Set<Post> likedPosts;
    64     @ManyToMany(fetch = FetchType.EAGER)
    65     @JoinTable(name = "post_dislike", joinColumns = @JoinColumn(name = "custom_user_details_id"), inverseJoinColumns = @JoinColumn(name = "post_id"))
    66     Set<Post> dislikedPosts;
     44
     45    public Set<PostVote> getVotes() {
     46        return votes;
     47    }
     48
     49    public void setVotes(Set<PostVote> votes) {
     50        this.votes = votes;
     51    }
     52
     53    @OneToMany(mappedBy = "user")
     54    private Set<PostVote> votes = new HashSet<>();
    6755
    6856    public CustomUserDetails(String fullName, String username, String email, String password, UserRole userRole) {
     
    122110    }
    123111
    124     public Set<Post> getLikedPosts() {
    125         return this.likedPosts;
     112    @Override
     113    public String toString() {
     114        return this.id.toString();
    126115    }
    127116
    128     public void setLikedPosts(Set<Post> likedPosts) {
    129         this.likedPosts = likedPosts;
    130     }
    131 
    132     public Set<Post> getDislikedPosts() {
    133         return this.dislikedPosts;
    134     }
    135 
    136     public void setDislikedPosts(Set<Post> dislikedPosts) {
    137         this.likedPosts = dislikedPosts;
    138     }
    139117}
  • springapp/src/main/java/mk/profesori/springapp/Model/Post.java

    r8d83180 rc68150f  
    33import java.time.LocalDateTime;
    44import java.util.ArrayList;
     5import java.util.HashSet;
    56import java.util.List;
    67import java.util.Set;
     
    5556
    5657    @ManyToOne
    57     @JoinColumn(name = "parent_post_id", nullable = true)
     58    @JoinColumn(name = "parent_post_id")
    5859    private Post parent;
     60
     61    @OneToMany(mappedBy = "post")
     62    private Set<PostVote> votes = new HashSet<>();
     63
     64    public Set<PostVote> getVotes() {
     65        return votes;
     66    }
     67
     68    public void setVotes(Set<PostVote> votes) {
     69        this.votes = votes;
     70    }
    5971
    6072    @OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
    6173    private List<Post> children = new ArrayList<>();
    62 
    63     @ManyToMany(mappedBy = "likedPosts")
    64     Set<CustomUserDetails> likes;
    65 
    66     @ManyToMany(mappedBy = "dislikedPosts")
    67     Set<CustomUserDetails> dislikes;
    6874
    6975    // getters and setters
     
    132138    }
    133139
    134     public Set<CustomUserDetails> getLikes() {
    135         return likes;
    136     }
    137 
    138     public void setLikes(Set<CustomUserDetails> likes) {
    139         this.likes = likes;
    140     }
    141 
    142     public Set<CustomUserDetails> getDislikes() {
    143         return dislikes;
    144     }
    145 
    146     public void setDislikes(Set<CustomUserDetails> dislikes) {
    147         this.dislikes = dislikes;
    148     }
    149 
    150140    // konstruktor so parent (koga e reply)
    151141    public Post(String title, String content, CustomUserDetails author, LocalDateTime timePosted,
     
    174164    }
    175165
     166    @Override
     167    public String toString() {
     168        return this.postId.toString();
     169    }
     170
    176171}
  • springapp/src/main/java/mk/profesori/springapp/Model/StudyProgramme.java

    r8d83180 rc68150f  
    1212import javax.persistence.ManyToOne;
    1313import javax.persistence.OneToMany;
    14 import javax.persistence.OneToOne;
    1514import javax.persistence.Table;
    1615
     
    4140    private Set<Subject> subjects = new HashSet<>();
    4241
    43     @OneToOne(mappedBy = "relatedStudyProgramme")
    44     private Section relatedSection;
    45 
    4642    // getters
    4743    public Long getStudyProgrammeId() {
     
    6561    }
    6662
    67     public Section getRelatedSection() {
    68         return relatedSection;
    69     }
    7063}
  • springapp/src/main/java/mk/profesori/springapp/Model/Subject.java

    r8d83180 rc68150f  
    1515
    1616import com.fasterxml.jackson.annotation.JsonIdentityInfo;
    17 import com.fasterxml.jackson.annotation.ObjectIdGenerators;
     17import com.voodoodyne.jackson.jsog.JSOGGenerator;
    1818
    1919@Entity
    2020@Table(name = "subject")
    21 @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "subjectId")
     21@JsonIdentityInfo(generator = JSOGGenerator.class)
    2222public class Subject {
    2323
  • springapp/src/main/java/mk/profesori/springapp/Model/University.java

    r8d83180 rc68150f  
    1515
    1616import com.fasterxml.jackson.annotation.JsonIdentityInfo;
    17 import com.fasterxml.jackson.annotation.ObjectIdGenerators;
     17import com.voodoodyne.jackson.jsog.JSOGGenerator;
    1818
    1919@Entity
    2020@Table(name = "university")
    21 @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "universityId")
     21@JsonIdentityInfo(generator = JSOGGenerator.class)
    2222public class University {
    2323
  • springapp/src/main/java/mk/profesori/springapp/Model/_Thread.java

    r8d83180 rc68150f  
    22
    33import java.time.LocalDateTime;
    4 import java.util.ArrayList;
     4
    55import java.util.List;
    66
    7 import javax.persistence.Column;
    87import javax.persistence.DiscriminatorValue;
    9 import javax.persistence.ElementCollection;
     8
    109import javax.persistence.Entity;
    1110import javax.persistence.JoinColumn;
    1211import javax.persistence.ManyToOne;
    1312
     13import lombok.NoArgsConstructor;
     14
    1415@Entity
    1516@DiscriminatorValue("thread")
     17@NoArgsConstructor
    1618public class _Thread extends Post {
    17 
    18     @Column(name = "tags") // unused
    19     @ElementCollection
    20     private List<String> tags = new ArrayList<>();
    21 
    22     @ManyToOne
    23     @JoinColumn(name = "section_id")
    24     private Section parentSection;
    2519
    2620    @ManyToOne
     
    3125    public _Thread(String title, String content, CustomUserDetails author, LocalDateTime timePosted,
    3226            LocalDateTime timeLastEdited,
    33             Post parent, List<Post> children, Section parentSection, Subject targetSubject) {
     27            Post parent, List<Post> children, Subject targetSubject) {
    3428        super(title, content, author, timePosted, timeLastEdited, parent, children);
    35         this.parentSection = parentSection;
    3629        this.targetSubject = targetSubject;
    3730    }
     
    3932    // konstruktor bez parent (koga NE e reply)
    4033    public _Thread(String title, String content, CustomUserDetails author, LocalDateTime timePosted,
    41             LocalDateTime timeLastEdited, List<Post> children, Section parentSection, Subject targetSubject) {
     34            LocalDateTime timeLastEdited, List<Post> children, Subject targetSubject) {
    4235        super(title, content, author, timePosted, timeLastEdited, children);
    43         this.parentSection = parentSection;
    4436        this.targetSubject = targetSubject;
    45     }
    46 
    47     // getters
    48     public List<String> getTags() {
    49         return tags;
    50     }
    51 
    52     public Section getParentSection() {
    53         return parentSection;
    5437    }
    5538
  • springapp/src/main/java/mk/profesori/springapp/Repository/OpinionRepository.java

    r8d83180 rc68150f  
    11package mk.profesori.springapp.Repository;
    22
     3import mk.profesori.springapp.Model.Opinion;
    34import org.springframework.data.repository.CrudRepository;
    45import org.springframework.stereotype.Repository;
    56
    6 import mk.profesori.springapp.Model.Opinion;
    7 
    87@Repository
    9 public interface OpinionRepository extends CrudRepository<Opinion, Long> {
    10     public Opinion findByPostId(Long id);
     8public interface OpinionRepository extends CrudRepository<Opinion,Long> {
     9    Opinion findByPostId(Long postId);
    1110}
  • springapp/src/main/java/mk/profesori/springapp/Security/SecurityConfiguration.java

    r8d83180 rc68150f  
    3737            @Override
    3838            public void addCorsMappings(CorsRegistry registry) {
    39                 registry.addMapping("/**").allowedOrigins("http://192.168.0.17:3000", "http://192.168.0.28:3000")
     39                registry.addMapping("/**").allowedOrigins("http://192.168.0.17:3000", "http://192.168.0.39:3000")
    4040                        .allowCredentials(true);
    4141            }
  • springapp/src/main/java/mk/profesori/springapp/Service/EmailValidator.java

    r8d83180 rc68150f  
    1313    public boolean test(String email) {
    1414       
    15         String regex = "^(.+)@(.+)$";
     15        String regex = "^(.+)@(.+)$"; //TODO bilosto@@ ->> illegal address exception od mailsender
    1616
    1717        Pattern pattern = Pattern.compile(regex);
  • springapp/src/main/java/mk/profesori/springapp/Service/MainService.java

    r8d83180 rc68150f  
    11package mk.profesori.springapp.Service;
     2
     3import mk.profesori.springapp.Model.*;
     4import mk.profesori.springapp.Repository.*;
     5import org.springframework.stereotype.Service;
    26
    37import java.util.ArrayList;
    48import java.util.List;
    59
    6 import org.springframework.beans.factory.annotation.Autowired;
    7 import org.springframework.stereotype.Service;
    8 
    9 import mk.profesori.springapp.Repository.CityRepository;
    10 import mk.profesori.springapp.Repository.FacultyRepository;
    11 import mk.profesori.springapp.Repository.OpinionRepository;
    12 import mk.profesori.springapp.Repository.ProfessorRepository;
    13 import mk.profesori.springapp.Repository.StudyProgrammeRepository;
    14 import mk.profesori.springapp.Repository.UniversityRepository;
    15 import mk.profesori.springapp.Repository.UserRepository;
    16 import mk.profesori.springapp.Model.City;
    17 import mk.profesori.springapp.Model.CustomUserDetails;
    18 import mk.profesori.springapp.Model.Faculty;
    19 import mk.profesori.springapp.Model.Opinion;
    20 import mk.profesori.springapp.Model.Professor;
    21 import mk.profesori.springapp.Model.StudyProgramme;
    22 import mk.profesori.springapp.Model.University;
    23 
    2410@Service
    2511public class MainService {
    2612
    27     @Autowired
    28     private ProfessorRepository professorRepository;
    29     @Autowired
    30     private StudyProgrammeRepository studyProgrammeRepository;
    31     @Autowired
    32     private FacultyRepository facultyRepository;
    33     @Autowired
    34     private UniversityRepository universityRepository;
    35     @Autowired
    36     private CityRepository cityRepository;
    37     @Autowired
    38     private OpinionRepository opinionRepository;
    39     @Autowired
    40     private UserRepository userRepository;
     13    private final ProfessorRepository professorRepository;
     14    private final StudyProgrammeRepository studyProgrammeRepository;
     15    private final FacultyRepository facultyRepository;
     16    private final UniversityRepository universityRepository;
     17    private final CityRepository cityRepository;
     18    private final OpinionRepository opinionRepository;
     19    private final _ThreadRepository _threadRepository;
     20    private final SubjectRepository subjectRepository;
     21    private final PostVoteRepository postVoteRepository;
     22    private final UserRepository userRepository;
     23
     24    public MainService(ProfessorRepository professorRepository, StudyProgrammeRepository studyProgrammeRepository, FacultyRepository facultyRepository, UniversityRepository universityRepository, CityRepository cityRepository, OpinionRepository opinionRepository, _ThreadRepository _threadRepository, SubjectRepository subjectRepository, PostVoteRepository postVoteRepository, UserRepository userRepository) {
     25        this.professorRepository = professorRepository;
     26        this.studyProgrammeRepository = studyProgrammeRepository;
     27        this.facultyRepository = facultyRepository;
     28        this.universityRepository = universityRepository;
     29        this.cityRepository = cityRepository;
     30        this.opinionRepository = opinionRepository;
     31        this._threadRepository = _threadRepository;
     32        this.subjectRepository = subjectRepository;
     33        this.postVoteRepository = postVoteRepository;
     34        this.userRepository = userRepository;
     35    }
    4136
    4237    public List<Professor> getAllProfessors() {
    4338
    44         List<Professor> list = new ArrayList<>();
    45         professorRepository.findAll().forEach(list::add);
    46         return list;
     39        return new ArrayList<>(professorRepository.findAll());
    4740    }
    4841
     
    5649        Faculty faculty = facultyRepository.findByFacultyId(facultyId);
    5750
    58         List<Professor> list = new ArrayList<>();
    59         professorRepository.findByFaculty(faculty).forEach(list::add);
    60         return list;
     51        return new ArrayList<>(professorRepository.findByFaculty(faculty));
    6152    }
    6253
    6354    public List<Professor> getProfessorsByNameContains(String contained) {
    64         List<Professor> list = new ArrayList<>();
    65         professorRepository.findByProfessorNameContainingIgnoreCase(contained).forEach(list::add);
    66         return list;
     55        return new ArrayList<>(professorRepository.findByProfessorNameContainingIgnoreCase(contained));
    6756    }
    6857
    6958    public List<StudyProgramme> getAllStudyProgrammes() {
    7059
    71         List<StudyProgramme> list = new ArrayList<>();
    72         studyProgrammeRepository.findAll().forEach(list::add);
    73         return list;
     60        return new ArrayList<>(studyProgrammeRepository.findAll());
    7461    }
    7562
     
    8370        Faculty faculty = facultyRepository.findByFacultyId(facultyId);
    8471
    85         List<StudyProgramme> list = new ArrayList<>();
    86         studyProgrammeRepository.findByFaculty(faculty).forEach(list::add);
    87         return list;
     72        return new ArrayList<>(studyProgrammeRepository.findByFaculty(faculty));
    8873    }
    8974
    9075    public List<Faculty> getAllFaculties() {
    91         List<Faculty> list = new ArrayList<>();
    92         facultyRepository.findAll().forEach(list::add);
    93         return list;
     76        return new ArrayList<>(facultyRepository.findAll());
    9477    }
    9578
     
    10285        University university = universityRepository.findByUniversityId(universityId);
    10386
    104         List<Faculty> list = new ArrayList<>();
    105         facultyRepository.findByUniversity(university).forEach(list::add);
    106         return list;
     87        return new ArrayList<>(facultyRepository.findByUniversity(university));
    10788    }
    10889
    10990    public List<University> getAllUniversities() {
    110         List<University> list = new ArrayList<>();
    111         universityRepository.findAll().forEach(list::add);
    112         return list;
     91        return new ArrayList<>(universityRepository.findAll());
    11392    }
    11493
     
    121100        City city = cityRepository.findByCityId(cityId);
    122101
    123         List<University> list = new ArrayList<>();
    124         universityRepository.findByCity(city).forEach(list::add);
    125         return list;
     102        return new ArrayList<>(universityRepository.findByCity(city));
    126103    }
    127104
    128105    public List<City> getAllCities() {
    129         List<City> list = new ArrayList<>();
    130         cityRepository.findAll().forEach(list::add);
    131         return list;
     106        return new ArrayList<>(cityRepository.findAll());
    132107    }
    133108
     
    136111    }
    137112
    138     public void addOpinion(String title, String content, Long professorId, CustomUserDetails currentUser) {
     113    public void addOpinion(String content, Long professorId, CustomUserDetails currentUser) {
    139114
    140115        Professor targetProfessor = professorRepository.findByProfessorId(professorId);
    141116
    142         Opinion opinionToAdd = new Opinion(title, content, currentUser, null, null,
     117        Opinion opinionToAdd = new Opinion(null, content, currentUser, null, null,
    143118                null, targetProfessor);
    144119
     
    155130        opinionRepository.save(opinionToAdd);
    156131
     132        //mozda ne treba
    157133        targetOpinion.getChildren().add(opinionToAdd);
    158134        opinionRepository.save(targetOpinion);
    159135    }
    160136
    161     public void upvoteOpinion(Long postId, CustomUserDetails currentUser) {
    162         Opinion targetOpinion = opinionRepository.findByPostId(postId);
     137    public void addThread(String title, String content, Long subjectId, CustomUserDetails currentUser) {
     138        Subject targetSubject = subjectRepository.findBySubjectId(subjectId);
    163139
    164         if (!targetOpinion.getLikes().contains(currentUser)) {
    165 
    166             targetOpinion.getLikes().add(currentUser);
    167             // opinionRepository.save(targetOpinion);
    168 
    169             targetOpinion.getAuthor().setKarma(targetOpinion.getAuthor().getKarma() + 1);
    170             userRepository.save(targetOpinion.getAuthor());
    171 
    172             currentUser.getLikedPosts().add(targetOpinion);
    173             userRepository.save(currentUser);
    174         }
     140        _Thread _threadToAdd = new _Thread(title, content, currentUser, null, null, null, targetSubject);
     141        _threadRepository.save(_threadToAdd);
    175142    }
    176143
     144    public void replyToThread(String content, Long subjectId, Long postId, CustomUserDetails currentUser) {
     145        Subject targetSubject = subjectRepository.findBySubjectId(subjectId);
     146        _Thread target_Thread = _threadRepository.findByPostId(postId);
     147
     148        _Thread _threadToAdd = new _Thread(null, content, currentUser, null, null, target_Thread, null, targetSubject);
     149        _threadRepository.save(_threadToAdd);
     150
     151        //mozda ne treba
     152        target_Thread.getChildren().add(_threadToAdd);
     153        _threadRepository.save(target_Thread);
     154    }
     155
     156    public Subject getSubjectById(Long subjectId) {
     157        return subjectRepository.findBySubjectId(subjectId);
     158    }
     159
     160    public _Thread get_ThreadById(Long postId) {
     161        return _threadRepository.findByPostId(postId);
     162    }
     163
     164    public void upvoteOpinion(Long postId, CustomUserDetails currentUser) {
     165        Post targetPost = opinionRepository.findByPostId(postId);
     166        PostVote voteToAdd = new PostVote(currentUser, targetPost, VoteType.UPVOTE);
     167        postVoteRepository.save(voteToAdd);
     168        targetPost.getAuthor().setKarma(targetPost.getAuthor().getKarma()+1);
     169        userRepository.save(targetPost.getAuthor());
     170    }
    177171    public void downvoteOpinion(Long postId, CustomUserDetails currentUser) {
    178         Opinion targetOpinion = opinionRepository.findByPostId(postId);
     172        Post targetPost = opinionRepository.findByPostId(postId);
     173        PostVote voteToAdd = new PostVote(currentUser, targetPost, VoteType.DOWNVOTE);
     174        postVoteRepository.save(voteToAdd);
     175        targetPost.getAuthor().setKarma(targetPost.getAuthor().getKarma()-1);
     176        userRepository.save(targetPost.getAuthor());
     177    }
    179178
    180         if (!targetOpinion.getDislikes().contains(currentUser)) {
     179    public void upvote_Thread(Long postId, CustomUserDetails currentUser) {
     180        Post targetPost = _threadRepository.findByPostId(postId);
     181        PostVote voteToAdd = new PostVote(currentUser, targetPost, VoteType.UPVOTE);
     182        postVoteRepository.save(voteToAdd);
     183        targetPost.getAuthor().setKarma(targetPost.getAuthor().getKarma()+1);
     184        userRepository.save(targetPost.getAuthor());
    181185
    182             targetOpinion.getDislikes().add(currentUser);
    183             // opinionRepository.save(targetOpinion);
    184 
    185             targetOpinion.getAuthor().setKarma(targetOpinion.getAuthor().getKarma() - 1);
    186             userRepository.save(targetOpinion.getAuthor());
    187 
    188             currentUser.getDislikedPosts().add(targetOpinion);
    189             userRepository.save(currentUser);
    190         }
     186    }
     187    public void downvote_Thread(Long postId, CustomUserDetails currentUser) {
     188        Post targetPost = _threadRepository.findByPostId(postId);
     189        PostVote voteToAdd = new PostVote(currentUser, targetPost, VoteType.DOWNVOTE);
     190        postVoteRepository.save(voteToAdd);
     191        targetPost.getAuthor().setKarma(targetPost.getAuthor().getKarma()-1);
     192        userRepository.save(targetPost.getAuthor());
    191193    }
    192194}
  • springapp/src/main/resources/application.properties

    r8d83180 rc68150f  
    44spring.datasource.password=1win7337
    55spring.jpa.hibernate.ddl-auto=update
     6spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
    67spring.jpa.show-sql=false
    7 spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
    88spring.jpa.properties.hibernate.format_sql=true
    99server.address=192.168.0.17
    10 spring.mail.host=192.168.0.28
     10spring.mail.host=192.168.0.39
    1111spring.mail.username=mailuser
    1212spring.mail.password=mailpass
Note: See TracChangeset for help on using the changeset viewer.