Exemplo n.º 1
0
    def post(self):
        now = get_utcnow()
        # Several messages here exceed the 80-column limit because django's
        # makemessages script can't handle messages split across lines. :(
        if self.config.use_family_name:
            if not (self.params.given_name and self.params.family_name):
                return self.error(400, _('The Given name and Family name are both required.  Please go back and try again.'))
        else:
            if not self.params.given_name:
                return self.error(400, _('Name is required.  Please go back and try again.'))
        if not self.params.author_name:
            self.params.author_name = self.params.given_name
        expiry_date = days_to_date(self.params.expiry_option or
                                   self.config.default_expiry_days)

        # If nothing was uploaded, just use the photo_url that was provided.
        photo, photo_url = (None, self.params.photo_url)
        note_photo, note_photo_url = (None, self.params.note_photo_url)
        try:
            # If a photo was uploaded, create a Photo entry and get the URL
            # where we serve it.
            if self.params.photo is not None:
                photo, photo_url = create_photo(self.params.photo, self)
            if self.params.note_photo is not None:
                note_photo, note_photo_url = \
                    create_photo(self.params.note_photo, self)
        except PhotoError, e:
            return self.error(400, e.message)
Exemplo n.º 2
0
    def post(self):
        now = get_utcnow()

        # Several messages here exceed the 80-column limit because django's
        # makemessages script can't handle messages split across lines. :(
        if self.config.use_family_name:
            if not (self.params.given_name and self.params.family_name):
                return self.error(400, _('The Given name and Family name are both required.  Please go back and try again.'))
        else:
            if not self.params.given_name:
                return self.error(400, _('Name is required.  Please go back and try again.'))
        if not self.params.author_name:
            if self.params.clone:
                return self.error(400, _('The Original author\'s name is required.  Please go back and try again.'))
            else:
                return self.error(400, _('Your name is required in the "Source" section.  Please go back and try again.'))

        if self.params.add_note:
            if not self.params.text:
                return self.error(400, _('Message is required. 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 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(400, _('Not authorized to post notes with the status "I have received information that this person is dead".'))

        source_date = None
        if self.params.source_date:
            try:
                source_date = validate_date(self.params.source_date)
            except ValueError:
                return self.error(400, _('Original posting date is not in YYYY-MM-DD format, or is a nonexistent date.  Please go back and try again.'))
            if source_date > now:
                return self.error(400, _('Date cannot be in the future.  Please go back and try again.'))

        expiry_date = days_to_date(self.params.expiry_option or
                                   self.config.default_expiry_days)

        # If nothing was uploaded, just use the photo_url that was provided.
        photo, photo_url = (None, self.params.photo_url)
        note_photo, note_photo_url = (None, self.params.note_photo_url)
        try:
            # If a photo was uploaded, create a Photo entry and get the URL
            # where we serve it.
            if self.params.photo is not None:
                photo, photo_url = create_photo(self.params.photo, self)
            if self.params.note_photo is not None:
                note_photo, note_photo_url = \
                    create_photo(self.params.note_photo, self)
        except PhotoError, e:
            return self.error(400, e.message)
Exemplo n.º 3
0
    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()
Exemplo n.º 4
0
    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)
            except PhotoError, e:
                return self.error(400, e.message)
            photo.put()
Exemplo n.º 5
0
    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)
            except PhotoError, e:
                return self.error(400, e.message)
            photo.put()
    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.given_name:
            return self.error(
                200, _('Your name is required in the "About you" section.  '
                       'Please go back and try again.'))
        if not self.params.author_name:
            self.params.author_name = self.params.given_name

        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 not person:
            return self.error(404,
                _("This person's entry does not exist or has been deleted."))
        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()
Exemplo n.º 7
0
 def post(self):
     # TODO: factor all this out somewhere shared
     person = Person.create_original(
         self.repo,
         entry_date=utils.get_utcnow(),
         family_name=self.params.family_name,
         given_name=self.params.given_name,
         age=self.params.age,
         sex=self.params.sex,
         home_city=self.params.home_city,
         home_state=self.params.home_state,
         home_country=self.params.home_country,
     )
     if self.params.photo:
         p, photo_url = photo.create_photo(self.params.photo, self)
         p.put()
         person.photo = p
         person.photo_url = photo_url
     person.update_index(['old', 'new'])
     person.put_new()
     json = {'personId': person.record_id}
     self._return_json(json)
