Skip to content

Commit

Permalink
Merge 06a16e3 into fdb5672
Browse files Browse the repository at this point in the history
  • Loading branch information
lenarother committed Nov 29, 2016
2 parents fdb5672 + 06a16e3 commit ca0c4df
Show file tree
Hide file tree
Showing 29 changed files with 600 additions and 260 deletions.
41 changes: 38 additions & 3 deletions barbeque/cms/toolbar.py
@@ -1,11 +1,15 @@
from cms.cms_toolbars import (
ADMIN_MENU_IDENTIFIER, PAGE_MENU_IDENTIFIER)
from django.utils.encoding import force_text

from cms.cms_toolbars import ADMIN_MENU_IDENTIFIER, PAGE_MENU_IDENTIFIER
from cms.extensions.toolbar import ExtensionToolbar
from cms.toolbar_base import CMSToolbar
from cms.toolbar_pool import toolbar_pool
from cms.toolbar.items import SideframeItem, ModalItem, SubMenu


@toolbar_pool.register
class ForceModalDialogToolbar(CMSToolbar):

def rebuild_menu(self, menu):
items = []
for item in menu.items:
Expand Down Expand Up @@ -40,4 +44,35 @@ def populate(self):
for menu in [menu for menu in menus if menu]:
self.rebuild_menu(menu)

toolbar_pool.register(ForceModalDialogToolbar)

class TitleExtensionToolbar(ExtensionToolbar):
model = None
insert_after = None

def get_item_position(self, menu):
position = None
for items in menu._memo.values():
for item in items:
if force_text(getattr(item, 'name', None)) in (
force_text(self.insert_after),
'{0}...'.format(self.insert_after)
):
position = menu._item_position(item) + 1
break

return position

def populate(self):
current_page_menu = self._setup_extension_toolbar()
if not current_page_menu or not self.page:
return

position = self.get_item_position(current_page_menu)

urls = self.get_title_extension_admin()
for title_extension, url in urls:
current_page_menu.add_modal_item(
self.model._meta.verbose_name,
url=url, position=position,
disabled=not self.toolbar.edit_mode
)
5 changes: 4 additions & 1 deletion barbeque/filer.py
Expand Up @@ -42,6 +42,9 @@ def __init__(self, verbose_name=None, *args, **kwargs):
kwargs['verbose_name'] = verbose_name
if 'related_name' not in kwargs:
kwargs['related_name'] = '+'
if kwargs.pop('blank', False) or kwargs.pop('null', False):
kwargs['null'] = True
kwargs['blank'] = True

self.extensions = kwargs.pop('extensions', None)
self.alt_text_required = kwargs.pop('alt_text_required', True)
Expand All @@ -51,7 +54,7 @@ def __init__(self, verbose_name=None, *args, **kwargs):
def formfield(self, **kwargs):
defaults = {
'extensions': self.extensions,
'alt_text_required': self.alt_text_required
'alt_text_required': self.alt_text_required,
}
defaults.update(kwargs)
return super(FilerFileField, self).formfield(**defaults)
33 changes: 22 additions & 11 deletions barbeque/files.py
Expand Up @@ -4,35 +4,46 @@
import uuid

from django.template.defaultfilters import slugify
from django.utils.deconstruct import deconstructible
from django.utils.encoding import force_text


def upload_to_path(base_path, attr=None, uuid_filename=False):
def upload_to_path_callback(instance, filename):
if attr:
parts = attr.split('__')
@deconstructible
class UploadToPath(object):

def __init__(self, base_path, attr=None, uuid_filename=False):
self.base_path = base_path
self.attr = attr
self.uuid_filename = uuid_filename

def __call__(self, instance, filename):
if self.attr:
parts = self.attr.split('__')
obj_path = parts[:-1]
field_name = parts[-1]

obj = instance
for part in obj_path:
obj = getattr(obj, part)

path = base_path % slugify(getattr(obj, field_name, '_'))
path = self.base_path % slugify(getattr(obj, field_name, '_'))
else:
path = base_path
path = self.base_path

filename_parts = filename.rsplit('.', 1)

if uuid_filename:
filename = str(uuid.uuid4())
if self.uuid_filename:
filename = force_text(uuid.uuid4())
else:
filename = slugify(filename_parts[0])

extension = len(filename_parts) > 1 and '.{0}'.format(filename_parts[-1]) or ''
extension = len(filename_parts) > 1 and u'.{0}'.format(filename_parts[-1]) or ''

