/   /   /  OroCRM and BAP Concept : Ui Placeholder best practice (More actions , inject holder , filters)

Note:

For more extensions and themes visit our store

OroCRM and BAP Concept : Ui Placeholder best practice (More actions , inject holder , filters)


The UiBundle in OroCRM is rich and  very flexible , but the courses about 'use case' and practice  is very few .
Ibnab Team decided to make a reference needed by programmers, combine theoretical and practical lessons .

Theoretical paragraph

We introduce in this course the idea of activityButton (action button) and activityLink (action link if you go for example to page contact view and click in More action , all this links is action links)  , and how you can use with  Placeholder  , how the system work ,how read from your placeholders.yml.

Ok inside Oro BAP you have bundle with name  UIBundle for managing layouts templates and actions without touching the core just with injection or implementation in real time of application execution  , in order to improve layouts and make them more flexible a new twig token placeholder is implemented. It allows us to combine several blocks (templates or actions) and output them in different places in twig templates. This way we can customize layouts without modifying twig templates.

Practical paragraph :
1 – Create your action link and button
So for inject your Two actions without touching the core or override the template of Oro you need pass by placeholder  .
ok inisde you Resource\config\inside placeholdes.yml :

  1.  
  2. items:
  3.     ibnab_doc_add_button:
  4.         template: IbnabDocBundle:Doc:activityButton.html.twig
  5.         acl: doc_defaut_create
  6.     ibnab_doc_add_link:
  7.         template: IbnabDocBundle:Doc:activityLink.html.twig
  8.         acl: doc_defaut_create
  9.  

This is very simple you declared here link and action button with it template and acl (access control level) ,

The content activityButton.html.twig :

  1.  
  2. {{ UI.clientButton({
  3.     'dataUrl': path(
  4.         'doc_defaut_create', {
  5.             entityClass: oro_class_name(entity, true),
  6.             entityId: entity.id
  7.     }),
  8.     'aCss': 'no-hash',
  9.     'iCss': 'icon-file-alt',
  10.     'dataId': entity.id,
  11.     'label' : 'doc_defaut_create'|trans,
  12.     'widget' : {
  13.         'type' : 'dialog',
  14.         'multiple' : true,
  15.         'refresh-widget-alias': 'activity-list-widget',
  16.         'options' : {
  17.             'alias': 'call-dialog',
  18.             'dialogOptions' : {
  19.                 'title' : 'doc_defaut_create'|trans,
  20.                 'allowMaximize': true,
  21.                 'allowMinimize': true,
  22.                 'dblclick': 'maximize',
  23.                 'maximizedHeightDecreaseBy': 'minimize-bar',
  24.                 'width': 500,
  25.                  autoResize: true,
  26.                  modal: true,
  27.                  minHeight: 100
  28.             }
  29.         }
  30.     }
  31. }) }}
  32.  


The important section is the type   UI.clientButton,  it give as result a button and when you click , it give you the dialog widget …
The content activityLink.html.twig  :
  1.  
  2. {{ UI.clientLink({
  3.     'dataUrl': path(
  4.         'doc_defaut_create', {
  5.             entityClass: oro_class_name(entity, true),
  6.             entityId: entity.id
  7.     }),
  8.     'aCss': 'no-hash',
  9.     'iCss': 'icon-file-alt',
  10.     'dataId': entity.id,
  11.     'label' : 'doc_defaut_create'|trans,
  12.     'widget' : {
  13.         'type' : 'dialog',
  14.         'multiple' : true,
  15.         'refresh-widget-alias': 'activity-list-widget',
  16.         'options' : {
  17.             'alias': 'call-dialog',
  18.             'dialogOptions' : {
  19.                 'title' : 'doc_defaut_create'|trans,
  20.                 'allowMaximize': true,
  21.                 'allowMinimize': true,
  22.                 'dblclick': 'maximize',
  23.                 'maximizedHeightDecreaseBy': 'minimize-bar',
  24.                 'width': 500,
  25.                  autoResize: true,
  26.                  modal: true,
  27.                  minHeight: 100
  28.             }
  29.         }
  30.     }
  31. }) }}
  32.  

But you need declare the responsible provider ( you need declare you actions to provider of UiBundle ) inside services.yml:
services:

  1.  
  2.     ibnab_docwidget_provider.actions:
  3.         parent: oro_ui.widget_provider.action_button.abstract                             
  4.         arguments:
  5.             - ibnab_doc_add_button
  6.             - ibnab_doc_add_link
  7.         tags:
  8.             - { name: oro_ui.view_action_provider, group: activity, priority: 100 }
  9.             - { name: oro_ui.update_action_provider, group: activity, priority: 100 }
  10.  

Ok you declared your actions to provider  but what is this :
in arguments you pass your 2 actions declared inside the placeholders.yml with the same identifier  , inside the tags you tell it I want render my action inside view and update screen , inside group activity , with order 100 normal case is the last ..

You can go to it and see the function  getWidgets:
orocrm2/vendor/oro/platform/src/Oro/Bundle/UIBundle/Provider/ActionButtonWidgetProvider.php

  1.  
  2.     public function getWidgets($object)
  3.     {
  4.         $result = [];
  5.         $buttonWidget = $this->placeholderProvider->getItem($this->buttonWidgetName, ['entity' => $object]);
  6.         if ($buttonWidget) {
  7.             $widget['name']   = $this->buttonWidgetName;
  8.             $widget['button'] = $buttonWidget;
  9.             if (!empty($this->linkWidgetName)) {
  10.                 $linkWidget = $this->placeholderProvider->getItem($this->linkWidgetName, ['entity' => $object]);
  11.                 if ($linkWidget) {
  12.                     $widget['link'] = $linkWidget;
  13.                 }
  14.             }
  15.             $result[] = $widget;
  16.         }
  17.         return $result;
  18.     }
  19.  

