Completed
Push — master ( cd60cf...f7a4c7 )
by
unknown
22s
created

src/Controllers/ElementalAreaController.php (2 issues)

1
<?php
2
3
namespace DNADesign\Elemental\Controllers;
4
5
use DNADesign\Elemental\Forms\EditFormFactory;
6
use DNADesign\Elemental\Models\BaseElement;
7
use Exception;
8
use Psr\Log\LoggerInterface;
9
use SilverStripe\Admin\LeftAndMain;
10
use SilverStripe\Control\HTTPRequest;
11
use SilverStripe\Control\HTTPResponse;
12
use SilverStripe\Control\HTTPResponse_Exception;
13
use SilverStripe\Core\Convert;
14
use SilverStripe\Core\Injector\Injector;
15
use SilverStripe\Forms\Form;
16
use SilverStripe\Security\SecurityToken;
17
18
/**
19
 * Controller for "ElementalArea" - handles loading and saving of in-line edit forms in an elemental area in admin
20
 */
21
class ElementalAreaController extends LeftAndMain
22
{
23
    const FORM_NAME_TEMPLATE = 'ElementForm_%s';
24
25
    private static $url_segment = 'elemental-area';
26
27
    private static $ignore_menuitem = true;
28
29
    private static $url_handlers = [
30
        // API access points with structured data
31
        'POST api/saveForm/$ID' => 'apiSaveForm',
32
    ];
33
34
    private static $allowed_actions = [
35
        'elementForm',
36
        'schema',
37
        'apiSaveForm',
38
    ];
39
40
    public function getClientConfig()
41
    {
42
        $clientConfig = parent::getClientConfig();
43
        $clientConfig['form']['elementForm'] = [
44
            'schemaUrl' => $this->Link('schema/elementForm'),
45
            'saveUrl' => $this->Link('api/saveForm'),
46
            'saveMethod' => 'post',
47
            'payloadFormat' => 'json',
48
            'formNameTemplate' => sprintf(static::FORM_NAME_TEMPLATE, '{id}'),
49
        ];
50
        return $clientConfig;
51
    }
52
53
    /**
54
     * @param HTTPRequest|null $request
55
     * @return Form
56
     * @throws HTTPResponse_Exception
57
     */
58
    public function elementForm(HTTPRequest $request = null)
59
    {
60
        // Get ID either from posted back value, or url parameter
61
        if (!$request) {
62
            $this->jsonError(400);
63
            return null;
64
        }
65
        $id = $request->param('ID');
66
        if (!$id) {
67
            $this->jsonError(400);
68
            return null;
69
        }
70
        return $this->getElementForm($id) ?: $this->jsonError(404);
0 ignored issues
show
$id of type string is incompatible with the type integer expected by parameter $elementID of DNADesign\Elemental\Cont...oller::getElementForm(). ( Ignorable by Annotation )

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

70
        return $this->getElementForm(/** @scrutinizer ignore-type */ $id) ?: $this->jsonError(404);
Loading history...
71
    }
72
73
    /**
74
     * @param int $elementID
75
     * @return Form|null Returns null if no element exists for the given ID
76
     */
77
    public function getElementForm($elementID)
78
    {
79
        $scaffolder = Injector::inst()->get(EditFormFactory::class);
80
        $element = BaseElement::get()->byID($elementID);
81
82
        if (!$element) {
83
            return null;
84
        }
85
86
        /** @var Form $form */
87
        $form = $scaffolder->getForm(
88
            $this,
89
            sprintf(static::FORM_NAME_TEMPLATE, $elementID),
90
            ['Record' => $element]
91
        );
92
93
        return $form;
94
    }
95
96
    /**
97
     * Save an inline edit form for a block
98
     *
99
     * @param HTTPRequest $request
100
     * @return HTTPResponse|null JSON encoded string or null if an exception is thrown
101
     * @throws HTTPResponse_Exception
102
     */
103
    public function apiSaveForm(HTTPRequest $request)
104
    {
105
        // Validate required input data
106
        if (!isset($this->urlParams['ID'])) {
107
            $this->jsonError(400);
108
            return null;
109
        }
110
111
        $data = Convert::json2array($request->getBody());
112
        if (empty($data)) {
113
            $this->jsonError(400);
114
            return null;
115
        }
116
117
        // Inject request body as request vars
118
        foreach ($data as $key => $value) {
119
            $request->offsetSet($key, $value);
120
        }
121
122
        // Check security token
123
        if (!SecurityToken::inst()->checkRequest($request)) {
124
            $this->jsonError(400);
125
            return null;
126
        }
127
128
        /** @var BaseElement $element */
129
        $element = BaseElement::get()->byID($this->urlParams['ID']);
130
        // Ensure the element can be edited by the current user
131
        if (!$element || !$element->canEdit()) {
132
            $this->jsonError(403);
133
            return null;
134
        }
135
136
        // Remove the pseudo namespaces that were added by the form factory
137
        $data = $this->removeNamespacesFromFields($data, $element->ID);
0 ignored issues
show
It seems like $data can also be of type boolean; however, parameter $data of DNADesign\Elemental\Cont...eNamespacesFromFields() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

137
        $data = $this->removeNamespacesFromFields(/** @scrutinizer ignore-type */ $data, $element->ID);
Loading history...
138
139
        try {
140
            $updated = false;
141
            $element->update($data);
142
            // Check if anything will actually be changed before writing
143
            if ($element->isChanged()) {
144
                $element->write();
145
                // Track changes so we can return to the client
146
                $updated = true;
147
            }
148
        } catch (Exception $ex) {
149
            Injector::inst()->get(LoggerInterface::class)->debug($ex->getMessage());
150
151
            $this->jsonError(500);
152
            return null;
153
        }
154
155
        $body = Convert::raw2json([
156
            'status' => 'success',
157
            'updated' => $updated,
158
        ]);
159
        return HTTPResponse::create($body)->addHeader('Content-Type', 'application/json');
160
    }
161
162
    /**
163
     * Remove the pseudo namespaces that were added to form fields by the form factory
164
     *
165
     * @param array $data
166
     * @param int $elementID
167
     * @return array
168
     */
169
    protected function removeNamespacesFromFields(array $data, $elementID)
170
    {
171
        $output = [];
172
        $template = sprintf(EditFormFactory::FIELD_NAMESPACE_TEMPLATE, $elementID, '');
173
        foreach ($data as $key => $value) {
174
            // Only look at fields that match the namespace template
175
            if (substr($key, 0, strlen($template)) !== $template) {
176
                continue;
177
            }
178
179
            $fieldName = substr($key, strlen($template));
180
            $output[$fieldName] = $value;
181
        }
182
        return $output;
183
    }
184
}
185