merged suff, commented out some of my own
authorNick Retallack <nickretallack@gmail.com>
Mon, 7 Dec 2009 02:05:00 +0000 (18:05 -0800)
committerNick Retallack <nickretallack@gmail.com>
Mon, 7 Dec 2009 02:05:00 +0000 (18:05 -0800)
21 files changed:
floof/config/routing.py
floof/controllers/account.py
floof/controllers/art.py
floof/controllers/gallery.py
floof/controllers/relation.py
floof/controllers/search.py
floof/controllers/tag.py
floof/controllers/user_settings.py
floof/controllers/users.py
floof/lib/search.py [deleted file]
floof/lib/tags.py [new file with mode: 0644]
floof/model/art.py
floof/model/relations.py [deleted file]
floof/model/search.py
floof/model/tags.py
floof/model/users.py
floof/public/layout.css
floof/templates/art/new.mako
floof/templates/art/show.mako
floof/templates/users/view.mako
floof/websetup.py

index f6906f4..7a0227a 100644 (file)
@@ -22,8 +22,8 @@ def make_map():
     require_POST = dict(conditions={'method': ['POST']})
 
     # get rid of trailing slashes
-    map.redirect('/*(url)/', '/{url}',
-                 _redirect_code='301 Moved Permanently')
+    map.redirect('/*(url)/', '/{url}',
+                 _redirect_code='301 Moved Permanently')
 
 
     # The ErrorController route (handles 404/500 error pages); it should
index 2c94736..2073d44 100644 (file)
@@ -97,6 +97,7 @@ class AccountController(BaseController):
 
         c.identity_url = session['register:identity_url']
         c.nickname = session.get('register:nickname', None)
+        # XXX hey, uh.  warn if this name is taken already.
 
         return render('/account/register.mako')
 
@@ -106,23 +107,27 @@ class AccountController(BaseController):
         identity_url = session['register:identity_url']
         username = request.params.get('username', None)
 
-        # XXX how do we return errors in some useful way?
-
         if not username:
-            return 'Please enter a username.'
+            h.flash(u'Please enter a username.')
+            return self.register()
 
         if User.query.filter_by(name=username).count():
-            return 'That username is taken.'
+            h.flash(u'This username is already taken.')
+            return self.register()
+
+        if not User.is_valid_name(username):
+            h.flash(u'This username is not valid.')
+            return self.register()
 
         # Create db records
-        user = User(name=username)
+        user = User(name=username, display_name=username)
         user.identity_urls.append(IdentityURL(url=identity_url))
         elixir.session.commit()
 
         # Log in
         session['user_id'] = user.id
         session.save()
+        h.flash(u'You are now logged in.')
 
-        # XXX how do we do success messages in some useful way?
         # XXX send me where I came from
         redirect('/')
index 9a2f1ba..0b89d98 100644 (file)
@@ -1,15 +1,15 @@
 import logging
 
-from pylons import config, request, response, session, tmpl_context as c, h
+from pylons import config, request, response, session, tmpl_context as c
 from pylons.controllers.util import abort, redirect
 from pylons import url
 from floof.lib.base import BaseController, render
 
 log = logging.getLogger(__name__)
 
-from floof.lib import file_storage as storage
-from floof.model.users import User
-from floof.model import Art, Rating, UserRelation
+from floof.lib import file_storage as storage, helpers as h
+from floof.model import Art, Rating, ArtUser
+from floof.model.art import ArtUserType
 from floof.model.comments import Discussion
 from floof.model.users import User, UserRelationship
 
@@ -18,7 +18,6 @@ import elixir
 import os.path
 import PIL
 import PIL.Image
-from sqlalchemy import func
 from sqlalchemy.exceptions import IntegrityError
 from sqlalchemy.orm.exc import NoResultFound
 from wtforms.validators import ValidationError
@@ -36,38 +35,6 @@ class ArtUploadForm(Form):
         if field.data == u'':
             raise ValidationError('File is required')
     
