Ejemplo n.º 1
0
    def post(self):
        if not self.params.text:
            return self.error(
                200, _('Message is required. Please go back and try again.'))

        if not self.params.author_name:
            return self.error(
                200,
                _('Your name is required in the "About you" section.  '
                  'Please go back and try again.'))

        if self.params.status == 'is_note_author' and not self.params.found:
            return self.error(
                200,
                _('Please check that you have been in contact with '
                  'the person after the earthquake, or change the '
                  '"Status of this person" field.'))

        note = Note.create_original(
            self.subdomain,
            entry_date=get_utcnow(),
            person_record_id=self.params.id,
            author_name=self.params.author_name,
            author_email=self.params.author_email,
            author_phone=self.params.author_phone,
            source_date=get_utcnow(),
            found=bool(self.params.found),
            status=self.params.status,
            email_of_found_person=self.params.email_of_found_person,
            phone_of_found_person=self.params.phone_of_found_person,
            last_known_location=self.params.last_known_location,
            text=self.params.text)
        entities_to_put = [note]

        # Update the Person based on the Note.
        person = Person.get(self.subdomain, self.params.id)
        if person:
            person.update_from_note(note)
            # Send notification to all people
            # who subscribed to updates on this person
            subscribe.send_notifications(person, note, self)

            entities_to_put.append(person)

        # Write one or both entities to the store.
        db.put(entities_to_put)

        # If user wants to subscribe to updates, redirect to the subscribe page
        if self.params.subscribe:
            return self.redirect('/subscribe',
                                 id=person.record_id,
                                 subscribe_email=self.params.author_email)

        # Redirect to this page so the browser's back button works properly.
        self.redirect('/view', id=self.params.id, query=self.params.query)
Ejemplo n.º 2
0
def send_notifications(handler, persons, notes):
    """For each note, send a notification to subscriber.

    Args:
       notes: List of notes for which to send notification.
       persons: Dictionary of persons impacted by the notes,
                indexed by person_record_id.
       handler: Handler used to send email notification.
    """
    for note in notes:
        person = persons[note.person_record_id]
        subscribe.send_notifications(handler, person, [note])
Ejemplo n.º 3
0
def send_notifications(handler, persons, notes):
    """For each note, send a notification to subscriber.

    Args:
       notes: List of notes for which to send notification.
       persons: Dictionary of persons impacted by the notes,
                indexed by person_record_id.
       handler: Handler used to send email notification.
    """
    for note in notes:
        person = persons[note.person_record_id]
        subscribe.send_notifications(handler, person, [note])
Ejemplo n.º 4
0
    def post(self):
        if not self.params.text:
            return self.error(
                200, _('Message is required. Please go back and try again.'))

        if not self.params.author_name:
            return self.error(
                200, _('Your name is required in the "About you" section.  '
                       'Please go back and try again.'))

        if self.params.status == 'is_note_author' and not self.params.found:
            return self.error(
                200, _('Please check that you have been in contact with '
                       'the person after the earthquake, or change the '
                       '"Status of this person" field.'))

        note = Note.create_original(
            self.subdomain,
            entry_date=get_utcnow(),
            person_record_id=self.params.id,
            author_name=self.params.author_name,
            author_email=self.params.author_email,
            author_phone=self.params.author_phone,
            source_date=get_utcnow(),
            found=bool(self.params.found),
            status=self.params.status,
            email_of_found_person=self.params.email_of_found_person,
            phone_of_found_person=self.params.phone_of_found_person,
            last_known_location=self.params.last_known_location,
            text=self.params.text)
        entities_to_put = [note]

        # Update the Person based on the Note.
        person = Person.get(self.subdomain, self.params.id)
        if person:
            person.update_from_note(note)
            # Send notification to all people
            # who subscribed to updates on this person
            subscribe.send_notifications(person, note, self)

            entities_to_put.append(person)

        # Write one or both entities to the store.
        db.put(entities_to_put)

        # If user wants to subscribe to updates, redirect to the subscribe page
        if self.params.subscribe:
            return self.redirect('/subscribe', id=person.record_id,
                                 subscribe_email=self.params.author_email)

        # Redirect to this page so the browser's back button works properly.
        self.redirect('/view', id=self.params.id, query=self.params.query)
