コード例 #1
0
def event_update_api(public_id):
    valid_public_id(public_id)
    try:
        event = g.db_session.query(Event).filter(
            Event.public_id == public_id,
            Event.namespace_id == g.namespace.id).one()
    except NoResultFound:
        raise NotFoundError("Couldn't find event {0}".format(public_id))
    if event.read_only:
        raise InputError('Cannot update read_only event.')

    data = request.get_json(force=True)
    valid_event_update(data, g.namespace, g.db_session)

    if 'participants' in data:
        for p in data['participants']:
            if 'status' not in p:
                p['status'] = 'noreply'

    for attr in ['title', 'description', 'location', 'when', 'participants']:
        if attr in data:
            setattr(event, attr, data[attr])

    g.db_session.commit()

    schedule_action('update_event', event, g.namespace.id, g.db_session,
                    calendar_uid=event.calendar.uid)

    return g.encoder.jsonify(event)
コード例 #2
0
ファイル: ns_api.py プロジェクト: askagirl/sync-engine
def event_update_api(public_id):
    valid_public_id(public_id)
    try:
        event = g.db_session.query(Event).filter(
            Event.public_id == public_id,
            Event.namespace_id == g.namespace.id).one()
    except NoResultFound:
        raise NotFoundError("Couldn't find event {0}".format(public_id))
    if event.read_only:
        raise InputError('Cannot update read_only event.')

    data = request.get_json(force=True)
    valid_event_update(data, g.namespace, g.db_session)

    if 'participants' in data:
        for p in data['participants']:
            if 'status' not in p:
                p['status'] = 'noreply'

    for attr in ['title', 'description', 'location', 'when', 'participants']:
        if attr in data:
            setattr(event, attr, data[attr])

    g.db_session.commit()

    schedule_action('update_event', event, g.namespace.id, g.db_session,
                    calendar_uid=event.calendar.uid)

    return g.encoder.jsonify(event)
コード例 #3
0
def send_draft(account_id, draft_id, db_session):
    """Send a previously created draft."""
    _send(account_id, draft_id, db_session)
    draft = db_session.query(Message).get(draft_id)
    # Schedule the deletion separately (we don't want to resend if sending
    # succeeds but deletion fails!)
    schedule_action('delete_draft', draft, draft.namespace.id, db_session)
コード例 #4
0
ファイル: __init__.py プロジェクト: 0xcd03/inbox
def send_draft(account_id, draft_id, db_session):
    """Send a previously created draft."""
    _send(account_id, draft_id, db_session)
    draft = db_session.query(Message).get(draft_id)
    # Schedule the deletion separately (we don't want to resend if sending
    # succeeds but deletion fails!)
    schedule_action('delete_draft', draft, draft.namespace.id, db_session)
コード例 #5
0
ファイル: ns_api.py プロジェクト: apolmig/inbox
def event_update_api(public_id):
    valid_public_id(public_id)
    data = request.get_json(force=True)

    valid_event_update(data, g.namespace, g.db_session)

    # Convert the data into our types where necessary
    # e.g. timestamps, participant_list
    if 'start' in data:
        data['start'] = datetime.utcfromtimestamp(int(data.get('start')))
    if 'end' in data:
        data['end'] = datetime.utcfromtimestamp(int(data.get('end')))
    if 'participants' in data:
        data['participant_list'] = []
        for p in data['participants']:
            if 'status' not in p:
                p['status'] = 'noreply'
            data['participant_list'].append(p)
        del data['participants']

    result = events.crud.update(g.namespace, g.db_session, public_id, data)

    if result is None:
        raise NotFoundError("Couldn't find event {0}".format(public_id))

    schedule_action('update_event', result, g.namespace.id, g.db_session)
    return g.encoder.jsonify(result)
コード例 #6
0
ファイル: ns_api.py プロジェクト: olofster/inbox
def event_update_api(public_id):
    valid_public_id(public_id)
    data = request.get_json(force=True)

    valid_event_update(data, g.namespace, g.db_session)

    # Convert the data into our types where necessary
    # e.g. timestamps, participant_list
    if 'start' in data:
        data['start'] = datetime.utcfromtimestamp(int(data.get('start')))
    if 'end' in data:
        data['end'] = datetime.utcfromtimestamp(int(data.get('end')))
    if 'participants' in data:
        data['participant_list'] = []
        for p in data['participants']:
            if 'status' not in p:
                p['status'] = 'noreply'
            data['participant_list'].append(p)
        del data['participants']

    result = events.crud.update(g.namespace, g.db_session, public_id, data)

    if result is None:
        raise NotFoundError("Couldn't find event {0}".format(public_id))

    schedule_action('update_event', result, g.namespace.id, g.db_session)
    return g.encoder.jsonify(result)
コード例 #7
0
def test_action_scheduling(db, default_account):
    event = add_fake_event(db.session, default_account.namespace.id)

    schedule_action('create_event', event, default_account.namespace.id,
                    db.session)
    db.session.commit()

    entry = db.session.query(ActionLog).filter(
        ActionLog.namespace_id == default_account.namespace.id,
        ActionLog.action == 'create_event').one()

    assert entry.discriminator == 'actionlog'
    assert entry.table_name == 'event' and entry.record_id == event.id
    assert not entry.extra_args

    schedule_action('delete_event',
                    event,
                    default_account.namespace.id,
                    db.session,
                    event_uid=event.uid,
                    calendar_name=event.calendar.name,
                    calendar_uid=event.calendar.uid)
    db.session.commit()

    entry = db.session.query(ActionLog).filter(
        ActionLog.namespace_id == default_account.namespace.id,
        ActionLog.action == 'delete_event').one()

    assert entry.discriminator == 'actionlog'
    assert entry.table_name == 'event' and entry.record_id == event.id
    assert entry.extra_args == \
        dict(event_uid=event.uid, calendar_name=event.calendar.name,
             calendar_uid=event.calendar.uid)
コード例 #8
0
ファイル: base.py プロジェクト: bsorin/inbox
def delete_draft(db_session, account, draft_public_id):
    """ Delete the draft with public_id = `draft_public_id`. """
    draft = db_session.query(Message).filter(
        Message.public_id == draft_public_id).one()
    thread = draft.thread
    namespace = draft.namespace

    assert draft.is_draft

    # Delete remotely.
    schedule_action('delete_draft', draft, draft.namespace.id, db_session,
                    inbox_uid=draft.inbox_uid,
                    message_id_header=draft.message_id_header)

    db_session.delete(draft)

    # Delete the thread if it would now be empty.
    if not thread.messages:
        db_session.delete(thread)
    elif not thread.drafts:
        # Otherwise, remove the drafts tag from the thread if there are no more
        # drafts on it.
        thread.remove_tag(namespace.tags['drafts'])

    db_session.commit()
コード例 #9
0
def test_action_scheduling(db, default_account):
    event = add_fake_event(db.session, default_account.namespace.id)

    schedule_action('create_event', event, default_account.namespace.id,
                    db.session)
    db.session.commit()

    entry = db.session.query(ActionLog).filter(
        ActionLog.namespace_id == default_account.namespace.id,
        ActionLog.action == 'create_event').one()

    assert entry.discriminator == 'actionlog'
    assert entry.table_name == 'event' and entry.record_id == event.id
    assert not entry.extra_args

    schedule_action('delete_event', event, default_account.namespace.id,
                    db.session, event_uid=event.uid,
                    calendar_name=event.calendar.name,
                    calendar_uid=event.calendar.uid)
    db.session.commit()

    entry = db.session.query(ActionLog).filter(
        ActionLog.namespace_id == default_account.namespace.id,
        ActionLog.action == 'delete_event').one()

    assert entry.discriminator == 'actionlog'
    assert entry.table_name == 'event' and entry.record_id == event.id
    assert entry.extra_args == \
        dict(event_uid=event.uid, calendar_name=event.calendar.name,
             calendar_uid=event.calendar.uid)
