1*208909d8SGreg Roach<?php 2*208909d8SGreg Roach 3*208909d8SGreg Roach/** 4*208909d8SGreg Roach * webtrees: online genealogy 5*208909d8SGreg Roach * Copyright (C) 2022 webtrees development team 6*208909d8SGreg Roach * This program is free software: you can redistribute it and/or modify 7*208909d8SGreg Roach * it under the terms of the GNU General Public License as published by 8*208909d8SGreg Roach * the Free Software Foundation, either version 3 of the License, or 9*208909d8SGreg Roach * (at your option) any later version. 10*208909d8SGreg Roach * This program is distributed in the hope that it will be useful, 11*208909d8SGreg Roach * but WITHOUT ANY WARRANTY; without even the implied warranty of 12*208909d8SGreg Roach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13*208909d8SGreg Roach * GNU General Public License for more details. 14*208909d8SGreg Roach * You should have received a copy of the GNU General Public License 15*208909d8SGreg Roach * along with this program. If not, see <https://www.gnu.org/licenses/>. 16*208909d8SGreg Roach */ 17*208909d8SGreg Roach 18*208909d8SGreg Roachdeclare(strict_types=1); 19*208909d8SGreg Roach 20*208909d8SGreg Roachnamespace Fisharebest\Webtrees\Factories; 21*208909d8SGreg Roach 22*208909d8SGreg Roachuse Fig\Http\Message\StatusCodeInterface; 23*208909d8SGreg Roachuse Fisharebest\Webtrees\Contracts\ResponseFactoryInterface; 24*208909d8SGreg Roachuse Fisharebest\Webtrees\Module\ModuleThemeInterface; 25*208909d8SGreg Roachuse Fisharebest\Webtrees\Registry; 26*208909d8SGreg Roachuse Fisharebest\Webtrees\Webtrees; 27*208909d8SGreg Roachuse Psr\Http\Message\ResponseFactoryInterface as PSR17ResponseFactoryInterface; 28*208909d8SGreg Roachuse Psr\Http\Message\ResponseInterface; 29*208909d8SGreg Roachuse Psr\Http\Message\ServerRequestInterface; 30*208909d8SGreg Roachuse Psr\Http\Message\StreamFactoryInterface; 31*208909d8SGreg Roachuse Psr\Http\Message\UriInterface; 32*208909d8SGreg Roach 33*208909d8SGreg Roachuse function app; 34*208909d8SGreg Roachuse function is_string; 35*208909d8SGreg Roachuse function json_encode; 36*208909d8SGreg Roachuse function view; 37*208909d8SGreg Roach 38*208909d8SGreg Roachuse const JSON_THROW_ON_ERROR; 39*208909d8SGreg Roachuse const JSON_UNESCAPED_UNICODE; 40*208909d8SGreg Roach 41*208909d8SGreg Roach/** 42*208909d8SGreg Roach * Make a PSR-7 response (using a PSR-17 response factory). 43*208909d8SGreg Roach */ 44*208909d8SGreg Roachclass ResponseFactory implements ResponseFactoryInterface 45*208909d8SGreg Roach{ 46*208909d8SGreg Roach private PSR17ResponseFactoryInterface $response_factory; 47*208909d8SGreg Roach 48*208909d8SGreg Roach private StreamFactoryInterface $stream_factory; 49*208909d8SGreg Roach 50*208909d8SGreg Roach /** 51*208909d8SGreg Roach * @param PSR17ResponseFactoryInterface $response_factory 52*208909d8SGreg Roach * @param StreamFactoryInterface $stream_factory 53*208909d8SGreg Roach */ 54*208909d8SGreg Roach public function __construct(PSR17ResponseFactoryInterface $response_factory, StreamFactoryInterface $stream_factory) 55*208909d8SGreg Roach { 56*208909d8SGreg Roach $this->response_factory = $response_factory; 57*208909d8SGreg Roach $this->stream_factory = $stream_factory; 58*208909d8SGreg Roach } 59*208909d8SGreg Roach 60*208909d8SGreg Roach /** 61*208909d8SGreg Roach * Redirect to a named route. 62*208909d8SGreg Roach * 63*208909d8SGreg Roach * @param string $route_name 64*208909d8SGreg Roach * @param array<bool|int|string|array<string>|null> $parameters 65*208909d8SGreg Roach * @param int $status 66*208909d8SGreg Roach * 67*208909d8SGreg Roach * @return ResponseInterface 68*208909d8SGreg Roach * 69*208909d8SGreg Roach */ 70*208909d8SGreg Roach public function redirect( 71*208909d8SGreg Roach string $route_name, 72*208909d8SGreg Roach array $parameters = [], 73*208909d8SGreg Roach int $status = StatusCodeInterface::STATUS_FOUND 74*208909d8SGreg Roach ): ResponseInterface { 75*208909d8SGreg Roach $url = Registry::routeFactory()->route($route_name, $parameters); 76*208909d8SGreg Roach 77*208909d8SGreg Roach return $this->redirectUrl($url, $status); 78*208909d8SGreg Roach } 79*208909d8SGreg Roach 80*208909d8SGreg Roach /** 81*208909d8SGreg Roach * Redirect to a URL. 82*208909d8SGreg Roach * 83*208909d8SGreg Roach * @param string|UriInterface $url 84*208909d8SGreg Roach * @param int $code 85*208909d8SGreg Roach * 86*208909d8SGreg Roach * @return ResponseInterface 87*208909d8SGreg Roach */ 88*208909d8SGreg Roach public function redirectUrl($url, int $code = StatusCodeInterface::STATUS_FOUND): ResponseInterface 89*208909d8SGreg Roach { 90*208909d8SGreg Roach return $this->response_factory 91*208909d8SGreg Roach ->createResponse($code) 92*208909d8SGreg Roach ->withHeader('Location', (string) $url); 93*208909d8SGreg Roach } 94*208909d8SGreg Roach 95*208909d8SGreg Roach /** 96*208909d8SGreg Roach * @param string|array<mixed>|object $content 97*208909d8SGreg Roach * @param int $code 98*208909d8SGreg Roach * @param array<string,string> $headers 99*208909d8SGreg Roach * 100*208909d8SGreg Roach * @return ResponseInterface 101*208909d8SGreg Roach */ 102*208909d8SGreg Roach public function response($content = '', int $code = StatusCodeInterface::STATUS_OK, array $headers = []): ResponseInterface 103*208909d8SGreg Roach { 104*208909d8SGreg Roach if ($content === '' && $code === StatusCodeInterface::STATUS_OK) { 105*208909d8SGreg Roach $code = StatusCodeInterface::STATUS_NO_CONTENT; 106*208909d8SGreg Roach } 107*208909d8SGreg Roach 108*208909d8SGreg Roach if (is_string($content)) { 109*208909d8SGreg Roach $headers['Content-Type'] ??= 'text/html; charset=UTF-8'; 110*208909d8SGreg Roach } else { 111*208909d8SGreg Roach $content = json_encode($content, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE); 112*208909d8SGreg Roach $headers['Content-Type'] ??= 'application/json'; 113*208909d8SGreg Roach } 114*208909d8SGreg Roach 115*208909d8SGreg Roach $stream = $this->stream_factory->createStream($content); 116*208909d8SGreg Roach 117*208909d8SGreg Roach $response = $this->response_factory 118*208909d8SGreg Roach ->createResponse($code) 119*208909d8SGreg Roach ->withBody($stream); 120*208909d8SGreg Roach 121*208909d8SGreg Roach foreach ($headers as $key => $value) { 122*208909d8SGreg Roach $response = $response->withHeader($key, $value); 123*208909d8SGreg Roach } 124*208909d8SGreg Roach 125*208909d8SGreg Roach return $response; 126*208909d8SGreg Roach } 127*208909d8SGreg Roach 128*208909d8SGreg Roach /** 129*208909d8SGreg Roach * Create and render a view, and embed it in an HTML page. 130*208909d8SGreg Roach * 131*208909d8SGreg Roach * @param string $view_name 132*208909d8SGreg Roach * @param array<mixed> $view_data 133*208909d8SGreg Roach * @param int $status 134*208909d8SGreg Roach * @param string $layout_name 135*208909d8SGreg Roach * 136*208909d8SGreg Roach * @return ResponseInterface 137*208909d8SGreg Roach */ 138*208909d8SGreg Roach public function view( 139*208909d8SGreg Roach string $view_name, 140*208909d8SGreg Roach array $view_data, 141*208909d8SGreg Roach int $status = StatusCodeInterface::STATUS_OK, 142*208909d8SGreg Roach string $layout_name = Webtrees::LAYOUT_DEFAULT 143*208909d8SGreg Roach ): ResponseInterface { 144*208909d8SGreg Roach // Render the view. 145*208909d8SGreg Roach $content = view($view_name, $view_data); 146*208909d8SGreg Roach 147*208909d8SGreg Roach // Make the view's data available to the layout. 148*208909d8SGreg Roach $layout_data = [ 149*208909d8SGreg Roach 'content' => $content, 150*208909d8SGreg Roach 'request' => app(ServerRequestInterface::class), 151*208909d8SGreg Roach 'theme' => app(ModuleThemeInterface::class), 152*208909d8SGreg Roach 'title' => $view_data['title'] ?? Webtrees::NAME, 153*208909d8SGreg Roach ]; 154*208909d8SGreg Roach 155*208909d8SGreg Roach // Embde the content in the layout. 156*208909d8SGreg Roach $html = view($layout_name, $layout_data); 157*208909d8SGreg Roach 158*208909d8SGreg Roach return $this->response($html, $status); 159*208909d8SGreg Roach } 160*208909d8SGreg Roach} 161