Completed
Branch master (ec991a)
by Osma
05:04 queued 02:03
created

WebController::sendFeedback()   B

Complexity

Conditions 6
Paths 24

Size

Total Lines 36
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 36
rs 8.439
cc 6
eloc 24
nc 24
nop 6
1
<?php
2
/**
3
 * Copyright (c) 2012-2013 Aalto University and University of Helsinki
4
 * MIT License
5
 * see LICENSE.txt for more information
6
 */
7
8
/**
9
 * Importing the dependencies.
10
 */
11
use \Punic\Language;
12
13
/**
14
 * WebController is an extension of the Controller that handles all
15
 * the requests originating from the view of the website.
16
 */
17
class WebController extends Controller
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
18
{
19
    /**
20
     * Provides access to the templating engine.
21
     * @property object $twig the twig templating engine.
22
     */
23
    public $twig;
24
25
    /**
26
     * Constructor for the WebController.
27
     * @param Model $model
28
     */
29
    public function __construct($model)
30
    {
31
        parent::__construct($model);
32
33
        // initialize Twig templates
34
        $tmp_dir = $model->getConfig()->getTemplateCache();
35
36
        // check if the cache pointed by config.inc exists, if not we create it.
37
        if (!file_exists($tmp_dir)) {
38
            mkdir($tmp_dir);
39
        }
40
41
        // specify where to look for templates and cache
42
        $loader = new Twig_Loader_Filesystem('view');
43
        // initialize Twig environment
44
        $this->twig = new Twig_Environment($loader, array('cache' => $tmp_dir,
45
            'auto_reload' => true, 'debug' => true));
46
        $this->twig->addExtension(new Twig_Extensions_Extension_I18n());
47
        //ENABLES DUMP() method for easy and fun debugging!
48
        $this->twig->addExtension(new Twig_Extension_Debug());
49
        // used for setting the base href for the relative urls
50
        $this->twig->addGlobal("BaseHref", $this->getBaseHref());
51
        // setting the service name string from the config.inc
52
        $this->twig->addGlobal("ServiceName", $this->model->getConfig()->getServiceName());
53
        // setting the service logo location from the config.inc
54
        if ($this->model->getConfig()->getServiceLogo() !== null) {
55
            $this->twig->addGlobal("ServiceLogo", $this->model->getConfig()->getServiceLogo());
56
        }
57
58
        // setting the service custom css file from the config.inc
59
        if ($this->model->getConfig()->getCustomCss() !== null) {
60
            $this->twig->addGlobal("ServiceCustomCss", $this->model->getConfig()->getCustomCss());
61
        }
62
        // used for displaying the ui language selection as a dropdown
63
        if ($this->model->getConfig()->getUiLanguageDropdown() !== null) {
64
            $this->twig->addGlobal("LanguageDropdown", $this->model->getConfig()->getUiLanguageDropdown());
65
        }
66
67
        // setting the list of properties to be displayed in the search results
68
        $this->twig->addGlobal("PreferredProperties", array('skos:prefLabel', 'skos:narrower', 'skos:broader', 'skosmos:memberOf', 'skos:altLabel', 'skos:related'));
69
70
        // register a Twig filter for generating URLs for vocabulary resources (concepts and groups)
71
        $controller = $this; // for use by anonymous function below
72
        $urlFilter = new Twig_SimpleFilter('link_url', function ($uri, $vocab, $lang, $type = 'page', $clang = null, $term = null) use ($controller) {
73
            // $vocab can either be null, a vocabulary id (string) or a Vocabulary object
74
            if ($vocab === null) {
75
                // target vocabulary is unknown, best bet is to link to the plain URI
76
                return $uri;
77
            } elseif (is_string($vocab)) {
78
                $vocid = $vocab;
79
                $vocab = $controller->model->getVocabulary($vocid);
80
            } else {
81
                $vocid = $vocab->getId();
82
            }
83
84
            $params = array();
85
            if (isset($clang) && $clang !== $lang) {
86
                $params['clang'] = $clang;
87
            }
88
89
            if (isset($term)) {
90
                $params['q'] = $term;
91
            }
92
93
            // case 1: URI within vocabulary namespace: use only local name
94
            $localname = $vocab->getLocalName($uri);
95
            if ($localname !== $uri && $localname === urlencode($localname)) {
96
                // check that the prefix stripping worked, and there are no problematic chars in localname
97
                $paramstr = sizeof($params) > 0 ? '?' . http_build_query($params) : '';
98
                if ($type && $type !== '' && $type !== 'vocab') {
99
                    return "$vocid/$lang/$type/$localname" . $paramstr;
100
                }
101
102
                return "$vocid/$lang/$localname" . $paramstr;
103
            }
104
105
            // case 2: URI outside vocabulary namespace, or has problematic chars
106
            // pass the full URI as parameter instead
107
            $params['uri'] = $uri;
108
            return "$vocid/$lang/$type/?" . http_build_query($params);
109
        });
110
        $this->twig->addFilter($urlFilter);
111
112
        // register a Twig filter for generating strings from language codes with CLDR
113
        $langFilter = new Twig_SimpleFilter('lang_name', function ($langcode, $lang) {
114
            return Language::getName($langcode, $lang);
115
        });
116
        $this->twig->addFilter($langFilter);
117
118
    }
119
120
    /**
121
     * Guess the language of the user. Return a language string that is one
122
     * of the supported languages defined in the $LANGUAGES setting, e.g. "fi".
123
     * @param string $vocab_id identifier for the vocabulary eg. 'yso'.
124
     * @return string returns the language choice as a numeric string value
125
     */
126
    public function guessLanguage($vocab_id = null)
127
    {
128
        // 1. select language based on SKOSMOS_LANGUAGE cookie
129
        if (filter_input(INPUT_COOKIE, 'SKOSMOS_LANGUAGE', FILTER_SANITIZE_STRING)) {
130
            return filter_input(INPUT_COOKIE, 'SKOSMOS_LANGUAGE', FILTER_SANITIZE_STRING);
131
        }
132
133
        // 2. if vocabulary given, select based on the default language of the vocabulary
134
        if ($vocab_id !== null && $vocab_id !== '') {
135
            try {
136
                $vocab = $this->model->getVocabulary($vocab_id);
137
                return $vocab->getConfig()->getDefaultLanguage();
138
            } catch (Exception $e) {
139
                // vocabulary id not found, move on to the next selection method
140
            }
141
        }
142
143
        // 3. select language based on Accept-Language header
144
        header('Vary: Accept-Language'); // inform caches that a decision was made based on Accept header
145
        $this->negotiator = new \Negotiation\LanguageNegotiator();
146
        $langcodes = array_keys($this->languages);
147
        $acceptLanguage = filter_input(INPUT_SERVER, 'HTTP_ACCEPT_LANGUAGE', FILTER_SANITIZE_STRING) ? filter_input(INPUT_SERVER, 'HTTP_ACCEPT_LANGUAGE', FILTER_SANITIZE_STRING) : '';
148
        $bestLang = $this->negotiator->getBest($acceptLanguage, $langcodes);
149
        if (isset($bestLang) && in_array($bestLang, $langcodes)) {
150
            return $bestLang->getValue();
151
        }
152
153
        // show default site or prompt for language
154
        return $langcodes[0];
155
    }
156
157
    /**
158
     * Loads and renders the view containing all the vocabularies.
159
     * @param Request $request
160
     */
161
    public function invokeVocabularies($request)
162
    {
163
        // set language parameters for gettext
164
        $this->setLanguageProperties($request->getLang());
165
        // load template
166
        $template = $this->twig->loadTemplate('light.twig');
167
        // set template variables
168
        $categoryLabel = $this->model->getClassificationLabel($request->getLang());
169
        $sortedVocabs = $this->model->getVocabularyList(false, true);
170
        $langList = $this->model->getLanguages($request->getLang());
171
172
        // render template
173
        echo $template->render(
174
            array(
175
                'sorted_vocabs' => $sortedVocabs,
176
                'category_label' => $categoryLabel,
177
                'languages' => $this->languages,
178
                'lang_list' => $langList,
179
                'request' => $request,
180
            ));
181
    }
182
183
    /**
184
     * Invokes the concept page of a single concept in a specific vocabulary.
185
     */
186
    public function invokeVocabularyConcept($request)
187
    {
188
        $lang = $request->getLang();
189
        $this->setLanguageProperties($lang);
190
        $vocab = $request->getVocab();
191
192
        $langcodes = $vocab->getConfig()->getShowLangCodes();
193
        $uri = $vocab->getConceptURI($request->getUri()); // make sure it's a full URI
194
195
        $results = $vocab->getConceptInfo($uri, $request->getContentLang());
196
        if (!$results) {
197
            $this->invokeGenericErrorPage($request);
198
            return;
199
        }
200
        $template = (in_array('skos:Concept', $results[0]->getType())) ? $this->twig->loadTemplate('concept-info.twig') : $this->twig->loadTemplate('group-contents.twig');
201
        
202
        $crumbs = $vocab->getBreadCrumbs($request->getContentLang(), $uri);
203
        echo $template->render(array(
204
            'search_results' => $results,
205
            'vocab' => $vocab,
206
            'languages' => $this->languages,
207
            'explicit_langcodes' => $langcodes,
208
            'bread_crumbs' => $crumbs['breadcrumbs'],
209
            'combined' => $crumbs['combined'],
210
            'request' => $request)
211
        );
212
    }
213
214
    /**
215
     * Invokes the feedback page with information of the users current vocabulary.
216
     */
217
    public function invokeFeedbackForm($request)
218
    {
219
        $template = $this->twig->loadTemplate('feedback.twig');
220
        $this->setLanguageProperties($request->getLang());
221
        $vocabList = $this->model->getVocabularyList(false);
222
        $vocab = $request->getVocab();
223
224
        $feedback_sent = false;
225
        $feedback_msg = null;
226
        if ($request->getQueryParamPOST('message')) {
227
            $feedback_sent = true;
228
            $feedback_msg = $request->getQueryParamPOST('message');
229
        }
230
        $feedback_name = $request->getQueryParamPOST('name');
231
        $feedback_email = $request->getQueryParamPOST('email');
232
        $feedback_vocab = $request->getQueryParamPOST('vocab');
233
        $feedback_vocab_email = ($vocab !== null) ? $vocab->getConfig()->getFeedbackRecipient() : null;
234
235
        // if the hidden field has been set a value we have found a spam bot
236
        // and we do not actually send the message.
237
        if ($feedback_sent && $request->getQueryParamPOST('trap') === '') {
238
            $this->sendFeedback($request, $feedback_msg, $feedback_name, $feedback_email, $feedback_vocab, $feedback_vocab_email);
239
        }
240
241
        echo $template->render(
242
            array(
243
                'languages' => $this->languages,
244
                'vocab' => $vocab,
245
                'vocabList' => $vocabList,
246
                'feedback_sent' => $feedback_sent,
247
                'request' => $request,
248
            ));
249
    }
250
251
    private function createFeedbackHeaders($fromName, $fromEmail, $toMail)
252
    {
253
        $headers = "MIME-Version: 1.0″ . '\r\n";
254
        $headers .= "Content-type: text/html; charset=UTF-8" . "\r\n";
255
        if ($toMail) {
256
            $headers .= "Cc: " . $this->model->getConfig()->getFeedbackAddress() . "\r\n";
257
        }
258
259
        $headers .= "From: $fromName <$fromEmail>" . "\r\n" . 'X-Mailer: PHP/' . phpversion();
260
        return $headers;
261
    }
262
263
    /**
264
     * Sends the user entered message through the php's mailer.
265
     * @param string $message only required parameter is the actual message.
266
     * @param string $fromName senders own name.
267
     * @param string $fromEmail senders email adress.
268
     * @param string $fromVocab which vocabulary is the feedback related to.
269
     */
270
    public function sendFeedback($request, $message, $fromName = null, $fromEmail = null, $fromVocab = null, $toMail = null)
271
    {
272
        $toAddress = ($toMail) ? $toMail : $this->model->getConfig()->getFeedbackAddress();
273
        if ($fromVocab !== null) {
274
            $message = 'Feedback from vocab: ' . strtoupper($fromVocab) . "<br />" . $message;
275
        }
276
277
        $subject = SERVICE_NAME . " feedback";
278
        $headers = $this->createFeedbackHeaders($fromName, $fromEmail, $toMail);
279
        $envelopeSender = FEEDBACK_ENVELOPE_SENDER;
280
        $params = empty($envelopeSender) ? '' : "-f $envelopeSender";
281
282
        // adding some information about the user for debugging purposes.
283
        $message = $message . "<br /><br /> Debugging information:"
284
            . "<br />Timestamp: " . date(DATE_RFC2822)
285
            . "<br />User agent: " . $request->getServerConstant('HTTP_USER_AGENT')
286
            . "<br />IP address: " . $request->getServerConstant('REMOTE_ADDR')
287
            . "<br />Referer: " . $request->getServerConstant('HTTP_REFERER');
288
289
        try {
290
            mail($toAddress, $subject, $message, $headers, $params);
291
        } catch (Exception $e) {
292
            header("HTTP/1.0 404 Not Found");
293
            $template = $this->twig->loadTemplate('error-page.twig');
294
            if ($this->model->getConfig()->getLogCaughtExceptions()) {
295
                error_log('Caught exception: ' . $e->getMessage());
296
            }
297
298
            echo $template->render(
299
                array(
300
                    'languages' => $this->languages,
301
                ));
302
303
            return;
304
        }
305
    }
306
307
    /**
308
     * Invokes the about page for the Skosmos service.
309
     */
310 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...
311
    {
312
        $template = $this->twig->loadTemplate('about.twig');
313
        $this->setLanguageProperties($request->getLang());
314
        $url = $request->getServerConstant('HTTP_HOST');
315
        $version = $this->model->getVersion();
316
317
        echo $template->render(
318
            array(
319
                'languages' => $this->languages,
320
                'version' => $version,
321
                'server_instance' => $url,
322
                'request' => $request,
323
            ));
324
    }
325
326
    /**
327
     * Invokes the search for concepts in all the availible ontologies.
328
     */
329
    public function invokeGlobalSearch($request)
330
    {
331
        $lang = $request->getLang();
332
        $template = $this->twig->loadTemplate('vocab-search-listing.twig');
333
        $this->setLanguageProperties($lang);
334
335
        $parameters = new ConceptSearchParameters($request, $this->model->getConfig());
336
337
        $vocabs = $request->getQueryParam('vocabs'); # optional
338
        // convert to vocids array to support multi-vocabulary search
339
        $vocids = ($vocabs !== null && $vocabs !== '') ? explode(' ', $vocabs) : null;
340
        $vocabObjects = array();
341
        if ($vocids) {
342
            foreach($vocids as $vocid) {
343
                $vocabObjects[] = $this->model->getVocabulary($vocid);
344
            }
345
        }
346
        $parameters->setVocabularies($vocabObjects);
347
348
        try {
349
            $count_and_results = $this->model->searchConceptsAndInfo($parameters);
350
        } catch (Exception $e) {
351
            header("HTTP/1.0 404 Not Found");
352
            if ($this->model->getConfig()->getLogCaughtExceptions()) {
353
                error_log('Caught exception: ' . $e->getMessage());
354
            }
355
            $this->invokeGenericErrorPage($request, $e->getMessage());
356
            return;
357
        }
358
        $counts = $count_and_results['count'];
359
        $search_results = $count_and_results['results'];
360
        $vocabList = $this->model->getVocabularyList();
361
        $sortedVocabs = $this->model->getVocabularyList(false, true);
362
        $langList = $this->model->getLanguages($lang);
363
364
        echo $template->render(
365
            array(
366
                'search_count' => $counts,
367
                'languages' => $this->languages,
368
                'search_results' => $search_results,
369
                'rest' => $parameters->getOffset()>0,
370
                'global_search' => true,
371
                'term' => $request->getQueryParam('q'),
372
                'lang_list' => $langList,
373
                'vocabs' => str_replace(' ', '+', $vocabs),
374
                'vocab_list' => $vocabList,
375
                'sorted_vocabs' => $sortedVocabs,
376
                'request' => $request,
377
                'parameters' => $parameters
378
            ));
379
    }
380
381
    /**
382
     * Invokes the search for a single vocabs concepts.
383
     */
384
    public function invokeVocabularySearch($request)
385
    {
386
        $template = $this->twig->loadTemplate('vocab-search-listing.twig');
387
        $this->setLanguageProperties($request->getLang());
388
        $vocab = $request->getVocab();
389
        try {
390
            $vocab_types = $this->model->getTypes($request->getVocabid(), $request->getLang());
391
        } catch (Exception $e) {
392
            header("HTTP/1.0 404 Not Found");
393
            if ($this->model->getConfig()->getLogCaughtExceptions()) {
394
                error_log('Caught exception: ' . $e->getMessage());
395
            }
396
397
            echo $template->render(
398
                array(
399
                    'languages' => $this->languages,
400
                ));
401
402
            return;
403
        }
404
405
        $langcodes = $vocab->getConfig()->getShowLangCodes();
406
        $parameters = new ConceptSearchParameters($request, $this->model->getConfig());
407
408
        try {
409
            $count_and_results = $this->model->searchConceptsAndInfo($parameters);
410
            $counts = $count_and_results['count'];
411
            $search_results = $count_and_results['results'];
412
        } catch (Exception $e) {
413
            header("HTTP/1.0 404 Not Found");
414
            if ($this->model->getConfig()->getLogCaughtExceptions()) {
415
                error_log('Caught exception: ' . $e->getMessage());
416
            }
417
418
            echo $template->render(
419
                array(
420
                    'languages' => $this->languages,
421
                    'vocab' => $vocab,
422
                    'term' => $request->getQueryParam('q'),
423
                ));
424
            return;
425
        }
426
        echo $template->render(
427
            array(
428
                'languages' => $this->languages,
429
                'vocab' => $vocab,
430
                'search_results' => $search_results,
431
                'search_count' => $counts,
432
                'rest' => $parameters->getOffset()>0,
433
                'limit_parent' => $parameters->getParentLimit(),
434
                'limit_type' =>  $request->getQueryParam('type') ? explode('+', $request->getQueryParam('type')) : null,
435
                'limit_group' => $parameters->getGroupLimit(),
436
                'limit_scheme' =>  $request->getQueryParam('scheme') ? explode('+', $request->getQueryParam('scheme')) : null,
437
                'group_index' => $vocab->listConceptGroups($request->getContentLang()),
438
                'parameters' => $parameters,
439
                'term' => $request->getQueryParam('q'),
440
                'types' => $vocab_types,
441
                'explicit_langcodes' => $langcodes,
442
                'request' => $request,
443
            ));
444
    }
445
446
    /**
447
     * Invokes the alphabetical listing for a specific vocabulary.
448
     */
449
    public function invokeAlphabeticalIndex($request)
450
    {
451
        $lang = $request->getLang();
452
        $this->setLanguageProperties($lang);
453
        $template = $this->twig->loadTemplate('alphabetical-index.twig');
454
        $vocab = $request->getVocab();
455
456
        $offset = ($request->getQueryParam('offset') && is_numeric($request->getQueryParam('offset')) && $request->getQueryParam('offset') >= 0) ? $request->getQueryParam('offset') : 0;
457
        if ($request->getQueryParam('limit')) {
458
            $count = $request->getQueryParam('limit');
459
        } else {
460
            $count = ($offset > 0) ? null : 250;
461
        }
462
463
        $content_lang = $request->getContentLang();
464
465
        $all_at_once = $vocab->getConfig()->getAlphabeticalFull();
466
        if (!$all_at_once) {
467
            $search_results = $vocab->searchConceptsAlphabetical($request->getLetter(), $count, $offset, $content_lang);
468
            $letters = $vocab->getAlphabet($content_lang);
469
        } else {
470
            $search_results = $vocab->searchConceptsAlphabetical('*', null, null, $content_lang);
471
            $letters = null;
472
        }
473
474
        $request->setContentLang($content_lang);
475
476
        echo $template->render(
477
            array(
478
                'languages' => $this->languages,
479
                'vocab' => $vocab,
480
                'alpha_results' => $search_results,
481
                'letters' => $letters,
482
                'all_letters' => $all_at_once,
483
                'request' => $request,
484
            ));
485
    }
486
487
    /**
488
     * Invokes the vocabulary group index page template.
489
     * @param boolean $stats set to true to get vocabulary statistics visible.
490
     */
491 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...
492
    {
493
        $lang = $request->getLang();
494
        $this->setLanguageProperties($lang);
495
        $template = $this->twig->loadTemplate('group-index.twig');
496
        $vocab = $request->getVocab();
497
498
        echo $template->render(
499
            array(
500
                'languages' => $this->languages,
501
                'stats' => $stats,
502
                'vocab' => $vocab,
503
                'request' => $request,
504
            ));
505
    }
506
507
    /**
508
     * Loads and renders the view containing a specific vocabulary.
509
     */
510
    public function invokeVocabularyHome($request)
511
    {
512
        $lang = $request->getLang();
513
        // set language parameters for gettext
514
        $this->setLanguageProperties($lang);
515
        $vocab = $request->getVocab();
516
517
        $defaultView = $vocab->getConfig()->getDefaultSidebarView();
518
        // load template
519
        if ($defaultView === 'groups') {
520
            $this->invokeGroupIndex($request, true);
521
            return;
522
        }
523
524
        $template = $this->twig->loadTemplate('vocab.twig');
525
526
        echo $template->render(
527
            array(
528
                'languages' => $this->languages,
529
                'vocab' => $vocab,
530
                'search_letter' => 'A',
531
                'active_tab' => $defaultView,
532
                'request' => $request,
533
            ));
534
    }
535
536
    /**
537
     * Invokes a very generic errorpage.
538
     */
539
    public function invokeGenericErrorPage($request, $message = null)
540
    {
541
        $this->setLanguageProperties($request->getLang());
542
        header("HTTP/1.0 404 Not Found");
543
        $template = $this->twig->loadTemplate('error-page.twig');
544
        echo $template->render(
545
            array(
546
                'languages' => $this->languages,
547
                'request' => $request,
548
                'message' => $message,
549
                'requested_page' => filter_input(INPUT_SERVER, 'REQUEST_URI', FILTER_SANITIZE_STRING),
550
            ));
551
    }
552
553
    /**
554
     * Loads and renders the view containing a list of recent changes in the vocabulary.
555
     * @param Request $request
556
     */
557
    public function invokeChangeList($request, $prop='dc:created')
558
    {
559
        // set language parameters for gettext
560
        $this->setLanguageProperties($request->getLang());
561
        $vocab = $request->getVocab();
562
        $offset = ($request->getQueryParam('offset') && is_numeric($request->getQueryParam('offset')) && $request->getQueryParam('offset') >= 0) ? $request->getQueryParam('offset') : 0;
563
        $changeList = $vocab->getChangeList($prop, $request->getContentLang(), $request->getLang(), $offset);
564
        // load template
565
        $template = $this->twig->loadTemplate('changes.twig');
566
567
        // render template
568
        echo $template->render(
569
            array(
570
                'vocab' => $vocab,
571
                'languages' => $this->languages,
572
                'request' => $request,
573
                'changeList' => $changeList)
574
            );
575
    }
576
577
}
578