Completed
Push — master ( 0abfbf...a9a357 )
by Tim
02:14
created

AbstractController::callActionMethod()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 16
rs 9.7333
c 0
b 0
f 0
cc 4
nc 4
nop 0
1
<?php
2
3
/**
4
 * Abstract controller.
5
 */
6
declare(strict_types=1);
7
8
namespace HDNET\Calendarize\Controller;
9
10
use HDNET\Calendarize\Domain\Repository\IndexRepository;
11
use HDNET\Calendarize\Event\GenericActionAssignmentEvent;
12
use HDNET\Calendarize\Event\GenericActionRedirectEvent;
13
use HDNET\Calendarize\Property\TypeConverter\AbstractBookingRequest;
14
use HDNET\Calendarize\Service\PluginConfigurationService;
15
use HDNET\Calendarize\Utility\DateTimeUtility;
16
use Psr\Http\Message\ResponseInterface;
17
use TYPO3\CMS\Core\Messaging\FlashMessage;
18
use TYPO3\CMS\Core\Utility\GeneralUtility;
19
use TYPO3\CMS\Core\Utility\HttpUtility;
20
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
21
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
22
use TYPO3\CMS\Extbase\Security\Cryptography\HashService;
23
use TYPO3\CMS\Frontend\Controller\ErrorController;
24
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
25
use TYPO3\CMS\Frontend\Page\PageAccessFailureReasons;
26
27
/**
28
 * Abstract controller.
29
 */
30
abstract class AbstractController extends ActionController
31
{
32
    /**
33
     * The index repository.
34
     *
35
     * @var \HDNET\Calendarize\Domain\Repository\IndexRepository
36
     */
37
    protected $indexRepository;
38
39
    /**
40
     * @var PluginConfigurationService
41
     */
42
    protected $pluginConfigurationService;
43
44
    /**
45
     * The feed formats and content types.
46
     *
47
     * @var array
48
     */
49
    protected $feedFormats = [
50
        'ics' => 'text/calendar',
51
        'xml' => 'application/xml',
52
        'atom' => 'application/rss+xml',
53
    ];
54
55
    /**
56
     * Inject plugin configuration service.
57
     *
58
     * @param PluginConfigurationService $pluginConfigurationService
59
     */
60
    public function injectPluginConfigurationService(PluginConfigurationService $pluginConfigurationService)
61
    {
62
        $this->pluginConfigurationService = $pluginConfigurationService;
63
    }
64
65
    /**
66
     * Inject index repository.
67
     *
68
     * @param \HDNET\Calendarize\Domain\Repository\IndexRepository $indexRepository
69
     */
70
    public function injectIndexRepository(IndexRepository $indexRepository)
71
    {
72
        $this->indexRepository = $indexRepository;
73
    }
74
75
    /**
76
     * Inject the configuration manager.
77
     *
78
     * @param ConfigurationManagerInterface $configurationManager
79
     */
80
    public function injectConfigurationManager(ConfigurationManagerInterface $configurationManager)
81
    {
82
        $this->configurationManager = $configurationManager;
83
        $this->settings = $this->configurationManager->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_SETTINGS);
84
        $this->settings = $this->pluginConfigurationService->respectPluginConfiguration((array)$this->settings);
85
    }
86
87
    /**
88
     * Init all actions.
89
     */
90
    public function initializeAction()
91
    {
92
        parent::initializeAction();
93
        AbstractBookingRequest::setConfigurations(GeneralUtility::trimExplode(',', $this->settings['configuration']));
94
    }
95
96
    /**
97
     * Calls the specified action method and passes the arguments.
98
     *
99
     * If the action returns a string, it is appended to the content in the
100
     * response object. If the action doesn't return anything and a valid
101
     * view exists, the view is rendered automatically.
102
     *
103
     * @api
104
     */
105
    protected function callActionMethod()
