Added a watch/unwatch button to user pages.
authorEevee <git@veekun.com>
Mon, 16 Nov 2009 05:21:07 +0000 (21:21 -0800)
committerEevee <git@veekun.com>
Mon, 16 Nov 2009 05:21:07 +0000 (21:21 -0800)
floof/config/routing.py
floof/controllers/user_settings.py [new file with mode: 0644]
floof/controllers/users.py
floof/lib/base.py
floof/model/forms.py [new file with mode: 0644]
floof/templates/base.mako
floof/templates/users/view.mako
floof/tests/functional/test_user_settings.py [new file with mode: 0644]

index 02e597f..44462f8 100644 (file)
@@ -42,9 +42,13 @@ def make_map():
         sub.connect('register',         '/account/register',        action='register')
         sub.connect('register_finish',  '/account/register_finish', action='register_finish', **require_POST)
 
-    # with map.submapper()
-    map.connect('/users', controller='users', action='list')
-    map.connect('user_page', '/users/{name}', controller='users', action='view')
+    # Specific user stuff
+    with map.submapper(controller='users') as sub:
+        sub.connect(                '/users',               action='list')
+        sub.connect('user_page',    '/users/{name}',        action='view')
+    with map.submapper(controller='user_settings') as sub:
+        sub.connect(                '/users/{name}/settings/relationships/toggle',
+                                                            action='rel_toggle', **require_POST)
 
     # Comments
     with map.submapper(controller='comments') as sub:
diff --git a/floof/controllers/user_settings.py b/floof/controllers/user_settings.py
new file mode 100644 (file)
index 0000000..8d8adf0
--- /dev/null
@@ -0,0 +1,79 @@
+import logging
+
+import elixir
+from pylons import request, response, session, tmpl_context as c
+from pylons.controllers.util import abort, redirect_to
+from sqlalchemy import func
+from sqlalchemy.orm.exc import NoResultFound
+
+import floof.lib.helpers as h
+from floof.lib.base import BaseController, render
+from floof.model.users import User, UserRelationship
+from floof.model.forms import UserRelationshipToggleForm
+
+log = logging.getLogger(__name__)
+
+class UserSettingsController(BaseController):
+
+    def rel_toggle(self, name):
+        """Adds or removes a single relationship with a single user.
+        
+        Expects to be called as a POST with `target_user_id`,
+        `type`, and `add_remove` as parameters.
+        """
+        try:
+            user = User.query.filter(func.lower(User.name) == name).one()
+        except NoResultFound:
+            abort(404)
+
+        schema = UserRelationshipToggleForm()
+        try:
+            form_result = schema.to_python(request.params)
+        except BaseException, e:
+            # The data for this form is generated entirely by the app.  If
+            # there are errors, the user has been dicking around.
+            abort(400)
+
+        # Grab any existing relationship row
+        rel = None
+        try:
+            rel = UserRelationship.query.filter_by(
+                user_id=user.id,
+                target_user_id=form_result['target_user'].id,
+                type=form_result['type'],
+            ).one()
+        except:
+            pass
+
+        # XXX shouldn't include "watching"...
+        target_name = form_result['target_user'].name
+        if form_result['add_remove'] == u'add':
+            # Adding
+            if rel:
+                # Already exists!  Nothing to do.
+                h.flash("You're already watching {name}..."
+                        .format(name=target_name))
+            else:
+                # Add it
+                UserRelationship(
+                    user_id=user.id,
+                    target_user_id=form_result['target_user'].id,
+                    type=form_result['type'],
+                )
+                h.flash("Now watching {name}."
+                        .format(name=target_name))
+        else:
+            # Removing
+            if rel:
+                # Toss it
+                rel.delete()
+                h.flash("No longer watching {name}.  How cruel!."
+                        .format(name=target_name))
+            else:
+                # Already gone!  Nothing to do.
+                h.flash("You're not watching {name}..."
+                        .format(name=target_name))
+                
+        elixir.session.commit()
+
+        self.redirect_to_referrer()
index 1e251f0..1a79c15 100644 (file)
@@ -6,7 +6,7 @@ from sqlalchemy import func
 from sqlalchemy.orm.exc import NoResultFound
 
 from floof.lib.base import BaseController, render
-from floof.model.users import User
+from floof.model.users import User, UserRelationship
 
 log = logging.getLogger(__name__)
 