return '%s%s%s' % (path, filename, extension)
return os.path.join(path, u'{0}{1}'.format(filename, extension))

return upload_to_path_callback

def upload_to_path(base_path, attr=None, uuid_filename=False):
return UploadToPath(base_path, attr=attr, uuid_filename=uuid_filename)


class MoveableNamedTemporaryFile(object):
Expand Down
9 changes: 9 additions & 0 deletions barbeque/forms/__init__.py
@@ -0,0 +1,9 @@
import warnings

from .mixins import FloppyformsLayoutMixin, ItemLimitInlineMixin, PlaceholderFormMixin # noqa


warnings.warn((
'Importing mixins directly from barbeque.forms is deprecated and will be removed '
'in barbeque 1.3. Use barbeque.forms.mixins instead.'
), DeprecationWarning)
14 changes: 14 additions & 0 deletions barbeque/forms.py → barbeque/forms/mixins.py
Expand Up @@ -61,3 +61,17 @@ def get_error_message(self, message, num):
message = message.format(num=num, verbose_name=verbose_name)

return message


class FloppyformsLayoutMixin(object):
row_classname = 'form-row'
div_template_name = 'modules/generic/form/layout/div.html'

def __init__(self, *args, **kwargs):
super(FloppyformsLayoutMixin, self).__init__(*args, **kwargs)
for name, field in self.fields.items():
widget = self.fields[name].widget
widget.widget_type = widget.__class__.__name__.lower()

def as_div(self):
return self._render_as(self.div_template_name)
39 changes: 39 additions & 0 deletions barbeque/forms/renderer.py
@@ -0,0 +1,39 @@
from floppyforms.forms import LayoutRenderer

from .mixins import FloppyformsLayoutMixin


class FieldsetRenderer(FloppyformsLayoutMixin, LayoutRenderer):
non_field_errors = None

def __init__(self, form, fields=None, exclude=None, primary=False, template=None):
assert fields or exclude is not None, 'Please provide fields or exclude argument.'

self.form = form
self.fields = fields or ()
self.exclude = exclude or ()
self.primary_fieldset = primary

if template:
self.div_template_name = template

def __str__(self):
return self.as_div()

def hidden_fields(self):
return self.form.hidden_fields() if self.primary_fieldset else ()

def non_field_errors(self):
return self.form.non_field_errors() if self.primary_fieldset else ()

def visible_fields(self):
form_visible_fields = self.form.visible_fields()

if self.fields:
fields = self.fields
else:
fields = [field.name for field in form_visible_fields]

filtered_fields = [field for field in fields if field not in self.exclude]

return [field for field in form_visible_fields if field.name in filtered_fields]
Binary file added barbeque/locale/de/LC_MESSAGES/django.mo
Binary file not shown.
62 changes: 62 additions & 0 deletions barbeque/locale/de/LC_MESSAGES/django.po
@@ -0,0 +1,62 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2016-11-28 11:54+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

#: barbeque/anylink.py:15
msgid "CMS Page"
msgstr "CMS Seite"

#: barbeque/exporter.py:119
msgid "Export as CSV"
msgstr "Als CSV exportieren"

#: barbeque/exporter.py:125
msgid "Export as XLSX"
msgstr "Als XLSX exportieren"

#: barbeque/filer.py:27
#, python-brace-format
msgid "Invalid file extension, allowed extensions: {0}"
msgstr "Ungültige Dateiendung, erlaubte Dateitypen: {0}"

#: barbeque/filer.py:33
msgid "Alternative text is missing for this file."
msgstr "Bildbeschreibung fehlt für diese Datei."

#: barbeque/forms/mixins.py:32
#, python-brace-format
msgid "Please provide at least {num} {verbose_name}."
msgstr "Mindestens {num} {verbose_name} benötigt."

#: barbeque/forms/mixins.py:33
#, python-brace-format
msgid "Please provide at most {num} {verbose_name}."
msgstr "Maximal {num} {verbose_name} erlaubt."

#: barbeque/validators.py:9
#, python-format
msgid "You must be at least %(limit_value)d years old."
msgstr "Sie müssen mindestens %(limit_value)d Jahre alt sein."

#: barbeque/validators.py:18
msgid "This email address is already in use."
msgstr "Diese E-Mail-Adresse ist bereits in Verwendung."

