diff options
| -rw-r--r-- | src/mailman/database/alembic/versions/7b254d88f122_members_and_list_moderation_action.py | 73 | ||||
| -rw-r--r-- | src/mailman/database/tests/test_migrations.py | 67 |
2 files changed, 140 insertions, 0 deletions
diff --git a/src/mailman/database/alembic/versions/7b254d88f122_members_and_list_moderation_action.py b/src/mailman/database/alembic/versions/7b254d88f122_members_and_list_moderation_action.py new file mode 100644 index 000000000..fb02ccaed --- /dev/null +++ b/src/mailman/database/alembic/versions/7b254d88f122_members_and_list_moderation_action.py @@ -0,0 +1,73 @@ +"""Members and list moderation action + +Revision ID: 7b254d88f122 +Revises: bfda02ab3a9b +Create Date: 2016-02-10 11:31:04.233619 + +This is a data-only migration. If a member has the same moderation action as +the mailing list's default, then set its moderation action to None and use the +fallback to the list's default. + +""" + +# revision identifiers, used by Alembic. +revision = '7b254d88f122' +down_revision = 'd4fbb4fd34ca' + +import sqlalchemy as sa +from alembic import op +from mailman.database.types import Enum +from mailman.interfaces.action import Action +from mailman.interfaces.member import MemberRole + +mailinglist_table = sa.sql.table( + 'mailinglist', + sa.sql.column('id', sa.Integer), + sa.sql.column('list_id', sa.Unicode), + sa.sql.column('default_member_action', Enum(Action)), + sa.sql.column('default_nonmember_action', Enum(Action)), + ) + +member_table = sa.sql.table( + 'member', + sa.sql.column('id', sa.Integer), + sa.sql.column('list_id', sa.Unicode), + sa.sql.column('role', Enum(MemberRole)), + sa.sql.column('moderation_action', Enum(Action)), + ) + +# This migration only considers members and nonmembers. +members_query = member_table.select().where(sa.or_( + member_table.c.role == MemberRole.member, + member_table.c.role == MemberRole.nonmember, + )) + + +def _get_default_action(connection, member): + mailing_list = connection.execute(mailinglist_table.select().where( + mailinglist_table.c.list_id == member['list_id'])).fetchone() + return mailing_list['default_{}_action'.format(member['role'].name)] + + +def upgrade(): + connection = op.get_bind() + for member in connection.execute(members_query).fetchall(): + default_action = _get_default_action(connection, member) + # If the (non)member's moderation action is the same as the mailing + # list's default, then set it to None. The moderation rule will + # fallback to the list's default. + if member['moderation_action'] == default_action: + connection.execute(member_table.update().where( + member_table.c.id == member['id'] + ).values(moderation_action=None)) + + +def downgrade(): + connection = op.get_bind() + for member in connection.execute(members_query.where( + member_table.c.moderation_action == None)).fetchall(): + default_action = _get_default_action(connection, member) + # Use the mailing list's default action + connection.execute(member_table.update().where( + member_table.c.id == member['id'] + ).values(moderation_action=default_action)) diff --git a/src/mailman/database/tests/test_migrations.py b/src/mailman/database/tests/test_migrations.py index 3087c46ee..bc394149b 100644 --- a/src/mailman/database/tests/test_migrations.py +++ b/src/mailman/database/tests/test_migrations.py @@ -218,3 +218,70 @@ class TestMigrations(unittest.TestCase): os.path.join(config.LIST_DATA_DIR, 'ant@example.com'))) self.assertTrue(os.path.exists(ant.data_path)) self.assertTrue(os.path.exists(bee.data_path)) + + def test_7b254d88f122_moderation_action(self): + from mailman.database.types import Enum + from mailman.interfaces.action import Action + from mailman.interfaces.member import MemberRole + from mailman.interfaces.usermanager import IUserManager + from zope.component import getUtility + mailinglist_table = sa.sql.table( + 'mailinglist', + sa.sql.column('id', sa.Integer), + sa.sql.column('list_id', sa.Unicode), + sa.sql.column('default_member_action', Enum(Action)), + sa.sql.column('default_nonmember_action', Enum(Action)), + ) + + member_table = sa.sql.table( + 'member', + sa.sql.column('id', sa.Integer), + sa.sql.column('list_id', sa.Unicode), + sa.sql.column('address_id', sa.Integer), + sa.sql.column('role', Enum(MemberRole)), + sa.sql.column('moderation_action', Enum(Action)), + ) + user_manager = getUtility(IUserManager) + with transaction(): + # Start at the previous revision + alembic.command.downgrade(alembic_cfg, 'd4fbb4fd34ca') + # Create a mailing list through the standard API. + ant = create_list('ant@example.com') + # Create members + anne = user_manager.create_address('anne@example.com') + bart = user_manager.create_address('bart@example.com') + cris = user_manager.create_address('cris@example.com') + dana = user_manager.create_address('dana@example.com') + config.db.store.flush() # to get the last auto-increment id. + config.db.store.execute(member_table.insert().values([ + {'address_id': anne.id, 'role': MemberRole.owner, + 'list_id': ant.list_id, 'moderation_action': Action.accept}, + {'address_id': bart.id, 'role': MemberRole.moderator, + 'list_id': ant.list_id, 'moderation_action': Action.accept}, + {'address_id': cris.id, 'role': MemberRole.member, + 'list_id': ant.list_id, 'moderation_action': Action.defer}, + {'address_id': dana.id, 'role': MemberRole.nonmember, + 'list_id': ant.list_id, 'moderation_action': Action.hold}, + ])) + # Upgrade and check the moderation_action. + alembic.command.upgrade(alembic_cfg, '7b254d88f122') + members = config.db.store.execute(sa.select([ + member_table.c.address_id, member_table.c.moderation_action, + ])).fetchall() + self.assertEqual(members, [ + (anne.id, Action.accept), + (bart.id, Action.accept), + (cris.id, None), + (dana.id, None), + ]) + # Downgrade and check. + alembic.command.downgrade(alembic_cfg, 'd4fbb4fd34ca') + members = config.db.store.execute(sa.select([ + member_table.c.address_id, member_table.c.moderation_action, + ])).fetchall() + self.assertEqual(members, [ + (anne.id, Action.accept), + (bart.id, Action.accept), + (cris.id, Action.defer), + (dana.id, Action.hold), + ]) |
