Example #1
0
func makeDirectChannelVisible(teamId string, channelId string) {
	var members []model.ChannelMember
	if result := <-Srv.Store.Channel().GetMembers(channelId); result.Err != nil {
		l4g.Error(utils.T("api.post.make_direct_channel_visible.get_members.error"), channelId, result.Err.Message)
		return
	} else {
		members = result.Data.([]model.ChannelMember)
	}

	if len(members) != 2 {
		l4g.Error(utils.T("api.post.make_direct_channel_visible.get_2_members.error"), channelId)
		return
	}

	// make sure the channel is visible to both members
	for i, member := range members {
		otherUserId := members[1-i].UserId

		if result := <-Srv.Store.Preference().Get(member.UserId, model.PREFERENCE_CATEGORY_DIRECT_CHANNEL_SHOW, otherUserId); result.Err != nil {
			// create a new preference since one doesn't exist yet
			preference := &model.Preference{
				UserId:   member.UserId,
				Category: model.PREFERENCE_CATEGORY_DIRECT_CHANNEL_SHOW,
				Name:     otherUserId,
				Value:    "true",
			}

			if saveResult := <-Srv.Store.Preference().Save(&model.Preferences{*preference}); saveResult.Err != nil {
				l4g.Error(utils.T("api.post.make_direct_channel_visible.save_pref.error"), member.UserId, otherUserId, saveResult.Err.Message)
			} else {
				message := model.NewMessage(teamId, channelId, member.UserId, model.ACTION_PREFERENCE_CHANGED)
				message.Add("preference", preference.ToJson())

				PublishAndForget(message)
			}
		} else {
			preference := result.Data.(model.Preference)

			if preference.Value != "true" {
				// update the existing preference to make the channel visible
				preference.Value = "true"

				if updateResult := <-Srv.Store.Preference().Save(&model.Preferences{preference}); updateResult.Err != nil {
					l4g.Error(utils.T("api.post.make_direct_channel_visible.update_pref.error"), member.UserId, otherUserId, updateResult.Err.Message)
				} else {
					message := model.NewMessage(teamId, channelId, member.UserId, model.ACTION_PREFERENCE_CHANGED)
					message.Add("preference", preference.ToJson())

					PublishAndForget(message)
				}
			}
		}
	}
}
Example #2
0
func CreateUser(c *Context, team *model.Team, user *model.User) *model.User {

	if !utils.Cfg.TeamSettings.EnableUserCreation {
		c.Err = model.NewAppError("CreateUser", "User creation has been disabled. Please ask your systems administrator for details.", "")
		return nil
	}

	channelRole := ""
	if team.Email == user.Email {
		user.Roles = model.ROLE_TEAM_ADMIN
		channelRole = model.CHANNEL_ROLE_ADMIN

		// Below is a speical case where the first user in the entire
		// system is granted the system_admin role instead of admin
		if result := <-Srv.Store.User().GetTotalUsersCount(); result.Err != nil {
			c.Err = result.Err
			return nil
		} else {
			count := result.Data.(int64)
			if count <= 0 {
				user.Roles = model.ROLE_SYSTEM_ADMIN
			}
		}

	} else {
		user.Roles = ""
	}

	user.MakeNonNil()

	if result := <-Srv.Store.User().Save(user); result.Err != nil {
		c.Err = result.Err
		l4g.Error("Couldn't save the user err=%v", result.Err)
		return nil
	} else {
		ruser := result.Data.(*model.User)

		// Soft error if there is an issue joining the default channels
		if err := JoinDefaultChannels(ruser, channelRole); err != nil {
			l4g.Error("Encountered an issue joining default channels user_id=%s, team_id=%s, err=%v", ruser.Id, ruser.TeamId, err)
		}

		addDirectChannelsAndForget(ruser)

		if user.EmailVerified {
			if cresult := <-Srv.Store.User().VerifyEmail(ruser.Id); cresult.Err != nil {
				l4g.Error("Failed to set email verified err=%v", cresult.Err)
			}
		}

		ruser.Sanitize(map[string]bool{})

		// This message goes to every channel, so the channelId is irrelevant
		message := model.NewMessage(team.Id, "", ruser.Id, model.ACTION_NEW_USER)

		PublishAndForget(message)

		return ruser
	}
}
Example #3
0
func removeChannelMember(c *Context, w http.ResponseWriter, r *http.Request) {
	params := mux.Vars(r)
	id := params["id"]

	data := model.MapFromJson(r.Body)
	userId := data["user_id"]

	if len(userId) != 26 {
		c.SetInvalidParam("addChannelMember", "user_id")
		return
	}

	sc := Srv.Store.Channel().Get(id)
	cmc := Srv.Store.Channel().GetMember(id, c.Session.UserId)

	if cresult := <-sc; cresult.Err != nil {
		c.Err = cresult.Err
		return
	} else if cmcresult := <-cmc; cmcresult.Err != nil {
		c.Err = cmcresult.Err
		return
	} else {
		channel := cresult.Data.(*model.Channel)
		channelMember := cmcresult.Data.(model.ChannelMember)

		if !c.HasPermissionsToTeam(channel.TeamId, "removeChannelMember") {
			return
		}

		if !strings.Contains(channelMember.Roles, model.CHANNEL_ROLE_ADMIN) && !strings.Contains(c.Session.Roles, model.ROLE_ADMIN) {
			c.Err = model.NewAppError("updateChannel", "You do not have the appropriate permissions ", "")
			c.Err.StatusCode = http.StatusForbidden
			return
		}

		if channel.DeleteAt > 0 {
			c.Err = model.NewAppError("updateChannel", "The channel has been archived or deleted", "")
			c.Err.StatusCode = http.StatusBadRequest
			return
		}

		if cmresult := <-Srv.Store.Channel().RemoveMember(id, userId); cmresult.Err != nil {
			c.Err = cmresult.Err
			return
		}

		message := model.NewMessage(c.Session.TeamId, "", userId, model.ACTION_USER_REMOVED)
		message.Add("channel_id", id)
		message.Add("remover", c.Session.UserId)
		PublishAndForget(message)

		c.LogAudit("name=" + channel.Name + " user_id=" + userId)

		result := make(map[string]string)
		result["channel_id"] = channel.Id
		result["removed_user_id"] = userId
		w.Write([]byte(model.MapToJson(result)))
	}

}
Example #4
0
func AddUserToChannel(user *model.User, channel *model.Channel) (*model.ChannelMember, *model.AppError) {
	if channel.DeleteAt > 0 {
		return nil, model.NewLocAppError("AddUserToChannel", "api.channel.add_user_to_channel.deleted.app_error", nil, "")
	}

	if channel.Type != model.CHANNEL_OPEN && channel.Type != model.CHANNEL_PRIVATE {
		return nil, model.NewLocAppError("AddUserToChannel", "api.channel.add_user_to_channel.type.app_error", nil, "")
	}

	if result := <-Srv.Store.Channel().GetMember(channel.Id, user.Id); result.Err != nil {
		if result.Err.Id != store.MISSING_MEMBER_ERROR {
			return nil, result.Err
		}
	} else {
		channelMember := result.Data.(model.ChannelMember)
		return &channelMember, nil
	}

	newMember := &model.ChannelMember{ChannelId: channel.Id, UserId: user.Id, NotifyProps: model.GetDefaultChannelNotifyProps()}
	if result := <-Srv.Store.Channel().SaveMember(newMember); result.Err != nil {
		l4g.Error("Failed to add member user_id=%v channel_id=%v err=%v", user.Id, channel.Id, result.Err)
		return nil, model.NewLocAppError("AddUserToChannel", "api.channel.add_user.to.channel.failed.app_error", nil, "")
	}

	go func() {
		InvalidateCacheForUser(user.Id)

		message := model.NewMessage(channel.TeamId, channel.Id, user.Id, model.ACTION_USER_ADDED)
		go Publish(message)
	}()

	return newMember, nil
}
Example #5
0
func JoinUserToTeam(team *model.Team, user *model.User) *model.AppError {

	tm := &model.TeamMember{TeamId: team.Id, UserId: user.Id}

	channelRole := ""
	if team.Email == user.Email {
		tm.Roles = model.ROLE_TEAM_ADMIN
		channelRole = model.CHANNEL_ROLE_ADMIN
	}

	if tmr := <-Srv.Store.Team().SaveMember(tm); tmr.Err != nil {
		if tmr.Err.Id == store.TEAM_MEMBER_EXISTS_ERROR {
			return nil
		}
		return tmr.Err
	}

	if uua := <-Srv.Store.User().UpdateUpdateAt(user.Id); uua.Err != nil {
		return uua.Err
	}

	// Soft error if there is an issue joining the default channels
	if err := JoinDefaultChannels(team.Id, user, channelRole); err != nil {
		l4g.Error(utils.T("api.user.create_user.joining.error"), user.Id, team.Id, err)
	}

	RemoveAllSessionsForUserId(user.Id)
	InvalidateCacheForUser(user.Id)

	// This message goes to every channel, so the channelId is irrelevant
	go Publish(model.NewMessage("", "", user.Id, model.ACTION_NEW_USER))

	return nil
}
Example #6
0
func updatePost(c *Context, w http.ResponseWriter, r *http.Request) {
	post := model.PostFromJson(r.Body)

	if post == nil {
		c.SetInvalidParam("updatePost", "post")
		return
	}

	cchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, post.ChannelId, c.Session.UserId)
	pchan := Srv.Store.Post().Get(post.Id)

	if !c.HasPermissionsToChannel(cchan, "updatePost") {
		return
	}

	var oldPost *model.Post
	if result := <-pchan; result.Err != nil {
		c.Err = result.Err
		return
	} else {
		oldPost = result.Data.(*model.PostList).Posts[post.Id]

		if oldPost == nil {
			c.Err = model.NewAppError("updatePost", "We couldn't find the existing post or comment to update.", "id="+post.Id)
			c.Err.StatusCode = http.StatusBadRequest
			return
		}

		if oldPost.UserId != c.Session.UserId {
			c.Err = model.NewAppError("updatePost", "You do not have the appropriate permissions", "oldUserId="+oldPost.UserId)
			c.Err.StatusCode = http.StatusForbidden
			return
		}

		if oldPost.DeleteAt != 0 {
			c.Err = model.NewAppError("updatePost", "You do not have the appropriate permissions", "Already delted id="+post.Id)
			c.Err.StatusCode = http.StatusForbidden
			return
		}
	}

	hashtags, _ := model.ParseHashtags(post.Message)

	if result := <-Srv.Store.Post().Update(oldPost, post.Message, hashtags); result.Err != nil {
		c.Err = result.Err
		return
	} else {
		rpost := result.Data.(*model.Post)

		message := model.NewMessage(c.Session.TeamId, rpost.ChannelId, c.Session.UserId, model.ACTION_POST_EDITED)
		message.Add("post_id", rpost.Id)
		message.Add("channel_id", rpost.ChannelId)
		message.Add("message", rpost.Message)

		store.PublishAndForget(message)

		w.Write([]byte(rpost.ToJson()))
	}
}
Example #7
0
func updatePost(c *Context, w http.ResponseWriter, r *http.Request) {
	post := model.PostFromJson(r.Body)

	if post == nil {
		c.SetInvalidParam("updatePost", "post")
		return
	}

	cchan := Srv.Store.Channel().CheckPermissionsTo(c.TeamId, post.ChannelId, c.Session.UserId)
	pchan := Srv.Store.Post().Get(post.Id)

	if !c.HasPermissionsToChannel(cchan, "updatePost") {
		return
	}

	var oldPost *model.Post
	if result := <-pchan; result.Err != nil {
		c.Err = result.Err
		return
	} else {
		oldPost = result.Data.(*model.PostList).Posts[post.Id]

		if oldPost == nil {
			c.Err = model.NewLocAppError("updatePost", "api.post.update_post.find.app_error", nil, "id="+post.Id)
			c.Err.StatusCode = http.StatusBadRequest
			return
		}

		if oldPost.UserId != c.Session.UserId {
			c.Err = model.NewLocAppError("updatePost", "api.post.update_post.permissions.app_error", nil, "oldUserId="+oldPost.UserId)
			c.Err.StatusCode = http.StatusForbidden
			return
		}

		if oldPost.DeleteAt != 0 {
			c.Err = model.NewLocAppError("updatePost", "api.post.update_post.permissions.app_error", nil,
				c.T("api.post.update_post.permissions_details.app_error", map[string]interface{}{"PostId": post.Id}))
			c.Err.StatusCode = http.StatusForbidden
			return
		}
	}

	hashtags, _ := model.ParseHashtags(post.Message)

	if result := <-Srv.Store.Post().Update(oldPost, post.Message, hashtags); result.Err != nil {
		c.Err = result.Err
		return
	} else {
		rpost := result.Data.(*model.Post)

		message := model.NewMessage(c.TeamId, rpost.ChannelId, c.Session.UserId, model.ACTION_POST_EDITED)
		message.Add("post", rpost.ToJson())

		PublishAndForget(message)

		w.Write([]byte(rpost.ToJson()))
	}
}
Example #8
0
func CreateUser(c *Context, team *model.Team, user *model.User) *model.User {

	channelRole := ""
	if team.Email == user.Email {
		user.Roles = model.ROLE_ADMIN
		channelRole = model.CHANNEL_ROLE_ADMIN
	} else {
		user.Roles = ""
	}

	user.MakeNonNil()
	if len(user.Props["theme"]) == 0 {
		user.AddProp("theme", utils.Cfg.TeamSettings.DefaultThemeColor)
	}

	if result := <-Srv.Store.User().Save(user); result.Err != nil {
		c.Err = result.Err
		return nil
	} else {
		ruser := result.Data.(*model.User)

		// Do not error if user cannot be added to the town-square channel
		if cresult := <-Srv.Store.Channel().GetByName(team.Id, "town-square"); cresult.Err != nil {
			l4g.Error("Failed to get town-square err=%v", cresult.Err)
		} else {
			cm := &model.ChannelMember{ChannelId: cresult.Data.(*model.Channel).Id, UserId: ruser.Id, NotifyLevel: model.CHANNEL_NOTIFY_ALL, Roles: channelRole}
			if cmresult := <-Srv.Store.Channel().SaveMember(cm); cmresult.Err != nil {
				l4g.Error("Failed to add member town-square err=%v", cmresult.Err)
			}
		}

		//fireAndForgetWelcomeEmail(strings.Split(ruser.FullName, " ")[0], ruser.Email, team.Name, c.TeamUrl+"/channels/town-square")

		if user.EmailVerified {
			if cresult := <-Srv.Store.User().VerifyEmail(ruser.Id); cresult.Err != nil {
				l4g.Error("Failed to get town-square err=%v", cresult.Err)
			}
		} else {
			FireAndForgetVerifyEmail(result.Data.(*model.User).Id, strings.Split(ruser.FullName, " ")[0], ruser.Email, team.Name, c.TeamUrl)
		}

		ruser.Sanitize(map[string]bool{})

		//This message goes to every channel, so the channelId is irrelevant
		message := model.NewMessage(team.Id, "", ruser.Id, model.ACTION_NEW_USER)

		store.PublishAndForget(message)

		return ruser
	}
}
Example #9
0
func updateLastViewedAt(c *Context, w http.ResponseWriter, r *http.Request) {
	params := mux.Vars(r)
	id := params["id"]

	Srv.Store.Channel().UpdateLastViewedAt(id, c.Session.UserId)

	message := model.NewMessage(c.Session.TeamId, id, c.Session.UserId, model.ACTION_VIEWED)
	message.Add("channel_id", id)

	store.PublishAndForget(message)

	result := make(map[string]string)
	result["id"] = id
	w.Write([]byte(model.MapToJson(result)))
}
Example #10
0
func CreateUser(c *Context, team *model.Team, user *model.User) *model.User {

	if !utils.Cfg.TeamSettings.EnableUserCreation {
		c.Err = model.NewAppError("CreateUser", "User creation has been disabled. Please ask your systems administrator for details.", "")
		return nil
	}

	channelRole := ""
	if team.Email == user.Email {
		user.Roles = model.ROLE_TEAM_ADMIN
		channelRole = model.CHANNEL_ROLE_ADMIN
	} else {
		user.Roles = ""
	}

	user.MakeNonNil()

	if result := <-Srv.Store.User().Save(user); result.Err != nil {
		c.Err = result.Err
		l4g.Error("Couldn't save the user err=%v", result.Err)
		return nil
	} else {
		ruser := result.Data.(*model.User)

		// Soft error if there is an issue joining the default channels
		if err := JoinDefaultChannels(ruser, channelRole); err != nil {
			l4g.Error("Encountered an issue joining default channels user_id=%s, team_id=%s, err=%v", ruser.Id, ruser.TeamId, err)
		}

		//fireAndForgetWelcomeEmail(ruser.FirstName, ruser.Email, team.Name, c.TeamURL+"/channels/town-square")
		if user.EmailVerified {
			if cresult := <-Srv.Store.User().VerifyEmail(ruser.Id); cresult.Err != nil {
				l4g.Error("Failed to set email verified err=%v", cresult.Err)
			}
		} else {
			FireAndForgetVerifyEmail(result.Data.(*model.User).Id, ruser.Email, team.Name, team.DisplayName, c.GetSiteURL(), c.GetTeamURLFromTeam(team))
		}

		ruser.Sanitize(map[string]bool{})

		// This message goes to every channel, so the channelId is irrelevant
		message := model.NewMessage(team.Id, "", ruser.Id, model.ACTION_NEW_USER)

		PublishAndForget(message)

		return ruser
	}
}
Example #11
0
func TestRedis(t *testing.T) {
	utils.LoadConfig("config.json")

	c := RedisClient()

	if c == nil {
		t.Fatal("should have a valid redis connection")
	}

	pubsub := c.PubSub()
	defer pubsub.Close()

	m := model.NewMessage(model.NewId(), model.NewId(), model.NewId(), model.ACTION_TYPING)
	m.Add("RootId", model.NewId())

	err := pubsub.Subscribe(m.TeamId)
	if err != nil {
		t.Fatal(err)
	}

	// should be the subscribe success message
	// lets gobble that up
	if _, err := pubsub.Receive(); err != nil {
		t.Fatal(err)
	}

	PublishAndForget(m)

	fmt.Println("here1")

	if msg, err := pubsub.Receive(); err != nil {
		t.Fatal(err)
	} else {

		rmsg := GetMessageFromPayload(msg)

		if m.TeamId != rmsg.TeamId {
			t.Fatal("Ids do not match")
		}

		if m.Props["RootId"] != rmsg.Props["RootId"] {
			t.Fatal("Ids do not match")
		}
	}

	RedisClose()
}
Example #12
0
func CreateUser(c *Context, team *model.Team, user *model.User) *model.User {

	channelRole := ""
	if team.Email == user.Email {
		user.Roles = model.ROLE_ADMIN
		channelRole = model.CHANNEL_ROLE_ADMIN
	} else {
		user.Roles = ""
	}

	user.MakeNonNil()
	if len(user.Props["theme"]) == 0 {
		user.AddProp("theme", utils.Cfg.TeamSettings.DefaultThemeColor)
	}

	if result := <-Srv.Store.User().Save(user); result.Err != nil {
		c.Err = result.Err
		return nil
	} else {
		ruser := result.Data.(*model.User)

		// Soft error if there is an issue joining the default channels
		if err := JoinDefaultChannels(c, ruser, channelRole); err != nil {
			l4g.Error("Encountered an issue joining default channels user_id=%s, team_id=%s, err=%v", ruser.Id, ruser.TeamId, err)
		}

		//fireAndForgetWelcomeEmail(strings.Split(ruser.FullName, " ")[0], ruser.Email, team.Name, c.TeamUrl+"/channels/town-square")

		if user.EmailVerified {
			if cresult := <-Srv.Store.User().VerifyEmail(ruser.Id); cresult.Err != nil {
				l4g.Error("Failed to set email verified err=%v", cresult.Err)
			}
		} else {
			FireAndForgetVerifyEmail(result.Data.(*model.User).Id, strings.Split(ruser.FullName, " ")[0], ruser.Email, team.Name, c.TeamUrl)
		}

		ruser.Sanitize(map[string]bool{})

		// This message goes to every channel, so the channelId is irrelevant
		message := model.NewMessage(team.Id, "", ruser.Id, model.ACTION_NEW_USER)

		store.PublishAndForget(message)

		return ruser
	}
}
Example #13
0
func RemoveUserFromChannel(userIdToRemove string, removerUserId string, channel *model.Channel) *model.AppError {
	if channel.DeleteAt > 0 {
		return model.NewLocAppError("updateChannel", "api.channel.remove_user_from_channel.deleted.app_error", nil, "")
	}

	if cmresult := <-Srv.Store.Channel().RemoveMember(channel.Id, userIdToRemove); cmresult.Err != nil {
		return cmresult.Err
	}

	InvalidateCacheForUser(userIdToRemove)

	message := model.NewMessage(channel.TeamId, channel.Id, userIdToRemove, model.ACTION_USER_REMOVED)
	message.Add("remover_id", removerUserId)
	go Publish(message)

	return nil
}
Example #14
0
func RemoveUserFromChannel(userIdToRemove string, removerUserId string, channel *model.Channel) *model.AppError {
	if channel.DeleteAt > 0 {
		return model.NewAppError("updateChannel", "The channel has been archived or deleted", "")
	}

	if cmresult := <-Srv.Store.Channel().RemoveMember(channel.Id, userIdToRemove); cmresult.Err != nil {
		return cmresult.Err
	}

	UpdateChannelAccessCacheAndForget(channel.TeamId, userIdToRemove, channel.Id)

	message := model.NewMessage(channel.TeamId, channel.Id, userIdToRemove, model.ACTION_USER_REMOVED)
	message.Add("remover_id", removerUserId)
	PublishAndForget(message)

	return nil
}
func setCollapsePreference(c *Context, value string) *model.CommandResponse {
	pref := model.Preference{
		UserId:   c.Session.UserId,
		Category: model.PREFERENCE_CATEGORY_DISPLAY_SETTINGS,
		Name:     model.PREFERENCE_NAME_COLLAPSE_SETTING,
		Value:    value,
	}

	if result := <-Srv.Store.Preference().Save(&model.Preferences{pref}); result.Err != nil {
		return &model.CommandResponse{Text: c.T("api.command_expand_collapse.fail.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
	}

	socketMessage := model.NewMessage("", "", c.Session.UserId, model.ACTION_PREFERENCE_CHANGED)
	socketMessage.Add("preference", pref.ToJson())
	go Publish(socketMessage)

	return &model.CommandResponse{}
}
Example #16
0
func SendEphemeralPost(teamId, userId string, post *model.Post) {
	post.Type = model.POST_EPHEMERAL

	// fill in fields which haven't been specified which have sensible defaults
	if post.Id == "" {
		post.Id = model.NewId()
	}
	if post.CreateAt == 0 {
		post.CreateAt = model.GetMillis()
	}
	if post.Props == nil {
		post.Props = model.StringInterface{}
	}
	if post.Filenames == nil {
		post.Filenames = []string{}
	}

	message := model.NewMessage(teamId, post.ChannelId, userId, model.ACTION_EPHEMERAL_MESSAGE)
	message.Add("post", post.ToJson())

	PublishAndForget(message)
}
Example #17
0
func CreateDirectChannel(userId string, otherUserId string) (*model.Channel, *model.AppError) {
	uc := Srv.Store.User().Get(otherUserId)

	channel := new(model.Channel)

	channel.DisplayName = ""
	channel.Name = model.GetDMNameFromIds(otherUserId, userId)

	channel.Header = ""
	channel.Type = model.CHANNEL_DIRECT

	if uresult := <-uc; uresult.Err != nil {
		return nil, model.NewLocAppError("CreateDirectChannel", "api.channel.create_direct_channel.invalid_user.app_error", nil, otherUserId)
	}

	cm1 := &model.ChannelMember{
		UserId:      userId,
		NotifyProps: model.GetDefaultChannelNotifyProps(),
	}
	cm2 := &model.ChannelMember{
		UserId:      otherUserId,
		NotifyProps: model.GetDefaultChannelNotifyProps(),
	}

	if result := <-Srv.Store.Channel().SaveDirectChannel(channel, cm1, cm2); result.Err != nil {
		if result.Err.Id == store.CHANNEL_EXISTS_ERROR {
			return result.Data.(*model.Channel), nil
		} else {
			return nil, result.Err
		}
	} else {
		message := model.NewMessage("", channel.Id, userId, model.ACTION_DIRECT_ADDED)
		message.Add("teammate_id", otherUserId)
		go Publish(message)

		return result.Data.(*model.Channel), nil
	}
}
Example #18
0
func updateLastViewedAt(c *Context, w http.ResponseWriter, r *http.Request) {
	params := mux.Vars(r)
	id := params["id"]

	Srv.Store.Channel().UpdateLastViewedAt(id, c.Session.UserId)

	preference := model.Preference{
		UserId:   c.Session.UserId,
		Category: model.PREFERENCE_CATEGORY_LAST,
		Name:     model.PREFERENCE_NAME_LAST_CHANNEL,
		Value:    id,
	}

	Srv.Store.Preference().Save(&model.Preferences{preference})

	message := model.NewMessage(c.Session.TeamId, id, c.Session.UserId, model.ACTION_CHANNEL_VIEWED)
	message.Add("channel_id", id)

	PublishAndForget(message)

	result := make(map[string]string)
	result["id"] = id
	w.Write([]byte(model.MapToJson(result)))
}
Example #19
0
func AddUserToChannel(user *model.User, channel *model.Channel) (*model.ChannelMember, *model.AppError) {
	if channel.DeleteAt > 0 {
		return nil, model.NewAppError("AddUserToChannel", "The channel has been archived or deleted", "")
	}

	if channel.Type != model.CHANNEL_OPEN && channel.Type != model.CHANNEL_PRIVATE {
		return nil, model.NewAppError("AddUserToChannel", "Can not add user to this channel type", "")
	}

	newMember := &model.ChannelMember{ChannelId: channel.Id, UserId: user.Id, NotifyProps: model.GetDefaultChannelNotifyProps()}
	if cmresult := <-Srv.Store.Channel().SaveMember(newMember); cmresult.Err != nil {
		l4g.Error("Failed to add member user_id=%v channel_id=%v err=%v", user.Id, channel.Id, cmresult.Err)
		return nil, model.NewAppError("AddUserToChannel", "Failed to add user to channel", "")
	}

	go func() {
		UpdateChannelAccessCache(channel.TeamId, user.Id, channel.Id)

		message := model.NewMessage(channel.TeamId, channel.Id, user.Id, model.ACTION_USER_ADDED)
		PublishAndForget(message)
	}()

	return newMember, nil
}
Example #20
0
func AddUserToChannel(user *model.User, channel *model.Channel) (*model.ChannelMember, *model.AppError) {
	if channel.DeleteAt > 0 {
		return nil, model.NewLocAppError("AddUserToChannel", "api.channel.add_user_to_channel.deleted.app_error", nil, "")
	}

	if channel.Type != model.CHANNEL_OPEN && channel.Type != model.CHANNEL_PRIVATE {
		return nil, model.NewLocAppError("AddUserToChannel", "api.channel.add_user_to_channel.type.app_error", nil, "")
	}

	newMember := &model.ChannelMember{ChannelId: channel.Id, UserId: user.Id, NotifyProps: model.GetDefaultChannelNotifyProps()}
	if cmresult := <-Srv.Store.Channel().SaveMember(newMember); cmresult.Err != nil {
		l4g.Error("Failed to add member user_id=%v channel_id=%v err=%v", user.Id, channel.Id, cmresult.Err)
		return nil, model.NewLocAppError("AddUserToChannel", "api.channel.add_user.to.channel.failed.app_error", nil, "")
	}

	go func() {
		UpdateChannelAccessCache(channel.TeamId, user.Id, channel.Id)

		message := model.NewMessage(channel.TeamId, channel.Id, user.Id, model.ACTION_USER_ADDED)
		PublishAndForget(message)
	}()

	return newMember, nil
}
Example #21
0
func sendNotifications(c *Context, post *model.Post, team *model.Team, channel *model.Channel, profileMap map[string]*model.User, members []model.ChannelMember) {
	var channelName string
	var bodyText string
	var subjectText string

	var mentionedUsers []string

	if _, ok := profileMap[post.UserId]; !ok {
		l4g.Error(utils.T("api.post.send_notifications_and_forget.user_id.error"), post.UserId)
		return
	}
	senderName := profileMap[post.UserId].Username

	toEmailMap := make(map[string]bool)

	if channel.Type == model.CHANNEL_DIRECT {

		var otherUserId string
		if userIds := strings.Split(channel.Name, "__"); userIds[0] == post.UserId {
			otherUserId = userIds[1]
			channelName = profileMap[userIds[1]].Username
		} else {
			otherUserId = userIds[0]
			channelName = profileMap[userIds[0]].Username
		}

		otherUser := profileMap[otherUserId]
		sendEmail := true
		if _, ok := otherUser.NotifyProps["email"]; ok && otherUser.NotifyProps["email"] == "false" {
			sendEmail = false
		}
		if sendEmail && (otherUser.IsOffline() || otherUser.IsAway()) {
			toEmailMap[otherUserId] = true
		}

	} else {
		// Find out who is a member of the channel, only keep those profiles
		tempProfileMap := make(map[string]*model.User)
		for _, member := range members {
			tempProfileMap[member.UserId] = profileMap[member.UserId]
		}

		profileMap = tempProfileMap

		// Build map for keywords
		keywordMap := make(map[string][]string)
		for _, profile := range profileMap {
			if len(profile.NotifyProps["mention_keys"]) > 0 {

				// Add all the user's mention keys
				splitKeys := strings.Split(profile.NotifyProps["mention_keys"], ",")
				for _, k := range splitKeys {
					keywordMap[k] = append(keywordMap[strings.ToLower(k)], profile.Id)
				}
			}

			// If turned on, add the user's case sensitive first name
			if profile.NotifyProps["first_name"] == "true" {
				keywordMap[profile.FirstName] = append(keywordMap[profile.FirstName], profile.Id)
			}

			// Add @all to keywords if user has them turned on
			// if profile.NotifyProps["all"] == "true" {
			// 	keywordMap["@all"] = append(keywordMap["@all"], profile.Id)
			// }

			// Add @channel to keywords if user has them turned on
			if profile.NotifyProps["channel"] == "true" {
				keywordMap["@channel"] = append(keywordMap["@channel"], profile.Id)
			}
		}

		// Build a map as a list of unique user_ids that are mentioned in this post
		splitF := func(c rune) bool {
			return model.SplitRunes[c]
		}
		splitMessage := strings.Fields(post.Message)
		for _, word := range splitMessage {
			var userIds []string

			// Non-case-sensitive check for regular keys
			if ids, match := keywordMap[strings.ToLower(word)]; match {
				userIds = append(userIds, ids...)
			}

			// Case-sensitive check for first name
			if ids, match := keywordMap[word]; match {
				userIds = append(userIds, ids...)
			}

			if len(userIds) == 0 {
				// No matches were found with the string split just on whitespace so try further splitting
				// the message on punctuation
				splitWords := strings.FieldsFunc(word, splitF)

				for _, splitWord := range splitWords {
					// Non-case-sensitive check for regular keys
					if ids, match := keywordMap[strings.ToLower(splitWord)]; match {
						userIds = append(userIds, ids...)
					}

					// Case-sensitive check for first name
					if ids, match := keywordMap[splitWord]; match {
						userIds = append(userIds, ids...)
					}
				}
			}

			for _, userId := range userIds {
				if post.UserId == userId && post.Props["from_webhook"] != "true" {
					continue
				}
				sendEmail := true
				if _, ok := profileMap[userId].NotifyProps["email"]; ok && profileMap[userId].NotifyProps["email"] == "false" {
					sendEmail = false
				}
				if sendEmail && (profileMap[userId].IsAway() || profileMap[userId].IsOffline()) {
					toEmailMap[userId] = true
				} else {
					toEmailMap[userId] = false
				}
			}
		}

		for id := range toEmailMap {
			updateMentionCountAndForget(post.ChannelId, id)
		}
	}

	if len(toEmailMap) != 0 {
		mentionedUsers = make([]string, 0, len(toEmailMap))
		for k := range toEmailMap {
			mentionedUsers = append(mentionedUsers, k)
		}

		teamURL := c.GetSiteURL() + "/" + team.Name

		// Build and send the emails
		tm := time.Unix(post.CreateAt/1000, 0)

		for id, doSend := range toEmailMap {

			if !doSend {
				continue
			}

			// skip if inactive
			if profileMap[id].DeleteAt > 0 {
				continue
			}

			userLocale := utils.GetUserTranslations(profileMap[id].Locale)

			if channel.Type == model.CHANNEL_DIRECT {
				bodyText = userLocale("api.post.send_notifications_and_forget.message_body")
				subjectText = userLocale("api.post.send_notifications_and_forget.message_subject")
			} else {
				bodyText = userLocale("api.post.send_notifications_and_forget.mention_body")
				subjectText = userLocale("api.post.send_notifications_and_forget.mention_subject")
				channelName = channel.DisplayName
			}

			month := userLocale(tm.Month().String())
			day := fmt.Sprintf("%d", tm.Day())
			year := fmt.Sprintf("%d", tm.Year())
			zone, _ := tm.Zone()

			subjectPage := utils.NewHTMLTemplate("post_subject", profileMap[id].Locale)
			subjectPage.Props["Subject"] = userLocale("api.templates.post_subject",
				map[string]interface{}{"SubjectText": subjectText, "TeamDisplayName": team.DisplayName,
					"Month": month[:3], "Day": day, "Year": year})
			subjectPage.Props["SiteName"] = utils.Cfg.TeamSettings.SiteName

			bodyPage := utils.NewHTMLTemplate("post_body", profileMap[id].Locale)
			bodyPage.Props["SiteURL"] = c.GetSiteURL()
			bodyPage.Props["PostMessage"] = model.ClearMentionTags(post.Message)
			bodyPage.Props["TeamLink"] = teamURL + "/channels/" + channel.Name
			bodyPage.Props["BodyText"] = bodyText
			bodyPage.Props["Button"] = userLocale("api.templates.post_body.button")
			bodyPage.Html["Info"] = template.HTML(userLocale("api.templates.post_body.info",
				map[string]interface{}{"ChannelName": channelName, "SenderName": senderName,
					"Hour": fmt.Sprintf("%02d", tm.Hour()), "Minute": fmt.Sprintf("%02d", tm.Minute()),
					"TimeZone": zone, "Month": month, "Day": day}))

			// attempt to fill in a message body if the post doesn't have any text
			if len(strings.TrimSpace(bodyPage.Props["PostMessage"])) == 0 && len(post.Filenames) > 0 {
				// extract the filenames from their paths and determine what type of files are attached
				filenames := make([]string, len(post.Filenames))
				onlyImages := true
				for i, filename := range post.Filenames {
					var err error
					if filenames[i], err = url.QueryUnescape(filepath.Base(filename)); err != nil {
						// this should never error since filepath was escaped using url.QueryEscape
						filenames[i] = filepath.Base(filename)
					}

					ext := filepath.Ext(filename)
					onlyImages = onlyImages && model.IsFileExtImage(ext)
				}
				filenamesString := strings.Join(filenames, ", ")

				var attachmentPrefix string
				if onlyImages {
					attachmentPrefix = "Image"
				} else {
					attachmentPrefix = "File"
				}
				if len(post.Filenames) > 1 {
					attachmentPrefix += "s"
				}

				bodyPage.Props["PostMessage"] = userLocale("api.post.send_notifications_and_forget.sent",
					map[string]interface{}{"Prefix": attachmentPrefix, "Filenames": filenamesString})
			}

			if err := utils.SendMail(profileMap[id].Email, subjectPage.Render(), bodyPage.Render()); err != nil {
				l4g.Error(utils.T("api.post.send_notifications_and_forget.send.error"), profileMap[id].Email, err)
			}

			if *utils.Cfg.EmailSettings.SendPushNotifications {
				sessionChan := Srv.Store.Session().GetSessions(id)
				if result := <-sessionChan; result.Err != nil {
					l4g.Error(utils.T("api.post.send_notifications_and_forget.sessions.error"), id, result.Err)
				} else {
					sessions := result.Data.([]*model.Session)
					alreadySeen := make(map[string]string)

					pushServer := *utils.Cfg.EmailSettings.PushNotificationServer
					if pushServer == model.MHPNS && (!utils.IsLicensed || !*utils.License.Features.MHPNS) {
						l4g.Warn(utils.T("api.post.send_notifications_and_forget.push_notification.mhpnsWarn"))
					} else {
						for _, session := range sessions {
							if len(session.DeviceId) > 0 && alreadySeen[session.DeviceId] == "" &&
								(strings.HasPrefix(session.DeviceId, model.PUSH_NOTIFY_APPLE+":") || strings.HasPrefix(session.DeviceId, model.PUSH_NOTIFY_ANDROID+":")) {
								alreadySeen[session.DeviceId] = session.DeviceId

								msg := model.PushNotification{}
								if badge := <-Srv.Store.User().GetUnreadCount(id); badge.Err != nil {
									msg.Badge = 1
									l4g.Error(utils.T("store.sql_user.get_unread_count.app_error"), id, badge.Err)
								} else {
									msg.Badge = int(badge.Data.(int64))
								}
								msg.ServerId = utils.CfgDiagnosticId
								msg.ChannelId = channel.Id
								msg.ChannelName = channel.Name

								if strings.HasPrefix(session.DeviceId, model.PUSH_NOTIFY_APPLE+":") {
									msg.Platform = model.PUSH_NOTIFY_APPLE
									msg.DeviceId = strings.TrimPrefix(session.DeviceId, model.PUSH_NOTIFY_APPLE+":")
								} else if strings.HasPrefix(session.DeviceId, model.PUSH_NOTIFY_ANDROID+":") {
									msg.Platform = model.PUSH_NOTIFY_ANDROID
									msg.DeviceId = strings.TrimPrefix(session.DeviceId, model.PUSH_NOTIFY_ANDROID+":")
								}

								if *utils.Cfg.EmailSettings.PushNotificationContents == model.FULL_NOTIFICATION {
									if channel.Type == model.CHANNEL_DIRECT {
										msg.Category = model.CATEGORY_DM
										msg.Message = "@" + senderName + ": " + model.ClearMentionTags(post.Message)
									} else {
										msg.Message = "@" + senderName + " @ " + channelName + ": " + model.ClearMentionTags(post.Message)
									}
								} else {
									if channel.Type == model.CHANNEL_DIRECT {
										msg.Category = model.CATEGORY_DM
										msg.Message = senderName + userLocale("api.post.send_notifications_and_forget.push_message")
									} else {
										msg.Message = senderName + userLocale("api.post.send_notifications_and_forget.push_mention") + channelName
									}
								}

								tr := &http.Transport{
									TLSClientConfig: &tls.Config{InsecureSkipVerify: *utils.Cfg.ServiceSettings.EnableInsecureOutgoingConnections},
								}
								httpClient := &http.Client{Transport: tr}
								request, _ := http.NewRequest("POST", pushServer+model.API_URL_SUFFIX_V1+"/send_push", strings.NewReader(msg.ToJson()))

								l4g.Debug(utils.T("api.post.send_notifications_and_forget.push_notification.debug"), msg.DeviceId, msg.Message)
								if _, err := httpClient.Do(request); err != nil {
									l4g.Error(utils.T("api.post.send_notifications_and_forget.push_notification.error"), id, err)
								}
							}
						}
					}
				}
			}
		}
	}

	message := model.NewMessage(c.TeamId, post.ChannelId, post.UserId, model.ACTION_POSTED)
	message.Add("post", post.ToJson())
	message.Add("channel_type", channel.Type)

	if len(post.Filenames) != 0 {
		message.Add("otherFile", "true")

		for _, filename := range post.Filenames {
			ext := filepath.Ext(filename)
			if model.IsFileExtImage(ext) {
				message.Add("image", "true")
				break
			}
		}
	}

	if len(mentionedUsers) != 0 {
		message.Add("mentions", model.ArrayToJson(mentionedUsers))
	}

	PublishAndForget(message)
}
Example #22
0
func sendNotificationsAndForget(c *Context, post *model.Post, team *model.Team, channel *model.Channel) {

	go func() {
		// Get a list of user names (to be used as keywords) and ids for the given team
		uchan := Srv.Store.User().GetProfiles(c.Session.TeamId)
		echan := Srv.Store.Channel().GetMembers(post.ChannelId)

		var channelName string
		var bodyText string
		var subjectText string
		if channel.Type == model.CHANNEL_DIRECT {
			bodyText = "You have one new message."
			subjectText = "New Direct Message"
		} else {
			bodyText = "You have one new mention."
			subjectText = "New Mention"
			channelName = channel.DisplayName
		}

		var mentionedUsers []string

		if result := <-uchan; result.Err != nil {
			l4g.Error("Failed to retrieve user profiles team_id=%v, err=%v", c.Session.TeamId, result.Err)
			return
		} else {
			profileMap := result.Data.(map[string]*model.User)

			if _, ok := profileMap[post.UserId]; !ok {
				l4g.Error("Post user_id not returned by GetProfiles user_id=%v", post.UserId)
				return
			}
			senderName := profileMap[post.UserId].Username

			toEmailMap := make(map[string]bool)

			if channel.Type == model.CHANNEL_DIRECT {

				var otherUserId string
				if userIds := strings.Split(channel.Name, "__"); userIds[0] == post.UserId {
					otherUserId = userIds[1]
					channelName = profileMap[userIds[1]].Username
				} else {
					otherUserId = userIds[0]
					channelName = profileMap[userIds[0]].Username
				}

				otherUser := profileMap[otherUserId]
				sendEmail := true
				if _, ok := otherUser.NotifyProps["email"]; ok && otherUser.NotifyProps["email"] == "false" {
					sendEmail = false
				}
				if sendEmail && (otherUser.IsOffline() || otherUser.IsAway()) {
					toEmailMap[otherUserId] = true
				}

			} else {

				// Find out who is a member of the channel, only keep those profiles
				if eResult := <-echan; eResult.Err != nil {
					l4g.Error("Failed to get channel members channel_id=%v err=%v", post.ChannelId, eResult.Err.Message)
					return
				} else {
					tempProfileMap := make(map[string]*model.User)
					members := eResult.Data.([]model.ChannelMember)
					for _, member := range members {
						tempProfileMap[member.UserId] = profileMap[member.UserId]
					}

					profileMap = tempProfileMap
				}

				// Build map for keywords
				keywordMap := make(map[string][]string)
				for _, profile := range profileMap {
					if len(profile.NotifyProps["mention_keys"]) > 0 {

						// Add all the user's mention keys
						splitKeys := strings.Split(profile.NotifyProps["mention_keys"], ",")
						for _, k := range splitKeys {
							keywordMap[k] = append(keywordMap[strings.ToLower(k)], profile.Id)
						}
					}

					// If turned on, add the user's case sensitive first name
					if profile.NotifyProps["first_name"] == "true" {
						keywordMap[profile.FirstName] = append(keywordMap[profile.FirstName], profile.Id)
					}

					// Add @all to keywords if user has them turned on
					// if profile.NotifyProps["all"] == "true" {
					// 	keywordMap["@all"] = append(keywordMap["@all"], profile.Id)
					// }

					// Add @channel to keywords if user has them turned on
					if profile.NotifyProps["channel"] == "true" {
						keywordMap["@channel"] = append(keywordMap["@channel"], profile.Id)
					}
				}

				// Build a map as a list of unique user_ids that are mentioned in this post
				splitF := func(c rune) bool {
					return model.SplitRunes[c]
				}
				splitMessage := strings.Fields(post.Message)
				for _, word := range splitMessage {
					var userIds []string

					// Non-case-sensitive check for regular keys
					if ids, match := keywordMap[strings.ToLower(word)]; match {
						userIds = append(userIds, ids...)
					}

					// Case-sensitive check for first name
					if ids, match := keywordMap[word]; match {
						userIds = append(userIds, ids...)
					}

					if len(userIds) == 0 {
						// No matches were found with the string split just on whitespace so try further splitting
						// the message on punctuation
						splitWords := strings.FieldsFunc(word, splitF)

						for _, splitWord := range splitWords {
							// Non-case-sensitive check for regular keys
							if ids, match := keywordMap[strings.ToLower(splitWord)]; match {
								userIds = append(userIds, ids...)
							}

							// Case-sensitive check for first name
							if ids, match := keywordMap[splitWord]; match {
								userIds = append(userIds, ids...)
							}
						}
					}

					for _, userId := range userIds {
						if post.UserId == userId {
							continue
						}
						sendEmail := true
						if _, ok := profileMap[userId].NotifyProps["email"]; ok && profileMap[userId].NotifyProps["email"] == "false" {
							sendEmail = false
						}
						if sendEmail && (profileMap[userId].IsAway() || profileMap[userId].IsOffline()) {
							toEmailMap[userId] = true
						} else {
							toEmailMap[userId] = false
						}
					}
				}

				for id := range toEmailMap {
					updateMentionCountAndForget(post.ChannelId, id)
				}
			}

			if len(toEmailMap) != 0 {
				mentionedUsers = make([]string, 0, len(toEmailMap))
				for k := range toEmailMap {
					mentionedUsers = append(mentionedUsers, k)
				}

				teamURL := c.GetSiteURL() + "/" + team.Name

				// Build and send the emails
				tm := time.Unix(post.CreateAt/1000, 0)

				subjectPage := NewServerTemplatePage("post_subject")
				subjectPage.Props["SiteURL"] = c.GetSiteURL()
				subjectPage.Props["TeamDisplayName"] = team.DisplayName
				subjectPage.Props["SubjectText"] = subjectText
				subjectPage.Props["Month"] = tm.Month().String()[:3]
				subjectPage.Props["Day"] = fmt.Sprintf("%d", tm.Day())
				subjectPage.Props["Year"] = fmt.Sprintf("%d", tm.Year())

				for id, doSend := range toEmailMap {

					if !doSend {
						continue
					}

					// skip if inactive
					if profileMap[id].DeleteAt > 0 {
						continue
					}

					bodyPage := NewServerTemplatePage("post_body")
					bodyPage.Props["SiteURL"] = c.GetSiteURL()
					bodyPage.Props["Nickname"] = profileMap[id].FirstName
					bodyPage.Props["TeamDisplayName"] = team.DisplayName
					bodyPage.Props["ChannelName"] = channelName
					bodyPage.Props["BodyText"] = bodyText
					bodyPage.Props["SenderName"] = senderName
					bodyPage.Props["Hour"] = fmt.Sprintf("%02d", tm.Hour())
					bodyPage.Props["Minute"] = fmt.Sprintf("%02d", tm.Minute())
					bodyPage.Props["Month"] = tm.Month().String()[:3]
					bodyPage.Props["Day"] = fmt.Sprintf("%d", tm.Day())
					bodyPage.Props["TimeZone"], _ = tm.Zone()
					bodyPage.Props["PostMessage"] = model.ClearMentionTags(post.Message)
					bodyPage.Props["TeamLink"] = teamURL + "/channels/" + channel.Name

					// attempt to fill in a message body if the post doesn't have any text
					if len(strings.TrimSpace(bodyPage.Props["PostMessage"])) == 0 && len(post.Filenames) > 0 {
						// extract the filenames from their paths and determine what type of files are attached
						filenames := make([]string, len(post.Filenames))
						onlyImages := true
						for i, filename := range post.Filenames {
							var err error
							if filenames[i], err = url.QueryUnescape(filepath.Base(filename)); err != nil {
								// this should never error since filepath was escaped using url.QueryEscape
								filenames[i] = filepath.Base(filename)
							}

							ext := filepath.Ext(filename)
							onlyImages = onlyImages && model.IsFileExtImage(ext)
						}
						filenamesString := strings.Join(filenames, ", ")

						var attachmentPrefix string
						if onlyImages {
							attachmentPrefix = "Image"
						} else {
							attachmentPrefix = "File"
						}
						if len(post.Filenames) > 1 {
							attachmentPrefix += "s"
						}

						bodyPage.Props["PostMessage"] = fmt.Sprintf("%s: %s sent", attachmentPrefix, filenamesString)
					}

					if err := utils.SendMail(profileMap[id].Email, subjectPage.Render(), bodyPage.Render()); err != nil {
						l4g.Error("Failed to send mention email successfully email=%v err=%v", profileMap[id].Email, err)
					}

					if *utils.Cfg.EmailSettings.SendPushNotifications {
						sessionChan := Srv.Store.Session().GetSessions(id)
						if result := <-sessionChan; result.Err != nil {
							l4g.Error("Failed to retrieve sessions in notifications id=%v, err=%v", id, result.Err)
						} else {
							sessions := result.Data.([]*model.Session)
							alreadySeen := make(map[string]string)

							for _, session := range sessions {
								if len(session.DeviceId) > 0 && alreadySeen[session.DeviceId] == "" && strings.HasPrefix(session.DeviceId, "apple:") {
									alreadySeen[session.DeviceId] = session.DeviceId

									msg := model.PushNotification{}
									msg.Platform = model.PUSH_NOTIFY_APPLE
									msg.Badge = 1
									msg.DeviceId = strings.TrimPrefix(session.DeviceId, "apple:")
									msg.ServerId = utils.CfgDiagnosticId

									if channel.Type == model.CHANNEL_DIRECT {
										msg.Message = senderName + " sent you a direct message"
									} else {
										msg.Message = senderName + " mentioned you in " + channelName
									}

									httpClient := http.Client{}
									request, _ := http.NewRequest("POST", *utils.Cfg.EmailSettings.PushNotificationServer+"/api/v1/send_push", strings.NewReader(msg.ToJson()))

									l4g.Debug("Sending push notification to " + msg.DeviceId + " with msg of '" + msg.Message + "'")
									if _, err := httpClient.Do(request); err != nil {
										l4g.Error("Failed to send push notificationid=%v, err=%v", id, err)
									}
								}
							}
						}
					}
				}
			}
		}

		message := model.NewMessage(c.Session.TeamId, post.ChannelId, post.UserId, model.ACTION_POSTED)
		message.Add("post", post.ToJson())

		if len(post.Filenames) != 0 {
			message.Add("otherFile", "true")

			for _, filename := range post.Filenames {
				ext := filepath.Ext(filename)
				if model.IsFileExtImage(ext) {
					message.Add("image", "true")
					break
				}
			}
		}

		if len(mentionedUsers) != 0 {
			message.Add("mentions", model.ArrayToJson(mentionedUsers))
		}

		PublishAndForget(message)
	}()
}
Example #23
0
func deletePost(c *Context, w http.ResponseWriter, r *http.Request) {
	params := mux.Vars(r)

	channelId := params["channel_id"]
	if len(channelId) != 26 {
		c.SetInvalidParam("deletePost", "channelId")
		return
	}

	postId := params["post_id"]
	if len(postId) != 26 {
		c.SetInvalidParam("deletePost", "postId")
		return
	}

	cchan := Srv.Store.Channel().CheckPermissionsTo(c.TeamId, channelId, c.Session.UserId)
	pchan := Srv.Store.Post().Get(postId)

	if result := <-pchan; result.Err != nil {
		c.Err = result.Err
		return
	} else {

		post := result.Data.(*model.PostList).Posts[postId]

		if !c.HasPermissionsToChannel(cchan, "deletePost") && !c.IsTeamAdmin() {
			return
		}

		if post == nil {
			c.SetInvalidParam("deletePost", "postId")
			return
		}

		if post.ChannelId != channelId {
			c.Err = model.NewLocAppError("deletePost", "api.post.delete_post.permissions.app_error", nil, "")
			c.Err.StatusCode = http.StatusForbidden
			return
		}

		if post.UserId != c.Session.UserId && !c.IsTeamAdmin() {
			c.Err = model.NewLocAppError("deletePost", "api.post.delete_post.permissions.app_error", nil, "")
			c.Err.StatusCode = http.StatusForbidden
			return
		}

		if dresult := <-Srv.Store.Post().Delete(postId, model.GetMillis()); dresult.Err != nil {
			c.Err = dresult.Err
			return
		}

		message := model.NewMessage(c.TeamId, post.ChannelId, c.Session.UserId, model.ACTION_POST_DELETED)
		message.Add("post", post.ToJson())

		PublishAndForget(message)
		DeletePostFilesAndForget(c.TeamId, post)

		result := make(map[string]string)
		result["id"] = postId
		w.Write([]byte(model.MapToJson(result)))
	}
}
Example #24
0
func sendNotifications(c *Context, post *model.Post, team *model.Team, channel *model.Channel, profileMap map[string]*model.User, members []model.ChannelMember) {
	if _, ok := profileMap[post.UserId]; !ok {
		l4g.Error(utils.T("api.post.send_notifications_and_forget.user_id.error"), post.UserId)
		return
	}

	mentionedUserIds := make(map[string]bool)
	alwaysNotifyUserIds := []string{}

	if channel.Type == model.CHANNEL_DIRECT {

		var otherUserId string
		if userIds := strings.Split(channel.Name, "__"); userIds[0] == post.UserId {
			otherUserId = userIds[1]
		} else {
			otherUserId = userIds[0]
		}

		mentionedUserIds[otherUserId] = true

	} else {
		// Find out who is a member of the channel, only keep those profiles
		tempProfileMap := make(map[string]*model.User)
		for _, member := range members {
			if profile, ok := profileMap[member.UserId]; ok {
				tempProfileMap[member.UserId] = profile
			}
		}

		profileMap = tempProfileMap

		// Build map for keywords
		keywordMap := make(map[string][]string)
		for _, profile := range profileMap {
			if len(profile.NotifyProps["mention_keys"]) > 0 {

				// Add all the user's mention keys
				splitKeys := strings.Split(profile.NotifyProps["mention_keys"], ",")
				for _, k := range splitKeys {
					keywordMap[k] = append(keywordMap[strings.ToLower(k)], profile.Id)
				}
			}

			// If turned on, add the user's case sensitive first name
			if profile.NotifyProps["first_name"] == "true" {
				keywordMap[profile.FirstName] = append(keywordMap[profile.FirstName], profile.Id)
			}

			// Add @channel and @all to keywords if user has them turned on
			if profile.NotifyProps["channel"] == "true" {
				keywordMap["@channel"] = append(keywordMap["@channel"], profile.Id)
				keywordMap["@all"] = append(keywordMap["@all"], profile.Id)
			}

			if profile.NotifyProps["push"] == model.USER_NOTIFY_ALL &&
				(post.UserId != profile.Id || post.Props["from_webhook"] == "true") &&
				!post.IsSystemMessage() {
				alwaysNotifyUserIds = append(alwaysNotifyUserIds, profile.Id)
			}
		}

		// Build a map as a list of unique user_ids that are mentioned in this post
		splitF := func(c rune) bool {
			return model.SplitRunes[c]
		}
		splitMessage := strings.Fields(post.Message)
		for _, word := range splitMessage {
			var userIds []string

			// Non-case-sensitive check for regular keys
			if ids, match := keywordMap[strings.ToLower(word)]; match {
				userIds = append(userIds, ids...)
			}

			// Case-sensitive check for first name
			if ids, match := keywordMap[word]; match {
				userIds = append(userIds, ids...)
			}

			if len(userIds) == 0 {
				// No matches were found with the string split just on whitespace so try further splitting
				// the message on punctuation
				splitWords := strings.FieldsFunc(word, splitF)

				for _, splitWord := range splitWords {
					// Non-case-sensitive check for regular keys
					if ids, match := keywordMap[strings.ToLower(splitWord)]; match {
						userIds = append(userIds, ids...)
					}

					// Case-sensitive check for first name
					if ids, match := keywordMap[splitWord]; match {
						userIds = append(userIds, ids...)
					}
				}
			}

			for _, userId := range userIds {
				if post.UserId == userId && post.Props["from_webhook"] != "true" {
					continue
				}

				mentionedUserIds[userId] = true
			}
		}

		for id := range mentionedUserIds {
			go updateMentionCount(post.ChannelId, id)
		}
	}

	mentionedUsersList := make([]string, 0, len(mentionedUserIds))

	senderName := ""
	if post.IsSystemMessage() {
		senderName = c.T("system.message.name")
	} else if profile, ok := profileMap[post.UserId]; ok {
		senderName = profile.Username
	}

	for id := range mentionedUserIds {
		mentionedUsersList = append(mentionedUsersList, id)
	}

	if utils.Cfg.EmailSettings.SendEmailNotifications {
		for _, id := range mentionedUsersList {
			userAllowsEmails := profileMap[id].NotifyProps["email"] != "false"

			if userAllowsEmails && (profileMap[id].IsAway() || profileMap[id].IsOffline()) {
				sendNotificationEmail(c, post, profileMap[id], channel, team, senderName)
			}
		}
	}

	sendPushNotifications := false
	if *utils.Cfg.EmailSettings.SendPushNotifications {
		pushServer := *utils.Cfg.EmailSettings.PushNotificationServer
		if pushServer == model.MHPNS && (!utils.IsLicensed || !*utils.License.Features.MHPNS) {
			l4g.Warn(utils.T("api.post.send_notifications_and_forget.push_notification.mhpnsWarn"))
			sendPushNotifications = false
		} else {
			sendPushNotifications = true
		}
	}

	if sendPushNotifications {
		for _, id := range mentionedUsersList {
			if profileMap[id].NotifyProps["push"] != "none" {
				sendPushNotification(post, profileMap[id], channel, senderName, true)
			}
		}
		for _, id := range alwaysNotifyUserIds {
			if _, ok := mentionedUserIds[id]; !ok {
				sendPushNotification(post, profileMap[id], channel, senderName, false)
			}
		}
	}

	message := model.NewMessage(c.TeamId, post.ChannelId, post.UserId, model.ACTION_POSTED)
	message.Add("post", post.ToJson())
	message.Add("channel_type", channel.Type)
	message.Add("channel_display_name", channel.DisplayName)
	message.Add("sender_name", senderName)
	message.Add("team_id", team.Id)

	if len(post.Filenames) != 0 {
		message.Add("otherFile", "true")

		for _, filename := range post.Filenames {
			ext := filepath.Ext(filename)
			if model.IsFileExtImage(ext) {
				message.Add("image", "true")
				break
			}
		}
	}

	if len(mentionedUsersList) != 0 {
		message.Add("mentions", model.ArrayToJson(mentionedUsersList))
	}

	go Publish(message)
}
Example #25
0
func fireAndForgetNotifications(post *model.Post, teamId, siteURL string) {

	go func() {
		// Get a list of user names (to be used as keywords) and ids for the given team
		uchan := Srv.Store.User().GetProfiles(teamId)
		echan := Srv.Store.Channel().GetMembers(post.ChannelId)
		cchan := Srv.Store.Channel().Get(post.ChannelId)
		tchan := Srv.Store.Team().Get(teamId)

		var channel *model.Channel
		var channelName string
		var bodyText string
		var subjectText string
		if result := <-cchan; result.Err != nil {
			l4g.Error("Failed to retrieve channel channel_id=%v, err=%v", post.ChannelId, result.Err)
			return
		} else {
			channel = result.Data.(*model.Channel)
			if channel.Type == model.CHANNEL_DIRECT {
				bodyText = "You have one new message."
				subjectText = "New Direct Message"
			} else {
				bodyText = "You have one new mention."
				subjectText = "New Mention"
				channelName = channel.DisplayName
			}
		}

		var mentionedUsers []string

		if result := <-uchan; result.Err != nil {
			l4g.Error("Failed to retrieve user profiles team_id=%v, err=%v", teamId, result.Err)
			return
		} else {
			profileMap := result.Data.(map[string]*model.User)

			if _, ok := profileMap[post.UserId]; !ok {
				l4g.Error("Post user_id not returned by GetProfiles user_id=%v", post.UserId)
				return
			}
			senderName := profileMap[post.UserId].Username

			toEmailMap := make(map[string]bool)

			if channel.Type == model.CHANNEL_DIRECT {

				var otherUserId string
				if userIds := strings.Split(channel.Name, "__"); userIds[0] == post.UserId {
					otherUserId = userIds[1]
					channelName = profileMap[userIds[1]].Username
				} else {
					otherUserId = userIds[0]
					channelName = profileMap[userIds[0]].Username
				}

				otherUser := profileMap[otherUserId]
				sendEmail := true
				if _, ok := otherUser.NotifyProps["email"]; ok && otherUser.NotifyProps["email"] == "false" {
					sendEmail = false
				}
				if sendEmail && (otherUser.IsOffline() || otherUser.IsAway()) {
					toEmailMap[otherUserId] = true
				}

			} else {

				// Find out who is a member of the channel, only keep those profiles
				if eResult := <-echan; eResult.Err != nil {
					l4g.Error("Failed to get channel members channel_id=%v err=%v", post.ChannelId, eResult.Err.Message)
					return
				} else {
					tempProfileMap := make(map[string]*model.User)
					members := eResult.Data.([]model.ChannelMember)
					for _, member := range members {
						tempProfileMap[member.UserId] = profileMap[member.UserId]
					}

					profileMap = tempProfileMap
				}

				// Build map for keywords
				keywordMap := make(map[string][]string)
				for _, profile := range profileMap {
					if len(profile.NotifyProps["mention_keys"]) > 0 {

						// Add all the user's mention keys
						splitKeys := strings.Split(profile.NotifyProps["mention_keys"], ",")
						for _, k := range splitKeys {
							keywordMap[k] = append(keywordMap[strings.ToLower(k)], profile.Id)
						}
					}

					// If turned on, add the user's case sensitive first name
					if profile.NotifyProps["first_name"] == "true" {
						keywordMap[profile.FirstName] = append(keywordMap[profile.FirstName], profile.Id)
					}

					// Add @all to keywords if user has them turned on
					if profile.NotifyProps["all"] == "true" {
						keywordMap["@all"] = append(keywordMap["@all"], profile.Id)
					}

					// Add @channel to keywords if user has them turned on
					if profile.NotifyProps["channel"] == "true" {
						keywordMap["@channel"] = append(keywordMap["@channel"], profile.Id)
					}
				}

				// Build a map as a list of unique user_ids that are mentioned in this post
				splitF := func(c rune) bool {
					return model.SplitRunes[c]
				}
				splitMessage := strings.FieldsFunc(post.Message, splitF)
				for _, word := range splitMessage {

					// Non-case-sensitive check for regular keys
					userIds1, keyMatch := keywordMap[strings.ToLower(word)]

					// Case-sensitive check for first name
					userIds2, firstNameMatch := keywordMap[word]

					userIds := append(userIds1, userIds2...)

					// If one of the non-case-senstive keys or the first name matches the word
					//  then we add en entry to the sendEmail map
					if keyMatch || firstNameMatch {
						for _, userId := range userIds {
							if post.UserId == userId {
								continue
							}
							sendEmail := true
							if _, ok := profileMap[userId].NotifyProps["email"]; ok && profileMap[userId].NotifyProps["email"] == "false" {
								sendEmail = false
							}
							if sendEmail && (profileMap[userId].IsAway() || profileMap[userId].IsOffline()) {
								toEmailMap[userId] = true
							} else {
								toEmailMap[userId] = false
							}
						}
					}
				}

				for id := range toEmailMap {
					fireAndForgetMentionUpdate(post.ChannelId, id)
				}
			}

			if len(toEmailMap) != 0 {
				mentionedUsers = make([]string, 0, len(toEmailMap))
				for k := range toEmailMap {
					mentionedUsers = append(mentionedUsers, k)
				}

				var teamDisplayName string
				var teamURL string
				if result := <-tchan; result.Err != nil {
					l4g.Error("Failed to retrieve team team_id=%v, err=%v", teamId, result.Err)
					return
				} else {
					teamDisplayName = result.Data.(*model.Team).DisplayName
					teamURL = siteURL + "/" + result.Data.(*model.Team).Name
				}

				// Build and send the emails
				location, _ := time.LoadLocation("UTC")
				tm := time.Unix(post.CreateAt/1000, 0).In(location)

				subjectPage := NewServerTemplatePage("post_subject")
				subjectPage.Props["SiteURL"] = siteURL
				subjectPage.Props["TeamDisplayName"] = teamDisplayName
				subjectPage.Props["SubjectText"] = subjectText
				subjectPage.Props["Month"] = tm.Month().String()[:3]
				subjectPage.Props["Day"] = fmt.Sprintf("%d", tm.Day())
				subjectPage.Props["Year"] = fmt.Sprintf("%d", tm.Year())

				for id, doSend := range toEmailMap {

					if !doSend {
						continue
					}

					// skip if inactive
					if profileMap[id].DeleteAt > 0 {
						continue
					}

					bodyPage := NewServerTemplatePage("post_body")
					bodyPage.Props["SiteURL"] = siteURL
					bodyPage.Props["Nickname"] = profileMap[id].FirstName
					bodyPage.Props["TeamDisplayName"] = teamDisplayName
					bodyPage.Props["ChannelName"] = channelName
					bodyPage.Props["BodyText"] = bodyText
					bodyPage.Props["SenderName"] = senderName
					bodyPage.Props["Hour"] = fmt.Sprintf("%02d", tm.Hour())
					bodyPage.Props["Minute"] = fmt.Sprintf("%02d", tm.Minute())
					bodyPage.Props["Month"] = tm.Month().String()[:3]
					bodyPage.Props["Day"] = fmt.Sprintf("%d", tm.Day())
					bodyPage.Props["PostMessage"] = model.ClearMentionTags(post.Message)
					bodyPage.Props["TeamLink"] = teamURL + "/channels/" + channel.Name

					// attempt to fill in a message body if the post doesn't have any text
					if len(strings.TrimSpace(bodyPage.Props["PostMessage"])) == 0 && len(post.Filenames) > 0 {
						// extract the filenames from their paths and determine what type of files are attached
						filenames := make([]string, len(post.Filenames))
						onlyImages := true
						for i, filename := range post.Filenames {
							var err error
							if filenames[i], err = url.QueryUnescape(filepath.Base(filename)); err != nil {
								// this should never error since filepath was escaped using url.QueryEscape
								filenames[i] = filepath.Base(filename)
							}

							ext := filepath.Ext(filename)
							onlyImages = onlyImages && model.IsFileExtImage(ext)
						}
						filenamesString := strings.Join(filenames, ", ")

						var attachmentPrefix string
						if onlyImages {
							attachmentPrefix = "Image"
						} else {
							attachmentPrefix = "File"
						}
						if len(post.Filenames) > 1 {
							attachmentPrefix += "s"
						}

						bodyPage.Props["PostMessage"] = fmt.Sprintf("%s: %s sent", attachmentPrefix, filenamesString)
					}

					if err := utils.SendMail(profileMap[id].Email, subjectPage.Render(), bodyPage.Render()); err != nil {
						l4g.Error("Failed to send mention email successfully email=%v err=%v", profileMap[id].Email, err)
					}

					if len(utils.Cfg.EmailSettings.ApplePushServer) > 0 {
						sessionChan := Srv.Store.Session().GetSessions(id)
						if result := <-sessionChan; result.Err != nil {
							l4g.Error("Failed to retrieve sessions in notifications id=%v, err=%v", id, result.Err)
						} else {
							sessions := result.Data.([]*model.Session)
							alreadySeen := make(map[string]string)

							for _, session := range sessions {
								if len(session.DeviceId) > 0 && alreadySeen[session.DeviceId] == "" {

									alreadySeen[session.DeviceId] = session.DeviceId

									utils.FireAndForgetSendAppleNotify(session.DeviceId, subjectPage.Render(), 1)
								}
							}
						}
					}
				}
			}
		}

		message := model.NewMessage(teamId, post.ChannelId, post.UserId, model.ACTION_POSTED)
		message.Add("post", post.ToJson())

		if len(post.Filenames) != 0 {
			message.Add("otherFile", "true")

			for _, filename := range post.Filenames {
				ext := filepath.Ext(filename)
				if model.IsFileExtImage(ext) {
					message.Add("image", "true")
					break
				}
			}
		}

		if len(mentionedUsers) != 0 {
			message.Add("mentions", model.ArrayToJson(mentionedUsers))
		}

		PublishAndForget(message)
	}()
}
Example #26
0
func fireAndForgetNotifications(post *model.Post, teamId, teamUrl string) {

	go func() {
		// Get a list of user names (to be used as keywords) and ids for the given team
		uchan := Srv.Store.User().GetProfiles(teamId)
		echan := Srv.Store.Channel().GetMembers(post.ChannelId)
		cchan := Srv.Store.Channel().Get(post.ChannelId)
		tchan := Srv.Store.Team().Get(teamId)

		var channel *model.Channel
		var channelName string
		var bodyText string
		var subjectText string
		if result := <-cchan; result.Err != nil {
			l4g.Error("Failed to retrieve channel channel_id=%v, err=%v", post.ChannelId, result.Err)
			return
		} else {
			channel = result.Data.(*model.Channel)
			if channel.Type == model.CHANNEL_DIRECT {
				bodyText = "You have one new message."
				subjectText = "New Direct Message"
			} else {
				bodyText = "You have one new mention."
				subjectText = "New Mention"
				channelName = channel.DisplayName
			}
		}

		var mentionedUsers []string

		if result := <-uchan; result.Err != nil {
			l4g.Error("Failed to retrieve user profiles team_id=%v, err=%v", teamId, result.Err)
			return
		} else {
			profileMap := result.Data.(map[string]*model.User)

			if _, ok := profileMap[post.UserId]; !ok {
				l4g.Error("Post user_id not returned by GetProfiles user_id=%v", post.UserId)
				return
			}
			senderName := profileMap[post.UserId].Username

			toEmailMap := make(map[string]bool)

			if channel.Type == model.CHANNEL_DIRECT {

				var otherUserId string
				if userIds := strings.Split(channel.Name, "__"); userIds[0] == post.UserId {
					otherUserId = userIds[1]
					channelName = profileMap[userIds[1]].Username
				} else {
					otherUserId = userIds[0]
					channelName = profileMap[userIds[0]].Username
				}

				otherUser := profileMap[otherUserId]
				sendEmail := true
				if _, ok := otherUser.NotifyProps["email"]; ok && otherUser.NotifyProps["email"] == "false" {
					sendEmail = false
				}
				if sendEmail && (otherUser.IsOffline() || otherUser.IsAway()) {
					toEmailMap[otherUserId] = true
				}

			} else {

				// Find out who is a member of the channel only keep those profiles
				if eResult := <-echan; eResult.Err != nil {
					l4g.Error("Failed to get channel members channel_id=%v err=%v", post.ChannelId, eResult.Err.Message)
					return
				} else {
					tempProfileMap := make(map[string]*model.User)
					members := eResult.Data.([]model.ChannelMember)
					for _, member := range members {
						tempProfileMap[member.UserId] = profileMap[member.UserId]
					}

					profileMap = tempProfileMap
				}

				// Build map for keywords
				keywordMap := make(map[string][]string)
				for _, profile := range profileMap {
					if len(profile.NotifyProps["mention_keys"]) > 0 {

						// Add all the user's mention keys
						splitKeys := strings.Split(profile.NotifyProps["mention_keys"], ",")
						for _, k := range splitKeys {
							keywordMap[k] = append(keywordMap[strings.ToLower(k)], profile.Id)
						}

						// If turned on, add the user's case sensitive first name
						if profile.NotifyProps["first_name"] == "true" {
							splitName := strings.Split(profile.FullName, " ")
							if len(splitName) > 0 && splitName[0] != "" {
								keywordMap[splitName[0]] = append(keywordMap[splitName[0]], profile.Id)
							}
						}
					}
				}

				// Build a map as a list of unique user_ids that are mentioned in this post
				splitF := func(c rune) bool {
					return model.SplitRunes[c]
				}
				splitMessage := strings.FieldsFunc(strings.Replace(post.Message, "<br>", " ", -1), splitF)
				for _, word := range splitMessage {

					// Non-case-sensitive check for regular keys
					userIds1, keyMatch := keywordMap[strings.ToLower(word)]

					// Case-sensitive check for first name
					userIds2, firstNameMatch := keywordMap[word]

					userIds := append(userIds1, userIds2...)

					// If one of the non-case-senstive keys or the first name matches the word
					//  then we add en entry to the sendEmail map
					if keyMatch || firstNameMatch {
						for _, userId := range userIds {
							if post.UserId == userId {
								continue
							}
							sendEmail := true
							if _, ok := profileMap[userId].NotifyProps["email"]; ok && profileMap[userId].NotifyProps["email"] == "false" {
								sendEmail = false
							}
							if sendEmail && (profileMap[userId].IsAway() || profileMap[userId].IsOffline()) {
								toEmailMap[userId] = true
							} else {
								toEmailMap[userId] = false
							}
						}
					}
				}

				for id, _ := range toEmailMap {
					fireAndForgetMentionUpdate(post.ChannelId, id)
				}
			}

			if len(toEmailMap) != 0 {
				mentionedUsers = make([]string, 0, len(toEmailMap))
				for k := range toEmailMap {
					mentionedUsers = append(mentionedUsers, k)
				}

				var teamName string
				if result := <-tchan; result.Err != nil {
					l4g.Error("Failed to retrieve team team_id=%v, err=%v", teamId, result.Err)
					return
				} else {
					teamName = result.Data.(*model.Team).Name
				}

				// Build and send the emails
				location, _ := time.LoadLocation("UTC")
				tm := time.Unix(post.CreateAt/1000, 0).In(location)

				subjectPage := NewServerTemplatePage("post_subject", teamUrl)
				subjectPage.Props["TeamName"] = teamName
				subjectPage.Props["SubjectText"] = subjectText
				subjectPage.Props["Month"] = tm.Month().String()[:3]
				subjectPage.Props["Day"] = fmt.Sprintf("%d", tm.Day())
				subjectPage.Props["Year"] = fmt.Sprintf("%d", tm.Year())

				for id, doSend := range toEmailMap {

					if !doSend {
						continue
					}

					// skip if inactive
					if profileMap[id].DeleteAt > 0 {
						continue
					}

					firstName := strings.Split(profileMap[id].FullName, " ")[0]

					bodyPage := NewServerTemplatePage("post_body", teamUrl)
					bodyPage.Props["FullName"] = firstName
					bodyPage.Props["TeamName"] = teamName
					bodyPage.Props["ChannelName"] = channelName
					bodyPage.Props["BodyText"] = bodyText
					bodyPage.Props["SenderName"] = senderName
					bodyPage.Props["Hour"] = fmt.Sprintf("%02d", tm.Hour())
					bodyPage.Props["Minute"] = fmt.Sprintf("%02d", tm.Minute())
					bodyPage.Props["Month"] = tm.Month().String()[:3]
					bodyPage.Props["Day"] = fmt.Sprintf("%d", tm.Day())
					bodyPage.Props["PostMessage"] = model.ClearMentionTags(post.Message)
					bodyPage.Props["TeamLink"] = teamUrl + "/channels/" + channel.Name

					if err := utils.SendMail(profileMap[id].Email, subjectPage.Render(), bodyPage.Render()); err != nil {
						l4g.Error("Failed to send mention email successfully email=%v err=%v", profileMap[id].Email, err)
					}

					if len(utils.Cfg.EmailSettings.ApplePushServer) > 0 {
						sessionChan := Srv.Store.Session().GetSessions(id)
						if result := <-sessionChan; result.Err != nil {
							l4g.Error("Failed to retrieve sessions in notifications id=%v, err=%v", id, result.Err)
						} else {
							sessions := result.Data.([]*model.Session)
							alreadySeen := make(map[string]string)

							for _, session := range sessions {
								if len(session.DeviceId) > 0 && alreadySeen[session.DeviceId] == "" {

									alreadySeen[session.DeviceId] = session.DeviceId

									utils.FireAndForgetSendAppleNotify(session.DeviceId, subjectPage.Render(), 1)
								}
							}
						}
					}
				}
			}
		}

		message := model.NewMessage(teamId, post.ChannelId, post.UserId, model.ACTION_POSTED)
		message.Add("post", post.ToJson())
		if len(mentionedUsers) != 0 {
			message.Add("mentions", model.ArrayToJson(mentionedUsers))
		}

		store.PublishAndForget(message)
	}()
}
Example #27
0
func TestSocket(t *testing.T) {
	Setup()

	url := "ws://*****:*****@nowhere.com", Type: model.TEAM_OPEN}
	team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)

	user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "*****@*****.**", Nickname: "Corey Hulen", Password: "******"}
	user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
	store.Must(Srv.Store.User().VerifyEmail(user1.Id))
	Client.LoginByEmail(team.Domain, user1.Email, "pwd")

	channel1 := &model.Channel{DisplayName: "Test Web Scoket 1", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
	channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)

	channel2 := &model.Channel{DisplayName: "Test Web Scoket 2", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
	channel2 = Client.Must(Client.CreateChannel(channel2)).Data.(*model.Channel)

	header1 := http.Header{}
	header1.Set(model.HEADER_AUTH, "BEARER "+Client.AuthToken)

	c1, _, err := websocket.DefaultDialer.Dial(url, header1)
	if err != nil {
		t.Fatal(err)
	}

	user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "*****@*****.**", Nickname: "Corey Hulen", Password: "******"}
	user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
	store.Must(Srv.Store.User().VerifyEmail(user2.Id))
	Client.LoginByEmail(team.Domain, user2.Email, "pwd")

	header2 := http.Header{}
	header2.Set(model.HEADER_AUTH, "BEARER "+Client.AuthToken)

	c2, _, err := websocket.DefaultDialer.Dial(url, header2)
	if err != nil {
		t.Fatal(err)
	}

	time.Sleep(300 * time.Millisecond)
	Client.Must(Client.JoinChannel(channel1.Id))

	// Read the join channel message that gets generated
	var rmsg model.Message
	if err := c2.ReadJSON(&rmsg); err != nil {
		t.Fatal(err)
	}

	// Test sending message without a channelId
	m := model.NewMessage("", "", "", model.ACTION_TYPING)
	m.Add("RootId", model.NewId())
	m.Add("ParentId", model.NewId())

	c1.WriteJSON(m)

	if err := c2.ReadJSON(&rmsg); err != nil {
		t.Fatal(err)
	}

	if team.Id != rmsg.TeamId {
		t.Fatal("Ids do not match")
	}

	if m.Props["RootId"] != rmsg.Props["RootId"] {
		t.Fatal("Ids do not match")
	}

	// Test sending messsage to Channel you have access to
	m = model.NewMessage("", channel1.Id, "", model.ACTION_TYPING)
	m.Add("RootId", model.NewId())
	m.Add("ParentId", model.NewId())

	c1.WriteJSON(m)

	if err := c2.ReadJSON(&rmsg); err != nil {
		t.Fatal(err)
	}

	if team.Id != rmsg.TeamId {
		t.Fatal("Ids do not match")
	}

	if m.Props["RootId"] != rmsg.Props["RootId"] {
		t.Fatal("Ids do not match")
	}

	// Test sending message to Channel you *do not* have access too
	m = model.NewMessage("", channel2.Id, "", model.ACTION_TYPING)
	m.Add("RootId", model.NewId())
	m.Add("ParentId", model.NewId())

	c1.WriteJSON(m)

	go func() {
		if err := c2.ReadJSON(&rmsg); err != nil {
			t.Fatal(err)
		}

		t.Fatal(err)
	}()

	time.Sleep(2 * time.Second)
}
Example #28
0
func deleteChannel(c *Context, w http.ResponseWriter, r *http.Request) {

	params := mux.Vars(r)
	id := params["channel_id"]

	sc := Srv.Store.Channel().Get(id)
	scm := Srv.Store.Channel().GetMember(id, c.Session.UserId)
	uc := Srv.Store.User().Get(c.Session.UserId)
	ihc := Srv.Store.Webhook().GetIncomingByChannel(id)
	ohc := Srv.Store.Webhook().GetOutgoingByChannel(id)

	if cresult := <-sc; cresult.Err != nil {
		c.Err = cresult.Err
		return
	} else if uresult := <-uc; uresult.Err != nil {
		c.Err = cresult.Err
		return
	} else if scmresult := <-scm; scmresult.Err != nil {
		c.Err = scmresult.Err
		return
	} else if ihcresult := <-ihc; ihcresult.Err != nil {
		c.Err = ihcresult.Err
		return
	} else if ohcresult := <-ohc; ohcresult.Err != nil {
		c.Err = ohcresult.Err
		return
	} else {
		channel := cresult.Data.(*model.Channel)
		user := uresult.Data.(*model.User)
		channelMember := scmresult.Data.(model.ChannelMember)
		incomingHooks := ihcresult.Data.([]*model.IncomingWebhook)
		outgoingHooks := ohcresult.Data.([]*model.OutgoingWebhook)

		if !c.HasPermissionsToTeam(channel.TeamId, "deleteChannel") {
			return
		}

		if !strings.Contains(channelMember.Roles, model.CHANNEL_ROLE_ADMIN) && !c.IsTeamAdmin() {
			c.Err = model.NewLocAppError("deleteChannel", "api.channel.delete_channel.permissions.app_error", nil, "")
			c.Err.StatusCode = http.StatusForbidden
			return
		}

		if channel.DeleteAt > 0 {
			c.Err = model.NewLocAppError("deleteChannel", "api.channel.delete_channel.deleted.app_error", nil, "")
			c.Err.StatusCode = http.StatusBadRequest
			return
		}

		if channel.Name == model.DEFAULT_CHANNEL {
			c.Err = model.NewLocAppError("deleteChannel", "api.channel.delete_channel.cannot.app_error", map[string]interface{}{"Channel": model.DEFAULT_CHANNEL}, "")
			c.Err.StatusCode = http.StatusBadRequest
			return
		}

		now := model.GetMillis()
		for _, hook := range incomingHooks {
			go func() {
				if result := <-Srv.Store.Webhook().DeleteIncoming(hook.Id, now); result.Err != nil {
					l4g.Error(utils.T("api.channel.delete_channel.incoming_webhook.error"), hook.Id)
				}
			}()
		}

		for _, hook := range outgoingHooks {
			go func() {
				if result := <-Srv.Store.Webhook().DeleteOutgoing(hook.Id, now); result.Err != nil {
					l4g.Error(utils.T("api.channel.delete_channel.outgoing_webhook.error"), hook.Id)
				}
			}()
		}

		if dresult := <-Srv.Store.Channel().Delete(channel.Id, model.GetMillis()); dresult.Err != nil {
			c.Err = dresult.Err
			return
		}

		c.LogAudit("name=" + channel.Name)

		go func() {
			InvalidateCacheForChannel(channel.Id)
			message := model.NewMessage(c.TeamId, channel.Id, c.Session.UserId, model.ACTION_CHANNEL_DELETED)
			go Publish(message)

			post := &model.Post{
				ChannelId: channel.Id,
				Message:   fmt.Sprintf(c.T("api.channel.delete_channel.archived"), user.Username),
				Type:      model.POST_CHANNEL_DELETED,
			}
			if _, err := CreatePost(c, post, false); err != nil {
				l4g.Error(utils.T("api.channel.delete_channel.failed_post.error"), err)
			}
		}()

		result := make(map[string]string)
		result["id"] = channel.Id
		w.Write([]byte(model.MapToJson(result)))
	}
}
Example #29
0
func addChannelMember(c *Context, w http.ResponseWriter, r *http.Request) {
	params := mux.Vars(r)
	id := params["id"]

	data := model.MapFromJson(r.Body)
	userId := data["user_id"]

	if len(userId) != 26 {
		c.SetInvalidParam("addChannelMember", "user_id")
		return
	}

	cchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, id, c.Session.UserId)
	sc := Srv.Store.Channel().Get(id)
	ouc := Srv.Store.User().Get(c.Session.UserId)
	nuc := Srv.Store.User().Get(userId)

	// Only need to be a member of the channel to add a new member
	if !c.HasPermissionsToChannel(cchan, "addChannelMember") {
		return
	}

	if nresult := <-nuc; nresult.Err != nil {
		c.Err = model.NewAppError("addChannelMember", "Failed to find user to be added", "")
		return
	} else if cresult := <-sc; cresult.Err != nil {
		c.Err = model.NewAppError("addChannelMember", "Failed to find channel", "")
		return
	} else {
		channel := cresult.Data.(*model.Channel)
		nUser := nresult.Data.(*model.User)

		if channel.DeleteAt > 0 {
			c.Err = model.NewAppError("updateChannel", "The channel has been archived or deleted", "")
			c.Err.StatusCode = http.StatusBadRequest
			return
		}

		if oresult := <-ouc; oresult.Err != nil {
			c.Err = model.NewAppError("addChannelMember", "Failed to find user doing the adding", "")
			return
		} else {
			oUser := oresult.Data.(*model.User)

			cm := &model.ChannelMember{ChannelId: channel.Id, UserId: userId, NotifyLevel: model.CHANNEL_NOTIFY_ALL}

			if cmresult := <-Srv.Store.Channel().SaveMember(cm); cmresult.Err != nil {
				l4g.Error("Failed to add member user_id=%v channel_id=%v err=%v", userId, id, cmresult.Err)
				c.Err = model.NewAppError("addChannelMember", "Failed to add user to channel", "")
				return
			}

			post := &model.Post{ChannelId: id, Message: fmt.Sprintf(
				`%v added to the channel by %v`,
				nUser.Username, oUser.Username), Type: model.POST_JOIN_LEAVE}
			if _, err := CreatePost(c, post, false); err != nil {
				l4g.Error("Failed to post add message %v", err)
				c.Err = model.NewAppError("addChannelMember", "Failed to add member to channel", "")
				return
			}

			c.LogAudit("name=" + channel.Name + " user_id=" + userId)

			message := model.NewMessage(c.Session.TeamId, "", userId, model.ACTION_USER_ADDED)

			PublishAndForget(message)

			<-Srv.Store.Channel().UpdateLastViewedAt(id, oUser.Id)
			w.Write([]byte(cm.ToJson()))
		}
	}
}
Example #30
0
func TestSocket(t *testing.T) {
	th := Setup().InitBasic()
	Client := th.BasicClient
	team := th.BasicTeam
	channel1 := th.BasicChannel
	channel2 := th.CreateChannel(Client, team)
	Client.Must(Client.AddChannelMember(channel1.Id, th.BasicUser2.Id))

	url := "ws://localhost" + utils.Cfg.ServiceSettings.ListenAddress + model.API_URL_SUFFIX + "/users/websocket"

	header1 := http.Header{}
	header1.Set(model.HEADER_AUTH, "BEARER "+Client.AuthToken)

	c1, _, err := websocket.DefaultDialer.Dial(url, header1)
	if err != nil {
		t.Fatal(err)
	}

	th.LoginBasic2()

	header2 := http.Header{}
	header2.Set(model.HEADER_AUTH, "BEARER "+Client.AuthToken)

	c2, _, err := websocket.DefaultDialer.Dial(url, header2)
	if err != nil {
		t.Fatal(err)
	}

	time.Sleep(300 * time.Millisecond)

	var rmsg model.Message

	// Test sending message without a channelId
	m := model.NewMessage(team.Id, "", "", model.ACTION_TYPING)
	m.Add("RootId", model.NewId())
	m.Add("ParentId", model.NewId())

	c1.WriteJSON(m)

	if err := c2.ReadJSON(&rmsg); err != nil {
		t.Fatal(err)
	}

	t.Log(rmsg.ToJson())

	if team.Id != rmsg.TeamId {
		t.Fatal("Ids do not match")
	}

	if m.Props["RootId"] != rmsg.Props["RootId"] {
		t.Fatal("Ids do not match")
	}

	// Test sending messsage to Channel you have access to
	m = model.NewMessage(team.Id, channel1.Id, "", model.ACTION_TYPING)
	m.Add("RootId", model.NewId())
	m.Add("ParentId", model.NewId())

	c1.WriteJSON(m)

	if err := c2.ReadJSON(&rmsg); err != nil {
		t.Fatal(err)
	}

	if team.Id != rmsg.TeamId {
		t.Fatal("Ids do not match")
	}

	if m.Props["RootId"] != rmsg.Props["RootId"] {
		t.Fatal("Ids do not match")
	}

	// Test sending message to Channel you *do not* have access too
	m = model.NewMessage("", channel2.Id, "", model.ACTION_TYPING)
	m.Add("RootId", model.NewId())
	m.Add("ParentId", model.NewId())

	c1.WriteJSON(m)

	go func() {
		if err := c2.ReadJSON(&rmsg); err != nil {
			t.Fatal(err)
		}

		t.Fatal(err)
	}()

	time.Sleep(2 * time.Second)
}