Pass string as variable name to for loop with nunjucks - for-loop

I have different sites, with different sitenames and try to get the right array on each site. So basically I want to pass the variable name dynamically to the for loop.
{% set sitename = "user" %}
{% set blockRef = sitename + 'Blocks' %} //result should be userBlocks
{% set userBlocks = [ 'chats', 'profile', 'settings' ] %}
{% set adminBlocks = [ 'chats', 'archive', 'profile', 'settings' ] %}
{% for blockName in blockRef %}
//user values from userBlocks array here
{% endfor %}
However, the passed name is interpreted as text and does not refer to the given array. Is there a way to make my code dynamic?

{% set sitename = "user" %}
{% set userBlocks = [ 'chats', 'profile', 'settings' ] %}
{% set adminBlocks = [ 'chats', 'archive', 'profile', 'settings' ] %}
{% set blocks = userBlocks if sitename == 'user' else adminBlocks %}
{% for blockName in blocks %}
//user values from userBlocks array here
{% endfor %}

Related

AnsibleError: template error while templating string: expected token ':', got

I have the following JSON file.
[
{
"NODE": "ha2(VRM02)",
"ROLE": "active",
"PHASE": "Actived",
"RESS": "normal",
},
{
"NODE": "ha1(VRM01)",
"ROLE": "standby",
"PHASE": "Deactived",
"RESS": "normal",
}
]
Through ansible I have it stored in a variable called "fusionquery1".
Through a template, I am trying to go through it to create a file with some data from the JSON file. Up to this part I don't get error.
{% for item in fusionquery1 %}
{% set item = fusionquery1[loop.index-1] %}
{{item.NODE}},NODE ROLE,NA,OK,cualitativo,igualA,ROLE,{{item.ROLE}}
{% endfor %}
My problem is when I want to add this conditional to the side of the above statement
{% if ({{item.NODE}} == "ha2(VRM02)" and {{item.ROLE}} == "active") or ({{item.NODE}} == "ha1(VRM01)" and {{item.ROLE}} == "standby") %}Ok{% else %}Failed{% endif %}
I get the following error
FAILED! => {"changed": false, "msg": "AnsibleError: template error while templating string: expected token ':', got '}'. String: {% for item in fusionquery1 %}\r\n{% set item = fusionquery1[loop.index-1] %}\r\n{{item.NODE}},NODE ROLE,NA,OK,cualitativo,igualA,ROLE,{{item.ROLE}},{% if ({{item.NODE}} == \"ha2(VRM02)\" and {{item.ROLE}} == \"active\") or ({{item.NODE}} == \"ha1(VRM01)\" and {{item.ROLE}} == \"standby\") %}Ok{% else %}Failed{% endif %}\r\n{% endfor %}\r\n"}
Remove the braces from the conditions, e.g.
- debug:
msg: |
{% for item in fusionquery1 %}
{% if (item.NODE == "ha2(VRM02)" and item.ROLE == "active") or
(item.NODE == "ha1(VRM01)" and item.ROLE == "standby") %}
Ok
{% else %}
Failed
{% endif %}
{% endfor %}
gives
msg: |-
Ok
Ok

How to use ansible template module with variable receiving value from hostvars?

