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.base import BaseController, render
-from pylons import url
+from floof.lib.dbhelpers import find_or_create
+from floof.lib import helpers as h
+from sqlalchemy import func
log = logging.getLogger(__name__)
-import elixir
-from floof.model import Art, Tag
-
class TagController(BaseController):
# TODO: login required
# 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)
+
+ tag_string = request.params.get('tags', '')
+
+ # Add or remove tags
+ 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.query.filter(func.lower(User.name) == tag_text) \
+ .one()
+
+ # 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=c.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=c.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=c.art, tagger=c.user, tagtext=tag)
+
+ else:
+ tag = TagText.get_by(text=tag_text)
+ if tag:
+ # XXX this will die
+ tag_assoc = Tag.get_by(art=c.art, tagger=c.user, tagtext=tag)
+ tag_assoc.delete()
+
elixir.session.commit()
redirect(url('show_art', id=c.art.id))
-
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.
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, )