Completed
Push — master ( 650298...bfb300 )
by Osma
02:33
created

WebController   F

Complexity

Total Complexity 109

Size/Duplication

Total Lines 742
Duplicated Lines 4.04 %

Coupling/Cohesion

Components 1
Dependencies 18

Importance

Changes 0
Metric Value
dl 30
loc 742
rs 1.858
c 0
b 0
f 0
wmc 109
lcom 1
cbo 18

22 Methods

Rating   Name   Duplication   Size   Complexity  
B __construct() 0 50 5
C linkUrlFilter() 0 38 14
B guessLanguage() 0 31 8
B listStyle() 0 11 8
A invokeVocabularies() 0 23 1
B invokeVocabularyConcept() 0 36 6
A getModifiedDate() 0 14 4
A getConceptModifiedDate() 0 18 5
A executeGitModifiedDateCommand() 0 10 2
A getGitModifiedDate() 0 19 4
A getConfigModifiedDate() 0 9 2
B invokeFeedbackForm() 0 36 6
A createFeedbackHeaders() 0 15 3
C sendFeedback() 0 43 10
A invokeAboutPage() 15 15 1
B invokeGlobalSearch() 0 51 7
B invokeVocabularySearch() 0 61 7
B invokeAlphabeticalIndex() 0 41 8
A invokeGroupIndex() 15 15 1
A invokeVocabularyHome() 0 25 2
A invokeGenericErrorPage() 0 13 1
A invokeChangeList() 0 19 4

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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
2
3
/**
4
 * Importing the dependencies.
5
 */
6
use \Punic\Language;
7
8
/**
9
 * WebController is an extension of the Controller that handles all
10
 * the requests originating from the view of the website.
11
 */
