Exemplo n.º 1
0
func ParticipatedChannelCount(u *url.URL, h http.Header, _ interface{}, c *models.Context) (int, http.Header, interface{}, error) {
	accountId, err := request.GetURIInt64(u, "id")
	if err != nil {
		return response.NewBadRequest(err)
	}

	if !c.IsLoggedIn() {
		return response.NewBadRequest(models.ErrNotLoggedIn)
	}

	if accountId != c.Client.Account.Id {
		return response.NewBadRequest(models.ErrAccessDenied)
	}

	query := request.GetQuery(u)
	query = c.OverrideQuery(query)

	if query.Type == "" {
		query.Type = models.Channel_TYPE_TOPIC
	}
	cp := models.NewChannelParticipant()
	a := &models.Account{Id: query.AccountId}

	return response.HandleResultAndError(cp.ParticipatedChannelCount(a, query))
}
Exemplo n.º 2
0
// 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()
}
Exemplo n.º 3
0
func updateStatus(participant *models.ChannelParticipant, query *request.Query, ctx *models.Context) (*models.ChannelParticipant, error) {

	if ok := ctx.IsLoggedIn(); !ok {
		return nil, models.ErrNotLoggedIn
	}

	query.AccountId = ctx.Client.Account.Id

	cp := models.NewChannelParticipant()
	cp.ChannelId = query.Id

	// check if the user is invited
	isInvited, err := cp.IsInvited(query.AccountId)
	if err != nil {
		return nil, err
	}

	if !isInvited {
		return nil, errors.New("uninvited user error")
	}

	cp.StatusConstant = participant.StatusConstant
	// update the status
	if err := cp.Update(); err != nil {
		return nil, err
	}

	return cp, nil
}
Exemplo n.º 4
0
// todo - refactor this part
func (f *Controller) MessageListDeleted(cml *models.ChannelMessageList) error {
	c, err := models.Cache.Channel.ById(cml.ChannelId)
	if err != nil {
		return err
	}

	// Since we are making hard deletes, we no longer need to check the
	// message existency
	cm := models.NewChannelMessage()
	cm.Id = cml.MessageId

	cp := models.NewChannelParticipant()
	cp.AccountId = c.CreatorId

	cue := &channelUpdatedEvent{
		Controller:           f,
		Channel:              c,
		ParentChannelMessage: cm,
		ChannelParticipant:   cp,
		EventType:            channelUpdatedEventMessageRemovedFromChannel,
	}

	if err := cue.notifyAllParticipants(); err != nil {
		return err
	}

	// f.sendNotification(cp.AccountId, ChannelUpdateEventName, cue)

	if err := f.sendChannelEvent(cml, cm, MessageRemovedEventName); err != nil {
		return err
	}

	return nil
}
Exemplo n.º 5
0
func RejectInvitation(channelId int64, token string) error {
	url := fmt.Sprintf("/channel/%d/invitation/reject", channelId)
	cp := models.NewChannelParticipant()
	_, err := marshallAndSendRequestWithAuth("POST", url, cp, token)

	return err
}
Exemplo n.º 6
0
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)
}
Exemplo n.º 7
0
// sendChannelUpdatedEvent sends channel updated events
func (cue *channelUpdatedEvent) notifyAllParticipants() error {
	cue.Controller.log.Debug("notifying all participants with: %+v", cue)

	// if it is not a valid event, return silently
	if !cue.isValidNotifyAllParticipantsEvent() {
		cue.Controller.log.Debug("not a valid event (%s) for notifying all participants", cue.EventType)
		return nil
	}

	// fetch all participants of related channel if you ask why we are not
	// sending those messaages to the channel's channel instead of sending
	// events as notifications?, because we are also sending unread counts of
	// the related channel's messages by the notifiee
	p := models.NewChannelParticipant()
	p.ChannelId = cue.Channel.Id
	// TODO use proper caching here
	participants, err := p.List(
		&request.Query{
			// make sure exempt users are getting reatime notifications
			ShowExempt: true,

			// lets say nodejs topic has 56K participants(yes it has), if we
			// dont limit we can take koding down. 100 is just a random number
			Limit: 100,

			// send the event to the recently active users
			Sort: map[string]string{"updated_at": "DESC"},
		},
	)

	if err != nil {
		cue.Controller.log.Error("Error occurred while fetching participants %s", err.Error())
		return err
	}

	if len(participants) == 0 {
		return nil
	}

	for i, cp := range participants {
		if !cue.isEligibleForBroadcastingToParticipant(cp.AccountId) {
			cue.Controller.log.Debug("not sending event (%s) to the participant  %d", cue.EventType, cp.AccountId)
			continue
		}

		cue.ChannelParticipant = &participants[i]

		err := cue.sendForParticipant()
		if err != nil {
			cue.Controller.log.Error("Error while sending notification (%s)", err.Error())
		}
	}

	return nil
}
Exemplo n.º 8
0
// ChannelMessageListUpdated event states that one of the channel_message_list
// record is updated, it means that one the pinned post's owner glanced it. - At
// least for now - If we decide on another giving another meaning on this event,
// we can rename the event.  PinnedPost's unread count is calculated from the
// last glanced reply's point. This message is user specific. There is no
// relation with channel participants or channel itself
func (f *Controller) ChannelMessageListUpdated(cml *models.ChannelMessageList) error {

	// find the user's pinned post channel
	// we need it for finding the account id
	c, err := models.Cache.Channel.ById(cml.ChannelId)
	if err != nil {
		return err
	}

	if c.TypeConstant != models.Channel_TYPE_PINNED_ACTIVITY {
		f.log.Error("please investigate here, we have updated the channel message list for a non-pinned post item %+v", c)
		return nil
	}

	// get the glanced message
	cm, err := models.Cache.Message.ById(cml.MessageId)
	if err != nil {
		return err
	}

	// No need to fetch the participant from database we are gonna use only the
	// account id
	cp := models.NewChannelParticipant()
	cp.AccountId = c.CreatorId
	cp.ChannelId = c.Id
	if err := cp.FetchParticipant(); err != nil {
		return err
	}

	cue := &channelUpdatedEvent{
		// inject controller for reaching to RMQ, log and other stuff
		Controller: f,

		// In which channel this event happened, we need groupName from the
		// channel because user can be in multiple groups, and all group-account
		// couples have separate channels
		Channel: c,

		// We need parentChannelMessage for calculating the unread count of it's replies
		ParentChannelMessage: cm,

		// ChannelParticipant is the reciever of this event
		ChannelParticipant: cp,

		// Assign event type
		EventType: channelUpdatedEventMessageUpdatedAtChannel,
	}

	if err := cue.sendForParticipant(); err != nil {
		return err
	}

	return nil
}
Exemplo n.º 9
0
// PinnedChannelListUpdated handles the events of pinned channel lists'.  When a
// user glance a pinned message or when someone posts reply to a message we are
// updating the channel message lists
func (f *Controller) PinnedChannelListUpdated(pclue *models.PinnedChannelListUpdatedEvent) error {
	// find the user's pinned post channel
	// we need it for finding the account id
	c := pclue.Channel

	if &c == nil {
		f.log.Error("channel was nil, discarding the message %+v", pclue)
		return nil
	}

	if c.TypeConstant != models.Channel_TYPE_PINNED_ACTIVITY {
		f.log.Error("please investigate here, we have updated the channel message list for a non-pinned post item %+v", c)
		return nil
	}

	// No need to fetch the participant from database we are gonna use only the
	// account id
	cp := models.NewChannelParticipant()
	cp.AccountId = c.CreatorId
	cp.ChannelId = c.Id
	if err := cp.FetchParticipant(); err != nil {
		return err
	}

	cue := &channelUpdatedEvent{
		// inject controller for reaching to RMQ, log and other stuff
		Controller: f,

		// In which channel this event happened, we need groupName from the
		// channel because user can be in multiple groups, and all group-account
		// couples have separate channels
		Channel: &c,

		// We need parentChannelMessage for calculating the unread count of it's replies
		ParentChannelMessage: &pclue.Message,

		// We need to find out that if the reply is created by a troll
		ReplyChannelMessage: &pclue.Reply,

		// ChannelParticipant is the reciever of this event
		ChannelParticipant: cp,

		// Assign event type
		EventType: channelUpdatedEventReplyAdded,
	}

	if err := cue.sendForParticipant(); err != nil {
		return err
	}

	return nil
}
Exemplo n.º 10
0
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)
}
Exemplo n.º 11
0
func (mwc *Controller) createChannelParticipants(s modelhelper.Selector, channelId int64) error {

	migrateRelationship := func(relationship interface{}) error {
		r := relationship.(*mongomodels.Relationship)

		if r.MigrationStatus == "Completed" {
			return nil
		}
		// fetch follower
		id, err := mwc.AccountIdByOldId(r.TargetId.Hex())
		if err != nil {
			mwc.log.Error("Participant account %s cannot be fetched: %s", r.TargetId.Hex(), err)
			return nil
		}

		cp := models.NewChannelParticipant()
		cp.ChannelId = channelId
		cp.AccountId = id
		cp.StatusConstant = models.ChannelParticipant_STATUS_ACTIVE
		cp.LastSeenAt = r.TimeStamp
		cp.UpdatedAt = r.TimeStamp
		cp.CreatedAt = r.TimeStamp
		if err := cp.CreateRaw(); err != nil {
			mwc.log.Error("Participant cannot be created: %s", err)
			return nil
		}

		r.MigrationStatus = "Completed"
		if err := modelhelper.UpdateRelationship(r); err != nil {
			mwc.log.Error("Participant relationship cannot be flagged as migrated: %s", err)
		}

		return nil
	}

	iterOptions := helpers.NewIterOptions()
	iterOptions.CollectionName = "relationships"
	iterOptions.F = migrateRelationship
	iterOptions.Filter = s
	iterOptions.Result = &mongomodels.Relationship{}
	iterOptions.Limit = 1000000000
	iterOptions.Skip = 0

	return helpers.Iter(modelhelper.Mongo, iterOptions)
}
Exemplo n.º 12
0
func channelParticipantOp(url string, channelId int64, token string, accountIds ...int64) (*models.ChannelParticipant, error) {

	res := make([]*models.ChannelParticipant, 0)
	for _, accountId := range accountIds {
		c := models.NewChannelParticipant()
		c.AccountId = accountId
		res = append(res, c)
	}

	cps, err := sendModelWithAuth("POST", url, &res, token)
	if err != nil {
		return nil, err
	}

	a := *(cps.(*[]*models.ChannelParticipant))

	return a[0], nil
}
Exemplo n.º 13
0
func handleChannelResponse(c models.Channel, q *request.Query) (int, http.Header, interface{}, error) {
	// add troll mode filter
	if c.MetaBits.Is(models.Troll) && !q.ShowExempt {
		return response.NewNotFound()
	}

	canOpen, err := c.CanOpen(q.AccountId)
	if err != nil {
		return response.NewBadRequest(err)
	}

	if !canOpen {
		cp := models.NewChannelParticipant()
		cp.ChannelId = c.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,
					c.Id,
				),
			)
		}
	}

	cc := models.NewChannelContainer()

	if err := cc.Fetch(c.GetId(), q); err != nil {
		return response.NewBadRequest(err)
	}

	cc.AddIsParticipant(q.AccountId)

	// TODO this should be in the channel cache by default
	cc.AddLastMessage(q.AccountId)
	cc.AddUnreadCount(q.AccountId)
	return response.HandleResultAndError(cc, cc.Err)
}
Exemplo n.º 14
0
func InviteChannelParticipant(channelId int64, token string, accountIds ...int64) (*models.ChannelParticipant, error) {
	url := fmt.Sprintf("/channel/%d/participants/add", channelId)

	res := make([]*models.ChannelParticipant, 0)
	for _, accountId := range accountIds {
		c := models.NewChannelParticipant()
		c.AccountId = accountId
		c.StatusConstant = models.ChannelParticipant_STATUS_REQUEST_PENDING
		res = append(res, c)
	}

	cps, err := sendModelWithAuth("POST", url, &res, token)
	if err != nil {
		return nil, err
	}

	a := *(cps.(*[]*models.ChannelParticipant))

	return a[0], nil
}
Exemplo n.º 15
0
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
}
Exemplo n.º 16
0
func fetchChannelParticipants(query *request.Query) ([]models.ChannelParticipantContainer, error) {
	cp := models.NewChannelParticipant()
	cp.ChannelId = query.Id

	participants, err := cp.List(query)
	if err != nil {
		return nil, err
	}

	cps := make([]models.ChannelParticipantContainer, len(participants))

	for i, participant := range participants {
		cpc, err := models.NewChannelParticipantContainer(participant)
		if err != nil {
			return cps, err
		}
		cps[i] = *cpc
	}

	return cps, nil
}
Exemplo n.º 17
0
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())
}
Exemplo n.º 18
0
// handleParticipantRemove removes a user from all channel participated channels
// in given group
func (c *Controller) handleParticipantRemove(cp *models.ChannelParticipant) error {
	channel, err := models.Cache.Channel.ById(cp.ChannelId)
	if err != nil {
		c.log.Error("Channel: %d is not found", cp.ChannelId)
		return nil
	}

	if channel.TypeConstant != models.Channel_TYPE_GROUP {
		return nil
	}

	if !models.IsIn(cp.StatusConstant,
		models.ChannelParticipant_STATUS_BLOCKED,
		models.ChannelParticipant_STATUS_LEFT,
	) {
		return nil
	}

	cpp := models.NewChannelParticipant()
	ids, err := cpp.FetchAllParticipatedChannelIdsInGroup(cp.AccountId, channel.GroupName)
	if err != nil && err != bongo.RecordNotFound {
		return err
	}

	if err == bongo.RecordNotFound {
		return nil
	}

	for _, id := range ids {
		ch := models.NewChannel()
		ch.Id = id
		if err := ch.RemoveParticipant(cp.AccountId); err != nil {
			return err
		}
	}
	return nil
}
Exemplo n.º 19
0
func checkChannelPrerequisites(channelId, requesterId int64, participants []*models.ChannelParticipant) error {
	if channelId == 0 || requesterId == 0 {
		return fmt.Errorf("values are not set. channelId: %d, requesterId: %d", channelId, requesterId)
	}

	if len(participants) == 0 {
		return errors.New("0 participant is given for participant operation")
	}

	c, err := models.Cache.Channel.ById(channelId)
	if err != nil {
		return err
	}

	canOpen, err := c.CanOpen(requesterId)
	if err != nil {
		return err
	}

	if !canOpen {
		return errors.New("can not open channel")
	}

	// admins can add users into group channels
	// if c.TypeConstant == models.Channel_TYPE_GROUP {
	// 	return errors.New("can not add/remove participants for group channel")
	// }

	if c.TypeConstant == models.Channel_TYPE_PINNED_ACTIVITY {
		return errors.New("can not add/remove participants for pinned activity channel")
	}

	if c.TypeConstant == models.Channel_TYPE_TOPIC {
		if len(participants) != 1 {
			return errors.New("you can not add only one participant into topic channel")
		}

		// with the new UI structure, now other users can add others into topic channels
		//
		// if participants[0].AccountId != requesterId {
		// 	return errors.New("you can not add others into topic channel")
		// }
	}

	// return early for non private message channels
	// no need to continue from here for other channels
	if c.TypeConstant != models.Channel_TYPE_PRIVATE_MESSAGE &&
		c.TypeConstant != models.Channel_TYPE_COLLABORATION &&
		c.TypeConstant != models.Channel_TYPE_GROUP &&
		c.TypeConstant != models.Channel_TYPE_BOT {
		return nil
	}

	// check if requester is a participant of the private message channel
	cp := models.NewChannelParticipant()
	cp.ChannelId = channelId
	isParticipant, err := cp.IsParticipant(requesterId)
	if err != nil {
		return err
	}

	if !isParticipant {
		return fmt.Errorf("%d is not a participant of the channel %d, only participants can add/remove", requesterId, channelId)
	}

	return nil
}