47963e10a9
Renamed the rrow_assign hook to topic_reply_row_assign and gave it access to more data. Fixed a bug where the topic store wouldn't fetch the last reply time for a topic. Refactored the process of adding and removing topics from and to a *Forum. Fixed a bug where editing the opening post of a topic would yield a vast number of <br>s instead of blank lines. Selecting text in Shadow now has it's own CSS instead of falling back onto the browser defaults. Fixed a bug in Shadow where not all the headers filled up the space they should. Fixed a bug in Shadow where the footer is broken on mobile. Added an ARIA Label to the topic list. Refactored the last poster logic to reduce the number of bugs. Renamed ReplyShort to Reply and Reply to ReplyUser. Added a Copy method to Reply, Group, Forum, User, and Topic. Rewrote Hello World into something slightly more useful for new plugin devs to learn off. Added the GetLength() method to ForumCache.
673 lines
22 KiB
Go
673 lines
22 KiB
Go
package main
|
|
|
|
import (
|
|
//"fmt"
|
|
"bytes"
|
|
"context"
|
|
"database/sql"
|
|
"errors"
|
|
"html"
|
|
"html/template"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"./query_gen/lib"
|
|
)
|
|
|
|
var socialgroupsListStmt *sql.Stmt
|
|
var socialgroupsMemberListStmt *sql.Stmt
|
|
var socialgroupsMemberListJoinStmt *sql.Stmt
|
|
var socialgroupsGetMemberStmt *sql.Stmt
|
|
var socialgroupsGetGroupStmt *sql.Stmt
|
|
var socialgroupsCreateGroupStmt *sql.Stmt
|
|
var socialgroupsAttachForumStmt *sql.Stmt
|
|
var socialgroupsUnattachForumStmt *sql.Stmt
|
|
var socialgroupsAddMemberStmt *sql.Stmt
|
|
|
|
// TODO: Add a better way of splitting up giant plugins like this
|
|
|
|
// SocialGroup is a struct representing a social group
|
|
type SocialGroup struct {
|
|
ID int
|
|
Link string
|
|
Name string
|
|
Desc string
|
|
Active bool
|
|
Privacy int /* 0: Public, 1: Protected, 2: Private */
|
|
|
|
// Who should be able to accept applications and create invites? Mods+ or just admins? Mods is a good start, we can ponder over whether we should make this more flexible in the future.
|
|
Joinable int /* 0: Private, 1: Anyone can join, 2: Applications, 3: Invite-only */
|
|
|
|
MemberCount int
|
|
Owner int
|
|
Backdrop string
|
|
CreatedAt string
|
|
LastUpdateTime string
|
|
|
|
MainForumID int
|
|
MainForum *Forum
|
|
Forums []*Forum
|
|
ExtData ExtData
|
|
}
|
|
|
|
type SocialGroupPage struct {
|
|
Title string
|
|
CurrentUser User
|
|
Header *HeaderVars
|
|
ItemList []*TopicsRow
|
|
Forum *Forum
|
|
SocialGroup *SocialGroup
|
|
Page int
|
|
LastPage int
|
|
}
|
|
|
|
// SocialGroupListPage is a page struct for constructing a list of every social group
|
|
type SocialGroupListPage struct {
|
|
Title string
|
|
CurrentUser User
|
|
Header *HeaderVars
|
|
GroupList []*SocialGroup
|
|
}
|
|
|
|
type SocialGroupMemberListPage struct {
|
|
Title string
|
|
CurrentUser User
|
|
Header *HeaderVars
|
|
ItemList []SocialGroupMember
|
|
SocialGroup *SocialGroup
|
|
Page int
|
|
LastPage int
|
|
}
|
|
|
|
// SocialGroupMember is a struct representing a specific member of a group, not to be confused with the global User struct.
|
|
type SocialGroupMember struct {
|
|
Link string
|
|
Rank int /* 0: Member. 1: Mod. 2: Admin. */
|
|
RankString string /* Member, Mod, Admin, Owner */
|
|
PostCount int
|
|
JoinedAt string
|
|
Offline bool // TODO: Need to track the online states of members when WebSockets are enabled
|
|
|
|
User User
|
|
}
|
|
|
|
// TODO: Add a plugin interface instead of having a bunch of argument to AddPlugin?
|
|
func init() {
|
|
plugins["socialgroups"] = NewPlugin("socialgroups", "Social Groups", "Azareal", "http://github.com/Azareal", "", "", "", initSocialgroups, nil, deactivateSocialgroups, installSocialgroups, nil)
|
|
}
|
|
|
|
func initSocialgroups() (err error) {
|
|
plugins["socialgroups"].AddHook("intercept_build_widgets", socialgroupsWidgets)
|
|
plugins["socialgroups"].AddHook("trow_assign", socialgroupsTrowAssign)
|
|
plugins["socialgroups"].AddHook("topic_create_pre_loop", socialgroupsTopicCreatePreLoop)
|
|
plugins["socialgroups"].AddHook("pre_render_view_forum", socialgroupsPreRenderViewForum)
|
|
plugins["socialgroups"].AddHook("simple_forum_check_pre_perms", socialgroupsForumCheck)
|
|
plugins["socialgroups"].AddHook("forum_check_pre_perms", socialgroupsForumCheck)
|
|
// TODO: Auto-grant this perm to admins upon installation?
|
|
registerPluginPerm("CreateSocialGroup")
|
|
router.HandleFunc("/groups/", socialgroupsGroupList)
|
|
router.HandleFunc("/group/", socialgroupsViewGroup)
|
|
router.HandleFunc("/group/create/", socialgroupsCreateGroup)
|
|
router.HandleFunc("/group/create/submit/", socialgroupsCreateGroupSubmit)
|
|
router.HandleFunc("/group/members/", socialgroupsMemberList)
|
|
|
|
socialgroupsListStmt, err = qgen.Builder.SimpleSelect("socialgroups", "sgid, name, desc, active, privacy, joinable, owner, memberCount, createdAt, lastUpdateTime", "", "", "")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
socialgroupsGetGroupStmt, err = qgen.Builder.SimpleSelect("socialgroups", "name, desc, active, privacy, joinable, owner, memberCount, mainForum, backdrop, createdAt, lastUpdateTime", "sgid = ?", "", "")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
socialgroupsMemberListStmt, err = qgen.Builder.SimpleSelect("socialgroups_members", "sgid, uid, rank, posts, joinedAt", "", "", "")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
socialgroupsMemberListJoinStmt, err = qgen.Builder.SimpleLeftJoin("socialgroups_members", "users", "users.uid, socialgroups_members.rank, socialgroups_members.posts, socialgroups_members.joinedAt, users.name, users.avatar", "socialgroups_members.uid = users.uid", "socialgroups_members.sgid = ?", "socialgroups_members.rank DESC, socialgroups_members.joinedat ASC", "")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
socialgroupsGetMemberStmt, err = qgen.Builder.SimpleSelect("socialgroups_members", "rank, posts, joinedAt", "sgid = ? AND uid = ?", "", "")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
socialgroupsCreateGroupStmt, err = qgen.Builder.SimpleInsert("socialgroups", "name, desc, active, privacy, joinable, owner, memberCount, mainForum, backdrop, createdAt, lastUpdateTime", "?,?,?,?,1,?,1,?,'',UTC_TIMESTAMP(),UTC_TIMESTAMP()")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
socialgroupsAttachForumStmt, err = qgen.Builder.SimpleUpdate("forums", "parentID = ?, parentType = 'socialgroup'", "fid = ?")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
socialgroupsUnattachForumStmt, err = qgen.Builder.SimpleUpdate("forums", "parentID = 0, parentType = ''", "fid = ?")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
socialgroupsAddMemberStmt, err = qgen.Builder.SimpleInsert("socialgroups_members", "sgid, uid, rank, posts, joinedAt", "?,?,?,0,UTC_TIMESTAMP()")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func deactivateSocialgroups() {
|
|
plugins["socialgroups"].RemoveHook("intercept_build_widgets", socialgroupsWidgets)
|
|
plugins["socialgroups"].RemoveHook("trow_assign", socialgroupsTrowAssign)
|
|
plugins["socialgroups"].RemoveHook("topic_create_pre_loop", socialgroupsTopicCreatePreLoop)
|
|
plugins["socialgroups"].RemoveHook("pre_render_view_forum", socialgroupsPreRenderViewForum)
|
|
plugins["socialgroups"].RemoveHook("simple_forum_check_pre_perms", socialgroupsForumCheck)
|
|
plugins["socialgroups"].RemoveHook("forum_check_pre_perms", socialgroupsForumCheck)
|
|
deregisterPluginPerm("CreateSocialGroup")
|
|
_ = router.RemoveFunc("/groups/")
|
|
_ = router.RemoveFunc("/group/")
|
|
_ = router.RemoveFunc("/group/create/")
|
|
_ = router.RemoveFunc("/group/create/submit/")
|
|
_ = socialgroupsListStmt.Close()
|
|
_ = socialgroupsMemberListStmt.Close()
|
|
_ = socialgroupsMemberListJoinStmt.Close()
|
|
_ = socialgroupsGetMemberStmt.Close()
|
|
_ = socialgroupsGetGroupStmt.Close()
|
|
_ = socialgroupsCreateGroupStmt.Close()
|
|
_ = socialgroupsAttachForumStmt.Close()
|
|
_ = socialgroupsUnattachForumStmt.Close()
|
|
_ = socialgroupsAddMemberStmt.Close()
|
|
}
|
|
|
|
// TODO: Stop accessing the query builder directly and add a feature in Gosora which is more easily reversed, if an error comes up during the installation process
|
|
func installSocialgroups() error {
|
|
sgTableStmt, err := qgen.Builder.CreateTable("socialgroups", "utf8mb4", "utf8mb4_general_ci",
|
|
[]qgen.DB_Table_Column{
|
|
qgen.DB_Table_Column{"sgid", "int", 0, false, true, ""},
|
|
qgen.DB_Table_Column{"name", "varchar", 100, false, false, ""},
|
|
qgen.DB_Table_Column{"desc", "varchar", 200, false, false, ""},
|
|
qgen.DB_Table_Column{"active", "boolean", 1, false, false, ""},
|
|
qgen.DB_Table_Column{"privacy", "smallint", 0, false, false, ""},
|
|
qgen.DB_Table_Column{"joinable", "smallint", 0, false, false, "0"},
|
|
qgen.DB_Table_Column{"owner", "int", 0, false, false, ""},
|
|
qgen.DB_Table_Column{"memberCount", "int", 0, false, false, ""},
|
|
qgen.DB_Table_Column{"mainForum", "int", 0, false, false, "0"}, // The board the user lands on when they click on a group, we'll make it possible for group admins to change what users land on
|
|
//qgen.DB_Table_Column{"boards","varchar",255,false,false,""}, // Cap the max number of boards at 8 to avoid overflowing the confines of a 64-bit integer?
|
|
qgen.DB_Table_Column{"backdrop", "varchar", 200, false, false, ""}, // File extension for the uploaded file, or an external link
|
|
qgen.DB_Table_Column{"createdAt", "createdAt", 0, false, false, ""},
|
|
qgen.DB_Table_Column{"lastUpdateTime", "datetime", 0, false, false, ""},
|
|
},
|
|
[]qgen.DB_Table_Key{
|
|
qgen.DB_Table_Key{"sgid", "primary"},
|
|
},
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
_, err = sgTableStmt.Exec()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
sgMembersTableStmt, err := qgen.Builder.CreateTable("socialgroups_members", "", "",
|
|
[]qgen.DB_Table_Column{
|
|
qgen.DB_Table_Column{"sgid", "int", 0, false, false, ""},
|
|
qgen.DB_Table_Column{"uid", "int", 0, false, false, ""},
|
|
qgen.DB_Table_Column{"rank", "int", 0, false, false, "0"}, /* 0: Member. 1: Mod. 2: Admin. */
|
|
qgen.DB_Table_Column{"posts", "int", 0, false, false, "0"}, /* Per-Group post count. Should we do some sort of score system? */
|
|
qgen.DB_Table_Column{"joinedAt", "datetime", 0, false, false, ""},
|
|
},
|
|
[]qgen.DB_Table_Key{},
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
_, err = sgMembersTableStmt.Exec()
|
|
return err
|
|
}
|
|
|
|
// TO-DO; Implement an uninstallation system into Gosora. And a better installation system.
|
|
func uninstallSocialgroups() error {
|
|
return nil
|
|
}
|
|
|
|
// TODO: Do this properly via the widget system
|
|
func socialgroupsCommonAreaWidgets(headerVars *HeaderVars) {
|
|
// TODO: Hot Groups? Featured Groups? Official Groups?
|
|
var b bytes.Buffer
|
|
var menu = WidgetMenu{"Social Groups", []WidgetMenuItem{
|
|
WidgetMenuItem{"Create Group", "/group/create/", false},
|
|
}}
|
|
|
|
err := templates.ExecuteTemplate(&b, "widget_menu.html", menu)
|
|
if err != nil {
|
|
LogError(err)
|
|
return
|
|
}
|
|
|
|
if themes[headerVars.ThemeName].Sidebars == "left" {
|
|
headerVars.Widgets.LeftSidebar = template.HTML(string(b.Bytes()))
|
|
} else if themes[headerVars.ThemeName].Sidebars == "right" || themes[headerVars.ThemeName].Sidebars == "both" {
|
|
headerVars.Widgets.RightSidebar = template.HTML(string(b.Bytes()))
|
|
}
|
|
}
|
|
|
|
// TODO: Do this properly via the widget system
|
|
// TODO: Make a better more customisable group widget system
|
|
func socialgroupsGroupWidgets(headerVars *HeaderVars, sgItem *SocialGroup) (success bool) {
|
|
return false // Disabled until the next commit
|
|
|
|
/*var b bytes.Buffer
|
|
var menu WidgetMenu = WidgetMenu{"Group Options", []WidgetMenuItem{
|
|
WidgetMenuItem{"Join", "/group/join/" + strconv.Itoa(sgItem.ID), false},
|
|
WidgetMenuItem{"Members", "/group/members/" + strconv.Itoa(sgItem.ID), false},
|
|
}}
|
|
|
|
err := templates.ExecuteTemplate(&b, "widget_menu.html", menu)
|
|
if err != nil {
|
|
LogError(err)
|
|
return false
|
|
}
|
|
|
|
if themes[headerVars.ThemeName].Sidebars == "left" {
|
|
headerVars.Widgets.LeftSidebar = template.HTML(string(b.Bytes()))
|
|
} else if themes[headerVars.ThemeName].Sidebars == "right" || themes[headerVars.ThemeName].Sidebars == "both" {
|
|
headerVars.Widgets.RightSidebar = template.HTML(string(b.Bytes()))
|
|
} else {
|
|
return false
|
|
}
|
|
return true*/
|
|
}
|
|
|
|
/*
|
|
Custom Pages
|
|
*/
|
|
|
|
func socialgroupsGroupList(w http.ResponseWriter, r *http.Request, user User) {
|
|
headerVars, ok := UserCheck(w, r, &user)
|
|
if !ok {
|
|
return
|
|
}
|
|
socialgroupsCommonAreaWidgets(headerVars)
|
|
|
|
rows, err := socialgroupsListStmt.Query()
|
|
if err != nil && err != ErrNoRows {
|
|
InternalError(err, w)
|
|
return
|
|
}
|
|
|
|
var sgList []*SocialGroup
|
|
for rows.Next() {
|
|
sgItem := &SocialGroup{ID: 0}
|
|
err := rows.Scan(&sgItem.ID, &sgItem.Name, &sgItem.Desc, &sgItem.Active, &sgItem.Privacy, &sgItem.Joinable, &sgItem.Owner, &sgItem.MemberCount, &sgItem.CreatedAt, &sgItem.LastUpdateTime)
|
|
if err != nil {
|
|
InternalError(err, w)
|
|
return
|
|
}
|
|
sgItem.Link = socialgroupsBuildGroupURL(nameToSlug(sgItem.Name), sgItem.ID)
|
|
sgList = append(sgList, sgItem)
|
|
}
|
|
err = rows.Err()
|
|
if err != nil {
|
|
InternalError(err, w)
|
|
return
|
|
}
|
|
rows.Close()
|
|
|
|
pi := SocialGroupListPage{"Group List", user, headerVars, sgList}
|
|
err = templates.ExecuteTemplate(w, "socialgroups_group_list.html", pi)
|
|
if err != nil {
|
|
InternalError(err, w)
|
|
}
|
|
}
|
|
|
|
func socialgroupsGetGroup(sgid int) (sgItem *SocialGroup, err error) {
|
|
sgItem = &SocialGroup{ID: sgid}
|
|
err = socialgroupsGetGroupStmt.QueryRow(sgid).Scan(&sgItem.Name, &sgItem.Desc, &sgItem.Active, &sgItem.Privacy, &sgItem.Joinable, &sgItem.Owner, &sgItem.MemberCount, &sgItem.MainForumID, &sgItem.Backdrop, &sgItem.CreatedAt, &sgItem.LastUpdateTime)
|
|
return sgItem, err
|
|
}
|
|
|
|
func socialgroupsViewGroup(w http.ResponseWriter, r *http.Request, user User) {
|
|
// SEO URLs...
|
|
halves := strings.Split(r.URL.Path[len("/group/"):], ".")
|
|
if len(halves) < 2 {
|
|
halves = append(halves, halves[0])
|
|
}
|
|
sgid, err := strconv.Atoi(halves[1])
|
|
if err != nil {
|
|
PreError("Not a valid group ID", w, r)
|
|
return
|
|
}
|
|
|
|
sgItem, err := socialgroupsGetGroup(sgid)
|
|
if err != nil {
|
|
LocalError("Bad group", w, r, user)
|
|
return
|
|
}
|
|
if !sgItem.Active {
|
|
NotFound(w, r)
|
|
}
|
|
|
|
// Re-route the request to routeForums
|
|
var ctx = context.WithValue(r.Context(), "socialgroups_current_group", sgItem)
|
|
routeForum(w, r.WithContext(ctx), user, strconv.Itoa(sgItem.MainForumID))
|
|
}
|
|
|
|
func socialgroupsCreateGroup(w http.ResponseWriter, r *http.Request, user User) {
|
|
headerVars, ok := UserCheck(w, r, &user)
|
|
if !ok {
|
|
return
|
|
}
|
|
// TODO: Add an approval queue mode for group creation
|
|
if !user.Loggedin || !user.PluginPerms["CreateSocialGroup"] {
|
|
NoPermissions(w, r, user)
|
|
return
|
|
}
|
|
socialgroupsCommonAreaWidgets(headerVars)
|
|
|
|
pi := Page{"Create Group", user, headerVars, tList, nil}
|
|
err := templates.ExecuteTemplate(w, "socialgroups_create_group.html", pi)
|
|
if err != nil {
|
|
InternalError(err, w)
|
|
}
|
|
}
|
|
|
|
func socialgroupsCreateGroupSubmit(w http.ResponseWriter, r *http.Request, user User) {
|
|
// TODO: Add an approval queue mode for group creation
|
|
if !user.Loggedin || !user.PluginPerms["CreateSocialGroup"] {
|
|
NoPermissions(w, r, user)
|
|
return
|
|
}
|
|
|
|
var groupActive = true
|
|
var groupName = html.EscapeString(r.PostFormValue("group_name"))
|
|
var groupDesc = html.EscapeString(r.PostFormValue("group_desc"))
|
|
var gprivacy = r.PostFormValue("group_privacy")
|
|
|
|
var groupPrivacy int
|
|
switch gprivacy {
|
|
case "0":
|
|
groupPrivacy = 0 // Public
|
|
case "1":
|
|
groupPrivacy = 1 // Protected
|
|
case "2":
|
|
groupPrivacy = 2 // private
|
|
default:
|
|
groupPrivacy = 0
|
|
}
|
|
|
|
// Create the backing forum
|
|
fid, err := fstore.Create(groupName, "", true, "")
|
|
if err != nil {
|
|
InternalError(err, w)
|
|
return
|
|
}
|
|
|
|
res, err := socialgroupsCreateGroupStmt.Exec(groupName, groupDesc, groupActive, groupPrivacy, user.ID, fid)
|
|
if err != nil {
|
|
InternalError(err, w)
|
|
return
|
|
}
|
|
lastID, err := res.LastInsertId()
|
|
if err != nil {
|
|
InternalError(err, w)
|
|
return
|
|
}
|
|
|
|
// Add the main backing forum to the forum list
|
|
err = socialgroupsAttachForum(int(lastID), fid)
|
|
if err != nil {
|
|
InternalError(err, w)
|
|
return
|
|
}
|
|
|
|
_, err = socialgroupsAddMemberStmt.Exec(lastID, user.ID, 2)
|
|
if err != nil {
|
|
InternalError(err, w)
|
|
return
|
|
}
|
|
|
|
http.Redirect(w, r, socialgroupsBuildGroupURL(nameToSlug(groupName), int(lastID)), http.StatusSeeOther)
|
|
}
|
|
|
|
func socialgroupsMemberList(w http.ResponseWriter, r *http.Request, user User) {
|
|
headerVars, ok := UserCheck(w, r, &user)
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
// SEO URLs...
|
|
halves := strings.Split(r.URL.Path[len("/group/members/"):], ".")
|
|
if len(halves) < 2 {
|
|
halves = append(halves, halves[0])
|
|
}
|
|
sgid, err := strconv.Atoi(halves[1])
|
|
if err != nil {
|
|
PreError("Not a valid group ID", w, r)
|
|
return
|
|
}
|
|
|
|
var sgItem = &SocialGroup{ID: sgid}
|
|
var mainForum int // Unused
|
|
err = socialgroupsGetGroupStmt.QueryRow(sgid).Scan(&sgItem.Name, &sgItem.Desc, &sgItem.Active, &sgItem.Privacy, &sgItem.Joinable, &sgItem.Owner, &sgItem.MemberCount, &mainForum, &sgItem.Backdrop, &sgItem.CreatedAt, &sgItem.LastUpdateTime)
|
|
if err != nil {
|
|
LocalError("Bad group", w, r, user)
|
|
return
|
|
}
|
|
sgItem.Link = socialgroupsBuildGroupURL(nameToSlug(sgItem.Name), sgItem.ID)
|
|
|
|
socialgroupsGroupWidgets(headerVars, sgItem)
|
|
|
|
rows, err := socialgroupsMemberListJoinStmt.Query(sgid)
|
|
if err != nil && err != ErrNoRows {
|
|
InternalError(err, w)
|
|
return
|
|
}
|
|
|
|
var sgMembers []SocialGroupMember
|
|
for rows.Next() {
|
|
sgMember := SocialGroupMember{PostCount: 0}
|
|
err := rows.Scan(&sgMember.User.ID, &sgMember.Rank, &sgMember.PostCount, &sgMember.JoinedAt, &sgMember.User.Name, &sgMember.User.Avatar)
|
|
if err != nil {
|
|
InternalError(err, w)
|
|
return
|
|
}
|
|
sgMember.Link = buildProfileURL(nameToSlug(sgMember.User.Name), sgMember.User.ID)
|
|
if sgMember.User.Avatar != "" {
|
|
if sgMember.User.Avatar[0] == '.' {
|
|
sgMember.User.Avatar = "/uploads/avatar_" + strconv.Itoa(sgMember.User.ID) + sgMember.User.Avatar
|
|
}
|
|
} else {
|
|
sgMember.User.Avatar = strings.Replace(config.Noavatar, "{id}", strconv.Itoa(sgMember.User.ID), 1)
|
|
}
|
|
sgMember.JoinedAt, _ = relativeTime(sgMember.JoinedAt)
|
|
if sgItem.Owner == sgMember.User.ID {
|
|
sgMember.RankString = "Owner"
|
|
} else {
|
|
switch sgMember.Rank {
|
|
case 0:
|
|
sgMember.RankString = "Member"
|
|
case 1:
|
|
sgMember.RankString = "Mod"
|
|
case 2:
|
|
sgMember.RankString = "Admin"
|
|
}
|
|
}
|
|
sgMembers = append(sgMembers, sgMember)
|
|
}
|
|
err = rows.Err()
|
|
if err != nil {
|
|
InternalError(err, w)
|
|
return
|
|
}
|
|
rows.Close()
|
|
|
|
pi := SocialGroupMemberListPage{"Group Member List", user, headerVars, sgMembers, sgItem, 0, 0}
|
|
// A plugin with plugins. Pluginception!
|
|
if preRenderHooks["pre_render_socialgroups_member_list"] != nil {
|
|
if runPreRenderHook("pre_render_socialgroups_member_list", w, r, &user, &pi) {
|
|
return
|
|
}
|
|
}
|
|
err = templates.ExecuteTemplate(w, "socialgroups_member_list.html", pi)
|
|
if err != nil {
|
|
InternalError(err, w)
|
|
}
|
|
}
|
|
|
|
func socialgroupsAttachForum(sgid int, fid int) error {
|
|
_, err := socialgroupsAttachForumStmt.Exec(sgid, fid)
|
|
return err
|
|
}
|
|
|
|
func socialgroupsUnattachForum(fid int) error {
|
|
_, err := socialgroupsAttachForumStmt.Exec(fid)
|
|
return err
|
|
}
|
|
|
|
func socialgroupsBuildGroupURL(slug string, id int) string {
|
|
if slug == "" {
|
|
return "/group/" + slug + "." + strconv.Itoa(id)
|
|
}
|
|
return "/group/" + strconv.Itoa(id)
|
|
}
|
|
|
|
/*
|
|
Hooks
|
|
*/
|
|
|
|
func socialgroupsPreRenderViewForum(w http.ResponseWriter, r *http.Request, user *User, data interface{}) (halt bool) {
|
|
pi := data.(*ForumPage)
|
|
if pi.Header.ExtData.items != nil {
|
|
if sgData, ok := pi.Header.ExtData.items["socialgroups_current_group"]; ok {
|
|
sgItem := sgData.(*SocialGroup)
|
|
|
|
sgpi := SocialGroupPage{pi.Title, pi.CurrentUser, pi.Header, pi.ItemList, pi.Forum, sgItem, pi.Page, pi.LastPage}
|
|
err := templates.ExecuteTemplate(w, "socialgroups_view_group.html", sgpi)
|
|
if err != nil {
|
|
LogError(err)
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func socialgroupsTrowAssign(args ...interface{}) interface{} {
|
|
var forum = args[1].(*Forum)
|
|
if forum.ParentType == "socialgroup" {
|
|
var topicItem = args[0].(*TopicsRow)
|
|
topicItem.ForumLink = "/group/" + strings.TrimPrefix(topicItem.ForumLink, getForumURLPrefix())
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// TODO: It would be nice, if you could select one of the boards in the group from that drop-down rather than just the one you got linked from
|
|
func socialgroupsTopicCreatePreLoop(args ...interface{}) interface{} {
|
|
var fid = args[2].(int)
|
|
if fstore.DirtyGet(fid).ParentType == "socialgroup" {
|
|
var strictmode = args[5].(*bool)
|
|
*strictmode = true
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// TODO: Add privacy options
|
|
// TODO: Add support for multiple boards and add per-board simplified permissions
|
|
// TODO: Take isJs into account for routes which expect JSON responses
|
|
func socialgroupsForumCheck(args ...interface{}) (skip interface{}) {
|
|
var r = args[1].(*http.Request)
|
|
var fid = args[3].(*int)
|
|
var forum = fstore.DirtyGet(*fid)
|
|
|
|
if forum.ParentType == "socialgroup" {
|
|
var err error
|
|
var w = args[0].(http.ResponseWriter)
|
|
var success = args[4].(*bool)
|
|
sgItem, ok := r.Context().Value("socialgroups_current_group").(*SocialGroup)
|
|
if !ok {
|
|
sgItem, err = socialgroupsGetGroup(forum.ParentID)
|
|
if err != nil {
|
|
InternalError(errors.New("Unable to find the parent group for a forum"), w)
|
|
*success = false
|
|
return false
|
|
}
|
|
if !sgItem.Active {
|
|
NotFound(w, r)
|
|
*success = false
|
|
return false
|
|
}
|
|
r = r.WithContext(context.WithValue(r.Context(), "socialgroups_current_group", sgItem))
|
|
}
|
|
|
|
var user = args[2].(*User)
|
|
var rank int
|
|
var posts int
|
|
var joinedAt string
|
|
|
|
// TODO: Group privacy settings. For now, groups are all globally visible
|
|
|
|
// Clear the default group permissions
|
|
// TODO: Do this more efficiently, doing it quick and dirty for now to get this out quickly
|
|
overrideForumPerms(&user.Perms, false)
|
|
user.Perms.ViewTopic = true
|
|
|
|
err = socialgroupsGetMemberStmt.QueryRow(sgItem.ID, user.ID).Scan(&rank, &posts, &joinedAt)
|
|
if err != nil && err != ErrNoRows {
|
|
*success = false
|
|
InternalError(err, w)
|
|
return false
|
|
} else if err != nil {
|
|
return true
|
|
}
|
|
|
|
// TODO: Implement bans properly by adding the Local Ban API in the next commit
|
|
if rank < 0 {
|
|
return true
|
|
}
|
|
|
|
// Basic permissions for members, more complicated permissions coming in the next commit!
|
|
if sgItem.Owner == user.ID {
|
|
overrideForumPerms(&user.Perms, true)
|
|
} else if rank == 0 {
|
|
user.Perms.LikeItem = true
|
|
user.Perms.CreateTopic = true
|
|
user.Perms.CreateReply = true
|
|
} else {
|
|
overrideForumPerms(&user.Perms, true)
|
|
}
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// TODO: Override redirects? I don't think this is needed quite yet
|
|
|
|
func socialgroupsWidgets(args ...interface{}) interface{} {
|
|
var zone = args[0].(string)
|
|
var headerVars = args[2].(*HeaderVars)
|
|
var request = args[3].(*http.Request)
|
|
|
|
if zone != "view_forum" {
|
|
return false
|
|
}
|
|
|
|
var forum = args[1].(*Forum)
|
|
if forum.ParentType == "socialgroup" {
|
|
// This is why I hate using contexts, all the daisy chains and interface casts x.x
|
|
sgItem, ok := request.Context().Value("socialgroups_current_group").(*SocialGroup)
|
|
if !ok {
|
|
LogError(errors.New("Unable to find a parent group in the context data"))
|
|
return false
|
|
}
|
|
|
|
if headerVars.ExtData.items == nil {
|
|
headerVars.ExtData.items = make(map[string]interface{})
|
|
}
|
|
headerVars.ExtData.items["socialgroups_current_group"] = sgItem
|
|
|
|
return socialgroupsGroupWidgets(headerVars, sgItem)
|
|
}
|
|
return false
|
|
}
|