Exemplo n.º 1
0
 def disconnect(self, close_code):
     if self.breakout_id:
         try:
             breakout = Breakout.objects.get(pk=self.breakout_id)
         except Breakout.DoesNotExist:
             breakout = None
         track("leave_breakout", self.scope['user'], breakout=breakout)
Exemplo n.º 2
0
    def connect(self):
        self.slug = self.scope['url_route']['kwargs']['slug']
        try:
            plenary = Plenary.objects.get(slug=self.slug)
        except Plenary.DoesNotExist:
            return self.handle_error('Plenary not found')

        # Remove previous presence if the user is reconnecting
        if self.scope['user'].is_authenticated:
            Presence.objects.filter(
                room__channel_name=plenary.channel_group_name,
                user=self.scope['user']).delete()

        # Handle max connections
        if plenary.max_participants > 0 and not plenary.has_admin(
                self.scope['user']):
            num_present = Presence.objects.filter(
                room__channel_name=plenary.channel_group_name).count()
            if num_present > plenary.max_participants:
                return self.close()

        if plenary.open and not self.scope['user'].is_authenticated:
            return self.handle_error(
                "Authentication required to connect to open plenaries")

        self.accept()
        # This joins the consumer's channel to the group for this plenary
        Room.objects.add(plenary.channel_group_name, self.channel_name,
                         self.scope['user'])
        track("join_plenary", self.scope['user'], plenary=plenary)
Exemplo n.º 3
0
    def handle_contact_card(self, data, plenary):
        payload = data['payload']

        user = self.scope['user']
        keys = [
            'receive_wrapup_emails',
            'email',
            'contact_card_email',
            'contact_card_twitter',
        ]
        for key in keys:
            if key in payload:
                setattr(user, key, payload[key])

        try:
            user.full_clean()
        except ValidationError as e:
            return self.handle_error(json_dumps(e.message_dict))
        user.save()

        track("change_contact_card", self.scope['user'], plenary=plenary)

        msg = prepare_message(type="auth",
                              payload=serialize_auth_state(user, plenary))
        self.send(text_data=msg['text'])

        for room in Room.objects.filter(presence__user__id=user.id).distinct():
            broadcast(room.channel_name,
                      type="users",
                      payload={user.id: user.serialize_public()})
Exemplo n.º 4
0
 def _join_error(self, error_code, error_msg):
     data = {
         'channel_name': self.channel_name,
         'members': [],
         'error': error_msg,
         "error_code": error_code,
     }
     #self.send(text_data=prepare_message(type='presence', payload=data).get('text_data'))
     self.send(text_data=json.dumps({'type': 'presence', 'payload': data}))
     track("error", self.scope['user'], data)
Exemplo n.º 5
0
    def handle_message_breakouts(self, data, plenary):
        if not plenary.has_admin(self.scope['user']):
            return self.handle_error("Must be an admin to message breakouts")

        msg_text = data['payload']['message']
        for breakout in plenary.breakout_set.active():
            broadcast(breakout.channel_group_name,
                      type='message_breakouts',
                      payload={'message': msg_text})
        track("message_breakouts",
              self.scope['user'], {'message': msg_text},
              plenary=plenary)
