Generate project_navigation_links
This function generates a list of selected links for important nodes such as Pages and Blog. This list of links is used in the templates to provide high level navigation of a Project.
This commit is contained in:
parent
c795015a3c
commit
9a9d15ce47
@ -94,6 +94,16 @@ def find_for_post(project, node):
|
|||||||
url=node.properties.url)
|
url=node.properties.url)
|
||||||
|
|
||||||
|
|
||||||
|
@register_node_finder('page')
|
||||||
|
def find_for_page(project, node):
|
||||||
|
"""Returns the URL for a page."""
|
||||||
|
|
||||||
|
project_id = project['_id']
|
||||||
|
|
||||||
|
the_project = project_url(project_id, project=project)
|
||||||
|
return url_for('projects.view_node', project_url=the_project.url, node_id=node.properties.url)
|
||||||
|
|
||||||
|
|
||||||
def find_for_other(project, node):
|
def find_for_other(project, node):
|
||||||
"""Fallback: Assets, textures, and other node types.
|
"""Fallback: Assets, textures, and other node types.
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@ from pillar import current_app
|
|||||||
from pillar.api.utils import utcnow
|
from pillar.api.utils import utcnow
|
||||||
from pillar.web import system_util
|
from pillar.web import system_util
|
||||||
from pillar.web import utils
|
from pillar.web import utils
|
||||||
|
from pillar.web.nodes import finders
|
||||||
from pillar.web.utils.jstree import jstree_get_children
|
from pillar.web.utils.jstree import jstree_get_children
|
||||||
import pillar.extension
|
import pillar.extension
|
||||||
|
|
||||||
@ -302,6 +303,52 @@ def view(project_url):
|
|||||||
'header_video_node': header_video_node})
|
'header_video_node': header_video_node})
|
||||||
|
|
||||||
|
|
||||||
|
def project_navigation_links(project, api) -> list:
|
||||||
|
"""Returns a list of nodes for the project, for top navigation display.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
project: A Project object.
|
||||||
|
api: the api client credential.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A list of links for the Project.
|
||||||
|
For example we display a link to the project blog if present, as well
|
||||||
|
as pages. The list is structured as follows:
|
||||||
|
|
||||||
|
[{'url': '/p/spring/about', 'label': 'About'},
|
||||||
|
{'url': '/p/spring/blog', 'label': 'Blog'}]
|
||||||
|
"""
|
||||||
|
|
||||||
|
links = []
|
||||||
|
|
||||||
|
# Fetch the blog
|
||||||
|
blog = Node.find_first({
|
||||||
|
'where': {'project': project._id, 'node_type': 'blog', '_deleted': {'$ne': True}},
|
||||||
|
'projection': {
|
||||||
|
'name': 1,
|
||||||
|
}
|
||||||
|
}, api=api)
|
||||||
|
|
||||||
|
if blog:
|
||||||
|
links.append({'url': finders.find_url_for_node(blog), 'label': blog.name})
|
||||||
|
|
||||||
|
# Fetch pages
|
||||||
|
pages = Node.all({
|
||||||
|
'where': {'project': project._id, 'node_type': 'page', '_deleted': {'$ne': True}},
|
||||||
|
'projection': {
|
||||||
|
'name': 1,
|
||||||
|
'properties.url': 1
|
||||||
|
}
|
||||||
|
}, api=api)
|
||||||
|
|
||||||
|
# Process the results and append the links to the list
|
||||||
|
for p in pages._items:
|
||||||
|
|
||||||
|
links.append({'url': finders.find_url_for_node(p), 'label': p.name})
|
||||||
|
|
||||||
|
return links
|
||||||
|
|
||||||
|
|
||||||
def render_project(project, api, extra_context=None, template_name=None):
|
def render_project(project, api, extra_context=None, template_name=None):
|
||||||
project.picture_square = utils.get_file(project.picture_square, api=api)
|
project.picture_square = utils.get_file(project.picture_square, api=api)
|
||||||
project.picture_header = utils.get_file(project.picture_header, api=api)
|
project.picture_header = utils.get_file(project.picture_header, api=api)
|
||||||
@ -370,13 +417,7 @@ def render_project(project, api, extra_context=None, template_name=None):
|
|||||||
|
|
||||||
extension_sidebar_links = current_app.extension_sidebar_links(project)
|
extension_sidebar_links = current_app.extension_sidebar_links(project)
|
||||||
|
|
||||||
pages = Node.all({
|
navigation_links = project_navigation_links(project, api)
|
||||||
'where': {'project': project._id, 'node_type': 'page', '_deleted': False},
|
|
||||||
'projection': {
|
|
||||||
'name': 1,
|
|
||||||
'url': 1
|
|
||||||
}
|
|
||||||
}, api=api)
|
|
||||||
|
|
||||||
return render_template(template_name,
|
return render_template(template_name,
|
||||||
api=api,
|
api=api,
|
||||||
@ -386,7 +427,7 @@ def render_project(project, api, extra_context=None, template_name=None):
|
|||||||
show_project=True,
|
show_project=True,
|
||||||
og_picture=project.picture_header,
|
og_picture=project.picture_header,
|
||||||
activity_stream=activity_stream,
|
activity_stream=activity_stream,
|
||||||
pages=pages._items,
|
navigation_links=navigation_links,
|
||||||
extension_sidebar_links=extension_sidebar_links,
|
extension_sidebar_links=extension_sidebar_links,
|
||||||
**extra_context)
|
**extra_context)
|
||||||
|
|
||||||
@ -456,16 +497,14 @@ def view_node(project_url, node_id):
|
|||||||
|
|
||||||
# Append _theatre to load the proper template
|
# Append _theatre to load the proper template
|
||||||
theatre = '_theatre' if theatre_mode else ''
|
theatre = '_theatre' if theatre_mode else ''
|
||||||
|
navigation_links = project_navigation_links(project, api)
|
||||||
|
|
||||||
if node.node_type == 'page':
|
if node.node_type == 'page':
|
||||||
pages = Node.all({
|
|
||||||
'where': {'project': project._id, 'node_type': 'page'},
|
|
||||||
'projection': {'name': 1}}, api=api)
|
|
||||||
return render_template('nodes/custom/page/view_embed.html',
|
return render_template('nodes/custom/page/view_embed.html',
|
||||||
api=api,
|
api=api,
|
||||||
node=node,
|
node=node,
|
||||||
project=project,
|
project=project,
|
||||||
pages=pages._items,
|
navigation_links=navigation_links,
|
||||||
og_picture=og_picture,)
|
og_picture=og_picture,)
|
||||||
|
|
||||||
extension_sidebar_links = current_app.extension_sidebar_links(project)
|
extension_sidebar_links = current_app.extension_sidebar_links(project)
|
||||||
@ -477,6 +516,7 @@ def view_node(project_url, node_id):
|
|||||||
show_node=True,
|
show_node=True,
|
||||||
show_project=False,
|
show_project=False,
|
||||||
og_picture=og_picture,
|
og_picture=og_picture,
|
||||||
|
navigation_links=navigation_links,
|
||||||
extension_sidebar_links=extension_sidebar_links)
|
extension_sidebar_links=extension_sidebar_links)
|
||||||
|
|
||||||
|
|
||||||
|
107
tests/test_web/test_projects.py
Normal file
107
tests/test_web/test_projects.py
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
from bson import ObjectId
|
||||||
|
|
||||||
|
from pillar.tests import AbstractPillarTest
|
||||||
|
from pillar.tests import common_test_data as ctd
|
||||||
|
from pillar.web import system_util
|
||||||
|
from pillar.web.projects.routes import project_navigation_links, find_project_or_404
|
||||||
|
|
||||||
|
|
||||||
|
class ProjectTest(AbstractPillarTest):
|
||||||
|
def setUp(self, **kwargs):
|
||||||
|
super().setUp(**kwargs)
|
||||||
|
|
||||||
|
self.pid, self.project = self.ensure_project_exists()
|
||||||
|
self.owner_uid = self.create_user(24 * 'a',
|
||||||
|
groups=[ctd.EXAMPLE_ADMIN_GROUP_ID],
|
||||||
|
token='admin-token')
|
||||||
|
|
||||||
|
# Create a Node of type page.
|
||||||
|
self.node_id = self.create_node({
|
||||||
|
'_id': ObjectId('572761099837730efe8e120d'),
|
||||||
|
'description': 'This the about page',
|
||||||
|
'node_type': 'page',
|
||||||
|
'user': self.owner_uid,
|
||||||
|
'properties': {
|
||||||
|
'status': 'published',
|
||||||
|
'url': 'about',
|
||||||
|
},
|
||||||
|
'name': 'About',
|
||||||
|
'project': self.pid,
|
||||||
|
})
|
||||||
|
|
||||||
|
self.user_uid = self.create_user(24 * 'b', groups=[ctd.EXAMPLE_ADMIN_GROUP_ID],
|
||||||
|
token='user-token')
|
||||||
|
|
||||||
|
def test_project_navigation_links_one_page(self):
|
||||||
|
"""Test link generation for project navigation."""
|
||||||
|
with self.app.test_request_context():
|
||||||
|
api = system_util.pillar_api()
|
||||||
|
project = find_project_or_404(self.project['url'], api=api)
|
||||||
|
navigation_links = project_navigation_links(project, api=api)
|
||||||
|
|
||||||
|
# We expect only one link to be in the list
|
||||||
|
links = [{'url': '/p/default-project/about', 'label': 'About'}]
|
||||||
|
self.assertListEqual(links, navigation_links)
|
||||||
|
|
||||||
|
def test_project_navigation_links_pages_and_blog(self):
|
||||||
|
"""Test link generation for a project with two Pages and one Blog."""
|
||||||
|
# Add one more page to the project
|
||||||
|
self.create_node({
|
||||||
|
'_id': ObjectId(),
|
||||||
|
'description': 'This the awards page',
|
||||||
|
'node_type': 'page',
|
||||||
|
'user': self.owner_uid,
|
||||||
|
'properties': {
|
||||||
|
'status': 'published',
|
||||||
|
'url': 'awards',
|
||||||
|
},
|
||||||
|
'name': 'Awards',
|
||||||
|
'project': self.pid,
|
||||||
|
})
|
||||||
|
# Create a Node of type blog.
|
||||||
|
self.create_node({
|
||||||
|
'_id': ObjectId(),
|
||||||
|
'description': 'This the blog page',
|
||||||
|
'node_type': 'blog',
|
||||||
|
'user': self.owner_uid,
|
||||||
|
'properties': {
|
||||||
|
'status': 'published',
|
||||||
|
},
|
||||||
|
'name': 'Blog',
|
||||||
|
'project': self.pid,
|
||||||
|
})
|
||||||
|
# Create a Node of type asset
|
||||||
|
self.create_node({
|
||||||
|
'_id': ObjectId(),
|
||||||
|
'description': 'This is an asset without file',
|
||||||
|
'node_type': 'asset',
|
||||||
|
'user': self.owner_uid,
|
||||||
|
'properties': {
|
||||||
|
'status': 'published',
|
||||||
|
'content_type': 'image',
|
||||||
|
},
|
||||||
|
'name': 'Image test',
|
||||||
|
'project': self.pid,
|
||||||
|
})
|
||||||
|
|
||||||
|
with self.app.test_request_context():
|
||||||
|
api = system_util.pillar_api()
|
||||||
|
project = find_project_or_404(self.project['url'], api=api)
|
||||||
|
navigation_links = project_navigation_links(project, api=api)
|
||||||
|
expected_list = [
|
||||||
|
{'url': '/blog/', 'label': 'Blog'}, # Blog is the first element of the list (since it's added first)
|
||||||
|
{'url': '/p/default-project/about', 'label': 'About'},
|
||||||
|
{'url': '/p/default-project/awards', 'label': 'Awards'}]
|
||||||
|
|
||||||
|
self.assertListEqual(expected_list, navigation_links)
|
||||||
|
|
||||||
|
def test_project_no_navigation_links(self):
|
||||||
|
"""Test link generation in a project with no Pages or Blog."""
|
||||||
|
with self.app.test_request_context():
|
||||||
|
# Delete the existing page from the database
|
||||||
|
self.app.db('nodes').delete_one({'_id': ObjectId('572761099837730efe8e120d')})
|
||||||
|
api = system_util.pillar_api()
|
||||||
|
project = find_project_or_404(self.project['url'], api=api)
|
||||||
|
navigation_links = project_navigation_links(project, api=api)
|
||||||
|
# This should result in an empty list
|
||||||
|
self.assertListEqual([], navigation_links)
|
Loading…
x
Reference in New Issue
Block a user