the browser-facing portion of osu!
at master 5.7 kB view raw
1<?php 2 3// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0. 4// See the LICENCE file in the repository root for full licence text. 5 6namespace App\Http\Controllers; 7 8use App\Libraries\LocaleMeta; 9use App\Libraries\OsuWiki; 10use App\Libraries\Search\WikiSuggestions; 11use App\Libraries\Search\WikiSuggestionsRequestParams; 12use App\Libraries\Wiki\WikiSitemap; 13use App\Libraries\WikiRedirect; 14use App\Models\Wiki; 15use Request; 16 17/** 18 * @group Wiki 19 */ 20class WikiController extends Controller 21{ 22 /** 23 * Get Wiki Page 24 * 25 * The wiki article or image data. 26 * 27 * --- 28 * 29 * ### Response Format 30 * 31 * Returns [WikiPage](#wikipage). 32 * 33 * @urlParam locale string required Two-letter language code of the wiki page. Example: en 34 * @urlParam path string required The path name of the wiki page. Example: Welcome 35 */ 36 public function show($locale = null, $path = null) 37 { 38 // redirect to default page if missing all parameters 39 if ($locale === null) { 40 return ujs_redirect(wiki_url(null, $this->locale())); 41 } 42 43 $cleanLocale = LocaleMeta::sanitizeCode($locale); 44 45 // if images slip through the markdown processing, redirect them to the correct place 46 if (OsuWiki::isImage($path)) { 47 $prependPath = $locale === 'images' || $cleanLocale === null ? $locale : null; 48 49 return ujs_redirect(wiki_image_url(concat_path([$prependPath, $path]))); 50 } 51 52 // if invalid locale, assume locale to be part of path and 53 // actual locale to be either user locale or passed as parameter 54 if ($cleanLocale === null) { 55 return ujs_redirect(wiki_url(concat_path([$locale, $path]), $this->locale())); 56 } 57 58 // in case locale is passed as query parameter (legacy url inside the page) 59 // or it's in wrong case, redirect to new path 60 $queryLocale = $this->locale(); 61 if ($queryLocale !== $locale && present($path)) { 62 return ujs_redirect(wiki_url($path, $queryLocale)); 63 } 64 65 // normalize path by making sure no trailing slash and encoded forward slash (%2F) 66 $rawPath = request()->getPathInfo(); 67 if (substr($rawPath, -1) === '/' || strpos($rawPath, '%2F') !== false) { 68 return ujs_redirect(wiki_url(rtrim($path, '/'), $locale)); 69 } 70 71 // legal pages should be displayed with their own style etc 72 if (starts_with("{$path}/", 'Legal/') && !is_api_request()) { 73 return ujs_redirect(wiki_url($path, $locale)); 74 } 75 76 $page = Wiki\Page::lookupForController($path, $locale); 77 78 if (!$page->isVisible()) { 79 $redirect = (new WikiRedirect())->sync(); 80 $redirectTarget = $redirect->resolve($path); 81 if ($redirectTarget !== null && $redirectTarget !== $path) { 82 return ujs_redirect(wiki_url(ltrim($redirectTarget, '/'), $locale)); 83 } 84 85 $redirectTarget = $redirect->resolve(concat_path([$locale, $path])); 86 if ($redirectTarget !== null && $redirectTarget !== $path) { 87 return ujs_redirect(wiki_url(ltrim($redirectTarget, '/'))); 88 } 89 90 if (!present($path)) { 91 return ujs_redirect(wiki_url($locale)); 92 } 93 94 $correctPath = Wiki\Page::searchPath($path, $locale); 95 if ($correctPath !== null && $correctPath !== $path) { 96 return ujs_redirect(wiki_url($correctPath, $locale)); 97 } 98 99 $status = 404; 100 } 101 102 if (is_json_request()) { 103 if (!$page->isVisible()) { 104 return response(null, 404); 105 } 106 107 return json_item($page, 'WikiPage'); 108 } 109 110 set_opengraph($page); 111 112 return ext_view( 113 $page->template(), 114 ['contentLocale' => $page->locale, 'page' => $page], 115 null, 116 $status ?? null 117 ); 118 } 119 120 public function image($path) 121 { 122 if (!OsuWiki::isImage($path)) { 123 return response('Invalid file format', 422); 124 } 125 126 $image = (new Wiki\Image($path))->sync(); 127 128 if (!$image->isVisible()) { 129 return response('Not found', 404); 130 } 131 132 $imageData = $image->get(); 133 134 return response($imageData['content'], 200) 135 ->header('Content-Type', $imageData['type']) 136 // 10 years max-age 137 ->header('Cache-Control', 'max-age=315360000, public'); 138 } 139 140 public function sitemap($locale) 141 { 142 if (!LocaleMeta::isValid($locale)) { 143 return ujs_redirect(route('wiki.sitemap', ['locale' => app()->getLocale()])); 144 } 145 146 return ext_view('wiki.sitemap', [ 147 'locale' => $locale, 148 'sitemap' => WikiSitemap::get(), 149 ]); 150 } 151 152 public function suggestions() 153 { 154 $search = new WikiSuggestions(new WikiSuggestionsRequestParams(request()->all())); 155 156 $response = []; 157 foreach ($search->response() as $hit) { 158 $response[] = [ 159 'highlight' => $hit->highlights('title.autocomplete')[0], 160 'path' => $hit->source('path'), 161 'title' => $hit->source('title'), 162 ]; 163 } 164 165 return $response; 166 } 167 168 public function update($locale, $path) 169 { 170 priv_check('WikiPageRefresh')->ensureCan(); 171 172 if (strtolower($path) === 'sitemap') { 173 WikiSitemap::expire(); 174 } else { 175 (new Wiki\Page($path, $locale))->sync(true); 176 } 177 178 return ujs_redirect(Request::getUri()); 179 } 180}