Ejemplo n.º 5
0
    def post(self):
        if not self.params.text:
            return self.error(
                200, _('Message is required. Please go back and try again.'))

        if not self.params.author_name:
            return self.error(
                200,
                _('Your name is required in the "About you" section.  Please go back and try again.'
                  ))

        # TODO: To reduce possible abuse, we currently limit to 3 person
        # match. We could guard using e.g. an XSRF token, which I don't know how
        # to build in GAE.

        ids = set()
        for i in [1, 2, 3]:
            id = getattr(self.params, 'id%d' % i)
            if not id:
                break
            ids.add(id)

        if len(ids) > 1:
            notes = []
            for person_id in ids:
                person = Person.get(self.repo, person_id)
                person_notes = []
                for other_id in ids - set([person_id]):
                    note = Note.create_original(
                        self.repo,
                        entry_date=get_utcnow(),
                        person_record_id=person_id,
                        linked_person_record_id=other_id,
                        text=self.params.text,
                        author_name=self.params.author_name,
                        author_phone=self.params.author_phone,
                        author_email=self.params.author_email,
                        source_date=get_utcnow())
                    person_notes.append(note)
                # Notify person's subscribers of all new duplicates. We do not
                # follow links since each Person record in the ids list gets its
                # own note. However, 1) when > 2 records are marked as
                # duplicates, subscribers will still receive multiple
                # notifications, and 2) subscribers to already-linked Persons
                # will not be notified of the new link.
                subscribe.send_notifications(self, person, person_notes, False)
                notes += person_notes
            # Write all notes to store
            db.put(notes)
        self.redirect('/view', id=self.params.id1)
Ejemplo n.º 6
0
    def post(self):
        if not self.params.text:
            return self.error(
                200, _('Message is required. Please go back and try again.'))

        if not self.params.author_name:
            return self.error(
                200, _('Your name is required in the "About you" section.  Please go back and try again.'))

        # TODO: To reduce possible abuse, we currently limit to 3 person
        # match. We could guard using e.g. an XSRF token, which I don't know how
        # to build in GAE.

        ids = set()
        for i in [1, 2, 3]:
            id = getattr(self.params, 'id%d' % i)
            if not id:
                break
            ids.add(id)

        if len(ids) > 1:
            notes = []
            for person_id in ids:
                person = Person.get(self.repo, person_id)
                person_notes = []
                for other_id in ids - set([person_id]):
                    note = Note.create_original(
                        self.repo,
                        entry_date=get_utcnow(),                        
                        person_record_id=person_id,
                        linked_person_record_id=other_id,
                        text=self.params.text,
                        author_name=self.params.author_name,
                        author_phone=self.params.author_phone,
                        author_email=self.params.author_email,
                        source_date=get_utcnow())
                    person_notes.append(note)
                # Notify person's subscribers of all new duplicates. We do not
                # follow links since each Person record in the ids list gets its
                # own note. However, 1) when > 2 records are marked as
                # duplicates, subscribers will still receive multiple
                # notifications, and 2) subscribers to already-linked Persons
                # will not be notified of the new link.
                subscribe.send_notifications(self, person, person_notes, False)
                notes += person_notes
            # Write all notes to store
            db.put(notes)
        self.redirect('/view', id=self.params.id1)
    def confirm_note_with_bad_words(self, note):
        """After a note containing bad words is confirmed by the author,
        we will:
        (1) set note.confirmed = True;
        (2) copy the note from NoteWithBadWords to Note;
        (3) log user action;
        (4) update person record. """
        note.confirmed = True;

        # Check whether the record author disabled notes on
        # this record during the time between the note author inputs the
        # note in the UI and confirms the note through email.
        person = model.Person.get(self.repo, note.person_record_id)
        if person.notes_disabled:
            return self.error(
                200, _('The author has disabled notes on this record.'))

        # Check whether the admin disabled reporting "believed_dead"
        # during the time between the note author inputs the
        # note in the UI and confirms the note through email.
        if (self.params.status == 'believed_dead' and
            not self.config.allow_believed_dead_via_ui):
            return self.error(
                200, _('Not authorized to post notes with the status '
                       '"believed_dead".'))

        # clone the flagged note to Note table.
        note_confirmed = model.Note.create_original(
            self.repo,
            entry_date=note.entry_date,
            person_record_id=note.person_record_id,
            author_name=note.author_name,
            author_email=note.author_email,
            author_phone=note.author_phone,
            source_date=note.source_date,
            author_made_contact=note.author_made_contact,
            status=note.status,
            email_of_found_person=note.email_of_found_person,
            phone_of_found_person=note.phone_of_found_person,
            last_known_location=note.last_known_location,
            text=note.text)
        entities_to_put = [note_confirmed]

        note.confirmed_copy_id = note_confirmed.get_record_id()
        entities_to_put.append(note)

        # Specially log 'believed_dead'.
        if note_confirmed.status == 'believed_dead':
            model.UserActionLog.put_new(
                    'mark_dead', note_confirmed, person.primary_full_name,
                    self.request.remote_addr)

        # Specially log a switch to an alive status.
        if (note_confirmed.status in ['believed_alive', 'is_note_author'] and
            person.latest_status not in ['believed_alive', 'is_note_author']):
            model.UserActionLog.put_new(
                    'mark_alive', note_confirmed, person.primary_full_name)

        # Update the Person based on the Note.
        if person:
            person.update_from_note(note_confirmed)
            # Send notification to all people
            # who subscribed to updates on this person
            subscribe.send_notifications(self, person, [note_confirmed])
            entities_to_put.append(person)

        # Write one or both entities to the store.
        db.put(entities_to_put)