This is simple too ,  it read all infos(key,value) and return for merge and inject to global result , but where is rendered  ?
You can go to :
orocrm2/vendor/oro/platform/src/Oro/Bundle/UIBundle/Resources/views/macros.html.twig
and see for example how UiBunlde inject the link action to dropdown , parameters.elements is the big merged actions result .

  1.  
  2. {#
  3.     Create dropdown button
  4.     Parameters - array:
  5.         [
  6.             'label' - button label
  7.             'elements' - dropdown elements
  8.             'html' - html from placeholder
  9.             'aCss'  - additional drop down class
  10.         ]
  11. #}
  12. {% macro dropdownButton(parameters) %}
  13.     <div class="btn-group">
  14.         <a class="btn dropdown-toggle" data-toggle="dropdown" href="#">
  15.             {% if parameters.iCss is defined %}
  16.                 <i class="{{ parameters.iCss }}"></i>
  17.             {% endif %}
  18.             {{ parameters.label }}
  19.             <span class="caret"></span>
  20.         </a>
  21.         <ul class="dropdown-menu {{ parameters.aCss is defined? parameters.aCss : '' }}">
  22.             {% if parameters.elements is defined and parameters.elements is not empty %}
  23.                 {% for item in parameters.elements %}
  24.                     {{ _self.dropdownItem(item) }}
  25.                 {% endfor %}
  26.             {% endif %}
  27.             {% if parameters.html is defined and parameters.html is not empty %}
  28.                 {{ parameters.html|raw }}
  29.             {% endif %}
  30.         </ul>
  31.     </div>
  32. {% endmacro %}
  33.  


Now oro render your action , but is rendered in all place calls cases and contact … how you can filter and showing in the specific place .

2 – Filter the place of where showing your actions :

Ok you can add tag applicable inside placeholders.yml for filtering (show or hide your action )

  1.  
  2. items:
  3.     ibnab_doc_add_button:
  4.         template: IbnabDocBundle:Doc:activityButton.html.twig
  5.         applicable: @ibnab_doc.placeholder.filter->isApplicable($entity$)
  6.         acl: doc_defaut_createview
  7.     ibnab_doc_add_link:
  8.         template: IbnabDocBundle:Doc:activityLink.html.twig
  9.         applicable: @ibnab_docplaceholder.filter->isApplicable($entity$)
  10.         acl: doc_defaut_createview
  11.  


But we use here the function isApplicable, and we pass as parameter the current entity of current view or update screen (related to route and principal entity of current screen ) , we using the class or identifier of service  @ibnab_doc.placeholder.filter , we need create inside services.yml :
  1.  
  2. parameters:
  3.     ibnab_doc.placeholder.filter.class:            Ibnab\Bundle\DocBundle\Placeholder\PlaceholderFilter  
  4. services:
  5.     ibnab_doc.placeholder.filter:
  6.         class: %ibnab_doc.placeholder.filter.class%
  7.         arguments:
  8.             - @oro_entity.doctrine_helper
  9.  


Go to Ibnab\Bundle\DocBundle\Placeholder and create class  PlaceholderFilter.php :
 with content example :
  1.  
  2. <?php
  3. namespace Ibnab\Bundle\DocBundle\Placeholder;
  4. use Doctrine\Common\Util\ClassUtils;
  5. use Oro\Bundle\EntityBundle\ORM\DoctrineHelper;
  6. use Oro\Bundle\EntityExtendBundle\Tools\ExtendHelper;
  7. class PlaceholderFilter
  8. {
  9.    /** @var DoctrineHelper */
  10.     protected $doctrineHelper;
  11.     /**
  12.      * @param DoctrineHelper $doctrineHelper
  13.      */
  14.     public function __construct(
  15.         DoctrineHelper $doctrineHelper
  16.     ) {
  17.         $this->doctrineHelper       = $doctrineHelper;
  18.     }
  19.     /**
  20.      *
  21.      * @param object $entity
  22.      * @return bool
  23.      */
  24.     public function isApplicable($entity)
  25.     {
  26.         if (!is_object($entity)
  27.             || !$this->doctrineHelper->isManageableEntity($entity)
  28.             || $this->doctrineHelper->isNewEntity($entity)
  29.         ) {
  30.             return false;
  31.         }
  32.         $className = ClassUtils::getClass($entity);
  33.         if($className == 'OroCRM\Bundle\CampaignBundle\Entity\Campaign')
  34.         {
  35.          return false;
  36.         }
  37.         return true;
  38.    }
  39. }
  40.  

If you see : with function  isApplicable we create simple filter = “don't show or render my 2 actions if the current principal entity or current of entity  OroCRM\Bundle\CampaignBundle\Entity\Campaign”
look great after explain …...

Comments

Related Posts

make your store more efficient

Solving problems. With open source technology. Professional results. That’s what makes Ibnab your best choice

IBNAB is a company made of a group of professionals whose work is providing secure open source solutions. Our company strives for reaching magnificent results with each experience and provides professional open source solutions that cover every part of the business process.