コード例 #10
0
ファイル: base.py プロジェクト: Analect/sync-engine
def delete_draft(db_session, account, draft):
    """ Delete the given draft. """
    thread = draft.thread
    namespace = draft.namespace
    assert draft.is_draft

    # Delete remotely.
    schedule_action('delete_draft',
                    draft,
                    draft.namespace.id,
                    db_session,
                    inbox_uid=draft.inbox_uid,
                    message_id_header=draft.message_id_header)

    db_session.delete(draft)

    # Delete the thread if it would now be empty.
    if not thread.messages:
        db_session.delete(thread)
    elif not thread.drafts:
        # Otherwise, remove the drafts tag from the thread if there are no more
        # drafts on it.
        thread.remove_tag(namespace.tags['drafts'])

    db_session.commit()
コード例 #11
0
ファイル: ns_api.py プロジェクト: wakermahmud/sync-engine
def draft_send_api():
    if request.content_type == "message/rfc822":
        msg = create_draft_from_mime(g.namespace.account, request.data,
                                     g.db_session)
        validate_draft_recipients(msg)
        resp = send_raw_mime(g.namespace.account, g.db_session, msg)
        return resp

    data = request.get_json(force=True)
    draft_public_id = data.get('draft_id')
    if draft_public_id is not None:
        draft = get_draft(draft_public_id, data.get('version'), g.namespace.id,
                          g.db_session)
        schedule_action('delete_draft',
                        draft,
                        draft.namespace.id,
                        g.db_session,
                        inbox_uid=draft.inbox_uid,
                        message_id_header=draft.message_id_header)
    else:
        draft = create_draft(data, g.namespace, g.db_session, syncback=False)

    validate_draft_recipients(draft)
    resp = send_draft(g.namespace.account, draft, g.db_session)
    return resp
コード例 #12
0
ファイル: update.py プロジェクト: zkryakgul/sync-engine
def update_message_labels(message, db_session, added_categories,
                          removed_categories, optimistic):
    special_label_map = {
        'inbox': '\\Inbox',
        'important': '\\Important',
        'all': '\\All',  # STOPSHIP(emfree): verify
        'trash': '\\Trash',
        'spam': '\\Spam'
    }

    validate_labels(db_session, added_categories, removed_categories)

    added_labels = []
    removed_labels = []
    for category in added_categories:
        if category.name in special_label_map:
            added_labels.append(special_label_map[category.name])
        elif category.name in ('drafts', 'sent'):
            raise InputError('The "{}" label cannot be changed'.format(
                category.name))
        else:
            added_labels.append(category.display_name)

    for category in removed_categories:
        if category.name in special_label_map:
            removed_labels.append(special_label_map[category.name])
        elif category.name in ('drafts', 'sent'):
            raise InputError('The "{}" label cannot be changed'.format(
                category.name))
        else:
            removed_labels.append(category.display_name)

    if optimistic:
        # Optimistically update message state,
        # in a manner consistent with Gmail.
        for cat in added_categories:
            message.categories.add(cat)

        for cat in removed_categories:
            # Removing '\\All'/ \\Trash'/ '\\Spam' does not do anything on Gmail
            # i.e. does not move the message to a different folder, so don't
            # discard the corresponding category yet.
            # If one of these has been *added* too, apply_gmail_label_rules()
            # will do the right thing to ensure mutual exclusion.
            if cat.name not in ('all', 'trash', 'spam'):
                message.categories.discard(cat)

        apply_gmail_label_rules(db_session, message, added_categories,
                                removed_categories)

        if removed_labels or added_labels:
            message.categories_changes = True

    if removed_labels or added_labels:
        schedule_action('change_labels',
                        message,
                        message.namespace_id,
                        removed_labels=removed_labels,
                        added_labels=added_labels,
                        db_session=db_session)
コード例 #13
0
ファイル: update.py プロジェクト: rskumar/sync-engine
def update_message_folder(message, db_session, category):
    # STOPSHIP(emfree): what about sent/inbox duality?
    if category not in message.categories:
        message.categories = [category]
        message.categories_changes = True
        schedule_action('move', message, message.namespace_id, db_session,
                        destination=category.display_name)
コード例 #14
0
ファイル: ns_api.py プロジェクト: htk/sync-engine
def folder_label_update_api(public_id):
    category_type = g.namespace.account.category_type
    valid_public_id(public_id)
    try:
        category = g.db_session.query(Category).filter(
            Category.namespace_id == g.namespace.id,
            Category.public_id == public_id).one()
    except NoResultFound:
        raise InputError("Couldn't find {} {}".format(
            category_type, public_id))
    if category.name is not None:
        raise InputError("Cannot modify a standard {}".format(category_type))

    data = request.get_json(force=True)
    display_name = data.get('display_name')
    valid_display_name(g.namespace.id, category_type, display_name,
                       g.db_session)

    current_name = category.display_name
    category.display_name = display_name
    g.db_session.flush()

    if category_type == 'folder':
        schedule_action('update_folder', category, g.namespace.id,
                        g.db_session, old_name=current_name)
    else:
        schedule_action('update_label', category, g.namespace.id,
                        g.db_session, old_name=current_name)

    # TODO[k]: Update corresponding folder/ label once syncback is successful,
    # rather than waiting for sync to pick it up?

    return g.encoder.jsonify(category)
コード例 #15
0
def delete_draft(db_session, account, draft_public_id):
    """ Delete the draft with public_id = `draft_public_id`. """
    draft = db_session.query(Message).filter(
        Message.public_id == draft_public_id).one()
    thread = draft.thread
    namespace = draft.namespace

    assert draft.is_draft

    # Delete remotely.
    schedule_action('delete_draft',
                    draft,
                    draft.namespace.id,
                    db_session,
                    inbox_uid=draft.inbox_uid,
                    message_id_header=draft.message_id_header)

    db_session.delete(draft)

    # Delete the thread if it would now be empty.
    if not thread.messages:
        db_session.delete(thread)
    elif not thread.drafts:
        # Otherwise, remove the drafts tag from the thread if there are no more
        # drafts on it.
        thread.remove_tag(namespace.tags['drafts'])

    db_session.commit()
コード例 #16
0
ファイル: ical.py プロジェクト: rskumar/sync-engine
def process_nylas_rsvps(db_session, message, account, rsvps):
    # The invite sending code generates invites with uids of the form
    # `[email protected]`. We couldn't use Event.uid for this because
    # it wouldn't work with Exchange (Exchange uids are of the form
    # 1:2323 and aren't guaranteed to be unique).
    new_uids = [
        _cleanup_nylas_uid(event.uid) for event in rsvps
        if '@nylas.com' in event.uid
    ]

    # Drop uids which aren't base36 uids.
    new_uids = [uid for uid in new_uids if valid_base36(uid)]

    # Get the list of events which share a uid with those we received.
    # Note that we're excluding events from "Emailed events" because
    # we don't want to process RSVPs to invites we received.
    existing_events = db_session.query(Event).filter(
        Event.namespace_id == account.namespace.id,
        Event.calendar_id != account.emailed_events_calendar_id,
        Event.public_id.in_(new_uids)).all()

    existing_events_table = {
        event.public_id: event
        for event in existing_events
    }

    for event in rsvps:
        event_uid = _cleanup_nylas_uid(event.uid)
        if event_uid not in existing_events_table:
            # We've received an RSVP to an event we never heard about. Save it,
            # maybe we'll sync the invite later.
            event.message = message
        else:
            # This is an event we already have in the db.
            existing_event = existing_events_table[event_uid]

            # Is the current event an update?
            if existing_event.sequence_number == event.sequence_number:
                merged_participants = existing_event.\
                    _partial_participants_merge(event)

                # We have to do this mumbo-jumbo because MutableList does
                # not register changes to nested elements.
                # We could probably change MutableList to handle it (see:
                # https://groups.google.com/d/msg/sqlalchemy/i2SIkLwVYRA/mp2WJFaQxnQJ)
                # but it seems very brittle.
                existing_event.participants = []
                for participant in merged_participants:
                    existing_event.participants.append(participant)

                # We need to sync back changes to the event manually
                if existing_event.calendar != account.emailed_events_calendar:
                    schedule_action('update_event',
                                    existing_event,
                                    existing_event.namespace.id,
                                    db_session,
                                    calendar_uid=existing_event.calendar.uid)

                db_session.flush()
