Example #1
0
def create_event(account_id, event_id, extra_args):
    with session_scope(account_id) as db_session:
        account = db_session.query(Account).get(account_id)
        event = db_session.query(Event).get(event_id)
        remote_create_event = module_registry[account.provider]. \
            remote_create_event

        remote_create_event(account, event, db_session, extra_args)

        notify_participants = extra_args.get('notify_participants', False)
        cancelled_participants = extra_args.get('cancelled_participants', [])
        # Do we need to send an RSVP message?
        # We use gmail's sendNotification API for google accounts.
        # but we need create and send an iCalendar invite ourselves
        # for non-gmail accounts.
        if notify_participants and account.provider != 'gmail':
            ical_file = generate_icalendar_invite(event).to_ical()
            send_invite(ical_file, event, account, invite_type='request')

            if cancelled_participants != []:
                # Some people got removed from the event. Send them a
                # cancellation email.
                event.status = 'cancelled'
                event.participants = cancelled_participants
                ical_file = generate_icalendar_invite(event,
                                                      invite_type='cancel').to_ical()
                send_invite(ical_file, event, account, invite_type='cancel')
Example #2
0
def create_event(account_id, event_id, extra_args):
    with session_scope(account_id) as db_session:
        account = db_session.query(Account).get(account_id)
        event = db_session.query(Event).get(event_id)
        remote_create_event = module_registry[account.provider]. \
            remote_create_event

        remote_create_event(account, event, db_session, extra_args)

        notify_participants = extra_args.get('notify_participants', False)
        cancelled_participants = extra_args.get('cancelled_participants', [])
        # Do we need to send an RSVP message?
        # We use gmail's sendNotification API for google accounts.
        # but we need create and send an iCalendar invite ourselves
        # for non-gmail accounts.
        if notify_participants and account.provider != 'gmail':
            ical_file = generate_icalendar_invite(event).to_ical()
            send_invite(ical_file, event, account, invite_type='request')

            if cancelled_participants != []:
                # Some people got removed from the event. Send them a
                # cancellation email.
                event.status = 'cancelled'
                event.participants = cancelled_participants
                ical_file = generate_icalendar_invite(event,
                                                      invite_type='cancel').to_ical()
                send_invite(ical_file, event, account, invite_type='cancel')
Example #3
0
def test_invite_generation(event, default_account):
    from inbox.events.ical import generate_icalendar_invite

    event.sequence_number = 1
    event.participants = [{'email': '*****@*****.**'},
                          {'email': '*****@*****.**'}]
    cal = generate_icalendar_invite(event)
    assert cal['method'] == 'REQUEST'

    for component in cal.walk():
        if component.name == "VEVENT":
            assert component.get('summary') == event.title
            assert int(component.get('sequence')) == event.sequence_number
            assert component.get('location') == event.location

            attendees = component.get('attendee', [])

            # the iCalendar python module doesn't return a list when
            # there's only one attendee. Go figure.
            if not isinstance(attendees, list):
                attendees = [attendees]

            for attendee in attendees:
                email = unicode(attendee)
                # strip mailto: if it exists
                if email.lower().startswith('mailto:'):
                    email = email[7:]

                assert email in ['*****@*****.**', '*****@*****.**']
Example #4
0
def test_invite_generation(event, default_account):
    from inbox.events.ical import generate_icalendar_invite

    event.sequence_number = 1
    event.participants = [{'email': '*****@*****.**'},
                          {'email': '*****@*****.**'}]
    cal = generate_icalendar_invite(event)
    assert cal['method'] == 'REQUEST'

    for component in cal.walk():
        if component.name == "VEVENT":
            assert component.get('summary') == event.title
            assert int(component.get('sequence')) == event.sequence_number
            assert component.get('location') == event.location

            attendees = component.get('attendee', [])

            # the iCalendar python module doesn't return a list when
            # there's only one attendee. Go figure.
            if not isinstance(attendees, list):
                attendees = [attendees]

            for attendee in attendees:
                email = unicode(attendee)
                # strip mailto: if it exists
                if email.lower().startswith('mailto:'):
                    email = email[7:]

                assert email in ['*****@*****.**', '*****@*****.**']
Example #5
0
def delete_event(account_id, event_id, extra_args):
    with session_scope(account_id) as db_session:
        account = db_session.query(Account).get(account_id)
        event = db_session.query(Event).get(event_id)
        notify_participants = extra_args.get('notify_participants', False)

        remote_delete_event = module_registry[account.provider]. \
            remote_delete_event
        event_uid = extra_args.pop('event_uid', None)
        calendar_name = extra_args.pop('calendar_name', None)

        # The calendar_uid argument is required for some providers, like EAS.
        calendar_uid = extra_args.pop('calendar_uid', None)

        if event.calendar == account.emailed_events_calendar:
            return

        remote_delete_event(account, event_uid, calendar_name, calendar_uid,
                            db_session, extra_args)

        # Finally, update the event.
        event.sequence_number += 1
        event.status = 'cancelled'
        db_session.commit()

        if notify_participants and account.provider != 'gmail':
            ical_file = generate_icalendar_invite(event,
                                                  invite_type='cancel').to_ical()

            send_invite(ical_file, event, account, invite_type='cancel')