Ejemplo n.º 8
0
class Handler(BaseHandler):
    def get(self):
        # Check the request parameters.
        if not self.params.id:
            return self.error(404, _('No person id was specified.'))
        try:
            person = Person.get(self.repo, self.params.id)
        # TODO(ichikawa) Consider removing this "except" clause.
        #     I don't think ValueError is thrown here.
        except ValueError:
            return self.error(
                404,
                _("This person's entry does not exist or has been deleted."))
        if not person:
            return self.error(
                404,
                _("This person's entry does not exist or has been deleted."))

        # Render the page.
        self.render('add_note.html', person=person)

    def post(self):
        """Post a note in person's record view page"""
        try:
            create.validate_note_data(
                config=self.config,
                status=self.params.status,
                author_name=self.params.author_name,
                author_email=self.params.author_email,
                author_made_contact=self.params.author_made_contact,
                text=self.params.text)
        except create.CreationError as e:
            return self.error(400, e.user_readable_message)

        person = Person.get(self.repo, self.params.id)
        if person.notes_disabled:
            return self.error(
                400,
                _('The author has disabled status updates '
                  'on this record.'))

        # If a photo was uploaded, create and store a new Photo entry and get
        # the URL where it's served; otherwise, use the note_photo_url provided.
        photo, photo_url = (None, self.params.note_photo_url)
        if self.params.note_photo is not None:
            try:
                photo, photo_url = create_photo(self.params.note_photo,
                                                self.repo,
                                                self.transitionary_get_url)
            except PhotoError, e:
                return self.error(400, e.message)
            photo.put()

        try:
            note = create.create_note(
                repo=self.repo,
                person=person,
                config=self.config,
                user_ip_address=self.request.remote_addr,
                status=self.params.status,
                source_date=get_utcnow(),
                author_name=self.params.author_name,
                author_email=self.params.author_email,
                author_phone=self.params.author_phone,
                author_made_contact=bool(self.params.author_made_contact),
                note_photo=photo,
                note_photo_url=photo_url,
                text=self.params.text,
                email_of_found_person=self.params.email_of_found_person,
                phone_of_found_person=self.params.phone_of_found_person,
                last_known_location=self.params.last_known_location,
                validate_data=False)
        except create.FlaggedNoteException as e:
            return self.redirect('/post_flagged_note',
                                 id=e.note.get_record_id(),
                                 author_email=e.note.author_email,
                                 repo=self.repo)

        # Update the Person based on the Note.
        if person:
            person.update_from_note(note)
            # Send notification to all people
            # who subscribed to updates on this person
            subscribe.send_notifications(self, person, [note])
            # write the updated person record to datastore
            db.put(person)

        # If user wants to subscribe to updates, redirect to the subscribe page
        if self.params.subscribe:
            return self.redirect('/subscribe',
                                 id=person.record_id,
                                 subscribe_email=self.params.author_email,
                                 context='add_note')

        # Redirect to view page so the browser's back button works properly.
        self.redirect('/view', id=self.params.id, query=self.params.query)