コード例 #17
0
ファイル: __init__.py プロジェクト: MobileWorks/inbox
def _send(account_id, draft_id, db_session):
    """Send the draft with id = `draft_id`."""
    account = db_session.query(Account).get(account_id)

    try:
        sendmail_client = get_sendmail_client(account)
    except SendMailException:
        log.error('Send Error', message="Failed to create sendmail client.",
                  account_id=account_id)
        raise
    try:
        draft = db_session.query(Message).filter(
            Message.id == draft_id).one()

    except NoResultFound:
        log.info('Send Error',
                 message='NoResultFound for draft_id {0}'.format(draft_id),
                 account_id=account_id)
        raise SendMailException('No draft with id {0}'.format(draft_id))

    except MultipleResultsFound:
        log.info('Send Error',
                 message='MultipleResultsFound for draft_id'
                         '{0}'.format(draft_id),
                 account_id=account_id)
        raise SendMailException('Multiple drafts with id {0}'.format(
            draft_id))

    if not draft.is_draft or draft.is_sent:
        return

    recipients = Recipients(draft.to_addr, draft.cc_addr, draft.bcc_addr)

    if not draft.is_reply:
        sendmail_client.send_new(db_session, draft, recipients)
    else:
        sendmail_client.send_reply(db_session, draft, recipients)

    if account.provider == 'icloud':
        # Special case because iCloud doesn't save
        # sent messages.
        schedule_action('save_sent_email', draft, draft.namespace.id,
                        db_session)

    # Update message
    draft.is_sent = True
    draft.is_draft = False
    draft.state = 'sent'

    # Update thread
    sent_tag = account.namespace.tags['sent']
    draft_tag = account.namespace.tags['drafts']
    draft.thread.apply_tag(sent_tag)
    # Remove the drafts tag from the thread if there are no more drafts.
    if not draft.thread.drafts:
        draft.thread.remove_tag(draft_tag)

    return draft
コード例 #18
0
ファイル: update.py プロジェクト: busseyl/sync-engine
def update_message_labels(message, db_session, added_categories,
                          removed_categories, optimistic):
    special_label_map = {
        'inbox': '\\Inbox',
        'important': '\\Important',
        'all': '\\All',  # STOPSHIP(emfree): verify
        'trash': '\\Trash',
        'spam': '\\Spam'
    }

    validate_labels(db_session, added_categories, removed_categories)

    added_labels = []
    removed_labels = []
    for category in added_categories:
        if category.name in special_label_map:
            added_labels.append(special_label_map[category.name])
        elif category.name in ('drafts', 'sent'):
            raise InputError('The "{}" label cannot be changed'.
                             format(category.name))
        else:
            added_labels.append(category.display_name)

    for category in removed_categories:
        if category.name in special_label_map:
            removed_labels.append(special_label_map[category.name])
        elif category.name in ('drafts', 'sent'):
            raise InputError('The "{}" label cannot be changed'.
                             format(category.name))
        else:
            removed_labels.append(category.display_name)

    if optimistic:
        # Optimistically update message state,
        # in a manner consistent with Gmail.
        for cat in added_categories:
            message.categories.add(cat)

        for cat in removed_categories:
            # Removing '\\All'/ \\Trash'/ '\\Spam' does not do anything on Gmail
            # i.e. does not move the message to a different folder, so don't
            # discard the corresponding category yet.
            # If one of these has been *added* too, apply_gmail_label_rules()
            # will do the right thing to ensure mutual exclusion.
            if cat.name not in ('all', 'trash', 'spam'):
                message.categories.discard(cat)

        apply_gmail_label_rules(db_session, message, added_categories, removed_categories)

        if removed_labels or added_labels:
            message.categories_changes = True

    if removed_labels or added_labels:
        schedule_action('change_labels', message, message.namespace_id,
                        removed_labels=removed_labels,
                        added_labels=added_labels,
                        db_session=db_session)
コード例 #19
0
ファイル: ns_api.py プロジェクト: wakermahmud/sync-engine
def event_create_api():
    g.parser.add_argument('notify_participants',
                          type=strict_bool,
                          location='args')
    args = strict_parse_args(g.parser, request.args)
    notify_participants = args['notify_participants']

    data = request.get_json(force=True)
    calendar = get_calendar(data.get('calendar_id'), g.namespace, g.db_session)

    if calendar.read_only:
        raise InputError("Can't create events on read_only calendar.")

    valid_event(data)

    title = data.get('title', '')
    description = data.get('description')
    location = data.get('location')
    when = data.get('when')
    busy = data.get('busy')
    # client libraries can send explicit key = None automagically
    if busy is None:
        busy = True

    participants = data.get('participants')
    if participants is None:
        participants = []

    for p in participants:
        if 'status' not in p:
            p['status'] = 'noreply'

    event = Event(calendar=calendar,
                  namespace=g.namespace,
                  uid=uuid.uuid4().hex,
                  provider_name=g.namespace.account.provider,
                  raw_data='',
                  title=title,
                  description=description,
                  location=location,
                  busy=busy,
                  when=when,
                  read_only=False,
                  is_owner=True,
                  participants=participants,
                  sequence_number=0,
                  source='local')
    g.db_session.add(event)
    g.db_session.flush()

    schedule_action('create_event',
                    event,
                    g.namespace.id,
                    g.db_session,
                    calendar_uid=event.calendar.uid,
                    notify_participants=notify_participants)
    return g.encoder.jsonify(event)
コード例 #20
0
ファイル: ns_api.py プロジェクト: jjayyoung/inbox
def draft_send_api():
    data = request.get_json(force=True)
    if data.get("draft_id") is None:
        if data.get("to") is None:
            return err(400, "Must specify either draft id + version or " "message recipients.")
    else:
        if data.get("version") is None:
            return err(400, "Must specify version to send")

    draft_public_id = data.get("draft_id")
    version = data.get("version")
    if draft_public_id is not None:
        try:
            valid_public_id(draft_public_id)
            draft = g.db_session.query(Message).filter(Message.public_id == draft_public_id).one()
        except InputError:
            return err(400, "Invalid public id {}".format(draft_public_id))
        except NoResultFound:
            return err(404, "No draft found with id {}".format(draft_public_id))

        if draft.namespace != g.namespace:
            return err(404, "No draft found with id {}".format(draft_public_id))

        if draft.is_sent or not draft.is_draft:
            return err(400, "Message with id {} is not a draft".format(draft_public_id))

        if not draft.to_addr:
            return err(400, "No 'to:' recipients specified")

        if draft.version != version:
            return err(
                409,
                "Draft {0}.{1} has already been updated to version {2}".format(draft_public_id, version, draft.version),
            )

        schedule_action("send_draft", draft, g.namespace.id, g.db_session)
    else:
        to = data.get("to")
        cc = data.get("cc")
        bcc = data.get("bcc")
        subject = data.get("subject")
        body = data.get("body")
        try:
            tags = get_tags(data.get("tags"), g.namespace.id, g.db_session)
            files = get_attachments(data.get("files"), g.namespace.id, g.db_session)
            replyto_thread = get_thread(data.get("reply_to_thread"), g.namespace.id, g.db_session)
        except InputError as e:
            return err(404, e.message)

        draft = sendmail.create_draft(
            g.db_session, g.namespace.account, to, subject, body, files, cc, bcc, tags, replyto_thread, syncback=False
        )
        schedule_action("send_directly", draft, g.namespace.id, g.db_session)

    draft.state = "sending"
    return g.encoder.jsonify(draft)
