Mercurial > hg > bitsyblog
diff bitsyblog/user.py @ 0:e3823be6a423
initial commit of bitsyblog, from https://svn.openplans.org/svn/standalone/bitsyblog/trunk/
author | k0s <k0scist@gmail.com> |
---|---|
date | Sat, 12 Sep 2009 16:06:57 -0400 |
parents | |
children | e5cbc53cacf8 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bitsyblog/user.py Sat Sep 12 16:06:57 2009 -0400 @@ -0,0 +1,212 @@ +import os +import random +import settings +import shutil +from bitsyblog import roles +from webob import exc + +class BitsyUser(object): + """interface class for a bitsyblog user""" + settings = {} + def __init__(self, name, password=''): + self.name = name + self.password = password + + # user behaves like a dictionary of settings + + def __getitem__(self, key): + return self.settings[key] + + def get(self, key, default=None): + return self.settings.get(key, default) + +class BitsyUsers(object): + """abstract class for bitsyblog user management""" + + def __iter__(self): return self.users() + + def __contains__(self, user): + return user in self.users() + + def __getitem__(self, user): + """return a user""" + if user not in self.users(): + raise KeyError + user = BitsyUser(user, self.password(user)) + user.settings = self.settings(user.name) + return user + + def passwords(self): + """returns a dictionary of { user: password }""" + passwords = {} + for user in self.users(): + passwords[user] = self.password(user) + return passwords + + ### interface methods to be specified by the child class + + def new(self, name, password): + """create a new user""" + + def users(self): + """returns the ids of all users (generator)""" + + def password(self, user): + """return the password for the user""" + + def settings(self, user): + """get user settings""" + + def write_settings(self, user, **kw): + """set attributes on a user""" + + +class FilespaceUsers(BitsyUsers): + """users that live on the filesystem""" + + def __init__(self, directory): + BitsyUsers.__init__(self) + self.directory = directory # directory to store user information in + + def home(self, user, *path): + return os.path.join(self.directory, user, *path) + + def pw_file(self, user): + return self.home(user, '.password') + + def secret(self, user): + secretfile = self.home(user, '.secret') + if os.path.exists(secretfile): + secret = int(file(secretfile).read().strip()) + else: + secret = random.randint(1024, 1024**4) + secretfile = file(secretfile, 'w') + print >> secretfile, secret + return secret + + def preferences_file(self, user): + return self.home(user, 'preferences.txt') + + def css(self, user, default): + css_dir = self.home(user, 'css') + css_files = [ i for i in os.listdir(css_dir) if i.endswith('.css') ] + if default: + default = '%s.css' % default + try: + index = css_files.index(default) + css_files.insert(0, css_files.pop(index)) + except ValueError: + pass + return [ dict(filename=i, name=i.rsplit('.css',1)[0], + css=file(os.path.join(css_dir, i)).read()) + for i in css_files ] + + ### interfaces for BitsyUsers + + def new(self, name, password): + """create a new user account""" + # XXX this shouldn't use HTTP exceptions + + if name in self.users(): + raise exc.HTTPForbidden("The name %s is already taken" % name).exception + + # characters forbidden in user name + forbidden = ' |<>./?,' + urls = [ 'join', 'login', 'logout', 'css', 'rss', 'atom', 'help' ] + if [ i for i in forbidden if i in name ]: + raise exc.HTTPForbidden("The name '%s' contains forbidden characters [%s]" % (user, forbidden)).exception + if name in urls: + raise exc.HTTPForbidden("The name '%s' is already used for a url" % user).exception + + home = self.home(name) + os.mkdir(home) + pw_file = file(self.pw_file(name), 'w') + print >> pw_file, password + + # setup entries structure for blog + entries = os.path.join(home, 'entries') + os.mkdir(entries) + for setting in roles['author']: + os.mkdir(os.path.join(entries, setting)) + + # setup user CSS + css_dir = os.path.join(home, 'css') + os.mkdir(css_dir) + shutil.copyfile(os.path.join(self.directory, 'site.css'), + os.path.join(css_dir, 'default.css')) + + + def users(self): + ignores = set(['.svn']) + for user in os.listdir(self.directory): + # ensure integrity of user folder + if user in ignores: + continue + if os.path.isdir(os.path.join(self.directory, user)): + yield user + + def password(self, user): + pw_file = '.password' # name of the password file on the filesystem + password = self.home(user, pw_file) + if os.path.exists(password): + return file(password).read().strip() + return '' # unspecified password + + def settings(self, name): + """returns a dictionary of user preferences from a file""" + filename = self.home(name, 'preferences.txt') + prefs = {} + if os.path.exists(filename): + prefs = file(filename).read().split('\n') + prefs = [ i for i in prefs if i.strip() ] + prefs = [ [ j.strip() for j in i.split(':', 1) ] + for i in prefs if ':' in i] + prefs = dict(prefs) + + # assemble friends from a list + friends = prefs.get('Friends') # can see secret blog posts + if friends: + prefs['Friends'] = friends.split(', ') + else: + prefs['Friends'] = [] + + # CSS files + prefs['CSS'] = self.css(name, prefs.get('Stylesheet')) + + return prefs + + def write_settings(self, name, **kw): + """write user settings to disk""" + + # generic stuff; could factor out + + newsettings = {} + errors = {} + + for setting in settings.form: + if kw.has_key(setting.name): + try: + setting.set(kw[setting.name]) + newsettings[setting.name] = setting.value + except settings.InvalidSettingError, e: + errors[setting.name] = str(e) + + if errors: + return errors + + # this makes the function depend on implemention + # i don't like this + new_css = newsettings.pop('CSS file') + if new_css: + filename = new_css['filename'] + css_file = file(self.home(name, 'css', filename), 'w') + print >> css_file, new_css['css'] + newsettings['CSS'] = filename.rsplit('.css', 1)[0] + + prefs = self.settings(name) + prefs.update(newsettings) + + # write the preferences to a file + preferences = file(self.preferences_file(name), 'w') + for key, value in prefs.items(): + print >> preferences, '%s: %s' % ( key, value )