func (mwc *Controller) createGroupChannel(groupName string) (*models.Channel, error) { c := models.NewChannel() c.Name = groupName if groupName == models.Channel_KODING_NAME { c.Name = "public" } c.GroupName = groupName c.TypeConstant = models.Channel_TYPE_GROUP group, err := modelhelper.GetGroup(groupName) if err != nil { return nil, err } if group.Visibility == "visible" { c.PrivacyConstant = models.Channel_PRIVACY_PUBLIC } else { c.PrivacyConstant = models.Channel_PRIVACY_PRIVATE } // find group owner creatorId, err := mwc.fetchGroupOwnerId(group) if err != nil { return nil, err } c.CreatorId = creatorId // create channel if err := c.Create(); err != nil { return nil, err } return c, nil }
// ByName finds topics by their name func ByName(u *url.URL, h http.Header, _ interface{}, context *models.Context) (int, http.Header, interface{}, error) { q := context.OverrideQuery(request.GetQuery(u)) if !context.IsLoggedIn() { return response.NewBadRequest(models.ErrNotLoggedIn) } if q.Type == "" { q.Type = models.Channel_TYPE_TOPIC } channel, err := models.NewChannel().ByName(q) if err != nil { if err == bongo.RecordNotFound { return response.NewNotFound() } if models.IsChannelLeafErr(err) { return http.StatusMovedPermanently, nil, nil, tigertonic.MovedPermanently{Err: err} } return response.NewBadRequest(err) } return handleChannelResponse(channel, q) }
// buildChannelWithRandomGroup creates a channel with group name "koding[randonnumber]" func buildChannelWithRandomGroup(creatorId int64) *models.Channel { c := models.NewChannel() rand.Seed(time.Now().UnixNano()) c.GroupName = c.GroupName + strconv.Itoa(rand.Intn(100000000)) return c }
// DeletePrivateChannelMessages deletes all channel messages from a private message channel // when there are no more participants func DeleteDesertedChannelMessages(channelId int64) error { c := models.NewChannel() if err := c.ById(channelId); err != nil { return err } if c.TypeConstant != models.Channel_TYPE_PRIVATE_MESSAGE && c.TypeConstant != models.Channel_TYPE_COLLABORATION { return nil } cp := models.NewChannelParticipant() cp.ChannelId = c.Id count, err := cp.FetchParticipantCount() if err != nil { return err } if count != 0 { return nil } // no need to keep the channel any more return c.Delete() }
func RemoveMulti(u *url.URL, h http.Header, participants []*models.ChannelParticipant, context *models.Context) (int, http.Header, interface{}, error) { query := context.OverrideQuery(request.GetQuery(u)) if err := checkChannelPrerequisites( query.Id, query.AccountId, participants, ); err != nil { return response.NewBadRequest(err) } ch := models.NewChannel() err := ch.ById(query.Id) if err != nil { return response.NewBadRequest(err) } if ch.TypeConstant == models.Channel_TYPE_BOT { return response.NewBadRequest(errors.New("can not remove participants for bot channel")) } isAdmin, err := modelhelper.IsAdmin(context.Client.Account.Nick, context.GroupName) if err != nil { return response.NewBadRequest(err) } for i := range participants { // if the requester is trying to remove some other user than themselves, and they are not the channel owner // return bad request if participants[i].AccountId != query.AccountId && query.AccountId != ch.CreatorId { if !isAdmin { return response.NewBadRequest(fmt.Errorf("User is not allowed to kick other users")) } } participants[i].ChannelId = query.Id if err := participants[i].Delete(); err != nil { return response.NewBadRequest(err) } if err := addLeaveActivity(query.Id, query.AccountId, participants[i]); err != nil { return response.NewBadRequest(err) } } // this could be moved into another worker, but i did not want to create a new worker that will be used // for just a few times go func() { if err := DeleteDesertedChannelMessages(query.Id); err != nil { runner.MustGetLogger().Error("Could not delete channel messages: %s", err.Error()) } }() go notifyParticipants(ch, models.ChannelParticipant_Removed_From_Channel_Event, participants) return response.NewOK(participants) }
func FetchPinnedActivityChannel(accountId int64, groupName string) (*models.Channel, error) { url := fmt.Sprintf("/activity/pin/channel?accountId=%d&groupName=%s", accountId, groupName) cm := models.NewChannel() cmI, err := sendModel("GET", url, cm) if err != nil { return nil, err } return cmI.(*models.Channel), nil }
func AddMulti(u *url.URL, h http.Header, participants []*models.ChannelParticipant, context *models.Context) (int, http.Header, interface{}, error) { query := context.OverrideQuery(request.GetQuery(u)) if err := checkChannelPrerequisites( query.Id, query.AccountId, participants, ); err != nil { return response.NewBadRequest(err) } ch := models.NewChannel() err := ch.ById(query.Id) if err != nil { return response.NewBadRequest(err) } if ch.TypeConstant == models.Channel_TYPE_BOT { return response.NewBadRequest(errors.New("can not add participants for bot channel")) } for i := range participants { participant := models.NewChannelParticipant() participant.ChannelId = query.Id // prevent duplicate participant addition isParticipant, err := participant.IsParticipant(participants[i].AccountId) if err != nil { return response.NewBadRequest(err) } if isParticipant { continue } participant.AccountId = participants[i].AccountId //We can add users with requestpending status if participants[i].StatusConstant != "" { participant.StatusConstant = participants[i].StatusConstant } if err := participant.Create(); err != nil { return response.NewBadRequest(err) } participants[i] = participant if err := addJoinActivity(query.Id, participant, query.AccountId); err != nil { return response.NewBadRequest(err) } } go notifyParticipants(ch, models.ChannelParticipant_Added_To_Channel_Event, participants) return response.NewOK(participants) }
// ByParticipants finds private message channels by their participants func ByParticipants(u *url.URL, h http.Header, _ interface{}, context *models.Context) (int, http.Header, interface{}, error) { // only logged in users if !context.IsLoggedIn() { return response.NewBadRequest(models.ErrNotLoggedIn) } query := request.GetQuery(u) query = context.OverrideQuery(query) participantsStr, ok := u.Query()["id"] if !ok { return response.NewBadRequest(errors.New("participants not set")) } if len(participantsStr) == 0 { return response.NewBadRequest(errors.New("at least one participant is required")) } unify := make(map[string]interface{}) // add current account to participants list unify[strconv.FormatInt(context.Client.Account.Id, 10)] = struct{}{} // remove duplicates from participants for i := range participantsStr { unify[participantsStr[i]] = struct{}{} } participants := make([]int64, 0) // convert strings to int64 for participantStr := range unify { i, err := strconv.ParseInt(participantStr, 10, 64) if err != nil { return response.NewBadRequest(err) } participants = append(participants, i) } channels, err := models.NewChannel().ByParticipants(participants, query) if err != nil { if err == bongo.RecordNotFound { return response.NewNotFound() } } cc := models.NewChannelContainers(). PopulateWith(channels, context.Client.Account.Id). AddLastMessage(context.Client.Account.Id). AddUnreadCount(context.Client.Account.Id) return response.HandleResultAndError(cc, cc.Err()) }
func (mwc *Controller) createChangelogChannel() { c := models.NewChannel() c.Name = "changelog" c.GroupName = models.Channel_KODING_NAME c.TypeConstant = models.Channel_TYPE_ANNOUNCEMENT c.PrivacyConstant = models.Channel_PRIVACY_PRIVATE c.CreatorId = 1 err := c.Create() if err != nil { mwc.log.Error("Could not create changelog channel: %s", err) } }
func DeleteChannel(creatorId, channelId int64) error { c := models.NewChannel() c.CreatorId = creatorId c.Id = channelId url := fmt.Sprintf("/channel/%d/delete", channelId) _, err := sendModel("POST", url, c) if err != nil { return err } return nil }
func New(log logging.Logger, client algoliasearch.Client, indexSuffix string) *Controller { // TODO later on listen channel_participant_added event and remove this koding channel fetch c := models.NewChannel() q := request.NewQuery() q.GroupName = "koding" q.Name = "public" q.Type = models.Channel_TYPE_GROUP channel, err := c.ByName(q) if err != nil { log.Error("Could not fetch koding channel: %s:", err) } var channelId string if channel.Id != 0 { channelId = strconv.FormatInt(channel.Id, 10) } controller := &Controller{ log: log, client: client, indexes: &IndexSet{ IndexTopics: &IndexSetItem{ Index: client.InitIndex(IndexTopics + indexSuffix), Settings: &Settings{ // empty slice means all properties will be searchable AttributesToIndex: []string{}, }, }, IndexAccounts: &IndexSetItem{ Index: client.InitIndex(IndexAccounts + indexSuffix), Settings: &Settings{ AttributesToIndex: []string{ "nick", "email", "firstName", "lastName", "_tags", }, UnretrievableAttributes: []string{"email"}, }, }, IndexMessages: &IndexSetItem{ Index: client.InitIndex(IndexMessages + indexSuffix), Settings: &Settings{ AttributesToIndex: []string{}, }, }, }, kodingChannelId: channelId, } return controller }
func createTypedChannel(creatorId int64, groupName, typeConstant string) (*models.Channel, error) { // create and account instance channel := models.NewChannel() channel.CreatorId = creatorId channel.GroupName = groupName channel.TypeConstant = typeConstant if err := channel.Create(); err != nil { return nil, err } return channel, nil }
func CreateGroupActivityChannel(creatorId int64, groupName string) (*models.Channel, error) { c := models.NewChannel() c.GroupName = groupName c.CreatorId = creatorId c.TypeConstant = models.Channel_TYPE_GROUP c.Name = groupName cm, err := sendModel("POST", "/channel", c) if err != nil { return nil, err } return cm.(*models.Channel), nil }
// Search searchs database against given channel name // but only returns topic channels func Search(u *url.URL, h http.Header, _ interface{}, context *models.Context) (int, http.Header, interface{}, error) { q := request.GetQuery(u) q = context.OverrideQuery(q) if q.Type != models.Channel_TYPE_LINKED_TOPIC { q.Type = models.Channel_TYPE_TOPIC } channelList, err := models.NewChannel().Search(q) if err != nil { return response.NewBadRequest(err) } return handleChannelListResponse(channelList, q) }
// this function is tested via integration tests func fetchChannelWithValidation(channelId int64) (*models.Channel, error) { c := models.NewChannel() if err := c.ById(channelId); err != nil { return nil, err } // add activity information for private message channel if c.TypeConstant != models.Channel_TYPE_PRIVATE_MESSAGE && c.TypeConstant != models.Channel_TYPE_COLLABORATION { return nil, ErrSkipActivity } return c, nil }
func setChangeLogChannel(log logging.Logger, group *kodingmodels.Group) { c := models.NewChannel() selector := map[string]interface{}{ "type_constant": models.Channel_TYPE_ANNOUNCEMENT, "group_name": models.Channel_KODING_NAME, } // if err is nil // it means we already have that channel err := c.One(bongo.NewQS(selector)) if err != nil && err != bongo.RecordNotFound { log.Error("err while fetching changelog channel:", err.Error()) return } if err == bongo.RecordNotFound { log.Error("postgres changelog couldn't found, creating it") acc, err := createChannelOwner(group) if err != nil { log.Error(err.Error()) return } c.Name = "changelog" c.CreatorId = acc.Id c.GroupName = models.Channel_KODING_NAME c.TypeConstant = models.Channel_TYPE_ANNOUNCEMENT c.PrivacyConstant = models.Channel_PRIVACY_PRIVATE if err := c.Create(); err != nil { log.Error("err while creating the koding channel:", err.Error()) return } } socialApiAnnouncementChannelId := strconv.FormatInt(c.Id, 10) if group.SocialApiAnnouncementChannelId == socialApiAnnouncementChannelId { log.Info("mongo and postgres socialApiAnnouncementChannel ids are same") return } log.Debug("mongo and postgres socialApiAnnouncementChannel ids are different, fixing it") if err := updateGroupPartially(group.Id, "socialApiAnnouncementChannelId", strconv.FormatInt(c.Id, 10)); err != nil { log.Error("err while udpating socialApiAnnouncementChannelId:", err.Error()) return } }
func CheckParticipation(u *url.URL, h http.Header, _ interface{}, context *models.Context) (int, http.Header, interface{}, error) { q := context.OverrideQuery(request.GetQuery(u)) if context.Client != nil && context.Client.Account != nil { q.AccountId = context.Client.Account.Id } if q.Type == "" || q.AccountId == 0 { return response.NewBadRequest(errors.New("type or accountid is not set")) } channel, err := models.NewChannel().ByName(q) if err != nil { return response.NewBadRequest(err) } res := models.NewCheckParticipationResponse() res.Channel = &channel res.Account = context.Client.Account if context.Client.Account != nil { res.AccountToken = context.Client.Account.Token } canOpen, err := channel.CanOpen(q.AccountId) if err != nil { return response.NewBadRequest(err) } if !canOpen { cp := models.NewChannelParticipant() cp.ChannelId = channel.Id isInvited, err := cp.IsInvited(q.AccountId) if err != nil { return response.NewBadRequest(err) } if !isInvited { return response.NewAccessDenied( fmt.Errorf( "account (%d) tried to retrieve the unattended channel (%d)", q.AccountId, channel.Id, ), ) } } return response.NewOK(res) }
func getPrivateChannels(q *request.Query) ([]models.Channel, error) { // build query for if q.AccountId == 0 || q.GroupName == "" { return nil, errors.New("request is not valid") } c := models.NewChannel() channelIds := make([]int64, 0) query := getUserChannelsQuery(q) if q.Name != "" { // use 'ilike' for case-insensitive search query = query.Where("api.channel.purpose ilike ?", "%"+q.Name+"%") } // add exempt clause if needed if !q.ShowExempt { query = query.Where("api.channel.meta_bits = ?", models.Safe) } query = query.Limit(q.Limit). Offset(q.Skip). Order("api.channel.updated_at DESC") rows, err := query.Rows() if err != nil { return nil, err } defer rows.Close() for rows.Next() { var channelId int64 err := rows.Scan(&channelId) if err == nil { channelIds = append(channelIds, channelId) } } channels, err := c.FetchByIds(channelIds) if err != nil { return nil, err } return channels, nil }
// TODO when we implement Team product, we will need a better caching mechanism func FetchGroupChannelId(groupName string) (int64, error) { if groupName == "koding" && publicChannel != nil { return publicChannel.Id, nil } channel := models.NewChannel() if groupName == "koding" { publicChannel = channel } if err := channel.FetchGroupChannel(groupName); err != nil { return 0, err } return channel.Id, nil }
func setPublicChannel(log logging.Logger, group *kodingmodels.Group) { c := models.NewChannel() selector := map[string]interface{}{ "type_constant": models.Channel_TYPE_GROUP, "group_name": models.Channel_KODING_NAME, } err := c.One(bongo.NewQS(selector)) if err != nil && err != bongo.RecordNotFound { log.Error("err while fetching koding channel:", err.Error()) return } if err == bongo.RecordNotFound { log.Debug("postgres group couldn't found, creating it") acc, err := createChannelOwner(group) if err != nil { log.Error(err.Error()) return } c.Name = "public" c.CreatorId = acc.Id c.GroupName = models.Channel_KODING_NAME c.TypeConstant = models.Channel_TYPE_GROUP c.PrivacyConstant = models.Channel_PRIVACY_PUBLIC if err := c.Create(); err != nil { log.Error("err while creating the koding channel: %s", err.Error()) return } } socialApiId := strconv.FormatInt(c.Id, 10) if group.SocialApiChannelId == socialApiId { log.Debug("mongo and postgres socialApiChannelId ids are same") return } log.Debug("mongo and postgres socialApiChannelId ids are different, fixing it") if err := updateGroupPartially(group.Id, "socialApiChannelId", socialApiId); err != nil { log.Error("err while udpating socialApiChannelId: %s", err.Error()) return } }
func Get(u *url.URL, h http.Header, _ interface{}, context *models.Context) (int, http.Header, interface{}, error) { id, err := request.GetURIInt64(u, "id") if err != nil { return response.NewBadRequest(err) } q := request.GetQuery(u) q = context.OverrideQuery(q) c := models.NewChannel() if err := c.ById(id); err != nil { if err == bongo.RecordNotFound { return response.NewNotFound() } return response.NewBadRequest(err) } return handleChannelResponse(*c, q) }
// ChannelCreated handles the channel create events, for now only handles the // channels that are topic channels, func (f *Controller) ChannelCreated(data *models.Channel) error { if data.TypeConstant != models.Channel_TYPE_TOPIC { return nil } // add public group channel id into tags for security publicChannel := models.NewChannel() err := publicChannel.FetchGroupChannel(data.GroupName) if err != nil { return err } return f.insert(IndexTopics, map[string]interface{}{ "objectID": strconv.FormatInt(data.Id, 10), "name": data.Name, "purpose": data.Purpose, "_tags": []string{strconv.FormatInt(publicChannel.Id, 10)}, }) }
// List lists only topic channels func List(u *url.URL, h http.Header, _ interface{}, context *models.Context) (int, http.Header, interface{}, error) { c := models.NewChannel() q := request.GetQuery(u) query := context.OverrideQuery(q) // only list topic or linked topic channels if query.Type != models.Channel_TYPE_LINKED_TOPIC { query.Type = models.Channel_TYPE_TOPIC } // TODO // refactor this function just to return channel ids // we cache wisely channelList, err := c.List(query) if err != nil { return response.NewBadRequest(err) } return handleChannelListResponse(channelList, query) }
func (mwc *Controller) createPublicChannel() *models.Channel { c := models.NewChannel() c.Name = "public" c.GroupName = models.Channel_KODING_NAME c.TypeConstant = models.Channel_TYPE_GROUP c.PrivacyConstant = models.Channel_PRIVACY_PUBLIC c.CreatorId = 1 err := c.Create() if err != nil { mwc.log.Error("Could not create public channel: %s", err) } cp := models.NewChannelParticipant() cp.ChannelId = c.Id cp.AccountId = 1 if err := cp.Create(); err != nil { mwc.log.Error("Could not add devrim to public channel: %s", err) } return c }
func CreateChannelByGroupNameAndType(creatorId int64, groupName, typeConstant, token string) (*models.Channel, error) { c := models.NewChannel() c.GroupName = groupName c.CreatorId = creatorId c.TypeConstant = typeConstant c.PrivacyConstant = models.Channel_PRIVACY_PUBLIC c.Name = c.Name + strconv.Itoa(rand.Intn(100000000)) res, err := marshallAndSendRequestWithAuth("POST", "/channel", c, token) if err != nil { return nil, err } cc := models.NewChannelContainer() err = json.Unmarshal(res, cc) if err != nil { return nil, err } return cc.Channel, nil }
func (h *Handler) GenerateKey(u *url.URL, header http.Header, _ interface{}, context *models.Context) (int, http.Header, interface{}, error) { var accountId int64 if context.IsLoggedIn() { accountId = context.Client.Account.Id } c := models.NewChannel() err := c.FetchGroupChannel(context.GroupName) if err != nil { return response.NewBadRequest(err) } searchOnlyKey, err := h.generateSearchOnlyKey(c, accountId) if err != nil { return response.NewBadRequest(err) } return response.NewOK(&Response{ApiKey: searchOnlyKey}) }
func CheckOwnership(u *url.URL, h http.Header) (int, http.Header, interface{}, error) { accountId, err := request.GetURIInt64(u, "id") if err != nil { return response.NewBadRequest(err) } query := request.GetQuery(u) ownershipResponse := func(err error) (int, http.Header, interface{}, error) { var success bool switch err { case bongo.RecordNotFound: success = false case nil: success = true default: return response.NewBadRequest(err) } return response.NewOK(map[string]bool{"success": success}) } switch query.Type { case "channel": channel := models.NewChannel() err = channel.One(&bongo.Query{ Selector: map[string]interface{}{ "id": query.ObjectId, "creator_id": accountId, }, }) case "channel-message": channelMessage := models.NewChannelMessage() err = channelMessage.One(&bongo.Query{ Selector: map[string]interface{}{ "id": query.ObjectId, "account_id": accountId, }, }) } return ownershipResponse(err) }
// EndPrivateMessage stops the collaboration session and deletes the all // messages from db func (c *Controller) EndPrivateMessage(ping *models.Ping) error { // if channel id is nil, there is nothing to do if ping.ChannelId == 0 { return nil } // fetch the channel channel := socialapimodels.NewChannel() if err := channel.ById(ping.ChannelId); err != nil { // if channel is not there, do not do anyting if err == bongo.RecordNotFound { return nil } return err } canOpen, err := channel.CanOpen(ping.AccountId) if err != nil { return err } if !canOpen { return nil // if the requester can not open the channel do not process } // delete the channel err = channel.Delete() if err != nil { return err } ws, err := modelhelper.GetWorkspaceByChannelId( strconv.FormatInt(ping.ChannelId, 10), ) if err != nil { return filterErr(err) } return modelhelper.UnsetSocialChannelFromWorkspace(ws.ObjectId) }
func getUserChannelsQuery(q *request.Query) *gorm.DB { c := models.NewChannel() if q.Type == "" { q.Type = models.Channel_TYPE_PRIVATE_MESSAGE } return bongo.B.DB. Model(c). Table(c.BongoName()). Select("api.channel_participant.channel_id"). Joins("left join api.channel_participant on api.channel_participant.channel_id = api.channel.id"). Where("api.channel_participant.account_id = ? and "+ "api.channel.group_name = ? and "+ "api.channel.type_constant = ? and "+ "api.channel_participant.status_constant = ?", q.AccountId, q.GroupName, q.Type, models.ChannelParticipant_STATUS_ACTIVE) }
func ListGroupChannels(u *url.URL, h http.Header, _ interface{}, c *models.Context) (int, http.Header, interface{}, error) { if !c.IsLoggedIn() { return response.NewBadRequest(models.ErrNotLoggedIn) } cp := models.NewChannelParticipant() cids, err := cp.FetchAllParticipatedChannelIdsInGroup(c.Client.Account.Id, c.GroupName) if err != nil { return response.NewBadRequest(err) } channels, err := models.NewChannel().FetchByIds(cids) if err != nil { return response.NewBadRequest(err) } cc := models.NewChannelContainers() cc.PopulateWith(channels, c.Client.Account.Id) return response.HandleResultAndError(cc, cc.Err()) }