コード例 #21
0
ファイル: update.py プロジェクト: rskumar/sync-engine
def update_message_flags(message, db_session, unread=None, starred=None):
    if unread is not None and unread == message.is_read:
        message.is_read = not unread
        schedule_action('mark_unread', message, message.namespace_id,
                        db_session, unread=unread)

    if starred is not None and starred != message.is_starred:
        message.is_starred = starred
        schedule_action('mark_starred', message, message.namespace_id,
                        db_session, starred=starred)
コード例 #22
0
ファイル: ns_api.py プロジェクト: htk/sync-engine
def event_create_api():
    g.parser.add_argument('notify_participants', type=strict_bool,
                          location='args')
    args = strict_parse_args(g.parser, request.args)
    notify_participants = args['notify_participants']

    data = request.get_json(force=True)
    calendar = get_calendar(data.get('calendar_id'),
                            g.namespace, g.db_session)

    if calendar.read_only:
        raise InputError("Can't create events on read_only calendar.")

    valid_event(data)

    title = data.get('title', '')
    description = data.get('description')
    location = data.get('location')
    when = data.get('when')
    busy = data.get('busy')
    # client libraries can send explicit key = None automagically
    if busy is None:
        busy = True

    participants = data.get('participants')
    if participants is None:
        participants = []

    for p in participants:
        if 'status' not in p:
            p['status'] = 'noreply'

    event = Event(
        calendar=calendar,
        namespace=g.namespace,
        uid=uuid.uuid4().hex,
        provider_name=g.namespace.account.provider,
        raw_data='',
        title=title,
        description=description,
        location=location,
        busy=busy,
        when=when,
        read_only=False,
        is_owner=True,
        participants=participants,
        sequence_number=0,
        source='local')
    g.db_session.add(event)
    g.db_session.flush()

    schedule_action('create_event', event, g.namespace.id, g.db_session,
                    calendar_uid=event.calendar.uid,
                    notify_participants=notify_participants)
    return g.encoder.jsonify(event)
コード例 #23
0
ファイル: ns_api.py プロジェクト: admix/inbox
def draft_send_api():
    data = request.get_json(force=True)
    if data.get('draft_id') is None and data.get('to') is None:
        return err(400, 'Must specify either draft id or message recipients.')

    draft_public_id = data.get('draft_id')
    if draft_public_id is not None:
        try:
            valid_public_id(draft_public_id)
            draft = g.db_session.query(Message).filter(
                Message.public_id == draft_public_id).one()
        except InputError:
            return err(400, 'Invalid public id {}'.format(draft_public_id))
        except NoResultFound:
            return err(404, 'No draft found with id {}'.
                       format(draft_public_id))

        if draft.namespace != g.namespace:
            return err(404, 'No draft found with id {}'.
                       format(draft_public_id))

        if draft.is_sent or not draft.is_draft:
            return err(400, 'Message with id {} is not a draft'.
                       format(draft_public_id))

        if not draft.to_addr:
            return err(400, "No 'to:' recipients specified")

        if not draft.is_latest:
            return err(409, 'Draft {} has already been updated to {}'.format(
                draft_public_id, g.encoder.cereal(draft.most_recent_revision)))

        schedule_action('send_draft', draft, g.namespace.id, g.db_session)
    else:
        to = data.get('to')
        cc = data.get('cc')
        bcc = data.get('bcc')
        subject = data.get('subject')
        body = data.get('body')
        try:
            tags = get_tags(data.get('tags'), g.namespace.id, g.db_session)
            files = get_attachments(data.get('files'), g.namespace.id,
                                    g.db_session)
            replyto_thread = get_thread(data.get('reply_to_thread'),
                                        g.namespace.id, g.db_session)
        except InputError as e:
            return err(404, e.message)

        draft = sendmail.create_draft(g.db_session, g.namespace.account, to,
                                      subject, body, files, cc, bcc,
                                      tags, replyto_thread, syncback=False)
        schedule_action('send_directly', draft, g.namespace.id, g.db_session)

    draft.state = 'sending'
    return g.encoder.jsonify(draft)
コード例 #24
0
ファイル: base.py プロジェクト: raoulwissink/inbox
def delete_draft(db_session, account, draft_public_id):
    """ Delete the draft with public_id = `draft_public_id`. """
    draft = db_session.query(SpoolMessage).filter(SpoolMessage.public_id == draft_public_id).one()

    assert draft.is_draft

    # Delete locally, make sure to delete all previous versions of this draft
    # present locally too.
    _delete_draft_versions(db_session, draft.id)
    # Delete remotely.
    schedule_action("delete_draft", draft, draft.namespace.id, db_session)
コード例 #25
0
ファイル: ical.py プロジェクト: rjmcguire/sync-engine
def process_nylas_rsvps(db_session, message, account, rsvps):
    # The invite sending code generates invites with uids of the form
    # `[email protected]`. We couldn't use Event.uid for this because
    # it wouldn't work with Exchange (Exchange uids are of the form
    # 1:2323 and aren't guaranteed to be unique).
    new_uids = [_cleanup_nylas_uid(event.uid) for event in rsvps
                if '@nylas.com' in event.uid]

    # Drop uids which aren't base36 uids.
    new_uids = [uid for uid in new_uids if valid_base36(uid)]

    # Get the list of events which share a uid with those we received.
    # Note that we're excluding events from "Emailed events" because
    # we don't want to process RSVPs to invites we received.
    existing_events = db_session.query(Event).filter(
        Event.namespace_id == account.namespace.id,
        Event.calendar_id != account.emailed_events_calendar_id,
        Event.public_id.in_(new_uids)).all()

    existing_events_table = {event.public_id: event
                             for event in existing_events}

    for event in rsvps:
        event_uid = _cleanup_nylas_uid(event.uid)
        if event_uid not in existing_events_table:
            # We've received an RSVP to an event we never heard about. Save it,
            # maybe we'll sync the invite later.
            event.message = message
        else:
            # This is an event we already have in the db.
            existing_event = existing_events_table[event_uid]

            # Is the current event an update?
            if existing_event.sequence_number == event.sequence_number:
                merged_participants = existing_event.\
                    _partial_participants_merge(event)

                # We have to do this mumbo-jumbo because MutableList does
                # not register changes to nested elements.
                # We could probably change MutableList to handle it (see:
                # https://groups.google.com/d/msg/sqlalchemy/i2SIkLwVYRA/mp2WJFaQxnQJ)
                # but it seems very brittle.
                existing_event.participants = []
                for participant in merged_participants:
                    existing_event.participants.append(participant)

                # We need to sync back changes to the event manually
                if existing_event.calendar != account.emailed_events_calendar:
                    schedule_action('update_event', existing_event,
                                    existing_event.namespace.id, db_session,
                                    calendar_uid=existing_event.calendar.uid)

                db_session.flush()
