Add body class to node in Drupal 8 - twig

I am experimenting with Drupal 8. I've created a local sandbox site and created a custom theme and a custom front page using Twig (page--front.html.twig). I want to add some custom CSS that just targets the front page and I am used to targeting the body.page-node-x (Drupal 7). In Drupal 8 I am having issues adding a body class to this custom page. The only body classes I am seeing are toolbar-tray-open toolbar-fixed toolbar-horizontal.

To add frontpage, logged-in and node type class for the body tag. Edit html.html.twig and add:
{%
set body_classes = [
logged_in ? 'logged-in',
not root_path ? 'frontpage' : 'path-' ~ root_path|clean_class,
node_type ? 'node--type-' ~ node_type|clean_class,
]
%}
<body{{ attributes.addClass(body_classes) }}>

You have need to add these code in " html.html.twig" template which you can find in core themes " classy" in drupal 8
<?php
{%
set body_classes = [
logged_in ? 'user-logged-in',
not root_path ? 'path-frontpage' : 'path-' ~ root_path|clean_class,
node_type ? 'page-node-type-' ~ node_type|clean_class,
]
%}
?>

Related

How to use safe filter on custom Django tag?

I'm trying to implement a custom Django tag that will formulate an import statement in Javascript to load my vue3 app as well as its components template html files using a get request in axios.
The custom tag in my templatetags directory looks like this:
templatetags/vuecomponents.py
from django import template
from django.templatetags.static import static
register = template.Library()
#register.simple_tag
def v_load_app_component(app_name, script_name, components):
components = components.strip("[]").split(", ")
app_script = static(f"js/{script_name}.js")
comps = [static(f"components/{name}.html") for name in components]
return f"import {{{ app_name }}} from \"{app_script}?{components[0]}={comps[0]}\""
Right now it only loads the first component as I just want a prototype. The only issue is when I drop this into a template like so:
createpost.html
<script type="module">
{% v_load_app_component "creator" "internalpostform" "[internalpostform]" %}
// OUTPUTS:
// import { creator } from "static/js/internalpostform.js?internalpostform=internalpostform.html"
creator.mount("#app")
</script>
It outputs the relevant import statement as:
import { creator } from "static/js/internalpostform.js?internalpostform=internalpostform.html"
With the double quotes escaped. Even when I tried to apply the safe filter ({% v_load_app_component "creator" "internalpostform" "[internalpostform]"|safe %}) it still escaped the output of my custom tag function.
How can I make it to where the output of my custom tag doesn't automatically have symbols converted to html entities?
I found it after a little digging in Django's documentation. The safe filter is only for variables i.e. {{ variable|safe }} but does not apply to a tag {% tag "argument"|safe %}.
To prevent Django from escaping the output of a tag you simply use {% autoescape off %}
{% autoescape off %}
{% v_load_app_component "creator" "internalpostform" "[internalpostform]" %}
{% endautoescape %}
This results in the desired behavior.

Djoser override activation email with template

