// 新建主题
// 复制默认主题到文件夹下
func (this *ThemeService) CopyDefaultTheme(userBlog info.UserBlog) (ok bool, themeId string) {
	newThemeId := bson.NewObjectId()
	themeId = newThemeId.Hex()
	userId := userBlog.UserId.Hex()
	themePath := this.getUserThemePath(userId, themeId)
	err := os.MkdirAll(themePath, 0755)
	if err != nil {
		return
	}
	// 复制默认主题
	defaultThemePath := this.getDefaultThemePath(userBlog.Style)
	err = CopyDir(defaultThemePath, themePath)
	if err != nil {
		return
	}

	// 保存到数据库中
	theme, _ := this.getThemeConfig(themePath)
	theme.ThemeId = newThemeId
	theme.Path = this.getUserThemePath2(userId, themeId)
	theme.CreatedTime = time.Now()
	theme.UpdatedTime = theme.CreatedTime
	theme.UserId = bson.ObjectIdHex(userId)

	ok = db.Insert(db.Themes, theme)
	return ok, themeId
}
// 添加或更新标签, 先查下是否存在, 不存在则添加, 存在则更新
// 都要统计下tag的note数
// 什么时候调用? 笔记添加Tag, 删除Tag时
// 删除note时, 都可以调用
// 万能
func (this *TagService) AddOrUpdateTag(userId string, tag string) info.NoteTag {
	userIdO := bson.ObjectIdHex(userId)
	noteTag := info.NoteTag{}
	db.GetByQ(db.NoteTags, bson.M{"UserId": userIdO, "Tag": tag}, &noteTag)

	// 存在, 则更新之
	if noteTag.TagId != "" {
		// 统计note数
		count := noteService.CountNoteByTag(userId, tag)
		noteTag.Count = count
		noteTag.UpdatedTime = time.Now()
		//		noteTag.Usn = userService.IncrUsn(userId), 更新count而已
		db.UpdateByIdAndUserId(db.NoteTags, noteTag.TagId.Hex(), userId, noteTag)
		return noteTag
	}

	// 不存在, 则创建之
	noteTag.TagId = bson.NewObjectId()
	noteTag.Count = 1
	noteTag.Tag = tag
	noteTag.UserId = bson.ObjectIdHex(userId)
	noteTag.CreatedTime = time.Now()
	noteTag.UpdatedTime = noteTag.CreatedTime
	noteTag.Usn = userService.IncrUsn(userId)
	noteTag.IsDeleted = false
	db.Insert(db.NoteTags, noteTag)

	return noteTag
}
// add attach
// api调用时, 添加attach之前是没有note的
// fromApi表示是api添加的, updateNote传过来的, 此时不要incNote's usn, 因为updateNote会inc的
func (this *AttachService) AddAttach(attach info.Attach, fromApi bool) (ok bool, msg string) {
	attach.CreatedTime = time.Now()
	ok = db.Insert(db.Attachs, attach)

	note := noteService.GetNoteById(attach.NoteId.Hex())

	// api调用时, 添加attach之前是没有note的
	var userId string
	if note.NoteId != "" {
		userId = note.UserId.Hex()
	} else {
		userId = attach.UploadUserId.Hex()
	}

	if ok {
		// 更新笔记的attachs num
		this.updateNoteAttachNum(attach.NoteId, 1)
	}

	if !fromApi {
		// 增长note's usn
		noteService.IncrNoteUsn(attach.NoteId.Hex(), userId)
	}

	return
}
// 添加一个notebook共享
// [ok]
func (this *ShareService) AddShareNotebook1(shareNotebook info.ShareNotebook) bool {
	// 添加一条记录说明两者存在关系
	this.AddHasShareNote(shareNotebook.UserId.Hex(), shareNotebook.ToUserId.Hex())

	shareNotebook.CreatedTime = time.Now()
	return db.Insert(db.ShareNotebooks, shareNotebook)
}
// 点赞
// retun ok , isLike
func (this *BlogService) LikeBlog(noteId, userId string) (ok bool, isLike bool) {
	ok = false
	isLike = false
	if noteId == "" || userId == "" {
		return
	}
	// 判断是否点过赞, 如果点过那么取消点赞
	note := noteService.GetNoteById(noteId)
	if !note.IsBlog /*|| note.UserId.Hex() == userId */ {
		return
	}

	noteIdO := bson.ObjectIdHex(noteId)
	userIdO := bson.ObjectIdHex(userId)
	var n int
	if !db.Has(db.BlogLikes, bson.M{"NoteId": noteIdO, "UserId": userIdO}) {
		n = 1
		// 添加之
		db.Insert(db.BlogLikes, info.BlogLike{LikeId: bson.NewObjectId(), NoteId: noteIdO, UserId: userIdO, CreatedTime: time.Now()})
		isLike = true
	} else {
		// 已点过, 那么删除之
		n = -1
		db.Delete(db.BlogLikes, bson.M{"NoteId": noteIdO, "UserId": userIdO})
		isLike = false
	}
	ok = db.Update(db.Notes, bson.M{"_id": noteIdO}, bson.M{"$inc": bson.M{"LikeNum": n}})

	return
}
func (this *NoteService) AddNote(note info.Note, fromApi bool) info.Note {
	if note.NoteId.Hex() == "" {
		noteId := bson.NewObjectId()
		note.NoteId = noteId
	}
	note.CreatedTime = time.Now()
	note.UpdatedTime = note.CreatedTime
	note.IsTrash = false
	note.UpdatedUserId = note.UserId
	note.UrlTitle = GetUrTitle(note.UserId.Hex(), note.Title, "note")
	note.Usn = userService.IncrUsn(note.UserId.Hex())

	notebookId := note.NotebookId.Hex()

	// api会传IsBlog, web不会传
	if !fromApi {
		// 设为blog
		note.IsBlog = notebookService.IsBlog(notebookId)
	}
	//	if note.IsBlog {
	note.PublicTime = note.UpdatedTime
	//	}

	db.Insert(db.Notes, note)

	// tag1
	tagService.AddTags(note.UserId.Hex(), note.Tags)

	// recount notebooks' notes number
	notebookService.ReCountNotebookNumberNotes(notebookId)

	return note
}
// 重新计算博客的标签
// 在设置设置/取消为博客时调用
func (this *BlogService) ReCountBlogTags(userId string) bool {
	// 得到所有博客
	notes := []info.Note{}
	userIdO := bson.ObjectIdHex(userId)
	query := bson.M{"UserId": userIdO, "IsTrash": false, "IsDeleted": false, "IsBlog": true}
	db.ListByQWithFields(db.Notes, query, []string{"Tags"}, &notes)

	db.DeleteAll(db.TagCounts, bson.M{"UserId": userIdO, "IsBlog": true})
	if notes == nil || len(notes) == 0 {
		return true
	}
	// 统计所有的Tags和数目
	tagsCount := map[string]int{}
	for _, note := range notes {
		tags := note.Tags
		if tags != nil && len(tags) > 0 {
			for _, tag := range tags {
				count := tagsCount[tag]
				count++
				tagsCount[tag] = count
			}
		}
	}
	// 一个个插入
	for tag, count := range tagsCount {
		db.Insert(db.TagCounts,
			info.TagCount{UserId: userIdO, IsBlog: true, Tag: tag, Count: count})
	}
	return true
}
// 更新或添加
func (this *BlogService) AddOrUpdateSingle(userId, singleId, title, content string) (ok bool) {
	ok = false
	if singleId != "" {
		ok = db.UpdateByIdAndUserIdMap(db.BlogSingles, singleId, userId, bson.M{
			"Title":       title,
			"Content":     content,
			"UpdatedTime": time.Now(),
		})
		if ok {
			// 还要修改UserBlog中的Singles
			this.updateBlogSingles(userId, false, false, singleId, title, "")
		}
		return
	}
	// 添加
	page := info.BlogSingle{
		SingleId:    bson.NewObjectId(),
		UserId:      bson.ObjectIdHex(userId),
		Title:       title,
		Content:     content,
		UrlTitle:    GetUrTitle(userId, title, "single"),
		CreatedTime: time.Now(),
	}
	page.UpdatedTime = page.CreatedTime
	ok = db.Insert(db.BlogSingles, page)

	// 还要修改UserBlog中的Singles
	this.updateBlogSingles(userId, false, true, page.SingleId.Hex(), title, page.UrlTitle)

	return
}
// 添加分组
func (this *GroupService) AddGroup(userId, title string) (bool, info.Group) {
	group := info.Group{
		GroupId:     bson.NewObjectId(),
		UserId:      bson.ObjectIdHex(userId),
		Title:       title,
		CreatedTime: time.Now(),
	}
	return db.Insert(db.Groups, group), group
}
// 新建历史
func (this *NoteContentHistoryService) newHistory(noteId, userId string, eachHistory info.EachHistory) {
	history := info.NoteContentHistory{NoteId: bson.ObjectIdHex(noteId),
		UserId:    bson.ObjectIdHex(userId),
		Histories: []info.EachHistory{eachHistory},
	}

	// 保存之
	db.Insert(db.NoteContentHistories, history)
}
// 添加笔记本内容
// [ok]
func (this *NoteService) AddNoteContent(noteContent info.NoteContent) info.NoteContent {
	noteContent.CreatedTime = time.Now()
	noteContent.UpdatedTime = noteContent.CreatedTime
	noteContent.UpdatedUserId = noteContent.UserId
	db.Insert(db.NoteContents, noteContent)

	// 更新笔记图片
	noteImageService.UpdateNoteImages(noteContent.UserId.Hex(), noteContent.NoteId.Hex(), "", noteContent.Content)

	return noteContent
}
// 通用方法
func (this *ConfigService) updateGlobalConfig(userId, key string, value interface{}, isArr, isMap, isArrMap bool) bool {
	// 判断是否存在
	if _, ok := this.GlobalAllConfigs[key]; !ok {
		// 需要添加
		config := info.Config{ConfigId: bson.NewObjectId(),
			UserId:      bson.ObjectIdHex(userId),
			Key:         key,
			IsArr:       isArr,
			IsMap:       isMap,
			IsArrMap:    isArrMap,
			UpdatedTime: time.Now(),
		}
		if isArr {
			v, _ := value.([]string)
			config.ValueArr = v
			this.GlobalArrayConfigs[key] = v
		} else if isMap {
			v, _ := value.(map[string]string)
			config.ValueMap = v
			this.GlobalMapConfigs[key] = v
		} else if isArrMap {
			v, _ := value.([]map[string]string)
			config.ValueArrMap = v
			this.GlobalArrMapConfigs[key] = v
		} else {
			v, _ := value.(string)
			config.ValueStr = v
			this.GlobalStringConfigs[key] = v
		}
		return db.Insert(db.Configs, config)
	} else {
		i := bson.M{"UpdatedTime": time.Now()}
		this.GlobalAllConfigs[key] = value
		if isArr {
			v, _ := value.([]string)
			i["ValueArr"] = v
			this.GlobalArrayConfigs[key] = v
		} else if isMap {
			v, _ := value.(map[string]string)
			i["ValueMap"] = v
			this.GlobalMapConfigs[key] = v
		} else if isArrMap {
			v, _ := value.([]map[string]string)
			i["ValueArrMap"] = v
			this.GlobalArrMapConfigs[key] = v
		} else {
			v, _ := value.(string)
			i["ValueStr"] = v
			this.GlobalStringConfigs[key] = v
		}
		return db.UpdateByQMap(db.Configs, bson.M{"UserId": bson.ObjectIdHex(userId), "Key": key}, i)
	}
}
// add Image
func (this *FileService) AddImage(image info.File, albumId, userId string, needCheckSize bool) (ok bool, msg string) {
	image.CreatedTime = time.Now()
	if albumId != "" {
		image.AlbumId = bson.ObjectIdHex(albumId)
	} else {
		image.AlbumId = bson.ObjectIdHex(DEFAULT_ALBUM_ID)
		image.IsDefaultAlbum = true
	}
	image.UserId = bson.ObjectIdHex(userId)

	ok = db.Insert(db.Files, image)
	return
}
func (this *SessionService) Get(sessionId string) info.Session {
	session := info.Session{}
	db.GetByQ(db.Sessions, bson.M{"SessionId": sessionId}, &session)

	// 如果没有session, 那么插入一条之
	if session.Id == "" {
		session.Id = bson.NewObjectId()
		session.SessionId = sessionId
		session.CreatedTime = time.Now()
		session.UpdatedTime = session.CreatedTime
		db.Insert(db.Sessions, session)
	}

	return session
}
// 共享笔记给分组
func (this *ShareService) AddShareNotebookGroup(userId, notebookId, groupId string, perm int) bool {
	if !groupService.IsExistsGroupUser(userId, groupId) {
		return false
	}

	// 先删除之
	this.DeleteShareNotebookGroup(userId, notebookId, groupId)

	shareNotebook := info.ShareNotebook{NotebookId: bson.ObjectIdHex(notebookId),
		UserId:      bson.ObjectIdHex(userId), // 冗余字段
		ToGroupId:   bson.ObjectIdHex(groupId),
		Perm:        perm,
		CreatedTime: time.Now(),
	}
	return db.Insert(db.ShareNotebooks, shareNotebook)
}
// 举报
func (this *BlogService) Report(noteId, commentId, reason, userId string) bool {
	note := noteService.GetNoteById(noteId)
	if !note.IsBlog {
		return false
	}

	report := info.Report{ReportId: bson.NewObjectId(),
		NoteId:      bson.ObjectIdHex(noteId),
		UserId:      bson.ObjectIdHex(userId),
		Reason:      reason,
		CreatedTime: time.Now(),
	}
	if commentId != "" {
		report.CommentId = bson.ObjectIdHex(commentId)
	}
	return db.Insert(db.Reports, report)
}
// 第三方测试没有userId
func (this *ShareService) AddShareNoteToUserId(noteId string, perm int, userId, toUserId string) (bool, string, string) {
	// 添加一条记录说明两者存在关系
	this.AddHasShareNote(userId, toUserId)

	// 先删除之
	db.Delete(db.ShareNotes, bson.M{"NoteId": bson.ObjectIdHex(noteId),
		"UserId":   bson.ObjectIdHex(userId),
		"ToUserId": bson.ObjectIdHex(toUserId),
	})

	shareNote := info.ShareNote{NoteId: bson.ObjectIdHex(noteId),
		UserId:      bson.ObjectIdHex(userId),
		ToUserId:    bson.ObjectIdHex(toUserId),
		Perm:        perm,
		CreatedTime: time.Now(),
	}
	return db.Insert(db.ShareNotes, shareNote), "", toUserId
}
// 添加用户
func (this *UserService) AddUser(user info.User) bool {
	if user.UserId == "" {
		user.UserId = bson.NewObjectId()
	}
	user.CreatedTime = time.Now()

	if user.Email != "" {
		user.Email = strings.ToLower(user.Email)

		// 发送验证邮箱
		go func() {
			emailService.RegisterSendActiveEmail(user, user.Email)
			// 发送给我 [email protected]
			emailService.SendEmail("*****@*****.**", "新增用户", "{header}用户名"+user.Email+"{footer}")
		}()
	}

	return db.Insert(db.Users, user)
}
// 安装主题
// 得到该主题路径
func (this *ThemeService) InstallTheme(userId, themeId string) (ok bool) {
	theme := this.GetThemeById(themeId)
	// 不是默认主题, 即不是admin用户的主题, 不能乱安装
	if !theme.IsDefault {
		return false
	}

	// 用户之前是否有主题?
	userBlog := blogService.GetUserBlog(userId)
	if userBlog.ThemeId == "" {
		this.NewThemeForFirst(userBlog)
	}

	// 生成新主题
	newThemeId := bson.NewObjectId()
	themeId = newThemeId.Hex()
	themePath := this.getUserThemePath(userId, themeId)
	err := os.MkdirAll(themePath, 0755)
	if err != nil {
		return
	}
	// 复制默认主题
	sourceThemePath := revel.BasePath + "/" + theme.Path
	err = CopyDir(sourceThemePath, themePath)
	if err != nil {
		return
	}

	// 保存到数据库中
	theme, _ = this.getThemeConfig(themePath)
	theme.ThemeId = newThemeId
	theme.Path = this.getUserThemePath2(userId, themeId)
	theme.CreatedTime = time.Now()
	theme.UpdatedTime = theme.CreatedTime
	theme.UserId = bson.ObjectIdHex(userId)

	ok = db.Insert(db.Themes, theme)

	// 激活之
	this.ActiveTheme(userId, themeId)

	return ok
}
// 导入主题
// path == /llllllll/..../public/upload/.../aa.zip, 绝对路径
func (this *ThemeService) ImportTheme(userId, path string) (ok bool, msg string) {
	themeIdO := bson.NewObjectId()
	themeId := themeIdO.Hex()
	targetPath := this.getUserThemePath(userId, themeId) // revel.BasePath + "/public/upload/" + userId + "/themes/" + themeId

	err := os.MkdirAll(targetPath, 0755)
	if err != nil {
		msg = "error"
		return
	}
	if ok, msg = archive.Unzip(path, targetPath); !ok {
		DeleteFile(targetPath)
		Log("oh no")
		return
	}

	// 主题验证
	if ok, msg = this.ValidateTheme(targetPath, "", ""); !ok {
		DeleteFile(targetPath)
		return
	}
	// 解压成功, 那么新建之
	// 保存到数据库中
	theme, _ := this.getThemeConfig(targetPath)
	if theme.Name == "" {
		ok = false
		DeleteFile(targetPath)
		msg = "解析错误"
		return
	}
	theme.ThemeId = themeIdO
	theme.Path = this.getUserThemePath2(userId, themeId)
	theme.CreatedTime = time.Now()
	theme.UpdatedTime = theme.CreatedTime
	theme.UserId = bson.ObjectIdHex(userId)

	ok = db.Insert(db.Themes, theme)
	if !ok {
		DeleteFile(targetPath)
	}
	DeleteFile(path)
	return
}
// 评论
// 在noteId博客下userId 给toUserId评论content
// commentId可为空(针对某条评论评论)
func (this *BlogService) Comment(noteId, toCommentId, userId, content string) (bool, info.BlogComment) {
	var comment info.BlogComment
	if content == "" {
		return false, comment
	}

	note := noteService.GetNoteById(noteId)
	if !note.IsBlog {
		return false, comment
	}

	comment = info.BlogComment{CommentId: bson.NewObjectId(),
		NoteId:      bson.ObjectIdHex(noteId),
		UserId:      bson.ObjectIdHex(userId),
		Content:     content,
		CreatedTime: time.Now(),
	}
	var comment2 = info.BlogComment{}
	if toCommentId != "" {
		comment2 = info.BlogComment{}
		db.Get(db.BlogComments, toCommentId, &comment2)
		if comment2.CommentId != "" {
			comment.ToCommentId = comment2.CommentId
			comment.ToUserId = comment2.UserId
		}
	} else {
		// comment.ToUserId = note.UserId
	}
	ok := db.Insert(db.BlogComments, comment)
	if ok {
		// 评论+1
		db.Update(db.Notes, bson.M{"_id": bson.ObjectIdHex(noteId)}, bson.M{"$inc": bson.M{"CommentNum": 1}})
	}

	if userId != note.UserId.Hex() || toCommentId != "" {
		go func() {
			this.sendEmail(note, comment2, userId, content)
		}()
	}

	return ok, comment
}
func (this *ThemeService) upgradeThemeBeta2(userId, style string, isActive bool) (ok bool) {
	// 解压成功, 那么新建之
	// 保存到数据库中
	targetPath := this.GetDefaultThemePath(style)
	theme, _ := this.getThemeConfig(revel.BasePath + "/" + targetPath)
	if theme.Name == "" {
		ok = false
		return
	}
	themeIdO := bson.NewObjectId()
	theme.ThemeId = themeIdO
	theme.Path = targetPath // public
	theme.CreatedTime = time.Now()
	theme.UpdatedTime = theme.CreatedTime
	theme.UserId = bson.ObjectIdHex(userId)
	theme.IsActive = isActive
	theme.IsDefault = true
	theme.Style = style
	ok = db.Insert(db.Themes, theme)
	return ok
}
// 为group添加用户
// 用户是否已存在?
func (this *GroupService) AddUser(ownUserId, groupId, userId string) (ok bool, msg string) {
	// groupId是否是ownUserId的?
	/*
		if !this.IsExistsGroupUser(ownUserId, groupId) {
			return false, "forbidden"
		}
	*/
	if !this.isMyGroup(ownUserId, groupId) {
		return false, "forbidden"
	}

	// 是否已存在
	if db.Has(db.GroupUsers, bson.M{"GroupId": bson.ObjectIdHex(groupId), "UserId": bson.ObjectIdHex(userId)}) {
		return false, "hasUsers"
	}

	return db.Insert(db.GroupUsers, info.GroupUser{
		GroupUserId: bson.NewObjectId(),
		GroupId:     bson.ObjectIdHex(groupId),
		UserId:      bson.ObjectIdHex(userId),
		CreatedTime: time.Now(),
	}), ""
}
// TODO 这个web可以用, 但api会传来, 不用用了
// 解析内容中的图片, 建立图片与note的关系
// <img src="/file/outputImage?fileId=12323232" />
// 图片必须是我的, 不然不添加
// imgSrc 防止博客修改了, 但内容删除了
func (this *NoteImageService) UpdateNoteImages(userId, noteId, imgSrc, content string) bool {
	// 让主图成为内容的一员
	if imgSrc != "" {
		content = "<img src=\"" + imgSrc + "\" >" + content
	}
	// life 添加getImage
	reg, _ := regexp.Compile("(outputImage|getImage)\\?fileId=([a-z0-9A-Z]{24})")
	find := reg.FindAllStringSubmatch(content, -1) // 查找所有的

	// 删除旧的
	db.DeleteAll(db.NoteImages, bson.M{"NoteId": bson.ObjectIdHex(noteId)})

	// 添加新的
	var fileId string
	noteImage := info.NoteImage{NoteId: bson.ObjectIdHex(noteId)}
	hasAdded := make(map[string]bool)
	if find != nil && len(find) > 0 {
		for _, each := range find {
			if each != nil && len(each) == 3 {
				fileId = each[2] // 现在有两个子表达式了
				// 之前没能添加过的
				if _, ok := hasAdded[fileId]; !ok {
					Log(fileId)
					// 判断是否是我的文件
					if fileService.IsMyFile(userId, fileId) {
						noteImage.ImageId = bson.ObjectIdHex(fileId)
						db.Insert(db.NoteImages, noteImage)
					}
					hasAdded[fileId] = true
				}
			}
		}
	}

	return true
}
func (this *UpgradeService) moveTag() {
	usnI := 1
	tags := []info.Tag{}
	db.ListByQ(db.Tags, bson.M{}, &tags)
	for _, eachTag := range tags {
		tagTitles := eachTag.Tags
		now := time.Now()
		if tagTitles != nil && len(tagTitles) > 0 {
			for _, tagTitle := range tagTitles {
				noteTag := info.NoteTag{}
				noteTag.TagId = bson.NewObjectId()
				noteTag.Count = 1
				noteTag.Tag = tagTitle
				noteTag.UserId = eachTag.UserId
				noteTag.CreatedTime = now
				noteTag.UpdatedTime = now
				noteTag.Usn = usnI
				noteTag.IsDeleted = false
				db.Insert(db.NoteTags, noteTag)
				usnI++
			}
		}
	}
}
// add album
func (this *AlbumService) AddAlbum(album info.Album) bool {
	album.CreatedTime = time.Now()
	album.Type = IMAGE_TYPE
	return db.Insert(db.Albums, album)
}
// 共享note, notebook时使用
func (this *ShareService) AddHasShareNote(userId, toUserId string) bool {
	db.Insert(db.HasShareNotes, info.HasShareNote{UserId: bson.ObjectIdHex(userId), ToUserId: bson.ObjectIdHex(toUserId)})
	return true
}
// 添加邮件日志
func (this *EmailService) AddEmailLog(email, subject, body string, ok bool, msg string) {
	log := info.EmailLog{LogId: bson.NewObjectId(), Email: email, Subject: subject, Body: body, Ok: ok, Msg: msg, CreatedTime: time.Now()}
	db.Insert(db.EmailLogs, log)
}
// 得到某博客具体信息
func (this *SuggestionService) AddSuggestion(suggestion info.Suggestion) bool {
	if suggestion.Id == "" {
		suggestion.Id = bson.NewObjectId()
	}
	return db.Insert(db.Suggestions, suggestion)
}