Exemplo n.º 6
0
    def handle_embeds(self, data, plenary):
        if not plenary.has_admin(self.scope['user']):
            return self.handle_error("Must be an admin to set embeds")

        error = None
        clean = []
        for embed in data.get('payload', {}).get('embeds', []):
            if not isinstance(embed, dict):
                error = "Malformed embed"
            elif not isinstance(embed.get('props'), dict):
                error = "Malformed embed: missing props"
            elif embed.get('type') not in ("youtube", "url"):
                error = "Invalid type: {}".format(embed['type'])
            else:
                parsed = urlparse(embed['props']['src'])
                if parsed.scheme != "https":
                    error = "Only https URLs allowed"
            if error:
                return self.handle_error(error)
            else:
                clean.append({
                    'props': {
                        'src': embed['props']['src']
                    },
                    'type': embed['type']
                })
        current = data['payload'].get('current', None)
        if current is not None and not isinstance(current, int):
            return self.handle_error("Invalid 'current' type")
        if isinstance(current, int) and (current < 0 or current > len(clean)):
            return self.handle_error("Invalid 'current' value")

        # Stop any current video sync if we're changing the current embed.
        if plenary.embeds and plenary.embeds['current'] != current:
            VideoSync.objects.pause_for_all(plenary.channel_group_name)

        plenary.embeds = {'embeds': clean, 'current': current}
        plenary.full_clean()
        plenary.save()
        broadcast(plenary.channel_group_name,
                  type='embeds',
                  payload=plenary.embeds)
        track("change_embeds",
              self.scope['user'],
              plenary.embeds,
              plenary=plenary)
Exemplo n.º 7
0
    def handle_chat(self, data, plenary):
        highlight = (
            data['payload'].get('highlight') and \
            (self.scope['user'].is_superuser or plenary.has_admin(self.scope['user']))
        )
        with transaction.atomic():
            chat_message = ChatMessage.objects.create(
                plenary=plenary,
                user=self.scope['user'],
                message=data['payload']['message'],
                highlight=highlight or False)
            # He comes. https://stackoverflow.com/a/1732454
            user_ids = re.findall(
                r'''<span [^>]*(?<= )data-mention-user-id=['"](\d+)['"][^>]*>''',
                data['payload']['message'])
            mentions = plenary.associated_users().filter(id__in=user_ids)
            chat_message.mentions.set(mentions)

            data = chat_message.serialize()
            broadcast(plenary.channel_group_name, type='chat', payload=data)
            track("plenary_chat", self.scope['user'], data, plenary=plenary)
Exemplo n.º 8
0
    def connect_to_breakout(self, breakout):
        # delete previous presences that a user might have had for this channel
        Presence.objects.filter(room__channel_name=breakout.channel_group_name,
                                user=self.scope['user']).delete()

        num_connections = Presence.objects.filter(
            room__channel_name=breakout.channel_group_name).count()

        # Enforce max attendees.
        if num_connections >= breakout.max_attendees:
            return self.send_over_capacity_error()

        elif self.scope['user'].is_authenticated and Presence.objects.filter(
                room__channel_name__startswith=Breakout.
                CHANNEL_GROUP_NAME_PREFIX,
                user=self.scope['user']).exists():
            # Only one connection per user.
            return self.send_already_connected_error()

        Room.objects.add(breakout.channel_group_name, self.channel_name,
                         self.scope['user'])
        track("join_breakout", self.scope['user'], breakout=breakout)
Exemplo n.º 9
0
    def handle_video_sync(self, data, plenary):
        if not plenary.has_admin(self.scope['user']):
            return self.handle_error("Must be an admin to control video sync")

        payload = data['payload']
        # Here, we're ignoring the payload['sync_id'] value and instead just
        # reusing plenary.channel_group_name as the sync_id. This is a convenient
        # way to ensure that the sync_id is the correct one for the plenary, and
        # prevents admins from abusing other rooms.  But we'll need to change this
        # if we ever want more than one video container with sync on a plenary at
        # once.
        if payload['action'] == "play":
            time_index = payload.get('time_index', 0)
            VideoSync.objects.start(
                sync_id=plenary.channel_group_name,
                channel_group_name=plenary.channel_group_name,
                time_index=time_index)
            track("start_play_for_all", self.scope['user'], plenary=plenary)
        elif payload['action'] == "pause":
            VideoSync.objects.pause_for_all(sync_id=plenary.channel_group_name)
            track("stop_play_for_all", self.scope['user'], plenary=plenary)
        elif payload['action'] == "endSync":
            VideoSync.objects.end_sync(sync_id=plenary.channel_group_name)
