summaryrefslogtreecommitdiffstats
path: root/library/Icinga/Web/Controller.php
blob: 008fbf6be18745c3ecea83db17166dd5a2f7a9d0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
<?php
/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */

namespace Icinga\Web;

use Icinga\Data\Filterable;
use Icinga\Data\Sortable;
use Icinga\Data\QueryInterface;
use Icinga\Exception\Http\HttpBadRequestException;
use Icinga\Exception\Http\HttpNotFoundException;
use Icinga\Web\Controller\ModuleActionController;
use Icinga\Web\Widget\Limiter;
use Icinga\Web\Widget\Paginator;
use Icinga\Web\Widget\SortBox;

/**
 * This is the controller all modules should inherit from
 * We will flip code with the ModuleActionController as soon as a couple
 * of pending feature branches are merged back to the master.
 *
 * @property View $view
 */
class Controller extends ModuleActionController
{
    /**
     * Cache for page size configured via user preferences
     *
     * @var false|int
     */
    protected $userPageSize;

    /**
     * @see ActionController::init
     */
    public function init()
    {
        parent::init();
        $this->handleSortControlSubmit();
    }

    /**
     * Check whether the sort control has been submitted and redirect using GET parameters
     */
    protected function handleSortControlSubmit()
    {
        $request = $this->getRequest();
        if (! $request->isPost()) {
            return;
        }

        if (($sort = $request->getPost('sort')) || ($direction = $request->getPost('dir'))) {
            $url = Url::fromRequest();
            if ($sort) {
                $url->setParam('sort', $sort);
                $url->remove('dir');
            } else {
                $url->setParam('dir', $direction);
            }

            $this->redirectNow($url);
        }
    }

    /**
     * Immediately respond w/ HTTP 400
     *
     * @param   string  $message    Exception message or exception format string
     * @param   mixed   ...$arg     Format string argument
     *
     * @throws  HttpBadRequestException
     */
    public function httpBadRequest($message)
    {
        throw HttpBadRequestException::create(func_get_args());
    }

    /**
     * Immediately respond w/ HTTP 404
     *
     * @param   string  $message    Exception message or exception format string
     * @param   mixed   ...$arg     Format string argument
     *
     * @throws  HttpNotFoundException
     */
    public function httpNotFound($message)
    {
        throw HttpNotFoundException::create(func_get_args());
    }

    /**
     * Render the given form using a simple view script
     *
     * @param   Form    $form
     * @param   string  $tab
     */
    public function renderForm(Form $form, $tab)
    {
        $this->getTabs()->add(uniqid(), array(
            'active'    => true,
            'label'     => $tab,
            'url'       => Url::fromRequest()
        ));
        $this->view->form = $form;
        $this->render('simple-form', null, true);
    }

    /**
     * Create a SortBox widget and apply its sort rules on the given query
     *
     * The widget is set on the `sortBox' view property only if the current view has not been requested as compact
     *
     * @param   array       $columns    An array containing the sort columns, with the
     *                                  submit value as the key and the label as the value
     * @param   Sortable    $query      Query to apply the user chosen sort rules on
     * @param   array       $defaults   An array containing default sort directions for specific columns
     *
     * @return  $this
     */
    protected function setupSortControl(array $columns, Sortable $query = null, array $defaults = null)
    {
        $request = $this->getRequest();
        $sortBox = SortBox::create('sortbox-' . $request->getActionName(), $columns, $defaults);
        $sortBox->setRequest($request);

        if ($query) {
            $sortBox->setQuery($query);
            $sortBox->handleRequest($request);
        }

        if (! $this->view->compact) {
            $this->view->sortBox = $sortBox;
        }

        return $this;
    }