106
    {
107
        parent::callActionMethod();
108
        if (isset($this->feedFormats[$this->request->getFormat()])) {
109
            $this->sendHeaderAndFilename($this->feedFormats[$this->request->getFormat()], $this->request->getFormat());
110
            if ($this->request->hasArgument('hmac')) {
111
                $hmac = $this->request->getArgument('hmac');
112
                if ($this->validatePluginHmac($hmac)) {
0 ignored issues
show
Bug introduced by
It seems like $hmac defined by $this->request->getArgument('hmac') on line 111 can also be of type array; however, HDNET\Calendarize\Contro...r::validatePluginHmac() does only seem to accept string, 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...
113
                    $this->sendHeaderAndFilename($this->feedFormats[$this->request->getFormat()], $this->request->getFormat());
114
                }
115
116
                return;
117
            }
118
            $this->sendHeaderAndFilename($this->feedFormats[$this->request->getFormat()], $this->request->getFormat());
119
        }
120
    }
121
122
    /**
123
     * Send the content type header and the right file extension in front of the content.
124
     *
125
     * @param $contentType
126
     * @param $fileExtension
127
     */
128
    protected function sendHeaderAndFilename($contentType, $fileExtension)
129
    {
130
        $testMode = (bool)$this->settings['feed']['debugMode'];
131
        if ($testMode) {
132
            header('Content-Type: text/plain; charset=utf-8');
133
        } else {
134
            header('Content-Type: ' . $contentType . '; charset=utf-8');
135
            header('Content-Disposition: inline; filename=calendar.' . $fileExtension);
136
        }
137
        switch ($this->request->getFormat()) {
138
            case 'ics':
139
                // Use CRLF, see https://tools.ietf.org/html/rfc5545#section-3.1
140
                echo str_replace("\n", "\r\n", $this->response->getContent());
141
                break;
142
            default:
143
                echo $this->response->getContent();
144
                break;
145
        }
146
        HttpUtility::setResponseCodeAndExit(HttpUtility::HTTP_STATUS_200);
147
    }
148
149
    /**
150
     * Extend the variables by the event and name and assign the variable to the view.
151
     */
152
    protected function eventExtendedAssignMultiple(array $variables, string $className, string $eventName)
153
    {
154
        // use this variable in your extension to add more custom variables
155
        $variables['extended'] = [];
156
        $variables['extended']['pluginHmac'] = $this->calculatePluginHmac();
157
        $variables['settings'] = $this->settings;
158
        $variables['contentObject'] = $this->configurationManager->getContentObject()->data;
159
160
        $event = new GenericActionAssignmentEvent($variables, $className, $eventName);
161
        $this->eventDispatcher->dispatch($event);
0 ignored issues
show
Documentation introduced by
$event is of type object<HDNET\Calendarize...cActionAssignmentEvent>, but the function expects a object<Psr\EventDispatcher\object>.

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...
162
163
        $this->view->assignMultiple($event->getVariables());
164
    }
165
166
    /**
167
     * A redirect that have a event included.
168
     */
169
    protected function eventExtendedRedirect(string $className, string $eventName, array $variables = [])
170
    {
171
        // set default variables for the redirect
172
        if (empty($variables)) {
173
            $variables['extended'] = [
174
                'actionName' => 'list',
175
                'controllerName' => null,
176
                'extensionName' => null,
177
                'arguments' => [],
178
                'pageUid' => $this->settings['listPid'],
179
                'delay' => 0,
180
                'statusCode' => 301,
181
            ];
182
            $variables['extended']['pluginHmac'] = $this->calculatePluginHmac();
183
            $variables['settings'] = $this->settings;
184
        }
185
186
        $event = new GenericActionRedirectEvent($variables, $className, $eventName);
187
        $this->eventDispatcher->dispatch($event);
0 ignored issues
show
Documentation introduced by
$event is of type object<HDNET\Calendarize...ricActionRedirectEvent>, but the function expects a object<Psr\EventDispatcher\object>.

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...
188
        $variables = $event->getVariables();
189
190
        $this->redirect(
191
            $variables['extended']['actionName'],
192
            $variables['extended']['controllerName'],
193
            $variables['extended']['extensionName'],
194
            $variables['extended']['arguments'],
195
            $variables['extended']['pageUid'],
196
            $variables['extended']['delay'],
197
            $variables['extended']['statusCode']
198
        );
199
    }
