浏览代码

N°637 Portal: Better feedback on exceptions and user session timeout on AJAX calls

git-svn-id: http://svn.code.sf.net/p/itop/code/trunk@4671 a333f486-631f-4898-b8df-5754b55c2be0
glajarige 8 年之前
父节点
当前提交
8e170324b3

+ 3 - 1
datamodels/2.x/itop-portal-base/cs.dict.itop-portal-base.php

@@ -42,10 +42,12 @@ Dict::Add('CS CZ', 'Czech', 'Čeština', array(
     'Portal:Button:Delete' => 'Smazat',
 	'Portal:EnvironmentBanner:Title' => 'You are currently in <strong>%1$s</strong> mode~~',
 	'Portal:EnvironmentBanner:GoToProduction' => 'Go back to PRODUCTION mode~~',
-	'Error:HTTP:404' => 'Stránka nenalezena',
+    'Error:HTTP:401' => 'Authentication~~',
+    'Error:HTTP:404' => 'Stránka nenalezena',
     'Error:HTTP:500' => 'Jejda! Nastal problém',
     'Error:HTTP:GetHelp' => 'Kontaktujte prosím administrátora, pokud problém přetrvá.',
     'Error:XHR:Fail' => 'Data se nepodařilo načíst, kontaktujte prosím administrátora.',
+    'Portal:ErrorUserLoggedOut' => 'You are logged out and need to log in again in order to continue.~~',
     'Portal:Datatables:Language:Processing' => 'Počkejte prosím',
     'Portal:Datatables:Language:Search' => 'Filtr :',
 	'Portal:Datatables:Language:LengthMenu' => 'Zobrazit _MENU_ položek na stránku',

+ 4 - 2
datamodels/2.x/itop-portal-base/de.dict.itop-portal-base.php

@@ -39,11 +39,13 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
 	'Portal:Button:Delete' => 'Löschen',
 	'Portal:EnvironmentBanner:Title' => 'Sie sind im Moment im <strong>%1$s</strong> Modus',
 	'Portal:EnvironmentBanner:GoToProduction' => 'Zurück zum PRODUCTION Modus',
-	'Error:HTTP:404' => 'Seite nicht gefunden.',
+    'Error:HTTP:401' => 'Authentication~~',
+    'Error:HTTP:404' => 'Seite nicht gefunden.',
 	'Error:HTTP:500' => 'Oops! Es ist ein Fehler aufgetreten.',
 	'Error:HTTP:GetHelp' => 'Bitte kontaktieren Sie Ihren iTop administrator falls das Problem öfter auftaucht.',
 	'Error:XHR:Fail' => 'Konnte Daten nicht laden, bitte kontaktieren Sie Ihren iTop administrator',
-	'Portal:Datatables:Language:Processing' => 'Bitte warten...',
+    'Portal:ErrorUserLoggedOut' => 'You are logged out and need to log in again in order to continue.~~',
+    'Portal:Datatables:Language:Processing' => 'Bitte warten...',
 	'Portal:Datatables:Language:Search' => 'Filter :',
 	'Portal:Datatables:Language:LengthMenu' => 'Anzahl _MENU_ Einträge pro Seite',
 	'Portal:Datatables:Language:ZeroRecords' => 'Keine Resultate',

+ 4 - 2
datamodels/2.x/itop-portal-base/en.dict.itop-portal-base.php

@@ -38,11 +38,13 @@ Dict::Add('EN US', 'English', 'English', array(
 	'Portal:Button:Delete' => 'Delete',
 	'Portal:EnvironmentBanner:Title' => 'You are currently in <strong>%1$s</strong> mode',
 	'Portal:EnvironmentBanner:GoToProduction' => 'Go back to PRODUCTION mode',
-	'Error:HTTP:404' => 'Page not found',
+    'Error:HTTP:401' => 'Authentication',
+    'Error:HTTP:404' => 'Page not found',
 	'Error:HTTP:500' => 'Oops! An error has occured.',
 	'Error:HTTP:GetHelp' => 'Please contact your iTop administrator if the problem keeps happening.',
 	'Error:XHR:Fail' => 'Could not load data, please contact your iTop administrator',
-	'Portal:Datatables:Language:Processing' => 'Please wait...',
+    'Portal:ErrorUserLoggedOut' => 'You are logged out and need to log in again in order to continue.',
+    'Portal:Datatables:Language:Processing' => 'Please wait...',
 	'Portal:Datatables:Language:Search' => 'Filter:',
 	'Portal:Datatables:Language:LengthMenu' => 'Display _MENU_ items per page',
 	'Portal:Datatables:Language:ZeroRecords' => 'No result',

+ 4 - 2
datamodels/2.x/itop-portal-base/es_cr.dict.itop-portal-base.php

@@ -38,11 +38,13 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
 	'Portal:Button:Delete' => 'Borrar',
 	'Portal:EnvironmentBanner:Title' => 'You are currently in <strong>%1$s</strong> mode~~',
 	'Portal:EnvironmentBanner:GoToProduction' => 'Go back to PRODUCTION mode~~',
-	'Error:HTTP:404' => 'Página no encontrada',
+    'Error:HTTP:401' => 'Authentication~~',
+    'Error:HTTP:404' => 'Página no encontrada',
 	'Error:HTTP:500' => '¡Vaya! Ha ocurrido un error.',
 	'Error:HTTP:GetHelp' => 'Póngase en contacto con el administrador de iTop si el problema persiste.',
 	'Error:XHR:Fail' => 'No se pudieron cargar datos, póngase en contacto con su administrador de iTop',
-	'Portal:Datatables:Language:Processing' => 'Por favor esperar...',
+    'Portal:ErrorUserLoggedOut' => 'You are logged out and need to log in again in order to continue.~~',
+    'Portal:Datatables:Language:Processing' => 'Por favor esperar...',
 	'Portal:Datatables:Language:Search' => 'Filtrar:',
 	'Portal:Datatables:Language:LengthMenu' => 'Mostrar _MENU_ elementos por página',
 	'Portal:Datatables:Language:ZeroRecords' => 'Sin resultados',

+ 4 - 2
datamodels/2.x/itop-portal-base/fr.dict.itop-portal-base.php

@@ -38,10 +38,12 @@ Dict::Add('FR FR', 'French', 'Français', array(
 	'Portal:Button:Delete' => 'Supprimer',
 	'Portal:EnvironmentBanner:Title' => 'Vous êtes dans le mode <strong>%1$s</strong>',
 	'Portal:EnvironmentBanner:GoToProduction' => 'Retourner au mode PRODUCTION',
-	'Error:HTTP:404' => 'Page non trouvée',
+    'Error:HTTP:401' => 'Authentification',
+    'Error:HTTP:404' => 'Page non trouvée',
 	'Error:HTTP:500' => 'Oups ! Une erreur est survenue.',
 	'Error:HTTP:GetHelp' => 'Si le problème persiste, veuillez contacter votre administrateur iTop.',
-	'Error:XHR:Fail' => 'Impossible de charger les données, veuillez contacter votre administrateur iTop si le problème persiste',
+	'Error:XHR:Fail' => 'Impossible de charger les données, veuillez contacter votre administrateur iTop si le problème persiste.',
+    'Portal:ErrorUserLoggedOut' => 'Vous êtes déconnecté et devez vous reconnecter pour continuer.',
 	'Portal:Datatables:Language:Processing' => 'Veuillez patienter...',
 	'Portal:Datatables:Language:Search' => 'Filtrer :',
 	'Portal:Datatables:Language:LengthMenu' => 'Afficher _MENU_ éléments par page',

+ 4 - 2
datamodels/2.x/itop-portal-base/nl.dict.itop-portal-base.php

@@ -36,11 +36,13 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', array(
 	'Portal:Button:Add' => 'Toevoegen',
 	'Portal:Button:Remove' => 'Verwijderen',
 	'Portal:Button:Delete' => 'Verwijderen',
-	'Error:HTTP:404' => 'Pagina kan niet worden gevonden',
+    'Error:HTTP:401' => 'Authentication~~',
+    'Error:HTTP:404' => 'Pagina kan niet worden gevonden',
 	'Error:HTTP:500' => 'Oeps! Er is een fout opgetreden',
 	'Error:HTTP:GetHelp' => 'Neem contact op met de beheerder als dit probleem zich blijft voordoen',
 	'Error:XHR:Fail' => 'De data kan niet worden geladen, neem contact op met de beheerder',
-	'Portal:Datatables:Language:Processing' => 'Even geduld...',
+    'Portal:ErrorUserLoggedOut' => 'You are logged out and need to log in again in order to continue.~~',
+    'Portal:Datatables:Language:Processing' => 'Even geduld...',
 	'Portal:Datatables:Language:Search' => 'Filter :',
 	'Portal:Datatables:Language:LengthMenu' => 'Toon _MENU_ items per pagina',
 	'Portal:Datatables:Language:ZeroRecords' => 'Geen resultaten',

+ 1 - 1
datamodels/2.x/itop-portal-base/portal/src/controllers/objectcontroller.class.inc.php

@@ -275,7 +275,7 @@ class ObjectController extends AbstractController
 	public function CreateFromFactoryAction(Request $oRequest, Application $oApp, $sObjectClass, $sObjectId, $sEncodedMethodName)
 	{
 		$sMethodName = base64_decode($sEncodedMethodName);
-		
+
 		// Checking that the factory method is valid
 		if (!is_callable($sMethodName))
 		{

+ 5 - 8
datamodels/2.x/itop-portal-base/portal/src/views/bricks/create/modal.html.twig

@@ -28,14 +28,11 @@
                 sUrl = AddParameterToUrl(sUrl, 'ar_token', '{{ ar_token }}');
                 // Loading form
                 oModalElem.find('.modal-content').load(sUrl, function(oData, sStatus, oXHR){
-                    var oResponse = (oXHR.responseJSON !== undefined) ? oXHR.responseJSON : JSON.parse(oXHR.responseText);
-
-                    // Note : This could be refactored for a global use
-                    oModalElem.html( $('#modal-for-alert').html() );
-                    oModalElem.find('.modal-title').html(oResponse.error_title);
-                    oModalElem.find('.modal-body .alert').html(oResponse.error_message)
-                        .removeClass('alert-success alert-info alert-warning alert-danger')
-                        .addClass('alert-danger');
+                    if(sStatus === 'error')
+                    {
+                        // Hiding modal in case of error as the general AJAX error handler will display a message
+                        oModalElem.modal('hide');
+                    }
                 });
             });
         });

+ 1 - 1
datamodels/2.x/itop-portal-base/portal/src/views/bricks/layout.html.twig

@@ -35,11 +35,11 @@
 {% block pPageLiveScriptHelpers %}
     {{ parent() }}
 
+	// Helpers used for brick's opening target
 	var SetActionUrl = function(oElem, sUrl)
 	{
 		oElem.attr('href', sUrl);
 	};
-
 	var SetActionOpeningTarget = function(oElem, sMode)
 	{
 		if(sMode === '{{ constant('Combodo\\iTop\\Portal\\Brick\\PortalBrick::ENUM_OPENING_TARGET_MODAL') }}')

+ 13 - 8
datamodels/2.x/itop-portal-base/portal/src/views/bricks/object/mode_search_regular.html.twig

@@ -172,22 +172,27 @@
 					{% endif %}
 				},
 				"error": function(oData, sError, sThrow){
+				    // Error returned by the framework
 					if(oData.responseJSON !== undefined && oData.responseJSON !== null)
 					{
 						var oResponse = oData.responseJSON;
 						// If we encounter an error
 						if(oResponse.exception !== undefined)
 						{
-							// Note : This could be refactored for a global use
-							$('#{{ sTableId }}').closest('.modal').html( $('#modal-for-alert').html() );
-							var oModalElem = $('#{{ sTableId }}').closest('.modal');
-							oModalElem.find('.modal-title').html(oResponse.error_title);
-							oModalElem.find('.modal-body .alert').html(oResponse.error_message)
-									.removeClass('alert-success alert-info alert-warning alert-danger')
-									.addClass('alert-danger');
-							oModalElem.modal('show');
+							{# Hiding modal in case of error as the general AJAX error handler will display a message #}
+                            {% if tIsModal is defined and tIsModal == true %}
+                            	$('#{{ sFormId }}').closest('.modal').modal('hide');
+                            {% endif %}
 						}
 					}
+					// Global failure
+					else
+					{
+						{# Hiding modal in case of error as the general AJAX error handler will display a message #}
+                        {% if tIsModal is defined and tIsModal == true %}
+                        	$('#{{ sFormId }}').closest('.modal').modal('hide');
+                        {% endif %}
+					}
 				}
 			}
 		});

+ 47 - 4
datamodels/2.x/itop-portal-base/portal/src/views/layout.html.twig

@@ -68,7 +68,7 @@
 		<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] ~ 'lib/latinise/latinise.min.js'|add_itop_version }}"></script>
 		{# Visible.js to check if an element is visible on screen #}
 		<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] ~ 'lib/jquery-visible/js/jquery.visible.min.js'|add_itop_version }}"></script>
