代码拉取完成,页面将自动刷新
/*
主题
*/
package gopher
import (
"fmt"
"html/template"
"net/http"
"sort"
"time"
"github.com/gorilla/mux"
"github.com/jimmykuu/gt-go-sdk"
"github.com/jimmykuu/wtforms"
"gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson"
)
// 用于测试
var testParam func() = func() {}
type City struct {
Name string `bson:"_id"`
MemberCount int `bson:"count"`
}
type ByCount []City
func (a ByCount) Len() int { return len(a) }
func (a ByCount) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a ByCount) Less(i, j int) bool { return a[i].MemberCount > a[j].MemberCount }
func topicsHandler(handler *Handler, conditions bson.M, sortBy string, url string, subActive string) {
page, err := getPage(handler.Request)
if err != nil {
message(handler, "页码错误", "页码错误", "error")
return
}
var nodes []Node
c := handler.DB.C(NODES)
c.Find(bson.M{"topiccount": bson.M{"$gt": 0}}).Sort("-topiccount").All(&nodes)
var status Status
c = handler.DB.C(STATUS)
c.Find(nil).One(&status)
c = handler.DB.C(CONTENTS)
var topTopics []Topic
if page == 1 {
c.Find(bson.M{"is_top": true}).Sort(sortBy).All(&topTopics)
var objectIds []bson.ObjectId
for _, topic := range topTopics {
objectIds = append(objectIds, topic.Id_)
}
if len(topTopics) > 0 {
conditions["_id"] = bson.M{"$not": bson.M{"$in": objectIds}}
}
}
pagination := NewPagination(c.Find(conditions).Sort(sortBy), url, PerPage)
var topics []Topic
query, err := pagination.Page(page)
if err != nil {
message(handler, "页码错误", "页码错误", "error")
return
}
query.(*mgo.Query).All(&topics)
var linkExchanges []LinkExchange
c = handler.DB.C(LINK_EXCHANGES)
c.Find(bson.M{"is_on_home": true}).All(&linkExchanges)
topics = append(topTopics, topics...)
c = handler.DB.C(USERS)
var cities []City
c.Pipe([]bson.M{bson.M{
"$group": bson.M{
"_id": "$location",
"count": bson.M{"$sum": 1},
},
}}).All(&cities)
sort.Sort(ByCount(cities))
var hotCities []City
count := 0
for _, city := range cities {
if city.Name != "" {
hotCities = append(hotCities, city)
count += 1
}
if count == 10 {
break
}
}
handler.renderTemplate("index.html", BASE, map[string]interface{}{
"nodes": nodes,
"cities": hotCities,
"status": status,
"topics": topics,
"linkExchanges": linkExchanges,
"pagination": pagination,
"page": page,
"active": "topic",
"subActive": subActive,
})
}
// URL: /
// 网站首页,列出按回帖时间倒序排列的第一页
func indexHandler(handler *Handler) {
topicsHandler(handler, bson.M{"content.type": TypeTopic}, "-latestrepliedat", "/", "latestReply")
}
// URL: /topics/latest
// 最新发布的主题,按照发布时间倒序排列
func latestTopicsHandler(handler *Handler) {
topicsHandler(handler, bson.M{"content.type": TypeTopic}, "-content.createdat", "/topics/latest", "latestCreate")
}
// URL: /topics/no_reply
// 无人回复的主题,按照发布时间倒序排列
func noReplyTopicsHandler(handler *Handler) {
topicsHandler(handler, bson.M{"content.type": TypeTopic, "content.commentcount": 0}, "-content.createdat", "/topics/no_reply", "noReply")
}
// URL: /topic/new
// 新建主题
func newTopicHandler(handler *Handler) {
user, _ := currentUser(handler)
if user.IsBlocked {
handler.Redirect("/user/blocked")
return
}
nodeID := mux.Vars(handler.Request)["node"]
var nodes []Node
c := handler.DB.C(NODES)
c.Find(nil).All(&nodes)
var choices = []wtforms.Choice{wtforms.Choice{}} // 第一个选项为空
for _, node := range nodes {
choices = append(choices, wtforms.Choice{Value: node.Id_.Hex(), Label: node.Name})
}
geeTest := geetest.NewGeeTest(Config.GtCaptchaId, Config.GtPrivateKey)
form := wtforms.NewForm(
wtforms.NewSelectField("node", "节点", choices, nodeID, &wtforms.Required{}),
wtforms.NewTextArea("title", "标题", "", &wtforms.Required{}),
wtforms.NewTextArea("editormd-markdown-doc", "内容", ""),
wtforms.NewTextArea("editormd-html-code", "HTML", ""),
wtforms.NewTextField("geetest_challenge", "challenge", ""),
wtforms.NewTextField("geetest_validate", "validate", ""),
wtforms.NewTextField("geetest_seccode", "seccode", ""),
)
if handler.Request.Method == "POST" {
if form.Validate(handler.Request) {
if !geeTest.Validate(form.Value("geetest_challenge"), form.Value("geetest_validate"), form.Value("geetest_seccode")) {
handler.renderTemplate("topic/form.html", BASE, map[string]interface{}{
"form": form,
"title": "新建",
"gtUrl": geeTest.EmbedURL(),
"captchaErr": true,
})
return
}
now := time.Now()
c = handler.DB.C(CONTENTS)
// 查找最新的一篇帖子,限制发帖间隔
var latestTopic Topic
err := c.Find(bson.M{"content.createdby": user.Id_}).Sort("-content.createdat").Limit(1).One(&latestTopic)
if err == nil {
fmt.Println("lastTopic", latestTopic.Content.CreatedAt)
if !latestTopic.Content.CreatedAt.Add(time.Minute * 30).Before(now) {
// 半小时内只能发一帖
message(handler, "发表主题过于频繁", "发表主题过于频繁,不能发布该主题", "error")
return
}
}
id_ := bson.NewObjectId()
nodeID := bson.ObjectIdHex(form.Value("node"))
err = c.Insert(&Topic{
Content: Content{
Id_: id_,
Type: TypeTopic,
Title: form.Value("title"),
Markdown: form.Value("editormd-markdown-doc"),
Html: template.HTML(form.Value("editormd-html-code")),
CreatedBy: user.Id_,
CreatedAt: now,
},
Id_: id_,
NodeId: nodeID,
LatestRepliedAt: now,
})
if err != nil {
fmt.Println("newTopicHandler:", err.Error())
return
}
// 增加Node.TopicCount
c = handler.DB.C(NODES)
c.Update(bson.M{"_id": nodeID}, bson.M{"$inc": bson.M{"topiccount": 1}})
c = handler.DB.C(STATUS)
c.Update(nil, bson.M{"$inc": bson.M{"topiccount": 1}})
http.Redirect(handler.ResponseWriter, handler.Request, "/t/"+id_.Hex(), http.StatusFound)
return
}
}
handler.renderTemplate("topic/form.html", BASE, map[string]interface{}{
"form": form,
"title": "新建",
"action": "/topic/new",
"active": "topic",
"gtUrl": geeTest.EmbedURL(),
})
}
// URL: /t/{topicId}/edit
// 编辑主题
func editTopicHandler(handler *Handler) {
user, _ := currentUser(handler)
topicId := bson.ObjectIdHex(mux.Vars(handler.Request)["topicId"])
c := handler.DB.C(CONTENTS)
var topic Topic
err := c.Find(bson.M{"_id": topicId, "content.type": TypeTopic}).One(&topic)
if err != nil {
message(handler, "没有该主题", "没有该主题,不能编辑", "error")
return
}
if !topic.CanEdit(user.Username, handler.DB) {
message(handler, "没有该权限", "对不起,你没有权限编辑该主题", "error")
return
}
var nodes []Node
c = handler.DB.C(NODES)
c.Find(nil).All(&nodes)
var choices = []wtforms.Choice{wtforms.Choice{}} // 第一个选项为空
for _, node := range nodes {
choices = append(choices, wtforms.Choice{Value: node.Id_.Hex(), Label: node.Name})
}
geeTest := geetest.NewGeeTest(Config.GtCaptchaId, Config.GtPrivateKey)
form := wtforms.NewForm(
wtforms.NewSelectField("node", "节点", choices, topic.NodeId.Hex(), &wtforms.Required{}),
wtforms.NewTextArea("title", "标题", topic.Title, &wtforms.Required{}),
wtforms.NewTextArea("editormd-markdown-doc", "内容", topic.Markdown),
wtforms.NewTextArea("editormd-html-code", "html", ""),
wtforms.NewTextField("geetest_challenge", "challenge", ""),
wtforms.NewTextField("geetest_validate", "validate", ""),
wtforms.NewTextField("geetest_seccode", "seccode", ""),
)
if handler.Request.Method == "POST" {
if form.Validate(handler.Request) {
if !geeTest.Validate(form.Value("geetest_challenge"), form.Value("geetest_validate"), form.Value("geetest_seccode")) {
handler.renderTemplate("topic/form.html", BASE, map[string]interface{}{
"form": form,
"gtUrl": geeTest.EmbedURL(),
"captchaErr": true,
})
return
}
nodeId := bson.ObjectIdHex(form.Value("node"))
c = handler.DB.C(CONTENTS)
c.Update(bson.M{"_id": topic.Id_}, bson.M{"$set": bson.M{
"nodeid": nodeId,
"content.title": form.Value("title"),
"content.markdown": form.Value("editormd-markdown-doc"),
"content.html": template.HTML(form.Value("editormd-html-code")),
"content.updatedat": time.Now(),
"content.updatedby": user.Id_.Hex(),
}})
// 如果两次的节点不同,更新节点的主题数量
if topic.NodeId != nodeId {
c = handler.DB.C(NODES)
c.Update(bson.M{"_id": topic.NodeId}, bson.M{"$inc": bson.M{"topiccount": -1}})
c.Update(bson.M{"_id": nodeId}, bson.M{"$inc": bson.M{"topiccount": 1}})
}
http.Redirect(handler.ResponseWriter, handler.Request, "/t/"+topic.Id_.Hex(), http.StatusFound)
return
}
}
handler.renderTemplate("topic/form.html", BASE, map[string]interface{}{
"form": form,
"title": "编辑",
"action": "/t/" + topicId + "/edit",
"active": "topic",
"gtUrl": geeTest.EmbedURL(),
})
}
// URL: /t/{topicId}
// 根据主题的ID,显示主题的信息及回复
func showTopicHandler(handler *Handler) {
testParam()
vars := mux.Vars(handler.Request)
topicId := vars["topicId"]
c := handler.DB.C(CONTENTS)
cusers := handler.DB.C(USERS)
topic := Topic{}
if !bson.IsObjectIdHex(topicId) {
http.NotFound(handler.ResponseWriter, handler.Request)
return
}
err := c.Find(bson.M{"_id": bson.ObjectIdHex(topicId), "content.type": TypeTopic}).One(&topic)
if err != nil {
logger.Println(err)
http.NotFound(handler.ResponseWriter, handler.Request)
return
}
c.UpdateId(bson.ObjectIdHex(topicId), bson.M{"$inc": bson.M{"content.hits": 1}})
user, has := currentUser(handler)
//去除新消息的提醒
if has {
replies := user.RecentReplies
ats := user.RecentAts
pos := -1
for k, v := range replies {
if v.ContentId == topicId {
pos = k
break
}
}
//数组的删除不是这么删的,早知如此就应该存链表了
if pos != -1 {
if pos == len(replies)-1 {
replies = replies[:pos]
} else {
replies = append(replies[:pos], replies[pos+1:]...)
}
cusers.Update(bson.M{"_id": user.Id_}, bson.M{"$set": bson.M{"recentreplies": replies}})
}
pos = -1
for k, v := range ats {
if v.ContentId == topicId {
pos = k
break
}
}
if pos != -1 {
if pos == len(ats)-1 {
ats = ats[:pos]
} else {
ats = append(ats[:pos], ats[pos+1:]...)
}
cusers.Update(bson.M{"_id": user.Id_}, bson.M{"$set": bson.M{"recentats": ats}})
}
}
handler.renderTemplate("topic/show.html", BASE, map[string]interface{}{
"topic": topic,
"active": "topic",
})
}
// URL: /go/{node}
// 列出节点下所有的主题
func topicInNodeHandler(handler *Handler) {
vars := mux.Vars(handler.Request)
nodeId := vars["node"]
c := handler.DB.C(NODES)
node := Node{}
err := c.Find(bson.M{"id": nodeId}).One(&node)
if err != nil {
message(handler, "没有此节点", "请联系管理员创建此节点", "error")
return
}
page, err := getPage(handler.Request)
if err != nil {
message(handler, "页码错误", "页码错误", "error")
return
}
c = handler.DB.C(CONTENTS)
pagination := NewPagination(c.Find(bson.M{"nodeid": node.Id_, "content.type": TypeTopic}).Sort("-latestrepliedat"), "/", 20)
var topics []Topic
query, err := pagination.Page(page)
if err != nil {
message(handler, "没有找到页面", "没有找到页面", "error")
return
}
query.(*mgo.Query).All(&topics)
handler.renderTemplate("/topic/list.html", BASE, map[string]interface{}{
"topics": topics,
"node": node,
"active": "topic",
})
}
// URL: /t/{topicId}/collect/
// 将主题收藏至当前用户的收藏夹
func collectTopicHandler(handler *Handler) {
vars := mux.Vars(handler.Request)
topicId := vars["topicId"]
t := time.Now()
user, _ := currentUser(handler)
for _, v := range user.TopicsCollected {
if v.TopicId == topicId {
return
}
}
user.TopicsCollected = append(user.TopicsCollected, CollectTopic{topicId, t})
c := handler.DB.C(USERS)
c.UpdateId(user.Id_, bson.M{"$set": bson.M{"topicscollected": user.TopicsCollected}})
http.Redirect(handler.ResponseWriter, handler.Request, "/member/"+user.Username+"/collect?p=1", http.StatusFound)
}
// URL: /t/{topicId}/delete
// 删除主题
func deleteTopicHandler(handler *Handler) {
vars := mux.Vars(handler.Request)
topicId := bson.ObjectIdHex(vars["topicId"])
c := handler.DB.C(CONTENTS)
topic := Topic{}
err := c.Find(bson.M{"_id": topicId, "content.type": TypeTopic}).One(&topic)
if err != nil {
fmt.Println("deleteTopic:", err.Error())
return
}
// Node统计数减一
c = handler.DB.C(NODES)
c.Update(bson.M{"_id": topic.NodeId}, bson.M{"$inc": bson.M{"topiccount": -1}})
c = handler.DB.C(STATUS)
// 统计的主题数减一,减去统计的回复数减去该主题的回复数
c.Update(nil, bson.M{"$inc": bson.M{"topiccount": -1, "replycount": -topic.CommentCount}})
//删除评论
c = handler.DB.C(COMMENTS)
if topic.CommentCount > 0 {
c.Remove(bson.M{"contentid": topic.Id_})
}
// 删除Topic记录
c = handler.DB.C(CONTENTS)
c.Remove(bson.M{"_id": topic.Id_})
http.Redirect(handler.ResponseWriter, handler.Request, "/", http.StatusFound)
}
// 列出置顶的主题
func listTopTopicsHandler(handler *Handler) {
var topics []Topic
c := handler.DB.C(CONTENTS)
c.Find(bson.M{"content.type": TypeTopic, "is_top": true}).All(&topics)
handler.renderTemplate("/topic/top_list.html", ADMIN, map[string]interface{}{
"topics": topics,
})
}
// 设置置顶
func setTopTopicHandler(handler *Handler) {
topicId := bson.ObjectIdHex(mux.Vars(handler.Request)["id"])
c := handler.DB.C(CONTENTS)
c.Update(bson.M{"_id": topicId}, bson.M{"$set": bson.M{"is_top": true}})
handler.Redirect("/t/" + topicId.Hex())
}
// 取消置顶
func cancelTopTopicHandler(handler *Handler) {
vars := mux.Vars(handler.Request)
topicId := bson.ObjectIdHex(vars["id"])
c := handler.DB.C(CONTENTS)
c.Update(bson.M{"_id": topicId}, bson.M{"$set": bson.M{"is_top": false}})
handler.Redirect("/admin/top/topics")
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。