OroPlatform: override Oro bundle template - symfony

Context
I'm trying to override the following template : vendor/oro/platform/src/Oro/Bundle/OrganizationBundle/Resources/views/BusinessUnit/update.html.twig
This template seems to belong to the OroOrganizationBundle bundle.
Issue
So, I have tried to put my override in the following path : templates/bundles/OroOrganizationBundle/BusinessUnit/update.html.twig according to Symfony 4.X documentation : https://symfony.com/doc/4.4/bundles/override.html#templates
I have cleared the cache : symfony console cache:clear but nothing changes.
Here is my override template :
{% extends 'OroOrganizationBundle:BusinessUnit:update.html.twig' %}
{% block content_data %}
{% set id = 'business_unit-profile' %}
{% set dataBlocks = [{
'title': 'General'|trans,
'class': 'active',
'subblocks': [{
'title': '',
'data': [
form_widget(form.appendUsers, {'id': 'businessUnitAppendUsers'}),
form_widget(form.removeUsers, {'id': 'businessUnitRemoveUsers'}),
form_row(form.name),
form_row(form.parentBusinessUnit),
form_row(form.phone),
form_row(form.website),
form_row(form.email),
form_row(form.fax),
]
}]
}] %}
{% set dataBlocks = dataBlocks|merge(oro_form_additional_data(form, 'Additional Override'|trans)) %}
{% set dataBlocks = dataBlocks|merge([{
'title' : 'oro.organization.businessunit.users.label'|trans,
'subblocks': [{
'title' : null,
'useSpan': false,
'data' : [dataGrid.renderGrid(gridName, {business_unit_id: entityId}, { cssClass: 'inner-grid' })]
}]
}] ) %}
{% set data = {
'formErrors': form_errors(form)? form_errors(form) : null,
'dataBlocks': dataBlocks
} %}
{{ parent() }}
{% endblock content_data %}
Here is the output of the following command line : symfony console debug:twig | grep Organization

Finally, I found a solution using this article from Oro documentation : https://doc.oroinc.com/frontend/back-office/templates/
The right path for my case was : src/Resources/OroOrganizationBundle/views/BusinessUnit/update.html.twig
I have tried to use the "extends" method from the Symfony documentation : https://symfony.com/doc/4.4/bundles/override.html#templates which consist to override only a specific block part. In my case, I needed to copy the entire file but it works.

Related

OroPlatform: override oro_datetime_widget options

