| Total Complexity | 62 | 
| Total Lines | 503 | 
| Duplicated Lines | 0 % | 
| Changes | 1 | ||
| Bugs | 0 | Features | 0 | 
Complex classes like WebController often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use WebController, and based on these observations, apply Extract Interface, too.
| 1 | <?php | ||
| 13 | class WebController extends Controller | ||
| 14 | { | ||
| 15 | /** | ||
| 16 | * Provides access to the templating engine. | ||
| 17 | * @property object $twig the twig templating engine. | ||
| 18 | */ | ||
| 19 | public $twig; | ||
| 20 | public $honeypot; | ||
| 21 | public $translator; | ||
| 22 | |||
| 23 | /** | ||
| 24 | * Constructor for the WebController. | ||
| 25 | * @param Model $model | ||
| 26 | */ | ||
| 27 | public function __construct($model) | ||
| 28 |     { | ||
| 29 | parent::__construct($model); | ||
| 30 | |||
| 31 | // initialize Twig templates | ||
| 32 | $tmpDir = $model->getConfig()->getTemplateCache(); | ||
| 33 | |||
| 34 | // check if the cache pointed by config.ttl exists, if not we create it. | ||
| 35 |         if (!file_exists($tmpDir)) { | ||
| 36 | mkdir($tmpDir); | ||
| 37 | } | ||
| 38 | |||
| 39 | // specify where to look for templates and cache | ||
| 40 | $loader = new \Twig\Loader\FilesystemLoader(__DIR__ . '/../view'); | ||
| 41 | // initialize Twig environment | ||
| 42 |         $this->twig = new \Twig\Environment($loader, array('cache' => $tmpDir,'auto_reload' => true)); | ||
| 43 | // used for setting the base href for the relative urls | ||
| 44 |         $this->twig->addGlobal("BaseHref", $this->getBaseHref()); | ||
| 45 | |||
| 46 | // pass the GlobalConfig object to templates so they can access configuration | ||
| 47 |         $this->twig->addGlobal("GlobalConfig", $this->model->getConfig()); | ||
| 48 | |||
| 49 | // setting the list of properties to be displayed in the search results | ||
| 50 |         $this->twig->addGlobal("PreferredProperties", array('skos:prefLabel', 'skos:narrower', 'skos:broader', 'skosmos:memberOf', 'skos:altLabel', 'skos:related')); | ||
| 51 | |||
| 52 | // register a Twig filter for generating URLs for global pages (landing, about, feedback, vocab-home..) | ||
| 53 | $this->twig->addExtension(new GlobalUrlExtension()); | ||
| 54 | |||
| 55 | // register a Twig filter for generating URLs for vocabulary resources (concepts and groups) | ||
| 56 | $this->twig->addExtension(new LinkUrlExtension($model)); | ||
| 57 | |||
| 58 | // register a Twig filter for generating strings from language codes with CLDR | ||
| 59 |         $langFilter = new \Twig\TwigFilter('lang_name', function ($langcode, $lang) { | ||
| 60 | return Language::getName($langcode, $lang); | ||
| 61 | }); | ||
| 62 | $this->twig->addFilter($langFilter); | ||
| 63 | |||
| 64 | $this->translator = $model->getTranslator(); | ||
| 65 | $this->twig->addExtension(new TranslationExtension($this->translator)); | ||
| 66 | |||
| 67 | // create the honeypot | ||
| 68 | $this->honeypot = new \Honeypot(); | ||
| 69 |         if (!$this->model->getConfig()->getHoneypotEnabled()) { | ||
| 70 | $this->honeypot->disable(); | ||
| 71 | } | ||
| 72 |         $this->twig->addGlobal('honeypot', $this->honeypot); | ||
| 73 | } | ||
| 74 | |||
| 75 | /** | ||
| 76 | * Guess the language of the user. Return a language string that is one | ||
| 77 | * of the supported languages defined in the $LANGUAGES setting, e.g. "fi". | ||
| 78 | * @param Request $request HTTP request | ||
| 79 | * @param string $vocid identifier for the vocabulary eg. 'yso'. | ||
| 80 | * @return string returns the language choice as a numeric string value | ||
| 81 | */ | ||
| 82 | public function guessLanguage($request, $vocid = null) | ||
| 83 |     { | ||
| 84 | // 1. select language based on SKOSMOS_LANGUAGE cookie | ||
| 85 |         $languageCookie = $request->getCookie('SKOSMOS_LANGUAGE'); | ||
| 86 |         if ($languageCookie) { | ||
| 87 | return $languageCookie; | ||
| 88 | } | ||
| 89 | |||
| 90 | // 2. if vocabulary given, select based on the default language of the vocabulary | ||
| 91 |         if ($vocid !== null && $vocid !== '') { | ||
| 92 |             try { | ||
| 93 | $vocab = $this->model->getVocabulary($vocid); | ||
| 94 | return $vocab->getConfig()->getDefaultLanguage(); | ||
| 95 |             } catch (Exception $e) { | ||
| 96 | // vocabulary id not found, move on to the next selection method | ||
| 97 | } | ||
| 98 | } | ||
| 99 | |||
| 100 | // 3. select language based on Accept-Language header | ||
| 101 |         header('Vary: Accept-Language'); // inform caches that a decision was made based on Accept header | ||
| 102 | $this->negotiator = new \Negotiation\LanguageNegotiator(); | ||
| 103 | $langcodes = array_keys($this->languages); | ||
| 104 | // using a random language from the configured UI languages when there is no accept language header set | ||
| 105 |         $acceptLanguage = $request->getServerConstant('HTTP_ACCEPT_LANGUAGE') ? $request->getServerConstant('HTTP_ACCEPT_LANGUAGE') : $langcodes[0]; | ||
| 106 | |||
| 107 | $bestLang = $this->negotiator->getBest($acceptLanguage, $langcodes); | ||
| 108 |         if (isset($bestLang) && in_array($bestLang->getValue(), $langcodes)) { | ||
| 109 | return $bestLang->getValue(); | ||
| 110 | } | ||
| 111 | |||
| 112 | // show default site or prompt for language | ||
| 113 | return $langcodes[0]; | ||
| 114 | } | ||
| 115 | |||
| 116 | /** | ||
| 117 | * Determines a css class that controls width and positioning of the vocabulary list element. | ||
| 118 | * The layout is wider if the left/right box templates have not been provided. | ||
| 119 | * @return string css class for the container eg. 'voclist-wide' or 'voclist-right' | ||
| 120 | */ | ||
| 121 | private function listStyle() | ||
| 122 |     { | ||
| 123 |         $left = file_exists('view/left.inc'); | ||
| 124 |         $right = file_exists('view/right.inc'); | ||
| 125 | $ret = 'voclist'; | ||
| 126 |         if (!$left && !$right) { | ||
| 127 | $ret .= '-wide'; | ||
| 128 |         } elseif (!($left && $right) && ($right || $left)) { | ||
| 129 | $ret .= ($right) ? '-left' : '-right'; | ||
| 130 | } | ||
| 131 | return $ret; | ||
| 132 | } | ||
| 133 | |||
| 134 | /** | ||
| 135 | * Loads and renders the landing page view. | ||
| 136 | * @param Request $request | ||
| 137 | */ | ||
| 138 | public function invokeLandingPage($request) | ||
| 158 | ) | ||
| 159 | ); | ||
| 160 | } | ||
| 161 | |||
| 162 | /** | ||
| 163 | * Invokes the concept page of a single concept in a specific vocabulary. | ||
| 164 | * | ||
| 165 | * @param Request $request | ||
| 166 | */ | ||
| 167 | public function invokeVocabularyConcept(Request $request) | ||
| 202 | ); | ||
| 203 | } | ||
| 204 | |||
| 205 | /** | ||
| 206 | * Invokes the feedback page with information of the users current vocabulary. | ||
| 207 | */ | ||
| 208 | public function invokeFeedbackForm($request) | ||
| 209 |     { | ||
| 210 |         $template = $this->twig->load('feedback.twig'); | ||
| 211 | $this->model->setLocale($request->getLang()); | ||
| 212 | $vocabList = $this->model->getVocabularyList(false); | ||
| 213 | $vocab = $request->getVocab(); | ||
| 214 | |||
| 215 | $feedbackSent = false; | ||
| 216 |         if ($request->getQueryParamPOST('message')) { | ||
| 217 | $feedbackSent = true; | ||
| 218 |             $feedbackMsg = $request->getQueryParamPOST('message'); | ||
| 219 |             $feedbackName = $request->getQueryParamPOST('name'); | ||
| 220 |             $feedbackEmail = $request->getQueryParamPOST('email'); | ||
| 221 |             $msgSubject = $request->getQueryParamPOST('msgsubject'); | ||
| 222 |             $feedbackVocab = $request->getQueryParamPOST('vocab'); | ||
| 223 | $feedbackVocabEmail = ($feedbackVocab !== null && $feedbackVocab !== '') ? | ||
| 224 | $this->model->getVocabulary($feedbackVocab)->getConfig()->getFeedbackRecipient() : null; | ||
| 225 | // if the hidden field has been set a value we have found a spam bot | ||
| 226 | // and we do not actually send the message. | ||
| 227 |             if ($this->honeypot->validateHoneypot($request->getQueryParamPOST('item-description')) && | ||
| 228 |                 $this->honeypot->validateHoneytime($request->getQueryParamPOST('user-captcha'), $this->model->getConfig()->getHoneypotTime())) { | ||
| 229 | $this->sendFeedback($request, $feedbackMsg, $msgSubject, $feedbackName, $feedbackEmail, $feedbackVocab, $feedbackVocabEmail); | ||
| 230 | } | ||
| 231 | } | ||
| 232 | echo $template->render( | ||
| 233 | array( | ||
| 234 | 'languages' => $this->languages, | ||
| 235 | 'vocab' => $vocab, | ||
| 236 | 'vocabList' => $vocabList, | ||
| 237 | 'feedback_sent' => $feedbackSent, | ||
| 238 | 'request' => $request, | ||
| 239 | ) | ||
| 240 | ); | ||
| 241 | } | ||
| 242 | |||
| 243 | private function createFeedbackHeaders($fromName, $fromEmail, $toMail, $sender) | ||
| 244 |     { | ||
| 245 | $headers = "MIME-Version: 1.0" . "\r\n"; | ||
| 246 | $headers .= "Content-type: text/html; charset=UTF-8" . "\r\n"; | ||
| 247 |         if (!empty($toMail)) { | ||
| 248 | $headers .= "Cc: " . $this->model->getConfig()->getFeedbackAddress() . "\r\n"; | ||
| 249 | } | ||
| 250 |         if (!empty($fromEmail)) { | ||
| 251 | $headers .= "Reply-To: $fromName <$fromEmail>\r\n"; | ||
| 252 | } | ||
| 253 | $service = $this->model->getConfig()->getServiceName(); | ||
| 254 | return $headers . "From: $fromName via $service <$sender>"; | ||
| 255 | } | ||
| 256 | |||
| 257 | /** | ||
| 258 | * Sends the user entered message through the php's mailer. | ||
| 259 | * @param string $message content given by user. | ||
| 260 | * @param string $messageSubject subject line given by user. | ||
| 261 | * @param string $fromName senders own name. | ||
| 262 | * @param string $fromEmail senders email address. | ||
| 263 | * @param string $fromVocab which vocabulary is the feedback related to. | ||
| 264 | */ | ||
| 265 | public function sendFeedback($request, $message, $messageSubject, $fromName = null, $fromEmail = null, $fromVocab = null, $toMail = null) | ||
| 266 |     { | ||
| 267 | $toAddress = ($toMail) ? $toMail : $this->model->getConfig()->getFeedbackAddress(); | ||
| 268 | $messageSubject = "[" . $this->model->getConfig()->getServiceName() . "] " . $messageSubject; | ||
| 269 |         if ($fromVocab !== null && $fromVocab !== '') { | ||
| 270 | $message = 'Feedback from vocab: ' . strtoupper($fromVocab) . "<br />" . $message; | ||
| 271 | } | ||
| 272 | $envelopeSender = $this->model->getConfig()->getFeedbackEnvelopeSender(); | ||
| 273 | // determine the sender address of the message | ||
| 274 | $sender = $this->model->getConfig()->getFeedbackSender(); | ||
| 275 |         if (empty($sender)) { | ||
| 276 | $sender = $envelopeSender; | ||
| 277 | } | ||
| 278 |         if (empty($sender)) { | ||
| 279 | $sender = $this->model->getConfig()->getFeedbackAddress(); | ||
| 280 | } | ||
| 281 | |||
| 282 | // determine sender name - default to "anonymous user" if not given by user | ||
| 283 |         if (empty($fromName)) { | ||
| 284 | $fromName = "anonymous user"; | ||
| 285 | } | ||
| 286 | $headers = $this->createFeedbackHeaders($fromName, $fromEmail, $toMail, $sender); | ||
| 287 | $params = empty($envelopeSender) ? '' : "-f $envelopeSender"; | ||
| 288 | // adding some information about the user for debugging purposes. | ||
| 289 | $message = $message . "<br /><br /> Debugging information:" | ||
| 290 | . "<br />Timestamp: " . date(DATE_RFC2822) | ||
| 291 |             . "<br />User agent: " . $request->getServerConstant('HTTP_USER_AGENT') | ||
| 292 |             . "<br />Referer: " . $request->getServerConstant('HTTP_REFERER'); | ||
| 293 | |||
| 294 |         try { | ||
| 295 | mail($toAddress, $messageSubject, $message, $headers, $params); | ||
| 296 |         } catch (Exception $e) { | ||
| 297 |             header("HTTP/1.0 404 Not Found"); | ||
| 298 |             $template = $this->twig->load('error.twig'); | ||
| 299 |             if ($this->model->getConfig()->getLogCaughtExceptions()) { | ||
| 300 |                 error_log('Caught exception: ' . $e->getMessage()); | ||
| 301 | } | ||
| 302 | |||
| 303 | echo $template->render( | ||
| 304 | array( | ||
| 305 | 'languages' => $this->languages, | ||
| 306 | ) | ||
| 307 | ); | ||
| 308 | |||
| 309 | return; | ||
| 310 | } | ||
| 311 | } | ||
| 312 | |||
| 313 | /** | ||
| 314 | * Invokes the about page for the Skosmos service. | ||
| 315 | */ | ||
| 316 | public function invokeAboutPage($request) | ||
| 317 |     { | ||
| 318 |         $template = $this->twig->load('about.twig'); | ||
| 319 | $this->model->setLocale($request->getLang()); | ||
| 320 |         $url = $request->getServerConstant('HTTP_HOST'); | ||
| 321 | |||
| 322 | echo $template->render( | ||
| 323 | array( | ||
| 324 | 'languages' => $this->languages, | ||
| 325 | 'server_instance' => $url, | ||
| 326 | 'request' => $request, | ||
| 327 | ) | ||
| 328 | ); | ||
| 329 | } | ||
| 330 | |||
| 331 | /** | ||
| 332 | * Invokes the search for concepts in all the available ontologies. | ||
| 333 | */ | ||
| 334 | public function invokeGlobalSearch($request) | ||
| 397 | ) | ||
| 398 | ); | ||
| 399 | } | ||
| 400 | |||
| 401 | /** | ||
| 402 | * Invokes the search for a single vocabs concepts. | ||
| 403 | */ | ||
| 404 | public function invokeVocabularySearch($request) | ||
| 470 | ) | ||
| 471 | ); | ||
| 472 | } | ||
| 473 | |||
| 474 | /** | ||
| 475 | * Loads and renders the view containing a specific vocabulary. | ||
| 476 | */ | ||
| 477 | public function invokeVocabularyHome($request) | ||
| 478 |     { | ||
| 479 | $lang = $request->getLang(); | ||
| 480 | $this->model->setLocale($request->getLang()); | ||
| 481 | $vocab = $request->getVocab(); | ||
| 482 | |||
| 483 | $defaultView = $vocab->getConfig()->getDefaultSidebarView(); | ||
| 484 | |||
| 485 | $pluginParameters = json_encode($vocab->getConfig()->getPluginParameters()); | ||
| 486 | |||
| 487 |         $template = $this->twig->load('vocab-home.twig'); | ||
| 488 | |||
| 489 | echo $template->render( | ||
| 490 | array( | ||
| 491 | 'languages' => $this->languages, | ||
| 492 | 'vocab' => $vocab, | ||
| 493 | 'search_letter' => 'A', | ||
| 494 | 'active_tab' => $defaultView, | ||
| 495 | 'request' => $request, | ||
| 496 | 'plugin_params' => $pluginParameters | ||
| 497 | ) | ||
| 498 | ); | ||
| 499 | } | ||
| 500 | |||
| 501 | /** | ||
| 502 | * Invokes a very generic errorpage. | ||
| 503 | */ | ||
| 504 | public function invokeGenericErrorPage($request, $message = null) | ||
| 516 | ) | ||
| 517 | ); | ||
| 518 | } | ||
| 519 | } | ||
| 520 |