From a10ceb0529f02abcbd9315559ff036e256627777 Mon Sep 17 00:00:00 2001 From: Steve Boyd Date: Tue, 24 Oct 2023 18:45:37 +1300 Subject: [PATCH] NEW LinkFieldController to handle FormSchema --- _config.php | 1 - src/Controllers/LinkFieldController.php | 179 ++++++++++++++++++++++++ src/Extensions/LeftAndMain.php | 3 +- src/Form/FormFactory.php | 2 +- 4 files changed, 182 insertions(+), 3 deletions(-) create mode 100644 src/Controllers/LinkFieldController.php diff --git a/_config.php b/_config.php index 9e519d4a..71c7914c 100644 --- a/_config.php +++ b/_config.php @@ -5,5 +5,4 @@ // Avoid creating global variables call_user_func(function () { - }); diff --git a/src/Controllers/LinkFieldController.php b/src/Controllers/LinkFieldController.php new file mode 100644 index 00000000..345e6833 --- /dev/null +++ b/src/Controllers/LinkFieldController.php @@ -0,0 +1,179 @@ + 'apiSaveForm', + # '$FormName/field/$FieldName' => 'formAction', // do we need this? + ]; + + private static $allowed_actions = [ + 'apiSaveForm', + // 'linkForm' needs to defined for the benefit of LeftAndMain::schema() even though + // 'getLinkForm()' is the method that's ultimately called + // it's pretty unintuitive and could be made better, though it works for now + 'linkForm', + ]; + + /** + * TODO have temporarily disabled for easier dev + */ + public function init() + { + parent::init(); + SecurityToken::disable(); + } + + /** + * Routed to be LeftAndMain::schema() + * /admin/linkfield/schema/linkForm/ + * + * Adapted from ElementalAreaController::getElementForm() + * + * @param int $elementID -- TODO not sure if this is required or not + * @return Form + */ + public function getLinkForm(int $linkID): Form + { + $formFactory = FormFactory::create(); + + // this is a hackish way to get the correct DataObject type, I'm not sure what the clean method is + $obj = Link::get()->byID($linkID); + if ($obj && $obj->exists()) { + $className = $obj->ClassName; + $obj = $className::get()->byID($linkID); + } else { + $obj = Link::create(); + } + + $list = (new Registry)->list(); + array_walk($list, function (&$item, $key) { + $item = $item->ClassName; + }); + $typesToTypeKeys = array_flip($list); + $typesToTypeKeys[Link::class] = 'link'; + $className = $obj->ClassName; + $linkTypeKey = $typesToTypeKeys[$className]; + + // add these headers to make dev life easier (should be default anyway) + $this->getRequest()->addHeader('X-Formschema-Request', 'auto,schema,state,errors'); + + /** @var Form $form */ + $form = $formFactory->getForm($this, "LinkForm_$linkID", [ + 'LinkType' => $obj, + 'LinkTypeKey' => $linkTypeKey, + + // do we need this? + 'Record' => $obj, + + // the following is here for (silverstripe/admin) LinkFormFactory::getRequiredContext() + 'RequireLinkText' => false + ]); + $form->addExtraClass('link-editor-editform__form'); + + if (!$obj->canEdit()) { + $form->makeReadonly(); + } + + return $form; + } + + /** + * Code adapted from https://github.com/silverstripe/silverstripe-elemental/pull/1113/files#diff-942115e4b8f6030e4d72ebc2b3a0772ec65e5dfcd08fbd0e677c70d1231daf28R189 + * + * Save an inline edit form for a Link + */ + public function apiSaveForm(HTTPRequest $request): HTTPResponse + { + $id = $this->param('ID'); // $this->urlParams['ID'] + // Validate required input data + if (!$id) { + $this->jsonError(400); + } + + $data = $request->postVars(); + // $data = json_decode($request->getBody(), true); + if (empty($data)) { + $this->jsonError(400); + } + + // Check security token + if (!SecurityToken::inst()->checkRequest($request)) { + $this->jsonError(400); + } + + /** @var BaseElement $element */ + $link = Link::get()->byID($id); + // Ensure the element can be edited by the current user + if (!$link || !$link->canEdit()) { + $this->jsonError(403); + } + + // get form and populate it with POSTed data + $form = $this->getLinkForm($id); + $form->loadDataFrom($data); + + // validate the Form + $validationResult = $form->validationResult(); + + // validate the DataObject + $element->updateFromFormData($data); + $validationResult->combineAnd($element->validate()); + + // handle validation failure and sent json formschema as response + if (!$validationResult->isValid()) { + // add headers to the request here so you don't need to do it in the client + // in the future I'd like these be the default response from formschema if + // the header wasn't defined + $request->addHeader('X-Formschema-Request', 'auto,schema,state,errors'); + // generate schema response + $url = $this->getRequest()->getURL(); // admin/elemntal-area/api/saveForm/3 + $response = $this->getSchemaResponse($url, $form, $validationResult); + // returning a 400 means that FormBuilder.js::handleSubmit() submitFn() + // that will end up in the catch() .. throw error block and the error + // will just end up in the javascript console + // $response->setStatusCode(400); + // + // return a 200 for now just to get things to work even though it's + // clearly the wrong code. Will require a PR to admin to fix this + $response->setStatusCode(200); + return $response; + } + + $updated = false; + if ($element->isChanged()) { + $element->write(); + // Track changes so we can return to the client + $updated = true; + } + + // create and send success json response + $response = $this->getResponse(); + $response->setBody(json_encode([ + 'status' => 'success', + 'updated' => $updated, + ])); + $response->addHeader('Content-Type', 'application/json'); + return $response; + } +} diff --git a/src/Extensions/LeftAndMain.php b/src/Extensions/LeftAndMain.php index d1173a3d..5617d1fc 100644 --- a/src/Extensions/LeftAndMain.php +++ b/src/Extensions/LeftAndMain.php @@ -19,7 +19,8 @@ public function init() public function updateClientConfig(&$clientConfig) { $clientConfig['form']['DynamicLink'] = [ - 'schemaUrl' => $this->getOwner()->Link('methodSchema/Modals/DynamicLink'), + // 'schemaUrl' => $this->getOwner()->Link('methodSchema/Modals/DynamicLink'), + 'schemaUrl' => $this->getOwner()->Link('linkfield/schema/linkForm/{id}'), ]; } } diff --git a/src/Form/FormFactory.php b/src/Form/FormFactory.php index e9f57f5c..6107cbb7 100644 --- a/src/Form/FormFactory.php +++ b/src/Form/FormFactory.php @@ -18,7 +18,7 @@ protected function getFormFields($controller, $name, $context) /** @var Type $type */ $type = $context['LinkType']; - if (!$type instanceof Type) { + if (!is_a($type, Type::class)) { throw new LogicException(sprintf('%s: LinkType must be provided and must be an instance of Type', static::class)); }