Browse Source

N°589 - Portal: Displaying a better error message when the portal crashes because the current contact's organization is not among the current user's allowed organizations. More over we introduced an exception handler to display a nicer web page and log the exception in the error log.

git-svn-id: http://svn.code.sf.net/p/itop/code/trunk@4557 a333f486-631f-4898-b8df-5754b55c2be0
glajarige 8 years ago
parent
commit
4fdb63c7db

+ 146 - 0
datamodels/2.x/itop-portal-base/portal/src/handlers/exceptionhandler.class.inc.php

@@ -0,0 +1,146 @@
+<?php
+
+// Copyright (C) 2010-2015 Combodo SARL
+//
+//   This file is part of iTop.
+//
+//   iTop is free software; you can redistribute it and/or modify
+//   it under the terms of the GNU Affero General Public License as published by
+//   the Free Software Foundation, either version 3 of the License, or
+//   (at your option) any later version.
+//
+//   iTop is distributed in the hope that it will be useful,
+//   but WITHOUT ANY WARRANTY; without even the implied warranty of
+//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//   GNU Affero General Public License for more details.
+//
+//   You should have received a copy of the GNU Affero General Public License
+//   along with iTop. If not, see <http://www.gnu.org/licenses/>
+
+namespace Combodo\iTop\Portal\Handler;
+
+use \Exception;
+use \Silex\Application;
+use \Symfony\Component\Debug\ExceptionHandler as BaseExceptionHandler;
+use \Symfony\Component\Debug\Exception\FlattenException;
+use \Symfony\Component\HttpFoundation\Request;
+use \IssueLog;
+use \Dict;
+
+/**
+ * Extends the default ExceptionHandler to provide a better template.
+ *
+ * @author Guillaume Lajarige
+ */
+class ExceptionHandler extends BaseExceptionHandler
+{
+    private $debug;
+    private $charset;
+    private $handler;
+    private $caughtBuffer;
+    private $caughtLength;
+    private $fileLinkFormat;
+
+    /**
+     * Sends a response for the given Exception.
+     *
+     * To be as fail-safe as possible, the exception is first handled
+     * by our simple exception handler, then by the user exception handler.
+     * The latter takes precedence and any output from the former is cancelled,
+     * if and only if nothing bad happens in this handling path.
+     */
+    public function handle(\Exception $exception)
+    {
+        IssueLog::Error('Portal: '.$exception->getMessage());
+
+        parent::handle($exception);
+    }
+
+    /**
+     * Gets the HTML content associated with the given exception.
+     *
+     * @param FlattenException $exception A FlattenException instance
+     *
+     * @return string The content as a string
+     */
+    public function getContent(FlattenException $exception)
+    {
+        switch ($exception->getStatusCode()) {
+            case 404:
+                $title = Dict::S('Error:HTTP:404');
+                break;
+            default:
+                $title = Dict::S('Error:HTTP:500');
+        }
+
+        $content = '';
+        if ($this->debug) {
+            try {
+                $count = count($exception->getAllPrevious());
+                $total = $count + 1;
+                foreach ($exception->toArray() as $position => $e) {
+                    $ind = $count - $position + 1;
+                    $class = $this->formatClass($e['class']);
+                    $message = nl2br($this->escapeHtml($e['message']));
+                    $content .= sprintf(<<<EOF
+                        <h2 class="block_exception clear_fix">
+                            <span class="exception_counter">%d/%d</span>
+                            <span class="exception_title">%s%s:</span>
+                            <span class="exception_message">%s</span>
+                        </h2>
+                        <div class="block">
+                            <ol class="traces list_exception">
+
+EOF
+                        , $ind, $total, $class, $this->formatPath($e['trace'][0]['file'], $e['trace'][0]['line']), $message);
+                    foreach ($e['trace'] as $trace) {
+                        $content .= '       <li>';
+                        if ($trace['function']) {
+                            $content .= sprintf('at %s%s%s(%s)', $this->formatClass($trace['class']), $trace['type'], $trace['function'], $this->formatArgs($trace['args']));
+                        }
+                        if (isset($trace['file']) && isset($trace['line'])) {
+                            $content .= $this->formatPath($trace['file'], $trace['line']);
+                        }
+                        $content .= "</li>\n";
+                    }
+
+                    $content .= "    </ol>\n</div>\n";
+                }
+            } catch (\Exception $e) {
+                // something nasty happened and we cannot throw an exception anymore
+                if ($this->debug) {
+                    $title = sprintf('Exception thrown when handling an exception (%s: %s)', get_class($e), $this->escapeHtml($e->getMessage()));
+                } else {
+                    $title = 'Whoops, looks like something went wrong.';
+                }
+            }
+        }
+        else{
+            $content = $exception->getMessage();
+        }
+
+        return <<<EOF
+            <div id="sf-resetcontent" class="sf-reset">
+                <h1>$title</h1>
+                <div class="content">$content</div>
+            </div>
+EOF;
+    }
+
+    /**
+     * Gets the stylesheet associated with the given exception.
+     *
+     * @param FlattenException $exception A FlattenException instance
+     *
+     * @return string The stylesheet as a string
+     */
+    public function getStylesheet(FlattenException $exception)
+    {
+        $parentStylesheet = parent::getStylesheet($exception);
+        $stylesheet = <<<EOF
+.sf-reset .content{ background-color: #FFFFFF; padding: 15px 28px; margin-bottom: 20px; -webkit-border-radius: 10px; -moz-border-radius: 10px; border-radius: 10px; border: 1px solid #ccc; font-size: 13px; }
+EOF;
+
+        return $stylesheet . $parentStylesheet;
+    }
+}

+ 20 - 2
datamodels/2.x/itop-portal-base/portal/src/helpers/applicationhelper.class.inc.php

@@ -22,7 +22,8 @@ namespace Combodo\iTop\Portal\Helper;
 use \Exception;
 use \Silex\Application;
 use \Symfony\Component\Debug\ErrorHandler;
-use \Symfony\Component\Debug\ExceptionHandler;
+//use \Symfony\Component\Debug\ExceptionHandler;
+use \Combodo\iTop\Portal\Handler\ExceptionHandler;
 use \Symfony\Component\HttpFoundation\Request;
 use \Twig_SimpleFilter;
 use \Dict;
@@ -248,9 +249,11 @@ class ApplicationHelper
 	 */
 	static function RegisterExceptionHandler(Application $oApp)
 	{
+	    // Intercepting fatal errors and exceptions
 		ErrorHandler::register();
 		ExceptionHandler::register(($oApp['debug'] === true));
 
+		// Intercepting manually aborted request
 		if (!$oApp['debug'])
 		{
 			$oApp->error(function(Exception $e, $code) use ($oApp)
@@ -489,7 +492,22 @@ class ApplicationHelper
 
 		// Contact
 		$sContactPhotoUrl = $oApp['combodo.portal.base.absolute_url'] . 'img/user-profile-default-256px.png';
-		$oContact = UserRights::GetContactObject();
+		// - Checking if we can load the contact
+        try{
+            $oContact = UserRights::GetContactObject();
+        }
+        catch(Exception $e)
+        {
+            $oAllowedOrgSet = $oUser->Get('allowed_org_list');
+            if($oAllowedOrgSet->Count() > 0)
+            {
+                throw new Exception('Could not load contact related to connected user. (Tip: Make sure the contact\'s organization is among the user\'s allowed organizations)');
+            }
+            else{
+                throw new Exception('Could not load contact related to connected user.');
+            }
+        }
+        // - Retrieving picture
 		if ($oContact)
 		{
 			if (MetaModel::IsValidAttCode(get_class($oContact), 'picture'))

+ 1 - 0
datamodels/2.x/itop-portal-base/portal/web/index.php

@@ -31,6 +31,7 @@ require_once APPROOT . '/core/moduledesign.class.inc.php';
 require_once APPROOT . '/application/loginwebpage.class.inc.php';
 require_once APPROOT . '/sources/autoload.php';
 // Portal
+require_once __DIR__ . '/../src/handlers/exceptionhandler.class.inc.php';
 require_once __DIR__ . '/../src/providers/urlgeneratorserviceprovider.class.inc.php';
 require_once __DIR__ . '/../src/helpers/urlgeneratorhelper.class.inc.php';
 require_once __DIR__ . '/../src/providers/contextmanipulatorserviceprovider.class.inc.php';