200
201
    /**
202
     * Return the controllerName, pluginName and actionName.
203
     *
204
     * @return string
205
     */
206
    protected function getStringForPluginHmac(): string
207
    {
208
        $actionMethodName = ucfirst($this->request->getControllerActionName());
209
        $pluginName = $this->request->getPluginName();
210
        $controllerName = $this->request->getControllerName();
211
212
        return $controllerName . $pluginName . $actionMethodName;
213
    }
214
215
    /**
216
     * Calculate the plugin Hmac.
217
     *
218
     * @return string $hmac
219
     *
220
     * @see \TYPO3\CMS\Extbase\Security\Cryptography\HashService::generateHmac()
221
     */
222
    protected function calculatePluginHmac()
223
    {
224
        $string = $this->getStringForPluginHmac();
225
226
        $hashService = GeneralUtility::makeInstance(HashService::class);
227
228
        return $hashService->generateHmac($string);
229
    }
230
231
    /**
232
     * \TYPO3\CMS\Extbase\Security\Cryptography\HashService::validateHmac().
233
     *
234
     * @param string $hmac
235
     *
236
     * @return bool
237
     */
238
    protected function validatePluginHmac(string $hmac): bool
239
    {
240
        $string = $this->getStringForPluginHmac();
241
242
        /** @var HashService $hashService */
243
        $hashService = GeneralUtility::makeInstance(HashService::class);
244
245
        return $hashService->validateHmac($string, $hmac);
246
    }
247
248
    /**
249
     * Check if the static template is included.
250
     */
251
    protected function checkStaticTemplateIsIncluded()
252
    {
253
        if (!isset($this->settings['dateFormat'])) {
254
            $this->addFlashMessage(
255
                'Basic configuration settings are missing. It seems, that the Static Extension TypoScript is not loaded to your TypoScript configuration. Please add the calendarize TS to your TS settings.',
256
                'Configuration Error',
257
                FlashMessage::ERROR
258
            );
259
        }
260
    }
261
262
    /**
263
     * Change the page title.
264
     *
265
     * @param string $title
266
     */
267
    protected function changePageTitle($title)
268
    {
269
        /** @var TypoScriptFrontendController $frontendController */
270
        $frontendController = $GLOBALS['TSFE'];
271
        $frontendController->page['title'] = $title;
272
        $frontendController->indexedDocTitle = $title;
273
    }
274
275
    /**
276
     * Add cache tags.
277
     *
278
     * @param array $tags
279
     */
280
    protected function addCacheTags(array $tags)
281
    {
282
        if ($GLOBALS['TSFE'] instanceof TypoScriptFrontendController) {
283
            $GLOBALS['TSFE']->addCacheTags($tags);
284
        }
285
    }
286
287
    protected function isDateOutOfTypoScriptConfiguration(\DateTime $dateTime): bool
288
    {
289
        $prev = DateTimeUtility::normalizeDateTimeSingle($this->settings['dateLimitBrowserPrev']);
290
        $next = DateTimeUtility::normalizeDateTimeSingle($this->settings['dateLimitBrowserNext']);
291
292
        return $prev > $dateTime || $next < $dateTime;
293
    }
294
295
    protected function return404Page(): ResponseInterface
296
    {
297
        return GeneralUtility::makeInstance(ErrorController::class)->pageNotFoundAction(
298
            $GLOBALS['TYPO3_REQUEST'],
299
            'The requested page does not exist',
300
            ['code' => PageAccessFailureReasons::PAGE_NOT_FOUND]
301
        );
302
    }
303
}
304