diff --git a/code/FormSchemaController.php b/code/FormSchemaController.php new file mode 100644 index 000000000..4458a28ba --- /dev/null +++ b/code/FormSchemaController.php @@ -0,0 +1,156 @@ + 'schema', + ]; + + private static array $dependencies = [ + 'FormSchema' => '%$' . FormSchema::class, + ]; + + /** + * Current form schema helper + */ + private ?FormSchema $schema = null; + + protected function init() + { + $this->beforeExtending('onInit', function () { + $this->prepareTinyMce(); + }); + parent::init(); + } + + /** + * Get the form schema helper for this controller + */ + public function getFormSchema(): FormSchema + { + if (!$this->schema) { + $this->schema = FormSchema::singleton(); + } + return $this->schema; + } + + /** + * Set the form schema helper for this controller + */ + public function setFormSchema(FormSchema $schema): static + { + $this->schema = $schema; + return $this; + } + + /** + * Gets a JSON schema representing the current edit form. + */ + public function schema(HTTPRequest $request): HTTPResponse + { + $formName = $request->param('FormName'); + $itemID = $request->param('ItemID'); + + if (!$formName) { + $this->jsonError(400, 'Missing request params'); + } + + // Most usages of this method (e.g. AssetAdmin) have two methods - one method is + // named the same as the form, and does not accept a parameter - the other prefixed + // with "get" and accepts an $itemID parameter. + // Some usages exclude the getter method, so we need to check for both. + $formMethod = "get{$formName}"; + if (!$this->hasMethod($formMethod)) { + $formMethod = $formName; + if (!$this->hasMethod($formMethod)) { + $this->jsonError(404, 'Form not found'); + } + } + + if (!$this->hasAction($formName)) { + $this->jsonError(401, 'Form not accessible'); + } + + if ($itemID) { + $form = $this->{$formMethod}($itemID); + } else { + $form = $this->{$formMethod}(); + } + $schemaID = $request->getURL(); + return $this->getSchemaResponse($schemaID, $form); + } + + /** + * Check if the current request has a X-Formschema-Request header set. + * Used by conditional logic that responds to validation results + */ + protected function getSchemaRequested(): bool + { + $parts = $this->getRequest()->getHeader(static::SCHEMA_HEADER); + return !empty($parts); + } + + /** + * Generate schema for the given form based on the X-Formschema-Request header value + * + * @param string $schemaID ID for this schema. Required. + * @param Form|null $form Required for 'state' or 'schema' response + * @param ValidationResult $errors Required for 'error' response + * @param array $extraData Any extra data to be merged with the schema response + */ + protected function getSchemaResponse(string $schemaID, ?Form $form = null, ValidationResult $errors = null, array $extraData = []): HTTPResponse + { + $parts = $this->getRequest()->getHeader(static::SCHEMA_HEADER); + $data = $this + ->getFormSchema() + ->getMultipartSchema($parts, $schemaID, $form, $errors); + + if ($extraData) { + $data = array_merge($data, $extraData); + } + + $response = new HTTPResponse(json_encode($data)); + $response->addHeader('Content-Type', 'application/json'); + return $response; + } + + private function prepareTinyMce(): void + { + // Set the members html editor config + if (Security::getCurrentUser()) { + HTMLEditorConfig::set_active_identifier(Security::getCurrentUser()->getHtmlEditorConfigForCMS()); + } + + // Set default values in the config if missing. These things can't be defined in the config + // file because insufficient information exists when that is being processed + $htmlEditorConfig = HTMLEditorConfig::get_active(); + $htmlEditorConfig->setOption('language', TinyMCEConfig::get_tinymce_lang()); + $langUrl = TinyMCEConfig::get_tinymce_lang_url(); + if ($langUrl) { + $htmlEditorConfig->setOption('language_url', $langUrl); + } + } +} diff --git a/code/LeftAndMain.php b/code/LeftAndMain.php index e6193ee8c..0b21d5183 100644 --- a/code/LeftAndMain.php +++ b/code/LeftAndMain.php @@ -19,18 +19,14 @@ use SilverStripe\Core\Injector\Injector; use SilverStripe\Core\Manifest\ModuleResourceLoader; use SilverStripe\Core\Manifest\VersionProvider; -use SilverStripe\Dev\Deprecation; use SilverStripe\Dev\TestOnly; use SilverStripe\Forms\DropdownField; use SilverStripe\Forms\FieldList; use SilverStripe\Forms\Form; use SilverStripe\Forms\FormAction; use SilverStripe\Forms\HiddenField; -use SilverStripe\Forms\HTMLEditor\HTMLEditorConfig; -use SilverStripe\Forms\HTMLEditor\TinyMCEConfig; use SilverStripe\Forms\LiteralField; use SilverStripe\Forms\PrintableTransformation; -use SilverStripe\Forms\Schema\FormSchema; use SilverStripe\i18n\i18n; use SilverStripe\Model\List\ArrayList; use SilverStripe\ORM\CMSPreviewable; @@ -57,14 +53,8 @@ * This is essentially an abstract class which should be subclassed. * See {@link CMSMain} for a good example. */ -class LeftAndMain extends AdminController implements PermissionProvider +class LeftAndMain extends FormSchemaController implements PermissionProvider { - - /** - * Form schema header identifier - */ - const SCHEMA_HEADER = 'X-Formschema-Request'; - /** * Enable front-end debugging (increases verbosity) in dev mode. * Will be ignored in live environments. @@ -104,7 +94,6 @@ class LeftAndMain extends AdminController implements PermissionProvider * * @config * @var string - * @deprecated 2.4.0 Will be renamed to model_class */ private static $model_class = null; @@ -116,32 +105,16 @@ class LeftAndMain extends AdminController implements PermissionProvider 'save', 'printable', 'show', - 'Modals', 'EditForm', 'AddForm', 'batchactions', 'BatchActionsForm', - 'schema', - 'methodSchema', - ]; - - private static $url_handlers = [ - 'GET schema/$FormName/$ItemID/$OtherItemID' => 'schema', - 'GET methodSchema/$Method/$FormName/$ItemID' => 'methodSchema', ]; private static $dependencies = [ - 'FormSchema' => '%$' . FormSchema::class, 'VersionProvider' => '%$' . VersionProvider::class, ]; - /** - * Current form schema helper - * - * @var FormSchema - */ - protected $schema = null; - /** * Current pageID for this request * @@ -336,142 +309,19 @@ public function getClientConfig(): array { // Add WYSIWYG link form schema before extensions are applied $this->beforeExtending('updateClientConfig', function (array &$clientConfig): void { + $modalController = ModalController::singleton(); $clientConfig['form'] = [ 'EditorExternalLink' => [ - 'schemaUrl' => $this->Link('methodSchema/Modals/EditorExternalLink'), + 'schemaUrl' => $modalController->Link('schema/EditorExternalLink'), ], 'EditorEmailLink' => [ - 'schemaUrl' => $this->Link('methodSchema/Modals/EditorEmailLink'), + 'schemaUrl' => $modalController->Link('schema/EditorEmailLink'), ], ]; }); return parent::getClientConfig(); } - /** - * Get form schema helper - * - * @return FormSchema - */ - public function getFormSchema() - { - return $this->schema; - } - - /** - * Set form schema helper for this controller - * - * @param FormSchema $schema - * @return $this - */ - public function setFormSchema(FormSchema $schema) - { - $this->schema = $schema; - return $this; - } - - /** - * Gets a JSON schema representing the current edit form. - */ - public function schema(HTTPRequest $request): HTTPResponse - { - $formName = $request->param('FormName'); - $itemID = $request->param('ItemID'); - - if (!$formName) { - $this->jsonError(400, 'Missing request params'); - } - - $formMethod = "get{$formName}"; - if (!$this->hasMethod($formMethod)) { - $this->jsonError(404, 'Form not found'); - } - - if (!$this->hasAction($formName)) { - $this->jsonError(401, 'Form not accessible'); - } - - if ($itemID) { - $form = $this->{$formMethod}($itemID); - } else { - $form = $this->{$formMethod}(); - } - $schemaID = $request->getURL(); - return $this->getSchemaResponse($schemaID, $form); - } - - /** - * Get the form schema from a given method. - * The method must return a Form. - */ - public function methodSchema(HTTPRequest $request): HTTPResponse - { - Deprecation::noticeWithNoReplacment('2.4.0', 'Will be replaced with SilverStripe\Admin\FormSchemaController::schema()'); - $method = $request->param('Method'); - $formName = $request->param('FormName'); - $itemID = $request->param('ItemID'); - - if (!$formName || !$method) { - $this->jsonError(400, 'Missing request params'); - } - - if (!$this->hasMethod($method)) { - $this->jsonError(404, 'Method not found'); - } - if (!$this->hasAction($method)) { - $this->jsonError(401, 'Method not accessible'); - } - - $methodItem = $this->{$method}(); - if (!$methodItem->hasMethod($formName)) { - $this->jsonError(404, 'Form not found'); - } - if (!$methodItem->hasAction($formName)) { - $this->jsonError(401, 'Form not accessible'); - } - - $form = $methodItem->{$formName}($itemID); - $schemaID = $request->getURL(); - - return $this->getSchemaResponse($schemaID, $form); - } - - /** - * Check if the current request has a X-Formschema-Request header set. - * Used by conditional logic that responds to validation results - * - * @return bool - */ - protected function getSchemaRequested() - { - $parts = $this->getRequest()->getHeader(static::SCHEMA_HEADER); - return !empty($parts); - } - - /** - * Generate schema for the given form based on the X-Formschema-Request header value - * - * @param string $schemaID ID for this schema. Required. - * @param Form $form Required for 'state' or 'schema' response - * @param ValidationResult $errors Required for 'error' response - * @param array $extraData Any extra data to be merged with the schema response - */ - protected function getSchemaResponse($schemaID, $form = null, ValidationResult $errors = null, $extraData = []): HTTPResponse - { - $parts = $this->getRequest()->getHeader(static::SCHEMA_HEADER); - $data = $this - ->getFormSchema() - ->getMultipartSchema($parts, $schemaID, $form, $errors); - - if ($extraData) { - $data = array_merge($data, $extraData); - } - - $response = new HTTPResponse(json_encode($data)); - $response->addHeader('Content-Type', 'application/json'); - return $response; - } - /** * Try to redirect to an appropriate admin section if we can't access this one */ @@ -499,20 +349,6 @@ protected function init() $this->extend('accessedCMS'); } - // Set the members html editor config - if (Security::getCurrentUser()) { - HTMLEditorConfig::set_active_identifier(Security::getCurrentUser()->getHtmlEditorConfigForCMS()); - } - - // Set default values in the config if missing. These things can't be defined in the config - // file because insufficient information exists when that is being processed - $htmlEditorConfig = HTMLEditorConfig::get_active(); - $htmlEditorConfig->setOption('language', TinyMCEConfig::get_tinymce_lang()); - $langUrl = TinyMCEConfig::get_tinymce_lang_url(); - if ($langUrl) { - $htmlEditorConfig->setOption('language_url', $langUrl); - } - Requirements::customScript(" window.ss = window.ss || {}; window.ss.config = " . $this->getCombinedClientConfig() . "; @@ -1261,18 +1097,6 @@ public function EmptyForm() return $form; } - /** - * Handler for all global modals - * - * @return ModalController - * @deprecated 2.4.0 Will be removed without equivalent functionality to replace it - */ - public function Modals() - { - Deprecation::noticeWithNoReplacment('2.4.0'); - return ModalController::create($this, "Modals"); - } - /** * Renders a panel containing tools which apply to all displayed * "content" (mostly through {@link EditForm()}), for example a tree navigation or a filter panel. diff --git a/code/ModalController.php b/code/ModalController.php index 1ed88c5c3..34e85e3a3 100644 --- a/code/ModalController.php +++ b/code/ModalController.php @@ -4,107 +4,48 @@ use SilverStripe\Admin\Forms\EditorEmailLinkFormFactory; use SilverStripe\Admin\Forms\EditorExternalLinkFormFactory; -use SilverStripe\Control\Controller; -use SilverStripe\Control\RequestHandler; -use SilverStripe\Dev\Deprecation; use SilverStripe\Forms\Form; /** * Parent controller for all CMS-global modals */ -class ModalController extends RequestHandler +class ModalController extends FormSchemaController { + private static ?string $url_segment = 'modals'; + private static $allowed_actions = [ 'EditorExternalLink', 'EditorEmailLink', ]; - public function Link($action = null) - { - return Controller::join_links( - $this->getController()->Link(), - $this->getName(), - $action, - '/' - ); - } - - /** - * @var Controller - * @deprecated 2.4.0 Will be removed without equivalent functionality to replace it - */ - protected $controller; - - /** - * @var string - */ - protected $name; - - public function __construct($controller, $name) - { - parent::__construct(); - - $this->controller = $controller; - $this->name = $name; - } - - public function getRequest() - { - return $this->controller->getRequest(); - } - - /** - * @return Controller - * @deprecated 2.4.0 Will be removed without equivalent functionality to replace it - */ - public function getController() - { - Deprecation::noticeWithNoReplacment('2.4.0'); - return $this->controller; - } - - /** - * Get urlsegment - * - * @return string - * @deprecated 2.4.0 Will be removed without equivalent functionality to replace it - */ - public function getName() - { - Deprecation::noticeWithNoReplacment('2.4.0'); - return $this->name; - } + private static string $required_permission_codes = 'CMS_ACCESS'; /** * Builds and returns the external link form - * - * @return Form */ - public function EditorExternalLink() + public function EditorExternalLink(): Form { // Show link text field if requested - $showLinkText = $this->controller->getRequest()->getVar('requireLinkText'); + $showLinkText = $this->getRequest()->getVar('requireLinkText'); $factory = EditorExternalLinkFormFactory::singleton(); return $factory->getForm( - $this->controller, - "{$this->name}/EditorExternalLink", + $this, + __FUNCTION__, [ 'RequireLinkText' => isset($showLinkText) ] ); } /** * Builds and returns the external link form - * - * @return Form */ - public function EditorEmailLink() + public function EditorEmailLink(): Form { // Show link text field if requested - $showLinkText = $this->controller->getRequest()->getVar('requireLinkText'); + $showLinkText = $this->getRequest()->getVar('requireLinkText'); $factory = EditorEmailLinkFormFactory::singleton(); return $factory->getForm( - $this->controller, - "{$this->name}/EditorEmailLink", + $this, + __FUNCTION__, [ 'RequireLinkText' => isset($showLinkText) ] ); }