Example #1
0
func SaveAuthority(form url.Values, opUser string) (errMsg string, err error) {
	authority := model.NewAuthority()
	err = util.ConvertAssign(authority, form)
	if err != nil {
		logger.Errorln("authority ConvertAssign error", err)
		errMsg = err.Error()
		return
	}

	authority.OpUser = opUser

	if authority.Aid != 0 {
		err = authority.Persist(authority)
	} else {
		authority.Ctime = util.TimeNow()

		_, err = authority.Insert()
	}

	if err != nil {
		errMsg = "内部服务器错误"
		logger.Errorln(errMsg, ":", err)
		return
	}

	global.AuthorityChan <- struct{}{}

	return
}
Example #2
0
// 发表评论(或回复)。
// objid 注册的评论对象
// uid 评论人
func PostComment(uid, objid int, form url.Values) (*model.Comment, error) {
	comment := model.NewComment()
	comment.Objid = objid
	objtype := util.MustInt(form.Get("objtype"))
	comment.Objtype = objtype
	comment.Uid = uid
	comment.Content = form.Get("content")

	// TODO:评论楼层怎么处理,避免冲突?最后的楼层信息保存在内存中?

	// 暂时只是从数据库中取出最后的评论楼层
	stringBuilder := util.NewBuffer()
	stringBuilder.Append("objid=").AppendInt(objid).Append(" AND objtype=").AppendInt(objtype)
	tmpCmt, err := model.NewComment().Where(stringBuilder.String()).Order("ctime DESC").Find()
	if err != nil {
		logger.Errorln("post comment service error:", err)
		return nil, err
	} else {
		comment.Floor = tmpCmt.Floor + 1
	}
	// 入评论库
	cid, err := comment.Insert()
	if err != nil {
		logger.Errorln("post comment service error:", err)
		return nil, err
	}
	comment.Cid = cid
	comment.Ctime = util.TimeNow()
	decodeCmtContent(comment)

	// 回调,不关心处理结果(有些对象可能不需要回调)
	if commenter, ok := commenters[objtype]; ok {
		logger.Debugf("评论[objid:%d] [objtype:%d] [uid:%d] 成功,通知被评论者更新", objid, objtype, uid)
		go commenter.UpdateComment(cid, objid, uid, time.Now().Format("2006-01-02 15:04:05"))
	}

	// 发评论,活跃度+5
	go IncUserWeight("uid="+strconv.Itoa(uid), 5)

	// 给被评论对象所有者发系统消息
	ext := map[string]interface{}{
		"objid":   objid,
		"objtype": objtype,
		"cid":     cid,
		"uid":     uid,
	}
	go SendSystemMsgTo(0, objtype, ext)

	// @某人 发系统消息
	go SendSysMsgAtUids(form.Get("uid"), ext)
	go SendSysMsgAtUsernames(form.Get("usernames"), ext)

	return comment, nil
}
Example #3
0
func SaveRole(form url.Values, opUser string) (errMsg string, err error) {

	role := model.NewRole()
	role.Name = form.Get("name")
	role.OpUser = opUser

	roleid := form.Get("roleid")
	isNew := roleid == ""
	if isNew {
		role.Ctime = util.TimeNow()

		_, err = role.Insert()
	} else {
		role.Roleid, err = strconv.Atoi(roleid)
		if err != nil {
			errMsg = "roleid invalid"
			logger.Errorln(errMsg, ":", err)
			return
		}
		err = role.Persist(role)
	}

	if err != nil {
		errMsg = "内部服务器错误"
		logger.Errorln(errMsg, ":", err)
		return
	}

	roleAuth := model.NewRoleAuthority()
	if !isNew {
		// 如果是更新角色,将之前的角色权限都删除
		roleAuth.Where("roleid=" + strconv.Itoa(role.Roleid)).Delete()
	}

	roleAuth.Roleid = role.Roleid
	roleAuth.OpUser = opUser

	// 增加角色拥有的权限
	for _, aid := range form["authorities[]"] {
		aid, err := strconv.Atoi(aid)
		if err != nil {
			continue
		}
		roleAuth.Aid = aid

		roleAuth.Insert()
	}

	global.RoleChan <- struct{}{}
	global.RoleAuthChan <- struct{}{}

	return
}
Example #4
0
func CreateUser(form url.Values) (errMsg string, err error) {
	if EmailExists(form.Get("email")) {
		err = errors.New("该邮箱已注册过")
		return
	}
	if UsernameExists(form.Get("username")) {
		err = errors.New("用户名已存在")
		return
	}
	// 存用户基本信息,产生自增长UID
	user := model.NewUser()
	err = util.ConvertAssign(user, form)
	if err != nil {
		logger.Errorln("user ConvertAssign error", err)
		errMsg = err.Error()
		return
	}
	user.Ctime = util.TimeNow()

	// 随机给一个默认头像
	user.Avatar = DefaultAvatars[rand.Intn(len(DefaultAvatars))]
	uid, err := user.Insert()
	if err != nil {
		errMsg = "内部服务器错误"
		logger.Errorln(errMsg, ":", err)
		return
	}

	// 存用户登录信息
	userLogin := model.NewUserLogin()
	err = util.ConvertAssign(userLogin, form)
	if err != nil {
		errMsg = err.Error()
		logger.Errorln("CreateUser error:", err)
		return
	}
	userLogin.Uid = uid
	_, err = userLogin.Insert()
	if err != nil {
		errMsg = "内部服务器错误"
		logger.Errorln(errMsg, ":", err)
		return
	}

	// 存用户角色信息
	userRole := model.NewUserRole()
	// 默认为初级会员
	userRole.Roleid = Roles[len(Roles)-1].Roleid
	userRole.Uid = uid
	if _, err = userRole.Insert(); err != nil {
		logger.Errorln("userRole insert Error:", err)
	}

	// 存用户活跃信息,初始活跃+2
	userActive := model.NewUserActive()
	userActive.Uid = uid
	userActive.Username = user.Username
	userActive.Avatar = user.Avatar
	userActive.Email = user.Email
	userActive.Weight = 2
	if _, err = userActive.Insert(); err != nil {
		logger.Errorln("UserActive insert Error:", err)
	}
	return
}
Example #5
0
// 处理 Reddit 中的一条资源
func dealRedditOneResource(contentSelection *goquery.Selection) error {
	aSelection := contentSelection.Find(".title a.title")

	title := aSelection.Text()
	if title == "" {
		return errors.New("title is empty")
	}

	resourceUrl, ok := aSelection.Attr("href")
	if !ok || resourceUrl == "" {
		return errors.New("resource url is empty")
	}

	isReddit := false

	resource := model.NewResource()
	// Reddit 自身的内容
	if contentSelection.HasClass("self") {
		isReddit = true
		resourceUrl = Reddit + resourceUrl
	}

	err := resource.Where("url=?", resourceUrl).Find("id")
	// 已经存在
	if resource.Id != 0 {
		// 如果是 reddit 本身的,可以更新评论信息
		if !isReddit {
			return errors.New("url" + resourceUrl + "has exists!")
		}
	}

	if isReddit {

		resource.Form = model.ContentForm

		var doc *goquery.Document

		if doc, err = goquery.NewDocument(resourceUrl); err != nil {
			return errors.New("goquery reddit.com/r/golang self newdocument error:" + err.Error())
		}

		content, err := doc.Find("#siteTable .usertext .md").Html()
		if err != nil {
			return err
		}

		doc.Find(".commentarea .comment .usertext .md").Each(func(i int, contentSel *goquery.Selection) {
			if i == 0 {
				content += `<hr/>**评论:**<br/><br/>`
			}

			comment, err := contentSel.Html()
			if err != nil {
				return
			}

			comment = strings.TrimSpace(comment)
			comment = resourceRe.ReplaceAllLiteralString(comment, "\n")

			author := contentSel.ParentsFiltered(".usertext").Prev().Find(".author").Text()
			content += author + ": <pre>" + comment + "</pre>"
		})

		if strings.TrimSpace(content) == "" {
			return errors.New("goquery reddit.com/r/golang self newdocument(" + resourceUrl + ") error: content is empty")
		}

		resource.Content = content

		// reddit 本身的,当做其他资源
		resource.Catid = 4
	} else {
		resource.Form = model.LinkForm

		// Github,是开源项目
		if contentSelection.Find(".title .domain a").Text() == "github.com" {
			resource.Catid = 2
		} else {
			resource.Catid = 1
		}
	}

	resource.Title = title
	resource.Url = resourceUrl
	resource.Uid = PresetUids[rand.Intn(4)]

	ctime := util.TimeNow()
	datetime, ok := contentSelection.Find(".tagline time").Attr("datetime")
	if ok {
		dtime, err := time.ParseInLocation(time.RFC3339, datetime, time.UTC)
		if err != nil {
			logger.Errorln("parse ctime error:", err)
		} else {
			ctime = dtime.Local().Format("2006-01-02 15:04:05")
		}
	}
	resource.Ctime = ctime

	if resource.Id == 0 {
		var id int64
		id, err = resource.Insert()

		if err != nil {
			return errors.New("insert into Resource error:" + err.Error())
		}

		// 存扩展信息
		resourceEx := model.NewResourceEx()
		resourceEx.Id = int(id)
		if _, err = resourceEx.Insert(); err != nil {
			return errors.New("insert into ResourceEx error:" + err.Error())
		}
	} else {
		if err = resource.Persist(resource); err != nil {
			return errors.New("persist resource:" + strconv.Itoa(resource.Id) + " error:" + err.Error())
		}
	}

	return nil
}
Example #6
0
// 获取url对应的文章并根据规则进行解析
func ParseArticle(articleUrl string, auto bool) (*model.Article, error) {
	articleUrl = strings.TrimSpace(articleUrl)
	if !strings.HasPrefix(articleUrl, "http") {
		articleUrl = "http://" + articleUrl
	}

	tmpArticle := model.NewArticle()
	err := tmpArticle.Where("url=" + articleUrl).Find("id")
	if err != nil || tmpArticle.Id != 0 {
		logger.Errorln(articleUrl, "has exists:", err)
		return nil, errors.New("has exists!")
	}

	urlPaths := strings.SplitN(articleUrl, "/", 5)
	domain := urlPaths[2]

	for k, v := range domainPatch {
		if strings.Contains(domain, k) && !strings.Contains(domain, "www."+k) {
			domain = v
			break
		}
	}

	rule := model.NewCrawlRule()
	err = rule.Where("domain=" + domain).Find()
	if err != nil {
		logger.Errorln("find rule by domain error:", err)
		return nil, err
	}

	if rule.Id == 0 {
		logger.Errorln("domain:", domain, "not exists!")
		return nil, errors.New("domain not exists")
	}

	var doc *goquery.Document
	if doc, err = goquery.NewDocument(articleUrl); err != nil {
		logger.Errorln("goquery newdocument error:", err)
		return nil, err
	}

	author, authorTxt := "", ""
	if rule.InUrl {
		index, err := strconv.Atoi(rule.Author)
		if err != nil {
			logger.Errorln("author rule is illegal:", rule.Author, "error:", err)
			return nil, err
		}
		author = urlPaths[index]
		authorTxt = author
	} else {
		if strings.HasPrefix(rule.Author, ".") || strings.HasPrefix(rule.Author, "#") {
			authorSelection := doc.Find(rule.Author)
			author, err = authorSelection.Html()
			if err != nil {
				logger.Errorln("goquery parse author error:", err)
				return nil, err
			}

			author = strings.TrimSpace(author)
			authorTxt = strings.TrimSpace(authorSelection.Text())
		} else {
			// 某些个人博客,页面中没有作者的信息,因此,规则中 author 即为 作者
			author = rule.Author
			authorTxt = rule.Author
		}
	}

	title := ""
	doc.Find(rule.Title).Each(func(i int, selection *goquery.Selection) {
		if title != "" {
			return
		}

		tmpTitle := strings.TrimSpace(strings.TrimPrefix(selection.Text(), "原"))
		tmpTitle = strings.TrimSpace(strings.TrimPrefix(tmpTitle, "荐"))
		tmpTitle = strings.TrimSpace(strings.TrimPrefix(tmpTitle, "转"))
		tmpTitle = strings.TrimSpace(strings.TrimPrefix(tmpTitle, "顶"))
		if tmpTitle != "" {
			title = tmpTitle
		}
	})

	if title == "" {
		logger.Errorln("url:", articleUrl, "parse title error:", err)
		return nil, err
	}

	replacer := strings.NewReplacer("[置顶]", "", "[原]", "", "[转]", "")
	title = strings.TrimSpace(replacer.Replace(title))

	contentSelection := doc.Find(rule.Content)

	// relative url -> abs url
	contentSelection.Find("img").Each(func(i int, s *goquery.Selection) {
		if v, ok := s.Attr("src"); ok {
			if !strings.HasPrefix(v, "http") {
				s.SetAttr("src", domain+v)
			}
		}
	})

	content, err := contentSelection.Html()
	if err != nil {
		logger.Errorln("goquery parse content error:", err)
		return nil, err
	}
	content = strings.TrimSpace(content)
	txt := strings.TrimSpace(contentSelection.Text())
	txt = articleRe.ReplaceAllLiteralString(txt, " ")
	txt = articleSpaceRe.ReplaceAllLiteralString(txt, " ")

	// 自动抓取,内容长度不能少于 300 字
	if auto && len(txt) < 300 {
		logger.Infoln(articleUrl, "content is short")
		return nil, errors.New("content is short")
	}

	pubDate := util.TimeNow()
	if rule.PubDate != "" {
		pubDate = strings.TrimSpace(doc.Find(rule.PubDate).First().Text())

		// sochina patch
		re := regexp.MustCompile("[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}")
		submatches := re.FindStringSubmatch(pubDate)
		if len(submatches) > 0 {
			pubDate = submatches[0]
		}
	}

	if pubDate == "" {
		pubDate = util.TimeNow()
	}

	article := model.NewArticle()
	article.Domain = domain
	article.Name = rule.Name
	article.Author = author
	article.AuthorTxt = authorTxt
	article.Title = title
	article.Content = content
	article.Txt = txt
	article.PubDate = pubDate
	article.Url = articleUrl
	article.Lang = rule.Lang
	article.Ctime = util.TimeNow()

	_, err = article.Insert()
	if err != nil {
		logger.Errorln("insert article error:", err)
		return nil, err
	}

	return article, nil
}
Example #7
0
// 增加(修改)资源
func PublishResource(user map[string]interface{}, form url.Values) (err error) {
	uid := user["uid"].(int)

	resource := model.NewResource()

	if form.Get("id") != "" {
		err = resource.Where("id=?", form.Get("id")).Find()
		if err != nil {
			logger.Errorln("Publish Resource find error:", err)
			return
		}

		isAdmin := false
		if _, ok := user["isadmin"]; ok {
			isAdmin = user["isadmin"].(bool)
		}
		if resource.Uid != uid && !isAdmin {
			err = NotModifyAuthorityErr
			return
		}

		fields := []string{"title", "catid", "form", "url", "content"}
		if form.Get("form") == model.LinkForm {
			form.Set("content", "")
		} else {
			form.Set("url", "")
		}

		id := form.Get("id")
		query, args := updateSetClause(form, fields)
		err = resource.Set(query, args...).Where("id=?", id).Update()
		if err != nil {
			logger.Errorf("更新資源 【%s】 信息失败:%s\n", id, err)
			return
		}

		// 修改資源,活跃度+2
		go IncUserWeight("uid="+strconv.Itoa(uid), 2)
	} else {

		util.ConvertAssign(resource, form)

		resource.Uid = uid
		resource.Ctime = util.TimeNow()

		var id int64
		id, err = resource.Insert()

		if err != nil {
			logger.Errorln("Publish Resource error:", err)
			return
		}

		// 存扩展信息
		resourceEx := model.NewResourceEx()
		resourceEx.Id = int(id)
		if _, err = resourceEx.Insert(); err != nil {
			logger.Errorln("PublishResource Ex error:", err)
			return
		}

		// 给 被@用户 发系统消息
		/*
			ext := map[string]interface{}{
				"objid":   id,
				"objtype": model.TYPE_RESOURCE,
				"uid":     user["uid"],
				"msgtype": model.MsgtypePublishAtMe,
			}
			go SendSysMsgAtUsernames(form.Get("usernames"), ext)
		*/

		// 发布主题,活跃度+10
		go IncUserWeight("uid="+strconv.Itoa(uid), 10)
	}

	return
}
Example #8
0
func PublishProject(user map[string]interface{}, form url.Values) (err error) {
	id := form.Get("id")
	isModify := id != ""

	if !isModify && ProjectUriExists(form.Get("uri")) {
		err = errors.New("uri存在")
		return
	}

	username := user["username"].(string)

	project := model.NewOpenProject()

	if isModify {
		err = project.Where("id=?", id).Find()
		if err != nil {
			logger.Errorln("Publish Project find error:", err)
			return
		}
		isAdmin := false
		if _, ok := user["isadmin"]; ok {
			isAdmin = user["isadmin"].(bool)
		}
		if project.Username != username && !isAdmin {
			err = NotModifyAuthorityErr
			return
		}

		util.ConvertAssign(project, form)
	} else {
		util.ConvertAssign(project, form)

		project.Username = username
		project.Ctime = util.TimeNow()
	}

	project.Uri = strings.ToLower(project.Uri)

	github := "github.com"
	pos := strings.Index(project.Src, github)
	if pos != -1 {
		project.Repo = project.Src[pos+len(github)+1:]
	}

	if !isModify {
		_, err = project.Insert()
	} else {
		err = project.Persist(project)
	}

	if err != nil {
		logger.Errorln("Publish Project error:", err)
	}

	// 发布項目,活跃度+10
	if uid, ok := user["uid"].(int); ok {
		weight := 10
		if isModify {
			weight = 2
		}
		go IncUserWeight("uid="+strconv.Itoa(uid), weight)
	}

	return
}
Example #9
0
// ParseOneProject 处理单个 project
func ParseOneProject(projectUrl string) error {
	if !strings.HasPrefix(projectUrl, "http") {
		projectUrl = OsChinaDomain + projectUrl
	}

	var (
		doc *goquery.Document
		err error
	)

	// 加上 ?fromerr=xfwefs,否则页面有 js 重定向
	if doc, err = goquery.NewDocument(projectUrl + "?fromerr=xfwefs"); err != nil {
		return errors.New("goquery fetch " + projectUrl + " error:" + err.Error())
	}

	// 标题
	category := strings.TrimSpace(doc.Find(".Project .name").Text())
	name := strings.TrimSpace(doc.Find(".Project .name u").Text())
	if category == "" && name == "" {
		return errors.New("projectUrl:" + projectUrl + " category and name are empty")
	}

	tmpIndex := strings.LastIndex(category, name)
	if tmpIndex != -1 {
		category = category[:tmpIndex]
	}

	// uri
	uri := projectUrl[strings.LastIndex(projectUrl, "/")+1:]

	project := model.NewOpenProject()

	err = project.Where("uri=?", uri).Find("id")
	// 已经存在
	if project.Id != 0 {
		return errors.New("url" + projectUrl + "has exists!")
	}

	logoSelection := doc.Find(".Project .PN img")
	if logoSelection.AttrOr("title", "") != "" {
		project.Logo = logoSelection.AttrOr("src", "")

		if !strings.HasPrefix(project.Logo, "http") {
			project.Logo = OsChinaDomain + project.Logo
		}

		project.Logo, err = UploadUrlFile(project.Logo, ProjectLogoPrefix)
		if err != nil {
			logger.Errorln("project logo upload error:", err)
		}
	}

	// 获取项目相关链接
	doc.Find("#Body .urls li").Each(func(i int, liSelection *goquery.Selection) {
		aSelection := liSelection.Find("a")
		uri := util.FetchRealUrl(OsChinaDomain + aSelection.AttrOr("href", ""))
		switch aSelection.Text() {
		case "软件首页":
			project.Home = uri
		case "软件文档":
			project.Doc = uri
		case "软件下载":
			project.Download = uri
		}
	})

	ctime := util.TimeNow()

	doc.Find("#Body .attrs li").Each(func(i int, liSelection *goquery.Selection) {
		aSelection := liSelection.Find("a")
		txt := aSelection.Text()
		if i == 0 {
			project.Licence = txt
			if txt == "未知" {
				project.Licence = "其他"
			}
		} else if i == 1 {
			project.Lang = txt
		} else if i == 2 {
			project.Os = txt
		} else if i == 3 {
			dtime, err := time.ParseInLocation("2006年01月02日", aSelection.Last().Text(), time.Local)
			if err != nil {
				logger.Errorln("parse ctime error:", err)
			} else {
				ctime = dtime.Local().Format("2006-01-02 15:04:05")
			}
		}
	})

	project.Name = name
	project.Category = category
	project.Uri = uri
	project.Repo = strings.TrimSpace(doc.Find("#Body .github-widget").AttrOr("data-repo", ""))
	project.Src = "https://github.com/" + project.Repo

	pos := strings.Index(project.Repo, "/")
	if pos > -1 {
		project.Author = project.Repo[:pos]
	} else {
		project.Author = "网友"
	}

	if project.Doc == "" {
		// TODO:暂时认为一定是 Go 语言
		project.Doc = "https://godoc.org/" + project.Src[8:]
	}

	desc := ""
	doc.Find("#Body .detail").Find("p").NextAll().Each(func(i int, domSelection *goquery.Selection) {
		doc.FindSelection(domSelection).WrapHtml(`<div id="tmp` + strconv.Itoa(i) + `"></div>`)
		domHtml, _ := doc.Find("#tmp" + strconv.Itoa(i)).Html()
		if domSelection.Is("pre") {
			desc += domHtml + "\n\n"
		} else {
			desc += html2md.Convert(domHtml) + "\n\n"
		}
	})

	project.Desc = strings.TrimSpace(desc)
	project.Username = PresetUsernames[rand.Intn(4)]
	project.Status = model.ProjectStatusOnline
	project.Ctime = ctime

	_, err = project.Insert()
	if err != nil {
		return errors.New("insert into open project error:" + err.Error())
	}

	return nil
}
Example #10
0
// 发布主题。入topics和topics_ex库
func PublishTopic(user map[string]interface{}, form url.Values) (err error) {
	uid := user["uid"].(int)

	topic := model.NewTopic()

	if form.Get("tid") != "" {
		err = topic.Where("tid=?", form.Get("tid")).Find()
		if err != nil {
			logger.Errorln("Publish Topic find error:", err)
			return
		}

		isAdmin := false
		if _, ok := user["isadmin"]; ok {
			isAdmin = user["isadmin"].(bool)
		}
		if topic.Uid != uid && !isAdmin {
			err = NotModifyAuthorityErr
			return
		}

		_, err = ModifyTopic(user, form)
		if err != nil {
			logger.Errorln("Publish Topic error:", err)
			return
		}
	} else {

		util.ConvertAssign(topic, form)

		topic.Uid = uid
		topic.Ctime = util.TimeNow()

		var tid int
		tid, err = topic.Insert()

		if err != nil {
			logger.Errorln("Publish Topic error:", err)
			return
		}

		// 存扩展信息
		topicEx := model.NewTopicEx()
		topicEx.Tid = tid
		_, err = topicEx.Insert()
		if err != nil {
			logger.Errorln("Insert TopicEx error:", err)
			return
		}

		// 给 被@用户 发系统消息
		ext := map[string]interface{}{
			"objid":   tid,
			"objtype": model.TYPE_TOPIC,
			"uid":     user["uid"],
			"msgtype": model.MsgtypePublishAtMe,
		}
		go SendSysMsgAtUsernames(form.Get("usernames"), ext)

		// 发布主题,活跃度+10
		go IncUserWeight("uid="+strconv.Itoa(uid), 10)
	}

	return
}