Restrict usernames to lowercase, digits, and hyphens.
[zzz-floof.git] / floof / model / users.py
index f51282c..bacdfe6 100644 (file)
@@ -1,19 +1,95 @@
-from sqlalchemy import Column, ForeignKey
-from sqlalchemy.orm import relation
-from sqlalchemy.types import Integer, Unicode
+#
+#   floof/floof/model/users.py
+#
+#   Copyright (c) 2009 Scribblr
+#
 
-from floof.model import meta
+import re
 
-__all__ = ['User', 'IdentityURL']
+from elixir import *
 
-class User(meta.TableBase):
-    __tablename__ = 'users'
-    id = Column(Integer, primary_key=True)
-    name = Column(Unicode(length=20), nullable=False)
+from search import GalleryWidget
 
-class IdentityURL(meta.TableBase):
-    __tablename__ = 'identity_urls'
-    url = Column(Unicode(length=255), primary_key=True)
-    user_id = Column(Integer, ForeignKey('users.id'))
+class User(Entity):
+    name = Field(Unicode(20))
+    display_name = Field(Unicode(20))
+    uploads = OneToMany('Art')
+    has_many('identity_urls', of_kind='IdentityURL')
+    searches = OneToMany('SavedSearch')
+    # galleries = OneToMany('GalleryWidget')
+    pages = OneToMany('UserPage', inverse="owner")
+    primary_page = OneToOne('UserPage', inverse="owner")
+    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
+
+    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 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.  The primary page is indicated in the user model.
+    """
+    
+    owner = ManyToOne('User', inverse="pages")
+    title = Field(String)
+
+    visible = Field(Boolean)
+    galleries = OneToMany('GalleryWidget')
+
+
+class UserRelationshipTypes(object):
+    IS_WATCHING = 1
+
+class UserRelationship(Entity):
+    """Represents some sort of connection between users.
+
+    For the moment, this means "watching".  Later, it may mean friending or
+    ignoring.
+
+    XXX: Watching should be made more general than this; it should have the
+    power of an arbitrary query per watched artist without being unintelligible
+    to users.
+    """
+
+    user = ManyToOne('User')
+    target_user = ManyToOne('User')
+    type = Field(Integer)  # UserRelationshipTypes above
 
-IdentityURL.user = relation(User, lazy=False, backref="identity_urls")