I'm using Timber (the WordPress plugin implementation of Twig) and the Advanced Custom Fields plugin gallery field. I have a working gallery under a custom post type, so ACF and Timber are working elswhere in the site, but not for a standard page. When I try to add a gallery to a page, all I end up with is empty markup for the img src tag.
I have this in page.php in the template folder:
$context = Timber::get_context();
$page = new TimberPost();
$context['page'] = $page;
if ($photos = $page->get_field('photos')) {
$context['photos'] = $photos;
}
I have this in default.twig in the templates/page/ folder in the theme (html removed for simplicity):
{% if page.photos %}
{% for p in page.photos %}
<img src="{{ p.sizes.gallery|relative }}" alt="{{ p.alt }}" />
{{ p.caption }}
{% endfor %}
{% endif %}
This results in the page source <img src="" alt="">.
If I use {{ dump(photos) }} inside the for p in page.photos statement, it dumps the array of images I have entered in the Gallery field on the backend. So the image array exists and it being output. The relative extension runs for all post types; removing it makes no difference here.
So why is the p.sizes.gallery|relative function not outputting each image's url and caption?
You append the data to the $context['photos'] so I believe you should change your code to check for if photos and iterate as for p in photos
Related
I am trying to re-create this Wordpress blog post on Jekyll. The post is made up of images with descriptions, with 5 or 6 such images per page.
I created the page with a Wordpress pagination plugin. Jekyll uses a paginate plugin to paginate blog menus, but it seems I cannot re-use that for a single blog post because:
the number of items per page must be hard-coded in _config.yml
I don't have the same number of elements on every page of my blog post anyway
I use a script to create this post, so I can use HTML, markdown, whatever. I could also make a separate file for each page--and hopefully, figure out how to direct Jekyll to find them without the date prefix.
I don't know Javascript or Ruby, but I can mostly find my way around. What I really need to know is which strategy to take so I can work toward that instead of spending all morning on something that won't work well.
So, how would you do it?
Here's my idea of what it could look like in Jekyll. This is a hack with some potential issues.
TL;DR
The blog post you have shared is using two different layouts for the start page and the subpages. The index has a different position for the menu and the intro. Also, the page content differs.
Good news: Jekyll has layouts, powerful collections (beside posts), as well as includes to reuse content. See the details with some notes below. I have also added short post about the solution to my own blog.
You need two layouts
_layouts\paginated-post.md
---
---
<p>tags here</p>
<h2>{{ page.title }}</h2>
<!-- page menu -->
<ul>
<li>{{ page.title }}</li>
{% for item in page.page_urls %}
<li>{{ item.title }} Palettes</li>
{% endfor %}
</ul>
{{ content }}
<h2>Pages</h2>
<ul>
<li>1</li>
{% for item in page.page_urls %}
{% assign page_number = forloop.index | plus: 1 %}
<li>{{ page_number }}</li>
{% endfor %}
</ul>
_layouts\paginated-post-index.md
---
---
<p>tags here</p>
<h2>{{ page.title }}</h2>
I created Artificial Intelligence to produce these palettes ...
<h2>Menu</h2>
<!-- page menu here -->
<ul>
<li>{{ page.title }}</li>
{% for item in page.page_urls %}
<li>{{ item.title }} Palettes</li>
{% endfor %}
</ul>
{{ content }}
<h2>Pages</h2>
<ul>
<li>1</li>
{% for item in page.page_urls %}
{% assign page_number = forloop.index | plus: 1 %}
<li>{{ page_number }}</li>
{% endfor %}
</ul>
Add a new collection
_config.yml
Note: I have chosen permalink path here, the defaults are optional if you set a layout in each collection item). Learn more about Jekyll collections in the docs.
collections:
palettes:
output: true
permalink: /:path/
defaults:
- scope:
path: ""
values:
layout: paginated-post
New collection folder (_palettes) with two files:
The index on each page defines the palettes index.
_palettes\index.md
---
layout: paginated-post-index
title: AI-Generated Palettes
index: palettes-index
page_urls:
- title: Wes Anderson
palette: Kind of Bird
url: /wes-anderson-kind-of-bird/
---
{% include palettes/wes.html %}
_palettes\wes-anderson-kind-of-bird.md
---
title: AI-Generated Palettes
subtitle: Wes Anderson – Kind of Bird
index: palettes-index
page_urls:
- title: Wes Anderson
palette: Kind of Bird
url: /wes-anderson-kind-of-bird/
---
{% include palettes/wes.html %}
Include file used above in _includes\palettes\wes.html
Note: the first line is the only line which will cause issues in other palette include files - maybe you will find your own solution or can just adjust the index here.
<h2>{{ page.page_urls[0].title }} </h2>
image here
Merin’s Fire: ff9506
Trojan Horse Brown: 7b571e
Minestrone: c4280d
Punch of Yellow: efd185
Root Brew: 2b0f0b
Green Ink: 12887f
I'm working on a design system and just getting my head around the component way of thinking.
We're using Nunjucks and Fractal.
We have an existing component, an accordion which gets it's data from a JSON object. The props being title and content.
I've built a component, like a table of contents, which displays a list of styled links, using a simple loop, pulling in from a ToC.config.js file.
I need to include that new component within the accordion component, the ToC has a title and an array containing urls and text for each link.
At present I've just copied the HTML from the accordion's view in Fractal, but that's probably not the way to go, but visually it's as the designer requires.
I can also render the accordion correctly if I put all the links, HTML and all in the config, as a string, but that's not the way it should work either.
What I want to do, which is probably the proper way, is pull that accordion in and populate it with my ToC component.
{% render '#my-accordion', 'accordion: items' %} doesn't do what I want and I can't seem to figure out how to achieve what I need to do.
Something like so:
{% render '#my-accordion' %}
// Pass in data from ToC, somehow
{{ title: title }}
{% for item in items %}
{{ item.text etc }}
{% endfor %}
Then I would have my accordion component, where its title, links and HTML etc are those from my ToC component. Sorry for the rough pseudo above, it's more an example of what I want to do. I can't copy code to an external resource.
using render doesn't appear to be the way to go, unless I'm missing something? I can't pass the list through as a string as it has classes and aria for the current page etc and content authors will be building pages with these.
A little nudge in the right direction would be great.
You can add a render-filter that similar to include and macro.
import 'nunjucks.min.js';
var env = new nunjucks.Environment({autoescape: true});
env.addFilter('render', function (template, ctx) {
const html = env.render(template, ctx); // try-catch
return env.filters.safe(html);
});
Usage
// table.njk
<table>
{% for e in rows %}
{% 'row.njk' | render({row: e}) %}
{% endfor %}
</table>
// row.njk
<tr>
{% for c in row.cols %}
<td> {{ c.name }} </td>
{% endfor %}
</tr>
Does anyone know how to check if a template is being accessed through a url route with the django template language?
Here is my situation: There's this template article.html, that shows all posts of a blog when accessed through blog-home url route, and that also shows only posts of a given user through user-posts url route.
This template works like that because what defines which posts will be shown are the classes in views.py.
That being said, here is what I tried to do: (I know its wrong)
{% if url == 'user-posts' %}
"Some HTML h1 tag I want do be shown only when this template is accessed via 'user-posts' url"
{% endif %}
How would be the correct way, if there's any, to write this if statement?
When Django matches a url pattern a ResolverMatch object is created and made available on the request object at request.resolver_match. This ResolverMatch object can be queried in your template to acheive what you want, the url_name attribute is the name of the url pattern that was matched
{% if request.resolver_match.url_name == 'user-posts' %}
You should create a custom templatetag,
my_app/templatetags/utils.py
from django import template
register = template.Library()
#register.simple_tag(takes_context=True)
def is_active_view(context, *view_names):
request = context.get('request')
for view_name in view_names:
if getattr(request.resolver_match, 'view_name', False) and request.resolver_match.view_name == view_name:
return True
return ''
And use it in your template this way, assuming blog-home is the url name you gave to your url :
blog-home.html
{% load utils %}
{% is_active_view 'blog-home' as display_blog_home_section %}
{% if display_blog_home_section %}
<!-- Your code -->
{% endif %}
NB : this template tag can check after multiple view names at once and support namespaced url :
{% is_active_view 'front:blog-home' 'front:blog-categories' as display %}
Here is a more optimized version of this if you are using boostrap and trying to make the menu options "active" when you are on that URL
from django import template
register = template.Library()
#register.simple_tag(takes_context=True)
def is_active_tab(context, *view_names):
request = context.get('request')
for view_name in view_names:
if getattr(request.resolver_match, 'view_name', '') == view_name:
return 'active'
return ''
And you use it like
<li class="nav-item {% is_active_tab 'games:list' %}">
<a class="nav-link" href="{% url 'games:list' %}">
{% translate "My Quizzes" %}
</a>
</li>
<li class="nav-item {% is_active_tab 'games:create' %}">
<a class="nav-link" href="{% url 'games:create' %}">
{% translate "New Game" %}
</a>
</li>
It adds an extra active classname to the class of the element if any of the view_names match the current view name. Makes the template writing much cleaner
I need to add a new OpenCart template file into another template file.
Essentially I've created a new head file in /theme/customtheme/template/common/ called "header_home.twig".
Then in home.twig, I've changed {{ header }} to say {{ header_home }}, but it's not displaying anything.
Basically, all I did was copy header.twig and rename it to header_home.twig, and put in "xxxxx" to see if it was calling the new file, which it's not. Instead, it's not displaying anything.
Here's what my home.twig now looks like:
{{ header_home }}
<div id="common-home" class="container">
<div class="row">{{ column_left }}
{% if column_left and column_right %}
{% set class = 'col-sm-6' %}
{% elseif column_left or column_right %}
{% set class = 'col-sm-9' %}
{% else %}
{% set class = 'col-sm-12' %}
{% endif %}
<div id="content" class="{{ class }}">{{ content_top }}{{ content_bottom }}</div>
{{ column_right }}</div>
</div>
{{ footer }}
I assume I'm somehow missing a step when it comes to adding a new template file? If someone could help me with adding in a new twig file, that would be fantastic.
You can either:
Change the name of Twig rendering template on the OpenCart level in the controller/common/header as #bogalakon pointed out (preferably to do this via ocmod so that your future OpenCart updates will not overwrite your hack).
or include another template on the Twig level
I.e.:
{{ header }} {# Original rendered to HTML OpenCart header - you can move it to your header_home.twig or just drop it #}
{% include customtheme/template/common/header_home.twig %}
<div id="common-home" class="container">
...
Twig is very powerful template language. And you can do much more than just a simple include! It's nice that OpenCart officially accepted it. But for now it's just Twig 1.24.2. Please refer to the Twig documentation.
I haven't learn OpenCart 3.x, but I guess you need to change
return $this->load->view('common/header', $data);
to
return $this->load->view('common/header_home', $data);
in catalog/controller/common/header.php.
In order to connect another template via the {{header_home}} construct, you need to create the header_home.php in the folder /controller/common/directory, create the header_home.twig file in the theme /*theme_name*/template/common/ folder and in the controller , which is responsible for the formation of the page, for example in the directory /controller/common/home.php
add a line:
'$ data [' header_home '] = $ this-> load-> controller (' common / header_home '); '
before:
'$ this-> response-> setOutput ($ this-> load-> view (' common / home ', $ data)); '
To add your own custom twig file and include it inside another existing one you need to do three things:
Create the actual custom twig file.
In your case it will be:
catalog/view/theme/customtheme/template/common/header_home.twig
Create a controller for that twig file. In your case you can just copy:
/catalog/controller/common/header.php and rename that to header_home.php. Edit this controller and change the class name to ControllerCommonHeaderHome usually on line 2.
Lastly, since you are going to include header_home inside home.twig, edit /catalog/controller/common/home.php and add the line $data['header'] = $this->load->controller('common/header_home'); after $data['header'] = $this->load->controller('common/header');
That's it. After you've done the steps above, you can now include {{ header_home }} inside home.twig. If you are editing the files directly, I find that sometimes I need to login to the admin of the website, go to design>theme editor, open up the files I added or changed, hit reset, and save. Refresh your website and you should see the changes.
The answer of #bogalakon is right but if you update your OpenCart core files you will lose your changes so, I suggest you to copy the header.php controller file and rename it to header_home.php and then edit the file and replace the line
return $this->load->view('common/header', $data);
with:
return $this->load->view('common/header_home', $data);
There are several ways to give a different value.
For example:
Consider the controller
$data['header'] = $this-> load-> controller ('common / header');
A variable {{header}} is available in the template
You can place a new template in another file (header_home.twig), and then you can immediately download a new template for use:
$data['**header**'] = $this-> load-> controller ('common / ***header_home***');
If you want use {{header}} in the template.
Or,
$data['**header_home**'] = $this-> load-> controller ('common / header_home');
For use {{header_home}} in the template.
I have made a few pages in grav with a taxonomy like this.
- Home (category type)
- programming (category type)
- stuff (category type)
- stuff1 (page type)
- stuff2 (page type)
- stuff3 (page type)
I've also made a template type called "category" which should hopefully grab all the links to stuff1/2/3 and place them on the "stuff" page as links. My code looks a bit like this:
{% block body %}
{% block content %}
<ul>
{% for p in self.children %}
<li>{{ p.title }}</li>
{% endfor %}
</ul>
{% endblock %}
{% endblock %}
The end goal is to just get a simple listing of links for children to the category something like:
<ul>
<li>stuff1</li>
<li>stuff2</li>
<li>stuff3</li>
</ul>
I've tried using page.children, self.children, and a few other things but nothing seems to be getting this to work the way that I want it to.
Any help would be appreciated.
I'm afraid I do not quite understand what you mean by 'category type' and 'page type...
The docs on Page Collections might be quite helpful.
Example:
If you want a page with template 'category.html.twig' which shows a list of urls to all pages containing a certain category (or tag), you can do the following:
create a page 'category.md' defining a collection of categories in frontmatter:
content:
items:
'#taxonomy.category': mycategory
Create a template 'category.html.twig' containing:
<ul>
{% for p in page.collection() %}
<li>{{p.title}}</li>
{% endfor %}
</ul>
The resulting page will look like:
<ul>
<li>Home</li>
<li>Body & Hero Classes</li>
</ul>