-		{# Base64.js #}
+        {# Base64.js #}
 		<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] ~ 'lib/jquery-base64/js/jquery.base64.min.js'|add_itop_version }}"></script>
 		{# Moment.js #}
 		<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] ~ 'lib/moment/js/moment.min.js'|add_itop_version }}"></script>
@@ -314,10 +314,12 @@
 	{% block pPageLiveScripts %}
 		<script type="text/javascript">
 			{% block pPageLiveScriptHelpers %}
+				// Helper to get the application root url
 				var GetAbsoluteUrlAppRoot = function()
 				{
 					return '{{ app['combodo.absolute_url'] }}';
 				};
+				// Helper to add a parameter to an url
 				var AddParameterToUrl = function(sUrl, sParamName, sParamValue)
 				{
 					sUrl += (sUrl.split('?')[1] ? '&':'?') + sParamName + '=' + sParamValue;
@@ -327,6 +329,33 @@
 				{
 					return '<div class="content_loader"><div class="icon glyphicon glyphicon-refresh"></div><div class="message">{{ 'Page:PleaseWait'|dict_s }}</div></div>';
 				}
+				var ShowLoginDialog = function()
+				{
+                    var oModalElem = $('#modal-for-alert').clone();
+                    oModalElem.attr('id', '');
+                    oModalElem.find('.modal-content .modal-header .modal-title').html('{{ 'Error:HTTP:401'|dict_s|escape('js') }}');
+                    oModalElem.find('.modal-content .modal-body .alert').addClass('alert-danger').html('{{ 'Portal:ErrorUserLoggedOut'|dict_s|escape('js') }}');
+
+                    oModalElem.find('.modal-content .modal-body button').replaceWith( $('<button type="button" class="btn btn-primary" onclick="javascript:window.location.reload();">{{ 'UI:LogOff:ClickHereToLoginAgain'|dict_s|escape('js') }}</button>') );
+
+                    oModalElem.appendTo('body');
+                    oModalElem.modal('show');
+				};
+				var ShowErrorDialog = function(sBody, sTitle)
+				{
+				    if(sTitle === undefined)
+					{
+					    sTitle = '{{ 'Error:HTTP:500'|dict_s|escape('js') }}';
+					}
+				    if(sBody === undefined)
+					{
+                        sBody = '{{ 'Error:XHR:Fail'|dict_s|escape('js') }}';
+					}
+					var oModalElem = $('#modal-for-alert');
+                    oModalElem.find('.modal-content .modal-header .modal-title').html(sTitle);
+                    oModalElem.find('.modal-content .modal-body .alert').addClass('alert-danger').html(sBody);
+                    oModalElem.modal('show');
+				};
 			{% endblock %}
 			
 			$(document).ready(function(){
@@ -354,10 +383,24 @@
 						
 						if( (sModalContent === '') || (sModalContent.replace(/[\n\r\t]+/g, '') === GetContentLoaderTemplate()) )
 						{
-							$(oEvent.target).find('.modal-content').html($('#modal-for-alert .modal-content').html());
-							$(oEvent.target).find('.modal-content .modal-header .modal-title').text('{{ 'Error:HTTP:500'|dict_s }}');
-							$(oEvent.target).find('.modal-content .modal-body .alert').addClass('alert-danger').text('{{ 'Error:XHR:Fail'|dict_s }}');
+							$(oEvent.target).modal('hide');
+						}
+					});
+
+					// Handle AJAX errors (exceptions (500), logout (401), ...)
+					$(document).ajaxError(function(oEvent, oXHR, oSettings, sError){
+					    if(oXHR.status === 401)
+						{
+						    ShowLoginDialog();
 						}
+                        else if(oXHR.status === 404)
+                        {
+                            ShowErrorDialog('{{ 'UI:ObjectDoesNotExist'|dict_s|escape('js') }}', '{{ 'Error:HTTP:404'|dict_s|escape('js') }}');
+                        }
+                        else
+                        {
+                            ShowErrorDialog();
+                        }
 					});
 				{% endblock %}
 			});

