Attachment rendering for posts & node descriptions.
This commit is contained in:
@@ -7,12 +7,14 @@ _file_embedded_schema = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ATTACHMENT_SLUG_REGEX = '[a-zA-Z0-9_ ]+'
|
||||||
|
|
||||||
_attachments_embedded_schema = {
|
_attachments_embedded_schema = {
|
||||||
'type': 'dict',
|
'type': 'dict',
|
||||||
# TODO: will be renamed to 'keyschema' in Cerberus 1.0
|
# TODO: will be renamed to 'keyschema' in Cerberus 1.0
|
||||||
'propertyschema': {
|
'propertyschema': {
|
||||||
'type': 'string',
|
'type': 'string',
|
||||||
'regex': '^[a-zA-Z0-9_ ]+$',
|
'regex': '^%s$' % ATTACHMENT_SLUG_REGEX,
|
||||||
},
|
},
|
||||||
'valueschema': {
|
'valueschema': {
|
||||||
'type': 'dict',
|
'type': 'dict',
|
||||||
|
@@ -691,7 +691,7 @@ def upgrade_attachment_schema(proj_url=None, all_projects=False):
|
|||||||
nodes = nodes_coll.find({
|
nodes = nodes_coll.find({
|
||||||
'project': project['_id'],
|
'project': project['_id'],
|
||||||
'node_type': {'$in': list(node_type_names)},
|
'node_type': {'$in': list(node_type_names)},
|
||||||
'properties.attachments.0': {'$exists': True},
|
'properties.attachments': {'$exists': True},
|
||||||
})
|
})
|
||||||
for node in nodes:
|
for node in nodes:
|
||||||
log.info(' - Updating schema on node %s (%s)', node['_id'], node.get('name'))
|
log.info(' - Updating schema on node %s (%s)', node['_id'], node.get('name'))
|
||||||
|
88
pillar/web/nodes/attachments.py
Normal file
88
pillar/web/nodes/attachments.py
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
import logging
|
||||||
|
import re
|
||||||
|
|
||||||
|
from bson import ObjectId
|
||||||
|
import flask
|
||||||
|
import pillarsdk
|
||||||
|
|
||||||
|
from pillar.api.node_types import ATTACHMENT_SLUG_REGEX
|
||||||
|
from pillar.web.utils import system_util
|
||||||
|
|
||||||
|
shortcode_re = re.compile(r'@\[(%s)\]' % ATTACHMENT_SLUG_REGEX)
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def render_attachments(node, field_value):
|
||||||
|
"""Renders attachments referenced in the field value.
|
||||||
|
|
||||||
|
Returns the rendered field.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# TODO: cache this based on the node's etag and attachment links expiry.
|
||||||
|
|
||||||
|
node_attachments = node[u'properties'][u'attachments']
|
||||||
|
if isinstance(node_attachments, list):
|
||||||
|
log.warning('Old-style attachments property found on node %s. Ignoring them, '
|
||||||
|
'will result in attachments not being found.', node[u'_id'])
|
||||||
|
return field_value
|
||||||
|
|
||||||
|
if not node_attachments:
|
||||||
|
return field_value
|
||||||
|
|
||||||
|
def replace(match):
|
||||||
|
slug = match.group(1)
|
||||||
|
|
||||||
|
try:
|
||||||
|
att = node_attachments[slug]
|
||||||
|
except KeyError:
|
||||||
|
return u'[attachment "%s" not found]' % slug
|
||||||
|
|
||||||
|
return render_attachment(att)
|
||||||
|
|
||||||
|
return shortcode_re.sub(replace, field_value)
|
||||||
|
|
||||||
|
|
||||||
|
def render_attachment(attachment):
|
||||||
|
"""Renders an attachment as HTML"""
|
||||||
|
|
||||||
|
oid = ObjectId(attachment[u'oid'])
|
||||||
|
collection = attachment.collection or u'files'
|
||||||
|
|
||||||
|
renderers = {
|
||||||
|
'files': render_attachment_file
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
renderer = renderers[collection]
|
||||||
|
except KeyError:
|
||||||
|
log.error(u'Unable to render attachment from collection %s', collection)
|
||||||
|
return u'Unable to render attachment'
|
||||||
|
|
||||||
|
return renderer(oid)
|
||||||
|
|
||||||
|
|
||||||
|
def render_attachment_file(oid):
|
||||||
|
"""Renders a file attachment."""
|
||||||
|
|
||||||
|
api = system_util.pillar_api()
|
||||||
|
sdk_file = pillarsdk.File.find(oid, api=api)
|
||||||
|
|
||||||
|
file_renderers = {
|
||||||
|
'image': render_attachment_file_image
|
||||||
|
}
|
||||||
|
|
||||||
|
mime_type_cat, _ = sdk_file.content_type.split('/', 1)
|
||||||
|
try:
|
||||||
|
renderer = file_renderers[mime_type_cat]
|
||||||
|
except KeyError:
|
||||||
|
return flask.render_template('nodes/attachments/file_generic.html', file=sdk_file)
|
||||||
|
|
||||||
|
return renderer(sdk_file)
|
||||||
|
|
||||||
|
|
||||||
|
def render_attachment_file_image(sdk_file):
|
||||||
|
"""Renders an image file."""
|
||||||
|
|
||||||
|
variations = {var.size: var for var in sdk_file.variations}
|
||||||
|
return flask.render_template('nodes/attachments/file_image.html',
|
||||||
|
file=sdk_file, vars=variations)
|
@@ -14,6 +14,7 @@ from pillar.web.nodes.routes import blueprint
|
|||||||
from pillar.web.nodes.routes import url_for_node
|
from pillar.web.nodes.routes import url_for_node
|
||||||
from pillar.web.nodes.forms import get_node_form
|
from pillar.web.nodes.forms import get_node_form
|
||||||
from pillar.web.nodes.forms import process_node_form
|
from pillar.web.nodes.forms import process_node_form
|
||||||
|
import pillar.web.nodes.attachments
|
||||||
from pillar.web.projects.routes import project_update_nodes_list
|
from pillar.web.projects.routes import project_update_nodes_list
|
||||||
|
|
||||||
|
|
||||||
@@ -53,6 +54,9 @@ def posts_view(project_id=None, project_url=None, url=None):
|
|||||||
if not (current_user.is_authenticated and post.has_method('PUT')):
|
if not (current_user.is_authenticated and post.has_method('PUT')):
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
||||||
|
post['properties']['content'] = pillar.web.nodes.attachments.render_attachments(
|
||||||
|
post, post['properties']['content'])
|
||||||
|
|
||||||
return render_template(
|
return render_template(
|
||||||
'nodes/custom/post/view.html',
|
'nodes/custom/post/view.html',
|
||||||
blog=blog,
|
blog=blog,
|
||||||
@@ -72,6 +76,9 @@ def posts_view(project_id=None, project_url=None, url=None):
|
|||||||
for post in posts._items:
|
for post in posts._items:
|
||||||
post.picture = get_file(post.picture, api=api)
|
post.picture = get_file(post.picture, api=api)
|
||||||
|
|
||||||
|
post['properties']['content'] = pillar.web.nodes.attachments.render_attachments(
|
||||||
|
post, post['properties']['content'])
|
||||||
|
|
||||||
return render_template(
|
return render_template(
|
||||||
'nodes/custom/blog/index.html',
|
'nodes/custom/blog/index.html',
|
||||||
node_type_post=node_type_post,
|
node_type_post=node_type_post,
|
||||||
|
@@ -22,6 +22,7 @@ from wtforms import SelectMultipleField
|
|||||||
from flask_login import login_required
|
from flask_login import login_required
|
||||||
from jinja2.exceptions import TemplateNotFound
|
from jinja2.exceptions import TemplateNotFound
|
||||||
|
|
||||||
|
import pillar.web.nodes.attachments
|
||||||
from pillar.web.utils import caching
|
from pillar.web.utils import caching
|
||||||
from pillar.web.nodes.forms import get_node_form
|
from pillar.web.nodes.forms import get_node_form
|
||||||
from pillar.web.nodes.forms import process_node_form
|
from pillar.web.nodes.forms import process_node_form
|
||||||
@@ -260,6 +261,8 @@ def _view_handler_asset(node, template_path, template_action, link_allowed):
|
|||||||
# Treat it as normal file (zip, blend, application, etc)
|
# Treat it as normal file (zip, blend, application, etc)
|
||||||
asset_type = 'file'
|
asset_type = 'file'
|
||||||
|
|
||||||
|
node['description'] = pillar.web.nodes.attachments.render_attachments(node, node['description'])
|
||||||
|
|
||||||
template_path = os.path.join(template_path, asset_type)
|
template_path = os.path.join(template_path, asset_type)
|
||||||
|
|
||||||
return template_path, template_action
|
return template_path, template_action
|
||||||
|
1
src/templates/nodes/attachments/file_generic.jade
Normal file
1
src/templates/nodes/attachments/file_generic.jade
Normal file
@@ -0,0 +1 @@
|
|||||||
|
a.attachment(href="{{ file.link }}") {{ file.filename }}
|
1
src/templates/nodes/attachments/file_image.jade
Normal file
1
src/templates/nodes/attachments/file_image.jade
Normal file
@@ -0,0 +1 @@
|
|||||||
|
img(src="{{ vars['l'].link }}",alt="{{ file.filename }}")
|
Reference in New Issue
Block a user