Context
I am actually trying to change the default placeholder for the time input of the OroDateTimeType::class.
I want to have, for example, the text Horaires instead of Temps.
Here is my form field in my FormType :
->add('expirationDate', OroDateTimeType::class, [
'label' => 'app.subscription.fields.expirationDate',
])
And in my twig view :
form_row(form.expirationDate)
Issue
At the beginning, I have tried to used the Symfony 4 placeholder option for DateTime type : https://symfony.com/doc/4.4/reference/forms/types/date.html#placeholder. It doesn't work because OroDateTime use a different datepicker and it override the Symfony values on load :
{# vendor/oro/platform/src/Oro/Bundle/FormBundle/Resources/views/Form/fields.html.twig #}
{% block oro_datetime_widget %}
{% set dateValidation = {'Date' : {}} %}
{% set timeValidation = {'Time' : {}} %}
{% if required %}
{% set dateValidation = dateValidation|merge({'NotBlank' : {}}) %}
{% set timeValidation = timeValidation|merge({'NotBlank' : {}}) %}
{% endif %}
{% if attribute(attr, 'class') is defined %}
{% set attrClass = attr['class'] %}
{% else %}
{% set attrClass = '' %}
{% endif %}
{% set options = {
view: 'oroui/js/app/views/datepicker/datetimepicker-view',
nativeMode: isMobileVersion(),
dateInputAttrs: {
placeholder: 'oro.form.choose_date'|trans,
id: id,
name: id,
class: 'input-small datepicker-input ' ~ attrClass,
'data-validation': dateValidation|json_encode(constant('JSON_FORCE_OBJECT')),
'aria-live': 'assertive',
autocomplete: 'off',
autocorrect: 'off',
autocapitalize: 'off'
},
datePickerOptions: {
altFormat: 'yy-mm-dd',
changeMonth: true,
changeYear: true,
yearRange: years|default('-80:+1'),
showButtonPanel: true
},
timeInputAttrs: {
placeholder: 'oro.form.choose_time'|trans,
id: 'time_selector_' ~ id,
name: 'time_selector_' ~ id,
class: 'input-small timepicker-input ' ~ attrClass,
'data-validation': timeValidation|json_encode(constant('JSON_FORCE_OBJECT'))
},
timePickerOptions: {
}
} %}
{% set id = 'hidden_' ~ id %}
{% set attr = attr|merge({
'data-page-component-module': 'oroui/js/app/components/view-component',
'data-page-component-options': options|json_encode(constant('JSON_FORCE_OBJECT'))
}) %}
{{ block('datetime_widget') }}
{% endblock oro_datetime_widget %}
If I change the value timeInputAttrs.placeholder from the options variable. It works.
But, I want to pass this variable to my specific form field, not globally.
UPDATE
I finally choose to change the oro.form.choose_time translation in my project globally.
So, in my Resources/translations/messages.fr_FR.yml I've created these lines :
oro:
form:
choose_time: Horaires
auth:
description:
main: Baltimore
Then, I've understand that translations are generated in a file located in var/cache/dev/translations/catalogue.fr_FR :
<?php
use Symfony\Component\Translation\MessageCatalogue;
$catalogue = new MessageCatalogue('fr_FR', array (
'messages' =>
array (
'oro.form.choose_time' => 'Temps',
'oro.auth.description.main' => 'Baltimore',
Here, I can see that the oro.auth.description.main change is applied, but the value for the key oro.form.choose_time is still the same.
Maybe I have a command to run ?
The easiest way to override any text in the Oro application UI is to override a translation for the message used to render it. As the form placeholder is translated as well, you can use this technic. If it's the only customization you need for the form, follow this guide.
If you want to override an HTML, you can extend the template by following the template overriding guide.
But, as you want to modify the label for a single form, then the best way would be to extend the form type and override the single form field with new options.

Sonata admin bundle: exclude custom admin from global search

Within my Symfony 3.4 project, I have 2 custom admins. Specially created for reporting services. Those admins do not have specific entities.
For the custom admins, I followed the Symfony recipe:
https://symfony.com/doc/3.x/bundles/SonataAdminBundle/cookbook/recipe_custom_view.html
Now, when searching items through the sonata global search, I get a
"Class does not exist" error in
vendor/sonata-project/admin-bundle/src/Resources/views/Core/search.html.twig.
This error is related to the custom admins.
Is there a solution to exclude these custom admins from the global search or to resolve this error?
Admin:
<?php
namespace MainBundle\Admin;
use Sonata\AdminBundle\Admin\AbstractAdmin;
use Sonata\AdminBundle\Route\RouteCollection;
class AccessRightsAdmin extends AbstractAdmin
{
protected $baseRoutePattern = 'accessrights';
protected $baseRouteName = 'Accessrights';
protected function configureRoutes(RouteCollection $collection)
{
$collection->clearExcept(array('list'));
$collection->add('accesRights', 'accessrights');
}
}
Service
services:
system.admin.accessrights:
class: MainBundle\Admin\AccessRightsAdmin
arguments: [~, ~, MainBundle:AccessRightsAdmin]
tags:
- { name: sonata.admin, manager_type: orm, group: sonata.admin.group.System, label: Accessrights }
calls:
- [ setTranslationDomain, [SonataAdminBundle]]
public: true
I found a solution and I'm going to leave it here in case someone need it.
The solution basically is to override the search.html.twig and ignore the admin you want from the search like so:
{% extends base_template %}
{% block title %}{{ 'title_search_results'|trans({'%query%': query}, 'SonataAdminBundle') }}{% endblock %}
{% block breadcrumb %}{% endblock %}
{% block content %}
<h2 class="page-header">{{ 'title_search_results'|trans({'%query%': query}, 'SonataAdminBundle') }}</h2>
{% if query is defined and query is not same as(false) %}
{% set count = 0 %}
<div class="row" data-masonry='{ "itemSelector": ".search-box-item" }'>
{% for group in groups %}
{% set display = group.roles is empty or is_granted(sonata_admin.adminPool.getOption('role_super_admin')) or group.roles|filter(role => is_granted(role))|length > 0 %}
{% if display %}
{% for admin in group.items %}
{% set count = count + 1 %}
{% if admin.code != 'bundle.admin.admin_to_ignore' %}{# in this line right here add the admin you want to ignore in your search #}
{% if admin.hasRoute('create') and admin.hasAccess('create') or admin.hasRoute('list') and admin.hasAccess('list') %}
{{ sonata_block_render({
'type': 'sonata.admin.block.search_result'
}, {
'query': query,
'admin_code': admin.code,
'page': 0,
'per_page': 10,
'icon': group.icon
}) }}
{% endif %}
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
</div>
{% endif %}
{% endblock %}
To override the file you need to put it under the following path:
app -> Resources -> SonataAdminBundle -> views -> Core -> search.html.twig

Override twig variable in included template in Symfony 3

I'm trying to override a variable in the included template.
Can I do this in a Symfony3 & Twig?
My twig template looks like this:
{% set foo = 'bar' %}
{% include 'first.html.twig' %}
{% include 'second.html.twig' %}
// first.html.twig
{{ foo }}
{% set foo = 'second' %}
// second.html.twig
{{ foo }}
I get such a result:
bar bar
But I expect:
bar second
The following Twig code:
{% set a = 42 %}
{{ include("first.twig") }}
Will be compiled to this one:
// line 1
$context["a"] = 42;
// line 2
echo twig_include($this->env, $context, "first.twig");
And twig_include prototype is:
# lib/Twig/Extension/Core.php
function twig_include(Twig_Environment $env, $context, $template, $variables = array(), $withContext = true, $ignoreMissing = false, $sandboxed = false)
So variables are passed by copy, not by reference in included templates. Thus your changes in included templates won't be reflected to including templates.
Moreover, since Twig 2.0, you can't call TwigEnvironment::addGlobal once twig runtime is initialized, so you can't glitch using simple extensions.
All in all, you can understand that if you need to update variables cross templates, it means some template contains business logic and Twig isn't build for that. You need to prepare the whole context in controllers.
Alternatively you may call a PHP class method from TWIG. Example of a page-counter needed when generating a pdf.
Custom class :
class PageCounter
{
private $pageNumber = 0;
public function incrementPageCounter()
{
$this->pageNumber ++;
return $this->pageNumber;
}
}
Controller:
....
$twigVariables = [
...
'pageCounter' => new PageCounter()
];
return $this->render('template.html.twig', $twigVariables);
Twig template (object pageCounter available from any included template)
{{ pageCounter.incrementPageCounter() }} / {{totalPages}}
You just need to do the check and override the variable with another variable :))
{% if name is defined %}
{% set foo = name %}
{% else %}
{% set foo = 'bar' %}
{% endif %}
{% include 'first.html.twig' %}
{% include 'second.html.twig' %}
// first.html.twig
{% set name = 'first' %}
// second.html.twig
{% set name = 'second' %}
Why not override your variable with your include tag/function like :
{% include 'first.html.twig' with {'foo': 'second'} %}
or :
{ include('first.html.twig', {foo: 'second'}) }}

Symfony - easyAdmin changing 'list.row_actions'

Currently I'm using Symfony 3 and implementing Easy Admin backend.
By default, the label available show as something like 'list.row_actions'.
I have read the documentation for Easy Admin, and manage to edit certain label such as Edit and Delete.
But come to confusion why I cannot edit the 'list.row_actions on the list table header.
The same problem I have with the Edit page, specifically the buttons:
Here is some of the configurations I add into config.yml under easy_admin tree:
config.yml
list:
actions:
- { name: 'new', label: 'New' }
- { name: 'search', label: 'Search' }
- { name: 'edit', label: 'Edit' }
- { name: 'delete', label: 'Delete' }
After several tweaking and failures, I have found a solution, it was documented but I did not expect it to be documented there nested.
Here's the github page: Advanced Design Configuration
We will overwrite the default html.twig file by overriding the {{ block }}
Create an Overwrite Folder
First, create a folder easy_admin inside Resources\views.
Suppose you want to change list.row_actions, head over to the source file from easy-admin usually inside:
vendor\javiereguiluz\easyadmin-bundle\views\includes\ In the last version
Finding the source
Find the list.html.twig file and check out its content for a brief. If you CTRL + F (Find) for list.row_actions, it will show you directly something like this. Now copy the whole block
{% block table_head %}
<tr>
{% for field, metadata in fields %}
{% set isSortingField = metadata.property == app.request.get('sortField') %}
{% set nextSortDirection = isSortingField ? (app.request.get('sortDirection') == 'DESC' ? 'ASC' : 'DESC') : 'DESC' %}
{% set _column_label = (metadata.label ?: field|humanize)|trans(_trans_parameters) %}
{% set _column_icon = isSortingField ? (nextSortDirection == 'DESC' ? 'fa-caret-up' : 'fa-caret-down') : 'fa-sort' %}
<th data-property-name="{{ metadata.property }}" class="{{ isSortingField ? 'sorted' }} {{ metadata.virtual ? 'virtual' }} {{ metadata.dataType|lower }} {{ metadata.css_class }}">
{% if metadata.sortable %}
<a href="{{ path('easyadmin', _request_parameters|merge({ sortField: metadata.property, sortDirection: nextSortDirection })) }}">
<i class="fa {{ _column_icon }}"></i>
{{ _column_label|raw }}
</a>
{% else %}
<span>{{ _column_label|raw }}</span>
{% endif %}
</th>
{% endfor %}
{% if _list_item_actions|length > 0 %}
<th>
<span>{{ 'list.row_actions'|trans(_trans_parameters, 'EasyAdminBundle') }}</span>
</th>
{% endif %}
</tr>
{% endblock table_head %}
Pasting to Overwrite File
To paste/overwrite this, you need to create a file with exact name as the filename where you copied it. In this case, list.html.twig. Then paste it inside the file
IMPORTANT : At top of the file, include this tag -
{% extends '#EasyAdmin/default/list.html.twig' %}
{% block table_head %}
<tr>
{% for field, metadata in fields %}
...code continues...
Why overwrite?
If you edit the file from the source code, you will have trouble deploying it especially if you're using git clone and then composer update.
Overwriting make sure that the code persist even when you deploy it.
I've had the same issue. As for me, I fixed it easier.
First of all try to use this
# app/config/config.yml (Symfony3)
# config/packages/framework.yaml (Symfony4)
framework:
translator: { fallbacks: "%locale%" }
But if you want to override with your translation, create this file:
# translations/EasyAdminBundle.en.yaml
list.row_actions: "My actions"

KNP Menu Bundle Translation Domain

I'm using Symfony 2.3 and the KnpMenuBundle.
Is it possible to use a translation domain for menu items?
Like this:
$menu['management']->addChild(
'msg.user.list',
array(
'route' => 'user_list',
'translation_domain' => 'navigation'
)
);
According to the Symfony documentation and KnpMenuBundle documentation,you may set the translation domain (menu in my snippets), while adding menu items in your MenuBuilder class:
$menu->addChild('Home', array('route' => 'homepage'))
->setExtra('translation_domain', 'menu');
You may better want to add the translation domain into the whole menu instead:
$menu = $this->factory->createItem('root')
->setExtra('translation_domain', 'menu');
Then create a file named knp_menu.html.twig in:
app/Resources/views/menu/
and put this in it:
{% extends 'knp_menu.html.twig' %}
{% block label %}
{% if options.allow_safe_labels and item.getExtra('safe_label', false) %}
{{ item.label | raw | trans(item.getExtra('translation_params', {}), item.getExtra('translation_domain', 'menu'))}}
{% else %}
{{ item.label | trans(item.getExtra('translation_params', {}), item.getExtra('translation_domain', 'menu')) }}
{% endif %}
{% endblock %}
(If the file already exists, just replace the {% block label %}{% endblock %} part)
Be carefull to have translation files (.xliff or whatever) naming strategy like:
{translation_domain}.{locale}.{extenstion}
for example:
menu.fa.xliff
In this path:
app/Resources/translations/
The last part is:
# app/config/config.yml
knp_menu:
twig:
template: knp_menu.html.twig
Up-Vote this nice tutorial.
Have fun!

Resources