+ 53 - 43
datamodels/2.x/itop-portal-base/portal/web/index.php

@@ -41,30 +41,19 @@ require_once __DIR__ . '/../src/helpers/scopevalidatorhelper.class.inc.php';
 require_once __DIR__ . '/../src/helpers/securityhelper.class.inc.php';
 require_once __DIR__ . '/../src/helpers/applicationhelper.class.inc.php';
 
+use \Silex\Application;
 use \Combodo\iTop\Portal\Helper\ApplicationHelper;
 
-// Checking user rights and prompt if needed
-LoginWebPage::DoLoginEx(PORTAL_ID);
-if (UserRights::GetContactId() == 0)
-{
-	die(Dict::S('Portal:ErrorNoContactForThisUser'));
-}
-
 // Stacking context tag so it knows we are in the portal
 $oContex = new ContextTag('GUI:Portal');
 $oContex2 = new ContextTag('Portal:' . PORTAL_MODULE_ID);
 
-if (!defined('DISABLE_DATA_LOCALIZER_PORTAL'))
-{
-	ApplicationContext::SetPluginProperty('QueryLocalizerPlugin', 'language_code', UserRights::GetUserLanguage());
-}
-
 // Checking if debug param is on
 $bDebug = (isset($_REQUEST['debug']) && ($_REQUEST['debug'] === 'true') );
 
 // Initializing Silex framework
 $oKPI = new ExecutionKPI();