-    # Also make this into a general User List field validator
-    """ PLEASE NOTE!  I just realized that I need to have a __str__ method on User
-    to get it to write the usernames back in the form when it redisplays them, since
-    this validator turns them into user objects instead.  This fact actually sounds dangerous
-    to me in the future, since it means I proably shouldn't be changing the data input
-    by the user right here in the validator, or the user will see the post-mangled data instead
-    of what they actually typed.  Hm.
-    
-    One solution to this could be to only look up the users after normal validation is over, 
-    and then manually add validation errors to the form if that fails.  But I think that kind of
-    sucks.  Perhaps the ideology in Formish, where they keep Validation and Conversion as
-    separate tasks, is a better way of doing it?  That way there is less risk of changing the user's
-    input -- you do that at the conversiot stage -- yet it is still encapsulated in the form workflow.
-    Hm.  But that means I'd have to query for the users in the validation step and throw them away,
-    or something equally stupid.  Guess there's no perfect solution here, but I thought it was
-    worth discussing.
-    
-    Btw, this is meant to be used by a field with multi user autocompletion on it (like on stackoverflow tags),
-    so the user should never actually submit anything invalid unless they disable javascript and force it.
-    """
-    def validate_by(self, field):
-        if not field.data:
-            raise ValidationError("Needs at least one creator")
-        user_names = field.data.split()
-        users = []
-        # TODO: Could totally do a filter__in here instead of picking them out individually
-        for user_name in user_names:
-            user = User.get_by(name=user_name)
-            if not user:
-                raise ValidationError("Couldn't find user %s" % user_name)
-            users.append(user)
-        field.data = users
 
 class ArtController(BaseController):
     def __before__(self, id=None):
@@ -128,11 +95,17 @@ class ArtController(BaseController):
         )
         c.art.discussion = Discussion(count=0)
 
-        if c.form.by_me and c.user not in c.form.by.data:
-            UserRelation(user=c.user, creator=c.user, kind="by", art=c.art)
-
-        for artist in c.form.by.data:
-            UserRelation(user=artist, creator=c.user, kind="by", art=c.art)
+        # <<<<<<< HEAD
+        #         if c.form.by_me and c.user not in c.form.by.data:
+        #             UserRelation(user=c.user, creator=c.user, kind="by", art=c.art)
+        # 
+        #         for artist in c.form.by.data:
+        #             UserRelation(user=artist, creator=c.user, kind="by", art=c.art)
+        # =======
+        # For the moment, cheerfully assume that people are uploading their own
+        # art
+        ArtUser(art=c.art, user=c.user, type=ArtUserType.BY)
+        # >>>>>>> origin/master
 
 
         try:
@@ -174,8 +147,7 @@ class ArtController(BaseController):
     def watchstream(self, name):
         """Watchstream for a certain user."""
         try:
-            c.watching_user = User.query.filter(func.lower(User.name) == name) \
-                                  .one()
+            c.watching_user = User.get_by(name=name)
         except NoResultFound:
             abort(404)
 
index e923e30..4a4ea5b 100644 (file)
@@ -1,9 +1,10 @@
 import logging
 
-from pylons import request, response, session, tmpl_context as c, h
+from pylons import request, response, session, tmpl_context as c
 from pylons.controllers.util import abort, redirect
 from pylons import url
 
+from floof.lib import helpers as h
 from floof.lib.base import BaseController, render
 
 log = logging.getLogger(__name__)
index 48e7a46..719fe2b 100644 (file)
@@ -1,8 +1,9 @@
 import logging
 
-from pylons import request, response, session, tmpl_context as c, h, url
+from pylons import request, response, session, tmpl_context as c, url
 from pylons.controllers.util import abort, redirect
 
+from floof.lib import helpers as h
 from floof.lib.base import BaseController, render
 
 log = logging.getLogger(__name__)
index f87ddae..8617966 100644 (file)
@@ -1,11 +1,12 @@
 import logging
 
-from pylons import request, response, session, tmpl_context as c, h
+from pylons import request, response, session, tmpl_context as c
 from pylons.controllers.util import abort, redirect
 from pylons import url
 
+from floof.lib import helpers as h
 from floof.lib.base import BaseController, render
-from floof.lib.search import do_search
+from floof.lib.tags import parse
 
 log = logging.getLogger(__name__)
 
@@ -20,7 +21,7 @@ class SearchController(BaseController):
             return self.save()
 
         c.query = request.params.get('query', '')
-        c.artwork = do_search(c.query)
+        c.artwork = parse(c.query).all()
         return render('/index.mako')
 
     # TODO: login required
index e5ce85b..5d13789 100644 (file)
@@ -1,16 +1,18 @@
 import logging
+import re
 
