Documentation ¶
Index ¶
- Variables
- func A(handler func(w http.ResponseWriter, r *http.Request, ctx *Context)) func(w http.ResponseWriter, r *http.Request)
- func ErrForbiddenHandler(w http.ResponseWriter, r *http.Request)
- func ErrNotFoundHandler(w http.ResponseWriter, r *http.Request)
- func ErrServerHandler(w http.ResponseWriter, r *http.Request)
- func FaviconHandler(w http.ResponseWriter, r *http.Request)
- func LogoutHandler(w http.ResponseWriter, r *http.Request)
- func ScriptHandler(w http.ResponseWriter, r *http.Request)
- func SendMail(to string, sub string, body string, config WikiConfig)
- func StyleHandler(w http.ResponseWriter, r *http.Request)
- func UA(handler func(w http.ResponseWriter, r *http.Request, ctx *Context)) func(w http.ResponseWriter, r *http.Request)
- type Context
- type NavLink
- type NavSection
- type WikiConfig
Constants ¶
This section is empty.
Variables ¶
View Source
var AdminConfigUpdateHandler = A(func(w http.ResponseWriter, r *http.Request, ctx *Context) { if !ctx.IsAdmin || r.Method != "POST" { ErrForbiddenHandler(w, r) return } models.WriteConfig(models.ConfigJSON, r.PostFormValue("config")) ctxCacheDate = time.Unix(0, 0) ctx.SetFlashMsg("Config updated") http.Redirect(w, r, "/admin", http.StatusSeeOther) })
View Source
var AdminFileMasterGroupHandler = A(func(w http.ResponseWriter, r *http.Request, ctx *Context) { if !ctx.IsAdmin || r.Method != "POST" { ErrForbiddenHandler(w, r) return } group := r.PostFormValue("group") var tmp string if group == models.EverybodyGroup || db.QueryRow(`SELECT id FROM groups WHERE name=?;`, group).Scan(&tmp) == nil { models.WriteConfig(models.FileMasterGroup, group) } else { ctx.SetFlashMsg("Group '" + group + "' not found") } http.Redirect(w, r, "/admin", http.StatusSeeOther) })
View Source
ErrForbiddenHandler(w, r) return } models.WriteConfig(models.FooterLinks, r.PostFormValue("footer")) ctxCacheDate = time.Unix(0, 0) ctx.SetFlashMsg("Footer updated") http.Redirect(w, r, "/admin", http.StatusSeeOther) })if !ctx.IsAdmin || r.Method != "POST" {
View Source
var AdminGroupCreateHandler = A(func(w http.ResponseWriter, r *http.Request, ctx *Context) { if !ctx.IsAdmin || r.Method != "POST" { ErrForbiddenHandler(w, r) return } groupname := r.PostFormValue("groupname") if groupname != models.EverybodyGroup { if err := models.ValidateName(groupname); err == nil { var tmp string if db.QueryRow(`SELECT id FROM groups WHERE name=?;`, groupname).Scan(&tmp) == sql.ErrNoRows { tNow := time.Now().Unix() db.Exec(`INSERT INTO groups(name, created_date, updated_date) VALUES(?, ?, ?);`, groupname, tNow, tNow) } else { ctx.SetFlashMsg("Group '" + groupname + "' already exits") } } else { ctx.SetFlashMsg(err.Error()) } } else { ctx.SetFlashMsg("Group name '" + groupname + "' is reserved") } http.Redirect(w, r, "/admin/groups", http.StatusSeeOther) })
View Source
var AdminGroupDeleteHandler = A(func(w http.ResponseWriter, r *http.Request, ctx *Context) { if !ctx.IsAdmin || r.Method != "POST" { ErrForbiddenHandler(w, r) return } groupname := r.PostFormValue("groupname") db.Exec(`DELETE FROM groups WHERE name=?;`, groupname) ctx.SetFlashMsg("Group deleted") http.Redirect(w, r, "/admin/groups", http.StatusSeeOther) })
View Source
var AdminGroupHandler = A(func(w http.ResponseWriter, r *http.Request, ctx *Context) { if !ctx.IsAdmin { ErrForbiddenHandler(w, r) return } var groups []string rows := db.Query(`SELECT name FROM groups;`) for rows.Next() { var group string rows.Scan(&group) groups = append(groups, group) } templates.Render(w, "admingroups.html", map[string]interface{}{ "ctx": ctx, "groups": groups, }) })
View Source
var AdminGroupMemberCreateHandler = A(func(w http.ResponseWriter, r *http.Request, ctx *Context) { if !ctx.IsAdmin || r.Method != "POST" { ErrForbiddenHandler(w, r) return } groupname := r.PostFormValue("groupname") username := r.PostFormValue("username") var groupID string if db.QueryRow(`SELECT id FROM groups WHERE name=?;`, groupname).Scan(&groupID) == nil { var userID string if db.QueryRow(`SELECT id FROM users WHERE username=?;`, username).Scan(&userID) == nil { var tmp string if db.QueryRow(`SELECT id FROM groupmembers WHERE userid=? AND groupid=?;`, userID, groupID).Scan(&tmp) == sql.ErrNoRows { tNow := time.Now().Unix() db.Exec(`INSERT INTO groupmembers(groupid, userid, created_date) VALUES(?, ?, ?);`, groupID, userID, tNow) ctx.SetFlashMsg("Added user '" + username + "'") } else { ctx.SetFlashMsg("User '" + username + "' already in this group") } } else { ctx.SetFlashMsg("User '" + username + "' not found") } } http.Redirect(w, r, "/admin/groupmembers?g="+groupname, http.StatusSeeOther) })
View Source
var AdminGroupMemberDeleteHandler = A(func(w http.ResponseWriter, r *http.Request, ctx *Context) { if !ctx.IsAdmin || r.Method != "POST" { ErrForbiddenHandler(w, r) return } groupname := r.PostFormValue("groupname") username := r.PostFormValue("username") var userID string if db.QueryRow(`SELECT id FROM users WHERE username=?;`, username).Scan(&userID) == nil { db.Exec(`DELETE FROM groupmembers WHERE userid=?;`, userID) ctx.SetFlashMsg("User '" + username + "' removed from this group") } else { ctx.SetFlashMsg("User '" + username + "' not in this group") } http.Redirect(w, r, "/admin/groupmembers?g="+groupname, http.StatusSeeOther) })
View Source
var AdminGroupMembersHandler = A(func(w http.ResponseWriter, r *http.Request, ctx *Context) { if !ctx.IsAdmin { ErrForbiddenHandler(w, r) return } groupname := r.FormValue("g") var members []string rows := db.Query(`SELECT users.username FROM groupmembers INNER JOIN users ON groupmembers.userid=users.id INNER JOIN groups ON groupmembers.groupid=groups.id AND groups.name=?;`, groupname) for rows.Next() { var member string rows.Scan(&member) members = append(members, member) } templates.Render(w, "admingroupmembers.html", map[string]interface{}{ "ctx": ctx, "groupname": groupname, "members": members, }) })
View Source
var AdminHandler = A(func(w http.ResponseWriter, r *http.Request, ctx *Context) { if !ctx.IsAdmin { ErrForbiddenHandler(w, r) return } templates.Render(w, "admin.html", map[string]interface{}{ "ctx": ctx, "PageMasterGroup": models.ReadPageMasterGroup(), "FileMasterGroup": models.ReadFileMasterGroup(), "config": models.ReadConfig(models.ConfigJSON), "header": models.ReadConfig(models.HeaderLinks), "footer": models.ReadConfig(models.FooterLinks), "nav": models.ReadConfig(models.NavSections), "illegal_names": models.ReadConfig(models.IllegalNames), "DefaultConfig": models.DefaultConfigJSON, "DefaultHeader": models.DefaultHeaderLinks, "DefaultFooter": models.DefaultFooterLinks, "DefaultNav": models.DefaultNavSections, "DefaultIllegalNames": models.DefaultIllegalNames, }) })
View Source
var AdminHeaderUpdateHandler = A(func(w http.ResponseWriter, r *http.Request, ctx *Context) { if !ctx.IsAdmin || r.Method != "POST" { ErrForbiddenHandler(w, r) return } models.WriteConfig(models.HeaderLinks, r.PostFormValue("header")) ctxCacheDate = time.Unix(0, 0) ctx.SetFlashMsg("Header updated") http.Redirect(w, r, "/admin", http.StatusSeeOther) })
View Source
var AdminIllegalNamesUpdateHandler = A(func(w http.ResponseWriter, r *http.Request, ctx *Context) { if !ctx.IsAdmin || r.Method != "POST" { ErrForbiddenHandler(w, r) return } models.WriteConfig(models.IllegalNames, r.PostFormValue("illegal_names")) ctxCacheDate = time.Unix(0, 0) ctx.SetFlashMsg("Illegal name list updated") http.Redirect(w, r, "/admin", http.StatusSeeOther) })
View Source
ErrForbiddenHandler(w, r) return } models.WriteConfig(models.NavSections, r.PostFormValue("nav")) ctxCacheDate = time.Unix(0, 0) ctx.SetFlashMsg("Nav updated") http.Redirect(w, r, "/admin", http.StatusSeeOther) })if !ctx.IsAdmin || r.Method != "POST" {
View Source
var AdminPageMasterGroupHandler = A(func(w http.ResponseWriter, r *http.Request, ctx *Context) { if !ctx.IsAdmin || r.Method != "POST" { ErrForbiddenHandler(w, r) return } group := r.PostFormValue("group") var tmp string if group == models.EverybodyGroup || db.QueryRow(`SELECT id FROM groups WHERE name=?;`, group).Scan(&tmp) == nil { models.WriteConfig(models.PageMasterGroup, group) } else { ctx.SetFlashMsg("Group '" + group + "' not found") } http.Redirect(w, r, "/admin", http.StatusSeeOther) })
View Source
var AdminUserHandler = A(func(w http.ResponseWriter, r *http.Request, ctx *Context) { if !ctx.IsAdmin { ErrForbiddenHandler(w, r) return } var users []string rows := db.Query(`SELECT username FROM users;`) for rows.Next() { var user string rows.Scan(&user) users = append(users, user) } templates.Render(w, "adminusers.html", map[string]interface{}{ "ctx": ctx, "users": users, }) })
View Source
var ChangepassHandler = A(func(w http.ResponseWriter, r *http.Request, ctx *Context) { if r.Method != "POST" { ErrForbiddenHandler(w, r) return } username := r.PostFormValue("username") oldPasswd := r.PostFormValue("oldpasswd") newPasswd := r.PostFormValue("passwd") newPasswd2 := r.PostFormValue("passwd2") if !ctx.IsAdmin { if err := models.VerifyPasswd(username, oldPasswd); err != nil { ctx.SetFlashMsg(err.Error()) http.Redirect(w, r, "/profile?u="+username, http.StatusSeeOther) return } } if err := models.ValidatePasswd(newPasswd); err != nil { ctx.SetFlashMsg(err.Error()) http.Redirect(w, r, "/profile?u="+username, http.StatusSeeOther) return } if newPasswd != newPasswd2 { ctx.SetFlashMsg("New passwords do not match") http.Redirect(w, r, "/profile?u="+username, http.StatusSeeOther) return } if err := models.UpdateUserPasswd(username, newPasswd); err != nil { ctx.SetFlashMsg(err.Error()) http.Redirect(w, r, "/profile?u="+username, http.StatusSeeOther) return } ctx.SetFlashMsg("Password changed successfully") http.Redirect(w, r, "/profile?u="+username, http.StatusSeeOther) return })
View Source
var FileCreateHandler = A(func(w http.ResponseWriter, r *http.Request, ctx *Context) { if r.Method != "POST" { ErrForbiddenHandler(w, r) return } isPageMaster := ctx.IsAdmin || models.IsUserInFileMasterGroup(ctx.UserName) if !isPageMaster { templates.Render(w, "accessdenied.html", map[string]interface{}{ "ctx": ctx, }) return } dataDir := ctx.Config.DataDir if dataDir == "" { ctx.SetFlashMsg("DataDir not configured. Contact admin.") http.Redirect(w, r, "/files/#flash", http.StatusSeeOther) return } if dataDir[len(dataDir)-1] != '/' { dataDir = dataDir + "/" } r.ParseMultipartForm(32 * 1024 * 1024) file, handler, err := r.FormFile("file") if err == nil { defer file.Close() if handler.Filename != "" { title := handler.Filename if err := models.IsPageTitleValid(title); err == nil { var tmp string if db.QueryRow(`SELECT id FROM pages WHERE title=?;`, title).Scan(&tmp) == sql.ErrNoRows { f, err := os.OpenFile(dataDir+title, os.O_WRONLY|os.O_CREATE, 0666) if err == nil { defer f.Close() io.Copy(f, file) now := time.Now().Unix() db.Exec(`INSERT INTO pages(title, is_file, created_date, updated_date) VALUES(?, ?, ?, ?);`, title, true, now, now) } else { log.Panicf("[ERROR] Error writing file: %s\n", err) } } else { ctx.SetFlashMsg("File already exists") } } else { ctx.SetFlashMsg(err.Error()) } } else { ctx.SetFlashMsg("Choose file to upload") } } http.Redirect(w, r, "/files/", http.StatusSeeOther) })
View Source
var FileUpdateHandler = A(func(w http.ResponseWriter, r *http.Request, ctx *Context) { if r.Method != "POST" { ErrForbiddenHandler(w, r) return } isPageMaster := ctx.IsAdmin || models.IsUserInFileMasterGroup(ctx.UserName) if !isPageMaster { templates.Render(w, "accessdenied.html", map[string]interface{}{ "ctx": ctx, }) return } cTitle := r.FormValue("t") title := strings.Replace(cTitle, "_", " ", -1) action := r.PostFormValue("action") if action == "Update" { editGroup := r.PostFormValue("editgroup") readGroup := r.PostFormValue("readgroup") var editGroupID, readGroupID string db.QueryRow(`SELECT id FROM groups WHERE name=?;`, editGroup).Scan(&editGroupID) db.QueryRow(`SELECT id FROM groups WHERE name=?;`, readGroup).Scan(&readGroupID) if editGroupID != "" { db.Exec(`UPDATE pages SET editgroupid=? WHERE title=?;`, editGroupID, title) } else { db.Exec(`UPDATE pages SET editgroupid=NULL WHERE title=?;`, title) } if readGroupID != "" { db.Exec(`UPDATE pages SET readgroupid=? WHERE title=?;`, readGroupID, title) } else { db.Exec(`UPDATE pages SET readgroupid=NULL WHERE title=?;`, title) } ctx.SetFlashMsg("Updated file " + title) } if action == "Delete" { dataDir := ctx.Config.DataDir if dataDir != "" { if dataDir[len(dataDir)-1] != '/' { dataDir = dataDir + "/" } db.Exec(`DELETE FROM pages WHERE title=?;`, title) if err := os.Remove(dataDir + title); err != nil { log.Printf("[ERROR] Error deleting file: %s\n", err.Error()) } ctx.SetFlashMsg("Deleted file " + title) } else { ctx.SetFlashMsg("DataDir not configured") } } http.Redirect(w, r, "/files/", http.StatusSeeOther) })
View Source
var FilesHandler = UA(func(w http.ResponseWriter, r *http.Request, ctx *Context) { isPageMaster := ctx.IsAdmin || models.IsUserInFileMasterGroup(ctx.UserName) cTitle := r.URL.Path[7:] title := strings.Replace(cTitle, "_", " ", -1) if title == "" { if !isPageMaster { templates.Render(w, "accessdenied.html", map[string]interface{}{ "ctx": ctx, }) return } type File struct { Title string CTitle string ReadGroup string EditGroup string } var files []File rows := db.Query(`SELECT title, readgroupid, editgroupid FROM pages WHERE is_file=? ORDER BY title;`, true) for rows.Next() { file := File{} var readGroupID, editGroupID sql.NullString rows.Scan(&file.Title, &readGroupID, &editGroupID) file.CTitle = strings.Replace(file.Title, " ", "_", -1) if readGroupID.Valid { db.QueryRow(`SELECT name FROM groups WHERE id=?;`, readGroupID).Scan(&file.ReadGroup) } if editGroupID.Valid { db.QueryRow(`SELECT name FROM groups WHERE id=?;`, editGroupID).Scan(&file.EditGroup) } files = append(files, file) } templates.Render(w, "filelist.html", map[string]interface{}{ "ctx": ctx, "files": files, }) return } dataDir := ctx.Config.DataDir if dataDir == "" { ErrNotFoundHandler(w, r) return } if dataDir[len(dataDir)-1] != '/' { dataDir = dataDir + "/" } row := db.QueryRow(`SELECT readgroupid FROM pages WHERE title=?;`, title) var readGroupID sql.NullString if row.Scan(&readGroupID) != nil { ErrNotFoundHandler(w, r) return } if !isPageMaster && readGroupID.Valid { row := db.QueryRow(`SELECT groupmembers.id FROM groupmembers INNER JOIN users ON users.id=groupmembers.userid AND users.username=? WHERE groupmembers.groupid=?;`, ctx.UserName, readGroupID) var tmp string if row.Scan(&tmp) != nil { templates.Render(w, "accessdenied.html", map[string]interface{}{ "ctx": ctx, }) return } } http.ServeFile(w, r, ctx.Config.DataDir+title) })
View Source
var ForgotpassHandler = UA(func(w http.ResponseWriter, r *http.Request, ctx *Context) { if r.Method == "POST" { username := r.PostFormValue("username") row := db.QueryRow(`SELECT email FROM users WHERE username=?;`, username) var email string if err := row.Scan(&email); err == nil { if strings.Contains(email, "@") { resetToken := randSeq(40) db.Exec(`UPDATE users SET reset_token=?, reset_token_date=? WHERE username=?;`, resetToken, int64(time.Now().Unix()), username) resetLink := "https://" + r.Host + "/resetpass?token=" + resetToken sub := ctx.Config.WikiName + " Password Recovery" msg := "Someone (hopefully you) requested we reset your password at " + ctx.Config.WikiName + ".\r\n" + "If you want to change it, visit " + resetLink + "\r\n\r\nIf not, just ignore this message." SendMail(email, sub, msg, ctx.Config) ctx.FlashMsg = "Password reset link has been sent to your email" } else { ctx.FlashMsg = "We don't have your email. Please contact the admin to reset your password" } } else { ctx.FlashMsg = "User not found" } } templates.Render(w, "forgotpass.html", map[string]interface{}{ "ctx": ctx, }) })
View Source
var LoginHandler = UA(func(w http.ResponseWriter, r *http.Request, ctx *Context) { redirectURL, err := url.QueryUnescape(r.FormValue("next")) if redirectURL == "" || err != nil { redirectURL = "/" } if ctx.IsUserValid { http.Redirect(w, r, redirectURL, http.StatusSeeOther) return } if r.Method == "POST" { userName := r.PostFormValue("username") passwd := r.PostFormValue("passwd") if len(userName) > 200 || len(passwd) > 200 { fmt.Fprint(w, "username / password too long.") return } if err = ctx.Authenticate(userName, passwd); err == nil { http.SetCookie(w, &http.Cookie{Name: "sessionid", Path: "/", Value: ctx.SessionID, HttpOnly: true}) http.Redirect(w, r, redirectURL, http.StatusSeeOther) return } else { ctx.FlashMsg = err.Error() } } templates.Render(w, "login.html", map[string]interface{}{ "ctx": ctx, "next": template.URL(url.QueryEscape(redirectURL)), }) })
View Source
var LogoutAllHandler = A(func(w http.ResponseWriter, r *http.Request, ctx *Context) { username := r.FormValue("u") if !ctx.IsAdmin && (username != ctx.UserName) { ErrForbiddenHandler(w, r) return } var userID string if db.QueryRow(`SELECT id FROM users WHERE users.username=?;`, username).Scan(&userID) == nil { db.Exec(`DELETE FROM sessions WHERE userid=?;`, userID) } http.Redirect(w, r, "/", http.StatusSeeOther) })
View Source
var PageCreateHandler = A(func(w http.ResponseWriter, r *http.Request, ctx *Context) { if r.Method != "POST" { ErrForbiddenHandler(w, r) return } isPageMaster := ctx.IsAdmin || models.IsUserInPageMasterGroup(ctx.UserName) if !isPageMaster { templates.Render(w, "accessdenied.html", map[string]interface{}{ "ctx": ctx, }) return } title := r.PostFormValue("title") cTitle := strings.Replace(title, " ", "_", -1) var tmp string if db.QueryRow(`SELECT id FROM pages WHERE title=?;`, title).Scan(&tmp) != sql.ErrNoRows { ctx.SetFlashMsg("Page already exists") http.Redirect(w, r, "/pages/#flash", http.StatusSeeOther) return } if err := models.IsPageTitleValid(title); err != nil { ctx.SetFlashMsg(err.Error()) http.Redirect(w, r, "/pages/#flash", http.StatusSeeOther) return } content := "# " + title tNow := time.Now().Unix() db.Exec(`INSERT INTO pages(title, content, discussion, created_date, updated_date) VALUES(?, ?, ?, ?, ?);`, title, content, content, tNow, tNow) pageMasterGroup := models.ReadPageMasterGroup() if pageMasterGroup != models.DefaultPageMasterGroup { var gID string if db.QueryRow(`SELECT id FROM groups WHERE name=?;`, pageMasterGroup).Scan(&gID) == nil { db.Exec(`UPDATE pages SET editgroupid=? WHERE title=?;`, gID, title) } } http.Redirect(w, r, "/editpage?t="+cTitle, http.StatusSeeOther) })
View Source
var PageEditHandler = A(func(w http.ResponseWriter, r *http.Request, ctx *Context) { isPageMaster := ctx.IsAdmin || models.IsUserInPageMasterGroup(ctx.UserName) cTitle := r.FormValue("t") title := strings.Replace(cTitle, "_", " ", -1) isDiscussion := (r.FormValue("d") != "") var editGroupID sql.NullString db.QueryRow(`SELECT editGroupID FROM pages WHERE title=?;`, title).Scan(&editGroupID) if !isPageMaster && editGroupID.Valid { var tmp string row := db.QueryRow(`SELECT groupmembers.id FROM groupmembers INNER JOIN users ON users.id=groupmembers.userid AND users.username=? WHERE groupmembers.groupid=?;`, ctx.UserName, editGroupID) if row.Scan(&tmp) != nil { templates.Render(w, "accessdenied.html", map[string]interface{}{ "ctx": ctx, }) return } } if r.Method == "POST" { action := r.PostFormValue("action") if r.PostFormValue("meta") == "" { if action == "Update" { content := r.PostFormValue("content") reURL := "/pages/" + cTitle if isDiscussion { db.Exec(`UPDATE pages SET discussion=? WHERE title=?;`, content, title) reURL += "?d=true" } else { db.Exec(`UPDATE pages SET content=? WHERE title=?;`, content, title) } http.Redirect(w, r, reURL, http.StatusSeeOther) return } } else { if action == "Update" { if ctx.IsAdmin { editGroup := r.PostFormValue("editgroup") readGroup := r.PostFormValue("readgroup") var editGroupID, readGroupID string db.QueryRow(`SELECT id FROM groups WHERE name=?;`, editGroup).Scan(&editGroupID) db.QueryRow(`SELECT id FROM groups WHERE name=?;`, readGroup).Scan(&readGroupID) if editGroupID != "" { db.Exec(`UPDATE pages SET editgroupid=? WHERE title=?;`, editGroupID, title) } else { db.Exec(`UPDATE pages SET editgroupid=NULL WHERE title=?;`, title) } if readGroupID != "" { db.Exec(`UPDATE pages SET readgroupid=? WHERE title=?;`, readGroupID, title) } else { db.Exec(`UPDATE pages SET readgroupid=NULL WHERE title=?;`, title) } ctx.SetFlashMsg("Updated post " + title) } } if action == "Delete" { if !isPageMaster { templates.Render(w, "accessdenied.html", map[string]interface{}{ "ctx": ctx, }) return } ctx.SetFlashMsg("Deleted post " + title) db.Exec(`DELETE FROM pages WHERE title=?;`, title) } http.Redirect(w, r, "/pages/", http.StatusSeeOther) return } } var row *db.Row if isDiscussion { row = db.QueryRow(`SELECT discussion FROM pages WHERE title=?;`, title) } else { row = db.QueryRow(`SELECT content FROM pages WHERE title=?;`, title) } var content string if row.Scan(&content) != nil { ErrNotFoundHandler(w, r) return } editURL := "/editpage?t=" + cTitle if isDiscussion { editURL += "&d=true" } templates.Render(w, "index.html", map[string]interface{}{ "ctx": ctx, "IsEditMode": true, "URL": "/pages/" + cTitle, "EditURL": "/editpage?t=" + cTitle, "Title": title, "cTitle": cTitle, "IsDiscussion": isDiscussion, "Content": content, }) })
View Source
var PagesHandler = UA(func(w http.ResponseWriter, r *http.Request, ctx *Context) { isPageMaster := ctx.IsAdmin || models.IsUserInPageMasterGroup(ctx.UserName) title := models.IndexPage cTitle := strings.Replace(title, " ", "_", -1) isDiscussion := (r.FormValue("d") != "") if r.URL.Path != "/" { cTitle = r.URL.Path[7:] title = strings.Replace(cTitle, "_", " ", -1) if title == models.IndexPage && !isDiscussion { http.Redirect(w, r, "/", http.StatusSeeOther) return } } if title == "" { if !isPageMaster { templates.Render(w, "accessdenied.html", map[string]interface{}{ "ctx": ctx, }) return } type Page struct { Title string CTitle string ReadGroup string EditGroup string } pages := []Page{} rows := db.Query(`SELECT title, readgroupid, editgroupid FROM pages WHERE is_file=? ORDER BY title;`, false) for rows.Next() { page := Page{} var readGroupID, editGroupID sql.NullString rows.Scan(&page.Title, &readGroupID, &editGroupID) page.CTitle = strings.Replace(page.Title, " ", "_", -1) if readGroupID.Valid { db.QueryRow(`SELECT name FROM groups WHERE id=?;`, readGroupID).Scan(&page.ReadGroup) } if editGroupID.Valid { db.QueryRow(`SELECT name FROM groups WHERE id=?;`, editGroupID).Scan(&page.EditGroup) } pages = append(pages, page) } templates.Render(w, "pagelist.html", map[string]interface{}{ "ctx": ctx, "pages": pages, }) return } // Render the relevant wiki page var row *db.Row if isDiscussion { row = db.QueryRow(`SELECT readgroupid, discussion FROM pages WHERE title=?;`, title) } else { row = db.QueryRow(`SELECT readgroupid, content FROM pages WHERE title=?;`, title) } var content string var readGroupID sql.NullString if row.Scan(&readGroupID, &content) != nil { ErrNotFoundHandler(w, r) return } if !isPageMaster && readGroupID.Valid { row := db.QueryRow(`SELECT groupmembers.id FROM groupmembers INNER JOIN users ON users.id=groupmembers.userid AND users.username=? WHERE groupmembers.groupid=?;`, ctx.UserName, readGroupID) var tmp string if row.Scan(&tmp) != nil { templates.Render(w, "accessdenied.html", map[string]interface{}{ "ctx": ctx, }) return } } unsafe := blackfriday.Run([]byte(strings.Replace(content, "\r\n", "\n", -1))) html := string(bluemonday.UGCPolicy().SanitizeBytes(unsafe)) type Link struct { name string URL string } type TOCItem struct { title Link subtitles []Link } var toc []TOCItem html = headerRe.ReplaceAllStringFunc(html, func(h string) string { n := string(h[2]) if n == "2" || n == "3" { if len(h) >= 10 { t := h[4 : len(h)-5] id := "m-" + n + "-" + base64.StdEncoding.EncodeToString([]byte(t)) link := Link{t, "#" + id} if n == "2" { var tocItem TOCItem tocItem.title = link toc = append(toc, tocItem) } if n == "3" { if len(toc) > 0 { toc[len(toc)-1].subtitles = append(toc[len(toc)-1].subtitles, link) } } return "<h" + n + " id=\"" + id + "\">" + t + "</h" + n + ">" } } return h }) tocHTML := "<div class=\"toc\"><ol>\n" for _, t := range toc { tocHTML += "<li><a href=\"" + t.title.URL + "\">" + t.title.name + "</a>" if len(t.subtitles) > 0 { tocHTML += "<ul>" for _, s := range t.subtitles { tocHTML += "<li><a href=\"" + s.URL + "\">" + s.name + "</a>" } tocHTML += "</ul>" } tocHTML += "</li>" } tocHTML += "\n</ol></div>" html = strings.Replace(html, "<p><strong>TOC</strong></p>", tocHTML, 1) if r.URL.Path != "" { ctx.PageTitle = title } editURL := "/editpage?t=" + cTitle if isDiscussion { editURL += "&d=true" } templates.Render(w, "index.html", map[string]interface{}{ "ctx": ctx, "Title": title, "cTitle": cTitle, "IsDiscussion": isDiscussion, "URL": "/pages/" + cTitle, "EditURL": editURL, "Content": template.HTML(html), }) })
View Source
var ProfileBanHandler = A(func(w http.ResponseWriter, r *http.Request, ctx *Context) { if r.Method != "POST" { ErrForbiddenHandler(w, r) return } username := r.PostFormValue("username") if !ctx.IsAdmin { ErrForbiddenHandler(w, r) return } db.Exec(`UPDATE users SET is_banned=? WHERE username=?;`, true, username) var userID string if db.QueryRow(`SELECT id FROM users WHERE username=?;`, username).Scan(&userID) == nil { db.Exec(`DELETE FROM sessions WHERE userid=?;`, userID) } http.Redirect(w, r, "/profile?u="+username, http.StatusSeeOther) })
View Source
var ProfileHandler = A(func(w http.ResponseWriter, r *http.Request, ctx *Context) { username := r.FormValue("u") if !ctx.IsAdmin && (username != ctx.UserName) { ErrForbiddenHandler(w, r) return } row := db.QueryRow(`SELECT email, is_banned FROM users WHERE username=?;`, username) var email string var isBanned bool if err := row.Scan(&email, &isBanned); err != nil { ErrNotFoundHandler(w, r) return } if isBanned && !ctx.IsAdmin { ErrNotFoundHandler(w, r) return } templates.Render(w, "profile.html", map[string]interface{}{ "ctx": ctx, "username": username, "email": email, "IsBanned": isBanned, }) })
View Source
var ProfileUnbanHandler = A(func(w http.ResponseWriter, r *http.Request, ctx *Context) { if r.Method != "POST" { ErrForbiddenHandler(w, r) return } username := r.PostFormValue("username") if !ctx.IsAdmin { ErrForbiddenHandler(w, r) return } db.Exec(`UPDATE users SET is_banned=? WHERE username=?;`, false, username) http.Redirect(w, r, "/profile?u="+username, http.StatusSeeOther) })
View Source
var ProfileUpdateHandler = A(func(w http.ResponseWriter, r *http.Request, ctx *Context) { if r.Method != "POST" { ErrForbiddenHandler(w, r) return } username := r.PostFormValue("username") if !ctx.IsAdmin && (username != ctx.UserName) { ErrForbiddenHandler(w, r) return } email := r.PostFormValue("email") db.Exec(`UPDATE users SET email=? WHERE username=?;`, email, username) ctx.SetFlashMsg("Email updated") http.Redirect(w, r, "/profile?u="+username, http.StatusSeeOther) })
View Source
var ResetpassHandler = UA(func(w http.ResponseWriter, r *http.Request, ctx *Context) { resetToken := r.FormValue("token") row := db.QueryRow(`SELECT username, reset_token_date FROM users WHERE reset_token=?;`, resetToken) var username string var rDate int64 if err := row.Scan(&username, &rDate); err != nil { ErrNotFoundHandler(w, r) return } resetTokenDate := time.Unix(rDate, 0) if resetTokenDate.Before(time.Now().Add(-100 * time.Hour)) { ErrNotFoundHandler(w, r) return } if r.Method == "POST" { passwd := r.PostFormValue("passwd") passwd2 := r.PostFormValue("passwd2") if passwd == passwd2 { if err := models.ValidatePasswd(passwd); err == nil { models.UpdateUserPasswd(username, passwd) db.Exec(`UPDATE users SET reset_token_date=0 WHERE username=?;`, username) http.Redirect(w, r, "/login", http.StatusSeeOther) return } else { ctx.FlashMsg = err.Error() } } else { ctx.FlashMsg = "New passwords do not match" } } templates.Render(w, "resetpass.html", map[string]interface{}{ "ctx": ctx, "ResetToken": resetToken, }) })
View Source
var SearchHandler = UA(func(w http.ResponseWriter, r *http.Request, ctx *Context) { searchTerms := r.FormValue("q") results := models.PageSearch(searchTerms) templates.Render(w, "search.html", map[string]interface{}{ "ctx": ctx, "Results": results, "SearchTerms": searchTerms, }) })
View Source
var SignupHandler = UA(func(w http.ResponseWriter, r *http.Request, ctx *Context) { if r.Method == "POST" { username := r.PostFormValue("username") passwd := r.PostFormValue("passwd") passwd2 := r.PostFormValue("passwd2") if ctx.IsAdmin || !ctx.Config.SignupDisabled { if err := models.ValidateName(username); err == nil { if passwd == passwd2 { if err := models.ValidatePasswd(passwd); err == nil { if err := models.CreateUser(username, passwd, "", false); err == nil { if ctx.IsAdmin { http.Redirect(w, r, "/admin/users", http.StatusSeeOther) return } else { if err := ctx.Authenticate(username, passwd); err == nil { http.SetCookie(w, &http.Cookie{Name: "sessionid", Path: "/", Value: ctx.SessionID, HttpOnly: true}) http.Redirect(w, r, "/", http.StatusSeeOther) return } else { ctx.FlashMsg = err.Error() } } } else { ctx.FlashMsg = err.Error() } } else { ctx.FlashMsg = err.Error() } } else { ctx.FlashMsg = "Passwords don't match" } } else { ctx.FlashMsg = err.Error() } } else { ctx.FlashMsg = "Signup disabled. Contact admin." } } templates.Render(w, "signup.html", map[string]interface{}{ "ctx": ctx, }) })
Functions ¶
func A ¶
func A(handler func(w http.ResponseWriter, r *http.Request, ctx *Context)) func(w http.ResponseWriter, r *http.Request)
func ErrForbiddenHandler ¶
func ErrForbiddenHandler(w http.ResponseWriter, r *http.Request)
func ErrNotFoundHandler ¶
func ErrNotFoundHandler(w http.ResponseWriter, r *http.Request)
func ErrServerHandler ¶
func ErrServerHandler(w http.ResponseWriter, r *http.Request)
func FaviconHandler ¶
func FaviconHandler(w http.ResponseWriter, r *http.Request)
func LogoutHandler ¶
func LogoutHandler(w http.ResponseWriter, r *http.Request)
func ScriptHandler ¶
func ScriptHandler(w http.ResponseWriter, r *http.Request)
func StyleHandler ¶
func StyleHandler(w http.ResponseWriter, r *http.Request)
Types ¶
type Context ¶
type Context struct { SessionID string UserName string IsUserValid bool IsAdmin bool AdminErrMsg string CSRFToken string FlashMsg string PageTitle string Config WikiConfig HeaderLinks []NavLink }
func ReadContext ¶
func (*Context) Authenticate ¶
func (*Context) SetFlashMsg ¶
func (*Context) ValidateCSRFToken ¶
type NavSection ¶
type NavSection struct {}
Source Files ¶
Click to show internal directories.
Click to hide internal directories.