-$oApp = new Silex\Application();
+$oApp = new Application();
 
 // Registring optional silex components
 $oApp->register(new Combodo\iTop\Portal\Provider\UrlGeneratorServiceProvider());
@@ -83,36 +72,57 @@ $oApp->register(new Silex\Provider\TwigServiceProvider(), array(
 $oApp->register(new Silex\Provider\HttpFragmentServiceProvider());
 $oKPI->ComputeAndReport('Initialization of the Silex application');
 
-// Configuring Silex application
-$oApp['debug'] = $bDebug;
-$oApp['combodo.current_environment'] = utils::GetCurrentEnvironment();
-$oApp['combodo.absolute_url'] = utils::GetAbsoluteUrlAppRoot();
-$oApp['combodo.portal.base.absolute_url'] = utils::GetAbsoluteUrlAppRoot() . 'env-' . utils::GetCurrentEnvironment() . '/itop-portal-base/portal/web/';
-$oApp['combodo.portal.base.absolute_path'] = MODULESROOT . '/itop-portal-base/portal/web/';
-$oApp['combodo.portal.instance.absolute_url'] = utils::GetAbsoluteUrlAppRoot() . 'env-' . utils::GetCurrentEnvironment() . '/' . PORTAL_MODULE_ID . '/';
-$oApp['combodo.portal.instance.id'] = PORTAL_MODULE_ID;
-$oApp['combodo.portal.instance.conf'] = array();
-$oApp['combodo.portal.instance.routes'] = array();
-
-// Registering error/exception handler in order to transform php error to exception
-ApplicationHelper::RegisterExceptionHandler($oApp);
-
-// Preparing portal foundations (Can't use Silex autoload through composer as we don't follow PSR conventions -filenames, functions-)
-$oKPI = new ExecutionKPI();
-ApplicationHelper::LoadControllers();
-ApplicationHelper::LoadRouters();
-ApplicationHelper::RegisterRoutes($oApp);
-ApplicationHelper::LoadBricks();
-ApplicationHelper::LoadFormManagers();
-ApplicationHelper::RegisterTwigExtensions($oApp['twig']);
-$oKPI->ComputeAndReport('Loading portal files (routers, controllers, ...)');
-
-// Loading portal configuration from the module design
-$oKPI = new ExecutionKPI();
-ApplicationHelper::LoadPortalConfiguration($oApp);
-$oKPI->ComputeAndReport('Parsing portal configuration');
-// Loading current user
-ApplicationHelper::LoadCurrentUser($oApp);
+$oApp->before(function(Symfony\Component\HttpFoundation\Request $oRequest, Silex\Application $oApp) use ($bDebug){
+    // Checking user rights and prompt if needed (401 HTTP code returned if XHR request)
+    $iExitMethod = ($oRequest->isXmlHttpRequest()) ? LoginWebPage::EXIT_RETURN : LoginWebPage::EXIT_PROMPT;
+    $iLogonRes = LoginWebPage::DoLoginEx(PORTAL_ID, false, $iExitMethod);
+    if( ($iExitMethod === LoginWebPage::EXIT_RETURN) && ($iLogonRes != 0) )
+    {
+        $oApp->abort(401, Dict::S('Portal:ErrorUserLoggedOut'));
+    }
+
+    if (UserRights::GetContactId() == 0)
+    {
+        $oApp->abort(500, Dict::S('Portal:ErrorNoContactForThisUser'));
+    }
+
+    // Enabling datalocalizer if needed
+    if (!defined('DISABLE_DATA_LOCALIZER_PORTAL'))
+    {
+        ApplicationContext::SetPluginProperty('QueryLocalizerPlugin', 'language_code', UserRights::GetUserLanguage());
+    }
+
+    // Configuring Silex application
+    $oApp['debug'] = $bDebug;
+    $oApp['combodo.current_environment'] = utils::GetCurrentEnvironment();
+    $oApp['combodo.absolute_url'] = utils::GetAbsoluteUrlAppRoot();
+    $oApp['combodo.portal.base.absolute_url'] = utils::GetAbsoluteUrlAppRoot() . 'env-' . utils::GetCurrentEnvironment() . '/itop-portal-base/portal/web/';
+    $oApp['combodo.portal.base.absolute_path'] = MODULESROOT . '/itop-portal-base/portal/web/';
+    $oApp['combodo.portal.instance.absolute_url'] = utils::GetAbsoluteUrlAppRoot() . 'env-' . utils::GetCurrentEnvironment() . '/' . PORTAL_MODULE_ID . '/';
+    $oApp['combodo.portal.instance.id'] = PORTAL_MODULE_ID;
+    $oApp['combodo.portal.instance.conf'] = array();
+    $oApp['combodo.portal.instance.routes'] = array();
+
+    // Registering error/exception handler in order to transform php error to exception
+    ApplicationHelper::RegisterExceptionHandler($oApp);
+
+    // Preparing portal foundations (Can't use Silex autoload through composer as we don't follow PSR conventions -filenames, functions-)
+    $oKPI = new ExecutionKPI();
+    ApplicationHelper::LoadControllers();
+    ApplicationHelper::LoadRouters();
+    ApplicationHelper::RegisterRoutes($oApp);
+    ApplicationHelper::LoadBricks();
+    ApplicationHelper::LoadFormManagers();
+    ApplicationHelper::RegisterTwigExtensions($oApp['twig']);
+    $oKPI->ComputeAndReport('Loading portal files (routers, controllers, ...)');
+
+    // Loading portal configuration from the module design
+    $oKPI = new ExecutionKPI();
+    ApplicationHelper::LoadPortalConfiguration($oApp);
+    $oKPI->ComputeAndReport('Parsing portal configuration');
+    // Loading current user
+    ApplicationHelper::LoadCurrentUser($oApp);
+}, Application::EARLY_EVENT);
 
 // Running application
 $oKPI = new ExecutionKPI();

+ 2 - 0
datamodels/2.x/itop-portal-base/ru.dict.itop-portal-base.php

@@ -23,10 +23,12 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
     'Portal:Button:Add' => 'Добавить',
     'Portal:Button:Remove' => 'Удалить',
     'Portal:Button:Delete' => 'Удалить',
+    'Error:HTTP:401' => 'Authentication~~',
     'Error:HTTP:404' => 'Страница не найдена',
     'Error:HTTP:500' => 'Упс! Произошла ошибка.',
     'Error:XHR:Fail' => 'Не удалось загрузить данные. Пожалуйста, свяжитесь с вашим администратором iTop.',
     'Error:HTTP:GetHelp' => 'Пожалуйста, свяжитесь с вашим администратором iTop, если проблема сохраняется.',
+    'Portal:ErrorUserLoggedOut' => 'You are logged out and need to log in again in order to continue.~~',
     'Portal:Datatables:Language:Processing' => 'Пожалуйста, подождите...',
     'Portal:Datatables:Language:Search' => 'Фильтр :',
     'Portal:Datatables:Language:LengthMenu' => 'Показывать _MENU_ элементов на странице',

+ 7 - 0
sources/renderer/bootstrap/fieldrenderer/bslinkedsetfieldrenderer.class.inc.php

@@ -451,6 +451,13 @@ EOF
 								sFormPath: '{$this->oField->GetFormPath()}',
 								sFieldId: '{$this->oField->GetId()}',
 								aObjectIdsToIgnore : aObjectIdsToIgnore
+							},
+							function(sResponseText, sStatus, oXHR){
+							    // Hiding modal in case of error as the general AJAX error handler will display a message
+							    if(sStatus === 'error')
+							    {
+							        oModalElem.modal('hide');
+							    }
 							}
 						);
 						oModalElem.modal('show');