-from pylons import request, response, session, tmpl_context as c, h
+from pylons import request, response, session, tmpl_context as c, url
 from pylons.controllers.util import abort, redirect
 
+import elixir
+from floof.model import Art, ArtUser, ArtUserType, Tag, TagText, User
+from floof.lib import helpers as h
 from floof.lib.base import BaseController, render
-from pylons import url
+from floof.lib.tags import add_tags
+from floof.lib.dbhelpers import find_or_create
 
 log = logging.getLogger(__name__)
 
-import elixir
-from floof.model import Art, Tag
-
 class TagController(BaseController):
 
     # TODO: login required
@@ -23,7 +25,9 @@ class TagController(BaseController):
     # TODO: login required
     def create(self, art_id):
         c.art = h.get_object_or_404(Art, id=art_id)
-        c.art.add_tags(request.params.get("tags",""), c.user)
-        elixir.session.commit()
-        redirect(url('show_art', id=c.art.id))
 
+        tag_string = request.params.get('tags', '')
+        add_tags(c.art, tag_string, c.user)
+
+        # Add or remove tags
+        redirect(url('show_art', id=c.art.id))
index 8d8adf0..de46e37 100644 (file)
@@ -3,7 +3,6 @@ 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
@@ -22,7 +21,7 @@ class UserSettingsController(BaseController):
         `type`, and `add_remove` as parameters.
         """
         try:
-            user = User.query.filter(func.lower(User.name) == name).one()
+            user = User.get_by(name=name)
         except NoResultFound:
             abort(404)
 
index 1a79c15..7c24c0f 100644 (file)
@@ -2,7 +2,6 @@ import logging
 
 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
 
 from floof.lib.base import BaseController, render
@@ -24,8 +23,7 @@ class UsersController(BaseController):
         """Userpage."""
 
         try:
-            c.this_user = User.query.filter(func.lower(User.name) == name) \
-                              .one()
+            c.this_user = User.get_by(name=name)
         except NoResultFound:
             abort(404)
 
diff --git a/floof/lib/search.py b/floof/lib/search.py
deleted file mode 100644 (file)
index 37d762d..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-from floof.model import Art, Tag, TagText
-
-def do_search(query):
-    tags = query.split()
-
-    tagtexts = TagText.query.filter(TagText.text.in_(tags))
-    tagtext_ids = [_.id for _ in tagtexts]
-
-    # Fetch art that has all the tags
-    artwork = Art.query.join(Tag) \
-                   .filter(Tag.tagtext_id.in_(tagtext_ids)) \
-                   .all()
-    return artwork
-
-
-
-
-
-
-# unfinished stuff
-def parse(query):
-    words = query.split()
-
-    tags = []
-    for word in words:
-        components = word.split(':')
-        if len(components) == 1:
-            # tags are plain.
-            tags.append(word)
-        elif components[0] == "rating":
-            if components[1].isnumeric():
-                score = int(components[1])
-            else:
-                score = Rating.reverse_options.get(components[1])
-
-            if -1 <= score <= 3:
-                pass
-                # TODO: Find stuff that has this rating
-                # Rating.query.filter(Rating.s)
-
-    tagtexts = TagText.query.filter(TagText.text.in_(tags))
-    tagtext_ids = map(lambda x:x.id, tagtexts)
-
diff --git a/floof/lib/tags.py b/floof/lib/tags.py
new file mode 100644 (file)
index 0000000..6186048
--- /dev/null
@@ -0,0 +1,128 @@
+from floof.model import Art, ArtUser, ArtUserType, Tag, TagText, User
+
+from dbhelpers import find_or_create
+import re
+def parse(search_string):
+    """Parses a search query, and returns a query object on Art.
+
+    Queries can contain:
+    - Regular tags: foo
+    - User relations: by:kalu, of:eevee, for:ootachi
+
+    Later:
+    - Negative versions of anything above: -by:eevee, -dongs
+    """
+
+    # XXX doesn't do negative querying yet.
+    # XXX could use some sane limits.
+
+    # We'll be building this as we go.
+    q = Art.query
+
+    terms = search_string.split()
+    for tag in terms:
+        if ':' in tag:
+            # This is a special tag; at the moment, by/for/of to indicate
+            # related users
+            prefix, tag = tag.split(':', 1)
+
+            # XXX what to do if this fails?  abort?  return empty query?
+            target_user = User.get_by(name=tag)
+
+            if prefix == 'by':
+                rel = ArtUserType.BY
+            elif prefix == 'for':
+                rel = ArtUserType.FOR
+            elif prefix == 'of':
+                rel = ArtUserType.OF
+            else:
+                # Bogus tag.  Not sure what to do here, so for the moment,
+                # ignore it
+                continue
+
+            # Inner join to the ArtUser table
+            q = q.join(ArtUser, aliased=True) \
+                 .filter(ArtUser.user == target_user) \
+                 .filter(ArtUser.type == rel)
+
+        else:
+            # Regular ol' tag
+            q = q.join(Tag, TagText, aliased=True) \
+                 .filter(TagText.text == tag)
+
+    return q
+
+def add_tags(art, tag_string, user):
+    """Takes a string that looks like a tag query, and effectively modifies the
+    art's tags to match it.
+    """
+
+    # XXX what to do with invalid tags?  just return them and let caller fix?
+    bad_tags = []
+    for tag_text in tag_string.split():
+        original_tag_text = tag_text
+        tag_text = tag_text.lower()
+
+        # Adding or removing a tag?
+        if tag_text[0] == '-':
+            add = False
+            tag_text = tag_text[1:]
+        else:
+            # Allow "+foo" to mean "add foo"
+            if tag_text[0] == '+':
+                tag_text = tag_text[1:]
+            add = True
+
+        # Check for special namespaces
+        prefix = None
+        if ':' in tag_text:
+            prefix, tag_text = tag_text.split(':', 1)
+            if prefix not in ['by', 'for', 'of']:
+                # This is bogus.  Skip it.
+                bad_tags.append(original_tag_text)
+                continue
+
+            if prefix == 'by':
+                # XXX this needs supporting.  silently ignore for now
+                continue
+
+        # Must be 3-50 alphanumeric characters
+        if not re.match('^[a-z0-9]{3,50}$', tag_text):
+            bad_tags.append(original_tag_text)
+            continue
+
+        # Do work!
+        if prefix:
+            target_user = User.get_by(name=tag_text)
+
+            # Special tag; at the moment, just a relationship
+            if prefix == 'by':
+                rel = ArtUserType.BY
+            elif prefix == 'for':
+                rel = ArtUserType.FOR
+            elif prefix == 'of':
+                rel = ArtUserType.OF
+
+            user_assoc_data = dict(art=art, user=target_user, type=rel)
+            if add:
+                find_or_create(ArtUser, **user_assoc_data)
+
+            else:
+                # XXX this will die for nonassociations
+                user_assoc = ArtUser.get_by(art=art, **user_assoc_data)
+                user_assoc.delete()
+
+        else:
+            # Regular tag
+            if add:
+                tag = find_or_create(TagText, text=tag_text)
+                find_or_create(Tag, art=art, tagger=user, tagtext=tag)
+
+            else:
+                tag = TagText.get_by(text=tag_text)
+                if tag:
+                    # XXX this will die
+                    tag_assoc = Tag.get_by(art=art, tagger=user, tagtext=tag)
+                    tag_assoc.delete()
+
+    elixir.session.commit()
index 8989e8f..0452e32 100644 (file)
@@ -4,7 +4,6 @@
 #   Copyright (c) 2009 Scribblr
 #
 
-# from elixir import Entity, Field, Integer, Unicode
 from elixir import *
 import elixir
 
@@ -12,12 +11,9 @@ from pylons import config
 
 from floof.lib.file_storage import get_path, save_file
 from floof.lib.dbhelpers import find_or_create, update_or_create
-import floof.model.comments
+from floof.model.users import User
 
 
-# Note: Art is the most important class.  To keep its size down, and to better organize the source code,
-# other modules will mix into it automatically by adding to its __bases__.
-
 class Art(Entity):
     title = Field(Unicode(120))
     original_filename = Field(Unicode(120))
@@ -28,8 +24,35 @@ class Art(Entity):
     tags = OneToMany('Tag')
     discussion = ManyToOne('Discussion')
 
-    user_relations = OneToMany('UserRelation')
+    art_users = OneToMany('ArtUser')
 
     @property
     def file_path(self):
         return get_path("art", self.hash)
+
+    # Associated users
+    # XXX ok these could stand to do the filtering sql-side
+    @property
+    def artists(self):
+        return (au.user for au in self.art_users if au.type == ArtUserType.BY)
+
+    @property
+    def recipients(self):
+        return (au.user for au in self.art_users if au.type == ArtUserType.FOR)
+
+    @property
+    def participants(self):
+        return (au.user for au in self.art_users if au.type == ArtUserType.OF)
+
+class ArtUserType(object):
+    BY = 1
+    FOR = 2
+    OF = 3
+
+class ArtUser(Entity):
+    art = ManyToOne('Art', required=True)
+    user = ManyToOne('User', required=True)
+    type = Field(Integer, required=True)  # ArtUserType
+
+    # TODO: admin log ought to remember who confirmed the relation.
+    #       (tag history will remember who proposed it)
diff --git a/floof/model/relations.py b/floof/model/relations.py
deleted file mode 100644 (file)
index a6fe020..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-from elixir import *
-from art import Art
-
-
-class UserRelation(Entity):
-    user = ManyToOne("User")
-    art = ManyToOne("Art")
-    kind = Field(String) # by for of
-    creator = ManyToOne("User")
-    confirmed_by_related_user = Field(Boolean)
-
-    # it is useful to record which authority figure on a given artwork
-    # confirmed the validity of this relation.
-    confirmed_by_authority = ManyToOne("User")
-    
-    def __init__(self, **kwargs):
-        super(UserRelation, self).__init__(**kwargs)
-        assert self.user and self.art and self.kind and self.creator
-        
-        if self.creator == self.user:
-            self.confirmed_by_related_user = True
-        # TODO: implement authorities
-        # if self.creator in self.art.authorities
-        #     self.confirmed_by_authority = self.creator
-
-    def __unicode__(self):
-        return "%s: %s" % (self.kind, self.related_user)
-
-
-class RelationMixin(object):
-    def add_relation(creator, kind, user):
-        return UserRelation(art=self, creator=creator, kind=kind, user=user)
-
-Art.__bases__ += (RelationMixin,)
index fb86f35..37f2d5f 100644 (file)
@@ -10,13 +10,6 @@ class SavedSearch(Entity):
     def __unicode__(self):
         return self.string
 
-    @property
-    def results(self):
-        # This caused some cyclic dependencies when I tried importing it
-        # at the module level.  I wonder why that is...
-        from floof.lib.search import do_search
-        return do_search(self.string)
-
 
 
 class GalleryWidget(Entity):
index 1a41278..b0a7072 100644 (file)
@@ -1,7 +1,5 @@
 from elixir import *
 from art import Art
-from floof.lib.dbhelpers import find_or_create, update_or_create
-
 
 class Tag(Entity):
     # look into how ondelete works.  This just sets a database property.
@@ -14,33 +12,9 @@ class Tag(Entity):
             return "(broken)"
         return unicode(self.tagtext)
 
-
 class TagText(Entity):
     text = Field(Unicode(50)) # gotta enforce this somehow
     tags = OneToMany('Tag')
 
     def __unicode__(self):
         return self.text
-
-
-class TagMixin(object):
-    def add_tags(self, tags, user):
-        for text in tags.split():
-            if text[0] == '-':
-                # Nega-tags
-                tagtext = TagText.get_by(text=text[1:])
-                if tagtext:
-                    tag = Tag.get_by(art=self, tagger=user, tagtext=tagtext)
-                    if tag:
-                        elixir.session.delete(tag)
-
-            else:
-                if len(text) > 50:
-                    raise "Long Tag!" # can we handle this more gracefully?
-                # sqlite seems happy to store strings much longer than the supplied limit...
-
-                # elixir should really have its own find_or_create.
-                tagtext = find_or_create(TagText, text=text)
-                tag     = find_or_create(Tag, art=self, tagger=user, tagtext=tagtext)
-                
-Art.__bases__ += (TagMixin, )
index 7601cf5..a6f3a6d 100644 (file)
@@ -4,12 +4,15 @@
 #   Copyright (c) 2009 Scribblr
 #
 
-# from elixir import Entity, Field, Unicode, belongs_to, has_many
+import re
+
 from elixir import *
+
 from search import GalleryWidget
 
 class User(Entity):
-    name = Field(Unicode(20))
+    name = Field(Unicode(20), unique=True)
+    display_name = Field(Unicode(20))
     uploads = OneToMany('Art')
     has_many('identity_urls', of_kind='IdentityURL')
     searches = OneToMany('SavedSearch')
@@ -19,10 +22,22 @@ class User(Entity):
     relationships = OneToMany('UserRelationship', inverse='user')
     target_of_relationships = OneToMany('UserRelationship', inverse='target_user')
 
+    @classmethod
+    def is_valid_name(cls, name):
+        """Returns True iff the name is a valid username.
+
+        Only lowercase letters, numbers, and hyphens are allowed.
+
+        Names must also be at least one character long, but no more than 20,
+        and cannot start or end with a hyphen.
+        """
+        return re.match('^[-a-z0-9]{1,20}$', name) \
+               and name[0] != '-' and name[-1] != '-'
+
 
     def __unicode__(self):
         return self.name
-    
+
     def __str__(self):
         return self.name
 
index bc7056e..7a7308b 100644 (file)
@@ -16,6 +16,7 @@ body { font-family: sans-serif; font-size: 12px; }
 
 /*** Common bits and pieces ***/
 h1 { margin: 0.5em 0 0.25em; font-size: 2em; border-bottom: 1px solid #404040; text-shadow: #a0a0a0 1px 1px 1px; }
+h2 { margin: 0.5em 0 0.25em; font-size: 1.5em; border-bottom: 1px dotted #606060; text-shadow: #a0a0a0 1px 1px 1px; }
 
 a { color: #647cc4; font-weight: bold; text-decoration: none; pointer: cursor; }
 a:visited { color: #48598e; }
index 684c004..0adc916 100644 (file)
@@ -1,22 +1,20 @@
 <%inherit file="/base.mako" />
 
 <h1>Add New Art</h1>
-<p>Now: Upload a file.  Later: Supply a link?  Not exclusive to uploading.</p>
-
-## Todo: write some macros to make outputting form fields easier.
-
 ${h.form(h.url('create_art'), multipart=True)}
 
-<div>
-    ${normal_field(c.form.by)}
-    ${checkbox_field(c.form.by_me)}
-</div>
-
-<div>${normal_field(c.form.file)}</div>
-
+##<<<<<<< HEAD
+##<div>
+##    ${normal_field(c.form.by)}
+##    ${checkbox_field(c.form.by_me)}
+##</div>
+##
+##<div>${normal_field(c.form.file)}</div>
+##=======
+## Todo: write some macros to make outputting form fields easier.
+${normal_field(c.form.file)}
+##>>>>>>> origin/master
 
-##Artist: ${h.text('artist')}
-##${h.file('file')}
 ${h.submit(None, 'Upload!')}
 ${h.end_form()}
 
index a38806d..9356447 100644 (file)
@@ -34,17 +34,15 @@ ${h.end_form()}
 
 <h2>Relations</h2>
 <ul>
-% for relation in c.art.user_relations:
-<li>${relation.kind}: ${relation.user}
+% for label, relations in (('Artist', c.art.artists), \
+                               ('Recipient', c.art.recipients), \
+                               ('Participant', c.art.participants)):
+% for user in relations:
+<li>${label}: ${user.name}
+% endfor
 % endfor
 </ul>
 
-<h2>Add Relations</h2>
-${h.form (h.url("create_relation", kind="by", art_id=c.art.id))}
-By: ${h.text('username')}
-${h.submit('add','Add')}
-${h.end_form()}
-
 <img class="full" src="${h.storage_url('art/medium', c.art.hash)}">
 
 ${comments.comment_block(c.art.discussion.comments)}
index 5b7e12d..eb24b2d 100644 (file)
@@ -21,7 +21,8 @@ ${h.form(url(controller='user_settings', action='rel_toggle', name=c.user.name.l
     % endif
 % endif
 
+<%! from floof.lib.search import parse %>
 % for gallery in c.this_user.primary_page.galleries:
 <h2>${gallery.string}</h2>
-${macros.thumbs(gallery.search.results)}
+${macros.thumbs(parse(gallery.tags.string))}
 % endfor
index 815832d..a4e935f 100644 (file)
@@ -23,7 +23,7 @@ def setup_app(command, conf, vars):
     # Users
     from floof.model.users import IdentityURL, User
     identity_url = IdentityURL(url=u'http://eevee.livejournal.com/')
-    user = User(name=u'Eevee')
+    user = User(name=u'eevee')
     user.identity_urls.append(identity_url)
 
     model.Session.commit()