Browse Source

[index] Improve error handling (#555)

Add additional information to error message:

- Name of the bridge
- Possible solutions
- Error description
- Error code
- Error message

* Output type changed from 'text' to 'html'
* Added styles for the error page
* Added a button to remotely open a GitHub issue

Closes #525
LogMANOriginal 6 years ago
parent
commit
38b56bf23a
3 changed files with 195 additions and 3 deletions
  1. 12 3
      index.php
  2. 149 0
      lib/Exceptions.php
  3. 34 0
      static/style.css

+ 12 - 3
index.php

@@ -162,8 +162,14 @@ try {
 		unset($params['_noproxy']);
 
 		// Load cache & data
-		$bridge->setCache($cache);
-		$bridge->setDatas($params);
+		try {
+			$bridge->setCache($cache);
+			$bridge->setDatas($params);
+		} catch(Exception $e){
+			header('HTTP/1.1 ' . $e->getCode() . ' ' . Http::getMessageForCode($e->getCode()));
+			header('Content-Type: text/html');
+			die(buildBridgeException($e, $bridge));
+		}
 
 		// Data transformation
 		try {
@@ -172,8 +178,11 @@ try {
 			$format->setExtraInfos($bridge->getExtraInfos());
 			$format->display();
 		} catch(Exception $e){
-			echo "The bridge has crashed. You should report this to the bridges maintainer";
+			header('HTTP/1.1 ' . $e->getCode() . ' ' . Http::getMessageForCode($e->getCode()));
+			header('Content-Type: text/html');
+			die(buildTransformException($e, $bridge));
 		}
+
 		die;
 	}
 }

+ 149 - 0
lib/Exceptions.php

@@ -58,3 +58,152 @@ class Http{
 		);
 	}
 }
+
+/**
+ * Returns an URL that automatically populates a new issue on GitHub based
+ * on the information provided
+ *
+ * @param $title string Sets the title of the issue
+ * @param $body string Sets the body of the issue (GitHub markdown applies)
+ * @param $labels mixed (optional) Specifies labels to add to the issue
+ * @param $maintainer string (optional) Specifies the maintainer for the issue.
+ * The maintainer only applies if part of the development team!
+ * @return string Returns a qualified URL to a new issue with populated conent.
+ * Returns null if title or body is null or empty
+ */
+function buildGitHubIssueQuery($title, $body, $labels = null, $maintainer = null){
+	if(!isset($title) || !isset($body) || empty($title) || empty($body)) {
+		return null;
+	}
+
+	// Add title and body
+	$uri = 'https://github.com/rss-bridge/rss-bridge/issues/new?title='
+		. urlencode($title)
+		. '&body='
+		. urlencode($body);
+
+	// Add labels
+	if(!is_null($labels) && is_array($labels) && count($labels) > 0) {
+		if(count($lables) === 1) {
+			$uri .= '&labels=' . urlencode($labels[0]);
+		} else {
+			foreach($labels as $label) {
+				$uri .= '&labels[]=' . urlencode($label);
+			}
+		}
+	} elseif(!is_null($labels) && is_string($labels)) {
+		$uri .= '&labels=' . urlencode($labels);
+	}
+
+	// Add maintainer
+	if(!empty($maintainer)) {
+		$uri .= '&assignee=' . urlencode($maintainer);
+	}
+
+	return $uri;
+}
+
+/**
+ * Returns the exception message as HTML string
+ *
+ * @param $e Exception The exception to show
+ * @param $bridge object The bridge object
+ * @return string Returns the exception as HTML string. Returns null if the
+ * provided parameter are invalid
+ */
+function buildBridgeException($e, $bridge){
+	if(!($e instanceof \Exception) || !($bridge instanceof \BridgeInterface)) {
+		return null;
+	}
+
+	$title = $bridge->getName() . ' failed with error ' . $e->getCode();
+
+	// Build a GitHub compatible message
+	$body = 'Error message: `'
+	. $e->getMessage()
+	. "`\nQuery string: `"
+	. $_SERVER['QUERY_STRING'] . '`';
+
+	$link = buildGitHubIssueQuery($title, $body, 'bug report', $bridge->getMaintainer());
+
+	$header = buildHeader($e, $bridge);
+	$message = "<strong>{$bridge->getName()}</strong> was
+unable to receive or process the remote website's content!";
+	$section = buildSection($e, $bridge, $message, $link);
+
+	return buildPage($title, $header, $section);
+}
+
+/**
+ * Returns the exception message as HTML string
+ *
+ * @param $e Exception The exception to show
+ * @param $bridge object The bridge object
+ * @return string Returns the exception as HTML string. Returns null if the
+ * provided parameter are invalid
+ */
+function buildTransformException($e, $bridge){
+	if(!($e instanceof \Exception) || !($bridge instanceof \BridgeInterface)) {
+		return null;
+	}
+
+	$title = $bridge->getName() . ' failed with error ' . $e->getCode();
+
+	// Build a GitHub compatible message
+	$body = 'Error message: `'
+	. $e->getMessage()
+	. "`\nQuery string: `"
+	. $_SERVER['QUERY_STRING'] . '`';
+
+	$link = buildGitHubIssueQuery($title, $body, 'bug report', $bridge->getMaintainer());
+	$header = buildHeader($e, $bridge);
+	$message = "RSS-Bridge was unable to transform the contents returned by
+<strong>{$bridge->getName()}</strong>!";
+	$section = buildSection($e, $bridge, $message, $link);
+
+	return buildPage($title, $header, $section);
+}
+
+function buildHeader($e, $bridge){
+	return <<<EOD
+<header>
+	<h1>Error {$e->getCode()}</h1>
+	<h2>{$e->getMessage()}</h2>
+	<p class="status">{$bridge->getName()}</p>
+</header>
+EOD;
+}
+
+function buildSection($e, $bridge, $message, $link){
+	return <<<EOD
+<section>
+	<p class="exception-message">{$message}</p>
+	<div class="advice">
+		<ul class="advice">
+			<li>Press Return to check your input parameters</li>
+			<li>Press F5 to retry</li>
+			<li>Open a GitHub Issue if this error persists</li>
+		</ul>
+	</div>
+	<a href="{$link}" title="After clicking this button you can review
+	the issue before submitting it"><button>Open GitHub Issue</button></a>
+	<p class="maintainer">{$bridge->getMaintainer()}</p>
+</section>
+EOD;
+}
+
+function buildPage($title, $header, $section){
+	return <<<EOD
+<!DOCTYPE html>
+<html lang="en">
+<head>
+	<title>{$title}</title>
+	<link href="static/style.css" rel="stylesheet">
+</head>
+<body>
+	{$header}
+	{$section}
+</body>
+</html>
+EOD;
+}

+ 34 - 0
static/style.css

@@ -244,3 +244,37 @@ h5 {
 	display: block;
 
 }
+
+/* Additional styles for error pages */
+
+.exception-message {
+
+	background-color: #c00000;
+	color: #FFFFFF;
+
+	font-weight: bold;
+
+	box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.3);
+	border-radius: 2px;
+	border: 1px solid transparent;
+
+	width: 80%;
+	margin: auto;
+	margin-bottom: 6px;
+
+}
+
+.advice {
+
+	margin-left: auto;
+	margin-right: auto;
+
+	display: table;
+
+}
+
+.advice > li {
+
+	text-align: left;
+
+}