#: barbeque/views/mixins.py:30
msgid "You must be logged in to access the requested page."
msgstr "Sie müssen angemeldet sein, um auf die gewünschte Seite zu gelangen"
38 changes: 38 additions & 0 deletions barbeque/templatetags/barbeque_tags.py
@@ -1,10 +1,17 @@
import re

from django import template
from django.contrib.staticfiles.storage import staticfiles_storage
from django.core.exceptions import ObjectDoesNotExist
from django.template.defaultfilters import stringfilter
from django.utils.html import conditional_escape
from django.utils.safestring import mark_safe

try:
from cms.models import Page
from cms.utils.moderator import use_draft
except ImportError:
pass

register = template.Library()

Expand All @@ -24,3 +31,34 @@ def set_tag(context, **kwargs):
@stringfilter
def starspan(value):
return mark_safe(STARSPAN_RE.sub(r'<span>\2</span>', conditional_escape(value)))


@register.simple_tag
def hashed_staticfile(path):
try:
return staticfiles_storage.hashed_name(path)
except (AttributeError, ValueError):
return path


@register.simple_tag(takes_context=True)
def page_titleextension(context, page_id, extension):
try:
page = Page.objects.get(pk=page_id)
if 'request' in context and use_draft(context['request']):
page = page.get_draft_object()
else:
page = page.get_public_object()
except NameError:
raise ImportError(
'django-cms is required when using page_titleextension tag')
except Page.DoesNotExist:
return None

if not page:
return None

try:
return getattr(page.get_title_obj(), extension)
except ObjectDoesNotExist:
return None
11 changes: 7 additions & 4 deletions barbeque/templatetags/buildcompress.py
Expand Up @@ -4,8 +4,7 @@
try:
from compressor.templatetags.compress import CompressorNode, OUTPUT_FILE
except ImportError:
CompressorNode = None
OUTPUT_FILE = None
pass


register = template.Library()
Expand All @@ -29,7 +28,11 @@ def buildcompress(parser, token):
args = token.split_contents()
assert len(args) == 2, 'Invalid arguments to buildcompress.'

if settings.DEBUG or not CompressorNode:
if settings.DEBUG:
return BuildCompressNoopNode()

return CompressorNode(nodelist, args[1], OUTPUT_FILE, None)
try:
return CompressorNode(nodelist, args[1], OUTPUT_FILE, None)
except NameError:
raise ImportError(
'django-compressor is required when using buildcompress tag')
2 changes: 2 additions & 0 deletions barbeque/tests/cms_urls.py
@@ -1,8 +1,10 @@
from django.conf.urls import include, url
from django.contrib import admin
from django.views.generic import TemplateView


urlpatterns = [
url(r'^admin/', include(admin.site.urls)),
url(r'^non-cms/', TemplateView.as_view(template_name='empty_template.html')),
url(r'', include('cms.urls')),
]
Empty file.
7 changes: 7 additions & 0 deletions barbeque/tests/resources/cmsapp/admin.py
@@ -0,0 +1,7 @@
from cms.extensions import TitleExtensionAdmin
from django.contrib import admin

from .models import ExtensionModel


admin.site.register(ExtensionModel, TitleExtensionAdmin)
12 changes: 12 additions & 0 deletions barbeque/tests/resources/cmsapp/cms_toolbars.py
@@ -0,0 +1,12 @@
from cms.toolbar_pool import toolbar_pool

from barbeque.cms.toolbar import TitleExtensionToolbar

from .models import ExtensionModel


class ExtensionToolbar(TitleExtensionToolbar):
model = ExtensionModel
insert_after = 'Advanced settings'

toolbar_pool.register(ExtensionToolbar)
15 changes: 15 additions & 0 deletions barbeque/tests/resources/cmsapp/models.py
@@ -0,0 +1,15 @@
from cms.extensions import TitleExtension
from cms.extensions.extension_pool import extension_pool
from django.db import models


class ExtensionModel(TitleExtension):
name = models.CharField(max_length=255)

class Meta:
verbose_name = 'Extension'

def __unicode__(self):
return self.name

extension_pool.register(ExtensionModel)
1 change: 1 addition & 0 deletions barbeque/tests/resources/mockapp/models.py
Expand Up @@ -13,3 +13,4 @@ class RelatedMockModel(models.Model):
class DummyModel(models.Model):
name = models.CharField(max_length=256)
slug = models.SlugField()
email = models.EmailField(blank=True)

0 comments on commit ca0c4df

Please sign in to comment.