    /**
     * Create a Limiter widget at the `limiter' view property
     *
     * In case the current view has been requested as compact this method does nothing.
     *
     * @param   int             $itemsPerPage   Default number of items per page
     *
     * @return  $this
     */
    protected function setupLimitControl($itemsPerPage = 25)
    {
        if (! $this->view->compact) {
            $this->view->limiter = new Limiter();
            $this->view->limiter->setDefaultLimit($this->getPageSize($itemsPerPage));
        }

        return $this;
    }

    /**
     * Get the page size configured via user preferences or return the default value
     *
     * @param   ?int $default
     *
     * @return  int
     */
    protected function getPageSize($default)
    {
        if ($this->userPageSize === null) {
            $user = $this->Auth()->getUser();
            if ($user !== null) {
                $pageSize = $user->getPreferences()->getValue('icingaweb', 'default_page_size');
                $this->userPageSize = $pageSize ? (int) $pageSize : false;
            } else {
                $this->userPageSize = false;
            }
        }

        return $this->userPageSize !== false ? $this->userPageSize : $default;
    }

    /**
     * Apply the given page limit and number on the given query and setup a paginator for it
     *
     * The $itemsPerPage and $pageNumber parameters are only applied if not available in the current request.
     * The paginator is set on the `paginator' view property only if the current view has not been requested as compact.
     *
     * @param   QueryInterface  $query          The query to create a paginator for
     * @param   int             $itemsPerPage   Default number of items per page
     * @param   int             $pageNumber     Default page number
     *
     * @return  $this
     */
    protected function setupPaginationControl(QueryInterface $query, $itemsPerPage = 25, $pageNumber = 0)
    {
        $request = $this->getRequest();
        $limit = $request->getParam('limit', $this->getPageSize($itemsPerPage));
        $page = $request->getParam('page', $pageNumber);
        $query->limit($limit, $page > 0 ? ($page - 1) * $limit : 0);

        if (! $this->view->compact) {
            $paginator = new Paginator();
            $paginator->setQuery($query);
            $this->view->paginator = $paginator;
        }

        return $this;
    }

    /**
     * Create a FilterEditor widget and apply the user's chosen filter options on the given filterable
     *
     * The widget is set on the `filterEditor' view property only if the current view has not been requested as compact.
     * The optional $filterColumns parameter should be an array of key-value pairs where the key is the name of the
     * column and the value the label to show to the user. The optional $searchColumns parameter should be an array
     * of column names to be used to handle quick searches.
     *
     * If the given filterable is an instance of Icinga\Data\FilterColumns, $filterable->getFilterColumns() and
     * $filterable->getSearchColumns() is called to provide the respective columns if $filterColumns or $searchColumns
     * is not given.
     *
     * @param   Filterable  $filterable         The filterable to create a filter editor for
     * @param   array       $filterColumns      The filter columns to offer to the user
     * @param   array       $searchColumns      The search columns to utilize for quick searches
     * @param   array       $preserveParams     The url parameters to preserve
     *
     * @return  $this
     *
     * @todo    Preserving and ignoring parameters should be configurable (another two method params? property magic?)
     */
    protected function setupFilterControl(
        Filterable $filterable,
        array $filterColumns = null,
        array $searchColumns = null,
        array $preserveParams = null
    ) {
        $defaultPreservedParams = array(
            'limit', // setupPaginationControl()
            'sort', // setupSortControl()
            'dir', // setupSortControl()
            'backend', // Framework
            'showCompact', // Framework
            '_dev' // Framework
        );

        $editor = Widget::create('filterEditor');
        /** @var \Icinga\Web\Widget\FilterEditor $editor */
        call_user_func_array(
            array($editor, 'preserveParams'),
            array_merge($defaultPreservedParams, $preserveParams ?: array())
        );

        $editor
            ->setQuery($filterable)
            ->ignoreParams('page') // setupPaginationControl()
            ->setColumns($filterColumns)
            ->setSearchColumns($searchColumns)
            ->handleRequest($this->getRequest());

        if ($this->view->compact) {
            $editor->setVisible(false);
        }

        $this->view->filterEditor = $editor;

        return $this;
    }
}