From 37f0d141e9bd36a97ee118ff670960903bafdc0e Mon Sep 17 00:00:00 2001 From: Henrik Levkowetz Date: Sat, 5 May 2018 12:37:15 +0000 Subject: [PATCH] Added a new field name_from_draft to Person, to hold the name field equivalent as captured from drafts, in case name has been modified by the user and we're asked to remove that info under GDPR. Added history for Email, and also an origin field to capture from where we got an email address (draft name, username, meeting registration, etc.) Added a log.assertion() to Email.save() in order to ensure we don't create any email without setting origin. - Legacy-Id: 15126 --- .../migrations/0003_auto_20180426_0517.py | 23 --------- ...427_0646.py => 0003_auto_20180504_1519.py} | 51 +++++++++++++++++-- ietf/person/models.py | 13 +++-- 3 files changed, 58 insertions(+), 29 deletions(-) delete mode 100644 ietf/person/migrations/0003_auto_20180426_0517.py rename ietf/person/migrations/{0004_auto_20180427_0646.py => 0003_auto_20180504_1519.py} (52%) diff --git a/ietf/person/migrations/0003_auto_20180426_0517.py b/ietf/person/migrations/0003_auto_20180426_0517.py deleted file mode 100644 index 0cad90d03..000000000 --- a/ietf/person/migrations/0003_auto_20180426_0517.py +++ /dev/null @@ -1,23 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.12 on 2018-04-26 05:17 -from __future__ import unicode_literals - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('person', '0002_auto_20180330_0808'), - ] - - operations = [ - migrations.RemoveField( - model_name='person', - name='address', - ), - migrations.RemoveField( - model_name='personhistory', - name='address', - ), - ] diff --git a/ietf/person/migrations/0004_auto_20180427_0646.py b/ietf/person/migrations/0003_auto_20180504_1519.py similarity index 52% rename from ietf/person/migrations/0004_auto_20180427_0646.py rename to ietf/person/migrations/0003_auto_20180504_1519.py index c9d83578c..1bbedbbec 100644 --- a/ietf/person/migrations/0004_auto_20180427_0646.py +++ b/ietf/person/migrations/0003_auto_20180504_1519.py @@ -1,21 +1,43 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.11.12 on 2018-04-27 06:46 +# Generated by Django 1.11.12 on 2018-05-04 15:19 from __future__ import unicode_literals import datetime from django.conf import settings +import django.core.validators from django.db import migrations, models import django.db.models.deletion +import ietf.utils.models class Migration(migrations.Migration): dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('person', '0003_auto_20180426_0517'), + ('person', '0002_auto_20180330_0808'), ] operations = [ + migrations.CreateModel( + name='HistoricalEmail', + fields=[ + ('address', models.CharField(db_index=True, max_length=64, validators=[django.core.validators.EmailValidator()])), + ('time', models.DateTimeField(blank=True, editable=False)), + ('primary', models.BooleanField(default=False)), + ('origin', models.CharField(default=b'', editable=False, max_length=150)), + ('active', models.BooleanField(default=True)), + ('history_id', models.AutoField(primary_key=True, serialize=False)), + ('history_date', models.DateTimeField()), + ('history_change_reason', models.CharField(max_length=100, null=True)), + ('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)), + ('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'ordering': ('-history_date', '-history_id'), + 'get_latest_by': 'history_date', + 'verbose_name': 'historical email', + }, + ), migrations.CreateModel( name='HistoricalPerson', fields=[ @@ -24,10 +46,10 @@ class Migration(migrations.Migration): ('name', models.CharField(db_index=True, help_text=b'Preferred form of name.', max_length=255, verbose_name=b'Full Name (Unicode)')), ('ascii', models.CharField(help_text=b'Name as rendered in ASCII (Latin, unaccented) characters.', max_length=255, verbose_name=b'Full Name (ASCII)')), ('ascii_short', models.CharField(blank=True, help_text=b'Example: A. Nonymous. Fill in this with initials and surname only if taking the initials and surname of the ASCII name above produces an incorrect initials-only form. (Blank is OK).', max_length=32, null=True, verbose_name=b'Abbreviated Name (ASCII)')), - ('affiliation', models.CharField(blank=True, help_text=b'Employer, university, sponsor, etc.', max_length=255)), ('biography', models.TextField(blank=True, help_text=b'Short biography for use on leadership pages. Use plain text or reStructuredText markup.')), ('photo', models.TextField(blank=True, default=None, max_length=100)), ('photo_thumb', models.TextField(blank=True, default=None, max_length=100)), + ('name_from_draft', models.CharField(editable=False, help_text=b'Name as found in a draft submission.', max_length=255, null=True, verbose_name=b'Full Name (from submission)')), ('history_id', models.AutoField(primary_key=True, serialize=False)), ('history_date', models.DateTimeField()), ('history_change_reason', models.CharField(max_length=100, null=True)), @@ -49,7 +71,30 @@ class Migration(migrations.Migration): model_name='personhistory', name='user', ), + migrations.RemoveField( + model_name='person', + name='address', + ), + migrations.RemoveField( + model_name='person', + name='affiliation', + ), + migrations.AddField( + model_name='email', + name='origin', + field=models.CharField(default=b'', editable=False, max_length=150), + ), + migrations.AddField( + model_name='person', + name='name_from_draft', + field=models.CharField(editable=False, help_text=b'Name as found in a draft submission.', max_length=255, null=True, verbose_name=b'Full Name (from submission)'), + ), migrations.DeleteModel( name='PersonHistory', ), + migrations.AddField( + model_name='historicalemail', + name='person', + field=ietf.utils.models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='person.Person'), + ), ] diff --git a/ietf/person/models.py b/ietf/person/models.py index acfcbb528..674d6fc5f 100644 --- a/ietf/person/models.py +++ b/ietf/person/models.py @@ -25,6 +25,7 @@ from ietf.utils.mail import send_mail_preformatted from ietf.utils.storage import NoLocationMigrationFileSystemStorage from ietf.utils.mail import formataddr from ietf.person.name import unidecode_name +from ietf.utils import log from ietf.utils.models import ForeignKey, OneToOneField @@ -39,10 +40,10 @@ class Person(models.Model): ascii = models.CharField("Full Name (ASCII)", max_length=255, help_text="Name as rendered in ASCII (Latin, unaccented) characters.") # The short ascii-form of the name. Also in alias table if non-null ascii_short = models.CharField("Abbreviated Name (ASCII)", max_length=32, null=True, blank=True, help_text="Example: A. Nonymous. Fill in this with initials and surname only if taking the initials and surname of the ASCII name above produces an incorrect initials-only form. (Blank is OK).") - affiliation = models.CharField(max_length=255, blank=True, help_text="Employer, university, sponsor, etc.") biography = models.TextField(blank=True, help_text="Short biography for use on leadership pages. Use plain text or reStructuredText markup.") photo = models.ImageField(storage=NoLocationMigrationFileSystemStorage(), upload_to=settings.PHOTOS_DIRNAME, blank=True, default=None) photo_thumb = models.ImageField(storage=NoLocationMigrationFileSystemStorage(), upload_to=settings.PHOTOS_DIRNAME, blank=True, default=None) + name_from_draft = models.CharField("Full Name (from submission)", null=True, max_length=255, editable=False, help_text="Name as found in a draft submission.") def __unicode__(self): return self.plain_name() @@ -195,7 +196,6 @@ class Person(models.Model): ct1['href'] = urljoin(hostscheme, self.json_url()) ct1['name'] = self.name ct1['ascii'] = self.ascii - ct1['affiliation']= self.affiliation return ct1 class Alias(models.Model): @@ -226,12 +226,15 @@ class Alias(models.Model): verbose_name_plural = "Aliases" class Email(models.Model): + history = HistoricalRecords() address = models.CharField(max_length=64, primary_key=True, validators=[validate_email]) person = ForeignKey(Person, null=True) time = models.DateTimeField(auto_now_add=True) primary = models.BooleanField(default=False) + origin = models.CharField(max_length=150, default='', editable=False) # User.username or Document.name active = models.BooleanField(default=True) # Old email addresses are *not* purged, as history - # information points to persons through these + # information points to persons through these + def __unicode__(self): return self.address or "Email object with id: %s"%self.pk @@ -275,6 +278,10 @@ class Email(models.Model): return return self.address + def save(self, *args, **kwargs): + if not self.origin: + log.assertion('self.origin') + super(Email, self).save(*args, **kwargs) # "{key.id}{salt}{hash} KEY_STRUCT = "i12s32s"