Mercurial > hg > bitsyblog
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 ) |