(Retrofit from trunk) #1284: Fixed portal issue when trying to re-open a ticket as a portal user. Cause was that the destination state had "must prompt" attributes that were all "read only" for the current user, making the entire form "read only" and therefore removing "submit" button. The user was the not able to complete the transition. Fix consists of skipping the form when all attributes are "read only" for the user.

Also :
- Refactored a portion of TWIG (Loader is now in an helper TWIG)
- Placed transition buttons to the right with the submit one as it was confusing

SVN:2.3[4338]
This commit is contained in:
Guillaume Lajarige
2016-08-23 12:41:56 +00:00
parent 93bbfeae1f
commit 950c868230
10 changed files with 130 additions and 78 deletions

View File

@@ -335,6 +335,9 @@ class ObjectController extends AbstractController
$oApp->abort(404, Dict::S('UI:ObjectDoesNotExist'));
}
// Retrieving request parameters
$sOperation = $oRequest->request->get('operation');
// Preparing a dedicated form for the stimulus application
$aFormProperties = array(
'id' => 'apply-stimulus',
@@ -372,14 +375,39 @@ class ObjectController extends AbstractController
'url' => $oApp['url_generator']->generate('p_object_edit', array('sObjectClass' => $sObjectClass, 'sObjectId' => $sObjectId))
);
// TODO : This is a ugly patch to avoid showing a modal with a readonly form to the user as it would prevent user from finishing the transition.
// Instead, we apply the stimulus directly here and then go to the edited object.
if ($sOperation === null)
{
if (isset($aData['form']['editable_fields_count']) && $aData['form']['editable_fields_count'] === 0)
{
$sOperation = 'redirect';
$oSubRequest = $oRequest;
$oSubRequest->request->set('operation', 'submit');
$oSubRequest->request->set('stimulus_code', null);
$aData = array('sMode' => 'apply_stimulus');
$aData['form'] = $this->HandleForm($oSubRequest, $oApp, $aData['sMode'], $sObjectClass, $sObjectId, $aFormProperties);
// Redefining the array to be as simple as possible :
$aData = array('redirection' =>
array('url' => $oApp['url_generator']->generate('p_object_edit', array('sObjectClass' => $sObjectClass, 'sObjectId' => $sObjectId)))
);
}
}
// Preparing response
if ($oRequest->isXmlHttpRequest())
{
// We have to check whether the 'operation' parameter is defined or not in order to know if the form is required via ajax (to be displayed as a modal dialog) or if it's a lifecycle call from a existing form.
if ($oRequest->request->get('operation') === null)
if ($sOperation === null)
{
$oResponse = $oApp['twig']->render('itop-portal-base/portal/src/views/bricks/object/modal.html.twig', $aData);
}
elseif ($sOperation === 'redirect')
{
$oResponse = $oApp['twig']->render('itop-portal-base/portal/src/views/modal/mode_loader.html.twig', $aData);
}
else
{
$oResponse = $oApp->json($aData);

View File

@@ -24,7 +24,7 @@
{% if form.buttons is defined and form.buttons.transitions is defined and form.buttons.transitions|length > 0 %}
<div class="form_btn_transitions">
{% for sStimulusCode, sStimulusLabel in form.buttons.transitions %}
<button class="btn btn-default form_btn_transition" type="submit" name="stimulus_code" value="{{ sStimulusCode }}">{{ sStimulusLabel }}</button>
<button class="btn btn-primary form_btn_transition" type="submit" name="stimulus_code" value="{{ sStimulusCode }}">{{ sStimulusLabel }}</button>
{% endfor %}
</div>
{% endif %}
@@ -67,60 +67,63 @@
// Sticky buttons handler
{% if sMode != 'view' %}
// Note : This pattern if to prevent performance issues
// - Cloning buttons
var oNormalRegularButtons_{{ sFormIdSanitized }} = $('#{{ sFormId }} .form_btn_regular');
var oStickyRegularButtons_{{ sFormIdSanitized }} = oNormalRegularButtons_{{ sFormIdSanitized }}.clone(true, true);
oStickyRegularButtons_{{ sFormIdSanitized }}.addClass('sticky');
if(oStickyRegularButtons_{{ sFormIdSanitized }}.find('.form_btn_submit span.glyphicon').length > 0)
if( $('#{{ sFormId }} .form_btn_regular button').length > 0 )
{
oStickyRegularButtons_{{ sFormIdSanitized }}.find('.form_btn_submit').html( oStickyRegularButtons_{{ sFormIdSanitized }}.find('.form_btn_submit span.glyphicon')[0].outerHTML );
}
if(oStickyRegularButtons_{{ sFormIdSanitized }}.find('.form_btn_cancel span.glyphicon').length > 0)
{
oStickyRegularButtons_{{ sFormIdSanitized }}.find('.form_btn_cancel').html( oStickyRegularButtons_{{ sFormIdSanitized }}.find('.form_btn_cancel span.glyphicon')[0].outerHTML );
}
$('#{{ sFormId }}').closest({% if tIsModal == true %}'.modal'{% else %}'#main-content'{% endif %}).append(oStickyRegularButtons_{{ sFormIdSanitized }});
// - Global timeout for any
var oScrollTimeout;
// - Scroll handler
scrollHandler_{{ sFormIdSanitized }} = function () {
if($('#{{ sFormId }} .form_buttons').visible())
// Note : This pattern if to prevent performance issues
// - Cloning buttons
var oNormalRegularButtons_{{ sFormIdSanitized }} = $('#{{ sFormId }} .form_btn_regular');
var oStickyRegularButtons_{{ sFormIdSanitized }} = oNormalRegularButtons_{{ sFormIdSanitized }}.clone(true, true);
oStickyRegularButtons_{{ sFormIdSanitized }}.addClass('sticky');
if(oStickyRegularButtons_{{ sFormIdSanitized }}.find('.form_btn_submit span.glyphicon').length > 0)
{
oStickyRegularButtons_{{ sFormIdSanitized }}.addClass('closed');
oStickyRegularButtons_{{ sFormIdSanitized }}.find('.form_btn_submit').html( oStickyRegularButtons_{{ sFormIdSanitized }}.find('.form_btn_submit span.glyphicon')[0].outerHTML );
}
else
if(oStickyRegularButtons_{{ sFormIdSanitized }}.find('.form_btn_cancel span.glyphicon').length > 0)
{
oStickyRegularButtons_{{ sFormIdSanitized }}.removeClass('closed');
oStickyRegularButtons_{{ sFormIdSanitized }}.find('.form_btn_cancel').html( oStickyRegularButtons_{{ sFormIdSanitized }}.find('.form_btn_cancel span.glyphicon')[0].outerHTML );
}
};
// - Event binding for scroll
$({% if tIsModal == true %}'.modal.in'{% else %}window{% endif %}).off('scroll').on('scroll', function () {
if (oScrollTimeout) {
// Clear the timeout, if one is pending
clearTimeout(oScrollTimeout);
oScrollTimeout = null;
}
oScrollTimeout = setTimeout(scrollHandler_{{ sFormIdSanitized }}, 50);
});
// - Event binding for linkedset collapse
$({% if tIsModal == true %}'.modal.in'{% else %}window{% endif %}).off('shown.bs.collapse hidden.bs.collapse').on('shown.bs.collapse hidden.bs.collapse', function () {
scrollHandler_{{ sFormIdSanitized }}();
});
// - Event binding for form building / updating
// Note : We do not want to 'off' the event or it will remove listeners from the widget
oFieldSet_{{ sFormIdSanitized }}.on('form_built', function(oEvent){
scrollHandler_{{ sFormIdSanitized }}();
});
// - Initial test
setTimeout(function(){ scrollHandler_{{ sFormIdSanitized }}(); }, 400);
// Remove sticky button when closing modal
$('#{{ sFormId }}').closest('.modal').on('hide.bs.modal', function () {
oStickyRegularButtons_{{ sFormIdSanitized }}.remove();
});
$('#{{ sFormId }}').closest({% if tIsModal == true %}'.modal'{% else %}'#main-content'{% endif %}).append(oStickyRegularButtons_{{ sFormIdSanitized }});
// - Global timeout for any
var oScrollTimeout;
// - Scroll handler
scrollHandler_{{ sFormIdSanitized }} = function () {
if($('#{{ sFormId }} .form_buttons').visible())
{
oStickyRegularButtons_{{ sFormIdSanitized }}.addClass('closed');
}
else
{
oStickyRegularButtons_{{ sFormIdSanitized }}.removeClass('closed');
}
};
// - Event binding for scroll
$({% if tIsModal == true %}'.modal.in'{% else %}window{% endif %}).off('scroll').on('scroll', function () {
if (oScrollTimeout) {
// Clear the timeout, if one is pending
clearTimeout(oScrollTimeout);
oScrollTimeout = null;
}
oScrollTimeout = setTimeout(scrollHandler_{{ sFormIdSanitized }}, 50);
});
// - Event binding for linkedset collapse
$({% if tIsModal == true %}'.modal.in'{% else %}window{% endif %}).off('shown.bs.collapse hidden.bs.collapse').on('shown.bs.collapse hidden.bs.collapse', function () {
scrollHandler_{{ sFormIdSanitized }}();
});
// - Event binding for form building / updating
// Note : We do not want to 'off' the event or it will remove listeners from the widget
oFieldSet_{{ sFormIdSanitized }}.on('form_built', function(oEvent){
scrollHandler_{{ sFormIdSanitized }}();
});
// - Initial test
setTimeout(function(){ scrollHandler_{{ sFormIdSanitized }}(); }, 400);
// Remove sticky button when closing modal
$('#{{ sFormId }}').closest('.modal').on('hide.bs.modal', function () {
oStickyRegularButtons_{{ sFormIdSanitized }}.remove();
});
}
{% endif %}
{% if tIsModal == true %}

View File

@@ -8,7 +8,7 @@
{% if form.buttons is defined and form.buttons.links is defined %}
<div class="form_btn_transitions">
{% for aLink in form.buttons.links %}
<a class="btn btn-default" href="{{ aLink.url }}">{{ aLink.label }}</a>
<a class="btn btn-primary" href="{{ aLink.url }}">{{ aLink.label }}</a>
{% endfor %}
</div>
{% endif %}

View File

@@ -0,0 +1,6 @@
<div class="content_loader">
<div class="icon glyphicon glyphicon-refresh"></div>
<div class="message">
{{ 'Page:PleaseWait'|dict_s }}
</div>
</div>

View File

@@ -22,7 +22,7 @@
{# This block can be used to add your own meta tags by extending the default template #}
{% block pPageExtraMetas %}
{% endblock %}
<title>{% block pPageTitle %}{% if sPageTitle is defined and sPageTitle is not null %}{{ sPageTitle }} - iTop{% else %}{{ 'Page:DefaultTitle'|dict_s }}{% endif %}{% endblock %}</title>
<title>{% block pPageTitle %}{% if sPageTitle is defined and sPageTitle is not null %}{{ sPageTitle }} - {{ constant('ITOP_APPLICATION') }}{% else %}{{ 'Page:DefaultTitle'|dict_s }}{% endif %}{% endblock %}</title>
<link rel="shortcut icon" href="{{ app['combodo.absolute_url'] }}images/favicon.ico?itopversion=$ITOP_VERSION$" />
{% block pPageStylesheets %}
{# First bootstrap core, lib themes, then bootstrap theme, portal adjustements #}
@@ -260,12 +260,7 @@
<div class="modal fade" id="modal-for-all" role="dialog">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="content_loader">
<div class="icon glyphicon glyphicon-refresh"></div>
<div class="message">
{{ 'Page:PleaseWait'|dict_s }}
</div>
</div>
{% include 'itop-portal-base/portal/src/views/helpers/loader.html.twig' %}
</div>
</div>
</div>
@@ -289,12 +284,7 @@
<div id="page_overlay" class="global_overlay">
<div class="overlay_content">
<div class="content_loader">
<div class="icon glyphicon glyphicon-refresh"></div>
<div class="message">
{{ 'Page:PleaseWait'|dict_s }}
</div>
</div>
{% include 'itop-portal-base/portal/src/views/helpers/loader.html.twig' %}
</div>
</div>
{% endblock %}

View File

@@ -1,12 +1,14 @@
{# modal/layout.html.twig #}
{# Base modal layout, used to fill Bootstrap .modal-content #}
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4 class="modal-title">{% block pModalTitle %}{% endblock %}</h4>
</div>
<div class="modal-body">{% block pModalBody %}{% endblock %}</div>
<div class="modal-footer">
{% block pModalFooter %}
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'Portal:ButtonClose'|dict_s }}</button>
{% endblock %}
</div>
{% block pModalContent %}
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4 class="modal-title">{% block pModalTitle %}{% endblock %}</h4>
</div>
<div class="modal-body">{% block pModalBody %}{% endblock %}</div>
<div class="modal-footer">
{% block pModalFooter %}
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'Portal:ButtonClose'|dict_s }}</button>
{% endblock %}
</div>
{% endblock %}

View File

@@ -0,0 +1,15 @@
{# itop-portal-base/portal/src/views/bricks/object/mode_loader.html.twig #}
{# Modal loader layout #}
{% extends 'itop-portal-base/portal/src/views/modal/layout.html.twig' %}
{% block pModalContent %}
{% include 'itop-portal-base/portal/src/views/helpers/loader.html.twig' %}
{% if redirection is defined and redirection.url is defined %}
<script type="text/javascript">
$(document).ready( function(){
window.location = '{{ redirection.url|raw }}';
});
</script>
{% endif %}
{% endblock %}

View File

@@ -816,7 +816,8 @@ table .group-actions {
@media (min-width: 768px) {
/* Making regular button sticky */
.form_buttons .form_btn_transitions {
float: left !important;
float: right !important;
margin-left: 3px;
}
.form_buttons .form_btn_regular {
text-align: right;

View File

@@ -857,7 +857,8 @@ table .group-actions .item-action-wrapper .panel-body > p:last-child{
}
@media (min-width: 768px){
.form_buttons .form_btn_transitions{
float: left !important;
float: right !important;
margin-left: 3px;
}
.form_buttons .form_btn_regular{
text-align: right;

View File

@@ -171,7 +171,13 @@ $(function()
oModalElem.attr('id', '').appendTo('body');
// Loading content
oModalElem.find('.modal-content').html($('#page_overlay .overlay_content').html());
oModalElem.find('.modal-content').load(sUrl);
oModalElem.find('.modal-content').load(sUrl, {
// Passing formmanager data to the next page, just in case it needs it (eg. when applying stimulus)
formmanager_class: me.options.formmanager_class,
formmanager_data: JSON.stringify(me.options.formmanager_data)
}
);
oModalElem.modal('show');
}
else