178 lines
6.1 KiB
Python
178 lines
6.1 KiB
Python
import functools
|
|
import logging
|
|
|
|
from pillarsdk import Node
|
|
from pillarsdk import Project
|
|
from pillarsdk.exceptions import ResourceNotFound
|
|
from flask import abort, url_for
|
|
from flask import current_app
|
|
from flask import render_template
|
|
from flask import redirect
|
|
from flask_login import login_required, current_user
|
|
from pillar.web.utils import system_util
|
|
from pillar.web.utils import attach_project_pictures
|
|
from pillar.web.utils import get_file
|
|
from pillar.web.utils import current_user_is_authenticated
|
|
|
|
from pillar.web.nodes.routes import blueprint
|
|
from pillar.web.nodes.routes import url_for_node
|
|
from pillar.web.nodes.forms import get_node_form
|
|
import pillar.web.nodes.attachments
|
|
from pillar.web.projects.routes import project_update_nodes_list
|
|
from pillar.web.projects.routes import project_navigation_links
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
|
# Cached, see setup_app() below.
|
|
def posts_view(project_id=None, project_url=None, url=None, *, archive=False, page=1):
|
|
"""View individual blogpost"""
|
|
|
|
if bool(project_id) == bool(project_url):
|
|
raise ValueError('posts_view(): pass either project_id or project_url')
|
|
|
|
if url and archive:
|
|
raise ValueError('posts_view(): cannot pass both url and archive')
|
|
|
|
api = system_util.pillar_api()
|
|
|
|
# Fetch project (for background images and links generation)
|
|
if project_id:
|
|
project = Project.find(project_id, api=api)
|
|
else:
|
|
project = Project.find_one({'where': {'url': project_url}}, api=api)
|
|
project_id = project['_id']
|
|
|
|
attach_project_pictures(project, api)
|
|
|
|
blog = Node.find_one({
|
|
'where': {'node_type': 'blog', 'project': project_id},
|
|
}, api=api)
|
|
|
|
status_query = {} if blog.has_method('PUT') else {'properties.status': 'published'}
|
|
posts = Node.all({
|
|
'where': {'parent': blog._id, **status_query},
|
|
'embedded': {'user': 1},
|
|
'sort': '-_created',
|
|
'max_results': 20 if archive else 5,
|
|
'page': page,
|
|
}, api=api)
|
|
|
|
for post in posts._items:
|
|
post.picture = get_file(post.picture, api=api)
|
|
post.url = url_for_node(node=post)
|
|
|
|
index_arch = 'archive' if archive else 'index'
|
|
template_path = f'nodes/custom/blog/{index_arch}.html',
|
|
|
|
if url:
|
|
post = Node.find_one({
|
|
'where': {'parent': blog._id, 'properties.url': url},
|
|
'embedded': {'node_type': 1, 'user': 1},
|
|
}, api=api)
|
|
|
|
if post.picture:
|
|
post.picture = get_file(post.picture, api=api)
|
|
|
|
post.url = url_for_node(node=post)
|
|
elif posts._items:
|
|
post = posts._items[0]
|
|
else:
|
|
post = None
|
|
|
|
if post is not None:
|
|
# If post is not published, check that the user is also the author of
|
|
# the post. If not, return an error.
|
|
if post.properties.status != "published":
|
|
if not (current_user.is_authenticated and post.has_method('PUT')):
|
|
abort(403)
|
|
|
|
can_create_blog_posts = project.node_type_has_method('post', 'POST', api=api)
|
|
|
|
# Use functools.partial so we can later pass page=X.
|
|
is_main_project = project_id == current_app.config['MAIN_PROJECT_ID']
|
|
if is_main_project:
|
|
url_func = functools.partial(url_for, 'main.main_blog_archive')
|
|
else:
|
|
url_func = functools.partial(url_for, 'main.project_blog_archive', project_url=project.url)
|
|
|
|
project.blog_archive_url = url_func()
|
|
pmeta = posts._meta
|
|
seen_now = pmeta['max_results'] * pmeta['page']
|
|
if pmeta['total'] > seen_now:
|
|
project.blog_archive_next = url_func(page=pmeta['page'] + 1)
|
|
else:
|
|
project.blog_archive_next = None
|
|
if pmeta['page'] > 1:
|
|
project.blog_archive_prev = url_func(page=pmeta['page'] - 1)
|
|
else:
|
|
project.blog_archive_prev = None
|
|
|
|
navigation_links = project_navigation_links(project, api)
|
|
|
|
return render_template(
|
|
template_path,
|
|
blog=blog,
|
|
node=post, # node is used by the generic comments rendering (see custom/_scripts.pug)
|
|
posts=posts._items,
|
|
posts_meta=pmeta,
|
|
more_posts_available=pmeta['total'] > pmeta['max_results'],
|
|
project=project,
|
|
node_type_post=project.get_node_type('post'),
|
|
can_create_blog_posts=can_create_blog_posts,
|
|
navigation_links=navigation_links,
|
|
api=api)
|
|
|
|
|
|
@blueprint.route("/posts/<project_id>/create", methods=['GET', 'POST'])
|
|
@login_required
|
|
def posts_create(project_id):
|
|
api = system_util.pillar_api()
|
|
try:
|
|
project = Project.find(project_id, api=api)
|
|
except ResourceNotFound:
|
|
return abort(404)
|
|
attach_project_pictures(project, api)
|
|
|
|
blog = Node.find_one({
|
|
'where': {'node_type': 'blog', 'project': project_id}}, api=api)
|
|
node_type = project.get_node_type('post')
|
|
# Check if user is allowed to create a post in the blog
|
|
if not project.node_type_has_method('post', 'POST', api=api):
|
|
return abort(403)
|
|
form = get_node_form(node_type)
|
|
if form.validate_on_submit():
|
|
# Create new post object from scratch
|
|
post_props = dict(
|
|
node_type='post',
|
|
name=form.name.data,
|
|
picture=form.picture.data,
|
|
user=current_user.objectid,
|
|
parent=blog._id,
|
|
project=project._id,
|
|
properties=dict(
|
|
content=form.content.data,
|
|
status=form.status.data,
|
|
url=form.url.data))
|
|
if form.picture.data == '':
|
|
post_props['picture'] = None
|
|
post = Node(post_props)
|
|
post.create(api=api)
|
|
# Only if the node is set as published, push it to the list
|
|
if post.properties.status == 'published':
|
|
project_update_nodes_list(post, project_id=project._id, list_name='blog')
|
|
return redirect(url_for_node(node=post))
|
|
form.parent.data = blog._id
|
|
return render_template('nodes/custom/post/create.html',
|
|
node_type=node_type,
|
|
form=form,
|
|
project=project,
|
|
api=api)
|
|
|
|
|
|
def setup_app(app):
|
|
global posts_view
|
|
|
|
memoize = app.cache.memoize(timeout=3600, unless=current_user_is_authenticated)
|
|
posts_view = memoize(posts_view)
|