Compare commits

...
This repository has been archived on 2023-02-21. You can view files and clone it, but cannot push or open issues or pull requests.

3 Commits

9 changed files with 138 additions and 67 deletions

8
README
View File

@ -108,7 +108,9 @@ The config dictonnary can contain the following keys:
content of the dictionnary is described later,
- `template`, the template in which to render the data sources, it
will receive a variable named `data_sources` in its context
containing property named after the `slug` field of each source.
containing property named after the `slug` field of each source,
- `accepted_http_status`, a list of integers giving the accepted HTTP status,
default is `[200]`.
A source definition is a dictionnary containing the following keys:
- `slug`, the field name to hold this source parsed value in the
@ -164,7 +166,9 @@ A source definition is a dictionnary containing the following keys:
present,
- `user_context`, whether the user must be part of the cache key. For retro
compatibility If authentication mechanism is OAuth2, it defaults to True
otherwise to False.
otherwise to False,
- `accepted_http_status`, a list of integers giving the accepted HTTP status,
default is taken from the renderer level.
Exemple with the JSON parser
----------------------------

View File

@ -1,9 +1,12 @@
import logging
from django.conf import settings
from django.utils.translation import ugettext_lazy as _
from cms.plugin_pool import plugin_pool
from cms.plugin_base import CMSPluginBase
from . import models
from . import models, utils
class BlurpPlugin(CMSPluginBase):
name = _('Blurp Plugin')
@ -12,26 +15,18 @@ class BlurpPlugin(CMSPluginBase):
render_template = ''
def render(self, context, instance, placeholder):
renderer = instance.get_renderer()
request = context.get('request')
ajax = context.get('ajaxy', True) and renderer.config.get('ajax', False)
if not ajax:
self.render_template = renderer.render_template()
return renderer.render(context)
else:
request = context.get('request')
context['plugin_id'] = instance.id
context['ajax_refresh'] = renderer.config.get('ajax_refresh', 0)
if request.GET:
context['plugin_args'] = '?{0}'.format(request.GET.urlencode())
# hack alert !!
self.render_template = 'cmsplugin_blurp/ajax.html'
logger = logging.getLogger(__name__)
try:
slug = instance.name
template, context = utils.render_blurp(context, slug)
self.render_template = template
return context
except Exception, e:
logger.exception('error while rendering blurp %s', slug)
if settings.TEMPLATE_DEBUG:
msg = 'error while rendering blurp %s: %s' % (slug, e)
self.render_template = 'cmsplugin_blurp/error.html'
return {'content': msg}
plugin_pool.register_plugin(BlurpPlugin)

View File

@ -40,3 +40,6 @@ class BaseRenderer(object):
def render_template(self):
'''Return a template path or a Template object'''
pass
def use_ajax(self, context):
return self.config.get('ajax', False)

View File

