Added User Pages, which you can now display galleries on. Also detected
authorNick Retallack <nickretallack@gmil.com>
Wed, 7 Oct 2009 06:01:20 +0000 (23:01 -0700)
committerNick Retallack <nickretallack@gmil.com>
Wed, 7 Oct 2009 06:01:20 +0000 (23:01 -0700)
duplicate submissions.
Made an absolute mess of routes, but I think I've fixed them all now.
Removed all url.current() calls because they don't seem to work, though
I'm not sure if that's because I added explicit=True or not.  I'm wary
of them anyhow after reading
http://pylonsbook.com/en/1.0/urls-routing-and-dispatch.html#route-memory

13 files changed:
floof/config/routing.py
floof/controllers/account.py
floof/controllers/art.py
floof/controllers/search.py
floof/controllers/tag.py
floof/model/art.py
floof/model/search.py
floof/model/users.py
floof/templates/account/register.mako
floof/templates/art/new.mako
floof/templates/base.mako
floof/templates/macros.mako
floof/templates/users/view.mako

index 7f46a2c..c9b4975 100644 (file)
@@ -31,32 +31,32 @@ def make_map():
     map.connect('/error/{action}', controller='error')
     map.connect('/error/{action}/{id}', controller='error')
 
     map.connect('/error/{action}', controller='error')
     map.connect('/error/{action}/{id}', controller='error')
 
-    map.connect('/', controller='main', action='index')
-
-    # User stuff
-    map.connect('/account/login', controller='account', action='login')
-    map.connect('/account/login_begin', controller='account', action='login_begin', **require_POST)
-    map.connect('/account/login_finish', controller='account', action='login_finish')
-    map.connect('/account/logout', controller='account', action='logout', **require_POST)
-    map.connect('/account/register', controller='account', action='register')
-    map.connect('/account/register_finish', controller='account', action='register_finish', **require_POST)
-
+    map.connect('home', '/', controller='main', action='index')
+
+    # Account stuff
+    with map.submapper(controller="account") as sub:
+        sub.connect('login',            '/account/login',           action='login')
+        sub.connect('login_begin',      '/account/login_begin',     action='login_begin', **require_POST)
+        sub.connect('login_finish',     '/account/login_finish',    action='login_finish')
+        sub.connect('logout',           '/account/logout',          action='logout', **require_POST)
+        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')
 
     map.connect('/users', controller='users', action='list')
     map.connect('user_page', '/users/{name}', controller='users', action='view')
 
-    # Art stuff
-    art = map.resource('art','art', controller="art", member={'rate':'PUT'})
-    # wow, it even works if you name the plural and singular the same thing.
-    # Resources documented here: http://routes.groovie.org/manual.html#restful-services
-    # It seems the first parameter (singular) is only ever used in route names, e.g. url('kitten', id=5).
-    # The second parameter, plural, is used everywhere else by default: in the url, controller name, 
-    # and the route name for the collection.  e.g. url('kittens') -> '/kittens' -> kittens.index().
-    # Since our controllers have singular names, we'll have to override this every time with the 'controller' parameter.
-    # Even singular routes use the plural in urls.  url('kitten', id=5) -> '/kittens/5'.
-    # And it appears that if the singular and plural are the same, either will match, so no harm done.
-    # It does mean, however, that if you have a None id accidentally, url('art', id=None) you'll get the same thing    
-    # as url('art').  I mean, you might have wanted a singular but you got a plural route instead.
-    
+
+    with map.submapper(controller="art") as sub:
+        sub.connect('new_art',      '/art/new',         action="new")
+        sub.connect('create_art',   '/art/create',      action="create")
+        sub.connect('rate_art',     '/art/{id}/rate',   action="rate")
+        sub.connect('show_art',     '/art/{id}',        action="show")
+        
+    with map.submapper(controller='tag') as sub:
+        sub.connect('delete_tag', '/art/{art_id}/tag/{id}')
+        sub.connect('create_tag', '/art/{art_id}/tag')
+
     map.resource('tag','tags', controller="tag",
         parent_resource=dict(member_name='art', collection_name='art'))
         # Yeah, parent resources are specified kinda dumb-ly.  Would be better if you could pass in the
     map.resource('tag','tags', controller="tag",
         parent_resource=dict(member_name='art', collection_name='art'))
         # Yeah, parent resources are specified kinda dumb-ly.  Would be better if you could pass in the
