/
util.py
300 lines (244 loc) · 10.1 KB
/
util.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
# -*- coding: utf-8 -*-
# Copyright (C) 2010 by Alexey S. Malakhov <brezerk@gmail.com>
# Opium <opium@jabber.com.ua>
#
# This file is part of Taverna
#
# Taverna is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Taverna is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Taverna. If not, see <http://www.gnu.org/licenses/>.
from django.shortcuts import render_to_response, redirect
from django.http import HttpResponseRedirect, HttpResponse
from django.core.context_processors import csrf
from django.core.urlresolvers import reverse as reverseURL
from urlparse import urljoin
from django.db import connection
from django.conf import settings
from openid.store.filestore import FileOpenIDStore
from openid.store import sqlstore
from django.core.paginator import Paginator
from django.core.cache import cache
from userauth.models import Profile
from blog.models import Blog
from django.contrib.syndication.views import Feed
class ExtendedPaginator(Paginator):
page_range = None
def page(self, number):
number = int(number)
if self.num_pages < 10:
self.page_range = range(1, self.num_pages + 1)
start = number - 5
end = number + 5
if start <= 10:
end = 10 if self.num_pages > 9 else self.num_pages
start = 1
elif end >= self.num_pages:
start = self.num_pages - 10
end = self.num_pages
self.page_range = range(start, end + 1)
return Paginator.page(self, number)
def clear_template_cache(key, *variables):
from django.utils.http import urlquote
from django.utils.hashcompat import md5_constructor
args = md5_constructor(u':'.join([urlquote(var) for var in variables]))
cache_key = 'template.cache.%s.%s' % (key, args.hexdigest())
if settings.DEBUG and cache.get(cache_key) is not None:
print "Removed template cache %s" % (cache_key)
else:
print "Attempted to delete %s" % cache_key
cache.delete(cache_key)
def invalidate_cache(post):
from forum.feeds import RssForum, RssComments, RssTrackerFeed
from blog.feeds import RssBlogFeed, RssBlog
"""
Be carefull with invalidation! :]
"""
RssTrackerFeed().clear_cache()
if post.thread is None:
return
if post.forum is not None and post.thread != post.pk:
# Invalidating forum tracker cache
RssForum().clear_cache(forum_id = post.forum.pk)
elif post.blog is not None:
# Blog post was changed
RssBlogFeed().clear_cache()
RssBlog().clear_cache(blog_id = post.thread.blog.pk)
# Invalidating post cache
clear_template_cache("blogpost", post.pk)
else:
# Comment for blog/forum was added/changed:
RssComments().clear_cache(post_id = post.thread.pk)
if post.thread.blog is not None:
# On new comment added we need invalidate start post cache
clear_template_cache("blogpost", post.thread.pk)
def modify_rating(post, cost = 1, positive = False):
from forum.models import Post
from django.db.models import F
if positive:
Post.objects.filter(id = post.pk).update(rating = F("rating") + cost)
Profile.objects.filter(id = post.owner.profile.pk).update(
force = F("force") + cost, karma = F("karma") + cost
)
else:
Post.objects.filter(id = post.pk).update(rating = F("rating") - cost)
Profile.objects.filter(id = post.owner.profile.pk).update(
force = F("force") - cost, karma = F("karma") - cost
)
invalidate_cache(post)
class CachedFeed(Feed):
def clear_cache(self, *args, **kwargs):
print "deleting "+ self.get_cache_key(*args, **kwargs)
cache.delete(self.get_cache_key())
def get_cache_key(self, *args, **kwargs):
return self.cache_prefix + ".".join([str(x) for x in args]) \
+ ".".join([str(x) for x in kwargs.keys()]) \
+ ".".join([str(x) for x in kwargs.values()])
def __call__(self, request, *args, **kwargs):
key = self.get_cache_key(*args, **kwargs)
reply = cache.get(key)
if reply is None:
reply = Feed.__call__(self, request, *args, **kwargs)
cache.set(key, reply, 100500) # About 28 hours :]
if settings.DEBUG:
print "Cache %s miss!" % (key)
else:
if settings.DEBUG:
print "Cache %s hit!" % (key)
return reply
def rr(template, mimetype=None, templates={}):
from django.template.loader import get_template
from django.template import Context
if template not in templates:
templates[template] = get_template(template)
def decor(view):
def wrapper(request, *args, **kwargs):
if request.user.is_authenticated():
# This is an a fixup for root account
try:
profile = request.user.profile
except:
profile = Profile(
user=request.user,
photo="",
openid_hash="",
karma=settings.START_RATING,
force=settings.START_RATING
)
profile.save()
try:
blog = Blog.objects.get(owner=request.user)
except Blog.DoesNotExist:
blog = Blog(owner=request.user, name=request.user.username)
blog.save()
except:
pass
if not profile.visible_name:
if request.path not in (reverseURL("userauth.views.profile_edit"),
reverseURL("userauth.views.openid_logout")):
return redirect("userauth.views.profile_edit")
val = view(request, *args, **kwargs)
if type(val) == type({}):
val.update({'user': request.user})
val.update(csrf(request))
if settings.TEMPLATE_DEBUG:
return render_to_response(template, val, mimetype=mimetype)
return HttpResponse(templates[template].render(Context(val)), mimetype = mimetype)
else:
return val
return wrapper
return decor
def getViewURL(req, view_name_or_obj, args=None, kwargs=None):
relative_url = reverseURL(view_name_or_obj, args=args, kwargs=kwargs)
full_path = req.META.get('SCRIPT_NAME', '') + relative_url
return urljoin(getBaseURL(req), full_path)
def getBaseURL(req):
"""
Given a Django web request object, returns the OpenID 'trust root'
for that request; namely, the absolute URL to the site root which
is serving the Django request. The trust root will include the
proper scheme and authority. It will lack a port if the port is
standard (80, 443).
"""
name = req.META['HTTP_HOST']
try:
name = name[:name.index(':')]
except:
pass
try:
port = int(req.META['SERVER_PORT'])
except:
port = 80
proto = req.META['SERVER_PROTOCOL']
if 'HTTPS' in proto:
proto = 'https'
else:
proto = 'http'
if port in [80, 443] or not port:
port = ''
else:
port = ':%s' % (port,)
url = "%s://%s%s/" % (proto, name, port)
return url
def getOpenIDStore(filestore_path, table_prefix):
"""
Returns an OpenID association store object based on the database
engine chosen for this Django application.
* If no database engine is chosen, a filesystem-based store will
be used whose path is filestore_path.
* If a database engine is chosen, a store object for that database
type will be returned.
* If the chosen engine is not supported by the OpenID library,
raise ImproperlyConfigured.
* If a database store is used, this will create the tables
necessary to use it. The table names will be prefixed with
table_prefix. DO NOT use the same table prefix for both an
OpenID consumer and an OpenID server in the same database.
The result of this function should be passed to the Consumer
constructor as the store parameter.
"""
if not settings.DATABASES:
return FileOpenIDStore(filestore_path)
# Possible side-effect: create a database connection if one isn't
# already open.
connection.cursor()
# Create table names to specify for SQL-backed stores.
tablenames = {
'associations_table': table_prefix + 'openid_associations',
'nonces_table': table_prefix + 'openid_nonces',
}
types = {
'django.db.backends.postgresql': sqlstore.PostgreSQLStore,
'django.db.backends.mysql': sqlstore.MySQLStore,
'django.db.backends.sqlite3': sqlstore.SQLiteStore,
}
try:
s = types[settings.DATABASES['default']['ENGINE']](connection.connection,
**tablenames)
except KeyError:
raise ImproperlyConfigured, \
"Database engine %s not supported by OpenID library" % \
(settings.DATABASE_ENGINE,)
try:
s.createTables()
except (SystemExit, KeyboardInterrupt, MemoryError), e:
raise
except:
# XXX This is not the Right Way to do this, but because the
# underlying database implementation might differ in behavior
# at this point, we can't reliably catch the right
# exception(s) here. Ideally, the SQL store in the OpenID
# library would catch exceptions that it expects and fail
# silently, but that could be bad, too. More ideally, the SQL
# store would not attempt to create tables it knows already
# exists.
pass
return s