Ejemplo n.º 9
0
class Handler(BaseHandler):

    def get(self):
        # Check the request parameters.
        if not self.params.id:
            return self.error(404, _('No person id was specified.'))
        try:
            person = Person.get(self.repo, self.params.id)
        # TODO(ichikawa) Consider removing this "except" clause.
        #     I don't think ValueError is thrown here.
        except ValueError:
            return self.error(404,
                _("This person's entry does not exist or has been deleted."))
        if not person:
            return self.error(404,
                _("This person's entry does not exist or has been deleted."))

        # Render the page.
        enable_notes_url = self.get_url('/enable_notes', id=self.params.id)

        self.render('add_note.html',
                    person=person,
                    enable_notes_url=enable_notes_url)

    def post(self):
        """Post a note in person's record view page"""
        if not self.params.text:
            return self.error(
                400, _('Message is required. Please go back and try again.'))

        if not self.params.author_name:
            return self.error(
                400, _('Your name is required in the "About you" section.  '
                       'Please go back and try again.'))

        if (self.params.status == 'is_note_author' and
            not self.params.author_made_contact):
            return self.error(
                400, _('Please check that you have been in contact with '
                       'the person after the disaster, or change the '
                       '"Status of this person" field.'))
        if (self.params.status == 'believed_dead' and
            not self.config.allow_believed_dead_via_ui):
            return self.error(
                400, _('Not authorized to post notes with the status '
                       '"believed_dead".'))

        if (self.params.author_email and
            not utils.validate_email(self.params.author_email)):
            return self.error(400, _(
                'The email address you entered appears to be invalid.'))

        person = Person.get(self.repo, self.params.id)
        if person.notes_disabled:
            return self.error(
                400, _('The author has disabled status updates '
                       'on this record.'))

        # If a photo was uploaded, create and store a new Photo entry and get
        # the URL where it's served; otherwise, use the note_photo_url provided.
        photo, photo_url = (None, self.params.note_photo_url)
        if self.params.note_photo is not None:
            try:
                photo, photo_url = create_photo(
                    self.params.note_photo, self.repo,
                    self.transitionary_get_url)
            except PhotoError, e:
                return self.error(400, e.message)
            photo.put()

        spam_detector = SpamDetector(self.config.bad_words)
        spam_score = spam_detector.estimate_spam_score(self.params.text)

        if (spam_score > 0):
            note = NoteWithBadWords.create_original(
                self.repo,
                entry_date=get_utcnow(),
                person_record_id=self.params.id,
                author_name=self.params.author_name,
                author_email=self.params.author_email,
                author_phone=self.params.author_phone,
                source_date=get_utcnow(),
                author_made_contact=bool(self.params.author_made_contact),
                status=self.params.status,
                email_of_found_person=self.params.email_of_found_person,
                phone_of_found_person=self.params.phone_of_found_person,
                last_known_location=self.params.last_known_location,
                text=self.params.text,
                photo=photo,
                photo_url=photo_url,
                spam_score=spam_score,
                confirmed=False)
            # Write the new NoteWithBadWords to the datastore
            note.put_new()
            # When the note is detected as spam, we do not update person record
            # or log action. We ask the note author for confirmation first.
            return self.redirect('/post_flagged_note', id=note.get_record_id(),
                                 author_email=note.author_email,
                                 repo=self.repo)
        else:
            note = Note.create_original(
                self.repo,
                entry_date=get_utcnow(),
                person_record_id=self.params.id,
                author_name=self.params.author_name,
                author_email=self.params.author_email,
                author_phone=self.params.author_phone,
                source_date=get_utcnow(),
                author_made_contact=bool(self.params.author_made_contact),
                status=self.params.status,
                email_of_found_person=self.params.email_of_found_person,
                phone_of_found_person=self.params.phone_of_found_person,
                last_known_location=self.params.last_known_location,
                text=self.params.text,
                photo=photo,
                photo_url=photo_url)
            # Write the new regular Note to the datastore
            note.put_new()

        # Specially log 'believed_dead'.
        if note.status == 'believed_dead':
            UserActionLog.put_new(
                'mark_dead', note, person.primary_full_name,
                self.request.remote_addr)

        # Specially log a switch to an alive status.
        if (note.status in ['believed_alive', 'is_note_author'] and
            person.latest_status not in ['believed_alive', 'is_note_author']):
            UserActionLog.put_new('mark_alive', note, person.primary_full_name)

        # Update the Person based on the Note.
        if person:
            person.update_from_note(note)
            # Send notification to all people
            # who subscribed to updates on this person
            subscribe.send_notifications(self, person, [note])
            # write the updated person record to datastore
            db.put(person)

        # If user wants to subscribe to updates, redirect to the subscribe page
        if self.params.subscribe:
            return self.redirect('/subscribe',
                                 id=person.record_id,
                                 subscribe_email=self.params.author_email,
                                 context='add_note')

        # Redirect to view page so the browser's back button works properly.
        self.redirect('/view', id=self.params.id, query=self.params.query)
    def confirm_note_with_bad_words(self, note):
        """After a note containing bad words is confirmed by the author,
        we will:
        (1) set note.confirmed = True;
        (2) copy the note from NoteWithBadWords to Note;
        (3) log user action;
        (4) update person record. """
        note.confirmed = True

        # Check whether the record author disabled notes on
        # this record during the time between the note author inputs the
        # note in the UI and confirms the note through email.
        person = model.Person.get(self.repo, note.person_record_id)
        if person.notes_disabled:
            return self.error(
                200, _('The author has disabled notes on this record.'))

        # Check whether the admin disabled reporting "believed_dead"
        # during the time between the note author inputs the
        # note in the UI and confirms the note through email.
        if (self.params.status == 'believed_dead'
                and not self.config.allow_believed_dead_via_ui):
            return self.error(
                200,
                _('Not authorized to post notes with the status '
                  '"believed_dead".'))

        # clone the flagged note to Note table.
        note_confirmed = model.Note.create_original(
            self.repo,
            entry_date=note.entry_date,
            person_record_id=note.person_record_id,
            author_name=note.author_name,
            author_email=note.author_email,
            author_phone=note.author_phone,
            source_date=note.source_date,
            author_made_contact=note.author_made_contact,
            status=note.status,
            email_of_found_person=note.email_of_found_person,
            phone_of_found_person=note.phone_of_found_person,
            last_known_location=note.last_known_location,
            text=note.text)
        entities_to_put = [note_confirmed]

        note.confirmed_copy_id = note_confirmed.get_record_id()
        entities_to_put.append(note)

        # Specially log 'believed_dead'.
        if note_confirmed.status == 'believed_dead':
            model.UserActionLog.put_new('mark_dead', note_confirmed,
                                        person.primary_full_name,
                                        self.request.remote_addr)

        # Specially log a switch to an alive status.
        if (note_confirmed.status in ['believed_alive', 'is_note_author']
                and person.latest_status
                not in ['believed_alive', 'is_note_author']):
            model.UserActionLog.put_new('mark_alive', note_confirmed,
                                        person.primary_full_name)

        # Update the Person based on the Note.
        if person:
            person.update_from_note(note_confirmed)
            # Send notification to all people
            # who subscribed to updates on this person
            subscribe.send_notifications(self, person, [note_confirmed])
            entities_to_put.append(person)

        # Write one or both entities to the store.
        db.put(entities_to_put)