Exemplo n.º 10
0
 def handle_error(self, error):
     data = prepare_message(type='error', error=error)
     self.send(text_data=data['text'])
     track("error", self.scope['user'], data)
Exemplo n.º 11
0
 def handle_record_speaker_stats(self, data, breakout):
     track("record_speaker_stats",
           self.scope['user'],
           {'speakerStats': data['payload']['speakerStats']},
           breakout=breakout)
Exemplo n.º 12
0
 def disconnect(self, close_code):
     try:
         plenary = Plenary.objects.get(slug=self.slug)
     except Plenary.DoesNotExist:
         return self.handle_error('Plenary not found')
     track("leave_plenary", self.scope['user'], plenary=plenary)
Exemplo n.º 13
0
    def handle_breakout(self, data, plenary):
        is_admin = plenary.has_admin(self.scope['user'])
        admin_required_error = lambda: self.handle_error(
            "Must be an admin to do that.")

        payload = data['payload']
        action = payload['action']

        if not is_admin and plenary.breakout_mode != "user":
            return admin_required_error()

        def respond_with_breakouts():
            # Not-too-efficient strategy: always respond with the full list of
            # breakouts from a new database query.  We can optimize this later if
            # needed.
            broadcast(
                plenary.channel_group_name,
                type='breakout_receive',
                payload=[b.serialize() for b in plenary.breakout_set.active()])

        # Handle actions
        if action == 'create':
            if not is_admin and plenary.breakout_mode != "user":
                return admin_required_error()
            if 'title' not in payload:
                return self.handle_error("Missing 'title'")
            if is_admin:
                etherpad_initial_text = payload.get('etherpad_initial_text',
                                                    '')
            else:
                etherpad_initial_text = None
            breakout = Breakout(
                plenary=plenary,
                title=payload['title'],
                etherpad_initial_text=etherpad_initial_text,
                max_attendees=payload.get('max_attendees') or 10,
                is_proposal=(not is_admin
                             or payload.get('is_proposal', False)),
                proposed_by=self.scope['user'])
            breakout.full_clean()
            breakout.save()
            if breakout.is_proposal:
                track("propose_breakout",
                      self.scope['user'],
                      breakout=breakout)
            return respond_with_breakouts()

        # For all actions other than create, we expect payload['id'] to contain the
        # id of the breakout to operate on.

        try:
            breakout = plenary.breakout_set.active().get(id=payload['id'])
        except (Breakout.DoesNotExist, KeyError):
            return self.handle_error("Breakout not found.")

        if action == 'delete':
            if not is_admin:
                return admin_required_error()
            breakout.active = False
            breakout.save()
            return respond_with_breakouts()

        elif action == 'modify':
            can_modify = (is_admin
                          or (plenary.breakout_mode == "user"
                              and breakout.proposed_by == self.scope['user']))
            if not can_modify:
                if plenary.breakout_mode == "user":
                    return self.handle_error(
                        "Must be breakout proposer or admin to do that")
                return admin_required_error()
            if 'title' in payload:
                breakout.title = payload['title']
            if is_admin and 'max_attendees' in payload:
                breakout.max_attendees = payload['max_attendees']
            breakout.save()
            return respond_with_breakouts()

        elif action == 'approve':
            if not is_admin:
                return admin_required_error()

            breakout.is_proposal = not breakout.is_proposal
            breakout.save()
            return respond_with_breakouts()

        elif action == 'vote':
            if plenary.breakout_mode != "user":
                return self.handle_error(
                    "Can only vote on user-proposed breakouts")
            # Toggle vote
            if breakout.votes.filter(pk=self.scope['user'].pk).exists():
                breakout.votes.remove(self.scope['user'])
            else:
                breakout.votes.add(self.scope['user'])
            track("change_breakout_vote",
                  self.scope['user'],
                  breakout=breakout)
            return respond_with_breakouts()