index e4f39f3..7dabaa6 100644 (file)
@@ -5,8 +5,8 @@ from openid.extensions.sreg import SRegRequest, SRegResponse
 from openid.store.filestore import FileOpenIDStore
 from sqlalchemy.orm.exc import NoResultFound
 
 from openid.store.filestore import FileOpenIDStore
 from sqlalchemy.orm.exc import NoResultFound
 
-from pylons import request, response, session, tmpl_context as c, url
-from pylons.controllers.util import abort, redirect_to
+from pylons import request, response, session, tmpl_context as c, url, h
+from pylons.controllers.util import abort, redirect
 from routes import url_for, request_config
 
 from floof.lib.base import BaseController, render
 from routes import url_for, request_config
 
 from floof.lib.base import BaseController, render
@@ -35,7 +35,7 @@ class AccountController(BaseController):
         return_url = url_for(host=host, controller='account', action='login_finish')
         new_url = auth_request.redirectURL(return_to=return_url,
                                            realm=protocol + '://' + host)
         return_url = url_for(host=host, controller='account', action='login_finish')
         new_url = auth_request.redirectURL(return_to=return_url,
                                            realm=protocol + '://' + host)
-        redirect_to(new_url)
+        redirect(new_url)
 
     def login_finish(self):
         """Step two of logging in; the OpenID provider redirects back here."""
 
     def login_finish(self):
         """Step two of logging in; the OpenID provider redirects back here."""
@@ -63,14 +63,14 @@ class AccountController(BaseController):
                 session['register:nickname'] = sreg_res['nickname']
 
             session.save()
                 session['register:nickname'] = sreg_res['nickname']
 
             session.save()
-            redirect_to(url.current(action='register'))
+            redirect(url('register'))
 
         # Remember who's logged in, and we're good to go
         session['user_id'] = user.id
         session.save()
 
         # XXX send me where I came from
 
         # Remember who's logged in, and we're good to go
         session['user_id'] = user.id
         session.save()
 
         # XXX send me where I came from
-        redirect_to('/')
+        redirect('/')
 
     def logout(self):
         """Log user out."""
 
     def logout(self):
         """Log user out."""
@@ -81,7 +81,7 @@ class AccountController(BaseController):
 
         # XXX success message
         # XXX send me where I came from
 
         # XXX success message
         # XXX send me where I came from
-        redirect_to('/')
+        redirect('/')
 
     def register(self):
         """Logging in with an unrecognized identity URL redirects here."""
 
     def register(self):
         """Logging in with an unrecognized identity URL redirects here."""
@@ -116,4 +116,4 @@ class AccountController(BaseController):
 
         # XXX how do we do success messages in some useful way?
         # XXX send me where I came from
 
         # XXX how do we do success messages in some useful way?
         # XXX send me where I came from
-        redirect_to('/')
+        redirect('/')
index 7ca1285..8dfc487 100644 (file)
@@ -10,6 +10,9 @@ log = logging.getLogger(__name__)
 import elixir
 from floof.model.art import Art, Rating
 
 import elixir
 from floof.model.art import Art, Rating
 
+from sqlalchemy.exceptions import IntegrityError
+
+
 class ArtController(BaseController):
     def __before__(self, id=None):
         super(ArtController, self).__before__()
 class ArtController(BaseController):
     def __before__(self, id=None):
         super(ArtController, self).__before__()
@@ -27,9 +30,19 @@ class ArtController(BaseController):
 
     # TODO: login required
     def create(self):
 
     # TODO: login required
     def create(self):
-        Art(uploaded_by=c.user, **request.params)
-        elixir.session.commit()
-        redirect_to(controller="main", action="index")
+        c.art = Art(uploader=c.user, **request.params)
+
+        try:
+            elixir.session.commit()
+            redirect(url('show_art', id=c.art.id))
+        except IntegrityError:
+            # hurr, there must be a better way to do this but I am lazy right now
+            hash = c.art.hash
+            elixir.session.rollback()
+            duplicate_art = Art.get_by(hash=hash)
+            h.flash("We already have that one.")
+            redirect(url('show_art', id=duplicate_art.id))
+
 
     def show(self, id):
         # c.art = h.get_object_or_404(Art, id=id)
 
     def show(self, id):
         # c.art = h.get_object_or_404(Art, id=id)
@@ -56,4 +69,4 @@ class ArtController(BaseController):
         c.art.rate(score, c.user)
         elixir.session.commit()
             
         c.art.rate(score, c.user)
         elixir.session.commit()
             