12
class WebController extends Controller
13
{
14
    /**
15
     * How long to store retrieved disk configuration for HTTP 304 header
16
     * from git information.
17
     */
18
    const GIT_MODIFIED_CONFIG_TTL = 600; // 10 minutes
19
    /**
20
     * Provides access to the templating engine.
21
     * @property object $twig the twig templating engine.
22
     */
23
    public $twig;
24
    public $honeypot;
25
26
    /**
27
     * Constructor for the WebController.
28
     * @param Model $model
29
     */
30
    public function __construct($model)
31
    {
32
        parent::__construct($model);
33
34
        // initialize Twig templates
35
        $tmpDir = $model->getConfig()->getTemplateCache();
36
37
        // check if the cache pointed by config.ttl exists, if not we create it.
38
        if (!file_exists($tmpDir)) {
39
            mkdir($tmpDir);
40
        }
41
42
        // specify where to look for templates and cache
43
        $loader = new Twig_Loader_Filesystem('view');
44
        // initialize Twig environment
45
        $this->twig = new Twig_Environment($loader, array('cache' => $tmpDir,'auto_reload' => true));
46
        $this->twig->addExtension(new Twig_Extensions_Extension_I18n());
47
        // used for setting the base href for the relative urls
48
        $this->twig->addGlobal("BaseHref", $this->getBaseHref());
49
        // setting the service name string from the config.ttl
50
        $this->twig->addGlobal("ServiceName", $this->model->getConfig()->getServiceName());
51
52
        // setting the service custom css file from the config.ttl
53
        if ($this->model->getConfig()->getCustomCss() !== null) {
54
            $this->twig->addGlobal("ServiceCustomCss", $this->model->getConfig()->getCustomCss());
55
        }
56
        // used for displaying the ui language selection as a dropdown
57
        if ($this->model->getConfig()->getUiLanguageDropdown() !== null) {
58
            $this->twig->addGlobal("LanguageDropdown", $this->model->getConfig()->getUiLanguageDropdown());
59
        }
60
61
        // setting the list of properties to be displayed in the search results
62
        $this->twig->addGlobal("PreferredProperties", array('skos:prefLabel', 'skos:narrower', 'skos:broader', 'skosmos:memberOf', 'skos:altLabel', 'skos:related'));
63
64
        // register a Twig filter for generating URLs for vocabulary resources (concepts and groups)
65
        $this->twig->addFilter(new Twig_SimpleFilter('link_url', array($this, 'linkUrlFilter')));
66
67
        // register a Twig filter for generating strings from language codes with CLDR
68
        $langFilter = new Twig_SimpleFilter('lang_name', function ($langcode, $lang) {
69
            return Language::getName($langcode, $lang);
70
        });
71
        $this->twig->addFilter($langFilter);
72
73
        // create the honeypot
74
        $this->honeypot = new \Honeypot();
75
        if (!$this->model->getConfig()->getHoneypotEnabled()) {
76
            $this->honeypot->disable();
77
        }
78
        $this->twig->addGlobal('honeypot', $this->honeypot);
79
    }
80
81
    /**
82
     * Creates Skosmos links from uris.
83
     * @param string $uri
84
     * @param Vocabulary $vocab
85
     * @param string $lang
86
     * @param string $type
87
     * @param string $clang content
88
     * @param string $term
89
     */
90
    public function linkUrlFilter($uri, $vocab, $lang, $type = 'page', $clang = null, $term = null) {
91
        // $vocab can either be null, a vocabulary id (string) or a Vocabulary object
92
        if ($vocab === null) {
93
            // target vocabulary is unknown, best bet is to link to the plain URI
94
            return $uri;
95
        } elseif (is_string($vocab)) {
96
            $vocid = $vocab;
97
            $vocab = $this->model->getVocabulary($vocid);
98
        } else {
99
            $vocid = $vocab->getId();
100
        }
101
102
        $params = array();
103
        if (isset($clang) && $clang !== $lang) {
104
            $params['clang'] = $clang;
105
        }
106
107
        if (isset($term)) {
108
            $params['q'] = $term;
109
        }
110
111
        // case 1: URI within vocabulary namespace: use only local name
112
        $localname = $vocab->getLocalName($uri);
113
        if ($localname !== $uri && $localname === urlencode($localname)) {
114
            // check that the prefix stripping worked, and there are no problematic chars in localname
115
            $paramstr = sizeof($params) > 0 ? '?' . http_build_query($params) : '';
116
            if ($type && $type !== '' && $type !== 'vocab' && !($localname === '' && $type === 'page')) {
117
                return "$vocid/$lang/$type/$localname" . $paramstr;
118
            }
119
120
            return "$vocid/$lang/$localname" . $paramstr;
121
        }
122
123
        // case 2: URI outside vocabulary namespace, or has problematic chars
124
        // pass the full URI as parameter instead
125
        $params['uri'] = $uri;
126
        return "$vocid/$lang/$type/?" . http_build_query($params);
127
    }
128
129
    /**
130
     * Guess the language of the user. Return a language string that is one
131
     * of the supported languages defined in the $LANGUAGES setting, e.g. "fi".
132
     * @param string $vocid identifier for the vocabulary eg. 'yso'.
133
     * @return string returns the language choice as a numeric string value
134
     */
135
    public function guessLanguage($vocid = null)
136
    {
137
        // 1. select language based on SKOSMOS_LANGUAGE cookie
138
        if (filter_input(INPUT_COOKIE, 'SKOSMOS_LANGUAGE', FILTER_SANITIZE_STRING)) {
139
            return filter_input(INPUT_COOKIE, 'SKOSMOS_LANGUAGE', FILTER_SANITIZE_STRING);
140
        }
141
142
        // 2. if vocabulary given, select based on the default language of the vocabulary
143
        if ($vocid !== null && $vocid !== '') {
144
            try {
145
                $vocab = $this->model->getVocabulary($vocid);
146
                return $vocab->getConfig()->getDefaultLanguage();
147
            } catch (Exception $e) {
148
                // vocabulary id not found, move on to the next selection method
149
            }
150
        }
151
152
        // 3. select language based on Accept-Language header
153
        header('Vary: Accept-Language'); // inform caches that a decision was made based on Accept header
154
        $this->negotiator = new \Negotiation\LanguageNegotiator();
155
        $langcodes = array_keys($this->languages);
156
        // using a random language from the configured UI languages when there is no accept language header set
157
        $acceptLanguage = filter_input(INPUT_SERVER, 'HTTP_ACCEPT_LANGUAGE', FILTER_SANITIZE_STRING) ? filter_input(INPUT_SERVER, 'HTTP_ACCEPT_LANGUAGE', FILTER_SANITIZE_STRING) : $langcodes[0];
158
        $bestLang = $this->negotiator->getBest($acceptLanguage, $langcodes);
159
        if (isset($bestLang) && in_array($bestLang, $langcodes)) {
160
            return $bestLang->getValue();
161
        }
162
163
        // show default site or prompt for language
164
        return $langcodes[0];
165
    }
166
167
    /**
168
     * Determines a css class that controls width and positioning of the vocabulary list element.
169
     * The layout is wider if the left/right box templates have not been provided.
170
     * @return string css class for the container eg. 'voclist-wide' or 'voclist-right'
171
     */
172
    private function listStyle() {
173
        $left = file_exists('view/left.inc');
174
        $right = file_exists('view/right.inc');
175
        $ret = 'voclist';
176
        if (!$left && !$right) {
177
            $ret .= '-wide';
178
        } else if (!($left && $right) && ($right || $left)) {
179
            $ret .= ($right) ? '-left' : '-right';
180
        }
181
        return $ret;
182
    }
183
184
    /**
185
     * Loads and renders the view containing all the vocabularies.
186
     * @param Request $request
187
     */
188
    public function invokeVocabularies($request)
189
    {
190
        // set language parameters for gettext
191
        $this->setLanguageProperties($request->getLang());
192
        // load template
193
        $template = $this->twig->loadTemplate('light.twig');
194
        // set template variables
195
        $categoryLabel = $this->model->getClassificationLabel($request->getLang());
196
        $sortedVocabs = $this->model->getVocabularyList(false, true);
197
        $langList = $this->model->getLanguages($request->getLang());
198
        $listStyle = $this->listStyle();
199
200
        // render template
201
        echo $template->render(
202
            array(
203
                'sorted_vocabs' => $sortedVocabs,
204
                'category_label' => $categoryLabel,
205
                'languages' => $this->languages,
206
                'lang_list' => $langList,
207
                'request' => $request,
208
                'list_style' => $listStyle
209
            ));
210
    }
211
212
    /**
213
     * Invokes the concept page of a single concept in a specific vocabulary.
214
     *
215
     * @param Request $request
216
     */
217
    public function invokeVocabularyConcept(Request $request)
218
    {
219
        $lang = $request->getLang();
220
        $this->setLanguageProperties($lang);
221
        $vocab = $request->getVocab();
222
223
        $langcodes = $vocab->getConfig()->getShowLangCodes();
224
        $uri = $vocab->getConceptURI($request->getUri()); // make sure it's a full URI
225
226
        $results = $vocab->getConceptInfo($uri, $request->getContentLang());
227
        if (!$results) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $results of type Concept[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
228
            $this->invokeGenericErrorPage($request);
229
            return;
230
        }
231
        $useModifiedDate = $vocab->getConfig()->getUseModifiedDate();
232
        if ($useModifiedDate) {
233
            $modifiedDate = $this->getModifiedDate($results[0], $vocab);
234
            // return if the controller sends the not modified header
235
            if ($this->sendNotModifiedHeader($modifiedDate)) {
0 ignored issues
show
Bug introduced by
It seems like $modifiedDate defined by $this->getModifiedDate($results[0], $vocab) on line 233 can also be of type null or string; however, Controller::sendNotModifiedHeader() does only seem to accept object<DateTime>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
236
                return;
237
            }
238
        }
239
        /** @var \Twig\Template $template */
240
        $template = (in_array('skos:Concept', $results[0]->getType()) || in_array('skos:ConceptScheme', $results[0]->getType())) ? $this->twig->loadTemplate('concept-info.twig') : $this->twig->loadTemplate('group-contents.twig');
241
242
        $crumbs = $vocab->getBreadCrumbs($request->getContentLang(), $uri);
243
        echo $template->render(array(
244
            'search_results' => $results,
245
            'vocab' => $vocab,
246
            'languages' => $this->languages,
247
            'explicit_langcodes' => $langcodes,
248
            'bread_crumbs' => $crumbs['breadcrumbs'],
249
            'combined' => $crumbs['combined'],
250
            'request' => $request)
251
        );
252
    }
253
254
    /**
255
     * Return the modified date. First try to get the modified date from the concept. If found, return this
256
     * date.
257
     *
258
     * Then try to get the modified date from the vocabulary's main concept scheme. If found, then return this
259
     * date.
260
     *
261
     * Finally, if no date found, return null.
262
     *
263
     * @param Concept $concept
264
     * @param Vocabulary $vocab
265
     * @return DateTime|null
266
     */
267
    protected function getModifiedDate(Concept $concept, Vocabulary $vocab)
268
    {
269
        $modifiedDate = null;
270
271
        $conceptModifiedDate = $this->getConceptModifiedDate($concept, $vocab);
272
        $gitModifiedDate = $this->getGitModifiedDate();
273
        $configModifiedDate = $this->getConfigModifiedDate();
274
275
        // max with an empty list raises an error and returns bool
276
        if ($conceptModifiedDate || $gitModifiedDate || $configModifiedDate) {
277
            $modifiedDate = max($conceptModifiedDate, $gitModifiedDate, $configModifiedDate);
278
        }
279
        return $modifiedDate;
280
    }
281
282
    /**
283
     * Get the concept modified date. Returns the concept modified date if available. Otherwise,
284
     * reverts to the modified date of the default concept scheme if available. Lastly, if it could
285
     * not get the modified date from the concept nor from the concept scheme, it returns null.
286
     *
287
     * @param Concept $concept concept used to retrieve modified date
288
     * @param Vocabulary $vocab vocabulary used to retrieve modified date
289
     * @return DateTime|null|string
290
     */
291
    protected function getConceptModifiedDate(Concept $concept, Vocabulary $vocab)
292
    {
293
        $modifiedDate = $concept->getModifiedDate();
294
        if (!$modifiedDate) {
295
            $conceptSchemeURI = $vocab->getDefaultConceptScheme();
296
            if ($conceptSchemeURI) {
297
                $conceptSchemeGraph = $vocab->getConceptScheme($conceptSchemeURI);
298
                if (!$conceptSchemeGraph->isEmpty()) {
0 ignored issues
show
Bug introduced by
The method isEmpty does only exist in EasyRdf\Graph, but not in EasyRdf\Http\Response and EasyRdf\Sparql\Result.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
299
                    $literal = $conceptSchemeGraph->getLiteral($conceptSchemeURI, "dc:modified");
0 ignored issues
show
Bug introduced by
The method getLiteral does only exist in EasyRdf\Graph, but not in EasyRdf\Http\Response and EasyRdf\Sparql\Result.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
300
                    if ($literal) {
301
                        $modifiedDate = $literal->getValue();
302
                    }
303
                }
304
            }
305
        }
306
307
        return $modifiedDate;
308
    }
309
310
    /**
311
     * Return the datetime of the latest commit, or null if git is not available or if the command failed
312
     * to execute.
313
     *
314
     * @see https://stackoverflow.com/a/33986403
315
     * @return DateTime|null
316
     */
317
    protected function getGitModifiedDate()
318
    {
319
        $commitDate = null;
0 ignored issues
show
Unused Code introduced by
$commitDate is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
320
        $cache = $this->model->getConfig()->getCache();
321
        $cacheKey = "git:modified_date";
322
        $gitCommand = 'git log -1 --date=iso --pretty=format:%cd';
323
        if ($cache->isAvailable()) {
324
            $commitDate = $cache->fetch($cacheKey);
325
            if (!$commitDate) {
326
                $commitDate = $this->executeGitModifiedDateCommand($gitCommand);
327
                if ($commitDate) {
328
                    $cache->store($cacheKey, $commitDate, static::GIT_MODIFIED_CONFIG_TTL);
329
                }
330
            }
331
        } else {
332
            $commitDate = $this->executeGitModifiedDateCommand($gitCommand);
333
        }
334
        return $commitDate;
335
    }
336
337
    /**
338
     * Execute the git command and return a parsed date time, or null if the command failed.
339
     *
340
     * @param string $gitCommand git command line that returns a formatted date time
341
     * @return DateTime|null
342
     */
343
    protected function executeGitModifiedDateCommand($gitCommand)
344
    {
345
        $commitDate = null;
346
        $commandOutput = @exec($gitCommand);
347
        if ($commandOutput) {
348
            $commitDate = new \DateTime(trim($commandOutput));
349
            $commitDate->setTimezone(new \DateTimeZone('UTC'));
350
        }
351
        return $commitDate;
352
    }
353
354
    /**
355
     * Return the datetime of the modified time of the config file. This value is read in the GlobalConfig
356
     * for every request, so we simply access that value and if not null, we will return a datetime. Otherwise,
357
     * we return a null value.
358
     *
359
     * @see http://php.net/manual/en/function.filemtime.php
360
     * @return DateTime|null
361
     */
362
    protected function getConfigModifiedDate()
363
    {
364
        $dateTime = null;
365
        $configModifiedTime = $this->model->getConfig()->getConfigModifiedTime();
366
        if (!is_null($configModifiedTime)) {
367
            $dateTime = (new DateTime())->setTimestamp($configModifiedTime);
368
        }
369
        return $dateTime;
370
    }
371
372
    /**
373
     * Invokes the feedback page with information of the users current vocabulary.
374
     */
375
    public function invokeFeedbackForm($request)
376
    {
377
        $template = $this->twig->loadTemplate('feedback.twig');
378
        $this->setLanguageProperties($request->getLang());
379
        $vocabList = $this->model->getVocabularyList(false);
380
        $vocab = $request->getVocab();
381
382
        $feedbackSent = false;
383
        $feedbackMsg = null;
384
        if ($request->getQueryParamPOST('message')) {
385
            $feedbackSent = true;
386
            $feedbackMsg = $request->getQueryParamPOST('message');
387
        }
388
        $feedbackName = $request->getQueryParamPOST('name');
389
        $feedbackEmail = $request->getQueryParamPOST('email');
390
        $feedbackVocab = $request->getQueryParamPOST('vocab');
391
392
        $feedbackVocabEmail = ($feedbackVocab !== null && $feedbackVocab !== '') ?
393
            $this->model->getVocabulary($feedbackVocab)->getConfig()->getFeedbackRecipient() : null;
394
395
        // if the hidden field has been set a value we have found a spam bot
396
        // and we do not actually send the message.
397
        if ($this->honeypot->validateHoneypot($request->getQueryParamPOST('item-description')) &&
398
            $this->honeypot->validateHoneytime($request->getQueryParamPOST('user-captcha'), $this->model->getConfig()->getHoneypotTime())) {
0 ignored issues
show
Documentation introduced by
$this->model->getConfig()->getHoneypotTime() is of type integer, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
399
            $this->sendFeedback($request, $feedbackMsg, $feedbackName, $feedbackEmail, $feedbackVocab, $feedbackVocabEmail);
400
        }
401
402
        echo $template->render(
403
            array(
404
                'languages' => $this->languages,
405
                'vocab' => $vocab,
406
                'vocabList' => $vocabList,
407
                'feedback_sent' => $feedbackSent,
408
                'request' => $request,
409
            ));
410
    }
411
412
    private function createFeedbackHeaders($fromName, $fromEmail, $toMail, $sender)
413
    {
414
        $headers = "MIME-Version: 1.0″ . '\r\n";
415
        $headers .= "Content-type: text/html; charset=UTF-8" . "\r\n";
416
        if (!empty($toMail)) {
417
            $headers .= "Cc: " . $this->model->getConfig()->getFeedbackAddress() . "\r\n";
418
        }
419
        if (!empty($fromEmail)) {
420
            $headers .= "Reply-To: $fromName <$fromEmail>\r\n";
421
        }
422
423
        $service = $this->model->getConfig()->getServiceName();
424
        $headers .= "From: $fromName via $service <$sender>";
425
        return $headers;
426
    }
427
428
    /**
429
     * Sends the user entered message through the php's mailer.
430
     * @param string $message only required parameter is the actual message.
431
     * @param string $fromName senders own name.
432
     * @param string $fromEmail senders email adress.
433
     * @param string $fromVocab which vocabulary is the feedback related to.
434
     */
435
    public function sendFeedback($request, $message, $fromName = null, $fromEmail = null, $fromVocab = null, $toMail = null)
436
    {
437
        $toAddress = ($toMail) ? $toMail : $this->model->getConfig()->getFeedbackAddress();
438
        if ($fromVocab !== null && $fromVocab !== '') {
439
            $message = 'Feedback from vocab: ' . strtoupper($fromVocab) . "<br />" . $message;
440
        }
441
442
        $envelopeSender = $this->model->getConfig()->getFeedbackEnvelopeSender();
443
        $subject = $this->model->getConfig()->getServiceName() . " feedback";
444
        // determine the sender address of the message
445
        $sender = $this->model->getConfig()->getFeedbackSender();
446
        if (empty($sender)) $sender = $envelopeSender;
447
        if (empty($sender)) $sender = $this->model->getConfig()->getFeedbackAddress();
448
449
        // determine sender name - default to "anonymous user" if not given by user
450
        if (empty($fromName)) $fromName = "anonymous user";
451
452
        $headers = $this->createFeedbackHeaders($fromName, $fromEmail, $toMail, $sender);
453
        $params = empty($envelopeSender) ? '' : "-f $envelopeSender";
454
455
        // adding some information about the user for debugging purposes.
456
        $message = $message . "<br /><br /> Debugging information:"
457
            . "<br />Timestamp: " . date(DATE_RFC2822)
458
            . "<br />User agent: " . $request->getServerConstant('HTTP_USER_AGENT')
459
            . "<br />Referer: " . $request->getServerConstant('HTTP_REFERER');
460
461
        try {
462
            mail($toAddress, $subject, $message, $headers, $params);
463
        } catch (Exception $e) {
464
            header("HTTP/1.0 404 Not Found");
465
            $template = $this->twig->loadTemplate('error-page.twig');
466
            if ($this->model->getConfig()->getLogCaughtExceptions()) {
467
                error_log('Caught exception: ' . $e->getMessage());
468
            }
469
470
            echo $template->render(
471
                array(
472
                    'languages' => $this->languages,
473
                ));
474
475
            return;
476
        }
477
    }
478
479
    /**
480
     * Invokes the about page for the Skosmos service.
481
     */
482 View Code Duplication
    public function invokeAboutPage($request)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
483
    {
484
        $template = $this->twig->loadTemplate('about.twig');
485
        $this->setLanguageProperties($request->getLang());
486
        $url = $request->getServerConstant('HTTP_HOST');
487
        $version = $this->model->getVersion();
488
489
        echo $template->render(
490
            array(
491
                'languages' => $this->languages,
492
                'version' => $version,
493
                'server_instance' => $url,
494
                'request' => $request,
495
            ));
496
    }
497
498
    /**
499
     * Invokes the search for concepts in all the availible ontologies.
500
     */
501
    public function invokeGlobalSearch($request)
502
    {
503
        $lang = $request->getLang();
504
        $template = $this->twig->loadTemplate('vocab-search-listing.twig');
505
        $this->setLanguageProperties($lang);
506
507
        $parameters = new ConceptSearchParameters($request, $this->model->getConfig());
508
509
        $vocabs = $request->getQueryParam('vocabs'); # optional
510
        // convert to vocids array to support multi-vocabulary search
511
        $vocids = ($vocabs !== null && $vocabs !== '') ? explode(' ', $vocabs) : null;
512
        $vocabObjects = array();
513
        if ($vocids) {
514
            foreach($vocids as $vocid) {
515
                $vocabObjects[] = $this->model->getVocabulary($vocid);
516
            }
517
        }
518
        $parameters->setVocabularies($vocabObjects);
519
520
        try {
521
            $countAndResults = $this->model->searchConceptsAndInfo($parameters);
522
        } catch (Exception $e) {
523
            header("HTTP/1.0 404 Not Found");
524
            if ($this->model->getConfig()->getLogCaughtExceptions()) {
525
                error_log('Caught exception: ' . $e->getMessage());
526
            }
527
            $this->invokeGenericErrorPage($request, $e->getMessage());
528
            return;
529
        }
530
        $counts = $countAndResults['count'];
531
        $searchResults = $countAndResults['results'];
532
        $vocabList = $this->model->getVocabularyList();
533
        $sortedVocabs = $this->model->getVocabularyList(false, true);
534
        $langList = $this->model->getLanguages($lang);
535
536
        echo $template->render(
537
            array(
538
                'search_count' => $counts,
539
                'languages' => $this->languages,
540
                'search_results' => $searchResults,
541
                'rest' => $parameters->getOffset()>0,
542
                'global_search' => true,
543
                'term' => $request->getQueryParamRaw('q'),
544
                'lang_list' => $langList,
545
                'vocabs' => str_replace(' ', '+', $vocabs),
546
                'vocab_list' => $vocabList,
547
                'sorted_vocabs' => $sortedVocabs,
548
                'request' => $request,
549
                'parameters' => $parameters
550
            ));
551
    }
552
553
    /**
554
     * Invokes the search for a single vocabs concepts.
555
     */
556
    public function invokeVocabularySearch($request)
557
    {
558
        $template = $this->twig->loadTemplate('vocab-search-listing.twig');
559
        $this->setLanguageProperties($request->getLang());
560
        $vocab = $request->getVocab();
561
        try {
562
            $vocabTypes = $this->model->getTypes($request->getVocabid(), $request->getLang());
563
        } catch (Exception $e) {
564
            header("HTTP/1.0 404 Not Found");
565
            if ($this->model->getConfig()->getLogCaughtExceptions()) {
566
                error_log('Caught exception: ' . $e->getMessage());
567
            }
568
569
            echo $template->render(
570
                array(
571
                    'languages' => $this->languages,
572
                ));
573
574
            return;
575
        }
576
577
        $langcodes = $vocab->getConfig()->getShowLangCodes();
578
        $parameters = new ConceptSearchParameters($request, $this->model->getConfig());
579
580
        try {
581
            $countAndResults = $this->model->searchConceptsAndInfo($parameters);
582
            $counts = $countAndResults['count'];
583
            $searchResults = $countAndResults['results'];
584
        } catch (Exception $e) {
585
            header("HTTP/1.0 404 Not Found");
586
            if ($this->model->getConfig()->getLogCaughtExceptions()) {
587
                error_log('Caught exception: ' . $e->getMessage());
588
            }
589
590
            echo $template->render(
591
                array(
592
                    'languages' => $this->languages,
593
                    'vocab' => $vocab,
594
                    'term' => $request->getQueryParam('q'),
595
                ));
596
            return;
597
        }
598
        echo $template->render(
599
            array(
600
                'languages' => $this->languages,
601
                'vocab' => $vocab,
602
                'search_results' => $searchResults,
603
                'search_count' => $counts,
604
                'rest' => $parameters->getOffset()>0,
605
                'limit_parent' => $parameters->getParentLimit(),
606
                'limit_type' =>  $request->getQueryParam('type') ? explode('+', $request->getQueryParam('type')) : null,
607
                'limit_group' => $parameters->getGroupLimit(),
608
                'limit_scheme' =>  $request->getQueryParam('scheme') ? explode('+', $request->getQueryParam('scheme')) : null,
609
                'group_index' => $vocab->listConceptGroups($request->getContentLang()),
610
                'parameters' => $parameters,
611
                'term' => $request->getQueryParamRaw('q'),
612
                'types' => $vocabTypes,
613
                'explicit_langcodes' => $langcodes,
614
                'request' => $request,
615
            ));
616
    }
617
618
    /**
619
     * Invokes the alphabetical listing for a specific vocabulary.
620
     */
621
    public function invokeAlphabeticalIndex($request)
622
    {
623
        $lang = $request->getLang();
624
        $this->setLanguageProperties($lang);
625
        $template = $this->twig->loadTemplate('alphabetical-index.twig');
626
        $vocab = $request->getVocab();
627
628
        $offset = ($request->getQueryParam('offset') && is_numeric($request->getQueryParam('offset')) && $request->getQueryParam('offset') >= 0) ? $request->getQueryParam('offset') : 0;
629
        if ($request->getQueryParam('limit')) {
630
            $count = $request->getQueryParam('limit');
631
        } else {
632
            $count = ($offset > 0) ? null : 250;
633
        }
634
635
        $contentLang = $request->getContentLang();
636
637
        $allAtOnce = $vocab->getConfig()->getAlphabeticalFull();
638
        if (!$allAtOnce) {
639
            $letters = $vocab->getAlphabet($contentLang);
640
            $letter = $request->getLetter();
641
            if ($letter === '') {
642
                $letter = $letters[0];
643
            }
644
            $searchResults = $vocab->searchConceptsAlphabetical($letter, $count, $offset, $contentLang);
645
        } else {
646
            $letters = null;
647
            $searchResults = $vocab->searchConceptsAlphabetical('*', null, null, $contentLang);
648
        }
649
650
        $request->setContentLang($contentLang);
651
652
        echo $template->render(
653
            array(
654
                'languages' => $this->languages,
655
                'vocab' => $vocab,
656
                'alpha_results' => $searchResults,
657
                'letters' => $letters,
658
                'all_letters' => $allAtOnce,
659
                'request' => $request,
660
            ));
661
    }
662
663
    /**
664
     * Invokes the vocabulary group index page template.
665
     * @param boolean $stats set to true to get vocabulary statistics visible.
666
     */
667 View Code Duplication
    public function invokeGroupIndex($request, $stats = false)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
668
    {
669
        $lang = $request->getLang();
670
        $this->setLanguageProperties($lang);
671
        $template = $this->twig->loadTemplate('group-index.twig');
672
        $vocab = $request->getVocab();
673
674
        echo $template->render(
675
            array(
676
                'languages' => $this->languages,
677
                'stats' => $stats,
678
                'vocab' => $vocab,
679
                'request' => $request,
680
            ));
681
    }
682
683
    /**
684
     * Loads and renders the view containing a specific vocabulary.
685
     */
686
    public function invokeVocabularyHome($request)
687
    {
688
        $lang = $request->getLang();
689
        // set language parameters for gettext
690
        $this->setLanguageProperties($lang);
691
        $vocab = $request->getVocab();
692
693
        $defaultView = $vocab->getConfig()->getDefaultSidebarView();
694
        // load template
695
        if ($defaultView === 'groups') {
696
            $this->invokeGroupIndex($request, true);
697
            return;
698
        }
699
700
        $template = $this->twig->loadTemplate('vocab.twig');
701
702
        echo $template->render(
703
            array(
704
                'languages' => $this->languages,
705
                'vocab' => $vocab,
706
                'search_letter' => 'A',
707
                'active_tab' => $defaultView,
708
                'request' => $request,
709
            ));
710
    }
711
712
    /**
713
     * Invokes a very generic errorpage.
714
     */
715
    public function invokeGenericErrorPage($request, $message = null)
716
    {
717
        $this->setLanguageProperties($request->getLang());
718
        header("HTTP/1.0 404 Not Found");
719
        $template = $this->twig->loadTemplate('error-page.twig');
720
        echo $template->render(
721
            array(
722
                'languages' => $this->languages,
723
                'request' => $request,
724
                'message' => $message,
725
                'requested_page' => filter_input(INPUT_SERVER, 'REQUEST_URI', FILTER_SANITIZE_STRING),
726
            ));
727
    }
728
729
    /**
730
     * Loads and renders the view containing a list of recent changes in the vocabulary.
731
     * @param Request $request
732
     */
733
    public function invokeChangeList($request, $prop='dc:created')
734
    {
735
        // set language parameters for gettext
736
        $this->setLanguageProperties($request->getLang());
737
        $vocab = $request->getVocab();
738
        $offset = ($request->getQueryParam('offset') && is_numeric($request->getQueryParam('offset')) && $request->getQueryParam('offset') >= 0) ? $request->getQueryParam('offset') : 0;
739
        $changeList = $vocab->getChangeList($prop, $request->getContentLang(), $request->getLang(), $offset);
740
        // load template
741
        $template = $this->twig->loadTemplate('changes.twig');
742
743
        // render template
744
        echo $template->render(
745
            array(
746
                'vocab' => $vocab,
747
                'languages' => $this->languages,
748
                'request' => $request,
749
                'changeList' => $changeList)
750
            );
751
    }
752
753
}
754