Exemplo n.º 8
0
 def post(self):
     # TODO: factor all this out somewhere shared
     person = Person.create_original(
         self.repo,
         entry_date=utils.get_utcnow(),
         family_name=self.params.family_name,
         given_name=self.params.given_name,
         age=self.params.age,
         sex=self.params.sex,
         home_city=self.params.home_city,
         home_state=self.params.home_state,
         home_country=self.params.home_country,
     )
     if self.params.photo:
         p, photo_url = photo.create_photo(self.params.photo, self)
         p.put()
         person.photo = p
         person.photo_url = photo_url
     person.update_index(['old', 'new'])
     person.put_new()
     json = {'personId': person.record_id}
     self._return_json(json)
Exemplo n.º 9
0
    def post(self):
        if not (self.auth and self.auth.domain_write_permission):
            self.info(403,
                      message='Missing or invalid authorization key',
                      style='plain')
            return

        # Check for empty body
        if not self.request.body:
            self.error(400, "Request body must not be empty")
            return

        # Size check for uploaded file
        if len(self.request.body) > PHOTO_UPLOAD_MAX_SIZE:
            self.error(400, "Size of uploaded file is greater than 10MB")
            return

        try:
            photo_img = images.Image(self.request.body)
            photo, photo_url = create_photo(photo_img, self)
        except PhotoError, e:
            self.error(400, e.message)
Exemplo n.º 10
0
def create_photo_from_input(handler, photo_upload, photo_url):
    """Creates a photo from a user-provided photo or URL.

    Either of the parameters may be used (but not both). Either way, it will
    return a Photo object and a URL with which to serve it.

    If neither parameter is provided, returns (None, None).

    Args:
      handler: a request handler (needed to generate URLs)
      photo_upload: optional; an images.Image for an uploaded photo
      photo_url: optional; a user-provided URL for a photo

    Returns:
      A tuple with an Image and URL with which to serve it, or (None, None) if
      neither an uploaded photo nor a URL was provided.
    """
    if photo_upload is not None:
        return create_photo(photo_upload, handler)
    elif photo_url:
        return create_photo_from_url(photo_url, handler)
    return (None, None)
Exemplo n.º 11
0
    def post(self):
        if not (self.auth and self.auth.domain_write_permission):
            self.info(
                403,
                message='Missing or invalid authorization key',
                style='plain')
            return

        # Check for empty body
        if not self.request.body:
            self.error(400, "Request body must not be empty")
            return

        # Size check for uploaded file
        if len(self.request.body) > PHOTO_UPLOAD_MAX_SIZE:
            self.error(400, "Size of uploaded file is greater than 10MB")
            return

        try:
            photo_img = images.Image(self.request.body)
            photo, photo_url = create_photo(photo_img, self)
        except PhotoError, e:
            self.error(400, e.message)