-        redirect(url('art', id=c.art.id))
+        redirect(url('show_art', id=c.art.id))
index e27e5c8..57ff4d2 100644 (file)
@@ -40,7 +40,7 @@ class SearchController(BaseController):
     # TODO: login required
     def display(self, id):
         c.search = h.get_object_or_404(SavedSearch, id=id)
     # TODO: login required
     def display(self, id):
         c.search = h.get_object_or_404(SavedSearch, id=id)
-        c.gallery = GalleryWidget(search=c.search, displayer=c.user)
+        c.gallery = GalleryWidget(search=c.search, page=c.user.primary_page)
         elixir.session.commit()
         redirect(url(controller="users", action="view", name=c.user.name))
         
         elixir.session.commit()
         redirect(url(controller="users", action="view", name=c.user.name))
         
index ef3c7ce..c00eaf6 100644 (file)
@@ -18,12 +18,12 @@ class TagController(BaseController):
         tag = h.get_object_or_404(Tag, id=id)
         elixir.session.delete(tag)
         elixir.session.commit()
         tag = h.get_object_or_404(Tag, id=id)
         elixir.session.delete(tag)
         elixir.session.commit()
-        redirect(url('art', id=art_id))
+        redirect(url('show_art', id=art_id))
         
     # 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["tags"], c.user)
         elixir.session.commit()
         
     # 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["tags"], c.user)
         elixir.session.commit()
-        redirect(url('art', id=c.art.id))
+        redirect(url('show_art', id=c.art.id))
 
 
index 9ff00c5..b709610 100644 (file)
@@ -16,9 +16,9 @@ from floof.lib.dbhelpers import find_or_create, update_or_create
 class Art(Entity):
     title = Field(Unicode(120))
     original_filename = Field(Unicode(120))
 class Art(Entity):
     title = Field(Unicode(120))
     original_filename = Field(Unicode(120))
-    hash = Field(String)
+    hash = Field(String, unique=True, required=True)
 
 
-    uploader = ManyToOne('User')
+    uploader = ManyToOne('User', required=True)
     tags = OneToMany('Tag')
 
     # def __init__(self, **kwargs):
     tags = OneToMany('Tag')
 
     # def __init__(self, **kwargs):
@@ -33,6 +33,7 @@ class Art(Entity):
 
     def set_file(self, file):
         self.hash = save_file("art", file)
 
     def set_file(self, file):
         self.hash = save_file("art", file)
+        self.original_filename = file.filename
 
     file = property(get_path, set_file)
 
 
     file = property(get_path, set_file)
 
index e812afc..be2bb9e 100644 (file)
@@ -17,9 +17,12 @@ class SavedSearch(Entity):
 
 
 class GalleryWidget(Entity):
 
 
 class GalleryWidget(Entity):
+    page = ManyToOne('UserPage')
     search = ManyToOne(SavedSearch)
     search = ManyToOne(SavedSearch)
-    displayer = ManyToOne('User') # determines whose page should it should show up on
-                                # Could be no-ones, if it's just a template.
+
+    # NOTE: no longer needed now that we have pages, I guess.
+    # displayer = ManyToOne('User') # determines whose page should it should show up on
+    #                             # Could be no-ones, if it's just a template.
     
     # Needs some fields for position on your page
 
     
     # Needs some fields for position on your page
 
@@ -30,13 +33,9 @@ class GalleryWidget(Entity):
     @string.setter
     def string(self, value):
         # TODO: should we delete the possibly orphaned saved search?
     @string.setter
     def string(self, value):
         # TODO: should we delete the possibly orphaned saved search?
-        if not self.displayer:
-            # TODO: may have to refactor this into an init if the key ordering is inconvenienc
-            raise "Oh no!  This gallery needs a displayer to set on the saved search."
-        
-        self.search = SavedSearch(author=self.displayer, string=value)
-        
+        # if not self.displayer:
+        #     # TODO: may have to refactor this into an init if the key ordering is inconvenienc
+        #     raise "Oh no!  This gallery needs a displayer to set on the saved search."
         
         
-# class UserPage(Entity):
-#     owner = ManyToOne('User')
-#     visible = Field(Boolean)
\ No newline at end of file
+        self.search = SavedSearch(author=getattr(self,"author",None), string=value)
+        
\ No newline at end of file
index d4a679a..86c31d8 100644 (file)
@@ -6,18 +6,51 @@
 
 # from elixir import Entity, Field, Unicode, belongs_to, has_many
 from elixir import *
 
 # from elixir import Entity, Field, Unicode, belongs_to, has_many
 from elixir import *
