Created
October 30, 2013 19:24
-
-
Save evilchili/7238593 to your computer and use it in GitHub Desktop.
monkeypatching pagemanager.published to honor protected assets' users/groups
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from copy import deepcopy | |
from django.db import models | |
from django.db.models import Q | |
from mezzanine.pages.models import Page | |
from mezzanine.core.models import RichText | |
from django.contrib.auth.models import Group, User | |
from mezzanine.pages.managers import PageManager | |
from django.utils.timezone import now | |
from django.conf import settings | |
from django.contrib.contenttypes.models import ContentType | |
#import logging | |
class ProtectedPage(Page, RichText): | |
""" | |
Create a new content type that behaves just like a RichText Page, except that it | |
adds a join to the django.contrib.auth Group and User fields. | |
""" | |
groups = models.ManyToManyField(Group, null=True) | |
users = models.ManyToManyField(User, null=True) | |
class ProtectedAsset(Page): | |
""" | |
A file available for download, but restricted to one or more users or groups. | |
""" | |
groups = models.ManyToManyField(Group, null=True, verbose_name="Permitted Groups") | |
users = models.ManyToManyField(User, null=True, verbose_name="Permitted Users") | |
path = models.FileField(upload_to="assets", verbose_name="File") | |
icon = models.ImageField(upload_to="icons", null=True, blank=True, verbose_name="Icon") | |
always_visible = models.BooleanField(default=False) | |
def save(self, *args, **kwargs): | |
""" | |
Override the default save method to enforce setting the slug to the filefield's url. | |
""" | |
# WAT: This doesn't include the upload_to subdirectory when the asset is created. :( | |
self.slug = self.path.url | |
super(ProtectedAsset, self).save(*args, **kwargs) | |
# | |
# MONKEY-PATCHES | |
# | |
original_get_absolute_url = deepcopy(Page.get_absolute_url) | |
def monkey_page_absolute_url(self): | |
""" | |
Monkeypatch Page.get_absolute_url to add support for Assets. | |
""" | |
if self.content_model == "protectedasset": | |
return self.slug | |
return original_get_absolute_url(self) | |
Page.get_absolute_url = monkey_page_absolute_url | |
protected_ctypes = ContentType.objects.filter(name__startswith="protected") | |
def monkey_pagemanager_published(self, for_user=None, include_login_required=False): | |
""" | |
Some models subclass Page and add the users and/or groups fields. For those that do, | |
we filter the set of "published" Page items (which include these subclass models) | |
by checking the items' users/groups against the current request user. | |
Note that this routine does not call super(PageManager, self).published(). | |
""" | |
from mezzanine.core.models import CONTENT_STATUS_PUBLISHED | |
# if the user is a staff member, we don't need to filter results; they see everything. | |
if for_user is not None and for_user.is_staff: | |
return self.all() | |
# generate a QuerySet object containing all published Page items. This is the same | |
# behaviour as PublishedManager.publish(), except that we specifically ignore anything | |
# with a non-page content model. | |
published = self.filter( | |
Q(content_model__in=["page", "richtextpage", "link"]), | |
Q(publish_date__lte=now()) | Q(publish_date__isnull=True), | |
Q(expiry_date__gte=now()) | Q(expiry_date__isnull=True), | |
Q(status=CONTENT_STATUS_PUBLISHED), | |
) | |
# if the user is logged in we must consider those items which subclass Page with users/groups. | |
if for_user is not None and not for_user.is_anonymous(): | |
# Query for each subclass's published items. We must do this because justing using self | |
# here will return Page items for each member, which will not have the subclasses's | |
# users/groups fields. | |
for ctype in protected_ctypes: | |
#logging.info("Checking for published %s items" % ctype) | |
items = ctype.model_class().objects.filter( | |
Q(groups__in=for_user.groups.all()), | |
Q(publish_date__lte=now()) | Q(publish_date__isnull=True), | |
Q(expiry_date__gte=now()) | Q(expiry_date__isnull=True), | |
Q(status=CONTENT_STATUS_PUBLISHED), | |
) | |
# Now look up the pages items for the subclasses's published items, by id. | |
items_pages = self.filter(id__in=[x.id for x in items.all()]) | |
# add this subclass's page items to the original published queryset. | |
# NB: HOW COOLS IS THIS? BITWISE OPERATIONS ON QUERYSETS! <3 <3 | |
published = published | items_pages | |
# Now we have a single queryset, published, which contains all published items. The last | |
# task is to honor the login_required bits like in the original PageManager.pulished(). | |
unauthenticated = for_user and not for_user.is_authenticated() | |
if ( | |
unauthenticated and | |
not include_login_required and | |
not settings.PAGES_PUBLISHED_INCLUDE_LOGIN_REQUIRED | |
): | |
published = published.exclude(login_required=True) | |
return published | |
PageManager.published = monkey_pagemanager_published |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment