Skip to content

Commit

Permalink
Merge ed64d37 into 1c0d24f
Browse files Browse the repository at this point in the history
  • Loading branch information
yakky committed Oct 22, 2019
2 parents 1c0d24f + ed64d37 commit 8490dd5
Show file tree
Hide file tree
Showing 24 changed files with 742 additions and 2 deletions.
2 changes: 2 additions & 0 deletions cms_helper.py
Expand Up @@ -24,7 +24,9 @@ def gettext(s):
'taggit_autosuggest',
'aldryn_apphooks_config',
'aldryn_search',
'djangocms_video',
'sortedm2m',
'tests.media_app',
],
LANGUAGE_CODE='en',
LANGUAGES=(
Expand Down
2 changes: 2 additions & 0 deletions djangocms_blog/media/__init__.py
@@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function, unicode_literals
112 changes: 112 additions & 0 deletions djangocms_blog/media/base.py
@@ -0,0 +1,112 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function, unicode_literals


class MediaAttachmentPluginMixin(object):
"""
Base class for media-enabled plugins.
Items that needs implementation in subclasses:
* media_id: property that provides the object id on the external platform
* media_url: property that provides the media public URL
* _media_autoconfiguration: configuration dictionary (see documentation for details)
"""
_media_autoconfiguration = {
'params': [],
'thumb_url': '',
'main_url': '',
'callable': None,
}
"""
Configuration dictionary. **All** the keys are required:
* ``'params'``: one or more regular expressions to retrieve the media ID
according to the provided ``media_url``. It **must** contain a capturing
group called ``media_id`` (see examples below).
* ``'thumb_url'``: URL of the intermediate resolution media cover (depending
on the plaform).
It supports string formatting via ``format`` by providing the return json
for the media according to the plugic specification.
* ``'main_url'``: URL of the maximum resolution media cover (depending
on the plaform).
It supports string formatting via ``%``-formatting by providing the
return json for the media according to the plugic specification.
* ``'callable'``: in case the above information are not recoverable from the
object URL, provide here the name of a method on the plugin model instance
taking the ``media_id`` as parameter and that builds the data required by
``thumb_url``, ``media_url`` strings to build the correct cover urls.
"""
_cached_params = None

@property
def media_params(self):
"""
Retrieves the media information.
Minimal keys returned:
* ``media_id``: object id on the external platform
* ``url``: full url to the public version of the media
In case the ``'callable'`` key in py:attr:` _media_autoconfiguration` is not ``None``, it
will be called instead (as method on the current model instance) to retrieve the
information with any required logic.
:return: media information dictionary
:rtype: dict
"""
if not self._cached_params:
for pattern in self._media_autoconfiguration['params']:
match = pattern.match(self.media_url)
if match:
if self._media_autoconfiguration['callable']:
self._cached_params = getattr(
self, self._media_autoconfiguration['callable']
)(**match.groupdict())
else:
self._cached_params = match.groupdict()
self._cached_params['url'] = self.media_url
return self._cached_params

@property
def media_url(self):
"""
Public URL of the object on the remote media.
As you will likely have a ``URL`` on the plugin model,
it's usually enough to return that value, but you are free to implement
any way to retrieve it.
:rtype: str
"""
raise NotImplementedError

@property
def media_id(self):
"""
ID of the object on the remote media.
:rtype: str
"""
try:
return self.media_params['media_id']
except KeyError:
return None

def get_main_image(self):
"""
URL of the media cover at maximum resolution
:rtype: str
"""
return self._media_autoconfiguration['main_url'] % self.media_params

def get_thumb_image(self):
"""
URL of the media cover at intermediate resolution
:rtype: str
"""
return self._media_autoconfiguration['thumb_url'] % self.media_params
23 changes: 23 additions & 0 deletions djangocms_blog/migrations/0038_post_media.py
@@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.24 on 2019-09-14 10:45
from __future__ import unicode_literals

import cms.models.fields
from django.db import migrations
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('cms', '0020_old_tree_cleanup'),
('djangocms_blog', '0037_auto_20190806_0743'),
]

