comparison 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
comparison
equal deleted inserted replaced
-1:000000000000 0:e3823be6a423
1 import os
2 import random
3 import settings
4 import shutil
5 from bitsyblog import roles
6 from webob import exc
7
8 class BitsyUser(object):
9 """interface class for a bitsyblog user"""
10 settings = {}
11 def __init__(self, name, password=''):
12 self.name = name
13 self.password = password
14
15 # user behaves like a dictionary of settings
16
17 def __getitem__(self, key):
18 return self.settings[key]
19
20 def get(self, key, default=None):
21 return self.settings.get(key, default)
22
23 class BitsyUsers(object):
24 """abstract class for bitsyblog user management"""
25
26 def __iter__(self): return self.users()
27
28 def __contains__(self, user):
29 return user in self.users()
30
31 def __getitem__(self, user):
32 """return a user"""
33 if user not in self.users():
34 raise KeyError
35 user = BitsyUser(user, self.password(user))
36 user.settings = self.settings(user.name)
37 return user
38
39 def passwords(self):
40 """returns a dictionary of { user: password }"""
41 passwords = {}
42 for user in self.users():
43 passwords[user] = self.password(user)
44 return passwords
45
46 ### interface methods to be specified by the child class
47
48 def new(self, name, password):
49 """create a new user"""
50
51 def users(self):
52 """returns the ids of all users (generator)"""
53
54 def password(self, user):
55 """return the password for the user"""
56
57 def settings(self, user):
58 """get user settings"""
59
60 def write_settings(self, user, **kw):
61 """set attributes on a user"""
62
63
64 class FilespaceUsers(BitsyUsers):
65 """users that live on the filesystem"""
66
67 def __init__(self, directory):
68 BitsyUsers.__init__(self)
69 self.directory = directory # directory to store user information in
70
71 def home(self, user, *path):
72 return os.path.join(self.directory, user, *path)
73
74 def pw_file(self, user):
75 return self.home(user, '.password')
76
77 def secret(self, user):
78 secretfile = self.home(user, '.secret')
79 if os.path.exists(secretfile):
80 secret = int(file(secretfile).read().strip())
81 else:
82 secret = random.randint(1024, 1024**4)
83 secretfile = file(secretfile, 'w')
84 print >> secretfile, secret
85 return secret
86
87 def preferences_file(self, user):
88 return self.home(user, 'preferences.txt')
89
90 def css(self, user, default):
91 css_dir = self.home(user, 'css')
92 css_files = [ i for i in os.listdir(css_dir) if i.endswith('.css') ]
93 if default:
94 default = '%s.css' % default
95 try:
96 index = css_files.index(default)
97 css_files.insert(0, css_files.pop(index))
98 except ValueError:
99 pass
100 return [ dict(filename=i, name=i.rsplit('.css',1)[0],
101 css=file(os.path.join(css_dir, i)).read())
102 for i in css_files ]
103
104 ### interfaces for BitsyUsers
105
106 def new(self, name, password):
107 """create a new user account"""
108 # XXX this shouldn't use HTTP exceptions
109
110 if name in self.users():
111 raise exc.HTTPForbidden("The name %s is already taken" % name).exception
112
113 # characters forbidden in user name
114 forbidden = ' |<>./?,'
115 urls = [ 'join', 'login', 'logout', 'css', 'rss', 'atom', 'help' ]
116 if [ i for i in forbidden if i in name ]:
117 raise exc.HTTPForbidden("The name '%s' contains forbidden characters [%s]" % (user, forbidden)).exception
118 if name in urls:
119 raise exc.HTTPForbidden("The name '%s' is already used for a url" % user).exception
120
121 home = self.home(name)
122 os.mkdir(home)
123 pw_file = file(self.pw_file(name), 'w')
124 print >> pw_file, password
125
126 # setup entries structure for blog
127 entries = os.path.join(home, 'entries')
128 os.mkdir(entries)
129 for setting in roles['author']:
130 os.mkdir(os.path.join(entries, setting))
131
132 # setup user CSS
133 css_dir = os.path.join(home, 'css')
134 os.mkdir(css_dir)
135 shutil.copyfile(os.path.join(self.directory, 'site.css'),
136 os.path.join(css_dir, 'default.css'))
137
138
139 def users(self):
140 ignores = set(['.svn'])
141 for user in os.listdir(self.directory):
142 # ensure integrity of user folder
143 if user in ignores:
144 continue
145 if os.path.isdir(os.path.join(self.directory, user)):
146 yield user
147
148 def password(self, user):
149 pw_file = '.password' # name of the password file on the filesystem
150 password = self.home(user, pw_file)
151 if os.path.exists(password):
152 return file(password).read().strip()
153 return '' # unspecified password
154
155 def settings(self, name):
156 """returns a dictionary of user preferences from a file"""
157 filename = self.home(name, 'preferences.txt')
158 prefs = {}
159 if os.path.exists(filename):
160 prefs = file(filename).read().split('\n')
161 prefs = [ i for i in prefs if i.strip() ]
162 prefs = [ [ j.strip() for j in i.split(':', 1) ]
163 for i in prefs if ':' in i]
164 prefs = dict(prefs)
165
166 # assemble friends from a list
167 friends = prefs.get('Friends') # can see secret blog posts
168 if friends:
169 prefs['Friends'] = friends.split(', ')
170 else:
171 prefs['Friends'] = []
172
173 # CSS files
174 prefs['CSS'] = self.css(name, prefs.get('Stylesheet'))
175
176 return prefs
177
178 def write_settings(self, name, **kw):
179 """write user settings to disk"""
180
181 # generic stuff; could factor out
182
183 newsettings = {}
184 errors = {}
185
186 for setting in settings.form:
187 if kw.has_key(setting.name):
188 try:
189 setting.set(kw[setting.name])
190 newsettings[setting.name] = setting.value
191 except settings.InvalidSettingError, e:
192 errors[setting.name] = str(e)
193
194 if errors:
195 return errors
196
197 # this makes the function depend on implemention
198 # i don't like this
199 new_css = newsettings.pop('CSS file')
200 if new_css:
201 filename = new_css['filename']
202 css_file = file(self.home(name, 'css', filename), 'w')
203 print >> css_file, new_css['css']
204 newsettings['CSS'] = filename.rsplit('.css', 1)[0]
205
206 prefs = self.settings(name)
207 prefs.update(newsettings)
208
209 # write the preferences to a file
210 preferences = file(self.preferences_file(name), 'w')
211 for key, value in prefs.items():
212 print >> preferences, '%s: %s' % ( key, value )