@ -37,6 +37,7 @@ class Renderer(template.TemplateRenderer):
'timeout': 10, # optional default is 1, it cannot be less than 1
'refresh': 3600, # optional, default is taken from the renderer level
'limit': 0, # optional, default is taken from the renderer level
'accepted_http_status': [200,400], # optional, default it taken from the renderer level
},
]
'template_name': 'data_from_xyz.html'
@ -51,9 +52,11 @@ class Renderer(template.TemplateRenderer):
# not limited if it is 0
# you can also override it in each source
'limit': 0, # optional default is 0
'accepted_http_status': [200,400], # optional, default is [200]
}
'''
__sources = None
@classmethod
def check_config(cls, config):
@ -78,12 +81,17 @@ class Renderer(template.TemplateRenderer):
yield 'missing signature_key string'
def get_sources(self, context):
for source in self.config['sources']:
slug = '{0}.{1}'.format(self.slug, source['slug'])
data = Data(slug, self.config, source, context)
yield source['slug'], data
if settings.TEMPLATE_DEBUG:
yield 'blurp_debug__', '\n'.join(self.debug_content(context))
if self.__sources is None:
sources = []
for source in self.config['sources']:
slug = '{0}.{1}'.format(self.slug, source['slug'])
data = Data(slug, self.config, source, context)
sources.append((source['slug'], data))
if settings.TEMPLATE_DEBUG:
sources.append(('blurp_debug__',
'\n'.join(self.debug_content(context))))
self.__sources = sources
return self.__sources
def debug_content(self, context):
try:
@ -100,6 +108,15 @@ class Renderer(template.TemplateRenderer):
except Exception, e:
yield u'slug {0!r}: pformat failed {1!r}'.format(slug, e)
def use_ajax(self, context):
'''Only use ajax if some content is not cached'''
if super(Renderer, self).use_ajax(context):
for source in self.get_sources():
if not source.content_is_cached():
return False
else:
return False
def render(self, context):
for slug, source in self.get_sources(context):
context[slug] = source
@ -141,6 +158,8 @@ class Data(object):
self.signature_key = source.get('signature_key')
self.parser_type = source.get('parser_type', 'raw')
self.content_type = source.get('content_type', self.MAPPING[self.parser_type])
self.accepted_http_status = source.get('accepted_http_status',
config.get('accepted_http_status', [200]))
self.user_context = source.get('user_context',
config.get('user_context', self.auth_mech == 'oauth2'))
pre_hash = 'datasource-{self.slug}-{self.url}-{self.limit}-' \
@ -195,7 +214,8 @@ class Data(object):
allow_redirects=self.redirects,
timeout=self.timeout,
stream=True)
request.raise_for_status()
if request.status_code not in self.accepted_http_status:
request.raise_for_status()
return request.raw, None
except HTTPError:
error = 'HTTP Error %s when loading URL %s for renderer %r' % (
@ -271,6 +291,23 @@ class Data(object):
UPDATE_THREADS = {}
CONDITIONS = {}
def content_is_cached(self, ignore_stale_content=True):
'''Test if some content is in cache'''
if self.__content is self.__CACHE_SENTINEL:
content, until = cache.get(self.key, (self.__CACHE_SENTINEL, None))
if not ignore_stale_content:
self.__content = content
if until is not None and ignore_stale_content and until < self.now:
return False
if self.refresh <= 0:
return False
if self.request and 'updatecache' in self.request.GET:
return False
self.__content = content
return True
else:
return True
def get_content(self):
if self.__content is not self.__CACHE_SENTINEL:
return self.__content

View File

@ -7,13 +7,13 @@
if ($ == undefined) {
alert('jQuery is missing');
} else {
var $container = $("#block-plugin-ajax-{{ plugin_id }}");
var $container = $("#block-plugin-ajax-{{ slug }}");
var ajax_refresh = {{ ajax_refresh }};
var exponential_refresh = ajax_refresh;
var reload = function () {
$container.removeClass('block-plugin-ajax-failed');
$container.addClass('block-plugin-ajax-loading');
$.getJSON('{% url 'ajax_render' plugin_id=plugin_id %}{{ plugin_args|safe }}', function (result) {
$.getJSON('{% url 'ajax_render' slug=slug %}{{ plugin_args|safe }}', function (result) {
$container.html(result.content);
$container.removeClass('block-plugin-ajax-loading');
if (ajax_refresh > 0) {
@ -38,7 +38,7 @@
</script>
{% endaddtoblock %}
<div id="block-plugin-ajax-{{ plugin_id }}" class='block-plugin-ajax block-plugin-ajax-loading'>
<div id="block-plugin-ajax-{{ slug }}" class='block-plugin-ajax block-plugin-ajax-loading'>
<div class="block-plugin-ajax-placeholder">{% trans "loading..." %}</div>
</div>

View File

@ -32,25 +32,22 @@ class RenderBlurp(Tag):
)
def render_tag(self, context, name):
renderer = utils.resolve_renderer(name)
if not renderer:
return ''
template = renderer.render_template()
context = renderer.render(context)
def render_tag(self, context, slug):
context.push()
try:
if not hasattr(template, 'render'):
template = template.Template(template)
return template.render(context)
except Exception, e:
logging.getLogger(__name__).exception('error while rendering %s',
renderer)
msg = ''
if settings.TEMPLATE_DEBUG:
msg += 'error while rendering %s: %s' % (renderer, e)
msg = '<pre>%s</pre>' % escape(msg)
return msg
logger = logging.getLogger(__name__)
try:
template, context = utils.render_blurp(context, slug)
return template.render(context)
except Exception, e:
logger.exception('error while rendering blurp %s', slug)
msg = ''
if settings.TEMPLATE_DEBUG:
msg += 'error while rendering blurp %s: %s' % (slug, e)
msg = '<pre>%s</pre>' % escape(msg)
return msg
finally:
context.pop()
@register.tag
class BlurpNode(Tag):
@ -60,9 +57,11 @@ class BlurpNode(Tag):
Argument('name'),
blocks=[('endblurp', 'nodelist')])
def render_tag(self, context, name, nodelist):
def render_tag(self, context, slug, nodelist):
context.push()
utils.insert_blurp_in_context(name, context)
output = self.nodelist.render(context)
context.pop()
return output
try:
utils.insert_blurp_in_context(slug, context)
output = self.nodelist.render(context)
return output
finally:
context.pop()

View File

@ -3,7 +3,7 @@ from django.conf.urls import patterns, url
from . import views
urlpatterns = patterns('',
url(r'^block-plugin-async/(?P<plugin_id>\d+)/$',
url(r'^block-plugin-async/(?P<slug>\w+)/$',
views.ajax_render,
name='ajax_render')
)

View File

@ -1,5 +1,8 @@
import logging
from django.utils.importlib import import_module
from django.utils.translation import ugettext_lazy as _
from django.template.loader import get_template
from . import app_settings
@ -34,3 +37,26 @@ def insert_blurp_in_context(name, context):
if not renderer:
return ''
return renderer.render(context)
def render_blurp(context, slug, ajax=True):
logger = logging.getLogger(__name__)
renderer = resolve_renderer(slug)
if not renderer:
logger.warning('renderer %s does not exist', slug)
return None, None
# Ajax rendering is canceled in the ajax view
ajax = ajax and renderer.use_ajax(context)
if ajax:
request = context.get('request')
context['slug'] = renderer.slug
context['ajax_refresh'] = renderer.config.get('ajax_refresh', 0)
if request.GET:
context['plugin_args'] = '?{0}'.format(request.GET.urlencode())
# hack alert !!
template = get_template('cmsplugin_blurp/ajax.html', context)
else:
template = renderer.render_template()
if not hasattr(template, 'render'):
template = template.Template(template)
context = renderer.render(context)
return template, context

View File

@ -2,20 +2,27 @@ import json
import logging
from django.http import HttpResponse
from django.template import RequestContext, loader
from django.shortcuts import get_object_or_404
from django.template import RequestContext
from django.utils.html import escape
from django.conf import settings
from cms.models import CMSPlugin
from . import utils
logger = logging.getLogger(__name__)
def ajax_render(request, plugin_id):
def ajax_render(request, slug):
context = RequestContext(request)
context['ajaxy'] = False
plugin = get_object_or_404(CMSPlugin, pk=plugin_id)
rendered = plugin.render_plugin(context)
# use another template to render accumulated js and css declarations from sekizai
content = loader.render_to_string('cmsplugin_blurp/sekizai_render.html',
{'content': rendered}, context)
return HttpResponse(json.dumps({'content': content}))
template, context = utils.render_blurp(context, slug, ajax=False)
result = {}
try:
template, context = utils.render_blurp(context, slug)
result['content'] = template.render(context)
except Exception, e:
logger.exception('error while rendering blurp %s', slug)
msg = ''
result['error'] = msg
if settings.TEMPLATE_DEBUG:
msg += 'error while rendering blurp %s: %s' % (slug, e)
result['content'] = '<pre>%s</pre>' % escape(msg)
return HttpResponse(json.dumps(result))