template = 'admin/philo/edit_inline/tabular_attribute.html'
-def hide_proxy_fields(cls, attname, proxy_field_set):
- val_set = set(getattr(cls, attname))
- if proxy_field_set & val_set:
- cls._hidden_attributes[attname] = list(val_set)
- setattr(cls, attname, list(val_set - proxy_field_set))
+# HACK to bypass model validation for proxy fields
+class SpoofedHiddenFields(object):
+ def __init__(self, proxy_fields, value):
+ self.value = value
+ self.spoofed = list(set(value) - set(proxy_fields))
+
+ def __get__(self, instance, owner):
+ if instance is None:
+ return self.spoofed
+ return self.value
+
+
+class SpoofedAddedFields(SpoofedHiddenFields):
+ def __init__(self, proxy_fields, value):
+ self.value = value
+ self.spoofed = list(set(value) | set(proxy_fields))
+
+
+def hide_proxy_fields(cls, attname):
+ val = getattr(cls, attname, [])
+ proxy_fields = getattr(cls, 'proxy_fields')
+ if val:
+ setattr(cls, attname, SpoofedHiddenFields(proxy_fields, val))
+
+def add_proxy_fields(cls, attname):
+ val = getattr(cls, attname, [])
+ proxy_fields = getattr(cls, 'proxy_fields')
+ setattr(cls, attname, SpoofedAddedFields(proxy_fields, val))
class EntityAdminMetaclass(admin.ModelAdmin.__metaclass__):
def __new__(cls, name, bases, attrs):
- # HACK to bypass model validation for proxy fields by masking them as readonly fields
new_class = super(EntityAdminMetaclass, cls).__new__(cls, name, bases, attrs)
- form = getattr(new_class, 'form', None)
- if form:
- opts = form._meta
- if issubclass(form, EntityForm) and opts.model:
- proxy_fields = proxy_fields_for_entity_model(opts.model).keys()
-
- # Store readonly fields iff they have been declared.
- if 'readonly_fields' in attrs or not hasattr(new_class, '_real_readonly_fields'):
- new_class._real_readonly_fields = new_class.readonly_fields
-
- readonly_fields = new_class.readonly_fields
- new_class.readonly_fields = list(set(readonly_fields) | set(proxy_fields))
-
- # Additional HACKS to handle raw_id_fields and other attributes that the admin
- # uses model._meta.get_field to validate.
- new_class._hidden_attributes = {}
- proxy_fields = set(proxy_fields)
- hide_proxy_fields(new_class, 'raw_id_fields', proxy_fields)
- #END HACK
+ hide_proxy_fields(new_class, 'raw_id_fields')
+ add_proxy_fields(new_class, 'readonly_fields')
return new_class
-
+# END HACK
class EntityAdmin(admin.ModelAdmin):
__metaclass__ = EntityAdminMetaclass
form = EntityForm
inlines = [AttributeInline]
save_on_top = True
-
- def __init__(self, *args, **kwargs):
- # HACK PART 2 restores the actual readonly fields etc. on __init__.
- if hasattr(self, '_real_readonly_fields'):
- self.readonly_fields = self.__class__._real_readonly_fields
- if hasattr(self, '_hidden_attributes'):
- for name, value in self._hidden_attributes.items():
- setattr(self, name, value)
- # END HACK
- super(EntityAdmin, self).__init__(*args, **kwargs)
+ proxy_fields = []
def formfield_for_dbfield(self, db_field, **kwargs):
"""
# kwargs['widget'] = widgets.FilteredSelectMultiple(db_field.verbose_name, (db_field.name in self.filter_vertical))
return db_field.formfield(**kwargs)
+
+ def get_form(self, request, obj=None, **kwargs):
+ """
+ Ensures that the form's proxy fields are included in its base fields.
+ This will not be required after http://code.djangoproject.com/ticket/14082 has been resolved
+ """
+ form = super(EntityAdmin, self).get_form(request, obj, **kwargs)
+ form.base_fields.update(form.proxy_fields)
+ return form
class TreeAdmin(MPTTModelAdmin):
try:
return Navigation.objects.filter(node__in=node.get_ancestors(include_self=True), key=key).order_by('-node__level')[0].node
except:
- if settings.TEMPLATE_DEBUG:
- raise
return node
\ No newline at end of file
from django.http import HttpResponseRedirect, Http404
from django.shortcuts import render_to_response
from django.template import RequestContext
-from django.utils.functional import update_wrapper
from django.utils.translation import ugettext_lazy as _
from philo.admin import EntityAdmin
from philo.contrib.sobol.models import Search, ResultURL, SearchView
+from functools import update_wrapper
class ResultURLInline(admin.TabularInline):
from django.conf import settings
from django.http import QueryDict
from django.utils.encoding import smart_str
-from django.utils.hashcompat import sha_constructor
from django.utils.http import urlquote_plus, urlquote
+from hashlib import sha1
SEARCH_ARG_GET_KEY = 'q'
def make_redirect_hash(search_arg, url):
- return sha_constructor(smart_str(search_arg + url + settings.SECRET_KEY)).hexdigest()[::2]
+ return sha1(smart_str(search_arg + url + settings.SECRET_KEY)).hexdigest()[::2]
def check_redirect_hash(hash, search_arg, url):
from django.conf import settings
from django.utils.http import int_to_base36, base36_to_int
from django.contrib.auth.tokens import PasswordResetTokenGenerator
+from hashlib import sha1
REGISTRATION_TIMEOUT_DAYS = getattr(settings, 'WALDO_REGISTRATION_TIMEOUT_DAYS', 1)
# By hashing on the internal state of the user and using state that is
# sure to change, we produce a hash that will be invalid as soon as it
# is used.
- from django.utils.hashcompat import sha_constructor
- hash = sha_constructor(settings.SECRET_KEY + unicode(user.id) + unicode(user.is_active) + user.last_login.strftime('%Y-%m-%d %H:%M:%S') + unicode(timestamp)).hexdigest()[::2]
+ hash = sha1(settings.SECRET_KEY + unicode(user.id) + unicode(user.is_active) + user.last_login.strftime('%Y-%m-%d %H:%M:%S') + unicode(timestamp)).hexdigest()[::2]
return '%s-%s' % (ts_b36, hash)
def _make_token_with_timestamp(self, user, email, timestamp):
ts_b36 = int_to_base36(timestamp)
- from django.utils.hashcompat import sha_constructor
- hash = sha_constructor(settings.SECRET_KEY + unicode(user.id) + user.email + email + unicode(timestamp)).hexdigest()[::2]
+ hash = sha1(settings.SECRET_KEY + unicode(user.id) + user.email + email + unicode(timestamp)).hexdigest()[::2]
return '%s-%s' % (ts_b36, hash)
-from django.forms.models import ModelFormMetaclass, ModelForm
+from django.forms.models import ModelFormMetaclass, ModelForm, ModelFormOptions
from django.utils.datastructures import SortedDict
from philo.utils import fattr
__all__ = ('EntityForm',)
-def proxy_fields_for_entity_model(entity_model, fields=None, exclude=None, widgets=None, formfield_callback=lambda f, **kwargs: f.formfield(**kwargs)):
+def proxy_fields_for_entity_model(entity_model, fields=None, exclude=None, widgets=None, formfield_callback=None):
field_list = []
ignored = []
opts = entity_model._entity_meta
kwargs = {'widget': widgets[f.name]}
else:
kwargs = {}
- formfield = formfield_callback(f, **kwargs)
+
+ if formfield_callback is None:
+ formfield = f.formfield(**kwargs)
+ elif not callable(formfield_callback):
+ raise TypeError('formfield_callback must be a function or callable')
+ else:
+ formfield = formfield_callback(f, **kwargs)
+
if formfield:
field_list.append((f.name, formfield))
else:
return field_dict
-# BEGIN HACK - This will not be required after http://code.djangoproject.com/ticket/14082 has been resolved
-
-class EntityFormBase(ModelForm):
- pass
-
-_old_metaclass_new = ModelFormMetaclass.__new__
-
-def _new_metaclass_new(cls, name, bases, attrs):
- formfield_callback = attrs.get('formfield_callback', lambda f, **kwargs: f.formfield(**kwargs))
- new_class = _old_metaclass_new(cls, name, bases, attrs)
- opts = new_class._meta
- if issubclass(new_class, EntityFormBase) and opts.model:
- # "override" proxy fields with declared fields by excluding them if there's a name conflict.
- exclude = (list(opts.exclude or []) + new_class.declared_fields.keys()) or None
- proxy_fields = proxy_fields_for_entity_model(opts.model, opts.fields, exclude, opts.widgets, formfield_callback) # don't pass in formfield_callback
+class EntityFormMetaclass(ModelFormMetaclass):
+ def __new__(cls, name, bases, attrs):
+ try:
+ parents = [b for b in bases if issubclass(b, EntityForm)]
+ except NameError:
+ # We are defining EntityForm itself
+ parents = None
+ sup = super(EntityFormMetaclass, cls)
+
+ if not parents:
+ # Then there's no business trying to use proxy fields.
+ return sup.__new__(cls, name, bases, attrs)
+
+ # Preemptively make a meta so we can screen out any proxy fields.
+ # After http://code.djangoproject.com/ticket/14082 has been resolved,
+ # perhaps switch to setting proxy fields as "declared"?
+ _opts = ModelFormOptions(attrs.get('Meta', None))
+
+ # Make another copy of opts to spoof the proxy fields not being there.
+ opts = ModelFormOptions(attrs.get('Meta', None))
+ if opts.model:
+ formfield_callback = attrs.get('formfield_callback', None)
+ proxy_fields = proxy_fields_for_entity_model(opts.model, opts.fields, opts.exclude, opts.widgets, formfield_callback)
+ else:
+ proxy_fields = {}
+
+ # Screen out all proxy fields from the meta
+ opts.fields = list(opts.fields or [])
+ opts.exclude = list(opts.exclude or [])
+
+ for field in proxy_fields.keys():
+ try:
+ opts.fields.remove(field)
+ except ValueError:
+ pass
+ try:
+ opts.exclude.remove(field)
+ except ValueError:
+ pass
+ opts.fields = opts.fields or None
+ opts.exclude = opts.exclude or None
+
+ # Put in the new Meta.
+ attrs['Meta'] = opts
+
+ new_class = sup.__new__(cls, name, bases, attrs)
+
+ intersections = set(new_class.declared_fields.keys()) & set(proxy_fields.keys())
+ for key in intersections:
+ proxy_fields.pop(key)
+
new_class.proxy_fields = proxy_fields
+ new_class._meta = _opts
new_class.base_fields.update(proxy_fields)
- return new_class
-
-ModelFormMetaclass.__new__ = staticmethod(_new_metaclass_new)
+ return new_class
-# END HACK
-
-class EntityForm(EntityFormBase): # Would inherit from ModelForm directly if it weren't for the above HACK
+class EntityForm(ModelForm):
+ __metaclass__ = EntityFormMetaclass
+
def __init__(self, *args, **kwargs):
initial = kwargs.pop('initial', None)
instance = kwargs.get('instance', None)
node, subpath = Node.objects.get_with_path(path, root=getattr(current_site, 'root_node', None), absolute_result=False)
except Node.DoesNotExist:
node = None
-
- if node:
+ else:
if subpath is None:
subpath = ""
subpath = "/" + subpath
- if trailing_slash and subpath[-1] != "/":
- subpath += "/"
-
- node.subpath = subpath
+ if not node.handles_subpath(subpath):
+ node = None
+ else:
+ if trailing_slash and subpath[-1] != "/":
+ subpath += "/"
+
+ node.subpath = subpath
request._found_node = node
request.__class__.node = LazyNode()
def process_view(self, request, view_func, view_args, view_kwargs):
- request._cached_node_path = view_kwargs.get('path', '/')
+ try:
+ request._cached_node_path = view_kwargs['path']
+ except KeyError:
+ pass
def process_exception(self, request, exception):
if settings.DEBUG or not hasattr(request, 'node') or not request.node:
class EntityBase(models.base.ModelBase):
def __new__(cls, name, bases, attrs):
+ entity_meta = attrs.pop('EntityMeta', None)
new = super(EntityBase, cls).__new__(cls, name, bases, attrs)
- entity_options = attrs.pop('EntityMeta', None)
- setattr(new, '_entity_meta', EntityOptions(entity_options))
+ new.add_to_class('_entity_meta', EntityOptions(entity_meta))
entity_class_prepared.send(sender=new)
return new
return contentlet_specs, contentreference_specs
def __unicode__(self):
- return self.get_path(pathsep=u' › ', field='name')
+ return self.name
class Meta:
app_label = 'philo'
urlpatterns = patterns('',
- url(r'^$', node_view, name='philo-root'),
+ url(r'^$', node_view, kwargs={'path': '/'}, name='philo-root'),
url(r'^(?P<path>.*)$', node_view, name='philo-node-by-path')
)
subpath = request.node.subpath
# Explicitly disallow trailing slashes if we are otherwise at a node's url.
- if request.path and request.path != "/" and request.path[-1] == "/" and subpath == "/":
+ if request._cached_node_path != "/" and request._cached_node_path[-1] == "/" and subpath == "/":
return HttpResponseRedirect(node.get_absolute_url())
if not node.handles_subpath(subpath):