I've been scouring for more information about how to do this, but there seems to be little to no documentation help.
Essentially want I want to do is make a new template for the activation email so the link can start with localhost:3000 instead of localhost:8000 (I'm using Vue for the frontend post request that's why)
I managed to find this: https://github.com/sunscrapers/djoser/blob/master/djoser/templates/email/activation.html
but when I added it to my own project, the default Djoser template is still being used.
This is how my settings.py looks like:
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
My activation URL which works if the 8000 is replaced by 3000 manually:
'ACTIVATION_URL': 'registration/activate/{uid}/{token}',
templates/email/activation.html:
{% block subject %}
{% blocktrans %}Account activation on {{ site_name }}{% endblocktrans %}
{% endblock subject %}
{% block text_body %}
{% blocktrans %}You're receiving this email becaus!!!!!!e you need to finish activation process on {{ site_name }}.{% endblocktrans %}
{% trans "Please go to the following page to activate account:" %}
{{ http }}://{{ localhost:3000 }}/{{ {% url 'registration/activate' uidb64=uid token=token %} }}
{% trans "Thanks for using our site!" %}
I suppose you need to override email.ActivationEmail, for example to core/email.py
from djoser import email
class ActivationEmail(email.ActivationEmail):
template_name = 'email/activation.html'
and add it to your settings.py
DJOSER = {
'EMAIL': {
'activation': 'core.email.ActivationEmail'
}
}
Here are emails which can be overwritten link
You can configure the DOMAIN and SITE_NAME in the settings.py file instead of changing the template.
Just put DOMAIN = ('localhost:3000') and SITE_NAME = ('YOUR_SITE_NAME')
DOMAIN = ('localhost:3000')
SITE_NAME = ('YOUR_SITE_NAME')
DJOSER = {
...
}
Source: https://stackoverflow.com/a/62586798/6568539 (I found that we don't need to use "config" as mentioned in this answer)
Without changing the Djoser email classes and settings.py, You can override the html templates in your project's template directory using the concepts here
You just simply have to create a new folder named email in your 'templates' folder and in it create html files with the exact names for the email templates that you would want to customize/override.
For example if you want to customize the password reset email, your custom file path should resemble this :
projectName/templates/email/password_reset.html
Here is a full list of all the Djoser email templates that you can customize
I found this closed thread and it did do the job of allowing me to render out a custom template. Might want to check this
the answer from Pavlo Naumenko works for me too so thanks a lot. But just a small note: I have to change the name of the file (I couldn't use "activation.html" on my own template). If I use it, django keeps sending the default template. Don't know why.
In case someone happens that too. I was using django 3.2.3 with djoser 2.1.0
it's working! I've just changed activation.html file in
~/.local/lib/python3.5/site-packages/djoser/templates/email
without any other changes

OpenCart 3.x - including a new template file

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.

Yii2 jsPosition POS_HEAD not working with Twig

I am use Yii2 advanced with Twig integration. I need include js and css files to head block and use AssetBundle for this.
If I use POS_BEGIN or POS_END it's work. Js and css load to page in current position. But if I set position as POS_HEAD render ignore this and no load files to page.
Please help. What am I doing wrong?
My AssetBundle
namespace frontend\assets;
use yii\web\AssetBundle;
class YandexAsset extends AssetBundle
{
public $basePath = '#webroot';
public $baseUrl = '#web';
public $css = [
'/css/map.css'
];
public $js = [
'js/map.js',
'js/test.js'
];
public $depends = [
'yii\web\YiiAsset',
'yii\bootstrap\BootstrapAsset',
];
public $jsOptions = [ 'position' => \yii\web\View::POS_HEAD ];
}
And connect in Twig template
{{ use('frontend/assets/YandexAsset')}}
{{ register_yandex_asset() }}
From http://www.yiiframework.com/doc-2.0/guide-structure-assets.html :
Asset dependencies are mainly specified through the
yii\web\AssetBundle::$depends property. In the AppAsset example, the
asset bundle depends on two other asset bundles: yii\web\YiiAsset and
yii\bootstrap\BootstrapAsset, which means the CSS and JavaScript files
in AppAsset will be included after those files in the two dependent
bundles.
both assets
'yii\web\YiiAsset',
'yii\bootstrap\BootstrapAsset',
are included at end of rendered page and your asset cannot be placed before them because they are depended of them.
if you want to include js file in POS_HEAD you can use registerJsFile() in your view or layout file
http://www.yiiframework.com/doc-2.0/yii-web-view.html#registerJsFile()-detail

Twig : Access to variable from outer scope in a form widget customization

I'm trying to customize a specific widget, like in the documentation : http://symfony.com/doc/current/cookbook/form/form_customization.html#how-to-customize-an-individual-field
The problem is that in this custom block, I need to use a variable from my actual template. I thought "blocks have access to variables from outer scopes", but apparently not in this case :
{% extends "CDASvBundle::layout.html.twig" %}
{% block _contact_activity1_widget %}
<select name="contact[activity1]">
{% for key, child_contact_categories in contact_categories_tab %}
<option value="{{key}}">{{child_contact_categories}}</option>
{% endfor %}
</select>
It's saying that contact_categories_tab is undefined, but outside of this block (in the normal content block for example), it works !
I tried something like :
{% use 'form_div_layout.html.twig' with contact_categories_tab as contact_categories_tab %}
But that doesn't either.. Though I'm not sure I understand if I have to use use and how !
I see one other solution that I haven't tried yet : put this customization in another template. But I don't really want to do that (few lines in a new template), there should be a way to do that in only ONE template ?!
Finally found the answer in a previous post :
Each symfony form type extents AbstractType class.
AbstactType class has method:
public function buildView(FormView $view, FormInterface $form, array $options)
{
$view->set('img_src', '120x100.jpg');
$view->set('my_variable', $foo);
}
You can create this method on your form type and next in your twig:
{{ asset(img_src) }}
Source : How to get entity or pass variable to Symfony2 twig form widget?

Resources