/* 解析完整html代码中charset属性 */ func HtmlMetaCharset(html string) string { html = strings.ToLower(html) top := beego.Substr(html, 0, 2000) //取出足够多的头部 start := strings.Index(top, "charset=") bs := []byte(top)[start+8:] out := string(bs) //截取charset后面的字符串 quoteStart := strings.Index(out, "\"") //找到第一个" ltStart := strings.Index(out, "<") //第一个< gtStart := strings.Index(out, ">") //第一个> eqStart := strings.Index(out, "=") //第一个= if quoteStart > -1 && quoteStart < ltStart && quoteStart < gtStart && quoteStart < eqStart { //"存在并且在< > =左边 return beego.Substr(out, 0, quoteStart) } return "" }
/* 获取分类部分文章内容 */ func (this *ArticleController) CategoryGetArticles() { category := &models.Category{} //获得页面数 page, _ := this.GetInt("page") if page < 1 { page = 1 } ok, _, _, articles, _, _ := category.FindByName(this.GetString("category"), int(page-1)*15, int(page)*15, 15) if !ok { //不存在此分类 this.StopRun() } results := make([]ArticleReturn, len(articles)) //查询每个文章的分类信息 for k, _ := range articles { category.FindOne(articles[k].Category) results[k].Art = articles[k] //查找第一个图片 results[k].Image = FindImages(results[k].Art.Content, 1)[0] //移除markdown标识 results[k].Art.Content = RemoveMarkdown(results[k].Art.Content) //截取长度200 if isMobile := IsMobile(this.Ctx.Input.UserAgent()); isMobile { results[k].Art.Content = beego.Substr(results[k].Art.Content, 0, 100) } else { results[k].Art.Content = beego.Substr(results[k].Art.Content, 0, 200) } results[k].CategoryName = category.Title results[k].CategoryPath = category.Name results[k].Time = results[k].Art.Time.Format("2006-01-02 15:04:05") } //查询每个文章作者信息和日期 member := &models.Member{} for k, _ := range articles { member.FindOne(articles[k].Uid.Hex()) results[k].AuthorName = member.Nickname } this.Data["json"] = results this.ServeJson() }
func genBackBtn(currentpath string) (out template.HTML) { if currentpath != "/" { subpath := beego.Substr(currentpath, 0, len(currentpath)-len(filepath.Base(currentpath))-1) return template.HTML("<a href=\"/index?path=" + subpath + "\" class=\"btn-sm btn-primary \">后退</a>") } return }
/* 获取游戏基本信息 */ func (this *GameController) GetInfo() { game := &models.Game{} ok, data := func() (bool, interface{}) { if !game.FindPath(this.GetString("game")) { return false, nil } //查询该游戏的下属分类 gameCategory := &models.GameCategory{} categorys := gameCategory.FindCategorys(game.Id) //遍历每个分类,查询前N个最新推送 news := make([][]*models.Topic, len(categorys)) topic := &models.Topic{} for k, _ := range categorys { // 推荐数量0则不查询 if categorys[k].Recommend == 0 { news[k] = nil continue } news[k] = topic.FindNew(game.Id, categorys[k].Id, categorys[k].Recommend) for key, _ := range news[k] { news[k][key].Content = beego.Substr(news[k][key].Content, 0, 200) } } return true, map[string]interface{}{ "Ok": true, "Game": game, //游戏信息 "News": news, //最新资讯(每个分类的) "Categorys": categorys, //分类信息 } }() this.Data["json"] = map[string]interface{}{ "ok": ok, "data": data, } this.ServeJson() }
// 获取列表 func (this *TagController) GetList() { ok, data := func() (bool, interface{}) { if len([]rune(this.GetString("name"))) > 30 { return false, "标签最大长度为30" } from, _ := this.GetInt("from") number, _ := this.GetInt("number") if number > 100 { return false, "最多显示100项" } //查找标签 topic := &models.Topic{} articles := topic.FindByTag(this.GetString("game"), this.GetString("tag"), from, number) //找出前3个图片并取内容前200个字符 images := make([][]string, len(articles)) for k, _ := range articles { //查询每个文章前3个图片 images[k] = FindImages(articles[k].Content, 3) //内容长度截取 articles[k].Content = beego.Substr(articles[k].Content, 0, 200) } //查询该分类文章总数 count := topic.FindTagCount(this.GetString("game"), this.GetString("tag")) return true, map[string]interface{}{ "articles": articles, "images": images, "count": count, } }() this.Data["json"] = map[string]interface{}{ "ok": ok, "data": data, } this.ServeJson() }
// 首页 func (this *MainController) Index() { this.Data["pageTitle"] = "系统概况" // 即将执行的任务 entries := jobs.GetEntries(30) jobList := make([]map[string]interface{}, len(entries)) for k, v := range entries { row := make(map[string]interface{}) job := v.Job.(*jobs.Job) row["task_id"] = job.GetId() row["task_name"] = job.GetName() row["next_time"] = beego.Date(v.Next, "Y-m-d H:i:s") jobList[k] = row } // 最近执行的日志 logs, _ := models.TaskLogGetList(1, 20) recentLogs := make([]map[string]interface{}, len(logs)) for k, v := range logs { task, err := models.TaskGetById(v.TaskId) taskName := "" if err == nil { taskName = task.TaskName } row := make(map[string]interface{}) row["task_name"] = taskName row["id"] = v.Id row["start_time"] = beego.Date(time.Unix(v.CreateTime, 0), "Y-m-d H:i:s") row["process_time"] = float64(v.ProcessTime) / 1000 row["ouput_size"] = libs.SizeFormat(float64(len(v.Output))) row["output"] = beego.Substr(v.Output, 0, 100) row["status"] = v.Status recentLogs[k] = row } this.Data["recentLogs"] = recentLogs this.Data["jobs"] = jobList this.Data["cpuNum"] = runtime.NumCPU() this.display() }
// 获取具有相同标签的文章 func (this *TagController) Same() { ok, data := func() (bool, interface{}) { // 查找文章 topic := &models.Topic{} if _ok := topic.FindOne(this.GetString("game"), this.GetString("id")); !_ok { return false, "文章不存在" } result := topic.Same() //内容截取长度20 for k, _ := range result { result[k].Content = beego.Substr(result[k].Content, 0, 20) } return true, result }() this.Data["json"] = map[string]interface{}{ "ok": ok, "data": data, } this.ServeJson() }
func QueryOpenId(accessToken string) (map[string]string, error) { var baseUrl = AuthConfig.String("openid_url") var fullRequestStr = baseUrl + "?access_token=" + accessToken resp, err := http.Get(fullRequestStr) if nil != err { beego.Error(err) return nil, err } result, err := ioutil.ReadAll(resp.Body) defer resp.Body.Close() if nil != err { beego.Error(err) return nil, err } content := string(result) if len(content) == 0 { beego.Error(err) return nil, err } beego.Debug("******************queryOpenID:before", content) start := strings.Index(content, "{") - 1 end := strings.LastIndex(content, "}") + 1 content = beego.Substr(content, start, end-start) beego.Debug("******************queryOpenID:after", content) var paramMap map[string]string err = json.Unmarshal([]byte(content), ¶mMap) return paramMap, err }
/* 发帖 回帖 回复 编辑请求-post */ func (this *GameController) AddTopic() { ok, data := func() (bool, interface{}) { if len([]rune(this.GetString("title"))) > 60 { return false, "标题最多60个字符" } if len([]rune(this.GetString("content"))) > 10000 { return false, "内容最多10000个字符" } //未登录 if this.GetSession("WOKUID") == nil { return false, "未登录" } //查询用户 member := &models.Member{} if ok := member.FindOne(this.GetSession("WOKUID").(string)); !ok { return false, "用户不存在" } //获取游戏信息 game := &models.Game{} if ok := game.FindPath(this.GetString("game")); !ok { return false, "该板块不存在" } //获取分类信息 gameCategory := &models.GameCategory{} if ok := gameCategory.Find(this.GetString("game"), this.GetString("category")); !ok { return false, "该分类不存在" } // 文档模式允许内容为空 if len([]rune(this.GetString("content"))) < 3 && gameCategory.Type != 1 { return false, "内容至少3个字符" } //3秒钟之内才能回帖 if time.Now().Sub(member.LastTime).Seconds() < 1 { return false, "发帖间隔1秒" } member.UpdateFinish() //回帖时间重置 switch this.GetString("type") { case "addTopic": //发帖 if gameCategory.Type == 1 && strings.Trim(this.GetString("title"), " ") == "" { return false, "标题不能为空" } //判断是否有权限 switch gameCategory.Add { case 0: //是否为管理员或版主 if member.Id != game.Manager && !inArray(member.Id.Hex(), game.Managers) { return false, "管理员或版主才能发帖" } break case 1: //登陆即可,不做限制 break } // 判断标签是否合法 tags := this.GetStrings("tag") // 标签数量限制、长度限制、同文章不能重复 if len(tags) >= 5 { return false, "最多5个标签" } for k, _ := range tags { if len([]rune(tags[k])) > 30 || len([]rune(tags[k])) < 1 { return false, "标签最大长度为30" } } copyTags := tags for k, _ := range tags { count := 0 for _k, _ := range copyTags { if copyTags[_k] == tags[k] { count++ if count >= 2 { return false, "标签不能重复" } } } } tag := &models.Tag{} // tag新增 for k, _ := range tags { tag.Upsert(game.Id, tags[k]) } // 实例化新增文章 topic := &models.Topic{} topic.SetId() //设置id // 判断分类类型 switch gameCategory.Type { case 0: // 论坛 case 1: // 文档 doc := &DocController{} isFolder, _ := this.GetBool("isFolder") // 新增文档 tempOk := false tempData := "" if tempOk, tempData = doc.Add(this.GetString("title"), isFolder, this.GetString("docId"), this.GetString("docParent"), gameCategory.Id.Hex(), topic.Id.Hex()); !tempOk { return false, tempData } // 如果是目录,不进行后续插入文章操作 if isFolder { return true, tempData } } var title string if this.GetString("title") != "" { title = this.GetString("title") } else { title = beego.Substr(this.GetString("content"), 0, 60) } // 文章赋值 topic.Game = this.GetString("game") topic.Title = title topic.Category = gameCategory.Id topic.Content = this.GetString("content") topic.Ip = this.Ctx.Input.IP() topic.Author = member.Id topic.AuthorName = member.Nickname topic.AuthorImage = member.Image topic.LastReply = topic.Author topic.Tag = tags // 查找内容中所有图片信息,并移动到正确位置,同时替换图片路径 var uploadSize int64 uploadSize, topic.Content = HandleNewImage(topic.Content, "game/"+game.Id+"/"+topic.Id.Hex()+"/") // 生成内容分词表 sego := &SegoController{} topic.ContentSego = sego.ToSlices(topic.Content, false) // 记录用户上传量 member.UploadSize += uploadSize member.Save() //插入数据库 objectId := topic.Insert() //TODO: 刷新缓存 //游戏活跃度+1 game.Hot++ //保存板块修改 game.SaveAll() return true, objectId.Hex() case "reply": //回复 //判断是否有权限 switch gameCategory.Add { case 0: //是否为管理员或版主 if member.Id != game.Manager && !inArray(member.Id.Hex(), game.Managers) { return false, "管理员或版主才能发帖" } break case 1: //登陆即可,不做限制 break } if len([]rune(this.GetString("content"))) > 5000 { return false, "回帖最多5000个字符" } //查找话题 topic := &models.Topic{} if ok := topic.FindById(this.GetString("topic")); !ok { //不存在此话题 return false, "回复的帖子不存在" } //回复数自增1,更新最后一个回复者的id topic.Reply++ topic.OutReply++ topic.LastReply = member.Id topic.LastReplyName = member.Nickname topic.LastTime = time.Now() topic.Save() //实例化回复 reply := &models.Reply{} reply.Game = this.GetString("game") reply.Topic = this.GetString("topic") reply.Content = this.GetString("content") reply.Author = member.Id reply.AuthorName = member.Nickname reply.AuthorImage = member.Image reply.Ip = this.Ctx.Input.IP() reply.SetId() // 查找内容中所有图片信息,并移动到正确位置,同时替换图片路径 var uploadSize int64 uploadSize, reply.Content = HandleNewImage(reply.Content, "game/"+game.Id+"/"+topic.Id.Hex()+"/"+reply.Id.Hex()+"/") // 记录用户上传量 member.UploadSize += uploadSize member.Save() //插入回复 reply.Insert() //推送消息,如果操作用户不是回复作者的话,通知他 if topic.Author != member.Id { //AddMessage(topic.Author.Hex(), "game", "/g/"+this.GetString("game")+"/"+this.GetString("topic")+".html?p="+this.GetString("page")+"#"+reply.Id.Hex(), "您的一个帖子有了新回复", "<img userImage='"+member.Image+"'> <span class='text-info'>"+member.Nickname+"</span> 回复了您的帖子:<span class='text-muted'> "+reply.Content+" </span>", "") } //游戏活跃度+1 game.Hot++ game.SaveAll() return true, reply.Id.Hex() case "rReply": //回复评论 //判断是否有权限 switch gameCategory.Add { case 0: //是否为管理员或版主 if member.Id != game.Manager && !inArray(member.Id.Hex(), game.Managers) { return false, "管理员或版主才能发帖" } break case 1: //登陆即可,不做限制 break } if len([]rune(this.GetString("content"))) > 500 { return false, "追加评论最多500个字符" } //查找话题 topic := &models.Topic{} if ok := topic.FindById(this.GetString("topic")); !ok { //不存在此话题 return false, "帖子不存在" } //查找回复 reply := &models.Reply{} if ok := reply.FindById(this.GetString("reply")); ok == false { //没有查找到回复则退出 return false, "回复不存在" } //实例化嵌套评论 newReply := &models.Reply{} newReply.Game = this.GetString("game") newReply.Topic = this.GetString("topic") newReply.Reply = reply.Id.Hex() newReply.Content = this.GetString("content") newReply.Author = member.Id newReply.AuthorName = member.Nickname newReply.AuthorImage = member.Image newReply.Ip = this.Ctx.Input.IP() newReply.SetId() //插入嵌套评论 newReply.Insert() //刷新前五个嵌套评论 reply.FreshCache() //回复的评论数量+1 reply.ReplyNumber++ reply.Save() //帖子回复数自增1,更新最后一个回复者的id topic.Reply++ topic.LastReply = member.Id topic.LastReplyName = member.Nickname topic.LastTime = time.Now() //保存 topic.Save() //TODO:刷新缓存 //推送消息,如果操作用户不是回复作者的话,通知他 if reply.Author != member.Id { //AddMessage(reply.Author.Hex(), "game", "/g/"+this.GetString("category")+"/"+this.GetString("topic")+"#"+newReply.Id.Hex(), "您的一个回复有了新评论", "<img userImage='"+member.Image+"'> <span class='text-info'>"+member.Nickname+"</span> 评论了您的回复:<span class='text-muted'> "+newReply.Content+" </span>", "") } return true, newReply.Id.Hex() case "edit": //编辑 //查询该话题 topic := &models.Topic{} topic.FindById(this.GetString("topic")) if topic == nil { //话题不存在 return false, "帖子不存在" } if member.Id != game.Manager && member.Id != topic.Author && !inArray(member.Id.Hex(), game.Managers) { //既不是管理组也不是作者 return false, "无权限" } // 删除编辑后删除的图片(必须包含前缀路径保证是此文章的) // 移动编辑后新增的图片(并返回更新路径后的内容) var uploadSize int64 uploadSize, topic.Content = HandleUpdateImage(topic.Content, this.GetString("content"), "game/"+game.Id+"/"+topic.Id.Hex()+"/") // 生成内容分词表 sego := &SegoController{} topic.ContentSego = sego.ToSlices(topic.Content, false) // 记录用户上传量 member.UploadSize += uploadSize member.Save() //保存 topic.Save() //TODO:刷新缓存 //推送消息,如果操作用户不是回复作者的话,通知他 if topic.Author != member.Id { //AddMessage(topic.Author.Hex(), "game", "/game/"+this.GetString("game")+"/"+this.GetString("topic")+".html", "您有一个帖子被管理员编辑", "<img userImage='"+member.Image+"'> <span class='text-info'>"+member.Nickname+"</span> 编辑了您的帖子:<span class='text-muted'> "+topic.Title+" </span>", "") } break } return true, "" }() this.Data["json"] = map[string]interface{}{ "ok": ok, "data": data, } this.ServeJson() }
//文件上传 func (this *base) upload(key string) (files []*models.UploadFile, err error) { //处理上传文件 var header *multipart.FileHeader var file multipart.File var f *os.File //根据年月选择文件夹 t := time.Now().Format(time.RFC3339) //文件夹是否存在或创建文件夹 UploadPath := beego.AppConfig.String("UploadPath") folder := utils.MergePath(UploadPath) err = utils.GetDir(folder) if err != nil { return } // //用户文件夹是否存在或创建文件夹 // folder = filepath.Join(folder, strconv.Itoa(int(this.xm.Updator))) // err = utils.GetDir(folder) // if err != nil { // return // } //文件夹是否存在或创建文件夹 UploadPath = path.Join(UploadPath, beego.Substr(t, 0, 7)) folder = path.Join(folder, beego.Substr(t, 0, 7)) err = utils.GetDir(folder) if err != nil { return } fs := this.Ctx.Request.MultipartForm.File[key] n := len(fs) if n == 0 { err = errors.New("files not found") return } for i := 0; i < n; i++ { header = fs[i] file, err = fs[i].Open() if err != nil { return } defer file.Close() //提取原始文件信息 disposition := strings.Split(header.Header.Get("Content-Disposition"), ";") var key, value string for _, v := range disposition { pos := strings.Index(v, "=") if pos > -1 { key = v[:pos] if strings.TrimSpace(key) == "filename" { value = strings.Replace(v[pos+1:], "\"", "", -1) break } } } // filename := filepath.Base(value) //新建文件 UploadPath = path.Join("/", UploadPath, fmt.Sprintf("%d%s", time.Now().UnixNano(), filepath.Ext(filename))) f, err = os.OpenFile(path.Join(folder, fmt.Sprintf("%d%s", time.Now().UnixNano(), filepath.Ext(filename))), os.O_WRONLY|os.O_CREATE, 0666) if err != nil { return } defer f.Close() io.Copy(f, file) upf := new(models.UploadFile) upf.Name = filename upf.Ext = filepath.Ext(filename) upf.Path = UploadPath fi, _ := f.Stat() upf.Size = fi.Size() files = append(files, upf) } return }
/* 分类显示页面 */ func (this *ArticleController) Category() { //是否用手机访问 isMobile := IsMobile(this.Ctx.Input.UserAgent()) //验证 valid := validation.Validation{} valid.Alpha(this.Ctx.Input.Param(":category"), "category") if valid.HasErrors() { //没有通过验证则返回首页 this.Ctx.Redirect(302, this.UrlFor("IndexController.Index")) this.StopRun() } //获得页面数 page, _ := this.GetInt("page") if page < 1 { page = 1 } category := &models.Category{} ok, parent, childs, articles, hots, allPage := category.FindByName(this.Ctx.Input.Param(":category"), (page-1)*15, page*15, 15) if !ok { //不存在此分类 this.Ctx.Redirect(302, this.UrlFor("IndexController.Index")) this.StopRun() } //保存分类信息 mainCategory := *category results := make([]ArticleReturn, len(articles)) //查询每个文章的分类信息 for k, _ := range articles { category.FindOne(articles[k].Category) results[k].Art = articles[k] //查找第一个图片 results[k].Image = FindImages(results[k].Art.Content, 1)[0] //移除markdown标识 results[k].Art.Content = RemoveMarkdown(results[k].Art.Content) //截取长度200 results[k].Art.Content = beego.Substr(results[k].Art.Content, 0, 200) results[k].CategoryName = category.Title results[k].CategoryPath = category.Name results[k].Time = results[k].Art.Time.Format("2006-01-02 15:04:05") } //查询每个文章作者信息 member := &models.Member{} for k, _ := range articles { member.FindOne(articles[k].Uid.Hex()) results[k].AuthorName = member.Nickname } //赋值模版变量 this.Data["category"] = mainCategory this.Data["parent"] = parent this.Data["childs"] = childs this.Data["articles"] = results this.Data["hots"] = hots this.Data["allpage"] = allPage this.Data["page"] = page //当前页数 if isMobile { //如果是手机访问 this.TplNames = "mobile/article/category.html" } else { this.TplNames = "article/category.html" } this.Render() }
//新增文章 func (this *ArticleController) Add() { ok, data := func() (bool, interface{}) { if this.GetSession("ID") == nil { return false, "未登录" } //查询用户 member := &models.Member{} if _ok, _data := member.FindOne(this.GetSession("ID").(string)); !_ok { return false, _data } category, _ := this.GetInt("category") if category != 0 && category != 1 { return false, "分类错误" } if len([]rune(this.GetString("title"))) > 60 { return false, "标题最长60" } if len([]rune(this.GetString("content"))) < 3 || len([]rune(this.GetString("content"))) > 20000 { return false, "内容长度3-20000" } // 判断标签是否合法 tags := this.GetStrings("tag") // 标签数量限制、长度限制、同文章不能重复 if len(tags) >= 5 { return false, "最多5个标签" } for k, _ := range tags { if len([]rune(tags[k])) > 15 || len([]rune(tags[k])) < 1 { return false, "标签最大长度为15" } } copyTags := tags for k, _ := range tags { count := 0 for _k, _ := range copyTags { if copyTags[_k] == tags[k] { count++ if count >= 2 { return false, "标签不能重复" } } } } tag := &models.Tag{} // tag新增 for k, _ := range tags { tag.Upsert(tags[k]) } article := &models.Article{} article.Uid = bson.ObjectIdHex(this.GetSession("ID").(string)) article.Category = category article.Title = this.GetString("title") article.Content = this.GetString("content") article.Tag = tags article.GetId() fmt.Println(article.Id) //如果标题为空,截取内容前15个字符 if article.Title == "" { article.Title = beego.Substr(article.Content, 0, 15) } // 查找内容中所有图片信息,并移动到正确位置,同时替换图片路径 var uploadSize int64 uploadSize, article.Content = HandleNewImage(article.Content, "article/"+article.Id.Hex()+"/") // 记录用户上传量 member.UploadSize += uploadSize member.Save() //用户发表量+1 member.AddArticleNumber() //插入文章 return article.Insert() }() this.Data["json"] = map[string]interface{}{ "ok": ok, "data": data, } this.ServeJson() }