operations = [
migrations.AddField(
model_name='post',
name='media',
field=cms.models.fields.PlaceholderField(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='media', slotname='media', to='cms.Placeholder'),
),
]
1 change: 1 addition & 0 deletions djangocms_blog/models.py
Expand Up @@ -243,6 +243,7 @@ class Post(KnockerModel, BlogMetaMixin, TranslatableModel):
configuration='BLOG_POST_TEXT_CKEDITOR'),
meta={'unique_together': (('language_code', 'slug'),)}
)
media = PlaceholderField('media', related_name='media')
content = PlaceholderField('post_content', related_name='post_content')
liveblog = PlaceholderField('live_blog', related_name='live_blog')
enable_liveblog = models.BooleanField(verbose_name=_('enable liveblog on post'),
Expand Down
@@ -1,4 +1,4 @@
{% load i18n easy_thumbnails_tags cms_tags %}
{% load djangocms_blog i18n easy_thumbnails_tags cms_tags %}

<article id="post-{{ post.slug }}" class="post-item">
<header>
Expand All @@ -15,6 +15,11 @@ <h4>{{ post.subtitle }}</h4>
{% thumbnail post.main_image post.thumbnail_options.size crop=post.thumbnail_options.crop upscale=post.thumbnail_options.upscale subject_location=post.main_image.subject_location as thumb %}
<img src="{{ thumb.url }}" alt="{{ post.main_image.default_alt_text }}" width="{{ thumb.width }}" height="{{ thumb.height }}" />
</div>
{% else %}
{% media_images post as previews %}
<div class="blog-visual">
{% for preview in previews %}<img src="{{ preview }}" />{% endfor %}
</div>
{% endif %}
<div class="blog-lead">
{% if not TRUNCWORDS_COUNT %}
Expand Down
4 changes: 3 additions & 1 deletion djangocms_blog/templates/djangocms_blog/post_detail.html
Expand Up @@ -15,7 +15,9 @@ <h3>{% render_model post "subtitle" %}</h3>
{% include "djangocms_blog/includes/blog_meta.html" %}
{% endblock %}
</header>
{% if post.main_image_id %}
{% if not post.main_image_id %}
<div class="blog-visual">{% render_placeholder post.media %}</div>
{% else %}
<div class="blog-visual">
{% thumbnail post.main_image post.full_image_options.size crop=post.full_image_options.crop upscale=post.full_image_options.upscale subject_location=post.main_image.subject_location as thumb %}
<img src="{{ thumb.url }}" alt="{{ post.main_image.default_alt_text }}" width="{{ thumb.width }}" height="{{ thumb.height }}" />
Expand Down
2 changes: 2 additions & 0 deletions djangocms_blog/templatetags/__init__.py
@@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function, unicode_literals
83 changes: 83 additions & 0 deletions djangocms_blog/templatetags/djangocms_blog.py
@@ -0,0 +1,83 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function, unicode_literals

from cms.utils.plugins import get_plugins
from django import template

register = template.Library()


@register.simple_tag(name='media_plugins', takes_context=True)
def media_plugins(context, post):
"""
Extract :py:class:`djangocms_blog.media.base.MediaAttachmentPluginMixin`
plugins from the ``media`` placeholder of the provided post.
They can be rendered with ``render_plugin`` templatetag:
.. code-block: python
{% media_plugins post as media_plugins %}
{% for plugin in media_plugins %}{% render_plugin plugin %}{% endfor %}
:param context: template context
:type context: dict
:param post: post instance
:type post: :py:class:`djangocms_blog.models.Post`
:return: list of :py:class:`djangocms_blog.media.base.MediaAttachmentPluginMixin` plugins
:rtype: List[djangocms_blog.media.base.MediaAttachmentPluginMixin]
"""
request = context['request']
if post.media.get_plugins().exists():
return get_plugins(request, post.media, None)
return []


@register.simple_tag(name='media_images', takes_context=True)
def media_images(context, post, main=True):
"""
Extract images of the given size from all the
:py:class:`djangocms_blog.media.base.MediaAttachmentPluginMixin`
plugins in the ``media`` placeholder of the provided post.
Support ``djangocms-video`` ``poster`` field in case the plugin
does not implement ``MediaAttachmentPluginMixin`` API.
Usage:
.. code-block: python
{% media_images post False as thumbs %}
{% for thumb in thumbs %}<img src="{{ thumb }}/>{% endfor %}
.. code-block: python
{% media_images post as main_images %}
{% for image in main_images %}<img src="{{ image }}/>{% endfor %}
:param context: template context
:type context: dict
:param post: post instance
:type post: :py:class:`djangocms_blog.models.Post`
:param main: retrieve main image or thumbnail
:type main: bool
:return: list of images urls
:rtype: list
"""
plugins = media_plugins(context, post)
if main:
image_method = 'get_main_image'
else:
image_method = 'get_thumb_image'
images = []
for plugin in plugins:
try:
images.append(getattr(plugin, image_method)())
except Exception:
try:
image = getattr(plugin, 'poster')
if image:
images.append(image.url)
except AttributeError:
pass
return images
1 change: 1 addition & 0 deletions docs/features/index.rst
Expand Up @@ -12,6 +12,7 @@ Features
permalinks
templates
shares
media
menu
multisite
related
Expand Down

0 comments on commit 8490dd5

Please sign in to comment.