We've a site built in Django-CMS and have developed a mobile version with alternative CSS to suit the smaller viewing area. As well as the usual navigation bar we want to include Next and Previous page links at the bottom of each page.
I know how to output the current page's siblings using this code:
{% show_menu current_page.level %}
What is the easiest way to output links to the next and previous page?
You can use {{ request.current_page.get_next_sibling }} and {{ request.current_page.get_previous_sibling }} in your templates to show the 'neighbor' pages (not that either or b
You can use the methods get_next_filtered_sibling and get_previous_filtered_sibling - but probably only for newer versions of the django cms.
Here are two template tags that return page objects which you might want to feed into the {% page_url ... %} template tag.
#register.assignment_tag(takes_context=True)
def get_next_page(context):
current_page = context['request'].current_page
return current_page.get_next_filtered_sibling(
publisher_is_draft=False
)
#register.assignment_tag(takes_context=True)
def get_prev_page(context):
current_page = context['request'].current_page
return current_page.get_previous_filtered_sibling(
publisher_is_draft=False
)
Related
I want to display a plain django-cms menu. I override the default menu/menu.html template, as I want to display the page's title, alongside the page's menu title. This is for a content navigation, where the additional info of the title is useful.
The default is (in the <a></a>):
{{ child.get_menu_title }}.
What I want is
{{ child.get_menu_title }}<span>{{ child.the_page_title }}</span>
But, somehow, I cant display the title alongside the menu_title. If the field menu_title is set, it overrides the title attribute of the NavigationNode, and it is returned when calling get_menu_title (obviously). Also, the title is not in the attr (NavigationNode attr).
I just ended using
{% load cms_tags %} {% page_attribute 'title' child.id %}
This might not be ideal concerning performance, but works very well. Open but for better solutions!
I have an ACF field that allows a choice of post types, these are then going to be put in a carousel. There could be any number as every module on the page is controlled by the admin users.
In the carousel template, I want to be able to get the recent posts for the selected post type. I can to this with:
{% set items = fn('get_posts', {'post_type': 'team' }) %}
Is there a way to do this without calling it as a function? I was thinking along the lines of:
{% set items = Posts(params) %}
Is this possible or is the function call the only/best way?
Thanks
Currently, what you want to do is not possible. You could use Timber::get_posts through the array notation to get an array of Timber posts instead of regular WordPress posts:
{% set items = fn(['Timber\Timber', 'get_posts'], { 'post_type': 'team' }) %}
But in the future, the recommended way to get posts will be to use Timber\PostQuery. In the upcoming version 2 of Timber, we will add a PostQuery function to Twig. This means that you’ll be able to do something like this:
{% for items in PostQuery(params) %}
{# Display item #}
{% endfor }
This would work well for simpler use cases. I’d also recommend what Luckyfella said, it may be better to not have this in Twig at all, but prepare everything in PHP and then pass the items on to the Twig view.
I've got a Twig base template like so:
{% include "./partials/navigation.html" %}
<!-- Main Wrapper -->
<div id="wrapper">
{% block content %}{% endblock content %}
</div>
I also have a route controller which is outputting the response content to the page using twig:
return template->render('path/to/teplate', args());
where args[] array is all the data needed for this bit: (different on every page)
{% block content %}{% endblock content %}
However my sidebar is being built separately through a menu builder class, and now it needs to render the results of building the menu to my template and populating ./partials/navigation.html.
An easy solution for me is to just append the results of the Menu Builder to the returned response of every controller (I can use a base controller class to do this as this menu appears on every page). However something about this feels unclean as if I have to render the same header/footer every time as well I'll have to be append all 3 outputs to the response. (maybe that is okay?)
Is there a better way of rendering several includes worth of content which each need their own DB lookups and other server-side logic?
Yes. They are called sub requests. Read the documentation here.
I am desperately trying to render a submenu in symfony-cmf.
Example
Structure:
page1
├─p1-subpage1
├─p1-subpage2
└─p1-subpage3
page2
├─p2-subpage1
└─p2-subpage2
Whenever the current page is somewhere within the page1 hierarchy it should use p1-subpage* to render the menu, when I am within the page2 hierarchy it should use p2-subpage* to render the menu. Technically that means it should set the current item to the parent of the 1st level (if it's not already on it) and render one level of nodes (e.g. knp_menu_render('main', { depth: 1 })).
The problem can be split in two parts:
Rendering a (sub-)menu from a given node
Retrieving the current node
Thoughts and Trials
TWIG: It has been suggested to support rendering submenus as a functionality of the KnpMenu itself, but it hasn't been done. As a workaround registering a twig extension has been provided by someone in the issue. However this extension is based on the getCurrentItem Method which has been removed with KNP-Menu 2.0. Although the cmf currently uses v1.1 of the knp-menubundle, this is going to change soon
TWIG: The CnertaBreadcrumbBundle would bring back this functionality, but depends on KNP-Menu 2.0 as well.
TWIG: Using a hack similar as suggested here. It checks the current URI, counts the number of slashes and decides based on that what to use. This could probably work. Problem here: I don't have cmfMainContent variable defined, nor can I find anything similar in my {{ dump() }} (nothing containig a menu either).
RouteVoter: The cmf itself has some MenuVoters itself, which are well documented what they are, but not how to use them. I don't think that there is any way to access that functionality whithin twig nor do I know how to intercept the menu building.
Thanks for any help.
have a look here for an example of using voters to make decision about what to highlight:
https://github.com/dbu/conference-tutorial-1.0/pull/20
aside from this we are making good progress on a KnpMenu 2.x compatible version of our MenuBundle but it might be until January until we make a stable release of it (but we might make one earlier .. we will see):
https://github.com/symfony-cmf/MenuBundle/pull/214
I have created a bundle yesterday for my own, similiar, use case.
However as all of my pages share the same route you might need to adapt it quite a bit.
I still think you might find some inspiration, especially for the second part of your problem.
My Bundle:
https://github.com/burki94/RecursiveMenuBundle/blob/master/README.md
AbstractRecursiveBuilder: https://github.com/burki94/RecursiveMenuBundle/blob/master/Menu/AbstractRecursiveBuilder.php:
This is not really a solution because it's not following my requirements of being compatible with KnpMenu 2.*. But this deprecated solution is easy:
{% set currentItem = knp_menu_get('main').currentItem %}
{% if currentItem is not null %}
{% if currentItem.getLevel() == 1 %}
{% set main = currentItem %}
{% else %}
{% set main = currentItem.getParent() %}
{% endif %}
{{ knp_menu_render(main, { 'template': 'ComBundle:Default:left_menu.html.twig', 'currentClass': 'uk-active' }) }}
{% endif %}
Say I'm having a base template like this:
// Default/index.html.twig
{% block javascripts %}
<script>//some script</script>
{% endblock %}
<div>
{{ render(controller(MyControllerBundle:Default:header)) }}
</div>
{{ text }}
<div>
{{ render(controller(MyControllerBundle:Default:footer)) }}
</div>
And this renders controllers having these templates:
// Default/header.html.twig
Header content
{% block javascripts %}
<script>//some additional scripts from the header</script>
{% endblock %}
and
// Default/footer.html.twig
Footer content
{% block javascripts %}
<script>//some additional scripts from the footer</script>
{% endblock %}
Is it possible somehow to use the javascripts block from the rendered sub controllers in the parent template?
I want to have all javascripts cumulated in one place.
Rendering from bottom up with extending is no option here because the template consists of
multiple blocks that are rendered by different controllers.
Is this possible somehow? Or is there a better approach to this?
Anything is possible however design-wise it might not be a good idea.
The render tag is really useful when it comes down to scaling and it used as a way to isolate a request. Each render call is considered as a sub-request and a cache strategy can be applied to it.
I'd highly advise you to read this documentation about HTTP caching and especially the part that talks about Edge Side Includes (or ESI).
When you use the render tag, think of it as a module you want to include in multiple pages and eventually cache.
You shouldn't interact with the master request because the sub request is isolated for caching (depending on the place you embed the render tag, the master request will be different which means you might get some unexpected results).
First of all, I'd create a layout template that every other pages extends. The layout template will declare all the basic blocks (javascript, css, footer, header, <head>, <body> - you can abstract in more templates if you want).
If you have logic for your footer or header split them into Twig functions (or filters) and handle the logic in Twig but keep it light (if it's too complicated or too spaghetti that means there is another way).
Avoid having multiple Javascript or CSS files per page. If you have some css or javascript that appears on some pages but not all of them it's still probably a good idea to merge them into one file (less DNS calls on the client side and once it's cached it will be faster to load the page).
If you have a administrator.js kind-of file, then you could include it as a separate file but if most requests come from administrators then you might want to include it with all the other files.
If you didn't know you can combine assets (js or css) into one file: more info in the Symfony documentation.
I didn't answer your "how" question because I'd strongly advise you to not implement such a system however I believe I've shared good guidelines to make an informed decision.
when extending / rendering other content in TWIG you can call the parent block: http://twig.sensiolabs.org/doc/functions/parent.html
this means that you can leave default as it is and inside header / footer define
{% block javascripts %}
{{ parent() }}
{# other scripts #}
{% endblock javascripts %}
I would suggest that you have different block name for the footer - that way you can include scripts outside of the header.
Also, it might be best to keeps scripts in one place - that way you can use assetic rewrite's later down the line : http://symfony.com/doc/current/cookbook/assetic/asset_management.html#including-javascript-files
exactly what #Pazi says in the comment: Do you need a controller? It looks pretty simple to just include the template by itself, without using a controller.
You might use the include tag to include the subtemplates.
{% include 'MyControllerBundle:Default:header.html.twig' %}
For reusing the javascript block from the rendered sub controllers, you could create a base template that contains the javascripts block. Then extend that base template file in your header and footer. Or just including the base template in them should work, too.