Ejemplo n.º 11
0
class Handler(BaseHandler):
    def get(self):
        # Check the request parameters.
        if not self.params.id:
            return self.error(404, 'No person id was specified.')
        try:
            person = Person.get(self.repo, self.params.id)
        except ValueError:
            return self.error(
                404,
                _("This person's entry does not exist or has been deleted."))
        if not person:
            return self.error(
                404,
                _("This person's entry does not exist or has been deleted."))
        standalone = self.request.get('standalone')

        # Check if private info should be revealed.
        content_id = 'view:' + self.params.id
        reveal_url = reveal.make_reveal_url(self, content_id)
        show_private_info = reveal.verify(content_id, self.params.signature)

        # Compute the local times for the date fields on the person.
        person.source_date_local_string = self.to_formatted_local_time(
            person.source_date)
        person.expiry_date_local_string = self.to_formatted_local_time(
            person.get_effective_expiry_date())

        # Get the notes and duplicate links.
        try:
            notes = person.get_notes()
        except datastore_errors.NeedIndexError:
            notes = []
        person.sex_text = get_person_sex_text(person)
        for note in notes:
            self.add_fields_to_notes(note)
        try:
            linked_persons = person.get_all_linked_persons()
        except datastore_errors.NeedIndexError:
            linked_persons = []
        linked_person_info = []
        for linked_person in linked_persons:
            try:
                linked_notes = linked_person.get_notes()
            except datastore_errors.NeedIndexError:
                linked_notes = []
            for note in linked_notes:
                self.add_fields_to_notes(note)
            linked_person_info.append(
                dict(id=linked_person.record_id,
                     name=linked_person.primary_full_name,
                     view_url=self.get_url('/view',
                                           id=linked_person.record_id),
                     notes=linked_notes))

        # Render the page.
        dupe_notes_url = self.get_url('/view',
                                      id=self.params.id,
                                      dupe_notes='yes')
        results_url = self.get_url('/results',
                                   role=self.params.role,
                                   query=self.params.query,
                                   given_name=self.params.given_name,
                                   family_name=self.params.family_name)
        feed_url = self.get_url('/feeds/note',
                                person_record_id=self.params.id,
                                repo=self.repo)
        subscribe_url = self.get_url('/subscribe', id=self.params.id)
        delete_url = self.get_url('/delete', id=self.params.id)
        disable_notes_url = self.get_url('/disable_notes', id=self.params.id)
        enable_notes_url = self.get_url('/enable_notes', id=self.params.id)
        extend_url = None
        extension_days = 0
        expiration_days = None
        expiry_date = person.get_effective_expiry_date()
        if expiry_date and not person.is_clone():
            expiration_delta = expiry_date - get_utcnow()
            extend_url = self.get_url('/extend', id=self.params.id)
            extension_days = extend.get_extension_days(self)
            if expiration_delta.days < EXPIRY_WARNING_THRESHOLD:
                # round 0 up to 1, to make the msg read better.
                expiration_days = expiration_delta.days + 1

        if person.is_clone():
            person.provider_name = person.get_original_domain()

        sanitize_urls(person)
        for note in notes:
            sanitize_urls(note)

        if person.profile_urls:
            person.profile_pages = get_profile_pages(person.profile_urls, self)

        self.render('view.html',
                    person=person,
                    notes=notes,
                    linked_person_info=linked_person_info,
                    standalone=standalone,
                    onload_function='view_page_loaded()',
                    show_private_info=show_private_info,
                    admin=users.is_current_user_admin(),
                    dupe_notes_url=dupe_notes_url,
                    results_url=results_url,
                    reveal_url=reveal_url,
                    feed_url=feed_url,
                    subscribe_url=subscribe_url,
                    delete_url=delete_url,
                    disable_notes_url=disable_notes_url,
                    enable_notes_url=enable_notes_url,
                    extend_url=extend_url,
                    extension_days=extension_days,
                    expiration_days=expiration_days)

    def post(self):
        if not self.params.text:
            return self.error(
                200, _('Message is required. Please go back and try again.'))

        if not self.params.author_name:
            return self.error(
                200,
                _('Your name is required in the "About you" section.  '
                  'Please go back and try again.'))

        if (self.params.status == 'is_note_author'
                and not self.params.author_made_contact):
            return self.error(
                200,
                _('Please check that you have been in contact with '
                  'the person after the earthquake, or change the '
                  '"Status of this person" field.'))

        if (self.params.status == 'believed_dead'
                and not self.config.allow_believed_dead_via_ui):
            return self.error(
                200,
                _('Not authorized to post notes with the status '
                  '"believed_dead".'))

        person = Person.get(self.repo, self.params.id)
        if person.notes_disabled:
            return self.error(
                200,
                _('The author has disabled status updates '
                  'on this record.'))

        # If a photo was uploaded, create and store a new Photo entry and get
        # the URL where it's served; otherwise, use the note_photo_url provided.
        photo, photo_url = (None, self.params.note_photo_url)
        if self.params.note_photo is not None:
            try:
                photo, photo_url = create_photo(self.params.note_photo, self)
            except PhotoError, e:
                return self.error(400, e.message)
            photo.put()

        spam_detector = SpamDetector(self.config.bad_words)
        spam_score = spam_detector.estimate_spam_score(self.params.text)

        if (spam_score > 0):
            note = NoteWithBadWords.create_original(
                self.repo,
                entry_date=get_utcnow(),
                person_record_id=self.params.id,
                author_name=self.params.author_name,
                author_email=self.params.author_email,
                author_phone=self.params.author_phone,
                source_date=get_utcnow(),
                author_made_contact=bool(self.params.author_made_contact),
                status=self.params.status,
                email_of_found_person=self.params.email_of_found_person,
                phone_of_found_person=self.params.phone_of_found_person,
                last_known_location=self.params.last_known_location,
                text=self.params.text,
                photo=photo,
                photo_url=photo_url,
                spam_score=spam_score,
                confirmed=False)
            # Write the new NoteWithBadWords to the datastore
            db.put(note)
            UserActionLog.put_new('add', note, copy_properties=False)
            # When the note is detected as spam, we do not update person record
            # or log action. We ask the note author for confirmation first.
            return self.redirect('/post_flagged_note',
                                 id=note.get_record_id(),
                                 author_email=note.author_email,
                                 repo=self.repo)
        else:
            note = Note.create_original(
                self.repo,
                entry_date=get_utcnow(),
                person_record_id=self.params.id,
                author_name=self.params.author_name,
                author_email=self.params.author_email,
                author_phone=self.params.author_phone,
                source_date=get_utcnow(),
                author_made_contact=bool(self.params.author_made_contact),
                status=self.params.status,
                email_of_found_person=self.params.email_of_found_person,
                phone_of_found_person=self.params.phone_of_found_person,
                last_known_location=self.params.last_known_location,
                text=self.params.text,
                photo=photo,
                photo_url=photo_url)
            # Write the new regular Note to the datastore
            db.put(note)
            UserActionLog.put_new('add', note, copy_properties=False)

        # Specially log 'believed_dead'.
        if note.status == 'believed_dead':
            UserActionLog.put_new('mark_dead', note, person.primary_full_name,
                                  self.request.remote_addr)

        # Specially log a switch to an alive status.
        if (note.status in ['believed_alive', 'is_note_author']
                and person.latest_status
                not in ['believed_alive', 'is_note_author']):
            UserActionLog.put_new('mark_alive', note, person.primary_full_name)

        # Update the Person based on the Note.
        if person:
            person.update_from_note(note)
            # Send notification to all people
            # who subscribed to updates on this person
            subscribe.send_notifications(self, person, [note])
            # write the updated person record to datastore
            db.put(person)

        # If user wants to subscribe to updates, redirect to the subscribe page
        if self.params.subscribe:
            return self.redirect('/subscribe',
                                 id=person.record_id,
                                 subscribe_email=self.params.author_email,
                                 context='add_note')

        # Redirect to this page so the browser's back button works properly.
        self.redirect('/view', id=self.params.id, query=self.params.query)