diff options
Diffstat (limited to 'mediagoblin/db')
-rw-r--r-- | mediagoblin/db/extratypes.py | 30 | ||||
-rw-r--r-- | mediagoblin/db/migration_tools.py | 33 | ||||
-rw-r--r-- | mediagoblin/db/migrations.py | 238 | ||||
-rw-r--r-- | mediagoblin/db/mixin.py | 1 | ||||
-rw-r--r-- | mediagoblin/db/models.py | 275 | ||||
-rw-r--r-- | mediagoblin/db/util.py | 1 |
6 files changed, 554 insertions, 24 deletions
diff --git a/mediagoblin/db/extratypes.py b/mediagoblin/db/extratypes.py index f2304af0..8e04d58d 100644 --- a/mediagoblin/db/extratypes.py +++ b/mediagoblin/db/extratypes.py @@ -15,6 +15,7 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. +from sqlalchemy.ext.mutable import Mutable from sqlalchemy.types import TypeDecorator, Unicode, TEXT import json @@ -38,7 +39,7 @@ class PathTupleWithSlashes(TypeDecorator): return value -# The following class and only this one class is in very +# The following two classes and only these two classes is in very # large parts based on example code from sqlalchemy. # # The original copyright notice and license follows: @@ -61,3 +62,30 @@ class JSONEncoded(TypeDecorator): if value is not None: value = json.loads(value) return value + + +class MutationDict(Mutable, dict): + @classmethod + def coerce(cls, key, value): + "Convert plain dictionaries to MutationDict." + + if not isinstance(value, MutationDict): + if isinstance(value, dict): + return MutationDict(value) + + # this call will raise ValueError + return Mutable.coerce(key, value) + else: + return value + + def __setitem__(self, key, value): + "Detect dictionary set events and emit change events." + + dict.__setitem__(self, key, value) + self.changed() + + def __delitem__(self, key): + "Detect dictionary del events and emit change events." + + dict.__delitem__(self, key) + self.changed() diff --git a/mediagoblin/db/migration_tools.py b/mediagoblin/db/migration_tools.py index e75f3757..e39070c3 100644 --- a/mediagoblin/db/migration_tools.py +++ b/mediagoblin/db/migration_tools.py @@ -16,6 +16,7 @@ from mediagoblin.tools.common import simple_printer from sqlalchemy import Table +from sqlalchemy.sql import select class TableAlreadyExists(Exception): pass @@ -286,3 +287,35 @@ def inspect_table(metadata, table_name): """Simple helper to get a ref to an already existing table""" return Table(table_name, metadata, autoload=True, autoload_with=metadata.bind) + +def replace_table_hack(db, old_table, replacement_table): + """ + A function to fully replace a current table with a new one for migrati- + -ons. This is necessary because some changes are made tricky in some situa- + -tion, for example, dropping a boolean column in sqlite is impossible w/o + this method + + :param old_table A ref to the old table, gotten through + inspect_table + + :param replacement_table A ref to the new table, gotten through + inspect_table + + Users are encouraged to sqlalchemy-migrate replace table solutions, unless + that is not possible... in which case, this solution works, + at least for sqlite. + """ + surviving_columns = replacement_table.columns.keys() + old_table_name = old_table.name + for row in db.execute(select( + [column for column in old_table.columns + if column.name in surviving_columns])): + + db.execute(replacement_table.insert().values(**row)) + db.commit() + + old_table.drop() + db.commit() + + replacement_table.rename(old_table_name) + db.commit() diff --git a/mediagoblin/db/migrations.py b/mediagoblin/db/migrations.py index e2a0bf26..426080a2 100644 --- a/mediagoblin/db/migrations.py +++ b/mediagoblin/db/migrations.py @@ -19,16 +19,18 @@ import uuid from sqlalchemy import (MetaData, Table, Column, Boolean, SmallInteger, Integer, Unicode, UnicodeText, DateTime, - ForeignKey) + ForeignKey, Date) from sqlalchemy.exc import ProgrammingError from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.sql import and_ from migrate.changeset.constraint import UniqueConstraint -from mediagoblin.db.extratypes import JSONEncoded -from mediagoblin.db.migration_tools import RegisterMigration, inspect_table -from mediagoblin.db.models import MediaEntry, Collection, User, MediaComment +from mediagoblin.db.extratypes import JSONEncoded, MutationDict +from mediagoblin.db.migration_tools import ( + RegisterMigration, inspect_table, replace_table_hack) +from mediagoblin.db.models import (MediaEntry, Collection, MediaComment, User, + Privilege) MIGRATIONS = {} @@ -469,13 +471,12 @@ def wants_notifications(db): """Add a wants_notifications field to User model""" metadata = MetaData(bind=db.bind) user_table = inspect_table(metadata, "core__users") - col = Column('wants_notifications', Boolean, default=True) col.create(user_table) - db.commit() + @RegisterMigration(16, MIGRATIONS) def upload_limits(db): """Add user upload limit columns""" @@ -494,3 +495,228 @@ def upload_limits(db): col.create(media_entry_table) db.commit() + + +@RegisterMigration(17, MIGRATIONS) +def add_file_metadata(db): + """Add file_metadata to MediaFile""" + metadata = MetaData(bind=db.bind) + media_file_table = inspect_table(metadata, "core__mediafiles") + + col = Column('file_metadata', MutationDict.as_mutable(JSONEncoded)) + col.create(media_file_table) + + db.commit() + +################### +# Moderation tables +################### + +class ReportBase_v0(declarative_base()): + __tablename__ = 'core__reports' + id = Column(Integer, primary_key=True) + reporter_id = Column(Integer, ForeignKey(User.id), nullable=False) + report_content = Column(UnicodeText) + reported_user_id = Column(Integer, ForeignKey(User.id), nullable=False) + created = Column(DateTime, nullable=False, default=datetime.datetime.now) + discriminator = Column('type', Unicode(50)) + resolver_id = Column(Integer, ForeignKey(User.id)) + resolved = Column(DateTime) + result = Column(UnicodeText) + __mapper_args__ = {'polymorphic_on': discriminator} + + +class CommentReport_v0(ReportBase_v0): + __tablename__ = 'core__reports_on_comments' + __mapper_args__ = {'polymorphic_identity': 'comment_report'} + + id = Column('id',Integer, ForeignKey('core__reports.id'), + primary_key=True) + comment_id = Column(Integer, ForeignKey(MediaComment.id), nullable=True) + + +class MediaReport_v0(ReportBase_v0): + __tablename__ = 'core__reports_on_media' + __mapper_args__ = {'polymorphic_identity': 'media_report'} + + id = Column('id',Integer, ForeignKey('core__reports.id'), primary_key=True) + media_entry_id = Column(Integer, ForeignKey(MediaEntry.id), nullable=True) + + +class UserBan_v0(declarative_base()): + __tablename__ = 'core__user_bans' + user_id = Column(Integer, ForeignKey(User.id), nullable=False, + primary_key=True) + expiration_date = Column(Date) + reason = Column(UnicodeText, nullable=False) + + +class Privilege_v0(declarative_base()): + __tablename__ = 'core__privileges' + id = Column(Integer, nullable=False, primary_key=True, unique=True) + privilege_name = Column(Unicode, nullable=False, unique=True) + + +class PrivilegeUserAssociation_v0(declarative_base()): + __tablename__ = 'core__privileges_users' + privilege_id = Column( + 'core__privilege_id', + Integer, + ForeignKey(User.id), + primary_key=True) + user_id = Column( + 'core__user_id', + Integer, + ForeignKey(Privilege.id), + primary_key=True) + + +PRIVILEGE_FOUNDATIONS_v0 = [{'privilege_name':u'admin'}, + {'privilege_name':u'moderator'}, + {'privilege_name':u'uploader'}, + {'privilege_name':u'reporter'}, + {'privilege_name':u'commenter'}, + {'privilege_name':u'active'}] + + +# vR1 stands for "version Rename 1". This only exists because we need +# to deal with dropping some booleans and it's otherwise impossible +# with sqlite. + +class User_vR1(declarative_base()): + __tablename__ = 'rename__users' + id = Column(Integer, primary_key=True) + username = Column(Unicode, nullable=False, unique=True) + email = Column(Unicode, nullable=False) + pw_hash = Column(Unicode) + created = Column(DateTime, nullable=False, default=datetime.datetime.now) + wants_comment_notification = Column(Boolean, default=True) + wants_notifications = Column(Boolean, default=True) + license_preference = Column(Unicode) + url = Column(Unicode) + bio = Column(UnicodeText) # ?? + uploaded = Column(Integer, default=0) + upload_limit = Column(Integer) + + +@RegisterMigration(18, MIGRATIONS) +def create_moderation_tables(db): + + # First, we will create the new tables in the database. + #-------------------------------------------------------------------------- + ReportBase_v0.__table__.create(db.bind) + CommentReport_v0.__table__.create(db.bind) + MediaReport_v0.__table__.create(db.bind) + UserBan_v0.__table__.create(db.bind) + Privilege_v0.__table__.create(db.bind) + PrivilegeUserAssociation_v0.__table__.create(db.bind) + + db.commit() + + # Then initialize the tables that we will later use + #-------------------------------------------------------------------------- + metadata = MetaData(bind=db.bind) + privileges_table= inspect_table(metadata, "core__privileges") + user_table = inspect_table(metadata, 'core__users') + user_privilege_assoc = inspect_table( + metadata, 'core__privileges_users') + + # This section initializes the default Privilege foundations, that + # would be created through the FOUNDATIONS system in a new instance + #-------------------------------------------------------------------------- + for parameters in PRIVILEGE_FOUNDATIONS_v0: + db.execute(privileges_table.insert().values(**parameters)) + + db.commit() + + # This next section takes the information from the old is_admin and status + # columns and converts those to the new privilege system + #-------------------------------------------------------------------------- + admin_users_ids, active_users_ids, inactive_users_ids = ( + db.execute( + user_table.select().where( + user_table.c.is_admin==True)).fetchall(), + db.execute( + user_table.select().where( + user_table.c.is_admin==False).where( + user_table.c.status==u"active")).fetchall(), + db.execute( + user_table.select().where( + user_table.c.is_admin==False).where( + user_table.c.status!=u"active")).fetchall()) + + # Get the ids for each of the privileges so we can reference them ~~~~~~~~~ + (admin_privilege_id, uploader_privilege_id, + reporter_privilege_id, commenter_privilege_id, + active_privilege_id) = [ + db.execute(privileges_table.select().where( + privileges_table.c.privilege_name==privilege_name)).first()['id'] + for privilege_name in + [u"admin",u"uploader",u"reporter",u"commenter",u"active"] + ] + + # Give each user the appopriate privileges depending whether they are an + # admin, an active user or an inactive user ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + for admin_user in admin_users_ids: + admin_user_id = admin_user['id'] + for privilege_id in [admin_privilege_id, uploader_privilege_id, + reporter_privilege_id, commenter_privilege_id, + active_privilege_id]: + db.execute(user_privilege_assoc.insert().values( + core__privilege_id=admin_user_id, + core__user_id=privilege_id)) + + for active_user in active_users_ids: + active_user_id = active_user['id'] + for privilege_id in [uploader_privilege_id, reporter_privilege_id, + commenter_privilege_id, active_privilege_id]: + db.execute(user_privilege_assoc.insert().values( + core__privilege_id=active_user_id, + core__user_id=privilege_id)) + + for inactive_user in inactive_users_ids: + inactive_user_id = inactive_user['id'] + for privilege_id in [uploader_privilege_id, reporter_privilege_id, + commenter_privilege_id]: + db.execute(user_privilege_assoc.insert().values( + core__privilege_id=inactive_user_id, + core__user_id=privilege_id)) + + db.commit() + + # And then, once the information is taken from is_admin & status columns + # we drop all of the vestigial columns from the User table. + #-------------------------------------------------------------------------- + if db.bind.url.drivername == 'sqlite': + # SQLite has some issues that make it *impossible* to drop boolean + # columns. So, the following code is a very hacky workaround which + # makes it possible. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + User_vR1.__table__.create(db.bind) + db.commit() + new_user_table = inspect_table(metadata, 'rename__users') + replace_table_hack(db, user_table, new_user_table) + else: + # If the db is not run using SQLite, this process is much simpler ~~~~~ + + status = user_table.columns['status'] + email_verified = user_table.columns['email_verified'] + is_admin = user_table.columns['is_admin'] + status.drop() + email_verified.drop() + is_admin.drop() + + db.commit() +@RegisterMigration(19, MIGRATIONS) +def drop_MediaEntry_collected(db): + """ + Drop unused MediaEntry.collected column + """ + metadata = MetaData(bind=db.bind) + + media_collected= inspect_table(metadata, 'core__media_entries') + media_collected = media_collected.columns['collected'] + + media_collected.drop() + + db.commit() diff --git a/mediagoblin/db/mixin.py b/mediagoblin/db/mixin.py index 57b27d83..25ce6642 100644 --- a/mediagoblin/db/mixin.py +++ b/mediagoblin/db/mixin.py @@ -46,7 +46,6 @@ class UserMixin(object): def bio_html(self): return cleaned_markdown_conversion(self.bio) - class GenerateSlugMixin(object): """ Mixin to add a generate_slug method to objects. diff --git a/mediagoblin/db/models.py b/mediagoblin/db/models.py index a2675678..b750375d 100644 --- a/mediagoblin/db/models.py +++ b/mediagoblin/db/models.py @@ -23,15 +23,15 @@ import datetime from sqlalchemy import Column, Integer, Unicode, UnicodeText, DateTime, \ Boolean, ForeignKey, UniqueConstraint, PrimaryKeyConstraint, \ - SmallInteger + SmallInteger, Date from sqlalchemy.orm import relationship, backref, with_polymorphic from sqlalchemy.orm.collections import attribute_mapped_collection from sqlalchemy.sql.expression import desc from sqlalchemy.ext.associationproxy import association_proxy from sqlalchemy.util import memoized_property - -from mediagoblin.db.extratypes import PathTupleWithSlashes, JSONEncoded +from mediagoblin.db.extratypes import (PathTupleWithSlashes, JSONEncoded, + MutationDict) from mediagoblin.db.base import Base, DictReadAttrProxy from mediagoblin.db.mixin import UserMixin, MediaEntryMixin, \ MediaCommentMixin, CollectionMixin, CollectionItemMixin @@ -48,6 +48,7 @@ from migrate import changeset _log = logging.getLogger(__name__) + class User(Base, UserMixin): """ TODO: We should consider moving some rarely used fields @@ -63,15 +64,12 @@ class User(Base, UserMixin): # point. email = Column(Unicode, nullable=False) pw_hash = Column(Unicode) - email_verified = Column(Boolean, default=False) created = Column(DateTime, nullable=False, default=datetime.datetime.now) - status = Column(Unicode, default=u"needs_email_verification", nullable=False) # Intented to be nullable=False, but migrations would not work for it # set to nullable=True implicitly. wants_comment_notification = Column(Boolean, default=True) wants_notifications = Column(Boolean, default=True) license_preference = Column(Unicode) - is_admin = Column(Boolean, default=False, nullable=False) url = Column(Unicode) bio = Column(UnicodeText) # ?? uploaded = Column(Integer, default=0) @@ -84,8 +82,8 @@ class User(Base, UserMixin): return '<{0} #{1} {2} {3} "{4}">'.format( self.__class__.__name__, self.id, - 'verified' if self.email_verified else 'non-verified', - 'admin' if self.is_admin else 'user', + 'verified' if self.has_privilege(u'active') else 'non-verified', + 'admin' if self.has_privilege(u'admin') else 'user', self.username) def delete(self, **kwargs): @@ -107,6 +105,36 @@ class User(Base, UserMixin): super(User, self).delete(**kwargs) _log.info('Deleted user "{0}" account'.format(self.username)) + def has_privilege(self,*priv_names): + """ + This method checks to make sure a user has all the correct privileges + to access a piece of content. + + :param priv_names A variable number of unicode objects which rep- + -resent the different privileges which may give + the user access to this content. If you pass + multiple arguments, the user will be granted + access if they have ANY of the privileges + passed. + """ + if len(priv_names) == 1: + priv = Privilege.query.filter( + Privilege.privilege_name==priv_names[0]).one() + return (priv in self.all_privileges) + elif len(priv_names) > 1: + return self.has_privilege(priv_names[0]) or \ + self.has_privilege(*priv_names[1:]) + return False + + def is_banned(self): + """ + Checks if this user is banned. + + :returns True if self is banned + :returns False if self is not + """ + return UserBan.query.get(self.id) is not None + class Client(Base): """ @@ -191,7 +219,6 @@ class MediaEntry(Base, MediaEntryMixin): state = Column(Unicode, default=u'unprocessed', nullable=False) # or use sqlalchemy.types.Enum? license = Column(Unicode) - collected = Column(Integer, default=0) file_size = Column(Integer, default=0) fail_error = Column(Unicode) @@ -267,6 +294,35 @@ class MediaEntry(Base, MediaEntryMixin): if media is not None: return media.url_for_self(urlgen) + def get_file_metadata(self, file_key, metadata_key=None): + """ + Return the file_metadata dict of a MediaFile. If metadata_key is given, + return the value of the key. + """ + media_file = MediaFile.query.filter_by(media_entry=self.id, + name=unicode(file_key)).first() + + if media_file: + if metadata_key: + return media_file.file_metadata.get(metadata_key, None) + + return media_file.file_metadata + + def set_file_metadata(self, file_key, **kwargs): + """ + Update the file_metadata of a MediaFile. + """ + media_file = MediaFile.query.filter_by(media_entry=self.id, + name=unicode(file_key)).first() + + file_metadata = media_file.file_metadata or {} + + for key, value in kwargs.iteritems(): + file_metadata[key] = value + + media_file.file_metadata = file_metadata + media_file.save() + @property def media_data(self): return getattr(self, self.media_data_ref) @@ -363,6 +419,7 @@ class MediaFile(Base): nullable=False) name_id = Column(SmallInteger, ForeignKey(FileKeynames.id), nullable=False) file_path = Column(PathTupleWithSlashes) + file_metadata = Column(MutationDict.as_mutable(JSONEncoded)) __table_args__ = ( PrimaryKeyConstraint('media_entry', 'name_id'), @@ -644,16 +701,198 @@ class ProcessingNotification(Notification): 'polymorphic_identity': 'processing_notification' } - with_polymorphic( Notification, [ProcessingNotification, CommentNotification]) +class ReportBase(Base): + """ + This is the basic report object which the other reports are based off of. + + :keyword reporter_id Holds the id of the user who created + the report, as an Integer column. + :keyword report_content Hold the explanation left by the repor- + -ter to indicate why they filed the + report in the first place, as a + Unicode column. + :keyword reported_user_id Holds the id of the user who created + the content which was reported, as + an Integer column. + :keyword created Holds a datetime column of when the re- + -port was filed. + :keyword discriminator This column distinguishes between the + different types of reports. + :keyword resolver_id Holds the id of the moderator/admin who + resolved the report. + :keyword resolved Holds the DateTime object which descri- + -bes when this report was resolved + :keyword result Holds the UnicodeText column of the + resolver's reasons for resolving + the report this way. Some of this + is auto-generated + """ + __tablename__ = 'core__reports' + id = Column(Integer, primary_key=True) + reporter_id = Column(Integer, ForeignKey(User.id), nullable=False) + reporter = relationship( + User, + backref=backref("reports_filed_by", + lazy="dynamic", + cascade="all, delete-orphan"), + primaryjoin="User.id==ReportBase.reporter_id") + report_content = Column(UnicodeText) + reported_user_id = Column(Integer, ForeignKey(User.id), nullable=False) + reported_user = relationship( + User, + backref=backref("reports_filed_on", + lazy="dynamic", + cascade="all, delete-orphan"), + primaryjoin="User.id==ReportBase.reported_user_id") + created = Column(DateTime, nullable=False, default=datetime.datetime.now()) + discriminator = Column('type', Unicode(50)) + resolver_id = Column(Integer, ForeignKey(User.id)) + resolver = relationship( + User, + backref=backref("reports_resolved_by", + lazy="dynamic", + cascade="all, delete-orphan"), + primaryjoin="User.id==ReportBase.resolver_id") + + resolved = Column(DateTime) + result = Column(UnicodeText) + __mapper_args__ = {'polymorphic_on': discriminator} + + def is_comment_report(self): + return self.discriminator=='comment_report' + + def is_media_entry_report(self): + return self.discriminator=='media_report' + + def is_archived_report(self): + return self.resolved is not None + + def archive(self,resolver_id, resolved, result): + self.resolver_id = resolver_id + self.resolved = resolved + self.result = result + + +class CommentReport(ReportBase): + """ + Reports that have been filed on comments. + :keyword comment_id Holds the integer value of the reported + comment's ID + """ + __tablename__ = 'core__reports_on_comments' + __mapper_args__ = {'polymorphic_identity': 'comment_report'} + + id = Column('id',Integer, ForeignKey('core__reports.id'), + primary_key=True) + comment_id = Column(Integer, ForeignKey(MediaComment.id), nullable=True) + comment = relationship( + MediaComment, backref=backref("reports_filed_on", + lazy="dynamic")) + + +class MediaReport(ReportBase): + """ + Reports that have been filed on media entries + :keyword media_entry_id Holds the integer value of the reported + media entry's ID + """ + __tablename__ = 'core__reports_on_media' + __mapper_args__ = {'polymorphic_identity': 'media_report'} + + id = Column('id',Integer, ForeignKey('core__reports.id'), + primary_key=True) + media_entry_id = Column(Integer, ForeignKey(MediaEntry.id), nullable=True) + media_entry = relationship( + MediaEntry, + backref=backref("reports_filed_on", + lazy="dynamic")) + +class UserBan(Base): + """ + Holds the information on a specific user's ban-state. As long as one of + these is attached to a user, they are banned from accessing mediagoblin. + When they try to log in, they are greeted with a page that tells them + the reason why they are banned and when (if ever) the ban will be + lifted + + :keyword user_id Holds the id of the user this object is + attached to. This is a one-to-one + relationship. + :keyword expiration_date Holds the date that the ban will be lifted. + If this is null, the ban is permanent + unless a moderator manually lifts it. + :keyword reason Holds the reason why the user was banned. + """ + __tablename__ = 'core__user_bans' + + user_id = Column(Integer, ForeignKey(User.id), nullable=False, + primary_key=True) + expiration_date = Column(Date) + reason = Column(UnicodeText, nullable=False) + + +class Privilege(Base): + """ + The Privilege table holds all of the different privileges a user can hold. + If a user 'has' a privilege, the User object is in a relationship with the + privilege object. + + :keyword privilege_name Holds a unicode object that is the recognizable + name of this privilege. This is the column + used for identifying whether or not a user + has a necessary privilege or not. + + """ + __tablename__ = 'core__privileges' + + id = Column(Integer, nullable=False, primary_key=True) + privilege_name = Column(Unicode, nullable=False, unique=True) + all_users = relationship( + User, + backref='all_privileges', + secondary="core__privileges_users") + + def __init__(self, privilege_name): + ''' + Currently consructors are required for tables that are initialized thru + the FOUNDATIONS system. This is because they need to be able to be con- + -structed by a list object holding their arg*s + ''' + self.privilege_name = privilege_name + + def __repr__(self): + return "<Privilege %s>" % (self.privilege_name) + + +class PrivilegeUserAssociation(Base): + ''' + This table holds the many-to-many relationship between User and Privilege + ''' + + __tablename__ = 'core__privileges_users' + + privilege_id = Column( + 'core__privilege_id', + Integer, + ForeignKey(User.id), + primary_key=True) + user_id = Column( + 'core__user_id', + Integer, + ForeignKey(Privilege.id), + primary_key=True) + MODELS = [ - User, Client, RequestToken, AccessToken, NonceTimestamp, MediaEntry, Tag, - MediaTag, MediaComment, Collection, CollectionItem, MediaFile, FileKeynames, - MediaAttachmentFile, ProcessingMetaData, Notification, CommentNotification, - ProcessingNotification, CommentSubscription] + User, MediaEntry, Tag, MediaTag, MediaComment, Collection, CollectionItem, + MediaFile, FileKeynames, MediaAttachmentFile, ProcessingMetaData, + Notification, CommentNotification, ProcessingNotification, Client, + CommentSubscription, ReportBase, CommentReport, MediaReport, UserBan, + Privilege, PrivilegeUserAssociation, + RequestToken, AccessToken, NonceTimestamp] """ Foundations are the default rows that are created immediately after the tables @@ -669,7 +908,13 @@ MODELS = [ FOUNDATIONS = {User:user_foundations} """ -FOUNDATIONS = {} +privilege_foundations = [{'privilege_name':u'admin'}, + {'privilege_name':u'moderator'}, + {'privilege_name':u'uploader'}, + {'privilege_name':u'reporter'}, + {'privilege_name':u'commenter'}, + {'privilege_name':u'active'}] +FOUNDATIONS = {Privilege:privilege_foundations} ###################################################### # Special, migrations-tracking table diff --git a/mediagoblin/db/util.py b/mediagoblin/db/util.py index 8431361a..7a0a3a73 100644 --- a/mediagoblin/db/util.py +++ b/mediagoblin/db/util.py @@ -67,7 +67,6 @@ def check_collection_slug_used(creator_id, slug, ignore_c_id): does_exist = Session.query(Collection.id).filter(filt).first() is not None return does_exist - if __name__ == '__main__': from mediagoblin.db.open import setup_connection_and_db_from_config |