Completed
Push — master ( b4d648...ce2a19 )
by Craig
19:25 queued 10:39
created

UserController::indexAction()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 5
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
/*
4
 * This file is part of the Zikula package.
5
 *
6
 * Copyright Zikula Foundation - http://zikula.org/
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Zikula\SearchModule\Controller;
13
14
use ModUtil;
15
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
16
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
17
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
18
use Symfony\Component\HttpFoundation\RedirectResponse;
19
use Symfony\Component\HttpFoundation\Request;
20
use Symfony\Component\HttpFoundation\Response;
21
use Symfony\Component\HttpKernel\HttpKernelInterface;
22
use Symfony\Component\Routing\RouterInterface;
23
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
24
use Zikula\Core\Controller\AbstractController;
25
use Zikula\Core\Response\PlainResponse;
26
use Zikula\SearchModule\AbstractSearchable;
27
28
/**
29
 * User controllers for the search module
30
 */
31
class UserController extends AbstractController
32
{
33
    /**
34
     * Main user function
35
     *
36
     * @return RedirectResponse
37
     */
38
    public function mainAction()
39
    {
40
        return $this->indexAction();
41
    }
42
43
    /**
44
     * Main user function
45
     *
46
     * @return RedirectResponse
47
     */
48
    public function indexAction()
49
    {
50
        // Security check will be done in form()
51
        return new RedirectResponse($this->get('router')->generate('zikulasearchmodule_user_form', [], RouterInterface::ABSOLUTE_URL));
52
    }
53
54
    /**
55
     * @Route("")
56
     * @Method("GET")
57
     * @Template
58
     *
59
     * Generate complete search form
60
     *
61
     * Generate the whole search form, including the various plugins options.
62
     * It uses the Search API's getallplugins() function to find plugins.
63
     *
64
     * @param mixed[] $vars {
0 ignored issues
show
Bug introduced by
There is no parameter named $vars. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
65
     *      @type string $q           search query
66
     *      @type string $searchtype  type of search being requested
67
     *      @type string $searchorder order to sort the results in
68
     *      @type array  $active      array of search plugins to search (if empty all plugins are used)
69
     *      @type array  $modvar      array with extrainfo for search plugins
70
     *                      }
71
     *
72
     * @return Response symfony response object
73
     *
74
     * @throws AccessDeniedException Thrown if the user doesn't have read access to the module
75
     */
76
    public function formAction(Request $request)
77
    {
78
        // Security check
79
        if (!$this->hasPermission('ZikulaSearchModule::', '::', ACCESS_READ)) {
80
            throw new AccessDeniedException();
81
        }
82
83
        $getData = $request->query;
84
85
        $vars = [
86
            'q' => $getData->get('q', ''),
87
            'searchtype' => $getData->getAlpha('searchtype', 'AND'),
88
            'searchorder' => $getData->getAlpha('searchorder', 'newest'),
89
            'numlimit' => $this->getVar('itemsperpage', 25),
90
            'active' => $getData->get('active'),
91
            'modvar' => $getData->get('modvar', [])
92
        ];
93
94
        // set some defaults
95
        $setActiveDefaults = false;
96
        if (!is_array($vars['active'])) {
97
            $setActiveDefaults = true;
98
            $vars['active'] = [];
99
        }
100
101
        if (!empty($vars['q']) && !$request->request->get('no-result', false)) {
102
            return $this->forwardRequest($request, 'search', [], $vars);
103
        }
104
105
        $searchableModules = $this->get('zikula_search_module.internal.searchable_module_collector')->getAll();
106
        if (count($searchableModules) == 0) {
107
            return $this->render('@ZikulaSearchModule/User/noplugins.html.twig');
108
        }
109
110
        $pluginOptions = [];
111
        foreach ($searchableModules as $moduleName => $searchableInstance) {
112
            if ($setActiveDefaults) {
113
                $vars['active'][$moduleName] = 1;
114
            }
115
            if ($this->getVar('disable_' . $moduleName)) {
116
                continue;
117
            }
118
            if (!$this->hasPermission('ZikulaSearchModule::Item', $moduleName . '::', ACCESS_READ)) {
119
                continue;
120
            }
121
            $active = !isset($vars['active']) || (isset($vars['active'][$moduleName]) && ($vars['active'][$moduleName] == 1));
122
            $pluginOptions[$moduleName] = $searchableInstance->getOptions($active, $vars['modvar']);
123
        }
124
125
        $templateParameters = array_merge($vars, [
126
            'pluginOptions' => $pluginOptions,
127
            'q' => $vars['q'],
128
            'searchType' => $vars['searchtype'],
129
            'searchOrder' => $vars['searchorder']
130
        ]);
131
132
        return $templateParameters;
1 ignored issue
show
Bug Best Practice introduced by
The return type of return $templateParameters; (array) is incompatible with the return type documented by Zikula\SearchModule\Cont...rController::formAction of type Symfony\Component\HttpFoundation\Response.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
133
    }
134
135
    /**
136
     * @Route("/results/{page}", requirements={"page"="\d+"})
137
     *
138
     * Perform the search then show the results
139
     *
140
     * This function includes all the search plugins, then call every one passing
141
     * an array that contains the string to search for, the boolean operators.
142
     *
143
     * @return Response symfony response object templated
144
     *
145
     * @throws \InvalidArgumentException Thrown if no search query parameters were provided
146
     * @throws AccessDeniedException Thrown if the user doesn't have read access to the module
147
     */
148
    public function searchAction(Request $request, $page = -1)
149
    {
150
        // Security check
151
        if (!$this->hasPermission('ZikulaSearchModule::', '::', ACCESS_READ)) {
152
            throw new AccessDeniedException();
153
        }
154
155
        // get parameter from HTTP input
156
        $vars = [
157
            'q' => $request->request->get('q'),
158
            'searchtype' => $request->request->get('searchtype', 'AND'),
159
            'searchorder' => $request->request->get('searchorder', 'newest'),
160
            'numlimit' => $this->getVar('itemsperpage', 25),
161
162
            // firstPage is used to identify the very first result page
163
            // - and to disable calls to plugins on the following pages
164
            'firstPage' => $page < 1,
165
            'page' => $page < 1 ? 1 : $page,
166
167
            'active' => $request->request->get('active'),
168
            // contains all form data from the modules search plugins
169
            'modvar' => $request->request->get('modvar')
170
        ];
171
172
        // The modulename exists in this array as key, if the checkbox was filled
173 View Code Duplication
        if (!isset($vars['active']) || !is_array($vars['active']) || empty($vars['active'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
174
            $vars['active'] = [];
175
        }
176
177 View Code Duplication
        if (!isset($vars['modvar']) || !is_array($vars['modvar']) || empty($vars['modvar'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
178
            $vars['modvar'] = [];
179
        }
180
181
        if (empty($vars['q']) && $vars['firstPage']) {
182
            $request->getSession()->getFlashBag()->add('error', $this->__('Error! You did not enter any keywords to search for.'));
183
184
            return new RedirectResponse($this->get('router')->generate('zikulasearchmodule_user_form'));
185
        }
186
187
        $result = ModUtil::apiFunc('ZikulaSearchModule', 'user', 'search', $vars);
188
189
        if ($result['resultCount'] == 0) {
190
            $request->getSession()->getFlashBag()->add('error', "
191
{$this->__('No search results found. You can try the following:')}
192
<ul>
193
    <li>{$this->__('Check that you spelled all words correctly.')}</li>
194
    <li>{$this->__('Use different keywords.')}</li>
195
    <li>{$this->__('Use keywords that are more general.')}</li>
196
    <li>{$this->__('Use fewer words.')}</li>
197
</ul>"
198
            );
199
200
            return $this->forwardRequest($request, 'form', $vars, ['no-result' => true]);
201
        }
202
203
        // Get number of chars to display in search summaries
204
        $limitSummary = $this->getVar('limitsummary', 200);
205
206
        $templateParameters = array_merge($vars, [
207
            'resultCount' => $result['resultCount'],
208
            'results' => $result['sqlResult'],
209
            'limitSummary' => $limitSummary,
210
            'errors' => isset($result['errors']) ? $result['errors'] : []
211
        ]);
212
213
        // log the search if on first page
214
        if ($vars['firstPage']) {
215
            ModUtil::apiFunc('ZikulaSearchModule', 'user', 'log', $vars);
216
        }
217
218
        return $this->render('@ZikulaSearchModule/User/results.html.twig', $templateParameters);
219
    }
220
221
    /**
222
     * @Route("/recent-searches")
223
     * @Template
224
     *
225
     * Display a list of recent searches
226
     *
227
     * @return Response symfony response object
228
     *
229
     * @throws AccessDeniedException Thrown if the user doesn't have read access to the module or no user is logged in
230
     */
231
    public function recentAction(Request $request)
232
    {
233
        // security check
234 View Code Duplication
        if (!$this->hasPermission('ZikulaSearchModule::', '::', ACCESS_READ)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
235
            || !$this->get('zikula_users_module.current_user')->isLoggedIn()) {
236
            throw new AccessDeniedException();
237
        }
238
239
        // Get parameters from whatever input we need.
240
        $startnum = $request->query->filter('startnum', 1, false, FILTER_VALIDATE_INT);
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a integer.

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...
241
        $itemsPerPage = $this->getVar('itemsperpage');
242
243
        $items = ModUtil::apiFunc('ZikulaSearchModule', 'user', 'getall', [
244
            'startnum' => $startnum,
245
            'numitems' => $itemsPerPage,
246
            'sortorder' => 'date'
247
        ]);
248
249
        $templateParameters = [
250
            'recentSearches' => $items,
251
            'pager' => [
252
                'amountOfItems' => ModUtil::apiFunc('ZikulaSearchModule', 'user', 'countitems'),
253
                'itemsPerPage'  => $itemsPerPage
254
            ]
255
        ];
256
257
        return $templateParameters;
258
    }
259
260
    /**
261
     * @Route("/opensearch", options={"i18n"=false})
262
     *
263
     * Generate xml for opensearch syndication
264
     *
265
     * @return PlainResponse Thrown if the user doesn't have read access to the module
266
     */
267
    public function opensearchAction()
268
    {
269
        if (!$this->hasPermission('ZikulaSearchModule::', '::', ACCESS_READ)) {
270
            throw new AccessDeniedException();
271
        }
272
273
        $variableApi = $this->get('zikula_extensions_module.api.variable');
274
        $templateParameters = [
275
            'siteName' => $variableApi->getSystemVar('sitename', $variableApi->getSystemVar('sitename_en')),
276
            'slogan' => $variableApi->getSystemVar('slogan', $variableApi->getSystemVar('slogan_en')),
277
            'metaKeywords' => $variableApi->getSystemVar('metakeywords', $variableApi->getSystemVar('metakeywords_en')),
278
            'adminMail' => $variableApi->getSystemVar('adminmail'),
279
            'hasAdultContent' => $variableApi->get('ZikulaSearchModule', 'opensearch_adult_content', false)
280
        ];
281
282
        return new PlainResponse($this->renderView('@ZikulaSearchModule/User/opensearch.xml.twig', $templateParameters), Response::HTTP_OK, ['Content-Type' => 'text/xml']);
283
    }
284
285
    /**
286
     * Forwards the request to another action of this controller.
287
     *
288
     * @param Request $request
289
     * @param         $action  The action to forwards to
290
     * @param array   $get     Array of GET parameters
291
     * @param array   $post    Array of POST parameters
292
     *
293
     * @return mixed
294
     */
295
    private function forwardRequest(Request $request, $action, $get = [], $post = [])
296
    {
297
        $path = ['_controller' => 'ZikulaSearchModule:User:' . $action];
298
        $subRequest = $request->duplicate($get, $post, $path);
299
300
        return $this->get('http_kernel')->handle($subRequest, HttpKernelInterface::SUB_REQUEST);
301
    }
302
}
303