In templates/config.py:
{% if env_value == 'Dev' %}
{% set x = {{hostvars['ces_dev']['ansible_host']}} %}
{% else %}
{% set x = {{hostvars['ces_demo']['ansible_host']}} %}
{% endif %}
API_CONFIG = {
'api_email_url': 'http://{{x}}:8080/api/users/mail',
}
In host inventory:
ces_dev ansible_ssh_private_key=<path> ansible_host=a.b.c.d
ces_demo ansible_ssh_private_key=<path> ansible_host=x.y.z.w
Expected output, if condition is met:
API_CONFIG = {
'api_email_url': 'http://a.b.c.d:8080/api/users/mail',
}
I am getting an error: "msg": "AnsibleError: template error while templating string: expected token 'colon', got '}'
How to resolve this and get the desired output?
I cracked the expected output myself, with several try-hit-error method. The solution is:
API_CONFIG = {
{% if env_value == 'Dev' %}
'api_email_url': 'http://{{hostvars['ces_dev']['ansible_host']}}:8080/api/users/mail',
'api_token_url': 'http://{{hostvars['ces_dev']['ansible_host']}}:8080/api/app/',
{% else %}
'api_email_url': 'http://{{hostvars['ces_demo']['ansible_host']}}:8080/api/users/mail',
'api_token_url': 'http://{{hostvars['ces_demo']['ansible_host']}}:8080/api/app/',
{% endif %}
}
The variables are expanded by default. For example
{% if env_value == 'Dev' %}
{% set x = hostvars.ces_dev.ansible_host %}
{% else %}
{% set x = hostvars.ces_demo.ansible_host %}
{% endif %}
API_CONFIG = {
'api_email_url': 'http://{{x}}:8080/api/users/mail',
}

How to compare date in nunjucks?

