diff --git a/MANIFEST.in b/MANIFEST.in index c4bf456..be20b63 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1 +1,2 @@ -include README.rst \ No newline at end of file +include README.rst +recursive-include boxes/templates * \ No newline at end of file diff --git a/boxes/admin.py b/boxes/admin.py index 80b406c..2ebeb93 100644 --- a/boxes/admin.py +++ b/boxes/admin.py @@ -1,6 +1,12 @@ from django.contrib import admin +import reversion + from boxes.models import Box -admin.site.register(Box) \ No newline at end of file +class BoxAdmin(reversion.VersionAdmin): + search_fields = ["content"] + + +admin.site.register(Box, BoxAdmin) diff --git a/boxes/authorization.py b/boxes/authorization.py deleted file mode 100644 index 5555d80..0000000 --- a/boxes/authorization.py +++ /dev/null @@ -1,20 +0,0 @@ -from django.conf import settings - -from boxes.utils import load_path_attr - - -def default_can_edit(request, *args, **kwargs): - """ - This is meant to be overridden in your project per domain specific - requirements. - """ - return request.user.is_staff - - -def load_can_edit(): - import_path = getattr(settings, "BOXES_CAN_EDIT_CALLABLE", None) - - if import_path is None: - return default_can_edit - - return load_path_attr(import_path) diff --git a/boxes/models.py b/boxes/models.py index d53afbf..d674f1a 100644 --- a/boxes/models.py +++ b/boxes/models.py @@ -8,10 +8,11 @@ class Box(models.Model): label = models.CharField(max_length=100, db_index=True) - content = models.TextField() + content = models.TextField(blank=True) created_by = models.ForeignKey(User, related_name="boxes") last_updated_by = models.ForeignKey(User, related_name="updated_boxes") + last_updated = models.DateTimeField(default=datetime.datetime.now) def __unicode__(self): return self.label diff --git a/boxes/templates/boxes/box.html b/boxes/templates/boxes/box.html new file mode 100644 index 0000000..ecf034f --- /dev/null +++ b/boxes/templates/boxes/box.html @@ -0,0 +1,26 @@ +{% load i18n %} + +{% if form %} + +{% endif %} + +
+ {% if form %} + Edit this content + {% endif %} + {{ box.content|safe }} +
diff --git a/boxes/templatetags/boxes_tags.py b/boxes/templatetags/boxes_tags.py index 5e1de23..2ba2981 100644 --- a/boxes/templatetags/boxes_tags.py +++ b/boxes/templatetags/boxes_tags.py @@ -1,94 +1,35 @@ from django import template -from django.core.exceptions import ImproperlyConfigured from django.core.urlresolvers import reverse -from django.utils.safestring import mark_safe -from django.utils.encoding import smart_str -from django.utils.translation import ugettext_lazy as _ -from django.template.defaulttags import kwarg_re from boxes.models import Box -from boxes.authorization import load_can_edit +from boxes.forms import BoxForm register = template.Library() -class BoxNode(template.Node): +@register.inclusion_tag("boxes/box.html", takes_context=True) +def box(context, label, show_edit=True, *args, **kwargs): - @classmethod - def handle(cls, parser, token): - bits = token.split_contents() - if len(bits) < 2: - raise template.TemplateSyntaxError( - "'box' takes at least one argument (label of the content to display)" - ) - args = [] - kwargs = {} - label = parser.compile_filter(bits[1]) - bits = bits[2:] - if len(bits): - for bit in bits: - match = kwarg_re.match(bit) - if not match: - raise template.TemplateSyntaxError("Malformed arguments to 'box' tag") - name, value = match.groups() - if name: - kwargs[name] = parser.compile_filter(value) - else: - args.append(parser.compile_filter(value)) - return cls(label, args, kwargs) + request = context["request"] + can_edit = request.user.has_perm("boxes.change_box") - def __init__(self, label, args, kwargs): - self.label = label - self.args = args - self.kwargs = kwargs + try: + box = Box.objects.get(label=label) + except Box.DoesNotExist: + box = None - def render(self, context): - try: - request = context["request"] - except KeyError: - raise ImproperlyConfigured('django-boxes requires the use of "django.core.context_processors.request" in TEMPLATE_CONTEXT_PROCESSORS') - - label = self.label.resolve(context) - args = [arg.resolve(context) for arg in self.args] - kwargs = dict([ - (smart_str(k, "ascii"), v.resolve(context)) - for k, v in self.kwargs.items() - ]) - - show_edit_link = load_can_edit()(request, *args, **kwargs) - - try: - box = Box.objects.get(label=label) - content = box.content.strip() - except Box.DoesNotExist: - box = None - content = "" - - if len(content) == 0: - content = _("

No content for this box has been created yet.

") - - # @@@ encode args/kwargs into querystring - if show_edit_link: - if box is None: - url = reverse("box_create", args=[label]) - link_text = unicode(_("Create")) - else: - url = reverse("box_edit", args=[box.pk]) - link_text = unicode(_("Edit")) - - content += " %s" % (url, link_text) - - return mark_safe(content) - - -@register.tag -def box(parser, token): - """ - {% box label [args] [kwargs] %} + if can_edit and show_edit: + form = BoxForm(instance=box, prefix=label) + form_action = reverse("box_edit", args=[label]) + else: + form = None + form_action = None - All args/kwargs are passed directly to callable defined in the setting - BOXES_CAN_EDIT_CALLABLE which returns True or False to determine if the - box can be edited. - """ - return BoxNode.handle(parser, token) + return { + "request": request, + "label": label, + "box": box, + "form": form, + "form_action": form_action, + } diff --git a/boxes/urls.py b/boxes/urls.py index 145d176..e385c7a 100644 --- a/boxes/urls.py +++ b/boxes/urls.py @@ -2,6 +2,5 @@ urlpatterns = patterns("boxes.views", - url(r"^([-\w]+)/create/$", "box_create", name="box_create"), - url(r"^(\d+)/edit/$", "box_edit", name="box_edit"), -) \ No newline at end of file + url(r"^([-\w]+)/edit/$", "box_edit", name="box_edit"), +) diff --git a/boxes/utils.py b/boxes/utils.py deleted file mode 100644 index 24b95c6..0000000 --- a/boxes/utils.py +++ /dev/null @@ -1,19 +0,0 @@ -from django.core.exceptions import ImproperlyConfigured -try: - from django.utils.importlib import import_module -except ImportError: - from importlib import import_module - - -def load_path_attr(path): - i = path.rfind(".") - module, attr = path[:i], path[i+1:] - try: - mod = import_module(module) - except ImportError, e: - raise ImproperlyConfigured("Error importing %s: '%s'" % (module, e)) - try: - attr = getattr(mod, attr) - except AttributeError: - raise ImproperlyConfigured("Module '%s' does not define a '%s'" % (module, attr)) - return attr diff --git a/boxes/views.py b/boxes/views.py index 1a373fd..7a8fff1 100644 --- a/boxes/views.py +++ b/boxes/views.py @@ -1,61 +1,51 @@ -from django.http import HttpResponseForbidden -from django.shortcuts import get_object_or_404, render_to_response +import datetime +import json + +from django.core.urlresolvers import reverse +from django.http import HttpResponse, HttpResponseForbidden +from django.shortcuts import redirect from django.template import RequestContext +from django.template.loader import render_to_string +from django.views.decorators.http import require_POST -from boxes.authorization import load_can_edit from boxes.forms import BoxForm from boxes.models import Box -# @@@ problem with this is that the box_edit.html and box_create.html won't have domain objects in context -def get_auth_vars(request): - auth_vars = {} - if request.method == "POST": - keys = [k for k in request.POST.keys() if k.startswith("boxes_auth_")] - for key in keys: - auth_vars[key.replace("boxes_auth_", "")] = request.POST.get(key) - auth_vars["user"] = request.user - return auth_vars - - -def box_edit(request, pk): - box = get_object_or_404(Box, pk=pk) - if request.method == "POST": - #if not load_can_edit()(request, **get_auth_vars(request)): - # return HttpResponseForbidden() - - form = BoxForm(request.POST, instance=box) - if form.is_valid(): - form.save() - return render_to_response("boxes/refresh.html", {}) - else: - form = BoxForm(instance=box) - ctx = { - "form": form, - "box": box, - } - ctx = RequestContext(request, ctx) - return render_to_response("boxes/box_edit.html", ctx) - - -def box_create(request, label): - if request.method == "POST": - #if not load_can_edit()(request, **get_auth_vars(request)): - # return HttpResponseForbidden() - - form = BoxForm(request.POST) - if form.is_valid(): +@require_POST +def box_edit(request, label): + + if not request.user.has_perm("boxes.change_box"): + return HttpResponseForbidden() + + next = request.GET.get("next") + + try: + box = Box.objects.get(label=label) + except Box.DoesNotExist: + box = None + + form = BoxForm(request.POST, instance=box, prefix=label) + + if form.is_valid(): + if box is None: box = form.save(commit=False) box.label = label box.created_by = request.user box.last_updated_by = request.user + box.last_updated = datetime.datetime.now() box.save() - return render_to_response("boxes/refresh.html", {}) - else: - form = BoxForm() - ctx = { - "form": form, - "label": label - } - ctx = RequestContext(request, ctx) - return render_to_response("boxes/box_create.html", ctx) + else: + form.save() + + if request.is_ajax(): + data = { + "html": render_to_string("boxes/box.html", { + "label": label, + "form": BoxForm(instance=box, prefix=label), + "box": box, + "form_action": reverse("box_edit", args=[label]) + }, context_instance=RequestContext(request)) + } + return HttpResponse(json.dumps(data), mimetype="application/json") + return redirect(next) diff --git a/setup.py b/setup.py index c07780e..0763c77 100644 --- a/setup.py +++ b/setup.py @@ -3,13 +3,16 @@ setup( name = "django-boxes", - version = "1.0b1.dev2", + version = "2.0b1.dev1", author = "Eldarion", author_email = "development@eldarion.com", description = "a reusable Django content-boxes application", long_description = open("README.rst").read(), license = "BSD", url = "http://github.com/eldarion/boxes", + install_requires = [ + "django-reversion", + ], packages = [ "boxes", "boxes.templatetags",