Forums › Forums › OroPlatform › OroPlatform – Programming Questions › Workflow transition form with custom page template
This topic contains 16 replies, has 3 voices, and was last updated by Bhavesh Tailor 7 years, 3 months ago.
Starting from March 1, 2020 the forum has been switched to the read-only mode. Please head to StackOverflow for support.
- CreatorTopic
- June 16, 2016 at 7:45 pm #34175
Here’s what I’m trying to accomplish: I have a custom entity Course with a OneToMany to another custom entity, Registration. The Course has a workflow attached which when transitioned to “Completed” asks the manager to mark each Registration as either “attended” or “absent” (using the approach outlined in the Editable Data Grid cells tutorial), which will then trigger a transition on the workflow of each individual Registration.
I’ve run into a few roadblocks that I was hoping someone could help me out on or suggest a better way to accomplish the above:
There doesn’t seem to be a way to add an arbitrary widget to transitionForm.html.twig, so I’ve copied it to my bundle, added my widget, then activated it by setting the page_template attribute on the “complete” transition:
snippet_from_workflow_yml_transitions_sectionYAML123456789101112131415161718complete:label: Mark Completedstep_to: completedtransition_definition: complete_definitionfrontend_options:icon: 'icon-ok'class: 'btn-success'display_type: pagepage_template: MyBundle:Course:Workflow/complete.html.twigform_options:attribute_fields:signoffData:form_type: oro_entity_changesetoptions:required: trueclass: Namespace\Bundle\MyBundle\Entity\Registrationconstraints:- NotBlank: ~My Twig-fu is not strong so I may just be missing the right way to extend that template and add the widget to var dataBlocks?
For now my approach on that works fine. Next step: where do I hook into the form processing so I can read the data from that oro_entity_changeset? The editable grid cells document shows a custom form handler, but WorkflowBundle’s WidgetController has no extension point in it’s form processing action (route: oro_workflow_widget_transition_form). (on further research, maybe a custom transition action is what I should be using here?)
That brings me to the third question: After tracing the process down through the above to the transition handler this line puzzles me:
PHP123if ($transition->getPageTemplate() || $transition->getDialogTemplate()) {return;}(link). The transition handler completely short-circuits if you’re using a custom page or dialog template. When I press the “submit” button on the transition form it looks like nothing happens, I just stay on (or come back to?) the transition form because the transition handler doesn’t return the transition success response. Is there a separate handler or process for transitions which have their own custom page templates? Can I just override the transition handler and remove the short-circuit?
- CreatorTopic
- AuthorReplies
- June 22, 2016 at 7:45 am #34176
Hi,
Could you please tell me what do you mean under arbitrary widget?
If you want to override transitionFormWidget variable that you must fully override vendor/oro/platform/src/Oro/Bundle/WorkflowBundle/Resources/views/Workflow/transitionForm.html.twig
If you want to customize form view, you can modify vendor/oro/platform/src/Oro/Bundle/WorkflowBundle/Resources/views/Widget/widget/transitionForm.html.twig
I want to understand what do you using editable grid cells with workflow transition form? If you want to update all yours Registration entities after choosing “attended” or “absent” just write your own transition_definitions. Here is example how you can do it.
P.S. editable grid cells is deprecated feature, better use inline editing.
June 22, 2016 at 5:15 pm #34177Could you please tell me what do you mean under arbitrary widget?
The transitionForm.html.twig page has one widget / section / datablock (not sure what the correct terminology is) called “General Information”. I was looking to add a second one to the page without having to copy the whole exisitng template to my own bundle just to add the necessary entry into the dataBlocks variable.
I want to understand what do you using editable grid cells with workflow transition form? If you want to update all yours Registration entities after choosing “attended” or “absent” just write your own transition_definitions. Here is example how you can do it.
When an admin triggers the “complete” transition on the course they must make a separate Complete/Incomplete selection per each one of those Registration records (basically a “did they attend?” question for each attendee).
I’ve set this up by overriding the transition form template to add the datagrid which populates selections into a hidden form elememt on the transition form. It’s probably not ideal but that seems to work fine (the admin’s selections in the datagrid are sent when the transition form is submitted). The part that’s tripping me up currently is that the transition handler does nothing when you specify a custom page template. Do I need to override the transition handler too?
P.S. editable grid cells is deprecated feature, better use inline editing.
Thanks for the tip!
June 23, 2016 at 7:26 am #34178I was looking to add a second one to the page without having to copy the whole exisitng template to my own bundle just to add the necessary entry into the dataBlocks variable.
Unfortunately you can’t add or modify dataBlocks without copy transitionForm.html.twig.
June 23, 2016 at 7:38 am #34179I have reproduced your task. I have created new custom entities:
- NewCourse with fields name (string), status (text), registration (OneToMany)
- NewRegistration with fields name (string), status (select)
Added workflow src/Custom/Bundle/OroBundle/Resources/config/workflow.yml:
YAML1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677workflows:custom_course:label: 'Course Workflow'entity: Extend\Entity\NewCourseentity_attribute: coursestart_step: newattributes:hidden:label: labeltype: stringsteps:new:label: 'New'order: 10allowed_transitions:- do_completedo_complete:label: 'Complete'order: 20allowed_transitions:- completedcompleted:label: 'Completed'order: 30allowed_transitions:- againtransitions:do_complete:label: 'Complete'step_to: completedis_start: trueis_unavailable_hidden: truedisplay_type: pagepage_template: CustomOroBundle:Workflow:transitionForm.html.twigfrontend_options:icon: 'icon-ok'class: 'btn-primary'form_options:attribute_fields:hidden:form_type: hiddentransition_definition: do_complete_definitionagain:label: 'Again'step_to: newis_unavailable_hidden: truefrontend_options:icon: 'icon-ok'class: 'btn-primary'transition_definition: again_definitiontransition_definitions:do_complete_definition:post_actions:- @tree:actions:- @call_method: # add Address to Contactobject: $coursemethod: setStatusmethod_parameters: ['Status is changed!']- @redirect:route: 'oro_entity_view'route_parameters:entityName: 'Extend_Entity_NewCourse'id: $course.idagain_definition:post_actions:- @tree:actions:- @call_method: # add Address to Contactobject: $coursemethod: setStatusmethod_parameters: ['']Added grid src/Custom/Bundle/OroBundle/Resources/config/datagrid.yml:
YAML1234567891011121314151617181920212223242526272829303132datagrid:course-grid:extended_entity_name: Extend\Entity\NewRegistrationsource:type: ormquery:select:- r.id- r.namefrom:- { table: Extend\Entity\NewRegistration, alias: r }where:and:- r.newcourse_registration = :course_idbind_parameters:- course_idinline_editing:enable: truecolumns:name:label: Namestatus:label: Statusfrontend_type: selectinline_editing:enable: trueeditor:view: oroform/js/app/views/editor/select-editor-viewautocomplete_api_accessor:class: oroui/js/tools/search-api-accessorproperties:id: ~I overrided vendor/oro/platform/src/Oro/Bundle/WorkflowBundle/Controller/WorkflowController.php, transitionAction and added
PHP1'workflowItem' => $workflowItemto second array parameter $this->render function
June 23, 2016 at 7:40 am #34180Added custom src/Custom/Bundle/OroBundle/Resources/views/Workflow/transitionForm.html.twig:
XHTML123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113{% set entity = null %}{% extends 'OroUIBundle:actions:view.html.twig' %}{% import 'OroUIBundle::macros.html.twig' as macros %}{% import 'OroDataGridBundle::macros.html.twig' as dataGrid %}{% set pageParams = transition.frontendOptions.page is defined ?transition.frontendOptions.page : null %}{% if pageParams.title is defined %}{% set pageTitle = pageParams.title %}{% else %}{% set pageTitle = transition.label %}{% endif %}{% if pageParams.parent_label is defined %}{% set indexLabel = pageParams.parent_label %}{% else %}{% set indexLabel = workflow.label %}{% endif %}{% if pageParams.parent_route is defined %}{% set indexPath = path(pageParams.parent_route, pageParams.parent_route_parameters is defined ? pageParams.parent_route_parameters : []) %}{% else %}{% set indexPath = null %}{% endif %}{% oro_title_set({params : {"%workflow_title%": pageTitle|trans ~ ' - ' ~ indexLabel|trans} }) %}{% block navButtons %}{% if indexPath %}{{ UI.cancelButton(indexPath) }}{% endif %}<div class="btn-group"><buttontype="button"class="btn btn-success"id="save-and-transit"data-transition-url="{{ transitionUrl }}">{{ 'Submit'|trans }}</button></div>{% endblock navButtons %}{% block pageHeader %}{% set breadcrumbs = {'indexPath': indexPath,'indexLabel': indexLabel|trans,'entityTitle': pageTitle|trans} %}{% block stats %}{% endblock %}{{ parent() }}{% endblock pageHeader %}{% block breadcrumb %}<ul class="breadcrumb">{{ indexLabel|trans }}{% endblock %}{% block content_data %}<div class="form-container">{{ oro_widget_render({'widgetType': 'block','url': transitionFormUrl,'alias': 'transition-form','loadingMaskEnabled': false}) }}<script type="text/javascript">require(['jquery', 'oroui/js/mediator', 'oroui/js/widget-manager', 'oroworkflow/js/transition-executor'],function($, mediator, widgetManager, performTransition) {var saveAndTransitBtn = $('#save-and-transit');widgetManager.getWidgetInstanceByAlias('transition-form', function(widget) {widget.on('beforeContentLoad', function() {mediator.execute('showLoading');});widget.on('formSave', function(data) {performTransition(saveAndTransitBtn, data)});widget.on('formSaveError', function() {mediator.execute('hideLoading');});});saveAndTransitBtn.on('click', function(e) {e.preventDefault();widgetManager.getWidgetInstanceByAlias('transition-form', function(widget) {widget.form.submit();});});});</script></div>{% set dataBlocks = [{'title' : 'Course Datagrid','subblocks': [{'title' : null,'useSpan': false,'data' : [dataGrid.renderGrid('course-grid', {course_id: workflowItem.entityId})]}]}]%}{% set id = 'transitionPage' %}{% set data = {'dataBlocks': dataBlocks} %}{{ parent() }}{% endblock content_data %}June 23, 2016 at 7:46 am #34181What we have:
- Custom transition template
- We can change Course status after submit
- Datagrid which can inline edit status of Registration entity
I hope these tips will help you reach the goal.
June 24, 2016 at 11:09 am #34182Wow, thanks! I’ve read through what you created there and it’s nearly identical to what I had…glad I was on the right track at least :)
- I use {{app.request.attributes.get(‘workflowItemId’)}} to retrieve the ID of the workflow whereas you overrode WorkflowController::transitionAction to inject it. I don’t like pulling request data directly into the template, so I’ll use your approach instead ;)
- You use the new inline editing (which interacts with the API to do async updates, right?) whereas I used the old editable grid cells method along with an oro_entity_changeset form element, which sends the entity modifications along with the transition form submission.
- I was missing the “redirect” action in my workflow transition definition
One additional requirement that I didn’t outline in my original post was that the changes made in the datagrid to the Registration entity shouldn’t be applied until the Course entity is actually transitioned. Otherwise, one could start the transition, change the record statuses, then not submit the transition form and the Registration entities will have been updated but the Course will not have been transitioned. So I’ll stick with using the oro_entity_changeset method for now as it gives me protection against that. (I could have the datagrid update a “shadow” field on the Registration (transitionalStatus, or similar) and then as part of the Course transition copy the values from there into the “status” field…then, if the course isn’t transitioned the “real” status field doesn’t get updated.
After picking up a few tips from your example and a bit more poking around myself I’ve sorted out everything that I was missing to get my approach to work. The major missing piece was needing to override Oro\Bundle\WorkflowBundle\Controller\WidgetController::transitionFormAction to process the data provided by oro_entity_changeset and store it in WorkflowItem#data:
PHP1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586<?php// namespace & use snipped for brevityclass WorkflowWidgetController extends BaseController{/*** @Route(* "/transition/edit/attributes/{workflowItemId}/{transitionName}",* name="oro_workflow_widget_transition_form"* )* @ParamConverter("workflowItem", options={"id"="workflowItemId"})* @AclAncestor("oro_workflow")* @param string $transitionName* @param WorkflowItem $workflowItem* @return array*/public function transitionFormAction($transitionName, WorkflowItem $workflowItem){/** @var WorkflowManager $workflowManager */$workflowManager = $this->get('oro_workflow.manager');$workflow = $workflowManager->getWorkflow($workflowItem);$transition = $workflow->getTransitionManager()->extractTransition($transitionName);$transitionForm = $this->getTransitionForm($workflowItem, $transition);$saved = false;if ($this->getRequest()->isMethod('POST')) {$transitionForm->submit($this->getRequest());if ($transitionForm->isValid()) {// START @HACK @HACK @HACK$this->processSignoffDataOnFormSubmit($transitionForm,$workflowItem);// END @HACK @HACK @HACK$workflowItem->setUpdated();$this->getEntityManager()->flush();$saved = true;$response = $this->get('oro_workflow.handler.transition_handler')->handle($transition, $workflowItem);if ($response) {return $response;}}}return $this->render($transition->getDialogTemplate() ?: self::DEFAULT_TRANSITION_TEMPLATE,array('transition' => $transition,'saved' => $saved,'workflowItem' => $workflowItem,'form' => $transitionForm->createView(),));}/*** If form has "signoff" data, process it and stash the result in WorkflowItem#data** @param Form $transitionForm* @param WorkflowItem $workflowItem*/private function processSignoffDataOnFormSubmit(Form $transitionForm, WorkflowItem $workflowItem){if ( ! $transitionForm->has('signoff') ) {return;}$changeset = [];foreach ($transitionForm->get('signoff')->getData() as $item) {if ( ! $item['entity'] instanceof CourseRegistration ) {// @TODO error handling?continue;}$changeset[$item['entity']->getId()] = $item['data'];}$workflowItem->getData()->set('signoff', $changeset);}}Is there a cleaner way to inject this logic into the transition form processing than overriding the whole controller action?
Thanks for your help!
June 27, 2016 at 3:23 am #34183which interacts with the API to do async updates, right?
Yes, you are right.
Is there a cleaner way to inject this logic into the transition form processing than overriding the whole controller action?
You can create form listener, but i’m not sure that you will get access to $workflowItem
One question, $transitionForm is creating from $workflowItem->getData(), i don’t understand, why do you select each signoff from form and again set it to $workflowItem data?
June 27, 2016 at 7:21 am #34184You can create form listener, but i’m not sure that you will get access to $workflowItem
Thanks, I’ll give that a try.
One question, $transitionForm is creating from $workflowItem->getData(), i don’t understand, why do you select each signoff from form and again set it to $workflowItem data?
In my workflow I have a custom action “complete_course_offering” to process the admin’s selection for each Registration (stored in the oro_entity_changeset):
12345678910111213complete_definition:post_actions:- @tree:actions:- @complete_course_offering:offering: $offering- @flash_message:message: 'The Course Offering has been marked as completed.'type: 'success'- @redirect:route: 'psaccess_course_offering_view'route_parameters:id: $offering.idThat custom action just iterates over the collection of Registration records for the Course and transits each record’s workflow based on what the admin selected (Complete or Incomplete).
When that custom action attempts to pull the data from the “signoff” element of the WorkflowItem, like this:
1$signoff = $workflowItem->getData()->get('signoff');It gets a NULL. The purpose of the
processSignoffDataOnFormSubmit
I showed in my previous post is to take the data submitted through the oro_entity_changeset element and stash it in the workflow item.I’ve also tried passing the signoff form data through a parameter on the action definition, eg:
123- @complete_course_offering:offering: $offeringsignoff: $signoff(where “signoff” is the name of the form element added under form_options -> attribute_fields section, and also a workflow attribute.)
July 2, 2016 at 4:44 pm #34185Marking as resolved
December 21, 2016 at 6:54 am #34186Hello ,
I want to add Consult date into Close as Won Pop-up Window . But i am not getting how to add this field into this popup form can you please suggest me because i am using orocrm first time so i dont have idea how to add ,
Thank You
BhaveshBhavesh
December 21, 2016 at 11:25 pm #34187Hello,
I got the solution for add field but need to override b2b_flow_sales_funnel/transitions.yml . I am not able to override can you please suggest how to override this transitions.yml using custom salesBundle.Thank you in advance ..
Bhavesh
Bhavesh
December 26, 2016 at 3:51 am #34188Hi
Simply you should clone your ‘B2B Sales Process Flow’ workflow, activate your cloned workflow, click edit workflow, find certain transition, click edit transition, go to attributes tab and add field. Detailed information about workflow you can find in documentation. Also i wrote useful topic about overriding parts of orocrm.
December 27, 2016 at 10:49 pm #34189Hi, Mike Kudelya
I did created clone workflow and now it’s working fine in my local server is there any way to export this and import into live server . i am doing try like already created workflow but is any any other way ?.
Thank You .
Bhavesh
December 29, 2016 at 5:27 am #34190Hi
You can’t import workflow on live server. You can consider these variants:
1. create sql dump of your local database or certain workflow tables (but i think it is bad idea)
2. customize orocrm and override transitions.yml
3. add the same field on live server. - AuthorReplies
The forum ‘OroPlatform – Programming Questions’ is closed to new topics and replies.