コード例 #26
0
def delete_draft(db_session, account, draft_public_id):
    """ Delete the draft with public_id = `draft_public_id`. """
    draft = db_session.query(Message).filter(
        Message.public_id == draft_public_id).one()

    assert draft.is_draft

    # Delete locally, make sure to delete all previous versions of this draft
    # present locally too.
    _delete_draft_versions(db_session, draft.id)
    # Delete remotely.
    schedule_action('delete_draft', draft, draft.namespace.id, db_session)
コード例 #27
0
def draft_send_api():
    data = request.get_json(force=True)
    if data.get('draft_id') is None and data.get('to') is None:
        return err(400, 'Must specify either draft id or message recipients.')

    draft_public_id = data.get('draft_id')
    if draft_public_id is not None:
        try:
            draft = g.db_session.query(Message).filter(
                Message.public_id == draft_public_id).one()
        except NoResultFound:
            return err(404, 'No draft found with id {}'.
                       format(draft_public_id))

        if draft.namespace != g.namespace:
            return err(404, 'No draft found with id {}'.
                       format(draft_public_id))

        if draft.is_sent or not draft.is_draft:
            return err(400, 'Message with id {} is not a draft'.
                       format(draft_public_id))

        if not draft.to_addr:
            return err(400, "No 'to:' recipients specified")

        if not draft.is_latest:
            return err(409, 'Draft {} has already been updated to {}'.format(
                draft_public_id, g.encoder.cereal(draft.most_recent_revision)))

        schedule_action('send_draft', draft, g.namespace.id, g.db_session)
    else:
        to = data.get('to')
        cc = data.get('cc')
        bcc = data.get('bcc')
        subject = data.get('subject')
        body = data.get('body')
        try:
            tags = get_tags(data.get('tags'), g.namespace.id, g.db_session)
            files = get_attachments(data.get('files'), g.namespace.id,
                                    g.db_session)
            replyto_thread = get_thread(data.get('reply_to_thread'),
                                        g.namespace.id, g.db_session)
        except InputError as e:
            return err(404, e.message)

        draft = sendmail.create_draft(g.db_session, g.namespace.account, to,
                                      subject, body, files, cc, bcc,
                                      tags, replyto_thread, syncback=False)
        schedule_action('send_directly', draft, g.namespace.id, g.db_session)

    draft.state = 'sending'
    return g.encoder.jsonify(draft)
コード例 #28
0
ファイル: update.py プロジェクト: zkryakgul/sync-engine-1
def update_message_folder(message, db_session, category, optimistic):
    # STOPSHIP(emfree): what about sent/inbox duality?
    if optimistic:
        message.categories = [category]
        message.categories_changes = True

    schedule_action(
        "move",
        message,
        message.namespace_id,
        db_session,
        destination=category.display_name,
    )
コード例 #29
0
ファイル: update.py プロジェクト: wakermahmud/sync-engine
def update_message_labels(message, db_session, label_public_ids):
    categories = set()
    for id_ in label_public_ids:
        try:
            category = db_session.query(Category).filter(
                Category.namespace_id == message.namespace_id,
                Category.public_id == id_).one()
            categories.add(category)
        except NoResultFound:
            raise InputError(u'Label {} does not exist'.format(id_))

    added_categories = categories - set(message.categories)
    removed_categories = set(message.categories) - categories

    added_labels = []
    removed_labels = []
    special_label_map = {
        'inbox': '\\Inbox',
        'important': '\\Important',
        'all': '\\All',  # STOPSHIP(emfree): verify
        'trash': '\\Trash',
        'spam': '\\Spam'
    }
    for category in added_categories:
        if category.name in special_label_map:
            added_labels.append(special_label_map[category.name])
        elif category.name in ('drafts', 'sent'):
            raise InputError('The "{}" label cannot be changed'.format(
                category.name))
        else:
            added_labels.append(category.display_name)

    for category in removed_categories:
        if category.name in special_label_map:
            removed_labels.append(special_label_map[category.name])
        elif category.name in ('drafts', 'sent'):
            raise InputError('The "{}" label cannot be changed'.format(
                category.name))
        else:
            removed_labels.append(category.display_name)

    # Optimistically update message state.
    message.categories = categories
    if removed_labels or added_labels:
        schedule_action('change_labels',
                        message,
                        message.namespace_id,
                        removed_labels=removed_labels,
                        added_labels=added_labels,
                        db_session=db_session)
コード例 #30
0
ファイル: update.py プロジェクト: askagirl/sync-engine
def update_message_folder(message, db_session, folder_public_id):
    try:
        category = db_session.query(Category).filter(
            Category.namespace_id == message.namespace_id,
            Category.public_id == folder_public_id).one()
    except NoResultFound:
        raise InputError(u'Folder {} does not exist'.
                         format(folder_public_id))

    # STOPSHIP(emfree): what about sent/inbox duality?
    if category not in message.categories:
        message.categories = [category]
        schedule_action('move', message, message.namespace_id, db_session,
                        destination=category.display_name)
コード例 #31
0
ファイル: ns_api.py プロジェクト: vjeantet/inbox
def event_create_api():
    # Handle ical uploads
    if request.headers['content-type'] == 'text/calendar':
        ics_str = request.data
        new_events = events.crud.create_from_ics(g.namespace, g.db_session,
                                                 ics_str)
        if not new_events:
            return err(400, "Couldn't parse .ics file.")

        return g.encoder.jsonify(new_events)

    data = request.get_json(force=True)
    try:
        calendar = get_calendar(data.get('calendar_id'),
                                g.namespace, g.db_session)
    except InputError as e:
        return err(404, e.message)

    if calendar.read_only:
        return err(400, "Can't create events on read_only calendar.")

    try:
        valid_event(data)
    except InputError as e:
        return err(404, e.message)

    title = data.get('title', '')
    description = data.get('description')
    location = data.get('location')
    reminders = data.get('reminders')
    recurrence = data.get('recurrence')
    when = data.get('when')

    participants = data.get('participants', [])
    for p in participants:
        if 'status' not in p:
            p['status'] = 'noreply'

    new_event = events.crud.create(g.namespace, g.db_session,
                                     calendar,
                                     title,
                                     description,
                                     location,
                                     reminders,
                                     recurrence,
                                     when,
                                     participants)

    schedule_action('create_event', new_event, g.namespace.id, g.db_session)
    return g.encoder.jsonify(new_event)
コード例 #32
0
def test_failed_event_creation(db, patched_syncback_task, default_account,
                               event):
    schedule_action("create_event", event, default_account.namespace.id,
                    db.session)
    schedule_action("update_event", event, default_account.namespace.id,
                    db.session)
    schedule_action("update_event", event, default_account.namespace.id,
                    db.session)
    schedule_action("delete_event", event, default_account.namespace.id,
                    db.session)
    db.session.commit()

    NUM_WORKERS = 2
    service = SyncbackService(
        syncback_id=0,
        process_number=0,
        total_processes=NUM_WORKERS,
        num_workers=NUM_WORKERS,
    )
    service._restart_workers()
    service._process_log()

    while not service.task_queue.empty():
        gevent.sleep(0.1)

    # This has to be a separate while-loop because there's a brief moment where
    # the task queue is empty, but num_idle_workers hasn't been updated yet.
    # On slower systems, we might need to sleep a bit between the while-loops.
    while service.num_idle_workers != NUM_WORKERS:
        gevent.sleep(0.1)

    q = db.session.query(ActionLog).filter_by(record_id=event.id).all()
    assert all(a.status == "failed" for a in q)