+from search import GalleryWidget
 
 class User(Entity):
     name = Field(Unicode(20))
     uploads = OneToMany('Art')
     has_many('identity_urls', of_kind='IdentityURL')
     searches = OneToMany('SavedSearch')
 
 class User(Entity):
     name = Field(Unicode(20))
     uploads = OneToMany('Art')
     has_many('identity_urls', of_kind='IdentityURL')
     searches = OneToMany('SavedSearch')
-    galleries = OneToMany('GalleryWidget')
+    # galleries = OneToMany('GalleryWidget')
+    pages = OneToMany('UserPage', inverse="owner")
+    primary_page = OneToOne('UserPage', inverse="owner")
+
     
     def __unicode__(self):
         return self.name
 
     
     def __unicode__(self):
         return self.name
 
+    def __init__(self, **kwargs):
+        super(User, self).__init__(**kwargs)
+        
+        
+        
+        # TODO: have this clone a standard starter page
+        self.primary_page = UserPage(owner=self, title="default", visible=True)
+        
+        # a starter gallery, just for fun
+        gallery = GalleryWidget(owner=self, string="awesome")
+        self.primary_page.galleries.append(gallery)
+
+
 class IdentityURL(Entity):
     url = Field(Unicode(255))
     belongs_to('user', of_kind='User')
 
 class IdentityURL(Entity):
     url = Field(Unicode(255))
     belongs_to('user', of_kind='User')
 
+
+
+class UserPage(Entity):
+    """A user can have multiple pages, though by default they only have one visible.
+    This is so that they can keep some nice themed pages lying around for special occasions.
+    Page templates that provide familiar interfaces will also be UserPage records.  Users will
+    see a panel full of them, and they can choose to clone those template pages to their own page list.
+    If more than one is set to visible, there would be tabs.
+    
+     """
+    
+    owner = ManyToOne('User', inverse="pages")
+    title = Field(String)
+    
+    visible = Field(Boolean)
+    galleries = OneToMany('GalleryWidget')
\ No newline at end of file
index 485012c..2de23a4 100644 (file)
@@ -2,7 +2,7 @@
 
 <p>Registering from <strong>${c.identity_url}</strong>.</p>
 
 
 <p>Registering from <strong>${c.identity_url}</strong>.</p>
 
-${h.form(url.current(action='register_finish'), method='POST')}
+${h.form(url('register_finish'), method='POST')}
 <dl class="form">
     <dt>Username</dt>
     <dd>${h.text('username', value=c.username)}</dd>
 <dl class="form">
     <dt>Username</dt>
     <dd>${h.text('username', value=c.username)}</dd>
index e2c00fb..3eda527 100644 (file)
@@ -3,7 +3,7 @@
 <h1>Add New Art</h1>
 <p>Now: Upload a file.  Later: Supply a link?  Not exclusive to uploading.</p>
 
 <h1>Add New Art</h1>
 <p>Now: Upload a file.  Later: Supply a link?  Not exclusive to uploading.</p>
 
-${h.form(h.url_for(controller='art', action='upload'), multipart=True)}
+${h.form(h.url('create_art'), multipart=True)}
 ${h.file('file')}
 ${h.submit(None, 'Upload!')}
 ${h.end_form()}
 ${h.file('file')}
 ${h.submit(None, 'Upload!')}
 ${h.end_form()}
index 7290a11..73f069c 100644 (file)
@@ -46,6 +46,17 @@ ${h.end_form()}
 
 
 </div>
 
 
 </div>
+
+<% messages = h.flash.pop_messages() %>
+% if messages:
+<ul id="flash-messages">
+    % for message in messages:
+    <li>${message}</li>
+    % endfor
+</ul>
+% endif
+
+
 <div id="body">
 ${next.body()}
 </div>
 <div id="body">
 ${next.body()}
 </div>
index 5633e5b..061b7b9 100644 (file)
@@ -2,7 +2,7 @@
     <ul class="artwork-grid">
         % for item in art:
             <li>
     <ul class="artwork-grid">
         % for item in art:
             <li>
-                <a href="${h.url("art", id=item.id)}">
+                <a href="${h.url("show_art", id=item.id)}">
                     <img width="180" src="${item.get_path()}">
                 </a>
             </li>
                     <img width="180" src="${item.get_path()}">
                 </a>
             </li>
index d128276..f9a9329 100644 (file)
@@ -3,7 +3,7 @@
 
 <p>This is the userpage for ${c.this_user.name}.</p>
 
 
 <p>This is the userpage for ${c.this_user.name}.</p>
 
-% for gallery in c.this_user.galleries:
+% 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
 <h2>${gallery.string}</h2>
 ${macros.thumbs(gallery.search.results)}
 % endfor
\ No newline at end of file