Exemplo n.º 12
0
    def post(self):
        now = get_utcnow()

        # Several messages here exceed the 80-column limit because django's
        # makemessages script can't handle messages split across lines. :(
        if self.config.use_family_name:
            if not (self.params.given_name and self.params.family_name):
                return self.error(400, _('The Given name and Family name are both required.  Please go back and try again.'))
        else:
            if not self.params.given_name:
                return self.error(400, _('Name is required.  Please go back and try again.'))

        # If user is inputting his/her own information, set some params automatically
        if self.params.own_info == 'yes':
            self.params.author_name = self.params.given_name
            self.params.status = 'is_note_author'
            self.params.author_made_contact = 'yes'
            if self.params.your_own_email:
                self.params.author_email = self.params.your_own_email
            if self.params.your_own_phone:
                self.params.author_phone = self.params.your_own_phone

        else:
            if not self.params.author_name:
                if self.params.clone:
                    return self.error(400, _('The Original author\'s name is required.  Please go back and try again.'))
                else:
                    return self.error(400, _('Your name is required in the "Source" section.  Please go back and try again.'))

        if self.params.add_note:
            if not self.params.text:
                return self.error(400, _('Message is required. 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 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(400, _('Not authorized to post notes with the status "I have received information that this person is dead".'))

        source_date = None
        if self.params.source_date:
            try:
                source_date = validate_date(self.params.source_date)
            except ValueError:
                return self.error(400, _('Original posting date is not in YYYY-MM-DD format, or is a nonexistent date.  Please go back and try again.'))
            if source_date > now:
                return self.error(400, _('Date cannot be in the future.  Please go back and try again.'))

        expiry_date = days_to_date(self.params.expiry_option or
                                   self.config.default_expiry_days)

        # If nothing was uploaded, just use the photo_url that was provided.
        photo, photo_url = (None, self.params.photo_url)
        note_photo, note_photo_url = (None, self.params.note_photo_url)
        try:
            # If a photo was uploaded, create a Photo entry and get the URL
            # where we serve it.
            if self.params.photo is not None:
                photo, photo_url = create_photo(self.params.photo, self)
            if self.params.note_photo is not None:
                note_photo, note_photo_url = \
                    create_photo(self.params.note_photo, self)
        except PhotoError, e:
            return self.error(400, e.message)
Exemplo n.º 13
0
    def post(self):
        now = get_utcnow()

        # Several messages here exceed the 80-column limit because django's
        # makemessages script can't handle messages split across lines. :(
        if self.config.use_family_name:
            if not (self.params.given_name and self.params.family_name):
                return self.error(400, _('The Given name and Family name are both required.  Please go back and try again.'))
        else:
            if not self.params.given_name:
                return self.error(400, _('Name is required.  Please go back and try again.'))

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

        # If user is inputting his/her own information, set some params automatically
        if self.params.own_info == 'yes':
            self.params.author_name = self.params.given_name
            self.params.status = 'is_note_author'
            self.params.author_made_contact = 'yes'
            if self.params.your_own_email:
                self.params.author_email = self.params.your_own_email
            if self.params.your_own_phone:
                self.params.author_phone = self.params.your_own_phone

        else:
            if not self.params.author_name:
                if self.params.clone:
                    return self.error(400, _('The Original author\'s name is required.  Please go back and try again.'))
                else:
                    return self.error(400, _('Your name is required in the "Source" section.  Please go back and try again.'))

        if self.params.add_note:
            if not self.params.text:
                return self.error(400, _('Message is required. 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 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(400, _('Not authorized to post notes with the status "I have received information that this person is dead".'))

        source_date = None
        if self.params.source_date:
            try:
                source_date = validate_date(self.params.source_date)
            except ValueError:
                return self.error(400, _('Original posting date is not in YYYY-MM-DD format, or is a nonexistent date.  Please go back and try again.'))
            if source_date > now:
                return self.error(400, _('Date cannot be in the future.  Please go back and try again.'))

        expiry_date = days_to_date(self.params.expiry_option or
                                   self.config.default_expiry_days)

        profile_urls = filter(
            lambda url: url, [self.params.profile_url1,
                              self.params.profile_url2,
                              self.params.profile_url3])
        url_validator = URLValidator(schemes=['http', 'https'])
        for profile_url in profile_urls:
            try:
                url_validator(profile_url)
            except ValidationError:
                return self.error(
                    400, _('Please only enter valid profile URLs.'))

        # If nothing was uploaded, just use the photo_url that was provided.
        photo, photo_url = (None, self.params.photo_url)
        note_photo, note_photo_url = (None, self.params.note_photo_url)
        try:
            # If a photo was uploaded, create a Photo entry and get the URL
            # where we serve it.
            if self.params.photo is not None:
                photo, photo_url = create_photo(self.params.photo, self)
            if self.params.note_photo is not None:
                note_photo, note_photo_url = \
                    create_photo(self.params.note_photo, self)
        except PhotoError, e:
            return self.error(400, e.message)
Exemplo n.º 14
0
def create_person(repo,
                  config,
                  user_ip_address,
                  given_name,
                  family_name,
                  own_info,
                  clone,
                  status,
                  source_name,
                  source_url,
                  source_date,
                  referrer,
                  author_name,
                  author_email,
                  author_phone,
                  author_made_contact,
                  users_own_email,
                  users_own_phone,
                  alternate_given_names,
                  alternate_family_names,
                  home_neighborhood,
                  home_city,
                  home_state,
                  home_postal_code,
                  home_country,
                  age,
                  sex,
                  description,
                  photo,
                  photo_url,
                  note_photo,
                  note_photo_url,
                  profile_urls,
                  add_note,
                  text,
                  email_of_found_person,
                  phone_of_found_person,
                  last_known_location,
                  url_builder,
                  source_domain=const.HOME_DOMAIN,
                  should_fuzzify_age=True,
                  expiry_option=None):
    now = get_utcnow()
    # Several messages here exceed the 80-column limit because django's
    # makemessages script can't handle messages split across lines. :(
    if config.use_family_name:
        if not (given_name and family_name):
            raise CreationError(
                _('The Given name and Family name are both required.  Please go back and try again.'
                  ))
    else:
        if not given_name:
            raise CreationError(
                _('Name is required.  Please go back and try again.'))

    # If user is inputting his/her own information, set some params automatically
    if own_info == 'yes':
        author_name = given_name
        status = 'is_note_author'
        author_made_contact = 'yes'
        if users_own_email:
            author_email = users_own_email
        if users_own_phone:
            author_phone = users_own_phone

    if (author_email and not validate_email(author_email)):
        raise CreationError(
            _('The email address you entered appears to be invalid.'))

    else:
        if not author_name:
            if clone:
                raise CreationError(
                    _('The Original author\'s name is required.  Please go back and try again.'
                      ))
            else:
                raise CreationError(
                    _('Your name is required in the "Source" section.  Please go back and try again.'
                      ))

    if add_note:
        if not text:
            raise CreationError(
                _('Message is required. Please go back and try again.'))
        if status == 'is_note_author' and not author_made_contact:
            raise CreationError(
                _('Please check that you have been in contact with the person after the earthquake, or change the "Status of this person" field.'
                  ))
        if status == 'believed_dead' and not config.allow_believed_dead_via_ui:
            raise CreationError(
                _('Not authorized to post notes with the status "I have received information that this person is dead".'
                  ))

    if source_date:
        try:
            source_date = validate_date(source_date)
        except ValueError:
            raise CreationError(
                _('Original posting date is not in YYYY-MM-DD format, or is a nonexistent date.  Please go back and try again.'
                  ))
        if source_date > now:
            raise CreationError(
                _('Date cannot be in the future.  Please go back and try again.'
                  ))

    expiry_date = days_to_date(expiry_option or config.default_expiry_days)

    profile_urls = profile_urls or []
    profile_urls = filter(lambda url: url, profile_urls)
    url_validator = URLValidator(schemes=['http', 'https'])
    for profile_url in profile_urls:
        try:
            url_validator(profile_url)
        except ValidationError:
            raise CreationError(_('Please only enter valid profile URLs.'))

    # If nothing was uploaded, just use the photo_url that was provided.
    try:
        # If a photo was uploaded, create a Photo entry and get the URL
        # where we serve it.
        if photo is not None:
            photo, photo_url = create_photo(photo, repo, url_builder)
        if note_photo is not None:
            note_photo, note_photo_url = create_photo(note_photo, repo,
                                                      url_builder)
    except PhotoError, e:
        raise CreationError(e.message)
Exemplo n.º 15
0
def create_person(
        repo,
        config,
        user_ip_address,
        given_name,
        family_name,
        own_info,
        clone,
        status,
        source_name,
        source_url,
        source_date,
        referrer,
        author_name,
        author_email,
        author_phone,
        author_made_contact,
        users_own_email,
        users_own_phone,
        alternate_given_names,
        alternate_family_names,
        home_neighborhood,
        home_city,
        home_state,
        home_postal_code,
        home_country,
        age,
        sex,
        description,
        person_photo,
        person_photo_url,
        note_photo,
        note_photo_url,
        profile_urls,
        add_note,
        text,
        email_of_found_person,
        phone_of_found_person,
        last_known_location,
        url_builder,
        source_domain=const.HOME_DOMAIN,
        should_fuzzify_age=True,
        expiry_option=None):
    now = get_utcnow()
    if config.use_family_name:
        if not (given_name and family_name):
            raise CreationError(_(
                'The Given name and Family name are both required.  Please go '
                'back and try again.'))
    else:
        if not given_name:
            raise CreationError(_(
                'Name is required.  Please go back and try again.'))

    # If user is inputting his/her own information, set some params automatically
    if own_info == 'yes':
        author_name = given_name
        status = 'is_note_author'
        author_made_contact = 'yes'
        if users_own_email:
            author_email = users_own_email
        if users_own_phone:
            author_phone = users_own_phone

    if (author_email and not validate_email(author_email)):
        raise CreationError(_(
            'The email address you entered appears to be invalid.'))

    else:
        if not author_name:
            if clone:
                raise CreationError(_(
                    'The Original author\'s name is required.  Please go back '
                    'and try again.'))
            else:
                raise CreationError(_(
                    'Your name is required in the "Source" section.  Please go '
                    'back and try again.'))

    if add_note:
        validate_note_data(
            config=config,
            status=status,
            author_name=author_name,
            author_email=author_email,
            author_made_contact=author_made_contact,
            text=text)

    if source_date:
        try:
            source_date = validate_date(source_date)
        except ValueError:
            raise CreationError(_(
                'Original posting date is not in YYYY-MM-DD format, or is a '
                'nonexistent date.  Please go back and try again.'))
        if source_date > now:
            raise CreationError(_(
                'Date cannot be in the future.  Please go back and try again.'))

    expiry_date = days_to_date(expiry_option or config.default_expiry_days)

    profile_urls = profile_urls or []
    profile_urls = filter(lambda url: url, profile_urls)
    url_validator = URLValidator(schemes=['http', 'https'])
    for profile_url in profile_urls:
        try:
            url_validator(profile_url)
        except ValidationError:
            raise CreationError(_('Please only enter valid profile URLs.'))

    # If nothing was uploaded, just use the photo URL that was provided.
    try:
        # If a photo was uploaded, create a Photo entry and get the URL
        # where we serve it.
        if person_photo is not None:
            person_photo, person_photo_url = photo.create_photo(
                person_photo, repo, url_builder)
        if note_photo is not None:
            note_photo, note_photo_url = photo.create_photo(
                note_photo, repo, url_builder)
    except photo.PhotoError as e:
        raise CreationError(e.message)
    # Finally, store the Photo. Past this point, we should NOT self.error.
    if person_photo:
        person_photo.put()
    if note_photo:
        note_photo.put()

    # Person records have to have a source_date; if none entered, use now.
    source_date = source_date or now

    # Determine the source name, or fill it in if the record is original
    # (i.e. created for the first time here, not copied from elsewhere).
    if not clone:
        # record originated here
        if referrer:
            source_name = "%s (referred by %s)" % (source_domain, referrer)
        else:
            source_name = source_domain

    if age and should_fuzzify_age:
        age = fuzzify_age(age)
    person = Person.create_original(
        repo,
        entry_date=now,
        expiry_date=expiry_date,
        given_name=given_name,
        family_name=family_name,
        full_name=get_full_name(given_name, family_name, config),
        alternate_names=get_full_name(
            alternate_given_names or '', alternate_family_names or '', config),
        description=description,
        sex=sex,
        age=age,
        home_city=home_city,
        home_state=home_state,
        home_postal_code=home_postal_code,
        home_neighborhood=home_neighborhood,
        home_country=home_country,
        profile_urls='\n'.join(profile_urls),
        author_name=author_name,
        author_phone=author_phone,
        author_email=author_email,
        source_url=source_url,
        source_date=source_date,
        source_name=source_name,
        photo=person_photo,
        photo_url=person_photo_url
    )
    person.update_index(['old', 'new'])

    if add_note:
        try:
            note = create_note(
                repo=repo,
                person=person,
                config=config,
                user_ip_address=user_ip_address,
                status=status,
                source_date=source_date,
                author_name=author_name,
                author_email=author_email,
                author_phone=author_phone,
                author_made_contact=bool(author_made_contact),
                note_photo=note_photo,
                note_photo_url=note_photo_url,
                text=text,
                email_of_found_person=email_of_found_person,
                phone_of_found_person=phone_of_found_person,
                last_known_location=last_known_location,
                # We called validate_note_data separately above.
                validate_data=False)
        except FlaggedNoteException as e:
            db.put(person)
            UserActionLog.put_new('add', person, copy_properties=False)
            # When the note is detected as spam, we do not update person
            # record with this note or log action. We ask the note author
            # for confirmation first.
            raise e

        person.update_from_note(note)

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

    # Write the person record to datastore
    person.put_new()

    # TODO(ryok): we could do this earlier so we don't neet to db.put twice.
    if not person.source_url and not clone:
        # Put again with the URL, now that we have a person_record_id.
        person.source_url = url_builder(
            '/view', repo, params={'id': person.record_id})
        db.put(person)

    # TODO(ryok): batch-put person, note, photo, note_photo here.

    return person