+ 16 - 3
sources/renderer/bootstrap/fieldrenderer/bsselectobjectfieldrenderer.class.inc.php

@@ -75,7 +75,6 @@ class BsSelectObjectFieldRenderer extends FieldRenderer
 				$oCountSet = new DBObjectSet($oSearch);
 				$iSetCount = $oCountSet->Count();
 				// Note : Autocomplete/Search is disabled for template fields as they are not external keys, thus they will just be displayed as regular select.
-				//$bRegularSelect = ($iSetCount <= $this->oField->GetMaximumComboLength());
 				$bRegularSelect = ( ($iSetCount <= $this->oField->GetMaximumComboLength()) || ($this->oField->GetSearchEndpoint() === null) || ($this->oField->GetSearchEndpoint() === '') );
 				unset($oCountSet);
 
@@ -386,7 +385,14 @@ EOF
 						{
 							sFormPath: '{$this->oField->GetFormPath()}',
 							sFieldId: '{$this->oField->GetId()}'
-						}
+						},
+                        function(sResponseText, sStatus, oXHR){
+                            // Hiding modal in case of error as the general AJAX error handler will display a message
+                            if(sStatus === 'error')
+                            {
+                                oModalElem.modal('hide');
+                            }
+                        }
 					);
 					oModalElem.modal('show');
 				});
@@ -433,7 +439,14 @@ EOF
 						formmanager_class: $(this).closest('.portal_form_handler').portal_form_handler('getOptions').formmanager_class,
 						formmanager_data: JSON.stringify($(this).closest('.portal_form_handler').portal_form_handler('getOptions').formmanager_data),
 						current_values: $(this).closest('.portal_form_handler').portal_form_handler('getCurrentValues')
-					}
+					},
+                    function(sResponseText, sStatus, oXHR){
+                        // Hiding modal in case of error as the general AJAX error handler will display a message
+                        if(sStatus === 'error')
+                        {
+                            oModalElem.modal('hide');
+                        }
+                    }
 				);
 				oModalElem.modal('show');
 			});