)
return urlpatterns
- def get_all_entries(self, request, node=None, extra_context=None):
+ def get_all_entries(self, request, extra_context=None):
return self.blog.entries.all(), extra_context
- def get_entries_by_ymd(self, request, year=None, month=None, day=None, node=None, extra_context=None):
+ def get_entries_by_ymd(self, request, year=None, month=None, day=None, extra_context=None):
if not self.entry_archive_page:
raise Http404
entries = self.blog.entries.all()
context.update({'year': year, 'month': month, 'day': day})
return entries, context
- def get_entries_by_tag(self, request, tag_slugs, node=None, extra_context=None):
+ def get_entries_by_tag(self, request, tag_slugs, extra_context=None):
tags = []
for tag_slug in tag_slugs.replace('+', '/').split('/'):
if tag_slug: # ignore blank slugs, handles for multiple consecutive separators (+ or /)
defaults.update(kwargs or {})
return super(BlogView, self).get_feed(feed_type, extra_context, defaults)
- def entry_view(self, request, slug, year=None, month=None, day=None, node=None, extra_context=None):
+ def entry_view(self, request, slug, year=None, month=None, day=None, extra_context=None):
entries = self.blog.entries.all()
if year:
entries = entries.filter(date__year=year)
context = self.get_context()
context.update(extra_context or {})
context.update({'entry': entry})
- return self.entry_page.render_to_response(node, request, extra_context=context)
+ return self.entry_page.render_to_response(request, extra_context=context)
- def tag_archive_view(self, request, node=None, extra_context=None):
+ def tag_archive_view(self, request, extra_context=None):
if not self.tag_archive_page:
raise Http404
context = {}
context.update(extra_context or {})
context.update({'blog': self.blog})
- return self.tag_archive_page.render_to_response(node, request, extra_context=context)
+ return self.tag_archive_page.render_to_response(request, extra_context=context)
class Newsletter(Entity, Titled):
def get_context(self):
return {'newsletter': self.newsletter}
- def get_all_articles(self, request, node, extra_context=None):
+ def get_all_articles(self, request, extra_context=None):
return self.newsletter.articles.all(), extra_context
- def get_articles_by_ymd(self, request, year, month=None, day=None, node=None, extra_context=None):
+ def get_articles_by_ymd(self, request, year, month=None, day=None, extra_context=None):
articles = self.newsletter.articles.filter(dat__year=year)
if month:
articles = articles.filter(date__month=month)
articles = articles.filter(date__day=day)
return articles
- def get_articles_by_issue(self, request, numbering, node=None, extra_context=None):
+ def get_articles_by_issue(self, request, numbering, extra_context=None):
try:
issue = self.newsletter.issues.get(numbering=numbering)
except:
context.update({'issue': issue})
return issue.articles.all(), context
- def article_view(self, request, slug, year=None, month=None, day=None, node=None, extra_context=None):
+ def article_view(self, request, slug, year=None, month=None, day=None, extra_context=None):
articles = self.newsletter.articles.all()
if year:
articles = articles.filter(date__year=year)
context = self.get_context()
context.update(extra_context or {})
context.update({'article': article})
- return self.article_page.render_to_response(node, request, extra_context=context)
+ return self.article_page.render_to_response(request, extra_context=context)
- def issue_archive_view(self, request, node=None, extra_context=None):
+ def issue_archive_view(self, request, extra_context=None):
if not self.issue_archive_page:
raise Http404
context = {}
context.update(extra_context or {})
context.update({'newsletter': self.newsletter})
- return self.issue_archive_page.render_to_response(node, request, extra_context=context)
+ return self.issue_archive_page.render_to_response(request, extra_context=context)
def add_item(self, feed, obj, kwargs=None):
defaults = {
"""
Wraps an object-fetching function and renders the results as a page.
"""
- def inner(request, node=None, extra_context=None, **kwargs):
- objects, extra_context = func(request=request, node=node, extra_context=extra_context, **kwargs)
+ def inner(request, extra_context=None, **kwargs):
+ objects, extra_context = func(request=request, extra_context=extra_context, **kwargs)
context = self.get_context()
context.update(extra_context or {})
else:
context.update({self.list_var: objects})
- return page.render_to_response(node, request, extra_context=context)
+ return page.render_to_response(request, extra_context=context)
return inner
"""
Wraps an object-fetching function and renders the results as a rss or atom feed.
"""
- def inner(request, node=None, extra_context=None, **kwargs):
- objects, extra_context = func(request=request, node=node, extra_context=extra_context, **kwargs)
+ def inner(request, extra_context=None, **kwargs):
+ objects, extra_context = func(request=request, extra_context=extra_context, **kwargs)
if 'HTTP_ACCEPT' in request.META and 'rss' in request.META['HTTP_ACCEPT'] and 'atom' not in request.META['HTTP_ACCEPT']:
feed_type = 'rss'
feed_type = 'atom'
current_site = Site.objects.get_current()
-
+ #Could this be done with request.path instead somehow?
feed_kwargs = {
- 'link': 'http://%s/%s/%s/' % (current_site.domain, node.get_absolute_url().strip('/'), reverse(reverse_name, urlconf=self, kwargs=kwargs).strip('/'))
+ 'link': 'http://%s/%s/%s/' % (current_site.domain, request.node.get_absolute_url().strip('/'), reverse(reverse_name, urlconf=self, kwargs=kwargs).strip('/'))
}
feed = self.get_feed(feed_type, extra_context, feed_kwargs)
for obj in objects:
kwargs = {
- 'link': 'http://%s/%s/%s/' % (current_site.domain, node.get_absolute_url().strip('/'), self.get_subpath(obj).strip('/'))
+ 'link': 'http://%s/%s/%s/' % (current_site.domain, request.node.get_absolute_url().strip('/'), self.get_subpath(obj).strip('/'))
}
self.add_item(feed, obj, kwargs=kwargs)
context.update(extra_dict or {})
return context
- def display_login_page(self, request, message, node=None, extra_context=None):
+ def display_login_page(self, request, message, extra_context=None):
request.session.set_test_cookie()
referrer = request.META.get('HTTP_REFERER', None)
redirect = '%s?%s' % (referrer[2], referrer[4])
if referrer is None:
- redirect = node.get_absolute_url()
+ redirect = request.node.get_absolute_url()
path = request.get_full_path()
if redirect != path:
'form': form
})
context.update(extra_context or {})
- return self.login_page.render_to_response(node, request, extra_context=context)
+ return self.login_page.render_to_response(request, extra_context=context)
- def login(self, request, node=None, extra_context=None):
+ def login(self, request, extra_context=None):
"""
Displays the login form for the given HttpRequest.
"""
if request.user.is_authenticated():
- return HttpResponseRedirect(node.get_absolute_url())
+ return HttpResponseRedirect(request.node.get_absolute_url())
context = self.get_context(extra_context)
message = _("Please log in again, because your session has expired.")
else:
message = ""
- return self.display_login_page(request, message, node, context)
+ return self.display_login_page(request, message, context)
# Check that the user accepts cookies.
if not request.session.test_cookie_worked():
message = _("Looks like your browser isn't configured to accept cookies. Please enable cookies, reload this page, and try again.")
- return self.display_login_page(request, message, node, context)
+ return self.display_login_page(request, message, context)
else:
request.session.delete_test_cookie()
" Try '%s' instead.") % user.username
else:
message = _("Usernames cannot contain the '@' character.")
- return self.display_login_page(request, message, node, context)
+ return self.display_login_page(request, message, context)
# The user data is correct; log in the user in and continue.
else:
try:
redirect = request.session.pop('redirect')
except KeyError:
- redirect = node.get_absolute_url()
+ redirect = request.node.get_absolute_url()
return HttpResponseRedirect(redirect)
else:
- return self.display_login_page(request, ERROR_MESSAGE, node, context)
+ return self.display_login_page(request, ERROR_MESSAGE, context)
login = never_cache(login)
def logout(self, request):
return auth_views.logout(request, request.META['HTTP_REFERER'])
def login_required(self, view):
- def inner(request, node=None, *args, **kwargs):
+ def inner(request, *args, **kwargs):
if not request.user.is_authenticated():
login_url = reverse('login', urlconf=self).strip('/')
- return HttpResponseRedirect('%s%s/' % (node.get_absolute_url(), login_url))
- return view(request, node=node, *args, **kwargs)
+ return HttpResponseRedirect('%s%s/' % (request.node.get_absolute_url(), login_url))
+ return view(request, *args, **kwargs)
return inner
else:
send_mail(subject, text_content, from_email, [email])
- def password_reset(self, request, node=None, extra_context=None, token_generator=password_token_generator):
+ def password_reset(self, request, extra_context=None, token_generator=password_token_generator):
if request.user.is_authenticated():
- return HttpResponseRedirect(node.get_absolute_url())
+ return HttpResponseRedirect(request.node.get_absolute_url())
if request.method == 'POST':
form = PasswordResetForm(request.POST)
current_site = Site.objects.get_current()
for user in form.users_cache:
token = token_generator.make_token(user)
- link = 'http://%s/%s/%s/' % (current_site.domain, node.get_absolute_url().strip('/'), reverse('password_reset_confirm', urlconf=self, kwargs={'uidb36': int_to_base36(user.id), 'token': token}).strip('/'))
+ link = 'http://%s/%s/%s/' % (current_site.domain, request.node.get_absolute_url().strip('/'), reverse('password_reset_confirm', urlconf=self, kwargs={'uidb36': int_to_base36(user.id), 'token': token}).strip('/'))
context = {
'link': link,
'username': user.username
context = self.get_context({'form': form})
context.update(extra_context or {})
- return self.password_reset_page.render_to_response(node, request, extra_context=context)
+ return self.password_reset_page.render_to_response(request, extra_context=context)
- def password_reset_confirm(self, request, node=None, extra_context=None, uidb36=None, token=None, token_generator=password_token_generator):
+ def password_reset_confirm(self, request, extra_context=None, uidb36=None, token=None, token_generator=password_token_generator):
"""
Checks that a given hash in a password reset link is valid. If so,
displays the password set form.
if form.is_valid():
form.save()
messages.add_message(request, messages.SUCCESS, "Password reset successful.")
- return HttpResponseRedirect('/%s/%s/' % (node.get_absolute_url().strip('/'), reverse('login', urlconf=self).strip('/')))
+ return HttpResponseRedirect('/%s/%s/' % (request.node.get_absolute_url().strip('/'), reverse('login', urlconf=self).strip('/')))
else:
form = SetPasswordForm(user)
context = self.get_context({'form': form})
- return self.password_set_page.render_to_response(node, request, extra_context=context)
+ return self.password_set_page.render_to_response(request, extra_context=context)
raise Http404
- def password_change(self, request, node=None, extra_context=None):
+ def password_change(self, request, extra_context=None):
if request.method == 'POST':
form = PasswordChangeForm(request.user, request.POST)
if form.is_valid():
context = self.get_context({'form': form})
context.update(extra_context or {})
- return self.password_change_page.render_to_response(node, request, extra_context=context)
+ return self.password_change_page.render_to_response(request, extra_context=context)
- def register(self, request, node=None, extra_context=None, token_generator=registration_token_generator):
+ def register(self, request, extra_context=None, token_generator=registration_token_generator):
if request.user.is_authenticated():
- return HttpResponseRedirect(node.get_absolute_url())
+ return HttpResponseRedirect(request.node.get_absolute_url())
if request.method == 'POST':
form = RegistrationForm(request.POST)
user = form.save()
current_site = Site.objects.get_current()
token = token_generator.make_token(user)
- link = 'http://%s/%s/%s/' % (current_site.domain, node.get_absolute_url().strip('/'), reverse('register_confirm', urlconf=self, kwargs={'uidb36': int_to_base36(user.id), 'token': token}).strip('/'))
+ link = 'http://%s/%s/%s/' % (current_site.domain, request.node.get_absolute_url().strip('/'), reverse('register_confirm', urlconf=self, kwargs={'uidb36': int_to_base36(user.id), 'token': token}).strip('/'))
context = {
'link': link
}
self.send_confirmation_email('Confirm account creation at %s' % current_site.name, user.email, self.register_confirmation_email, context)
messages.add_message(request, messages.SUCCESS, 'An email has been sent to %s with details on activating your account.' % user.email, fail_silently=True)
- return HttpResponseRedirect(node.get_absolute_url())
+ return HttpResponseRedirect(request.node.get_absolute_url())
else:
form = RegistrationForm()
context = self.get_context({'form': form})
context.update(extra_context or {})
- return self.register_page.render_to_response(node, request, extra_context=context)
+ return self.register_page.render_to_response(request, extra_context=context)
- def register_confirm(self, request, node=None, extra_context=None, uidb36=None, token=None, token_generator=registration_token_generator):
+ def register_confirm(self, request, extra_context=None, uidb36=None, token=None, token_generator=registration_token_generator):
"""
Checks that a given hash in a registration link is valid and activates
the given account. If so, log them in and redirect to
# if anything goes wrong, ABSOLUTELY make sure that the true password is restored.
user.password = true_password
user.save()
- return self.post_register_confirm_redirect(request, node)
+ return self.post_register_confirm_redirect(request)
raise Http404
- def post_register_confirm_redirect(self, request, node):
- return HttpResponseRedirect(node.get_absolute_url())
+ def post_register_confirm_redirect(self, request):
+ return HttpResponseRedirect(request.node.get_absolute_url())
class Meta:
abstract = True
return form_instances
- def account_view(self, request, node=None, extra_context=None, token_generator=email_token_generator, *args, **kwargs):
+ def account_view(self, request, extra_context=None, token_generator=email_token_generator, *args, **kwargs):
if request.method == 'POST':
form_instances = self.get_account_form_instances(request.user, request.POST)
current_email = request.user.email
current_site = Site.objects.get_current()
token = token_generator.make_token(request.user, email)
- link = 'http://%s/%s/%s/' % (current_site.domain, node.get_absolute_url().strip('/'), reverse('email_change_confirm', urlconf=self, kwargs={'uidb36': int_to_base36(request.user.id), 'email': email.replace('@', '+'), 'token': token}).strip('/'))
+ link = 'http://%s/%s/%s/' % (current_site.domain, request.node.get_absolute_url().strip('/'), reverse('email_change_confirm', urlconf=self, kwargs={'uidb36': int_to_base36(request.user.id), 'email': email.replace('@', '+'), 'token': token}).strip('/'))
context = {
'link': link
}
'forms': form_instances
})
context.update(extra_context or {})
- return self.manage_account_page.render_to_response(node, request, extra_context=context)
+ return self.manage_account_page.render_to_response(request, extra_context=context)
def has_valid_account(self, user):
user_form, profile_form = self.get_account_forms()
inner = self.login_required(inner)
return inner
- def post_register_confirm_redirect(self, request, node):
+ def post_register_confirm_redirect(self, request):
messages.add_message(request, messages.INFO, 'Welcome! Please fill in some more information.', fail_silently=True)
- return HttpResponseRedirect('/%s/%s/' % (node.get_absolute_url().strip('/'), reverse('account', urlconf=self).strip('/')))
+ return HttpResponseRedirect('/%s/%s/' % (request.node.get_absolute_url().strip('/'), reverse('account', urlconf=self).strip('/')))
- def email_change_confirm(self, request, node=None, extra_context=None, uidb36=None, token=None, email=None, token_generator=email_token_generator):
+ def email_change_confirm(self, request, extra_context=None, uidb36=None, token=None, email=None, token_generator=email_token_generator):
"""
Checks that a given hash in an email change link is valid. If so, changes the email and redirects to the account page.
"""
user.email = email
user.save()
messages.add_message(request, messages.SUCCESS, 'Email changed successfully.')
- return HttpResponseRedirect('/%s/%s/' % (node.get_absolute_url().strip('/'), reverse('account', urlconf=self).strip('/')))
+ return HttpResponseRedirect('/%s/%s/' % (request.node.get_absolute_url().strip('/'), reverse('account', urlconf=self).strip('/')))
raise Http404
+from django.core.exceptions import ImproperlyConfigured
+
+
+MIDDLEWARE_NOT_CONFIGURED = ImproperlyConfigured("""Philo requires the RequestNode middleware to be installed. Edit your MIDDLEWARE_CLASSES setting to insert 'philo.middleware.RequestNodeMiddleware'.""")
+
+
class ViewDoesNotProvideSubpaths(Exception):
""" Raised by get_subpath when the View does not provide subpaths (the default). """
silent_variable_failure = True
--- /dev/null
+from django.contrib.sites.models import Site
+from philo.models import Node
+
+
+class LazyNode(object):
+ def __get__(self, request, obj_type=None):
+ if not hasattr(request, '_cached_node_path'):
+ return None
+
+ if not hasattr(request, '_found_node'):
+ try:
+ current_site = Site.objects.get_current()
+ except Site.DoesNotExist:
+ current_site = None
+
+ try:
+ node, subpath = Node.objects.get_with_path(request._cached_node_path, root=getattr(current_site, 'root_node', None), absolute_result=False)
+ except Node.DoesNotExist:
+ node = None
+
+ if node:
+ node.subpath = subpath
+
+ request._found_node = node
+
+ return request._found_node
+
+
+class RequestNodeMiddleware(object):
+ """Middleware to process the request's path and attach the closest ancestor node."""
+ def process_request(self, request):
+ request.__class__.node = LazyNode()
+
+ def process_view(self, request, view_func, view_args, view_kwargs):
+ request._cached_node_path = view_kwargs.get('path', '/')
\ No newline at end of file
from django.core.urlresolvers import resolve, clear_url_caches
from django.template import add_to_builtins as register_templatetags
from inspect import getargspec
+from philo.exceptions import MIDDLEWARE_NOT_CONFIGURED
from philo.models.base import TreeEntity, Entity, QuerySetMapper, register_value_model
from philo.utils import ContentTypeSubclassLimiter
from philo.validators import RedirectValidator
return self.view.accepts_subpath
return False
- def render_to_response(self, request, path=None, subpath=None, extra_context=None):
- return self.view.render_to_response(self, request, path, subpath, extra_context)
+ def render_to_response(self, request, extra_context=None):
+ return self.view.render_to_response(request, extra_context)
def get_absolute_url(self):
root = Site.objects.get_current().root_node
def attributes_with_node(self, node):
return QuerySetMapper(self.attribute_set, passthrough=node.attributes)
- def render_to_response(self, node, request, path=None, subpath=None, extra_context=None):
+ def render_to_response(self, request, extra_context=None):
+ if not hasattr(request, 'node'):
+ raise MIDDLEWARE_NOT_CONFIGURED
+
extra_context = extra_context or {}
- view_about_to_render.send(sender=self, node=node, request=request, path=path, subpath=subpath, extra_context=extra_context)
- response = self.actually_render_to_response(node, request, path, subpath, extra_context)
+ view_about_to_render.send(sender=self, request=request, extra_context=extra_context)
+ response = self.actually_render_to_response(request, extra_context)
view_finished_rendering.send(sender=self, response=response)
return response
- def actually_render_to_response(self, node, request, path=None, subpath=None, extra_context=None):
+ def actually_render_to_response(self, request, extra_context=None):
raise NotImplementedError('View subclasses must implement render_to_response.')
class Meta:
urlpatterns = []
- def actually_render_to_response(self, node, request, path=None, subpath=None, extra_context=None):
+ def actually_render_to_response(self, request, extra_context=None):
clear_url_caches()
+ subpath = request.node.subpath
if not subpath:
subpath = ""
subpath = "/" + subpath
if 'extra_context' in kwargs:
extra_context.update(kwargs['extra_context'])
kwargs['extra_context'] = extra_context
- if 'node' in view_args[0] or view_args[2] is not None:
- kwargs['node'] = node
return view(request, *args, **kwargs)
class Meta:
target = models.CharField(max_length=200, validators=[RedirectValidator()])
status_code = models.IntegerField(choices=STATUS_CODES, default=302, verbose_name='redirect type')
- def actually_render_to_response(self, node, request, path=None, subpath=None, extra_context=None):
+ def actually_render_to_response(self, request, extra_context=None):
response = HttpResponseRedirect(self.target)
response.status_code = self.status_code
return response
app_label = 'philo'
+# Why does this exist?
class File(View):
""" For storing arbitrary files """
mimetype = models.CharField(max_length=255)
file = models.FileField(upload_to='philo/files/%Y/%m/%d')
- def actually_render_to_response(self, node, request, path=None, subpath=None, extra_context=None):
+ def actually_render_to_response(self, request, extra_context=None):
wrapper = FileWrapper(self.file)
response = HttpResponse(wrapper, content_type=self.mimetype)
response['Content-Length'] = self.file.size
return self._containers
containers = property(get_containers)
- def render_to_string(self, node=None, request=None, path=None, subpath=None, extra_context=None):
+ def render_to_string(self, request=None, extra_context=None):
context = {}
context.update(extra_context or {})
context.update({'page': self, 'attributes': self.attributes})
- if node and request:
- context.update({'node': node, 'attributes': self.attributes_with_node(node)})
- page_about_to_render_to_string.send(sender=self, node=node, request=request, extra_context=context)
+ if request:
+ context.update({'node': request.node, 'attributes': self.attributes_with_node(request.node)})
+ page_about_to_render_to_string.send(sender=self, request=request, extra_context=context)
string = self.template.django_template.render(RequestContext(request, context))
else:
- page_about_to_render_to_string.send(sender=self, node=node, request=request, extra_context=context)
+ page_about_to_render_to_string.send(sender=self, request=request, extra_context=context)
string = self.template.django_template.render(Context(context))
page_finished_rendering_to_string.send(sender=self, string=string)
return string
- def actually_render_to_response(self, node, request, path=None, subpath=None, extra_context=None):
- return HttpResponse(self.render_to_string(node, request, path, subpath, extra_context), mimetype=self.template.mimetype)
+ def actually_render_to_response(self, request, extra_context=None):
+ return HttpResponse(self.render_to_string(request, extra_context), mimetype=self.template.mimetype)
def __unicode__(self):
return self.title
entity_class_prepared = Signal(providing_args=['class'])
-view_about_to_render = Signal(providing_args=['node', 'request', 'path', 'subpath', 'extra_context'])
+view_about_to_render = Signal(providing_args=['request', 'extra_context'])
view_finished_rendering = Signal(providing_args=['response'])
-page_about_to_render_to_string = Signal(providing_args=['node', 'request', 'path', 'subpath', 'extra_context'])
+page_about_to_render_to_string = Signal(providing_args=['request', 'extra_context'])
page_finished_rendering_to_string = Signal(providing_args=['string'])
from django.http import Http404, HttpResponse
from django.template import RequestContext
from django.views.decorators.vary import vary_on_headers
+from philo.exceptions import MIDDLEWARE_NOT_CONFIGURED
from philo.models import Node
@vary_on_headers('Accept')
def node_view(request, path=None, **kwargs):
- node = None
- subpath = None
- if path is None:
- path = '/'
- current_site = Site.objects.get_current()
- try:
- node, subpath = Node.objects.get_with_path(path, root=current_site.root_node, absolute_result=False)
- except Node.DoesNotExist:
- raise Http404
+ if not hasattr(request, 'node'):
+ raise MIDDLEWARE_NOT_CONFIGURED
- if not node:
+ if not request.node:
raise Http404
+ node = request.node
+ subpath = request.node.subpath
+
try:
if subpath and not node.accepts_subpath:
raise Http404
- return node.render_to_response(request, path=path, subpath=subpath)
+ return node.render_to_response(request, kwargs)
except Http404, e:
if settings.DEBUG:
raise
extra_context = {'exception': e}
- return Http404View.render_to_response(node, request, path, subpath, extra_context)
+ return Http404View.render_to_response(request, extra_context)
except Exception, e:
if settings.DEBUG:
raise
extra_context = {'exception': e}
- return Http500View.render_to_response(node, request, path, subpath, extra_context)
+ return Http500View.render_to_response(request, extra_context)
except:
raise e
\ No newline at end of file