Example #6
0
def update_event(account_id, event_id, extra_args):
    with session_scope(account_id) as db_session:
        account = db_session.query(Account).get(account_id)
        event = db_session.query(Event).get(event_id)

        # Update our copy of the event before sending it.
        if 'event_data' in extra_args:
            data = extra_args['event_data']
            for attr in Event.API_MODIFIABLE_FIELDS:
                if attr in extra_args['event_data']:
                    setattr(event, attr, data[attr])

            event.sequence_number += 1

        # It doesn't make sense to update or delete an event we imported from
        # an iCalendar file.
        if event.calendar == account.emailed_events_calendar:
            return

        remote_update_event = module_registry[account.provider]. \
            remote_update_event

        remote_update_event(account, event, db_session, extra_args)

        notify_participants = extra_args.get('notify_participants', False)

        if notify_participants and account.provider != 'gmail':
            ical_file = generate_icalendar_invite(event).to_ical()
            send_invite(ical_file, event, account, invite_type='update')

        db_session.commit()
Example #7
0
def test_invite_generation(event, default_account):
    from inbox.events.ical import generate_icalendar_invite

    event.sequence_number = 1
    event.participants = [{
        "email": "*****@*****.**"
    }, {
        "email": "*****@*****.**"
    }]
    cal = generate_icalendar_invite(event)
    assert cal["method"] == "REQUEST"

    for component in cal.walk():
        if component.name == "VEVENT":
            assert component.get("summary") == event.title
            assert int(component.get("sequence")) == event.sequence_number
            assert component.get("location") == event.location

            attendees = component.get("attendee", [])

            # the iCalendar python module doesn't return a list when
            # there's only one attendee. Go figure.
            if not isinstance(attendees, list):
                attendees = [attendees]

            for attendee in attendees:
                email = str(attendee)
                # strip mailto: if it exists
                if email.lower().startswith("mailto:"):
                    email = email[7:]

                assert email in ["*****@*****.**", "*****@*****.**"]
Example #8
0
def update_event(account_id, event_id, db_session, extra_args):
    account = db_session.query(Account).get(account_id)
    event = db_session.query(Event).get(event_id)

    remote_update_event = module_registry[account.provider].remote_update_event

    remote_update_event(account, event, db_session, extra_args)

    notify_participants = extra_args.get('notify_participants', False)

    if notify_participants and account.provider != 'gmail':
        ical_file = generate_icalendar_invite(event).to_ical()
        send_invite(ical_file, event, account, invite_type='update')
Example #9
0
def create_event(account_id, event_id, db_session, extra_args):
    account = db_session.query(Account).get(account_id)
    event = db_session.query(Event).get(event_id)
    remote_create_event = module_registry[account.provider].remote_create_event

    remote_create_event(account, event, db_session, extra_args)

    notify_participants = extra_args.get('notify_participants', False)
    # Do we need to send an RSVP message?
    # We use gmail's sendNotification API for google accounts.
    # but we need create and send an iCalendar invite ourselves
    # for non-gmail accounts.
    if notify_participants and account.provider != 'gmail':
        ical_file = generate_icalendar_invite(event).to_ical()
        send_invite(ical_file, event, account, invite_type='request')
Example #10
0
def update_event(account_id, event_id, db_session, extra_args):
    account = db_session.query(Account).get(account_id)
    event = db_session.query(Event).get(event_id)

    # It doesn't make sense to update or delete an event we imported from
    # an iCalendar file.
    if event.calendar == account.emailed_events_calendar:
        return

    remote_update_event = module_registry[account.provider].remote_update_event

    remote_update_event(account, event, db_session, extra_args)

    notify_participants = extra_args.get('notify_participants', False)

    if notify_participants and account.provider != 'gmail':
        ical_file = generate_icalendar_invite(event).to_ical()
        send_invite(ical_file, event, account, invite_type='update')