コード例 #33
0
def event_create_api():
    # Handle ical uploads
    if request.headers.get('content-type') == 'text/calendar':
        ics_str = request.data
        new_events = events.crud.create_from_ics(g.namespace, g.db_session,
                                                 ics_str)
        if not new_events:
            return err(400, "Couldn't parse .ics file.")

        return g.encoder.jsonify(new_events)

    data = request.get_json(force=True)
    try:
        calendar = get_calendar(data.get('calendar_id'),
                                g.namespace, g.db_session)
    except InputError as e:
        return err(404, str(e))

    if calendar.read_only:
        return err(400, "Can't create events on read_only calendar.")

    try:
        valid_event(data)
    except InputError as e:
        return err(404, str(e))

    title = data.get('title', '')
    description = data.get('description')
    location = data.get('location')
    reminders = data.get('reminders')
    recurrence = data.get('recurrence')
    when = data.get('when')

    participants = data.get('participants', [])
    for p in participants:
        if 'status' not in p:
            p['status'] = 'noreply'

    new_event = events.crud.create(g.namespace, g.db_session,
                                     calendar,
                                     title,
                                     description,
                                     location,
                                     reminders,
                                     recurrence,
                                     when,
                                     participants)

    schedule_action('create_event', new_event, g.namespace.id, g.db_session)
    return g.encoder.jsonify(new_event)
コード例 #34
0
ファイル: update.py プロジェクト: askagirl/sync-engine
def update_message_labels(message, db_session, label_public_ids):
    categories = set()
    for id_ in label_public_ids:
        try:
            category = db_session.query(Category).filter(
                Category.namespace_id == message.namespace_id,
                Category.public_id == id_).one()
            categories.add(category)
        except NoResultFound:
            raise InputError(u'Label {} does not exist'.format(id_))

    added_categories = categories - set(message.categories)
    removed_categories = set(message.categories) - categories

    added_labels = []
    removed_labels = []
    special_label_map = {
        'inbox': '\\Inbox',
        'important': '\\Important',
        'all': '\\All',  # STOPSHIP(emfree): verify
        'trash': '\\Trash',
        'spam': '\\Spam'
    }
    for category in added_categories:
        if category.name in special_label_map:
            added_labels.append(special_label_map[category.name])
        elif category.name in ('drafts', 'sent'):
            raise InputError('The "{}" label cannot be changed'.
                             format(category.name))
        else:
            added_labels.append(category.display_name)

    for category in removed_categories:
        if category.name in special_label_map:
            removed_labels.append(special_label_map[category.name])
        elif category.name in ('drafts', 'sent'):
            raise InputError('The "{}" label cannot be changed'.
                             format(category.name))
        else:
            removed_labels.append(category.display_name)

    # Optimistically update message state.
    message.categories = categories
    if removed_labels or added_labels:
        schedule_action('change_labels', message, message.namespace_id,
                        removed_labels=removed_labels,
                        added_labels=added_labels,
                        db_session=db_session)
コード例 #35
0
ファイル: update.py プロジェクト: wakermahmud/sync-engine
def update_message_folder(message, db_session, folder_public_id):
    try:
        category = db_session.query(Category).filter(
            Category.namespace_id == message.namespace_id,
            Category.public_id == folder_public_id).one()
    except NoResultFound:
        raise InputError(u'Folder {} does not exist'.format(folder_public_id))

    # STOPSHIP(emfree): what about sent/inbox duality?
    if category not in message.categories:
        message.categories = [category]
        schedule_action('move',
                        message,
                        message.namespace_id,
                        db_session,
                        destination=category.display_name)
コード例 #36
0
ファイル: update.py プロジェクト: ivicac/sync-engine
def update_message_flags(message, db_session, optimistic, unread=None, starred=None):
    if unread is not None:
        if optimistic:
            message.is_read = not unread

        schedule_action(
            "mark_unread", message, message.namespace_id, db_session, unread=unread
        )

    if starred is not None:
        if optimistic:
            message.is_starred = starred

        schedule_action(
            "mark_starred", message, message.namespace_id, db_session, starred=starred
        )
コード例 #37
0
ファイル: sending.py プロジェクト: EthanBlackburn/sync-engine
def send_draft(account, draft, db_session, schedule_remote_delete):
    """Send the draft with id = `draft_id`."""
    try:
        sendmail_client = get_sendmail_client(account)
        sendmail_client.send(draft)
    except SendMailException as exc:
        kwargs = {}
        if exc.failures:
            kwargs['failures'] = exc.failures
        if exc.server_error:
            kwargs['server_error'] = exc.server_error
        return err(exc.http_code, exc.message, **kwargs)

    # We want to return success to the API client if the message was sent, even
    # if there are errors in post-send updating. Otherwise the client may think
    # the send has failed. So wrap the rest of the work in try/except.
    try:
        if account.provider == 'icloud':
            # Special case because iCloud doesn't save sent messages.
            schedule_action('save_sent_email', draft, draft.namespace.id,
                            db_session)
        if schedule_remote_delete:
            schedule_action('delete_draft',
                            draft,
                            draft.namespace.id,
                            db_session,
                            inbox_uid=draft.inbox_uid,
                            message_id_header=draft.message_id_header)

        # Update message
        draft.is_sent = True
        draft.is_draft = False
        draft.received_date = datetime.utcnow()

        # Update thread
        sent_tag = account.namespace.tags['sent']
        draft_tag = account.namespace.tags['drafts']
        thread = draft.thread
        thread.apply_tag(sent_tag)
        # Remove the drafts tag from the thread if there are no more drafts.
        if not draft.thread.drafts:
            thread.remove_tag(draft_tag)
        thread.update_from_message(None, draft)
    except Exception as e:
        log.error('Error in post-send processing', error=e, exc_info=True)

    return APIEncoder().jsonify(draft)
コード例 #38
0
ファイル: ns_api.py プロジェクト: wakermahmud/sync-engine
def event_update_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(
            Event.public_id == public_id,
            Event.namespace_id == g.namespace.id).one()
    except NoResultFound:
        raise NotFoundError("Couldn't find event {0}".format(public_id))
    if event.read_only:
        raise InputError('Cannot update read_only event.')
    if (isinstance(event, RecurringEvent)
            or isinstance(event, RecurringEventOverride)):
        raise InputError('Cannot update a recurring event yet.')

    data = request.get_json(force=True)
    valid_event_update(data, g.namespace, g.db_session)

    if 'participants' in data:
        for p in data['participants']:
            if 'status' not in p:
                p['status'] = 'noreply'

    # Don't update an event if we don't need to.
    if noop_event_update(event, data):
        return g.encoder.jsonify(event)

    for attr in ['title', 'description', 'location', 'when', 'participants']:
        if attr in data:
            setattr(event, attr, data[attr])

    event.sequence_number += 1
    g.db_session.commit()

    schedule_action('update_event',
                    event,
                    g.namespace.id,
                    g.db_session,
                    calendar_uid=event.calendar.uid,
                    notify_participants=notify_participants)

    return g.encoder.jsonify(event)
コード例 #39
0
ファイル: base.py プロジェクト: askagirl/sync-engine
def delete_draft(db_session, account, draft):
    """ Delete the given draft. """
    thread = draft.thread
    assert draft.is_draft

    # Delete remotely.
    schedule_action('delete_draft', draft, draft.namespace.id, db_session,
                    inbox_uid=draft.inbox_uid,
                    message_id_header=draft.message_id_header)

    db_session.delete(draft)

    # Delete the thread if it would now be empty.
    if not thread.messages:
        db_session.delete(thread)

    db_session.commit()
コード例 #40
0
def schedule_test_action(db_session, account):
    from inbox.models.category import Category

    category_type = 'label' if account.provider == 'gmail' else 'folder'
    category = Category.find_or_create(
        db_session, account.namespace.id, name=None,
        display_name='{}-{}'.format(account.id, random.randint(1, 356)),
        type_=category_type)
    db_session.flush()

    if category_type == 'folder':
        schedule_action('create_folder', category, account.namespace.id,
                        db_session)
    else:
        schedule_action('create_label', category, account.namespace.id,
                        db_session)
    db_session.commit()
