diff --git a/handlers/about.go b/handlers/about.go index 47df6f6..c4a9bd7 100644 --- a/handlers/about.go +++ b/handlers/about.go @@ -1,46 +1,46 @@ package handlers import ( - "io/ioutil" - "log" - "net/http" - "github.com/gorilla/sessions" - "html/template" + "github.com/gorilla/sessions" + "html/template" + "io/ioutil" + "log" + "net/http" ) func AboutHandler(app *App) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - session := r.Context().Value("session").(*sessions.Session) - loggedIn := session.Values["user_id"] != nil - cookie, _ := r.Cookie("threadr_cookie_banner") + return func(w http.ResponseWriter, r *http.Request) { + session := r.Context().Value("session").(*sessions.Session) + loggedIn := session.Values["user_id"] != nil + cookie, _ := r.Cookie("threadr_cookie_banner") - aboutContent, err := ioutil.ReadFile("config/about_page.htmlbody") - if err != nil { - log.Printf("Error reading about_page.htmlbody: %v", err) - http.Error(w, "Internal Server Error", http.StatusInternalServerError) - return - } + aboutContent, err := ioutil.ReadFile("config/about_page.htmlbody") + if err != nil { + log.Printf("Error reading about_page.htmlbody: %v", err) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } - data := struct { - PageData - AboutContent template.HTML - }{ - PageData: PageData{ - Title: "ThreadR - About", - Navbar: "about", - LoggedIn: loggedIn, - ShowCookieBanner: cookie == nil || cookie.Value != "accepted", - BasePath: app.Config.ThreadrDir, - StaticPath: app.Config.ThreadrDir + "/static", - CurrentURL: r.URL.Path, - }, - AboutContent: template.HTML(aboutContent), - } + data := struct { + PageData + AboutContent template.HTML + }{ + PageData: PageData{ + Title: "ThreadR - About", + Navbar: "about", + LoggedIn: loggedIn, + ShowCookieBanner: cookie == nil || cookie.Value != "accepted", + BasePath: app.Config.ThreadrDir, + StaticPath: app.Config.ThreadrDir + "/static", + CurrentURL: r.URL.RequestURI(), + }, + AboutContent: template.HTML(aboutContent), + } - if err := app.Tmpl.ExecuteTemplate(w, "about", data); err != nil { - log.Printf("Error executing template in AboutHandler: %v", err) - http.Error(w, "Internal Server Error", http.StatusInternalServerError) - return - } - } + if err := app.Tmpl.ExecuteTemplate(w, "about", data); err != nil { + log.Printf("Error executing template in AboutHandler: %v", err) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + } } diff --git a/handlers/board.go b/handlers/board.go index 8486949..8f8de81 100644 --- a/handlers/board.go +++ b/handlers/board.go @@ -1,131 +1,131 @@ package handlers import ( - "log" - "net/http" - "strconv" - "threadr/models" - "github.com/gorilla/sessions" + "github.com/gorilla/sessions" + "log" + "net/http" + "strconv" + "threadr/models" ) func BoardHandler(app *App) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - session := r.Context().Value("session").(*sessions.Session) - loggedIn := session.Values["user_id"] != nil - userID, _ := session.Values["user_id"].(int) - cookie, _ := r.Cookie("threadr_cookie_banner") + return func(w http.ResponseWriter, r *http.Request) { + session := r.Context().Value("session").(*sessions.Session) + loggedIn := session.Values["user_id"] != nil + userID, _ := session.Values["user_id"].(int) + cookie, _ := r.Cookie("threadr_cookie_banner") - boardIDStr := r.URL.Query().Get("id") - boardID, err := strconv.Atoi(boardIDStr) - if err != nil { - http.Error(w, "Invalid board ID", http.StatusBadRequest) - return - } + boardIDStr := r.URL.Query().Get("id") + boardID, err := strconv.Atoi(boardIDStr) + if err != nil { + http.Error(w, "Invalid board ID", http.StatusBadRequest) + return + } - board, err := models.GetBoardByID(app.DB, boardID) - if err != nil { - log.Printf("Error fetching board: %v", err) - http.Error(w, "Internal Server Error", http.StatusInternalServerError) - return - } - if board == nil { - http.Error(w, "Board not found", http.StatusNotFound) - return - } + board, err := models.GetBoardByID(app.DB, boardID) + if err != nil { + log.Printf("Error fetching board: %v", err) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + if board == nil { + http.Error(w, "Board not found", http.StatusNotFound) + return + } - if board.Type == "chat" { - http.Redirect(w, r, app.Config.ThreadrDir+"/chat/?id="+boardIDStr, http.StatusFound) - return - } + if board.Type == "chat" { + http.Redirect(w, r, app.Config.ThreadrDir+"/chat/?id="+boardIDStr, http.StatusFound) + return + } - if board.Private { - if !loggedIn { - http.Redirect(w, r, app.Config.ThreadrDir+"/login/", http.StatusFound) - return - } - hasPerm, err := models.HasBoardPermission(app.DB, userID, boardID, models.PermViewBoard) - if err != nil { - log.Printf("Error checking permission: %v", err) - http.Error(w, "Internal Server Error", http.StatusInternalServerError) - return - } - if !hasPerm { - http.Error(w, "You do not have permission to view this board", http.StatusForbidden) - return - } - } + if board.Private { + if !loggedIn { + http.Redirect(w, r, app.Config.ThreadrDir+"/login/", http.StatusFound) + return + } + hasPerm, err := models.HasBoardPermission(app.DB, userID, boardID, models.PermViewBoard) + if err != nil { + log.Printf("Error checking permission: %v", err) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + if !hasPerm { + http.Error(w, "You do not have permission to view this board", http.StatusForbidden) + return + } + } - if r.Method == http.MethodPost && loggedIn { - action := r.URL.Query().Get("action") - if action == "create_thread" { - title := r.FormValue("title") - if title == "" { - http.Error(w, "Thread title is required", http.StatusBadRequest) - return - } - if board.Private { - hasPerm, err := models.HasBoardPermission(app.DB, userID, boardID, models.PermPostInBoard) - if err != nil { - log.Printf("Error checking permission: %v", err) - http.Error(w, "Internal Server Error", http.StatusInternalServerError) - return - } - if !hasPerm { - http.Error(w, "You do not have permission to post in this board", http.StatusForbidden) - return - } - } - thread := models.Thread{ - BoardID: boardID, - Title: title, - CreatedByUserID: userID, - } - err = models.CreateThread(app.DB, thread) - if err != nil { - log.Printf("Error creating thread: %v", err) - http.Error(w, "Internal Server Error", http.StatusInternalServerError) - return - } - var threadID int - err = app.DB.QueryRow("SELECT LAST_INSERT_ID()").Scan(&threadID) - if err != nil { - log.Printf("Error getting last insert id: %v", err) - http.Error(w, "Internal Server Error", http.StatusInternalServerError) - return - } - http.Redirect(w, r, app.Config.ThreadrDir+"/thread/?id="+strconv.Itoa(threadID), http.StatusFound) - return - } - } + if r.Method == http.MethodPost && loggedIn { + action := r.URL.Query().Get("action") + if action == "create_thread" { + title := r.FormValue("title") + if title == "" { + http.Error(w, "Thread title is required", http.StatusBadRequest) + return + } + if board.Private { + hasPerm, err := models.HasBoardPermission(app.DB, userID, boardID, models.PermPostInBoard) + if err != nil { + log.Printf("Error checking permission: %v", err) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + if !hasPerm { + http.Error(w, "You do not have permission to post in this board", http.StatusForbidden) + return + } + } + thread := models.Thread{ + BoardID: boardID, + Title: title, + CreatedByUserID: userID, + } + err = models.CreateThread(app.DB, thread) + if err != nil { + log.Printf("Error creating thread: %v", err) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + var threadID int + err = app.DB.QueryRow("SELECT LAST_INSERT_ID()").Scan(&threadID) + if err != nil { + log.Printf("Error getting last insert id: %v", err) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + http.Redirect(w, r, app.Config.ThreadrDir+"/thread/?id="+strconv.Itoa(threadID), http.StatusFound) + return + } + } - threads, err := models.GetThreadsByBoardID(app.DB, boardID) - if err != nil { - log.Printf("Error fetching threads: %v", err) - http.Error(w, "Internal Server Error", http.StatusInternalServerError) - return - } + threads, err := models.GetThreadsByBoardID(app.DB, boardID) + if err != nil { + log.Printf("Error fetching threads: %v", err) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } - data := struct { - PageData - Board models.Board - Threads []models.Thread - }{ - PageData: PageData{ - Title: "ThreadR - " + board.Name, - Navbar: "boards", - LoggedIn: loggedIn, - ShowCookieBanner: cookie == nil || cookie.Value != "accepted", - BasePath: app.Config.ThreadrDir, - StaticPath: app.Config.ThreadrDir + "/static", - CurrentURL: r.URL.Path, - }, - Board: *board, - Threads: threads, - } - if err := app.Tmpl.ExecuteTemplate(w, "board", data); err != nil { - log.Printf("Error executing template in BoardHandler: %v", err) - http.Error(w, "Internal Server Error", http.StatusInternalServerError) - return - } - } -} \ No newline at end of file + data := struct { + PageData + Board models.Board + Threads []models.Thread + }{ + PageData: PageData{ + Title: "ThreadR - " + board.Name, + Navbar: "boards", + LoggedIn: loggedIn, + ShowCookieBanner: cookie == nil || cookie.Value != "accepted", + BasePath: app.Config.ThreadrDir, + StaticPath: app.Config.ThreadrDir + "/static", + CurrentURL: r.URL.RequestURI(), + }, + Board: *board, + Threads: threads, + } + if err := app.Tmpl.ExecuteTemplate(w, "board", data); err != nil { + log.Printf("Error executing template in BoardHandler: %v", err) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + } +} diff --git a/handlers/boards.go b/handlers/boards.go index 99d272f..21105a3 100644 --- a/handlers/boards.go +++ b/handlers/boards.go @@ -1,120 +1,120 @@ package handlers import ( - "log" - "net/http" - "strconv" - "threadr/models" - "github.com/gorilla/sessions" + "github.com/gorilla/sessions" + "log" + "net/http" + "strconv" + "threadr/models" ) func BoardsHandler(app *App) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - session := r.Context().Value("session").(*sessions.Session) - loggedIn := session.Values["user_id"] != nil - cookie, _ := r.Cookie("threadr_cookie_banner") - userID, _ := session.Values["user_id"].(int) - isAdmin := false - - if loggedIn { - user, err := models.GetUserByID(app.DB, userID) - if err != nil { - log.Printf("Error fetching user: %v", err) - } else if user != nil { - isAdmin = models.HasGlobalPermission(user, models.PermCreateBoard) - } - } + return func(w http.ResponseWriter, r *http.Request) { + session := r.Context().Value("session").(*sessions.Session) + loggedIn := session.Values["user_id"] != nil + cookie, _ := r.Cookie("threadr_cookie_banner") + userID, _ := session.Values["user_id"].(int) + isAdmin := false - if r.Method == http.MethodPost && loggedIn && isAdmin { - name := r.FormValue("name") - description := r.FormValue("description") - boardType := r.FormValue("type") + if loggedIn { + user, err := models.GetUserByID(app.DB, userID) + if err != nil { + log.Printf("Error fetching user: %v", err) + } else if user != nil { + isAdmin = models.HasGlobalPermission(user, models.PermCreateBoard) + } + } - if name == "" { - http.Error(w, "Board name is required", http.StatusBadRequest) - return - } - if boardType != "classic" && boardType != "chat" { - boardType = "classic" - } + if r.Method == http.MethodPost && loggedIn && isAdmin { + name := r.FormValue("name") + description := r.FormValue("description") + boardType := r.FormValue("type") - board := models.Board{ - Name: name, - Description: description, - Private: false, - PublicVisible: true, - Type: boardType, - } - query := "INSERT INTO boards (name, description, private, public_visible, type) VALUES (?, ?, ?, ?, ?)" - result, err := app.DB.Exec(query, board.Name, board.Description, board.Private, board.PublicVisible, board.Type) - if err != nil { - log.Printf("Error creating board: %v", err) - http.Error(w, "Failed to create board", http.StatusInternalServerError) - return - } - boardID, _ := result.LastInsertId() - - var redirectURL string - if boardType == "chat" { - redirectURL = app.Config.ThreadrDir + "/chat/?id=" + strconv.FormatInt(boardID, 10) - } else { - redirectURL = app.Config.ThreadrDir + "/board/?id=" + strconv.FormatInt(boardID, 10) - } - http.Redirect(w, r, redirectURL, http.StatusFound) - return - } + if name == "" { + http.Error(w, "Board name is required", http.StatusBadRequest) + return + } + if boardType != "classic" && boardType != "chat" { + boardType = "classic" + } - publicBoards, err := models.GetAllBoards(app.DB, false) - if err != nil { - log.Printf("Error fetching public boards: %v", err) - http.Error(w, "Internal Server Error", http.StatusInternalServerError) - return - } - var privateBoards []models.Board - if loggedIn { - privateBoards, err = models.GetAllBoards(app.DB, true) - if err != nil { - log.Printf("Error fetching private boards: %v", err) - http.Error(w, "Internal Server Error", http.StatusInternalServerError) - return - } - var accessiblePrivateBoards []models.Board - for _, board := range privateBoards { - hasPerm, err := models.HasBoardPermission(app.DB, userID, board.ID, models.PermViewBoard) - if err != nil { - log.Printf("Error checking permission: %v", err) - continue - } - if hasPerm { - accessiblePrivateBoards = append(accessiblePrivateBoards, board) - } - } - privateBoards = accessiblePrivateBoards - } + board := models.Board{ + Name: name, + Description: description, + Private: false, + PublicVisible: true, + Type: boardType, + } + query := "INSERT INTO boards (name, description, private, public_visible, type) VALUES (?, ?, ?, ?, ?)" + result, err := app.DB.Exec(query, board.Name, board.Description, board.Private, board.PublicVisible, board.Type) + if err != nil { + log.Printf("Error creating board: %v", err) + http.Error(w, "Failed to create board", http.StatusInternalServerError) + return + } + boardID, _ := result.LastInsertId() - data := struct { - PageData - PublicBoards []models.Board - PrivateBoards []models.Board - IsAdmin bool - }{ - PageData: PageData{ - Title: "ThreadR - Boards", - Navbar: "boards", - LoggedIn: loggedIn, - ShowCookieBanner: cookie == nil || cookie.Value != "accepted", - BasePath: app.Config.ThreadrDir, - StaticPath: app.Config.ThreadrDir + "/static", - CurrentURL: r.URL.Path, - }, - PublicBoards: publicBoards, - PrivateBoards: privateBoards, - IsAdmin: isAdmin, - } - if err := app.Tmpl.ExecuteTemplate(w, "boards", data); err != nil { - log.Printf("Error executing template in BoardsHandler: %v", err) - http.Error(w, "Internal Server Error", http.StatusInternalServerError) - return - } - } -} \ No newline at end of file + var redirectURL string + if boardType == "chat" { + redirectURL = app.Config.ThreadrDir + "/chat/?id=" + strconv.FormatInt(boardID, 10) + } else { + redirectURL = app.Config.ThreadrDir + "/board/?id=" + strconv.FormatInt(boardID, 10) + } + http.Redirect(w, r, redirectURL, http.StatusFound) + return + } + + publicBoards, err := models.GetAllBoards(app.DB, false) + if err != nil { + log.Printf("Error fetching public boards: %v", err) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + var privateBoards []models.Board + if loggedIn { + privateBoards, err = models.GetAllBoards(app.DB, true) + if err != nil { + log.Printf("Error fetching private boards: %v", err) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + var accessiblePrivateBoards []models.Board + for _, board := range privateBoards { + hasPerm, err := models.HasBoardPermission(app.DB, userID, board.ID, models.PermViewBoard) + if err != nil { + log.Printf("Error checking permission: %v", err) + continue + } + if hasPerm { + accessiblePrivateBoards = append(accessiblePrivateBoards, board) + } + } + privateBoards = accessiblePrivateBoards + } + + data := struct { + PageData + PublicBoards []models.Board + PrivateBoards []models.Board + IsAdmin bool + }{ + PageData: PageData{ + Title: "ThreadR - Boards", + Navbar: "boards", + LoggedIn: loggedIn, + ShowCookieBanner: cookie == nil || cookie.Value != "accepted", + BasePath: app.Config.ThreadrDir, + StaticPath: app.Config.ThreadrDir + "/static", + CurrentURL: r.URL.RequestURI(), + }, + PublicBoards: publicBoards, + PrivateBoards: privateBoards, + IsAdmin: isAdmin, + } + if err := app.Tmpl.ExecuteTemplate(w, "boards", data); err != nil { + log.Printf("Error executing template in BoardsHandler: %v", err) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + } +} diff --git a/handlers/chat.go b/handlers/chat.go index 5314cd7..01b6609 100644 --- a/handlers/chat.go +++ b/handlers/chat.go @@ -237,7 +237,7 @@ func ChatHandler(app *App) http.HandlerFunc { ShowCookieBanner: cookie == nil || cookie.Value != "accepted", BasePath: app.Config.ThreadrDir, StaticPath: app.Config.ThreadrDir + "/static", - CurrentURL: r.URL.Path, + CurrentURL: r.URL.RequestURI(), ContentTemplate: "chat-content", BodyClass: "chat-page", }, diff --git a/handlers/home.go b/handlers/home.go index 0ddfcd3..956cc0e 100644 --- a/handlers/home.go +++ b/handlers/home.go @@ -1,34 +1,34 @@ package handlers import ( - "log" - "net/http" - "path/filepath" - "github.com/gorilla/sessions" + "github.com/gorilla/sessions" + "log" + "net/http" + "path/filepath" ) func HomeHandler(app *App) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - session := r.Context().Value("session").(*sessions.Session) - loggedIn := session.Values["user_id"] != nil - cookie, _ := r.Cookie("threadr_cookie_banner") - data := struct { - PageData - }{ - PageData: PageData{ - Title: "ThreadR - Home", - Navbar: "home", - LoggedIn: loggedIn, - ShowCookieBanner: cookie == nil || cookie.Value != "accepted", - BasePath: app.Config.ThreadrDir, - StaticPath: filepath.Join(app.Config.ThreadrDir, "static"), - CurrentURL: r.URL.String(), - }, - } - if err := app.Tmpl.ExecuteTemplate(w, "home", data); err != nil { - log.Printf("Error executing template in HomeHandler: %v", err) - http.Error(w, "Internal Server Error", http.StatusInternalServerError) - return - } - } -} \ No newline at end of file + return func(w http.ResponseWriter, r *http.Request) { + session := r.Context().Value("session").(*sessions.Session) + loggedIn := session.Values["user_id"] != nil + cookie, _ := r.Cookie("threadr_cookie_banner") + data := struct { + PageData + }{ + PageData: PageData{ + Title: "ThreadR - Home", + Navbar: "home", + LoggedIn: loggedIn, + ShowCookieBanner: cookie == nil || cookie.Value != "accepted", + BasePath: app.Config.ThreadrDir, + StaticPath: filepath.Join(app.Config.ThreadrDir, "static"), + CurrentURL: r.URL.RequestURI(), + }, + } + if err := app.Tmpl.ExecuteTemplate(w, "home", data); err != nil { + log.Printf("Error executing template in HomeHandler: %v", err) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + } +} diff --git a/handlers/login.go b/handlers/login.go index 9f1f258..71dd808 100644 --- a/handlers/login.go +++ b/handlers/login.go @@ -52,7 +52,7 @@ func LoginHandler(app *App) http.HandlerFunc { LoggedIn: false, BasePath: app.Config.ThreadrDir, StaticPath: app.Config.ThreadrDir + "/static", - CurrentURL: r.URL.Path, + CurrentURL: r.URL.RequestURI(), }, Error: "", } diff --git a/handlers/news.go b/handlers/news.go index 2c10a85..1aff94d 100644 --- a/handlers/news.go +++ b/handlers/news.go @@ -1,98 +1,98 @@ package handlers import ( - "log" - "net/http" - "strconv" - "threadr/models" - "github.com/gorilla/sessions" + "github.com/gorilla/sessions" + "log" + "net/http" + "strconv" + "threadr/models" ) func NewsHandler(app *App) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - session := r.Context().Value("session").(*sessions.Session) - loggedIn := session.Values["user_id"] != nil - cookie, _ := r.Cookie("threadr_cookie_banner") - userID, _ := session.Values["user_id"].(int) - isAdmin := false - - if loggedIn { - user, err := models.GetUserByID(app.DB, userID) - if err != nil { - log.Printf("Error fetching user: %v", err) - } else if user != nil { - isAdmin = models.HasGlobalPermission(user, models.PermManageUsers) - } - } + return func(w http.ResponseWriter, r *http.Request) { + session := r.Context().Value("session").(*sessions.Session) + loggedIn := session.Values["user_id"] != nil + cookie, _ := r.Cookie("threadr_cookie_banner") + userID, _ := session.Values["user_id"].(int) + isAdmin := false - if r.Method == http.MethodPost && loggedIn && isAdmin { - if action := r.URL.Query().Get("action"); action == "delete" { - newsIDStr := r.URL.Query().Get("id") - newsID, err := strconv.Atoi(newsIDStr) - if err != nil { - http.Error(w, "Invalid news ID", http.StatusBadRequest) - return - } - err = models.DeleteNews(app.DB, newsID) - if err != nil { - log.Printf("Error deleting news item: %v", err) - http.Error(w, "Failed to delete news item", http.StatusInternalServerError) - return - } - http.Redirect(w, r, app.Config.ThreadrDir+"/news/", http.StatusFound) - return - } else { - title := r.FormValue("title") - content := r.FormValue("content") - if title != "" && content != "" { - news := models.News{ - Title: title, - Content: content, - PostedBy: userID, - } - err := models.CreateNews(app.DB, news) - if err != nil { - log.Printf("Error creating news item: %v", err) - http.Error(w, "Failed to create news item", http.StatusInternalServerError) - return - } - http.Redirect(w, r, app.Config.ThreadrDir+"/news/", http.StatusFound) - return - } else { - http.Error(w, "Title and content are required", http.StatusBadRequest) - return - } - } - } + if loggedIn { + user, err := models.GetUserByID(app.DB, userID) + if err != nil { + log.Printf("Error fetching user: %v", err) + } else if user != nil { + isAdmin = models.HasGlobalPermission(user, models.PermManageUsers) + } + } - newsItems, err := models.GetAllNews(app.DB) - if err != nil { - log.Printf("Error fetching news items: %v", err) - http.Error(w, "Internal Server Error", http.StatusInternalServerError) - return - } + if r.Method == http.MethodPost && loggedIn && isAdmin { + if action := r.URL.Query().Get("action"); action == "delete" { + newsIDStr := r.URL.Query().Get("id") + newsID, err := strconv.Atoi(newsIDStr) + if err != nil { + http.Error(w, "Invalid news ID", http.StatusBadRequest) + return + } + err = models.DeleteNews(app.DB, newsID) + if err != nil { + log.Printf("Error deleting news item: %v", err) + http.Error(w, "Failed to delete news item", http.StatusInternalServerError) + return + } + http.Redirect(w, r, app.Config.ThreadrDir+"/news/", http.StatusFound) + return + } else { + title := r.FormValue("title") + content := r.FormValue("content") + if title != "" && content != "" { + news := models.News{ + Title: title, + Content: content, + PostedBy: userID, + } + err := models.CreateNews(app.DB, news) + if err != nil { + log.Printf("Error creating news item: %v", err) + http.Error(w, "Failed to create news item", http.StatusInternalServerError) + return + } + http.Redirect(w, r, app.Config.ThreadrDir+"/news/", http.StatusFound) + return + } else { + http.Error(w, "Title and content are required", http.StatusBadRequest) + return + } + } + } - data := struct { - PageData - News []models.News - IsAdmin bool - }{ - PageData: PageData{ - Title: "ThreadR - News", - Navbar: "news", - LoggedIn: loggedIn, - ShowCookieBanner: cookie == nil || cookie.Value != "accepted", - BasePath: app.Config.ThreadrDir, - StaticPath: app.Config.ThreadrDir + "/static", - CurrentURL: r.URL.Path, - }, - News: newsItems, - IsAdmin: isAdmin, - } - if err := app.Tmpl.ExecuteTemplate(w, "news", data); err != nil { - log.Printf("Error executing template in NewsHandler: %v", err) - http.Error(w, "Internal Server Error", http.StatusInternalServerError) - return - } - } -} \ No newline at end of file + newsItems, err := models.GetAllNews(app.DB) + if err != nil { + log.Printf("Error fetching news items: %v", err) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + + data := struct { + PageData + News []models.News + IsAdmin bool + }{ + PageData: PageData{ + Title: "ThreadR - News", + Navbar: "news", + LoggedIn: loggedIn, + ShowCookieBanner: cookie == nil || cookie.Value != "accepted", + BasePath: app.Config.ThreadrDir, + StaticPath: app.Config.ThreadrDir + "/static", + CurrentURL: r.URL.RequestURI(), + }, + News: newsItems, + IsAdmin: isAdmin, + } + if err := app.Tmpl.ExecuteTemplate(w, "news", data); err != nil { + log.Printf("Error executing template in NewsHandler: %v", err) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + } +} diff --git a/handlers/preferences.go b/handlers/preferences.go index 53d658b..28b407a 100644 --- a/handlers/preferences.go +++ b/handlers/preferences.go @@ -68,7 +68,7 @@ func PreferencesHandler(app *App) http.HandlerFunc { ShowCookieBanner: false, BasePath: app.Config.ThreadrDir, StaticPath: app.Config.ThreadrDir + "/static", - CurrentURL: r.URL.Path, + CurrentURL: r.URL.RequestURI(), ContentTemplate: "preferences-content", }, Preferences: prefs, diff --git a/handlers/profile.go b/handlers/profile.go index 5a454ec..c1f8baf 100644 --- a/handlers/profile.go +++ b/handlers/profile.go @@ -1,55 +1,55 @@ package handlers import ( - "log" - "net/http" - "threadr/models" - "github.com/gorilla/sessions" + "github.com/gorilla/sessions" + "log" + "net/http" + "threadr/models" ) func ProfileHandler(app *App) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - session := r.Context().Value("session").(*sessions.Session) - userID, ok := session.Values["user_id"].(int) - if !ok { - http.Redirect(w, r, app.Config.ThreadrDir+"/login/", http.StatusFound) - return - } - user, err := models.GetUserByID(app.DB, userID) - if err != nil { - log.Printf("Error fetching user in ProfileHandler: %v", err) - http.Error(w, "Internal Server Error", http.StatusInternalServerError) - return - } - if user == nil { - http.Error(w, "User not found", http.StatusNotFound) - return - } - displayName := user.DisplayName - if displayName == "" { - displayName = user.Username - } - data := struct { - PageData - User models.User - DisplayName string - }{ - PageData: PageData{ - Title: "ThreadR - Profile", - Navbar: "profile", - LoggedIn: true, - ShowCookieBanner: false, - BasePath: app.Config.ThreadrDir, - StaticPath: app.Config.ThreadrDir + "/static", - CurrentURL: r.URL.Path, - }, - User: *user, - DisplayName: displayName, - } - if err := app.Tmpl.ExecuteTemplate(w, "profile", data); err != nil { - log.Printf("Error executing template in ProfileHandler: %v", err) - http.Error(w, "Internal Server Error", http.StatusInternalServerError) - return - } - } -} \ No newline at end of file + return func(w http.ResponseWriter, r *http.Request) { + session := r.Context().Value("session").(*sessions.Session) + userID, ok := session.Values["user_id"].(int) + if !ok { + http.Redirect(w, r, app.Config.ThreadrDir+"/login/", http.StatusFound) + return + } + user, err := models.GetUserByID(app.DB, userID) + if err != nil { + log.Printf("Error fetching user in ProfileHandler: %v", err) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + if user == nil { + http.Error(w, "User not found", http.StatusNotFound) + return + } + displayName := user.DisplayName + if displayName == "" { + displayName = user.Username + } + data := struct { + PageData + User models.User + DisplayName string + }{ + PageData: PageData{ + Title: "ThreadR - Profile", + Navbar: "profile", + LoggedIn: true, + ShowCookieBanner: false, + BasePath: app.Config.ThreadrDir, + StaticPath: app.Config.ThreadrDir + "/static", + CurrentURL: r.URL.RequestURI(), + }, + User: *user, + DisplayName: displayName, + } + if err := app.Tmpl.ExecuteTemplate(w, "profile", data); err != nil { + log.Printf("Error executing template in ProfileHandler: %v", err) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + } +} diff --git a/handlers/profile_edit.go b/handlers/profile_edit.go index 38a7483..4b0a43a 100644 --- a/handlers/profile_edit.go +++ b/handlers/profile_edit.go @@ -39,9 +39,9 @@ func ProfileEditHandler(app *App) http.HandlerFunc { // Create file record in the database fileRecord := models.File{ - OriginalName: handler.Filename, - Hash: fileHash, - HashAlgorithm: "sha256", + OriginalName: handler.Filename, + Hash: fileHash, + HashAlgorithm: "sha256", } fileID, err := models.CreateFile(app.DB, fileRecord) if err != nil { @@ -95,36 +95,36 @@ func ProfileEditHandler(app *App) http.HandlerFunc { return } - user, err := models.GetUserByID(app.DB, userID) - if err != nil { - log.Printf("Error fetching user: %v", err) - http.Error(w, "Internal Server Error", http.StatusInternalServerError) - return - } - if user == nil { - http.Error(w, "User not found", http.StatusNotFound) - return - } + user, err := models.GetUserByID(app.DB, userID) + if err != nil { + log.Printf("Error fetching user: %v", err) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + if user == nil { + http.Error(w, "User not found", http.StatusNotFound) + return + } - data := struct { - PageData - User models.User - }{ - PageData: PageData{ - Title: "ThreadR - Edit Profile", - Navbar: "profile", - LoggedIn: true, - ShowCookieBanner: false, - BasePath: app.Config.ThreadrDir, - StaticPath: app.Config.ThreadrDir + "/static", - CurrentURL: r.URL.Path, - }, - User: *user, - } - if err := app.Tmpl.ExecuteTemplate(w, "profile_edit", data); err != nil { - log.Printf("Error executing template in ProfileEditHandler: %v", err) - http.Error(w, "Internal Server Error", http.StatusInternalServerError) - return - } - } + data := struct { + PageData + User models.User + }{ + PageData: PageData{ + Title: "ThreadR - Edit Profile", + Navbar: "profile", + LoggedIn: true, + ShowCookieBanner: false, + BasePath: app.Config.ThreadrDir, + StaticPath: app.Config.ThreadrDir + "/static", + CurrentURL: r.URL.RequestURI(), + }, + User: *user, + } + if err := app.Tmpl.ExecuteTemplate(w, "profile_edit", data); err != nil { + log.Printf("Error executing template in ProfileEditHandler: %v", err) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + } } diff --git a/handlers/signup.go b/handlers/signup.go index 7f6961f..0404679 100644 --- a/handlers/signup.go +++ b/handlers/signup.go @@ -30,7 +30,7 @@ func SignupHandler(app *App) http.HandlerFunc { ShowCookieBanner: cookie == nil || cookie.Value != "accepted", BasePath: app.Config.ThreadrDir, StaticPath: app.Config.ThreadrDir + "/static", - CurrentURL: r.URL.Path, + CurrentURL: r.URL.RequestURI(), }, Error: "Passwords do not match. Please try again.", } @@ -56,7 +56,7 @@ func SignupHandler(app *App) http.HandlerFunc { ShowCookieBanner: cookie == nil || cookie.Value != "accepted", BasePath: app.Config.ThreadrDir, StaticPath: app.Config.ThreadrDir + "/static", - CurrentURL: r.URL.Path, + CurrentURL: r.URL.RequestURI(), }, Error: "An error occurred during sign up. Please try again.", } @@ -81,7 +81,7 @@ func SignupHandler(app *App) http.HandlerFunc { ShowCookieBanner: cookie == nil || cookie.Value != "accepted", BasePath: app.Config.ThreadrDir, StaticPath: app.Config.ThreadrDir + "/static", - CurrentURL: r.URL.Path, + CurrentURL: r.URL.RequestURI(), }, Error: "", } diff --git a/handlers/thread.go b/handlers/thread.go index 312a8e8..a99e73d 100644 --- a/handlers/thread.go +++ b/handlers/thread.go @@ -122,7 +122,7 @@ func ThreadHandler(app *App) http.HandlerFunc { ShowCookieBanner: cookie == nil || cookie.Value != "accepted", BasePath: app.Config.ThreadrDir, StaticPath: app.Config.ThreadrDir + "/static", - CurrentURL: r.URL.Path, + CurrentURL: r.URL.RequestURI(), }, Thread: *thread, Board: *board, diff --git a/handlers/userhome.go b/handlers/userhome.go index 679aec8..7e80e65 100644 --- a/handlers/userhome.go +++ b/handlers/userhome.go @@ -1,49 +1,49 @@ package handlers import ( - "log" - "net/http" - "threadr/models" - "github.com/gorilla/sessions" + "github.com/gorilla/sessions" + "log" + "net/http" + "threadr/models" ) func UserHomeHandler(app *App) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - session := r.Context().Value("session").(*sessions.Session) - userID, ok := session.Values["user_id"].(int) - if !ok { - http.Redirect(w, r, app.Config.ThreadrDir+"/login/", http.StatusFound) - return - } - user, err := models.GetUserByID(app.DB, userID) - if err != nil { - log.Printf("Error fetching user in UserHomeHandler: %v", err) - http.Error(w, "Internal Server Error", http.StatusInternalServerError) - return - } - if user == nil { - http.Error(w, "User not found", http.StatusNotFound) - return - } - data := struct { - PageData - Username string - }{ - PageData: PageData{ - Title: "ThreadR - User Home", - Navbar: "userhome", - LoggedIn: true, - ShowCookieBanner: false, - BasePath: app.Config.ThreadrDir, - StaticPath: app.Config.ThreadrDir + "/static", - CurrentURL: r.URL.Path, - }, - Username: user.Username, - } - if err := app.Tmpl.ExecuteTemplate(w, "userhome", data); err != nil { - log.Printf("Error executing template in UserHomeHandler: %v", err) - http.Error(w, "Internal Server Error", http.StatusInternalServerError) - return - } - } -} \ No newline at end of file + return func(w http.ResponseWriter, r *http.Request) { + session := r.Context().Value("session").(*sessions.Session) + userID, ok := session.Values["user_id"].(int) + if !ok { + http.Redirect(w, r, app.Config.ThreadrDir+"/login/", http.StatusFound) + return + } + user, err := models.GetUserByID(app.DB, userID) + if err != nil { + log.Printf("Error fetching user in UserHomeHandler: %v", err) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + if user == nil { + http.Error(w, "User not found", http.StatusNotFound) + return + } + data := struct { + PageData + Username string + }{ + PageData: PageData{ + Title: "ThreadR - User Home", + Navbar: "userhome", + LoggedIn: true, + ShowCookieBanner: false, + BasePath: app.Config.ThreadrDir, + StaticPath: app.Config.ThreadrDir + "/static", + CurrentURL: r.URL.RequestURI(), + }, + Username: user.Username, + } + if err := app.Tmpl.ExecuteTemplate(w, "userhome", data); err != nil { + log.Printf("Error executing template in UserHomeHandler: %v", err) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + } +} diff --git a/static/style.css b/static/style.css index 0e2a857..a9afac3 100644 --- a/static/style.css +++ b/static/style.css @@ -551,6 +551,26 @@ p.thread-info { position: relative; } +.chat-message .post-actions { + position: absolute; + top: -12px; + right: 0; + margin: 0; + opacity: 0; + pointer-events: none; + background-color: #fef6e4; + border: 1px solid #001858; + border-radius: 4px; + padding: 2px 4px; + z-index: 10; + transition: opacity 0.15s ease; +} + +.chat-message:hover .post-actions { + opacity: 1; + pointer-events: auto; +} + .chat-message-header { display: flex; align-items: center; @@ -622,20 +642,16 @@ p.thread-info { font-size: 0.9em; } -.chat-message:hover .post-actions { - opacity: 1; -} - -.post-actions a { +.chat-message .post-actions a { color: #001858; text-decoration: none; font-size: 0.8em; padding: 2px 5px; - border: 1px solid #001858; + border: none; border-radius: 3px; } -.post-actions a:hover { +.chat-message .post-actions a:hover { background-color: #8bd3dd; color: #fef6e4; } @@ -851,12 +867,16 @@ p.thread-info { background-color: #555; } - .post-actions a { - color: #fef6e4; + .chat-message .post-actions { + background-color: #444; border-color: #fef6e4; } - .post-actions a:hover { + .chat-message .post-actions a { + color: #fef6e4; + } + + .chat-message .post-actions a:hover { background-color: #8bd3dd; color: #001858; }