from migrate.changeset.constraint import UniqueConstraint
-from mediagoblin.db.extratypes import JSONEncoded
+from mediagoblin.db.extratypes import JSONEncoded, MutationDict
from mediagoblin.db.migration_tools import RegisterMigration, inspect_table
- from mediagoblin.db.models import MediaEntry, Collection, User, MediaComment
+ from mediagoblin.db.models import (MediaEntry, Collection, MediaComment, User,
+ Privilege)
MIGRATIONS = {}
"""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()
+
+ 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'}]
+
+
+ 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) # ??
+
+ @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==1)).fetchall(),
+ db.execute(
+ user_table.select().where(
+ user_table.c.is_admin==0).where(
+ user_table.c.status==u"active")).fetchall(),
+ db.execute(
+ user_table.select().where(
+ user_table.c.is_admin==0).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 inactivated 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 the 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')
+ for row in db.execute(user_table.select()):
+ db.execute(new_user_table.insert().values(
+ username=row.username,
+ email=row.email,
+ pw_hash=row.pw_hash,
+ created=row.created,
+ wants_comment_notification=row.wants_comment_notification,
+ wants_notifications=row.wants_notifications,
+ license_preference=row.license_preference,
+ url=row.url,
+ bio=row.bio))
+
+ db.commit()
+ user_table.drop()
+
+ db.commit()
+ new_user_table.rename("core__users")
+ else:
+ # If the db is not 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(16, MIGRATIONS)
+def upload_limits(db):
+ """Add user upload limit columns"""
+ metadata = MetaData(bind=db.bind)
+
+ user_table = inspect_table(metadata, 'core__users')
+ media_entry_table = inspect_table(metadata, 'core__media_entries')
+
+ col = Column('uploaded', Integer, default=0)
+ col.create(user_table)
+
+ col = Column('upload_limit', Integer)
+ col.create(user_table)
+
+ col = Column('file_size', Integer, default=0)
+ 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()
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)
+ upload_limit = Column(Integer)
## TODO
# plugin data would be in a separate model
{% block mediagoblin_content -%}
- {# If no user... #}
- {% if not user %}
- <p>{% trans %}Sorry, no such user found.{% endtrans %}</p>
-
- {# User exists, but needs verification #}
- {% elif not user.has_privilege('active') %}
- {% if user == request.user %}
- {# this should only be visible when you are this user #}
- <div class="form_box">
- <h1>{% trans %}Email verification needed{% endtrans %}</h1>
+ <h1>
+ {%- trans username=user.username %}{{ username }}'s profile{% endtrans -%}
+ </h1>
+ {% if not user.url and not user.bio %}
+ {% if request.user and (request.user.id == user.id) %}
+ <div class="profile_sidebar empty_space">
<p>
- {% trans -%}
- Almost done! Your account still needs to be activated.
- {%- endtrans %}
- </p>
- <p>
- {% trans -%}
- An email should arrive in a few moments with instructions on how to do so.
- {%- endtrans %}
+ {% trans %}Here's a spot to tell others about yourself.{% endtrans %}
</p>
- <p>{% trans %}In case it doesn't:{% endtrans %}</p>
-
- <a href="{{ request.urlgen('mediagoblin.auth.resend_verification') }}"
- class="button_action_highlight">{% trans %}Resend verification email{% endtrans %}</a>
- </div>
+ <a href="{{ request.urlgen('mediagoblin.edit.profile',
+ user=user.username) }}" class="button_action">
+ {%- trans %}Edit profile{% endtrans -%}
+ </a>
{% else %}
- {# if the user is not you, but still needs to verify their email #}
- <div class="form_box">
- <h1>{% trans %}Email verification needed{% endtrans %}</h1>
-
+ <div class="profile_sidebar empty_space">
<p>
{% trans -%}
- Someone has registered an account with this username, but it still has to be activated.
- {%- endtrans %}
- </p>
-
- <p>
- {% trans login_url=request.urlgen('mediagoblin.auth.login') -%}
- If you are that person but you've lost your verification email, you can <a href="{{ login_url }}">log in</a> and resend it.
+ This user hasn't filled in their profile (yet).
{%- endtrans %}
</p>
- </div>
{% endif %}
-
- {# Active(?) (or at least verified at some point) user, horray! #}
{% else %}
- <h1>
- {%- trans username=user.username %}{{ username }}'s profile{% endtrans -%}
- </h1>
-
- {% if not user.url and not user.bio %}
- {% if request.user and (request.user.id == user.id) %}
- <div class="profile_sidebar empty_space">
- <p>
- {% trans %}Here's a spot to tell others about yourself.{% endtrans %}
- </p>
- <a href="{{ request.urlgen('mediagoblin.edit.profile',
- user=user.username) }}" class="button_action">
- {%- trans %}Edit profile{% endtrans -%}
- </a>
- {% else %}
- <div class="profile_sidebar empty_space">
- <p>
- {% trans -%}
- This user hasn't filled in their profile (yet).
- {%- endtrans %}
- </p>
+ <div class="profile_sidebar">
+ {% include "mediagoblin/utils/profile.html" %}
+ {% if request.user and
- (request.user.id == user.id or request.user.is_admin) %}
++ (request.user.id == user.id or request.user.has_privilege('admin')) %}
+ <a href="{{ request.urlgen('mediagoblin.edit.profile',
+ user=user.username) }}">
+ {%- trans %}Edit profile{% endtrans -%}
+ </a>
{% endif %}
- {% else %}
- <div class="profile_sidebar">
- {% include "mediagoblin/utils/profile.html" %}
- {% if request.user and
- (request.user.id == user.id or request.user.has_privilege('admin')) %}
- <a href="{{ request.urlgen('mediagoblin.edit.profile',
- user=user.username) }}">
- {%- trans %}Edit profile{% endtrans -%}
- </a>
- {% endif %}
- {% endif %}
+ {% endif %}
+ <p>
+ <a href="{{ request.urlgen('mediagoblin.user_pages.collection_list',
+ user=user.username) }}">
+ {%- trans %}Browse collections{% endtrans -%}
+ </a>
+ </p>
+ </div>
+
+ {% if media_entries.count() %}
+ <div class="profile_showcase">
+ {{ object_gallery(request, media_entries, pagination,
+ pagination_base_url=user_gallery_url, col_number=3) }}
+ {% include "mediagoblin/utils/object_gallery.html" %}
+ <div class="clear"></div>
<p>
- <a href="{{ request.urlgen('mediagoblin.user_pages.collection_list',
- user=user.username) }}">
- {%- trans %}Browse collections{% endtrans -%}
+ <a href="{{ user_gallery_url }}">
+ {% trans username=user.username -%}
+ View all of {{ username }}'s media{% endtrans -%}
</a>
</p>
+ {% set feed_url = request.urlgen(
+ 'mediagoblin.user_pages.atom_feed',
+ user=user.username) %}
+ {% include "mediagoblin/utils/feed_link.html" %}
</div>
-
- {% if media_entries.count() %}
- <div class="profile_showcase">
- {{ object_gallery(request, media_entries, pagination,
- pagination_base_url=user_gallery_url, col_number=3) }}
- {% include "mediagoblin/utils/object_gallery.html" %}
- <div class="clear"></div>
+ {% else %}
+ {% if request.user and (request.user.id == user.id) %}
+ <div class="profile_showcase empty_space">
<p>
- <a href="{{ user_gallery_url }}">
- {% trans username=user.username -%}
- View all of {{ username }}'s media{% endtrans -%}
- </a>
+ {% trans -%}
+ This is where your media will appear, but you don't seem to have added anything yet.
+ {%- endtrans %}
</p>
- {% set feed_url = request.urlgen(
- 'mediagoblin.user_pages.atom_feed',
- user=user.username) %}
- {% include "mediagoblin/utils/feed_link.html" %}
+ <a class="button_action"
+ href="{{ request.urlgen('mediagoblin.submit.start') }}">
+ {%- trans %}Add media{% endtrans -%}
+ </a>
</div>
{% else %}
- {% if request.user and (request.user.id == user.id) %}
- <div class="profile_showcase empty_space">
- <p>
- {% trans -%}
- This is where your media will appear, but you don't seem to have added anything yet.
- {%- endtrans %}
- </p>
- <a class="button_action"
- href="{{ request.urlgen('mediagoblin.submit.start') }}">
- {%- trans %}Add media{% endtrans -%}
- </a>
- </div>
- {% else %}
- <div class="profile_showcase empty_space">
- <p>
- {% trans -%}
- There doesn't seem to be any media here yet...
- {%- endtrans %}
- </p>
- </div>
- {% endif %}
+ <div class="profile_showcase empty_space">
+ <p>
+ {% trans -%}
+ There doesn't seem to be any media here yet...
+ {%- endtrans %}
+ </p>
+ </div>
{% endif %}
- <div class="clear"></div>
{% endif %}
+ <div class="clear"></div>
{% endblock %}
--- /dev/null
- {% if user.status == "needs_email_verification" %}
+{#
+# GNU MediaGoblin -- federated, autonomous media hosting
+# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# This is the main user homepage for non-active users that still need
+# registration etc.
+#}
+{% extends "mediagoblin/base.html" %}
+
+{% block title %}
+ {%- if user -%}
+ {%- trans username=user.username -%}
+ {{ username }}'s profile
+ {%- endtrans %} — {{ super() }}
+ {%- else -%}
+ {{ super() }}
+ {%- endif -%}
+{% endblock %}
+
+
+{% block mediagoblin_content -%}
+ {# User exists, but needs verification #}
++ {% if not user.has_privilege('active') %}
+ {% if user == request.user %}
+ {# this should only be visible when you are this user #}
+ <div class="form_box">
+ <h1>{% trans %}Email verification needed{% endtrans %}</h1>
+
+ <p>
+ {% trans -%}
+ Almost done! Your account still needs to be activated.
+ {%- endtrans %}
+ </p>
+ <p>
+ {% trans -%}
+ An email should arrive in a few moments with instructions on how to do so.
+ {%- endtrans %}
+ </p>
+ <p>{% trans %}In case it doesn't:{% endtrans %}</p>
+
+ <a href="{{ request.urlgen('mediagoblin.auth.resend_verification') }}"
+ class="button_action_highlight">{% trans %}Resend verification email{% endtrans %}</a>
+ </div>
+ {% else %}
+ {# if the user is not you, but still needs to verify their email #}
+ <div class="form_box">
+ <h1>{% trans %}Email verification needed{% endtrans %}</h1>
+
+ <p>
+ {% trans -%}
+ Someone has registered an account with this username, but it still has to be activated.
+ {%- endtrans %}
+ </p>
+ <p>
+ {% trans login_url=request.urlgen('mediagoblin.auth.login') -%}
+ If you are that person but you've lost your verification email, you can <a href="{{ login_url }}">log in</a> and resend it.
+ {%- endtrans %}
+ </p>
+ </div>
+ {% endif %}
+
+ {# Active(?) (or at least verified at some point) user, horray! #}
+ {% else %}
+ <h1>
+ {%- trans username=user.username %}{{ username }}{% endtrans -%}
+ </h1>
+ <p>{{ username }} is not active.</p>
+ <div class="clear"></div>
+ {% endif %}
+{% endblock %}
response.follow()
## Did we redirect to the proper page? Use the right template?
- assert urlparse.urlsplit(response.location)[2] == '/u/happygirl/'
+ assert urlparse.urlsplit(response.location)[2] == '/u/angrygirl/'
- assert 'mediagoblin/user_pages/user.html' in template.TEMPLATE_TEST_CONTEXT
+ assert 'mediagoblin/user_pages/user_nonactive.html' in template.TEMPLATE_TEST_CONTEXT
## Make sure user is in place
new_user = mg_globals.database.User.query.filter_by(
- username=u'happygirl').first()
+ username=u'angrygirl').first()
assert new_user
- assert new_user.status == u'needs_email_verification'
- assert new_user.email_verified == False
+ ## Make sure that the proper privileges are granted on registration
+
+ assert new_user.has_privilege(u'commenter')
+ assert new_user.has_privilege(u'uploader')
+ assert new_user.has_privilege(u'reporter')
+ assert not new_user.has_privilege(u'active')
## Make sure user is logged in
request = template.TEMPLATE_TEST_CONTEXT[
- 'mediagoblin/user_pages/user.html']['request']
+ 'mediagoblin/user_pages/user_nonactive.html']['request']
assert request.session['user_id'] == unicode(new_user.id)
## Make sure we get email confirmation, and try verifying
## Make sure link to change password is sent by email
assert len(mail.EMAIL_TEST_INBOX) == 1
message = mail.EMAIL_TEST_INBOX.pop()
- assert message['To'] == 'happygrrl@example.org'
+ assert message['To'] == 'angrygrrl@example.org'
email_context = template.TEMPLATE_TEST_CONTEXT[
- 'mediagoblin/auth/fp_verification_email.txt']
+ 'mediagoblin/plugins/basic_auth/fp_verification_email.txt']
#TODO - change the name of verification_url to something forgot-password-ish
assert email_context['verification_url'] in message.get_payload(decode=True)
response, context = self.do_post({'title': u'Normal upload 3 (pdf)'},
do_follow=True,
**self.upload_data(GOOD_PDF))
- self.check_url(response, '/u/{0}/'.format(self.test_user.username))
+ self.check_url(response, '/u/{0}/'.format(self.our_user().username))
assert 'mediagoblin/user_pages/user.html' in context
+ def test_default_upload_limits(self):
+ self.user_upload_limits(uploaded=500)
+
+ # User uploaded should be 500
+ assert self.test_user.uploaded == 500
+
+ response, context = self.do_post({'title': u'Normal upload 4'},
+ do_follow=True,
+ **self.upload_data(GOOD_JPG))
+ self.check_url(response, '/u/{0}/'.format(self.test_user.username))
+ assert 'mediagoblin/user_pages/user.html' in context
+
+ # Reload user
+ self.test_user = User.query.filter_by(
+ username=self.test_user.username
+ ).first()
+
+ # Shouldn't have uploaded
+ assert self.test_user.uploaded == 500
+
+ def test_user_upload_limit(self):
+ self.user_upload_limits(uploaded=25, upload_limit=25)
+
+ # User uploaded should be 25
+ assert self.test_user.uploaded == 25
+
+ response, context = self.do_post({'title': u'Normal upload 5'},
+ do_follow=True,
+ **self.upload_data(GOOD_JPG))
+ self.check_url(response, '/u/{0}/'.format(self.test_user.username))
+ assert 'mediagoblin/user_pages/user.html' in context
+
+ # Reload user
+ self.test_user = User.query.filter_by(
+ username=self.test_user.username
+ ).first()
+
+ # Shouldn't have uploaded
+ assert self.test_user.uploaded == 25
+
+ def test_user_under_limit(self):
+ self.user_upload_limits(uploaded=499)
+
+ # User uploaded should be 499
+ assert self.test_user.uploaded == 499
+
+ response, context = self.do_post({'title': u'Normal upload 6'},
+ do_follow=False,
+ **self.upload_data(MED_PNG))
+ form = context['mediagoblin/submit/start.html']['submit_form']
+ assert form.file.errors == [u'Sorry, uploading this file will put you'
+ ' over your upload limit.']
+
+ # Reload user
+ self.test_user = User.query.filter_by(
+ username=self.test_user.username
+ ).first()
+
+ # Shouldn't have uploaded
+ assert self.test_user.uploaded == 499
+
+ def test_big_file(self):
+ response, context = self.do_post({'title': u'Normal upload 7'},
+ do_follow=False,
+ **self.upload_data(BIG_PNG))
+
+ form = context['mediagoblin/submit/start.html']['submit_form']
+ assert form.file.errors == [u'Sorry, the file size is too big.']
+
def check_media(self, request, find_data, count=None):
media = MediaEntry.query.filter_by(**find_data)
if count is not None:
user = User.query.filter_by(username=request.matchdict['user']).first()
if not user:
return render_404(request)
- elif user.status != u'active':
+ elif not user.has_privilege(u'active'):
return render_to_response(
request,
- 'mediagoblin/user_pages/user.html',
+ 'mediagoblin/user_pages/user_nonactive.html',
{'user': user})
cursor = MediaEntry.query.\