Example #11
0
def event_delete_api(public_id):
    g.parser.add_argument('notify_participants',
                          type=strict_bool,
                          location='args')
    args = strict_parse_args(g.parser, request.args)
    notify_participants = args['notify_participants']

    valid_public_id(public_id)
    try:
        event = g.db_session.query(Event).filter_by(
            public_id=public_id, namespace_id=g.namespace.id).one()
    except NoResultFound:
        raise NotFoundError("Couldn't find event {0}".format(public_id))
    if event.calendar.read_only:
        raise InputError(
            'Cannot delete event {} from read_only calendar.'.format(
                public_id))

    # Set the local event status to 'cancelled' rather than deleting it,
    # in order to be consistent with how we sync deleted events from the
    # remote, and consequently return them through the events, delta sync APIs
    event.sequence_number += 1
    event.status = 'cancelled'
    g.db_session.commit()

    account = g.namespace.account

    # FIXME @karim: do this in the syncback thread instead.
    if notify_participants and account.provider != 'gmail':
        ical_file = generate_icalendar_invite(event,
                                              invite_type='cancel').to_ical()

        send_invite(ical_file, event, account, invite_type='cancel')

    schedule_action('delete_event',
                    event,
                    g.namespace.id,
                    g.db_session,
                    event_uid=event.uid,
                    calendar_name=event.calendar.name,
                    calendar_uid=event.calendar.uid,
                    notify_participants=notify_participants)

    return g.encoder.jsonify(None)
Example #12
0
def create_email(from_name,
                 from_email,
                 reply_to,
                 inbox_uid,
                 to_addr,
                 cc_addr,
                 bcc_addr,
                 subject,
                 html,
                 in_reply_to,
                 references,
                 attachments,
                 event=None):
    """
    Creates a MIME email message (both body and sets the needed headers).

    Parameters
    ----------
    from_name: string
        The name aka phrase of the sender.
    from_email: string
        The sender's email address.
    to_addr, cc_addr, bcc_addr: list of pairs (name, email_address), or None
        Message recipients.
    reply_to: tuple or None
        Indicates the mailbox in (name, email_address) format to which
        the author of the message suggests that replies be sent.
    subject : string
        a utf-8 encoded string
    html : string
        a utf-8 encoded string
    in_reply_to: string or None
        If this message is a reply, the Message-Id of the message being replied
        to.
    references: list or None
        If this message is a reply, the Message-Ids of prior messages in the
        thread.
    attachments: list of dicts, optional
        a list of dicts(filename, data, content_type)
    """
    html = html if html else ''
    plaintext = html2text(html)

    # Create a multipart/alternative message
    msg = mime.create.multipart('alternative')
    msg.append(mime.create.text('plain', plaintext),
               mime.create.text('html', html))

    if event:
        ical_txt = generate_icalendar_invite(event).to_ical()
        msg.append(
            mime.create.text('calendar; method=REQUEST',
                             ical_txt,
                             charset='utf8'))

    # Create an outer multipart/mixed message
    if attachments:
        text_msg = msg
        msg = mime.create.multipart('mixed')

        # The first part is the multipart/alternative text part
        msg.append(text_msg)

        # The subsequent parts are the attachment parts
        for a in attachments:
            # Disposition should be inline if we add Content-ID
            msg.append(
                mime.create.attachment(a['content_type'],
                                       a['data'],
                                       filename=a['filename'],
                                       disposition='attachment'))

    msg.headers['Subject'] = subject if subject else ''

    # Gmail sets the From: header to the default sending account. We can
    # however set our own custom phrase i.e. the name that appears next to the
    # email address (useful if the user has multiple aliases and wants to
    # specify which to send as), see: http://lee-phillips.org/gmailRewriting/
    # For other providers, we simply use name = ''
    from_addr = address.EmailAddress(from_name, from_email)
    msg.headers['From'] = from_addr.full_spec()

    # Need to set these headers so recipients know we sent the email to them
    # TODO(emfree): should these really be unicode?
    if to_addr:
        full_to_specs = [
            _get_full_spec_without_validation(name, spec)
            for name, spec in to_addr
        ]
        msg.headers['To'] = u', '.join(full_to_specs)
    if cc_addr:
        full_cc_specs = [
            _get_full_spec_without_validation(name, spec)
            for name, spec in cc_addr
        ]
        msg.headers['Cc'] = u', '.join(full_cc_specs)
    if bcc_addr:
        full_bcc_specs = [
            _get_full_spec_without_validation(name, spec)
            for name, spec in bcc_addr
        ]
        msg.headers['Bcc'] = u', '.join(full_bcc_specs)
    if reply_to:
        # reply_to is only ever a list with one element
        msg.headers['Reply-To'] = _get_full_spec_without_validation(
            reply_to[0][0], reply_to[0][1])

    add_inbox_headers(msg, inbox_uid)

    if in_reply_to:
        msg.headers['In-Reply-To'] = in_reply_to
    if references:
        msg.headers['References'] = '\t'.join(references)

    rfcmsg = _rfc_transform(msg)

    return rfcmsg