added more add topic code and some documentation
This commit is contained in:
+1
-1
@@ -291,7 +291,7 @@ func NewTopic(ctxt ui.AmContext) (string, any, error) {
|
|||||||
lines, _ := checker.Lines()
|
lines, _ := checker.Lines()
|
||||||
|
|
||||||
// Add the topic!
|
// Add the topic!
|
||||||
topic, err := database.AmNewTopic(conf, ctxt.CurrentUser(), topicName, zeroPostPseud, zeroPost, int32(lines))
|
topic, err := database.AmNewTopic(conf, ctxt.CurrentUser(), topicName, zeroPostPseud, zeroPost, int32(lines), ctxt.RemoteIP())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ui.ErrorPage(ctxt, err)
|
return ui.ErrorPage(ctxt, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,6 +58,23 @@ const (
|
|||||||
AuditCommunityJoinKey = 210
|
AuditCommunityJoinKey = 210
|
||||||
AuditCommunitySecurity = 211
|
AuditCommunitySecurity = 211
|
||||||
AuditCommunityDelete = 212
|
AuditCommunityDelete = 212
|
||||||
|
AuditConferenceCreate = 301
|
||||||
|
AuditConferenceSecurity = 302
|
||||||
|
AuditConferenceName = 303
|
||||||
|
AuditConferenceAlias = 304
|
||||||
|
AuditConferenceMembership = 305
|
||||||
|
AuditConferenceCreateTopic = 306
|
||||||
|
AuditConferenceDeleteTopic = 307
|
||||||
|
AudotConferenceFreezeTopic = 308
|
||||||
|
AuditConferenceArchiveTopic = 309
|
||||||
|
AuditConferencePostMessage = 310
|
||||||
|
AuditConferenceHideMessage = 311
|
||||||
|
AuditConferenceScribbleMessage = 312
|
||||||
|
AuditConferenceNukeMessage = 313
|
||||||
|
AuditConferenceUploadAttachment = 314
|
||||||
|
AuditConferenceDelete = 315
|
||||||
|
AuditConferenceMoveMessage = 316
|
||||||
|
AuditConferenceStickyTopic = 317
|
||||||
)
|
)
|
||||||
|
|
||||||
// auditWriteQueue is a channel to store audit records in the background.
|
// auditWriteQueue is a channel to store audit records in the background.
|
||||||
|
|||||||
+20
-20
@@ -21,29 +21,29 @@ import (
|
|||||||
// Conference struct is the top-level structure for a conference.
|
// Conference struct is the top-level structure for a conference.
|
||||||
type Conference struct {
|
type Conference struct {
|
||||||
Mutex sync.Mutex
|
Mutex sync.Mutex
|
||||||
ConfId int32 `db:"confid"`
|
ConfId int32 `db:"confid"` // unique conference ID
|
||||||
CreateDate time.Time `db:"createdate"`
|
CreateDate time.Time `db:"createdate"` // date of creation
|
||||||
LastUpdate *time.Time `db:"lastupdate"`
|
LastUpdate *time.Time `db:"lastupdate"` // date of last update
|
||||||
ReadLevel uint16 `db:"read_lvl"`
|
ReadLevel uint16 `db:"read_lvl"` // level required to read
|
||||||
PostLevel uint16 `db:"post_lvl"`
|
PostLevel uint16 `db:"post_lvl"` // level required to post
|
||||||
CreateLevel uint16 `db:"create_lvl"`
|
CreateLevel uint16 `db:"create_lvl"` // level required to create topics
|
||||||
HideLevel uint16 `db:"hide_lvl"`
|
HideLevel uint16 `db:"hide_lvl"` // level required to hide posts
|
||||||
NukeLevel uint16 `db:"nuke_lvl"`
|
NukeLevel uint16 `db:"nuke_lvl"` // level required to nuke posts
|
||||||
ChangeLevel uint16 `db:"change_lvl"`
|
ChangeLevel uint16 `db:"change_lvl"` // level required to change conference
|
||||||
DeleteLevel uint16 `db:"delete_lvl"`
|
DeleteLevel uint16 `db:"delete_lvl"` // level required to delete conference
|
||||||
TopTopic int16 `db:"top_topic"`
|
TopTopic int16 `db:"top_topic"` // highest topic number in use
|
||||||
Name string `db:"name"`
|
Name string `db:"name"` // conference name
|
||||||
Description *string `db:"descr"`
|
Description *string `db:"descr"` // conference description
|
||||||
IconUrl *string `db:"icon_url"`
|
IconUrl *string `db:"icon_url"` // conference icon URL
|
||||||
Color *string `db:"color"`
|
Color *string `db:"color"` // color for conference
|
||||||
}
|
}
|
||||||
|
|
||||||
type ConferenceSettings struct {
|
type ConferenceSettings struct {
|
||||||
ConfId int32 `db:"confid"`
|
ConfId int32 `db:"confid"` // conference ID
|
||||||
Uid int32 `db:"uid"`
|
Uid int32 `db:"uid"` // user ID
|
||||||
DefaultPseud *string `db:"default_pseud"`
|
DefaultPseud *string `db:"default_pseud"` // default pseud to use in this conference
|
||||||
LastRead *time.Time `db:"last_read"`
|
LastRead *time.Time `db:"last_read"` // last read time
|
||||||
LastPost *time.Time `db:"last_post"`
|
LastPost *time.Time `db:"last_post"` // last post time
|
||||||
}
|
}
|
||||||
|
|
||||||
// conferenceCache is the cache for Conference objects.
|
// conferenceCache is the cache for Conference objects.
|
||||||
|
|||||||
+51
-42
@@ -18,28 +18,28 @@ import (
|
|||||||
|
|
||||||
// Topic is the top-level structure detailing topics.
|
// Topic is the top-level structure detailing topics.
|
||||||
type Topic struct {
|
type Topic struct {
|
||||||
TopicId int32 `db:"topicid"`
|
TopicId int32 `db:"topicid"` // unique ID of the topic
|
||||||
ConfId int32 `db:"confid"`
|
ConfId int32 `db:"confid"` // conference this topic is in
|
||||||
Number int16 `db:"num"`
|
Number int16 `db:"num"` // topic number
|
||||||
CreatorUid int32 `db:"creator_uid"`
|
CreatorUid int32 `db:"creator_uid"` // UID of topic creator
|
||||||
TopMessage int32 `db:"top_message"`
|
TopMessage int32 `db:"top_message"` // highest message number in topic
|
||||||
Frozen bool `db:"frozen"`
|
Frozen bool `db:"frozen"` // frozen topic
|
||||||
Archived bool `db:"archived"`
|
Archived bool `db:"archived"` // archived topic
|
||||||
Sticky bool `db:"sticky"`
|
Sticky bool `db:"sticky"` // sticky topic
|
||||||
CreateDate time.Time `db:"createdate"`
|
CreateDate time.Time `db:"createdate"` // creation date
|
||||||
LastUpdate time.Time `db:"lastupdate"`
|
LastUpdate time.Time `db:"lastupdate"` // last update date
|
||||||
Name string `db:"name"`
|
Name string `db:"name"` // topic name
|
||||||
}
|
}
|
||||||
|
|
||||||
// TopicSettings contains per-user settings for topics, including the "last read" message pointer.
|
// TopicSettings contains per-user settings for topics, including the "last read" message pointer.
|
||||||
type TopicSettings struct {
|
type TopicSettings struct {
|
||||||
TopicId int32 `db:"topicid"`
|
TopicId int32 `db:"topicid"` // unique ID of the topic
|
||||||
Uid int32 `db:"uid"`
|
Uid int32 `db:"uid"` // UID of the user
|
||||||
Hidden bool `db:"hidden"`
|
Hidden bool `db:"hidden"` // has user hidden topic?
|
||||||
LastMessage int32 `db:"last_message"`
|
LastMessage int32 `db:"last_message"` // last message read
|
||||||
LastRead *time.Time `db:"last_read"`
|
LastRead *time.Time `db:"last_read"` // time of last read
|
||||||
LastPost *time.Time `db:"last_post"`
|
LastPost *time.Time `db:"last_post"` // time of last post
|
||||||
Subscribe bool `db:"subscribe"`
|
Subscribe bool `db:"subscribe"` // subscribed to topic updates?
|
||||||
}
|
}
|
||||||
|
|
||||||
// TopicSummary is a smaller data structure that gets topic information to create the topic list display.
|
// TopicSummary is a smaller data structure that gets topic information to create the topic list display.
|
||||||
@@ -53,6 +53,7 @@ type TopicSummary struct {
|
|||||||
Frozen bool
|
Frozen bool
|
||||||
Archived bool
|
Archived bool
|
||||||
Subscribed bool
|
Subscribed bool
|
||||||
|
Hidden bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func AmGetTopic(topicId int32) (*Topic, error) {
|
func AmGetTopic(topicId int32) (*Topic, error) {
|
||||||
@@ -72,19 +73,19 @@ func AmGetTopic(topicId int32) (*Topic, error) {
|
|||||||
|
|
||||||
// View and sort constants for AmListTopics.
|
// View and sort constants for AmListTopics.
|
||||||
const (
|
const (
|
||||||
TopicViewAll = 0
|
TopicViewAll = 0 // list all topics
|
||||||
TopicViewNew = 1
|
TopicViewNew = 1 // list only visible topics with new messages
|
||||||
TopicViewActive = 2
|
TopicViewActive = 2 // list only visible topics, active first
|
||||||
TopicViewAllVisible = 3
|
TopicViewAllVisible = 3 // list only visible topics
|
||||||
TopicViewHidden = 4
|
TopicViewHidden = 4 // list only hidden topics
|
||||||
TopicViewArchive = 5
|
TopicViewArchive = 5 // list only archived, non-hidden topics
|
||||||
|
|
||||||
TopicSortID = 0
|
TopicSortID = 0 // sort by topic ID
|
||||||
TopicSortNumber = 1
|
TopicSortNumber = 1 // sort by topic number
|
||||||
TopicSortName = 2
|
TopicSortName = 2 // sort by name
|
||||||
TopicSortUnread = 3
|
TopicSortUnread = 3 // sort by number of unread messages
|
||||||
TopicSortTotal = 4
|
TopicSortTotal = 4 // sort by total number of messages
|
||||||
TopicSortDate = 5
|
TopicSortDate = 5 // sort by date of last update
|
||||||
)
|
)
|
||||||
|
|
||||||
/* AmListTopics produces a list of topic summary information according to specific options.
|
/* AmListTopics produces a list of topic summary information according to specific options.
|
||||||
@@ -121,13 +122,13 @@ func AmListTopics(confid int32, uid int32, viewOption int, sortOption int, ignor
|
|||||||
if !ignoreSticky {
|
if !ignoreSticky {
|
||||||
tail = "(t.sticky = 1 OR " + tail + ")"
|
tail = "(t.sticky = 1 OR " + tail + ")"
|
||||||
}
|
}
|
||||||
whereClause = "t.archived = 0 AND IFNULL(s.hidden,0) = 0 AND " + tail
|
whereClause = "t.archived = 0 AND hidden = 0 AND " + tail
|
||||||
case TopicViewActive, TopicViewAllVisible:
|
case TopicViewActive, TopicViewAllVisible:
|
||||||
whereClause = "t.archived = 0 AND IFNULL(s.hidden,0) = 0"
|
whereClause = "t.archived = 0 AND hidden = 0"
|
||||||
case TopicViewHidden:
|
case TopicViewHidden:
|
||||||
whereClause = "IFNULL(s.hidden,0) = 1"
|
whereClause = "hidden = 1"
|
||||||
case TopicViewArchive:
|
case TopicViewArchive:
|
||||||
whereClause = "t.archived = 1 AND IFNULL(s.hidden,0) = 0"
|
whereClause = "t.archived = 1 AND hidden = 0"
|
||||||
default:
|
default:
|
||||||
return nil, errors.New("invalid view option specified")
|
return nil, errors.New("invalid view option specified")
|
||||||
}
|
}
|
||||||
@@ -180,14 +181,14 @@ func AmListTopics(confid int32, uid int32, viewOption int, sortOption int, ignor
|
|||||||
var fullStatement strings.Builder
|
var fullStatement strings.Builder
|
||||||
fullStatement.WriteString("SELECT t.topicid, t.num, t.name, (t.top_message - IFNULL(s.last_message,-1)) AS unread, ")
|
fullStatement.WriteString("SELECT t.topicid, t.num, t.name, (t.top_message - IFNULL(s.last_message,-1)) AS unread, ")
|
||||||
fullStatement.WriteString("(t.top_message + 1) AS total, t.lastupdate, t.frozen, t.archived, IFNULL(s.subscribe,0) AS subscribe, ")
|
fullStatement.WriteString("(t.top_message + 1) AS total, t.lastupdate, t.frozen, t.archived, IFNULL(s.subscribe,0) AS subscribe, ")
|
||||||
fullStatement.WriteString("t.sticky, GREATEST(SIGN(t.top_message - IFNULL(s.last_message,-1)),0) AS newflag ")
|
fullStatement.WriteString("IFNULL(s.hidden,0) AS hidden, t.sticky, GREATEST(SIGN(t.top_message - IFNULL(s.last_message,-1)),0) AS newflag ")
|
||||||
fullStatement.WriteString("FROM topics t LEFT JOIN topicsettings s ON t.topicid = s.topicid AND s.uid = ? WHERE t.confid = ? ")
|
fullStatement.WriteString("FROM topics t LEFT JOIN topicsettings s ON t.topicid = s.topicid AND s.uid = ? WHERE t.confid = ? ")
|
||||||
if whereClause != "" {
|
if whereClause != "" {
|
||||||
fullStatement.WriteString("AND ")
|
fullStatement.WriteString("AND ")
|
||||||
fullStatement.WriteString(whereClause)
|
fullStatement.WriteString(whereClause)
|
||||||
}
|
}
|
||||||
fullStatement.WriteString(" ORDER BY ")
|
fullStatement.WriteString(" ORDER BY ")
|
||||||
if ignoreSticky {
|
if !ignoreSticky {
|
||||||
fullStatement.WriteString("t.sticky DESC, ")
|
fullStatement.WriteString("t.sticky DESC, ")
|
||||||
}
|
}
|
||||||
if viewOption == TopicViewActive {
|
if viewOption == TopicViewActive {
|
||||||
@@ -204,13 +205,19 @@ func AmListTopics(confid int32, uid int32, viewOption int, sortOption int, ignor
|
|||||||
for rs.Next() {
|
for rs.Next() {
|
||||||
var rec TopicSummary
|
var rec TopicSummary
|
||||||
rs.Scan(&rec.TopicID, &rec.Number, &rec.Name, &rec.Unread, &rec.Total, &rec.LastUpdate, &rec.Frozen, &rec.Archived,
|
rs.Scan(&rec.TopicID, &rec.Number, &rec.Name, &rec.Unread, &rec.Total, &rec.LastUpdate, &rec.Frozen, &rec.Archived,
|
||||||
&rec.Subscribed)
|
&rec.Subscribed, &rec.Hidden)
|
||||||
rc = append(rc, &rec)
|
rc = append(rc, &rec)
|
||||||
}
|
}
|
||||||
return rc, nil
|
return rc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func AmNewTopic(conf *Conference, user *User, title string, zeroPostPseud string, zeroPost string, zeroPostLines int32) (*Topic, error) {
|
func AmNewTopic(conf *Conference, user *User, title string, zeroPostPseud string, zeroPost string,
|
||||||
|
zeroPostLines int32, ipaddr string) (*Topic, error) {
|
||||||
|
var ar *AuditRecord = nil
|
||||||
|
defer func() {
|
||||||
|
AmStoreAudit(ar)
|
||||||
|
}()
|
||||||
|
|
||||||
unlock := true
|
unlock := true
|
||||||
amdb.Exec("LOCK TABLES confs WRITE, topics WRITE, topicsettings WRITE, posts WRITE, postdata WRITE;")
|
amdb.Exec("LOCK TABLES confs WRITE, topics WRITE, topicsettings WRITE, posts WRITE, postdata WRITE;")
|
||||||
defer func() {
|
defer func() {
|
||||||
@@ -227,11 +234,13 @@ func AmNewTopic(conf *Conference, user *User, title string, zeroPostPseud string
|
|||||||
conf.Mutex.Unlock()
|
conf.Mutex.Unlock()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
// Retrieve the ID of the new topic.
|
||||||
xid, err := rs.LastInsertId()
|
xid, err := rs.LastInsertId()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
conf.Mutex.Unlock()
|
conf.Mutex.Unlock()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
// Get the topic.
|
||||||
topic, err := AmGetTopic(int32(xid))
|
topic, err := AmGetTopic(int32(xid))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
conf.Mutex.Unlock()
|
conf.Mutex.Unlock()
|
||||||
@@ -258,10 +267,8 @@ func AmNewTopic(conf *Conference, user *User, title string, zeroPostPseud string
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
newPostId := int32(xid)
|
|
||||||
|
|
||||||
// Add the post data.
|
// Add the post data.
|
||||||
_, err = amdb.Exec("INSERT INTO postdata (postid, data) VALUES (?, ?)", newPostId, zeroPost)
|
_, err = amdb.Exec("INSERT INTO postdata (postid, data) VALUES (?, ?)", int32(xid), zeroPost)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -276,7 +283,9 @@ func AmNewTopic(conf *Conference, user *User, title string, zeroPostPseud string
|
|||||||
amdb.Exec("UNLOCK TABLES;")
|
amdb.Exec("UNLOCK TABLES;")
|
||||||
unlock = false
|
unlock = false
|
||||||
|
|
||||||
// TODO: audit record
|
// create audit record
|
||||||
|
ar = AmNewAudit(AuditConferenceCreateTopic, user.Uid, ipaddr, fmt.Sprintf("confid=%d", conf.ConfId),
|
||||||
|
fmt.Sprintf("num=%d", topic.Number), fmt.Sprintf("name=%s", topic.Name))
|
||||||
|
|
||||||
return topic, nil
|
return topic, nil
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-19
@@ -30,9 +30,9 @@ import (
|
|||||||
|
|
||||||
// UserPrefs represents the user's preferences in a table (one row per user).
|
// UserPrefs represents the user's preferences in a table (one row per user).
|
||||||
type UserPrefs struct {
|
type UserPrefs struct {
|
||||||
Uid int32 `db:"uid"`
|
Uid int32 `db:"uid"` // user ID
|
||||||
TimeZoneID string `db:"tzid"`
|
TimeZoneID string `db:"tzid"` // ID of default timezone
|
||||||
LocaleID string `db:"localeid"`
|
LocaleID string `db:"localeid"` // ID of default locale
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadLocale reads the locale out of the prefs, adjusting for Go use.
|
// ReadLocale reads the locale out of the prefs, adjusting for Go use.
|
||||||
@@ -100,22 +100,22 @@ func (p *UserPrefs) Location() *time.Location {
|
|||||||
// User represents a user in the Amsterdam database.
|
// User represents a user in the Amsterdam database.
|
||||||
type User struct {
|
type User struct {
|
||||||
Mutex sync.RWMutex
|
Mutex sync.RWMutex
|
||||||
Uid int32 `db:"uid"`
|
Uid int32 `db:"uid"` // unique ID of user
|
||||||
Username string `db:"username"`
|
Username string `db:"username"` // user name
|
||||||
Passhash string `db:"passhash"`
|
Passhash string `db:"passhash"` // password hash
|
||||||
Tokenauth *string `db:"tokenauth"`
|
Tokenauth *string `db:"tokenauth"` // token authorization information
|
||||||
ContactID int32 `db:"contactid"`
|
ContactID int32 `db:"contactid"` // contact information ID
|
||||||
IsAnon bool `db:"is_anon"`
|
IsAnon bool `db:"is_anon"` // is this the anonymous user?
|
||||||
VerifyEMail bool `db:"verify_email"`
|
VerifyEMail bool `db:"verify_email"` // is E-mail address verified?
|
||||||
Lockout bool `db:"lockout"`
|
Lockout bool `db:"lockout"` // is this user locked out?
|
||||||
AccessTries int16 `db:"access_tries"`
|
AccessTries int16 `db:"access_tries"` // how many timews has the user tried to access?
|
||||||
EmailConfNum int32 `db:"email_confnum"`
|
EmailConfNum int32 `db:"email_confnum"` // E-mail confirmation number
|
||||||
BaseLevel uint16 `db:"base_lvl"`
|
BaseLevel uint16 `db:"base_lvl"` // base access level of the user
|
||||||
Created time.Time `db:"created"`
|
Created time.Time `db:"created"` // account creation time
|
||||||
LastAccess *time.Time `db:"lastaccess"`
|
LastAccess *time.Time `db:"lastaccess"` // last access (login) time
|
||||||
PassReminder string `db:"passreminder"`
|
PassReminder string `db:"passreminder"` // last update time
|
||||||
Description *string `db:"description"`
|
Description *string `db:"description"` // description
|
||||||
DOB *time.Time `db:"dob"`
|
DOB *time.Time `db:"dob"` // date of birth
|
||||||
flags *util.OptionSet
|
flags *util.OptionSet
|
||||||
prefs *UserPrefs
|
prefs *UserPrefs
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user