コード例 #41
0
ファイル: base.py プロジェクト: arquivo1/sync-engine
def delete_draft(db_session, account, draft):
    """ Delete the given draft. """
    thread = draft.thread
    assert draft.is_draft

    # Delete remotely.
    schedule_action('delete_draft', draft, draft.namespace.id, db_session,
                    inbox_uid=draft.inbox_uid,
                    message_id_header=draft.message_id_header)

    db_session.delete(draft)

    # Delete the thread if it would now be empty.
    if not thread.messages:
        db_session.delete(thread)

    db_session.commit()
コード例 #42
0
ファイル: base.py プロジェクト: raoulwissink/inbox
def update_draft(
    db_session, account, parent_draft, to=None, subject=None, body=None, blocks=None, cc=None, bcc=None, tags=None
):
    """
    Update draft.

    To maintain our messages are immutable invariant, we create a new draft
    message object.


    Returns
    -------
    SpoolMessage
        The new draft message object.


    Notes
    -----
    Messages, including draft messages, are immutable in Inbox.
    So to update a draft, we create a new draft message object and
    return its public_id (which is different than the original's).

    """
    to_addr = _parse_recipients(to) if to else parent_draft.to_addr
    cc_addr = _parse_recipients(cc) if cc else parent_draft.cc_addr
    bcc_addr = _parse_recipients(bcc) if bcc else parent_draft.bcc_addr
    subject = subject or parent_draft.subject
    body = body or parent_draft.sanitized_body
    blocks = blocks or [p for p in parent_draft.parts if p.is_attachment]

    new_draft = create_and_save_draft(
        db_session,
        account,
        to_addr,
        subject,
        body,
        blocks,
        cc_addr,
        bcc_addr,
        tags,
        parent_draft.thread,
        parent_draft.is_reply,
        parent_draft,
    )
    schedule_action("delete_draft", parent_draft, parent_draft.namespace.id, db_session)
    return new_draft
コード例 #43
0
def schedule_test_action(db_session, account):
    from inbox.models.category import Category

    category_type = 'label' if account.provider == 'gmail' else 'folder'
    category = Category.find_or_create(
        db_session, account.namespace.id, name=None,
        display_name='{}-{}'.format(account.id, random.randint(1, 356)),
        type_=category_type)
    db_session.flush()

    if category_type == 'folder':
        schedule_action('create_folder', category, account.namespace.id,
                        db_session)
    else:
        schedule_action('create_label', category, account.namespace.id,
                        db_session)
    db_session.commit()
コード例 #44
0
ファイル: base.py プロジェクト: nixon1333/inbox
def delete_draft(db_session, account, draft_public_id):
    """ Delete the draft with public_id = `draft_public_id`. """
    draft = db_session.query(Message).filter(
        Message.public_id == draft_public_id).one()

    assert draft.is_draft

    db_session.delete(draft)

    # Remove the drafts tag from the thread if there are no more drafts.
    if not draft.thread.drafts:
        draft.thread.remove_tag(draft.namespace.tags['drafts'])

    db_session.commit()

    # Delete remotely.
    schedule_action('delete_draft', draft, draft.namespace.id, db_session,
                    inbox_uid=draft.inbox_uid)
コード例 #45
0
ファイル: ns_api.py プロジェクト: apolmig/inbox
def event_delete_api(public_id):
    valid_public_id(public_id)
    try:
        event = g.db_session.query(Event).filter_by(
            public_id=public_id,
            namespace_id=g.namespace.id). \
            options(subqueryload(Event.calendar)).one()
    except NoResultFound:
        raise NotFoundError("Couldn't find event {0}".format(public_id))
    if event.calendar.read_only:
        raise NotFoundError('Cannot delete event {} from read_only '
                            'calendar.'.format(public_id))

    schedule_action('delete_event', event, g.namespace.id, g.db_session,
                    event_uid=event.uid,
                    calendar_name=event.calendar.name)
    events.crud.delete(g.namespace, g.db_session, public_id)
    return g.encoder.jsonify(None)
コード例 #46
0
ファイル: base.py プロジェクト: IAmAlexander/inbox
def delete_draft(db_session, account, draft_public_id):
    """ Delete the draft with public_id = `draft_public_id`. """
    draft = db_session.query(Message).filter(
        Message.public_id == draft_public_id).one()

    assert draft.is_draft

    db_session.delete(draft)

    # Remove the drafts tag from the thread if there are no more drafts.
    if not draft.thread.drafts:
        draft.thread.remove_tag(draft.namespace.tags['drafts'])

    db_session.commit()

    # Delete remotely.
    schedule_action('delete_draft', draft, draft.namespace.id, db_session,
                    inbox_uid=draft.inbox_uid)
コード例 #47
0
def update_draft(db_session,
                 account,
                 parent_draft,
                 to=None,
                 subject=None,
                 body=None,
                 blocks=None,
                 cc=None,
                 bcc=None,
                 tags=None):
    """
    Update draft.

    To maintain our messages are immutable invariant, we create a new draft
    message object.


    Returns
    -------
    Message
        The new draft message object.


    Notes
    -----
    Messages, including draft messages, are immutable in Inbox.
    So to update a draft, we create a new draft message object and
    return its public_id (which is different than the original's).

    """
    to_addr = _parse_recipients(to) if to else parent_draft.to_addr
    cc_addr = _parse_recipients(cc) if cc else parent_draft.cc_addr
    bcc_addr = _parse_recipients(bcc) if bcc else parent_draft.bcc_addr
    subject = subject or parent_draft.subject
    body = body or parent_draft.sanitized_body
    blocks = blocks or [p for p in parent_draft.parts if p.is_attachment]

    new_draft = create_and_save_draft(db_session, account, to_addr, subject,
                                      body, blocks, cc_addr, bcc_addr, tags,
                                      parent_draft.thread,
                                      parent_draft.is_reply, parent_draft)
    schedule_action('delete_draft', parent_draft, parent_draft.namespace.id,
                    db_session)
    return new_draft
コード例 #48
0
ファイル: ns_api.py プロジェクト: wakermahmud/sync-engine
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)
コード例 #49
0
ファイル: sending.py プロジェクト: Analect/sync-engine
def send_draft(account, draft, db_session, schedule_remote_delete):
    """Send the draft with id = `draft_id`."""
    try:
        sendmail_client = get_sendmail_client(account)
        sendmail_client.send(draft)
    except SendMailException as exc:
        kwargs = {}
        if exc.failures:
            kwargs['failures'] = exc.failures
        if exc.server_error:
            kwargs['server_error'] = exc.server_error
        return err(exc.http_code, exc.message, **kwargs)

    # We want to return success to the API client if the message was sent, even
    # if there are errors in post-send updating. Otherwise the client may think
    # the send has failed. So wrap the rest of the work in try/except.
    try:
        if account.provider == 'icloud':
            # Special case because iCloud doesn't save sent messages.
            schedule_action('save_sent_email', draft, draft.namespace.id,
                            db_session)
        if schedule_remote_delete:
            schedule_action('delete_draft', draft, draft.namespace.id,
                            db_session, inbox_uid=draft.inbox_uid,
                            message_id_header=draft.message_id_header)

        # Update message
        draft.is_sent = True
        draft.is_draft = False
        draft.received_date = datetime.utcnow()

        # Update thread
        sent_tag = account.namespace.tags['sent']
        draft_tag = account.namespace.tags['drafts']
        thread = draft.thread
        thread.apply_tag(sent_tag)
        # Remove the drafts tag from the thread if there are no more drafts.
        if not draft.thread.drafts:
            thread.remove_tag(draft_tag)
        thread.update_from_message(None, draft)
    except Exception as e:
        log.error('Error in post-send processing', error=e, exc_info=True)

    return APIEncoder().jsonify(draft)
