Issues (24)

src/ModelAdminPlus.php (1 issue)

1
<?php
2
3
namespace ilateral\SilverStripe\ModelAdminPlus;
4
5
use SilverStripe\Forms\Form;
6
use SilverStripe\ORM\ArrayList;
7
use SilverStripe\Admin\ModelAdmin;
8
use SilverStripe\View\Requirements;
9
use Colymba\BulkManager\BulkManager;
10
use SilverStripe\Core\Config\Config;
11
use SilverStripe\Control\HTTPRequest;
12
use SilverStripe\Forms\GridField\GridField;
13
use Colymba\BulkManager\BulkAction\UnlinkHandler;
14
use SilverStripe\Forms\GridField\GridFieldConfig;
15
use SilverStripe\Forms\GridField\GridFieldButtonRow;
16
use SilverStripe\Forms\GridField\GridFieldPaginator;
17
use ilateral\SilverStripe\ModelAdminPlus\AutoCompleteField;
18
use Symbiote\GridFieldExtensions\GridFieldConfigurablePaginator;
19
use SilverStripe\Forms\GridField\GridFieldFilterHeader as SSGridFieldFilterHeader;
20
21
/**
22
 * Custom version of model admin that adds extra features
23
 * (such as submitting search results via a POST, saving the query
24
 * as a session and automatic Bulk Editing support)
25
 *
26
 * @author ilateral
27
 * @package ModelAdminPlus
28
 */
