Commit | Line | Data |
---|---|---|
11ade432 AE |
1 | <?php |
2 | namespace wcf\system\request; | |
375ced03 | 3 | use wcf\data\page\PageCache; |
3cd3cfc3 | 4 | use wcf\data\DatabaseObjectDecorator; |
11ade432 | 5 | use wcf\system\application\ApplicationHandler; |
3295fb92 | 6 | use wcf\system\language\LanguageFactory; |
c8a4a1f5 | 7 | use wcf\system\Regex; |
11ade432 | 8 | use wcf\system\SingletonFactory; |
bc62b9c1 | 9 | use wcf\system\WCF; |
1db37793 | 10 | use wcf\util\StringUtil; |
11ade432 AE |
11 | |
12 | /** | |
13 | * Handles relative links within the wcf. | |
14 | * | |
9f959ced | 15 | * @author Marcel Werk |
c839bd49 | 16 | * @copyright 2001-2018 WoltLab GmbH |
11ade432 | 17 | * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php> |
e71525e4 | 18 | * @package WoltLabSuite\Core\System\Request |
11ade432 AE |
19 | */ |
20 | class LinkHandler extends SingletonFactory { | |
c8a4a1f5 AE |
21 | /** |
22 | * regex object to filter title | |
39abe192 | 23 | * @var RegEx |
c8a4a1f5 | 24 | */ |
39abe192 | 25 | protected $titleRegex; |
c8a4a1f5 | 26 | |
99a64f13 MW |
27 | /** |
28 | * title search strings | |
39abe192 | 29 | * @var string[] |
99a64f13 | 30 | */ |
39abe192 | 31 | protected $titleSearch = []; |
99a64f13 MW |
32 | |
33 | /** | |
34 | * title replacement strings | |
39abe192 | 35 | * @var string[] |
99a64f13 | 36 | */ |
39abe192 | 37 | protected $titleReplace = []; |
99a64f13 | 38 | |
bc62b9c1 | 39 | |
c8a4a1f5 | 40 | /** |
0fcfe5f6 | 41 | * @inheritDoc |
c8a4a1f5 AE |
42 | */ |
43 | protected function init() { | |
8c5d0070 | 44 | $this->titleRegex = new Regex('[^\p{L}\p{N}]+', Regex::UTF_8); |
99a64f13 MW |
45 | |
46 | if (defined('URL_TITLE_COMPONENT_REPLACEMENT') && URL_TITLE_COMPONENT_REPLACEMENT) { | |
47 | $replacements = explode("\n", StringUtil::unifyNewlines(StringUtil::trim(URL_TITLE_COMPONENT_REPLACEMENT))); | |
48 | foreach ($replacements as $replacement) { | |
49 | if (strpos($replacement, '=') === false) continue; | |
50 | $components = explode('=', $replacement); | |
51 | $this->titleSearch[] = $components[0]; | |
52 | $this->titleReplace[] = $components[1]; | |
53 | } | |
54 | } | |
c8a4a1f5 AE |
55 | } |
56 | ||
11ade432 AE |
57 | /** |
58 | * Returns a relative link. | |
59 | * | |
6dcaf901 | 60 | * @param string $controller |
39bea7dd MS |
61 | * @param array $parameters |
62 | * @param string $url | |
11ade432 AE |
63 | * @return string |
64 | */ | |
39abe192 | 65 | public function getLink($controller = null, array $parameters = [], $url = '') { |
f98e7cf2 | 66 | $abbreviation = 'wcf'; |
ccedf3ab | 67 | $anchor = ''; |
f341086b | 68 | $isACP = $originIsACP = RequestHandler::getInstance()->isACPRequest(); |
39abe192 | 69 | $isRaw = false; |
d8954071 AE |
70 | $encodeTitle = true; |
71 | ||
72 | /** | |
e71525e4 | 73 | * @deprecated 3.0 - no longer required |
d8954071 | 74 | */ |
13b7bb1f | 75 | /** @noinspection PhpUnusedLocalVariableInspection */ |
d8954071 | 76 | $appendSession = false; |
bfb61bb4 AE |
77 | |
78 | // enforce a certain level of sanitation and protection for links embedded in emails | |
d13cae5b MW |
79 | if (isset($parameters['isEmail'])) { |
80 | if ((bool)$parameters['isEmail']) { | |
81 | $parameters['forceFrontend'] = true; | |
82 | } | |
83 | ||
fda7b2bc | 84 | unset($parameters['isEmail']); |
bfb61bb4 AE |
85 | } |
86 | ||
f98e7cf2 AE |
87 | if (isset($parameters['application'])) { |
88 | $abbreviation = $parameters['application']; | |
f98e7cf2 AE |
89 | } |
90 | if (isset($parameters['isRaw'])) { | |
91 | $isRaw = $parameters['isRaw']; | |
92 | unset($parameters['isRaw']); | |
11ade432 | 93 | } |
876d2d83 | 94 | if (isset($parameters['appendSession'])) { |
876d2d83 AE |
95 | unset($parameters['appendSession']); |
96 | } | |
636f9c1f AE |
97 | if (isset($parameters['isACP'])) { |
98 | $isACP = (bool) $parameters['isACP']; | |
99 | unset($parameters['isACP']); | |
636f9c1f | 100 | } |
c5614b1a MW |
101 | if (isset($parameters['forceFrontend'])) { |
102 | if ($parameters['forceFrontend'] && $isACP) { | |
103 | $isACP = false; | |
c5614b1a MW |
104 | } |
105 | unset($parameters['forceFrontend']); | |
692c7b30 | 106 | } |
74622428 | 107 | if (isset($parameters['forceWCF'])) { |
e71525e4 | 108 | /** @deprecated 3.0 */ |
74622428 AE |
109 | unset($parameters['forceWCF']); |
110 | } | |
5fb44399 | 111 | |
19c8cd5f MW |
112 | if (isset($parameters['encodeTitle'])) { |
113 | $encodeTitle = $parameters['encodeTitle']; | |
114 | unset($parameters['encodeTitle']); | |
115 | } | |
11ade432 | 116 | |
ccedf3ab AE |
117 | // remove anchor before parsing |
118 | if (($pos = strpos($url, '#')) !== false) { | |
119 | $anchor = substr($url, $pos); | |
120 | $url = substr($url, 0, $pos); | |
121 | } | |
122 | ||
f98e7cf2 | 123 | // build route |
9c720493 | 124 | if ($controller === null) { |
191b8391 TD |
125 | if ($isACP) { |
126 | $controller = 'Index'; | |
127 | } | |
128 | else { | |
dbf41d8a AE |
129 | if (!empty($parameters['application']) && $abbreviation !== 'wcf') { |
130 | $application = ApplicationHandler::getInstance()->getApplication($abbreviation); | |
131 | if ($application === null) { | |
132 | throw new \RuntimeException("Unknown abbreviation '" . $abbreviation . "'."); | |
133 | } | |
134 | ||
135 | $landingPage = PageCache::getInstance()->getPage($application->landingPageID); | |
136 | if ($landingPage === null) { | |
137 | $landingPage = PageCache::getInstance()->getPageByController(WCF::getApplicationObject($application)->getPrimaryController()); | |
138 | } | |
139 | ||
140 | if ($landingPage !== null) { | |
141 | return $landingPage->getLink(); | |
142 | } | |
143 | } | |
144 | ||
375ced03 | 145 | return PageCache::getInstance()->getLandingPage()->getLink(); |
191b8391 | 146 | } |
9c720493 AE |
147 | } |
148 | ||
149 | // handle object | |
150 | if (isset($parameters['object'])) { | |
c8a4a1f5 | 151 | if (!($parameters['object'] instanceof IRouteController) && $parameters['object'] instanceof DatabaseObjectDecorator && $parameters['object']->getDecoratedObject() instanceof IRouteController) { |
9c720493 | 152 | $parameters['object'] = $parameters['object']->getDecoratedObject(); |
97b6b000 AE |
153 | } |
154 | ||
c8a4a1f5 | 155 | if ($parameters['object'] instanceof IRouteController) { |
29846bb1 | 156 | $parameters['id'] = $parameters['object']->getObjectID(); |
9c720493 | 157 | $parameters['title'] = $parameters['object']->getTitle(); |
0602bb11 | 158 | } |
9c720493 | 159 | } |
461b9ab8 | 160 | unset($parameters['object']); |
9c720493 AE |
161 | |
162 | if (isset($parameters['title'])) { | |
99a64f13 MW |
163 | // component replacement |
164 | if (!empty($this->titleSearch)) { | |
165 | $parameters['title'] = str_replace($this->titleSearch, $this->titleReplace, $parameters['title']); | |
166 | } | |
167 | ||
9c720493 | 168 | // remove illegal characters |
c8a4a1f5 | 169 | $parameters['title'] = trim($this->titleRegex->replace($parameters['title'], '-'), '-'); |
a44843c3 AE |
170 | |
171 | // trim to 80 characters | |
84299745 | 172 | $parameters['title'] = rtrim(mb_substr($parameters['title'], 0, 80), '-'); |
b9f49efd | 173 | $parameters['title'] = mb_strtolower($parameters['title']); |
a97905ff | 174 | |
19c8cd5f MW |
175 | // encode title |
176 | if ($encodeTitle) $parameters['title'] = rawurlencode($parameters['title']); | |
9c720493 AE |
177 | } |
178 | ||
f3aa5021 | 179 | $parameters['controller'] = $controller; |
c2de61fb | 180 | $routeURL = RouteHandler::getInstance()->buildRoute($abbreviation, $parameters, $isACP); |
9c720493 AE |
181 | if (!$isRaw && !empty($url)) { |
182 | $routeURL .= (strpos($routeURL, '?') === false) ? '?' : '&'; | |
11ade432 | 183 | } |
034b6a78 AE |
184 | |
185 | // encode certain characters | |
186 | if (!empty($url)) { | |
39abe192 | 187 | $url = str_replace(['[', ']'], ['%5B', '%5D'], $url); |
034b6a78 AE |
188 | } |
189 | ||
9c720493 | 190 | $url = $routeURL . $url; |
11ade432 | 191 | |
a75dfffb AE |
192 | // handle applications |
193 | if (!PACKAGE_ID) { | |
39abe192 | 194 | $url = RouteHandler::getHost() . RouteHandler::getPath(['acp']) . ($isACP ? 'acp/' : '') . $url; |
a75dfffb AE |
195 | } |
196 | else { | |
f341086b | 197 | if (RequestHandler::getInstance()->inRescueMode()) { |
39abe192 | 198 | $pageURL = RouteHandler::getHost() . str_replace('//', '/', RouteHandler::getPath(['acp'])); |
f98e7cf2 | 199 | } |
c3299383 | 200 | else { |
c308c947 AE |
201 | $application = ApplicationHandler::getInstance()->getApplication($abbreviation); |
202 | if ($application === null) { | |
203 | throw new \InvalidArgumentException("Unknown application identifier '{$abbreviation}'."); | |
204 | } | |
205 | ||
206 | $pageURL = $application->getPageURL(); | |
f98e7cf2 AE |
207 | } |
208 | ||
c3299383 | 209 | $url = $pageURL . ($isACP ? 'acp/' : '') . $url; |
11ade432 AE |
210 | } |
211 | ||
ccedf3ab AE |
212 | // append previously removed anchor |
213 | $url .= $anchor; | |
214 | ||
f98e7cf2 | 215 | return $url; |
11ade432 | 216 | } |
3295fb92 AE |
217 | |
218 | /** | |
219 | * Returns the full URL to a CMS page. The `$languageID` parameter is optional and if not | |
220 | * present (or the integer value `-1` is given) will cause the handler to pick the correct | |
221 | * language version based upon the user's language. | |
222 | * | |
223 | * Passing in an illegal page id will cause this method to fail silently, returning an | |
224 | * empty string. | |
225 | * | |
2f53b086 MS |
226 | * @param integer $pageID page id |
227 | * @param integer $languageID language id, optional | |
228 | * @return string full URL of empty string if `$pageID` is invalid | |
e71525e4 | 229 | * @since 3.0 |
3295fb92 AE |
230 | */ |
231 | public function getCmsLink($pageID, $languageID = -1) { | |
232 | // use current language | |
233 | if ($languageID === -1) { | |
234 | $data = ControllerMap::getInstance()->lookupCmsPage($pageID, WCF::getLanguage()->languageID); | |
235 | ||
236 | // no result | |
237 | if ($data === null) { | |
238 | // attempt to use the default language instead | |
239 | if (LanguageFactory::getInstance()->getDefaultLanguageID() != WCF::getLanguage()->languageID) { | |
240 | $data = ControllerMap::getInstance()->lookupCmsPage($pageID, LanguageFactory::getInstance()->getDefaultLanguageID()); | |
241 | } | |
242 | ||
243 | // no result, possibly this is a non-multilingual page | |
244 | if ($data === null) { | |
245 | $data = ControllerMap::getInstance()->lookupCmsPage($pageID, null); | |
246 | } | |
247 | ||
248 | // still no result, page does not exist at all | |
249 | if ($data === null) { | |
250 | return ''; | |
251 | } | |
252 | } | |
253 | } | |
254 | else { | |
255 | $data = ControllerMap::getInstance()->lookupCmsPage($pageID, $languageID); | |
256 | ||
257 | // no result, page does not exist or at least not in the given language | |
258 | if ($data === null) { | |
259 | return ''; | |
260 | } | |
261 | } | |
262 | ||
263 | return $this->getLink($data['controller'], [ | |
b10d35d1 MW |
264 | 'application' => $data['application'], |
265 | 'forceFrontend' => true | |
3295fb92 AE |
266 | ]); |
267 | } | |
11ade432 | 268 | } |