So I have an array object.
var abc = [{ "title": "abc", "validUntil": "9/7/2019"];
I'm not sure how to compare date in nunjucks. I also think this can be done in the loop itself.
<div>
{% for a in abc %}
{% if new Date(offer.validUntil) > new Date() %}
{{a.title}}
{% endif %}
{% endfor %}
</div>
You can define a global function toDate
var nunjucks = require('nunjucks');
var env = nunjucks.configure();
// returns `now` if no argument is passed
env.addGlobal('toDate', function(date) {
return date ? new Date(date) : new Date();
});
var html = env.renderString(`
<div>
{% for offer in offers %}
{% if toDate(offer.validUntil) > toDate() %}
{{offer.title}}
{% endif %}
{% endfor %}
</div>
`,
{
offers: [
{title: 'Some offer title', validUntil: '9/7/2019'},
{title: 'Another offer title', validUntil: '1/6/2019'}
]
});
console.log(html);
The another way is to define a custom filter isActual
var nunjucks = require('nunjucks');
var env = nunjucks.configure();
env.addFilter('isActual', function(offers) {
return offers.filter(offer => new Date(offer.validUntil) > new Date());
});
var html = env.renderString(
`
<div>
{% for offer in offers | isActual %}
{{offer.title}}
{% endfor %}
</div>
`,
{
offers: [
{title: 'Some offer title', validUntil: '9/7/2019'},
{title: 'Another offer title', validUntil: '1/6/2019'}
]
});
console.log(html);
P.S. Pass date is a string like 9/7/2019 is a bad idea. The date intepretation (dd.mm.yyyy or mm.dd.yyyy) depends on browser setting. I recommend to use unix-epoch: new Date().getTime().

ansible, jinja template with loops, losing newlines [duplicate]

This question already has answers here:
How do I get an Ansible template to honor new lines after a conditional
(5 answers)
Closed 4 years ago.
Trying to build a JSON file from a template. It works ok as such, but for some reason newlines within loop constructs go missing, which I find rather irksome; the file "works" (is machine readable just fine), but for human consumption it's pretty ill-suited.
Here's my template:
{
"Users":
[
{% for username,user in _vpn_user_accounts.items() %}
{
"Name" : "{{ user.name }}",
"Hash" : "{{ user.hash }}",
"Cns" : [
{% for cn in user.cns.get(server_name, []) %}
"{{ cn }}"{% if not loop.last -%},{% endif %}
{% endfor %}
],
"key_ids" : [
{% for key in user.get( 'keys' , []) %}
{% if key.public is defined %}
"{{ key.public }}"{% if not loop.last %},{% endif %}
{% endif %}
{% endfor %}
],
"comment" : "{{ user.get( 'comment', '' ) }}"
} {% if not loop.last %},{% endif %}
{% endfor %}
]
}
Here's some sample data:
- andrej:
name: "andrej"
hash: "$2b$10$8EF3H.../Wj0RchEqU6"
cns:
h:
- "andrej_linux_h_201808171440"
- "andrej_linuxvm_h_201809131031"
- "andrej_mac_h_201808171441"
- "andrej_phone_h_201808171441"
w:
- "andrej_linux_w_201808171439"
- "andrej_linuxvm_w_201809131031"
- "andrej_mac_w_201808171441"
- "andrej_phone_w_201808171441"
keys:
- name: "andrej"
public: "kbjrvtni"
- name: "andrej2"
public: "ijrltifu"
- name: "andrej3"
public: "rbcvncbt"
comment: "systems"
This is my desired outcome (running against server "w"):
{
"Users":
[
{
"Name" : "andrej",
"Hash" : "$2b$10$8EF3H.../Wj0RchEqU6",
"Cns" : [
"andrej_linux_w_201808171439",
"andrej_linuxvm_w_201809131031",
"andrej_mac_w_201808171441",
"andrej_phone_w_201808171441"
],
"key_ids" : [
"kbjrvtni",
"ijrltifu",
"rbcvncbt"
],
"comment" : "systems guy"
}
]
}
This is what I get:
{
"Users":
[
{
"Name" : "andrej",
"Hash" : "$2b$10$8EF3H.../Wj0RchEqU6",
"Cns" : [
"andrej_linux_w_201808171439", "andrej_linuxvm_w_201809131031", "andrej_mac_w_201808171441", "andrej_phone_w_201808171441" ],
"key_ids" : [
"kbjrvtni", "ijrltifu", "rbcvncbt" ],
"comment" : "systems guy"
}
]
}
I have experimented with #Jinja2: trim_blocks and #Jinja2: keep_newline, neither of which showed the desired result. Well, trim_blocks kind of did, but it also gave me a bunch of empty lines where the jinja conditionals were ... unsatisfactory.
Any hints on how to resolve this? As I said, it works, but it irks me immensely that I can't get human readable, nice output.
And this little change actually made the problem go away in the end.
#jinja2: trim_blocks:False
{
"Users":
[
{% for username,user in _vpn_user_accounts.items() %}
{
"Name" : "{{ user.name }}",
"Hash" : "{{ user.hash }}",
"Cns" : [
{%- for cn in user.cns.get(server_name, []) %}
"{{ cn }}"{% if not loop.last -%},{% endif %}
{%- endfor %}
],
"key_ids" : [
{%- for key in user.get( 'keys' , []) %}
{% if key.public is defined %}
"{{ key.public }}"{% if not loop.last %},{% endif %}
{% endif %}
{%- endfor %}
],
"comment" : "{{ user.get( 'comment', '' ) }}"
} {% if not loop.last %},{% endif %}
{% endfor %}
]
}

Custom pagination in OctoberCMS

I have a code to do query from form:
Painting::where('type',input("type"))->where('material',input("material"))->whereHas('artist', function($q)
{
$q->where('artist_slug', '=', $this->param('slug'));
})->paginate(15);
How can I do custom pagination with page numbers list? Or maybe dinamicaly loading
Check out the RainLab Blog Plugin it has a good example, also see here .
But here is a more complete example if you want to add URL friendly paginations paintings/2 where 2 is the page number and/or handle URL parameters paintings/2?type=something&material=something
In your Painting Model add a scope for listing paintings on the front-end :
public function scopeListPaintings($query, $options)
{
extract(array_merge([
'page' => 1,
'perPage' => 30,
'material' => null,
'type' => null,
'artistSlug' => null,
], $options));
if( !empty($artistSlug) ){
$query->whereHas('artist', function($q)
{
$q->where('artist_slug', $artistSlug );
});
}
if( !empty($material) ){
$query->where( 'material' , $material)
}
if( !empty($type) ){
$query->where( 'type' , $type)
}
return $query->paginate( $perPage, $page );
}
Then in your Painting Component Define the properties and call the previous scope ;
public $paintings ;
public $pageNumber;
public $perPage;
public $urlParams;
public function defineProperties()
{
return [
'pageNumber' => [
'title' => 'Page #',
'description' => 'Paintings Page #',
'type' => 'string',
'default' => '{{ :page }}',
],
'perPage' => [
'title' => 'Paintings per page',
'type' => 'string',
'default' => '30',
]
.. ect make sure to add it to your page markup
];
}
private function propertyOrParam($name, $default = null)
{
$value = $this->property($name, $default);
if (substr($value, 0, 1) == ':')
return $this->param($value, $default);
return $value;
}
public function getPaintings()
{
/** Pagination */
$this->pageNumber = $this->propertyOrParam('pageNumber') ;
$this->perPage = $this->propertyOrParam('perPage');
/** Url Params if exist */
$params = Request::query()
$this->page['urlParams'] = $this->urlParams = !empty( $params ) ? http_build_query( $params ) : null ;
return (new Painting)->ListPaintings([
'page' => $this->pageNumber,
'perPage' => $this->perPage,
'type' => input('type'),
'material' => input('material'),
'artistSlug' => $this->propertyOrParam('slug')
]);
}
public function onRun()
{
$this->page['paintings'] = $this->paintings = $this->getPaintings() ;
// Redirect to Last page if page # not found in request
if ($this->pageNumber > $this->paintings->lastPage() && $this->pageNumber > 1){
return Redirect::to($this->currentPageUrl([ $this->paramName('pageNumber') => $this->paintings->lastPage().$this->urlParams ]));
}
}
Then your can create a global partial in your theme to handle paginations - You can reuse it all over your site - add the following snippet ( Borrowed from the Forum Plugin ) ;
Pagination.htm
{% set paginationEnabled =
records.currentPage > 1 or
records.lastPage > 1 or
records.lastPage > records.currentPage
%}
{% if paginationEnabled %}
{# How many pages to display around the current page #}
{% set n = 2 %}
{% set currentPageZeroBased = records.currentPage-1 %}
{% set pageLinks = [] %}
{% set pageSet = [] %}
{% set startOffset = max(currentPageZeroBased - n, 0) %}
{% if (startOffset + 2*n+1) > (records.lastPage-1) %}
{% set startOffset = max(records.lastPage - 2*n - 1, 0) %}
{% endif %}
{% for page in 1..records.lastPage %}
{% set pageLinks = pageLinks|merge([page]) %}
{% endfor %}
{% set activeBlock = pageLinks|slice(startOffset, 2*n + 1) %}
{% if startOffset > 0 %}
{% set pageSet = pageSet|merge([1]) %}
{% if startOffset > 1 %}
{% set pageSet = pageSet|merge(['...']) %}
{% endif %}
{% endif %}
{% set pageSet = pageSet|merge(activeBlock) %}
{% set diffToEnd = (records.lastPage-1) - (startOffset + 2*n+1) + 1 %}
{% if diffToEnd > 0 %}
{% if diffToEnd > 1 %}
{% set pageSet = pageSet|merge(['...']) %}
{% endif %}
{% set pageSet = pageSet|merge([records.lastPage]) %}
{% endif %}
<div>
<div>
<div>
<div>
<span>Records <b>{{records.firstItem|trim}} - {{records.lastItem|trim}}</b> Of {{ records.total|number_format(0, '.', ',')}}</span>
<span>Page {{ records.currentPage }} of {{ records.lastPage }}</span>
</div>
</div>
</div>
<div>
<ul>
{% if records.currentPage > 1 %}
<li>
<i class="fa fa-angle-left"></i>
</li>
{% endif %}
{% for page in pageSet %}
{% if page == '...' %}
<li>
{{ page }}
</li>
{% else %}
<li class="{{ page == records.currentPage ? 'active' }}">
{{ page }}
</li>
{% endif %}
{% endfor %}
{% if records.lastPage > records.currentPage %}
<li>
<i class="fa fa-angle-right"></i>
</li>
{% endif %}
</ul>
</div>
</div>
{% endif %}
Then in your page or component ;
{% for painting in paintings %}
....
{% endfor %}
// Add the pagination
{% partial "pagination" records=paintings %}

Resources