@@ -29,4 +29,11 @@ class UsersController(BaseController):
         except NoResultFound:
             abort(404)
 
+        rels = UserRelationship.query.filter_by(
+            user_id=c.user.id,
+            target_user_id=c.this_user.id,
+        ).all()
+
+        c.relationships = [_.type for _ in rels]
+
         return render('/users/view.mako')
index 169493c..b42d2be 100644 (file)
@@ -3,8 +3,9 @@
 Provides the BaseController class for subclassing.
 """
 from pylons.controllers import WSGIController
+from pylons.controllers.util import abort, redirect
 from pylons.templating import render_mako as render
-from pylons import config, session, tmpl_context as c
+from pylons import config, request, session, tmpl_context as c
 from routes import request_config
 
 from floof import model
@@ -34,3 +35,11 @@ class BaseController(WSGIController):
             return WSGIController.__call__(self, environ, start_response)
         finally:
             model.Session.remove()
+
+
+    def redirect_to_referrer(self):
+        """Performs a redirect_to to wherever we came from.  Used for stuff
+        like logging in.
+        """
+        referrer = request.headers.get('REFERER', '/')
+        redirect(referrer, code=302)
diff --git a/floof/model/forms.py b/floof/model/forms.py
new file mode 100644 (file)
index 0000000..c316c38
--- /dev/null
@@ -0,0 +1,37 @@
+"""FormEncode validators."""
+
+import formencode
+
+from floof.model.users import User, UserRelationshipTypes
+
+class UniqueExistingRow(formencode.FancyValidator):
+    """Given a column object, converts a unique value from that column into the
+    corresponding row object.
+    """
+    def __init__(self, table, column):
+        self.table = table
+        self.column = column
+        super(formencode.FancyValidator, self).__init__()
+
+    def _to_python(self, value, state):
+        try:
+            row = self.table.query.filter(self.column == value).one()
+        except BaseException, e:
+            raise formencode.Invalid(
+                'No unique row.',
+                value, state
+            )
+        return row
+
+### user_settings
+
+class UserRelationshipToggleForm(formencode.Schema):
+    target_user = UniqueExistingRow(User, User.id)
+    type = formencode.compound.Pipe(
+        formencode.validators.Int(),
+        formencode.validators.OneOf(
+            [v for (k, v) in UserRelationshipTypes.__dict__.items()
+                if k[0] != '_']
+        ),
+    )
+    add_remove = formencode.validators.OneOf([u'add', u'remove'])
index 0f3597c..60e8adb 100644 (file)
@@ -17,7 +17,7 @@
 ## | <a href="${h.url_for("/users/"+c.user}">Your Page</a>
 % endif
 
-${h.form(h.url_for('search'), method='GET')}
+${h.form(url('search'), method='GET')}
 ${h.text('query', c.query)}
 ${h.submit('button', 'Search')}
 
index f9a9329..5b7e12d 100644 (file)
@@ -3,7 +3,25 @@
 
 <p>This is the userpage for ${c.this_user.name}.</p>
 
+<%! from floof.model.users import UserRelationshipTypes %>
+% if c.this_user == c.user:
+## Nothing
+<% pass %>\
+% else:
+${h.form(url(controller='user_settings', action='rel_toggle', name=c.user.name.lower()), method='POST')}
+<p>
+    <input type="hidden" name="target_user" value="${c.this_user.id}">
+    <input type="hidden" name="type" value="${UserRelationshipTypes.IS_WATCHING}">
+    % if UserRelationshipTypes.IS_WATCHING in c.relationships:
+    <input type="hidden" name="add_remove" value="remove">
+    <input type="submit" value="Unwatch">
+    % else:
+    <input type="hidden" name="add_remove" value="add">
+    <input type="submit" value="Watch">
+    % endif
+% endif
+
 % for gallery in c.this_user.primary_page.galleries:
 <h2>${gallery.string}</h2>
 ${macros.thumbs(gallery.search.results)}
-% endfor
\ No newline at end of file
+% endfor
diff --git a/floof/tests/functional/test_user_settings.py b/floof/tests/functional/test_user_settings.py
new file mode 100644 (file)
index 0000000..b28f8be
--- /dev/null
@@ -0,0 +1,7 @@
+from floof.tests import *
+
+class TestUserSettingsController(TestController):
+
+    def test_index(self):
+        response = self.app.get(url(controller='user_settings', action='index'))
+        # Test response...