Skip to content

Commit

Permalink
NEW LinkFieldController to handle FormSchema
Browse files Browse the repository at this point in the history
  • Loading branch information
emteknetnz committed Oct 24, 2023
1 parent f0a3b25 commit a10ceb0
Show file tree
Hide file tree
Showing 4 changed files with 182 additions and 3 deletions.
1 change: 0 additions & 1 deletion _config.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,4 @@

// Avoid creating global variables
call_user_func(function () {

});
179 changes: 179 additions & 0 deletions src/Controllers/LinkFieldController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
<?php

namespace SilverStripe\LinkField\Controllers;

use SilverStripe\Admin\LeftAndMain;
use SilverStripe\Control\HTTPRequest;
use SilverStripe\Control\HTTPResponse;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\LinkField\Form\FormFactory;
use SilverStripe\Forms\Form;
use SilverStripe\LinkField\Models\Link;
use SilverStripe\LinkField\Type\Registry;
use SilverStripe\Security\SecurityToken;
use SilverStripe\ORM\ValidationResult;

/**
* this will hopefully get replaced by a more generic centralised FormSchemaController
*/
class LinkFieldController extends LeftAndMain
{
private static $url_segment = 'linkfield';

// copying ElementalAreaController
private static $url_handlers = [
// API access points with structured data
'POST api/saveForm/$ID' => '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/<linkID>
*
* 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;
}
}
3 changes: 2 additions & 1 deletion src/Extensions/LeftAndMain.php
Original file line number Diff line number Diff line change
Expand Up @@ -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}'),
];
}
}
2 changes: 1 addition & 1 deletion src/Form/FormFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}

Expand Down

0 comments on commit a10ceb0

Please sign in to comment.