From: tilly-Q <nattilypigeonfowl@gmail.com>
Date: Sun, 8 Sep 2013 22:26:37 +0000 (-0400)
Subject: This was a quick update, I mostly worked on the transition from using the old
X-Git-Url: https://vcs.fsf.org/?a=commitdiff_plain;h=25625107b6c7805b474ad7da976171991b259e58;p=mediagoblin.git

This was a quick update, I mostly worked on the transition from using the old
User table columns (is_admin, status, email_verified) and making sure that
their functionality is instead completely handled by privileges. I also worked
on the meta pages which I hope to finish soon. I set up migrations to ensure
the default privileges are given to users that should have them. Lastly, I made
it so that banned users can log out.

===============================================================================
    Made Sure the Vestigial Columns of the User Table were not being Used
===============================================================================
--\ mediagoblin/auth/views.py
--\ mediagoblin/db/models.py
--\ mediagoblin/templates/mediagoblin/base.html
--\ mediagoblin/templates/mediagoblin/moderation/user.html
--\ mediagoblin/templates/mediagoblin/user_pages/collection_lis$
--\ mediagoblin/templates/mediagoblin/user_pages/user.html
--\ mediagoblin/tests/test_auth.py
--\ mediagoblin/tests/test_persona.py
--\ mediagoblin/user_pages/views.py

===============================================================================
    Wrote the Migrations to Set up the Default Privileges
===============================================================================
--\ mediagoblin/db/migrations.py
--\ mediagoblin/gmg_commands/users.py

===============================================================================
    Work on the Meta Pages
===============================================================================
--\ mediagoblin/meta/routing.py
--\ mediagoblin/meta/views.py
--\ mediagoblin/static/css/base.css
--\ mediagoblin/templates/mediagoblin/meta/terms_of_service.html

===============================================================================
    Small Changes
===============================================================================
--\ mediagoblin/templates/mediagoblin/base.html
--| Benevolently made it so that banned users can log out

===============================================================================
X   X   X   X   X   X   X   X   X   X   X   X   X   X   X   X   X   X   X   X
===============================================================================
---

diff --git a/mediagoblin/auth/views.py b/mediagoblin/auth/views.py
index 7d95b81a..5e2a2af0 100644
--- a/mediagoblin/auth/views.py
+++ b/mediagoblin/auth/views.py
@@ -150,9 +150,7 @@ def verify_email(request):
 
     user = User.query.filter_by(id=int(token)).first()
 