29
abstract class ModelAdminPlus extends ModelAdmin
30
{
31
    const EXPORT_FIELDS = "export_fields";
32
33
    const ACTION_SUGGEST = 'suggest';
34
35
    /**
36
     * Automatically convert date fields on gridfields
37
     * to use `Date.Nice`.
38
     *
39
     * @var boolean
40
     */
41
    private static $auto_convert_dates = true;
42
43
    /**
44
     * Automatically convert DB text fields to AutoComplete fields
45
     *
46
     * @var boolean
47
     */
48
    private static $convert_to_autocomplete = true;
49
50
    private static $allowed_actions = [
51
        "SearchForm",
52
        self::ACTION_SUGGEST
53
    ];
54
55
    /**
56
     * List of currently registered ModelAdminSnippets, that is represented as
57
     * a list of classnames.
58
     *
59
     * These snippets are then setup when ModelAdminPlus is initilised and
60
     * rendered into the ModelAdminPlus content template.
61
     * 
62
     * This list can also be a divided up by managed classnames, so that snippets will
63
     * only be loaded when accessing that class, EG:
64
     * 
65
     * $registered_snippets = [
66
     *      MyObject::class => [
67
     *          MySnippetOne::class,
68
     *          MySnippetTwo::class
69
     *      ]
70
     * ];
71
     *
72
     * @var array
73
     */
74
    private static $registered_snippets = [];
75
76
    /**
77
     * Setup snippets for current screen
78
     */
79
    public function getSnippets()
80
    {
81
        $snippets = ArrayList::create();
82
        $model_class = $this->getModelClass();
83
84
        // Setup any model admin plus snippets
85
        foreach ($this->config()->registered_snippets as $key => $value) {
86
            if (is_int($key)) {
87
                $snippet = $this->createSnippetObject($value);
88
                $snippets->add($snippet);
89
            }
90
91
            if (is_array($value) && $key == $model_class) {
92
                foreach ($value as $snippet_class) {
93
                    $snippet = $this->createSnippetObject($snippet_class);
94
                    $snippets->add($snippet);
95
                }
96
            }
97
        }
98
99
        $snippets = $snippets->sort("Order", "DESC");
100
101
        $this->extend("updateSnippets", $snippets);
102
103
        return $snippets;
104
    }
105
106
    protected function createSnippetObject(string $class): ModelAdminSnippet
107
    {
108
        $snippet = new $class('snippets-before');
109
        $snippet->setParent($this);
110
        return $snippet;
111
    }
112
113
    public function init()
114
    {
115
        parent::init();
116
117
        Requirements::add_i18n_javascript('silverstripe/cms: client/lang', false, true);
0 ignored issues
show
Deprecated Code introduced by
The function SilverStripe\View\Requir...::add_i18n_javascript() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

117
        /** @scrutinizer ignore-deprecated */ Requirements::add_i18n_javascript('silverstripe/cms: client/lang', false, true);
Loading history...
118
119
        $clear = $this->getRequest()->getVar("clear");
120
121
        if (isset($clear) && $clear == 1) {
122
            $this->clearSearchSession();
123
            // Remove clear flag
124
            return $this->redirect(
125
                $this->Link(
126
                    $this->sanitiseClassName($this->modelClass)
127
                )
128
            );
129
        }
130
    }
131
132
    /**
133
     * Get the default export fields for the current model.
134
     *
135
     * First this checks if there is an `export_fields` config variable set on
136
     * the model class, if not, it reverts to the default behaviour.
137
     *
138
     * @return array
139
     */
140
    public function getExportFields()
141
    {
142
        $export_fields = Config::inst()->get(
143
            $this->modelClass,
144
            self::EXPORT_FIELDS
145
        );
146
147
        if (isset($export_fields) && is_array($export_fields)) {
148
            $fields = $export_fields;
149
        } else {
150
            $fields = parent::getExportFields();
151
        }
152
153
        $this->extend("updateExportFields", $fields);
154
155
        return $fields;
156
    }
157
158
    /**
159
     * Get the name of the session to be useed by this model admin's search
160
     * form.
161
     *
162
     * @return string
163
     */
164
    public function getSearchSessionName()
165
    {
166
        $curr = $this->sanitiseClassName(self::class);
167
        $model = $this->sanitiseClassName($this->modelClass);
168
        return $curr . "." . $model;
169
    }
170
171
    /**
172
     * Empty the current search session
173
     *
174
     * @return Session
175
     */
176
    public function clearSearchSession()
177
    {
178
        $session = $this->getRequest()->getSession();
179
        return $session->clear($this->getSearchSessionName());
180
    }
181
182
    /**
183
     * Get the current search session
184
     *
185
     * @return Session
186
     */
187
    public function getSearchSession()
188
    {
189
        $session = $this->getRequest()->getSession();
190
        return $session->get($this->getSearchSessionName());
191
    }
192
193
    /**
194
     * Set some data to a search session. This needs to be an array of
195
     * data (like the data submitted by a form).
196
     *
197
     * @param array $data An array of data to store in the session
198
     *
199
     * @return self
200
     */
201
    public function setSearchSession($data)
202
    {
203
        $session = $this->getRequest()->getSession();
204
        return $session->set($this->getSearchSessionName(), $data);
205
    }
206
207
    /**
208
     * Get the current search results, combined with any saved
209
     * search results and resturn (as an array).
210
     *
211
     * @return array
212
     */
213
    public function getSearchData()
214
    {
215
        $data = $this->getSearchSession();
216
217
        if (!$data || $data && !is_array($data)) {
218
            $data = [];
219
        }
220
221
        return $data;
222
    }
223
224
    protected function getGridField(): GridField
225
    {
226
        $field = parent::getGridField();
227
228
        if ($this->config()->auto_convert_dates) {
229
            GridFieldDateFinder::create($field)->convertDateFields();
230
        }
231
232
        return $field;
233
    }
234
235
    protected function getGridFieldConfig(): GridFieldConfig
236
    {
237
        $config = parent::getGridFieldConfig();
238
239
        // Add bulk editing to gridfield
240
        $manager = new BulkManager();
241
        $manager->removeBulkAction(UnlinkHandler::class);
242
243
        $config
244
            ->addComponent(new GridFieldSnippetRow(), GridFieldButtonRow::class)
245
            ->removeComponentsByType(GridFieldPaginator::class)
246
            ->addComponent($manager)
247
            ->addComponent(new GridFieldConfigurablePaginator());
248
249
        // Switch to custom filter header
250
        if ($config->getComponentsByType(SSGridFieldFilterHeader::class)->exists()) {
251
            $config
252
            ->removeComponentsByType(SSGridFieldFilterHeader::class)
253
            ->addComponent(new GridFieldFilterHeader(
254
                false,
255
                function ($context) {
256
                    $this->extend('updateSearchContext', $context);
257
                },
258
                function ($form) {
259
                    $this->extend('updateSearchForm', $form);
260
                }
261
            ));
262
        }
263
264
        // Add custom snippets
265
        foreach ($this->getSnippets() as $snippet) {
266
            $config->addComponent($snippet);
267
        }
268
269
        return $config;
270
    }
271
272
    /**
273
     * Find and return the recommended suggestion for an autocomplete
274
     * field
275
     *
276
     * @param array $data Submitted form
277
     * @param Form  $form The current form
278
     *
279
     * @return HTTPResponse
280
     */
281
    public function suggest(HTTPRequest $request)
282
    {
283
        $name = $request->param('n');
284
        $grid = $this->getGridField();
285
286
        // Manually re-assign gridfield to edit form
287
        $form = $this->getEditForm();
288
        $grid->setForm($form);
289
290
        $config = $grid->getConfig();
291
        /** @var GridFieldFilterHeader */
292
        $search = $config->getComponentByType(GridFieldFilterHeader::class);
293
        $form = isset($search) ? $search->getSearchForm($grid) : null;
294
        
295
        /** @var AutoCompleteField */
296
        $field = isset($form) ? $form->Fields()->fieldByName($name) : null;
297
298
        if (isset($field)) {
299
            return $field->Suggest($request);
300
        }
301
302
        // the response body
303
        return json_encode([]);
304
    }
305
}
306