]> BookStack Code Mirror - bookstack/blob - app/Exceptions/Handler.php
Merge pull request #1845 from SoarinFerret/add-close-icon-to-notifications
[bookstack] / app / Exceptions / Handler.php
1 <?php
2
3 namespace BookStack\Exceptions;
4
5 use Exception;
6 use Illuminate\Auth\Access\AuthorizationException;
7 use Illuminate\Auth\AuthenticationException;
8 use Illuminate\Database\Eloquent\ModelNotFoundException;
9 use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
10 use Illuminate\Http\JsonResponse;
11 use Illuminate\Http\Request;
12 use Illuminate\Http\Response;
13 use Illuminate\Validation\ValidationException;
14 use Symfony\Component\HttpKernel\Exception\HttpException;
15 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
16
17 class Handler extends ExceptionHandler
18 {
19     /**
20      * A list of the exception types that should not be reported.
21      *
22      * @var array
23      */
24     protected $dontReport = [
25         AuthorizationException::class,
26         HttpException::class,
27         ModelNotFoundException::class,
28         ValidationException::class,
29     ];
30
31     /**
32      * Report or log an exception.
33      * This is a great spot to send exceptions to Sentry, Bugsnag, etc.
34      *
35      * @param  \Exception $e
36      * @return mixed
37      * @throws Exception
38      */
39     public function report(Exception $e)
40     {
41         return parent::report($e);
42     }
43
44     /**
45      * Render an exception into an HTTP response.
46      *
47      * @param  \Illuminate\Http\Request $request
48      * @param  \Exception $e
49      * @return \Illuminate\Http\Response
50      */
51     public function render($request, Exception $e)
52     {
53         if ($this->isApiRequest($request)) {
54             return $this->renderApiException($e);
55         }
56
57         // Handle notify exceptions which will redirect to the
58         // specified location then show a notification message.
59         if ($this->isExceptionType($e, NotifyException::class)) {
60             session()->flash('error', $this->getOriginalMessage($e));
61             return redirect($e->redirectLocation);
62         }
63
64         // Handle pretty exceptions which will show a friendly application-fitting page
65         // Which will include the basic message to point the user roughly to the cause.
66         if ($this->isExceptionType($e, PrettyException::class)  && !config('app.debug')) {
67             $message = $this->getOriginalMessage($e);
68             $code = ($e->getCode() === 0) ? 500 : $e->getCode();
69             return response()->view('errors/' . $code, ['message' => $message], $code);
70         }
71
72         // Handle 404 errors with a loaded session to enable showing user-specific information
73         if ($this->isExceptionType($e, NotFoundHttpException::class)) {
74             return \Route::respondWithRoute('fallback');
75         }
76
77         return parent::render($request, $e);
78     }
79
80     /**
81      * Check if the given request is an API request.
82      */
83     protected function isApiRequest(Request $request): bool
84     {
85         return strpos($request->path(), 'api/') === 0;
86     }
87
88     /**
89      * Render an exception when the API is in use.
90      */
91     protected function renderApiException(Exception $e): JsonResponse
92     {
93         $code = $e->getCode() === 0 ? 500 : $e->getCode();
94         $headers = [];
95         if ($e instanceof HttpException) {
96             $code = $e->getStatusCode();
97             $headers = $e->getHeaders();
98         }
99
100         $responseData = [
101             'error' => [
102                 'message' => $e->getMessage(),
103             ]
104         ];
105
106         if ($e instanceof ValidationException) {
107             $responseData['error']['validation'] = $e->errors();
108             $code = $e->status;
109         }
110
111         $responseData['error']['code'] = $code;
112         return new JsonResponse($responseData, $code, $headers);
113     }
114
115     /**
116      * Check the exception chain to compare against the original exception type.
117      * @param Exception $e
118      * @param $type
119      * @return bool
120      */
121     protected function isExceptionType(Exception $e, $type)
122     {
123         do {
124             if (is_a($e, $type)) {
125                 return true;
126             }
127         } while ($e = $e->getPrevious());
128         return false;
129     }
130
131     /**
132      * Get original exception message.
133      * @param Exception $e
134      * @return string
135      */
136     protected function getOriginalMessage(Exception $e)
137     {
138         do {
139             $message = $e->getMessage();
140         } while ($e = $e->getPrevious());
141         return $message;
142     }
143
144     /**
145      * Convert an authentication exception into an unauthenticated response.
146      *
147      * @param  \Illuminate\Http\Request  $request
148      * @param  \Illuminate\Auth\AuthenticationException  $exception
149      * @return \Illuminate\Http\Response
150      */
151     protected function unauthenticated($request, AuthenticationException $exception)
152     {
153         if ($request->expectsJson()) {
154             return response()->json(['error' => 'Unauthenticated.'], 401);
155         }
156
157         return redirect()->guest('login');
158     }
159
160     /**
161      * Convert a validation exception into a JSON response.
162      *
163      * @param  \Illuminate\Http\Request  $request
164      * @param  \Illuminate\Validation\ValidationException  $exception
165      * @return \Illuminate\Http\JsonResponse
166      */
167     protected function invalidJson($request, ValidationException $exception)
168     {
169         return response()->json($exception->errors(), $exception->status);
170     }
171 }