-    if user and user.email_verified is False:
-        user.status = u'active'
-        user.email_verified = True
+    if user and user.has_privilege(u'active') is False:
         user.verification_key = None
         user.all_privileges.append(
             Privilege.query.filter(
@@ -191,7 +189,7 @@ def resend_activation(request):
 
         return redirect(request, 'mediagoblin.auth.login')
 
-    if request.user.email_verified:
+    if request.user.has_privilege(u'active'):
         messages.add_message(
             request,
             messages.ERROR,
@@ -256,7 +254,7 @@ def forgot_password(request):
         success_message=_("An email has been sent with instructions "
                           "on how to change your password.")
 
-    if user and not(user.email_verified and user.status == 'active'):
+    if user and not(user.has_privilege(u'active')):
         # Don't send reminder because user is inactive or has no verified email
         messages.add_message(request,
             messages.WARNING,
@@ -312,8 +310,8 @@ def verify_forgot_password(request):
         return redirect(
             request, 'index')
 
-    # check if user active and has email verified
-    if user.email_verified and user.status == 'active':
+    # check if user active
+    if user.has_privilege(u'active'):
 
         cp_form = auth_forms.ChangePassForm(formdata_vars)
 
@@ -333,13 +331,13 @@ def verify_forgot_password(request):
                 'mediagoblin/auth/change_fp.html',
                 {'cp_form': cp_form,})
 
-    if not user.email_verified:
+    if not user.has_privilege(u'active'):
         messages.add_message(
             request, messages.ERROR,
             _('You need to verify your email before you can reset your'
               ' password.'))
 
-    if not user.status == 'active':
+    if not user.has_privilege(u'active'):
         messages.add_message(
             request, messages.ERROR,
             _('You are no longer an active user. Please contact the system'
diff --git a/mediagoblin/db/migrations.py b/mediagoblin/db/migrations.py
index 6659feb3..1c0a9291 100644
--- a/mediagoblin/db/migrations.py
+++ b/mediagoblin/db/migrations.py
@@ -29,7 +29,8 @@ 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, Privilege, ReportBase)
+                                   MediaComment, Privilege, ReportBase,
+                                   FOUNDATIONS)
 
 MIGRATIONS = {}
 
@@ -531,6 +532,40 @@ def create_moderation_tables(db):
     UserBan_v0.__table__.create(db.bind)
     Privilege_v0.__table__.create(db.bind)
     PrivilegeUserAssociation_v0.__table__.create(db.bind)
+
     db.commit()
 
+    for parameters in FOUNDATIONS[Privilege]:
+        p = Privilege(**parameters)
+        p.save()
+
 
+@RegisterMigration(16, MIGRATIONS)
+def update_user_privilege_columns(db):
+    metadata = MetaData(bind=db.bind)
+    default_privileges = Privilege.query.filter(
+        Privilege.privilege_name !=u'admin').filter(
+        Privilege.privilege_name !=u'moderator').filter(
+        Privilege.privilege_name !=u'active').all()
+    admin_privilege = Privilege.query.filter(
+        Privilege.privilege_name ==u'admin').first()
+    active_privilege = Privilege.query.filter(
+        Privilege.privilege_name ==u'active').first()
+    for inactive_user in User.query.filter(
+        User.status!=u'active').filter(
+        User.is_admin==False).all():
+
+        inactive_user.all_privileges = default_privileges
+        inactive_user.save()
+    for user in User.query.filter(
+        User.status==u'active').filter(
+        User.is_admin==False).all():
+
+        user.all_privileges = default_privileges + [active_privilege]
+        user.save()
+    for admin_user in User.query.filter(
+        User.is_admin==True).all():
+
+        admin_user.all_privileges = default_privileges + [
+            admin_privilege, active_privilege]
+        admin_user.save()
diff --git a/mediagoblin/db/models.py b/mediagoblin/db/models.py
index f25dc32c..5b77a85d 100644
--- a/mediagoblin/db/models.py
+++ b/mediagoblin/db/models.py
@@ -81,8 +81,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):
diff --git a/mediagoblin/gmg_commands/users.py b/mediagoblin/gmg_commands/users.py
index d319cef9..4a730d9e 100644
--- a/mediagoblin/gmg_commands/users.py
+++ b/mediagoblin/gmg_commands/users.py
@@ -53,8 +53,6 @@ def adduser(args):
         entry.username = unicode(args.username.lower())
         entry.email = unicode(args.email)
         entry.pw_hash = auth.gen_password_hash(args.password)
-        entry.status = u'active'
-        entry.email_verified = True
         default_privileges = [
             db.Privilege.query.filter(
                 db.Privilege.privilege_name==u'commenter').one(),
diff --git a/mediagoblin/meta/routing.py b/mediagoblin/meta/routing.py
index e61bc065..b74cb52d 100644
--- a/mediagoblin/meta/routing.py
+++ b/mediagoblin/meta/routing.py
@@ -15,9 +15,9 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 meta_routes = [
-    ('mediagoblin.meta.code_of_conduct',
-        '/coc/',
-        'mediagoblin.meta.views:code_of_conduct'),
+    ('mediagoblin.meta.terms_of_service',
+        '/tos/',
+        'mediagoblin.meta.views:terms_of_service'),
     ('mediagoblin.meta.reports_panel',
         '/reports/',
         'mediagoblin.meta.views:public_reports_panel'),
diff --git a/mediagoblin/meta/views.py b/mediagoblin/meta/views.py
index 3df0688c..79940a22 100644
--- a/mediagoblin/meta/views.py
+++ b/mediagoblin/meta/views.py
@@ -17,9 +17,9 @@
 from mediagoblin.tools.response import render_to_response
 
 
-def code_of_conduct(request):
+def terms_of_service(request):
     return render_to_response(request,
-        'mediagoblin/meta/code_of_conduct.html',
+        'mediagoblin/meta/terms_of_service.html',
         {})
 
 def public_reports_panel(request):
diff --git a/mediagoblin/static/css/base.css b/mediagoblin/static/css/base.css
index 7fcbb93e..d9610675 100644
--- a/mediagoblin/static/css/base.css
+++ b/mediagoblin/static/css/base.css
@@ -663,13 +663,15 @@ table td.user_without_privilege {
     margin-bottom: 10px;
 }
 #code_of_conduct_list li {
-    margin-top:5px;
+    margin:5px 0 15px 25px;
 }
-ol.nested_sublist{
+.nested_sublist {
     margin: 5px 0 10px 25px;
     font-size:80%;
 }
-
+.nested_sublist li {
+    margin-bottom: 10px;
+}
 
 /* ASCII art and code */
 
diff --git a/mediagoblin/templates/mediagoblin/base.html b/mediagoblin/templates/mediagoblin/base.html
index 278cebb6..40cec5f7 100644
--- a/mediagoblin/templates/mediagoblin/base.html
+++ b/mediagoblin/templates/mediagoblin/base.html
@@ -59,7 +59,7 @@
           {% block mediagoblin_header_title %}{% endblock %}
           <div class="header_right">
             {%- if request.user %}
-              {% if request.user and request.user.status == 'active' and not request.user.is_banned() %}
+              {% if request.user and request.user.has_privilege('active') and not request.user.is_banned() %}
 
                 {% set notification_count = get_notification_count(request.user.id) %}
                 {% if notification_count %}
@@ -68,7 +68,7 @@
                 {% endif %}
                 <a href="javascript:;" class="button_action header_dropdown_down">&#9660;</a>
                 <a href="javascript:;" class="button_action header_dropdown_up">&#9650;</a>
-              {% elif request.user and request.user.status == "needs_email_verification" %}
+              {% elif request.user and not request.user.has_privilege('active') %}
               {# the following link should only appear when verification is needed #}
                 <a href="{{ request.urlgen('mediagoblin.user_pages.user_home',
                                  user=request.user.username) }}"
@@ -81,6 +81,14 @@
                  "javascript:;"
                {% endif %}
                >{% trans %}log out{% endtrans %}</a>
+              {% elif request.user and request.user.is_banned() %}
+                <a id="logout" href=
+               {% if persona is not defined %}
+                 "{{ request.urlgen('mediagoblin.auth.logout') }}"
+               {% else %}
+                 "javascript:;"
+               {% endif %}
+               >{% trans %}log out{% endtrans %}</a>
               {% endif %}
             {%- elif auth %}
             <a href=
@@ -95,7 +103,7 @@
             {%- endif %}
           </div>
           <div class="clear"></div>
-          {% if request.user and request.user.status == 'active' %}
+          {% if request.user and request.user.has_privilege('active') %}
             <div class="header_dropdown">
               <p>
                 <span class="dropdown_title">
diff --git a/mediagoblin/templates/mediagoblin/meta/terms_of_service.html b/mediagoblin/templates/mediagoblin/meta/terms_of_service.html
index 0951c044..b998d1c3 100644
--- a/mediagoblin/templates/mediagoblin/meta/terms_of_service.html
+++ b/mediagoblin/templates/mediagoblin/meta/terms_of_service.html
@@ -1,3 +1,27 @@
+{#
+# 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/>.
+#}
+{% extends "mediagoblin/base.html" %}
+
+{% block title %}
+  Terms of Service
+{% endblock %}
+
+{% block mediagoblin_content -%}
 <h2>The gist</h2>
 
 <h2>Terms of Service</h2>
@@ -14,8 +38,8 @@ that may be published from time to time on this Site by Operator (collectively,
 
 Please read this Agreement carefully before accessing or using the Website. By accessing or using any part of the web site, you agree to become bound by the terms and conditions of this agreement. If you do not agree to all the terms and conditions of this agreement, then you may not access the Website or use any services. If these terms and conditions are considered an offer by Operator, acceptance is expressly limited to these terms. The Website is available only to individuals who are at least 13 years old.
 
-<ol>
-  <li>Your {{ app_config['html_title'] }} Account and Site. If you create a
+<ol id="code_of_conduct_list">
+  <li><strong>Your {{ app_config['html_title'] }} Account and Site.</strong> If you create a
     notice stream on the Website, you are responsible for maintaining the
     security of your account and notice stream, and you are fully responsible
     for all activities that occur under the account and any other actions taken
@@ -30,7 +54,7 @@ Please read this Agreement carefully before accessing or using the Website. By a
     including any damages of any kind incurred as a result of such acts or
     omissions.
   </li>
-  <li>Responsibility of Contributors. If you operate a notice stream, comment
+  <li><strong>Responsibility of Contributors.</strong> If you operate a notice stream, comment
     on a notice stream, post material to the Website, post links on the
     Website, or otherwise make (or allow any third party to make) material
     available by means of the Website (any such material, “Content”), You are
@@ -38,7 +62,7 @@ Please read this Agreement carefully before accessing or using the Website. By a
     Content. That is the case regardless of whether the Content in question
     constitutes text, graphics, an audio file, or computer software. By making
     Content available, you represent and warrant that:
-    <ul>
+    <ul class="nested_sublist">
       <li>the downloading, copying and use of the Content will not infringe
         the proprietary rights, including but not limited to the copyright,
         patent, trademark or trade secret rights, of any third party;
@@ -102,7 +126,7 @@ Please read this Agreement carefully before accessing or using the Website. By a
     (ii) terminate or deny access to and use of the Website to any individual
     or entity for any reason, in Operator’s sole discretion.
   </li>
-  <li>Responsibility of Website Visitors. Operator has not reviewed, and cannot
+  <li><strong>Responsibility of Website Visitors.</strong> Operator has not reviewed, and cannot
     review, all of the material, including computer software, posted to the
     Website, and cannot therefore be responsible for that material’s content,
     use or effects. By operating the Website, Operator does not represent or
@@ -121,7 +145,7 @@ Please read this Agreement carefully before accessing or using the Website. By a
     visitors of the Website, or from any downloading by those visitors of
     content there posted.
   </li>
-  <li>Content Posted on Other Websites. We have not reviewed, and cannot
+  <li><strong>Content Posted on Other Websites.</strong> We have not reviewed, and cannot
     review, all of the material, including computer software, made available
     through the websites and webpages to which {{ app_config['html_title'] }}
     links, and that link to {{ app_config['html_title'] }}. Operator does not
@@ -134,7 +158,7 @@ Please read this Agreement carefully before accessing or using the Website. By a
     disclaims any responsibility for any harm resulting from your use of
     external websites and webpages.
   </li>
-  <li>Copyright Infringement and DMCA Policy. As Operator asks others to
+  <li><strong>Copyright Infringement and DMCA Policy.</strong> As Operator asks others to
     respect its intellectual property rights, it respects the intellectual
     property rights of others. If you believe that material located on or
     linked to by {{ app_config['html_title'] }} violates your copyright, you
@@ -148,7 +172,7 @@ Please read this Agreement carefully before accessing or using the Website. By a
     the Website. In the case of such termination, Operator will have no
     obligation to provide a refund of any amounts previously paid to Operator.
   </li>
-  <li>Intellectual Property. This Agreement does not transfer from Operator to
+  <li><strong>Intellectual Property.</strong> This Agreement does not transfer from Operator to
     you any Operator or third party intellectual property, and all right,
     title and interest in and to such property will remain (as between the
     parties) solely with Operator. {{ app_config['html_title'] }}, the
@@ -161,7 +185,7 @@ Please read this Agreement carefully before accessing or using the Website. By a
     Website grants you no right or license to reproduce or otherwise use any
     Operator or third-party trademarks.
   </li>
-  <li>Changes. Operator reserves the right, at its sole discretion, to modify
+  <li><strong>Changes.</strong> Operator reserves the right, at its sole discretion, to modify
     or replace any part of this Agreement. It is your responsibility to check
     this Agreement periodically for changes. Your continued use of or access
     to the Website following the posting of any changes to this Agreement
@@ -170,7 +194,7 @@ Please read this Agreement carefully before accessing or using the Website. By a
     release of new tools and resources). Such new features and/or services
     shall be subject to the terms and conditions of this Agreement.
   </li>
-  <li>Termination. Operator may terminate your access to all or any part of
+  <li><strong>Termination.</strong> Operator may terminate your access to all or any part of
     the Website at any time, with or without cause, with or without notice,
     effective immediately. If you wish to terminate this Agreement or your
     {{ app_config['html_title'] }} account (if you have one), you may simply
@@ -179,7 +203,7 @@ Please read this Agreement carefully before accessing or using the Website. By a
     including, without limitation, ownership provisions, warranty disclaimers,
     indemnity and limitations of liability.
   </li>
-  <li>Disclaimer of Warranties. The Website is provided “as is”. Operator and
+  <li><strong>Disclaimer of Warranties.</strong> The Website is provided “as is”. Operator and
     its suppliers and licensors hereby disclaim all warranties of any kind,
     express or implied, including, without limitation, the warranties of
     merchantability, fitness for a particular purpose and non-infringement.
@@ -189,7 +213,7 @@ Please read this Agreement carefully before accessing or using the Website. By a
     understand that you download from, or otherwise obtain content or services
     through, the Website at your own discretion and risk.
   </li>
-  <li>Limitation of Liability. In no event will Operator, or its suppliers or
+  <li><strong>Limitation of Liability.</strong> In no event will Operator, or its suppliers or
     licensors, be liable with respect to any subject matter of this agreement
     under any contract, negligence, strict liability or other legal or
     equitable theory for: (i) any special, incidental or consequential damages;
@@ -201,7 +225,7 @@ Please read this Agreement carefully before accessing or using the Website. By a
     reasonable control. The foregoing shall not apply to the extent prohibited
     by applicable law.
   </li>
-  <li>General Representation and Warranty. You represent and warrant that (i)
+  <li><strong>General Representation and Warranty.</strong> You represent and warrant that (i)
     your use of the Website will be in strict accordance with the Operator
     Privacy Policy, with this Agreement and with all applicable laws and
     regulations (including without limitation any local laws or regulations in
@@ -212,13 +236,13 @@ Please read this Agreement carefully before accessing or using the Website. By a
     infringe or misappropriate the intellectual property rights of any third
     party.
   </li>
-  <li>Indemnification. You agree to indemnify and hold harmless Operator, its
+  <li><strong>Indemnification.</strong> You agree to indemnify and hold harmless Operator, its
     contractors, and its licensors, and their respective directors, officers,
     employees and agents from and against any and all claims and expenses,
     including attorneys’ fees, arising out of your use of the Website,
     including but not limited to out of your violation this Agreement.
   </li>
-  <li>Miscellaneous. This Agreement constitutes the entire agreement between
+  <li><strong>Miscellaneous.</strong> This Agreement constitutes the entire agreement between
     Operator and you concerning the subject matter hereof, and they may only
     be modified by a written amendment signed by an authorized executive of
     Operator, or by the posting by Operator of a revised version. If any part
@@ -241,3 +265,5 @@ ShareAlike 3.0 License. Modifications to remove reference to "VIP services",
 rename "blog" to "notice stream", remove the choice-of-venue clause, and add
 variables specific to instances of this software made by Control Yourself, Inc.
 and made available under the terms of the same license.
+
+{% endblock -%}
diff --git a/mediagoblin/templates/mediagoblin/moderation/user.html b/mediagoblin/templates/mediagoblin/moderation/user.html
index 1be9dd80..17a08603 100644
--- a/mediagoblin/templates/mediagoblin/moderation/user.html
+++ b/mediagoblin/templates/mediagoblin/moderation/user.html
@@ -37,7 +37,7 @@
   {% if not user %}
     <p>{% trans %}Sorry, no such user found.{% endtrans %}</p>
   {# User exists, but needs verification #}
-  {% elif user.status == "needs_email_verification" %}
+  {% elif not user.has_privilege('active') %}
     <div class="form_box">
     <h1>{% trans %}Email verification needed{% endtrans %}</h1>
     <p>
diff --git a/mediagoblin/templates/mediagoblin/user_pages/collection_list.html b/mediagoblin/templates/mediagoblin/user_pages/collection_list.html
index 8ac0b988..4b449c76 100644
--- a/mediagoblin/templates/mediagoblin/user_pages/collection_list.html
+++ b/mediagoblin/templates/mediagoblin/user_pages/collection_list.html
@@ -34,7 +34,7 @@
   </h1>
 
   {% if request.user %}
-    {% if request.user.status == 'active' %}
+    {% if request.user.has_privilege('active') %}
       <p>
         <a href="{{ request.urlgen('mediagoblin.submit.collection',
                                             user=user.username) }}">
diff --git a/mediagoblin/templates/mediagoblin/user_pages/user.html b/mediagoblin/templates/mediagoblin/user_pages/user.html
index de92fb5e..d4ce764f 100644
--- a/mediagoblin/templates/mediagoblin/user_pages/user.html
+++ b/mediagoblin/templates/mediagoblin/user_pages/user.html
@@ -43,7 +43,7 @@
     <p>{% trans %}Sorry, no such user found.{% endtrans %}</p>
 
   {# User exists, but needs verification #}
-  {% elif user.status == "needs_email_verification" %}
+  {% elif not user.status.has_privilege('active') %}
     {% if user == request.user %}
       {# this should only be visible when you are this user #}
       <div class="form_box">
diff --git a/mediagoblin/tests/test_auth.py b/mediagoblin/tests/test_auth.py
index edbd27ee..63c12682 100644
--- a/mediagoblin/tests/test_auth.py
+++ b/mediagoblin/tests/test_auth.py
@@ -96,8 +96,6 @@ def test_register_views(test_app):
     new_user = mg_globals.database.User.query.filter_by(
         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
 
@@ -137,8 +135,6 @@ def test_register_views(test_app):
     new_user = mg_globals.database.User.query.filter_by(
         username=u'angrygirl').first()
     assert new_user
-    assert new_user.status == u'needs_email_verification'
-    assert new_user.email_verified == False
 
     ## Verify the email activation works
     template.clear_test_template_context()
@@ -151,8 +147,6 @@ def test_register_views(test_app):
     new_user = mg_globals.database.User.query.filter_by(
         username=u'angrygirl').first()
     assert new_user
-    assert new_user.status == u'active'
-    assert new_user.email_verified == True
 
     # Uniqueness checks
     # -----------------
diff --git a/mediagoblin/tests/test_persona.py b/mediagoblin/tests/test_persona.py
index 919877c9..3e9bf22f 100644
--- a/mediagoblin/tests/test_persona.py
+++ b/mediagoblin/tests/test_persona.py
@@ -112,8 +112,6 @@ class TestPersonaPlugin(object):
             # Get user and detach from session
             test_user = mg_globals.database.User.query.filter_by(
                 username=u'chris').first()
-            test_user.email_verified = True
-            test_user.status = u'active'
             test_user.save()
             test_user = mg_globals.database.User.query.filter_by(
                 username=u'chris').first()
diff --git a/mediagoblin/user_pages/views.py b/mediagoblin/user_pages/views.py
index 931b740c..4f555884 100644
--- a/mediagoblin/user_pages/views.py
+++ b/mediagoblin/user_pages/views.py
@@ -57,7 +57,7 @@ def user_home(request, page):
     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',
@@ -474,9 +474,8 @@ def atom_feed(request):
     generates the atom feed with the newest images
     """
     user = User.query.filter_by(
-        username = request.matchdict['user'],
-        status = u'active').first()
-    if not user:
+        username = request.matchdict['user']).first()
+    if not user or not user.has_privilege(u'active'):
         return render_404(request)
 
     cursor = MediaEntry.query.filter_by(
@@ -537,9 +536,8 @@ def collection_atom_feed(request):
     generates the atom feed with the newest images from a collection
     """
     user = User.query.filter_by(
-        username = request.matchdict['user'],
-        status = u'active').first()
-    if not user:
+        username = request.matchdict['user']).first()
+    if not user or not user.has_privilege(u'active'):
         return render_404(request)
 
     collection = Collection.query.filter_by(