コード例 #50
0
def event_delete_api(public_id):
    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))

    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)
    g.db_session.delete(event)
    g.db_session.commit()
    return g.encoder.jsonify(None)
コード例 #51
0
ファイル: ns_api.py プロジェクト: htk/sync-engine
def event_update_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(
            Event.public_id == public_id,
            Event.namespace_id == g.namespace.id).one()
    except NoResultFound:
        raise NotFoundError("Couldn't find event {0}".format(public_id))
    if event.read_only:
        raise InputError('Cannot update read_only event.')
    if (isinstance(event, RecurringEvent) or
            isinstance(event, RecurringEventOverride)):
        raise InputError('Cannot update a recurring event yet.')

    data = request.get_json(force=True)
    valid_event_update(data, g.namespace, g.db_session)

    if 'participants' in data:
        for p in data['participants']:
            if 'status' not in p:
                p['status'] = 'noreply'

    # Don't update an event if we don't need to.
    if noop_event_update(event, data):
        return g.encoder.jsonify(event)

    for attr in ['title', 'description', 'location', 'when', 'participants']:
        if attr in data:
            setattr(event, attr, data[attr])

    event.sequence_number += 1
    g.db_session.commit()

    schedule_action('update_event', event, g.namespace.id, g.db_session,
                    calendar_uid=event.calendar.uid,
                    notify_participants=notify_participants)

    return g.encoder.jsonify(event)
コード例 #52
0
ファイル: ns_api.py プロジェクト: htk/sync-engine
def folders_labels_create_api():
    category_type = g.namespace.account.category_type
    data = request.get_json(force=True)
    display_name = data.get('display_name')

    valid_display_name(g.namespace.id, category_type, display_name,
                       g.db_session)

    category = Category.find_or_create(g.db_session, g.namespace.id,
                                       name=None, display_name=display_name,
                                       type_=category_type)
    g.db_session.flush()

    if category_type == 'folder':
        schedule_action('create_folder', category, g.namespace.id, g.db_session)
    else:
        schedule_action('create_label', category, g.namespace.id, g.db_session)

    return g.encoder.jsonify(category)
コード例 #53
0
def event_create_api():
    data = request.get_json(force=True)
    calendar = get_calendar(data.get('calendar_id'), g.namespace, g.db_session)

    if calendar.read_only:
        raise InputError("Can't create events on read_only calendar.")

    valid_event(data)

    title = data.get('title', '')
    description = data.get('description')
    location = data.get('location')
    when = data.get('when')
    busy = data.get('busy', True)

    participants = data.get('participants', [])
    for p in participants:
        if 'status' not in p:
            p['status'] = 'noreply'

    event = Event(calendar=calendar,
                  namespace=g.namespace,
                  uid=uuid.uuid4().hex,
                  provider_name=g.namespace.account.provider,
                  raw_data='',
                  title=title,
                  description=description,
                  location=location,
                  busy=busy,
                  when=when,
                  read_only=False,
                  is_owner=True,
                  participants=participants,
                  source='local')
    g.db_session.add(event)
    g.db_session.flush()

    schedule_action('create_event',
                    event,
                    g.namespace.id,
                    g.db_session,
                    calendar_uid=event.calendar.uid)
    return g.encoder.jsonify(event)
コード例 #54
0
ファイル: ns_api.py プロジェクト: EthanBlackburn/sync-engine
def event_create_api():
    data = request.get_json(force=True)
    calendar = get_calendar(data.get('calendar_id'),
                            g.namespace, g.db_session)

    if calendar.read_only:
        raise InputError("Can't create events on read_only calendar.")

    valid_event(data)

    title = data.get('title', '')
    description = data.get('description')
    location = data.get('location')
    when = data.get('when')
    busy = data.get('busy', True)

    participants = data.get('participants', [])
    for p in participants:
        if 'status' not in p:
            p['status'] = 'noreply'

    event = Event(
        calendar=calendar,
        namespace=g.namespace,
        uid=uuid.uuid4().hex,
        provider_name=g.namespace.account.provider,
        raw_data='',
        title=title,
        description=description,
        location=location,
        busy=busy,
        when=when,
        read_only=False,
        is_owner=True,
        participants=participants,
        source='local')
    g.db_session.add(event)
    g.db_session.flush()

    schedule_action('create_event', event, g.namespace.id, g.db_session,
                    calendar_uid=event.calendar.uid)
    return g.encoder.jsonify(event)
コード例 #55
0
ファイル: ns_api.py プロジェクト: vjeantet/inbox
def event_delete_api(public_id):
    try:
        valid_public_id(public_id)
        event = g.db_session.query(Event).filter_by(
            public_id=public_id). \
            options(subqueryload(Event.calendar)).one()
    except InputError:
        return err(400, 'Invalid event id {}'.format(public_id))
    except NoResultFound:
        return err(404, 'No event found with public_id {}'.
                   format(public_id))
    if event.namespace != g.namespace:
        return err(404, 'No event found with public_id {}'.
                   format(public_id))
    if event.calendar.read_only:
        return err(404, 'Cannot delete event with public_id {} from '
                   ' read_only calendar.'.format(public_id))

    result = events.crud.delete(g.namespace, g.db_session, public_id)
    schedule_action('delete_event', event, g.namespace.id, g.db_session)
    return g.encoder.jsonify(result)
コード例 #56
0
ファイル: update.py プロジェクト: rskumar/sync-engine
def update_message_labels(message, db_session, added_categories,
                          removed_categories):
    special_label_map = {
        'inbox': '\\Inbox',
        'important': '\\Important',
        'all': '\\All',  # STOPSHIP(emfree): verify
        'trash': '\\Trash',
        'spam': '\\Spam'
    }
    added_labels = []
    removed_labels = []
    for category in added_categories:
        if category.name in special_label_map:
            added_labels.append(special_label_map[category.name])
        elif category.name in ('drafts', 'sent'):
            raise InputError('The "{}" label cannot be changed'.
                             format(category.name))
        else:
            added_labels.append(category.display_name)

    for category in removed_categories:
        if category.name in special_label_map:
            removed_labels.append(special_label_map[category.name])
        elif category.name in ('drafts', 'sent'):
            raise InputError('The "{}" label cannot be changed'.
                             format(category.name))
        else:
            removed_labels.append(category.display_name)

    # Optimistically update message state.
    for cat in added_categories:
        message.categories.add(cat)
    for cat in removed_categories:
        message.categories.discard(cat)
    if removed_labels or added_labels:
        message.categories_changes = True
        schedule_action('change_labels', message, message.namespace_id,
                        removed_labels=removed_labels,
                        added_labels=added_labels,
                        db_session=db_session)
コード例 #57
0
ファイル: ns_api.py プロジェクト: askagirl/sync-engine
def draft_send_api():
    if request.content_type == "message/rfc822":
        msg = create_draft_from_mime(g.namespace.account, request.data,
                                     g.db_session)
        validate_draft_recipients(msg)
        resp = send_raw_mime(g.namespace.account, g.db_session, msg)
        return resp

    data = request.get_json(force=True)
    draft_public_id = data.get('draft_id')
    if draft_public_id is not None:
        draft = get_draft(draft_public_id, data.get('version'), g.namespace.id,
                          g.db_session)
        schedule_action('delete_draft', draft, draft.namespace.id,
                        g.db_session, inbox_uid=draft.inbox_uid,
                        message_id_header=draft.message_id_header)
    else:
        draft = create_draft(data, g.namespace, g.db_session, syncback=False)

    validate_draft_recipients(draft)
    resp = send_draft(g.namespace.account, draft, g.db_session)
    return resp