Source of file HistoryViewerController.php
Size: 11,376 Bytes - Last Modified: 2021-12-23T10:36:08+00:00
/var/www/docs.ssmods.com/process/src/src/Controllers/HistoryViewerController.php
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349 | <?php namespace SilverStripe\VersionedAdmin\Controllers; use InvalidArgumentException; use SilverStripe\Admin\LeftAndMain; use SilverStripe\Admin\LeftAndMainFormRequestHandler; use SilverStripe\Control\HTTPRequest; use SilverStripe\Control\HTTPResponse; use SilverStripe\Core\Injector\Injector; use SilverStripe\Forms\Form; use SilverStripe\Forms\FormFactory; use SilverStripe\ORM\DataList; use SilverStripe\ORM\DataObject; use SilverStripe\ORM\FieldType\DBDatetime; use SilverStripe\Versioned\Versioned; use SilverStripe\VersionedAdmin\Forms\DataObjectVersionFormFactory; use SilverStripe\VersionedAdmin\Forms\DiffTransformation; /** * The HistoryViewerController provides AJAX endpoints for React to enable functionality, such as retrieving the form * schema. */ class HistoryViewerController extends LeftAndMain { /** * @var string */ const FORM_NAME_VERSION = 'versionForm'; /** * @var string */ const FORM_NAME_COMPARE = 'compareForm'; private static $url_segment = 'historyviewer'; private static $url_rule = '/$Action'; private static $url_priority = 10; private static $required_permission_codes = 'CMS_ACCESS_CMSMain'; private static $allowed_actions = [ self::FORM_NAME_VERSION, self::FORM_NAME_COMPARE, 'schema', ]; /** * An array of supported form names that can be requested through the schema * * @var string[] */ protected $formNames = [self::FORM_NAME_VERSION, self::FORM_NAME_COMPARE]; public function getClientConfig() { $clientConfig = parent::getClientConfig(); foreach ($this->formNames as $formName) { $clientConfig['form'][$formName] = [ 'schemaUrl' => $this->Link('schema/' . $formName), ]; } return $clientConfig; } /** * Gets a JSON schema representing the current version detail form. * * WARNING: Experimental API. * @internal * @param HTTPRequest $request * @return HTTPResponse */ public function schema($request) { $formName = $request->param('FormName'); if (!in_array($formName, $this->formNames)) { return parent::schema($request); } return $this->generateSchemaForForm($formName, $request); } /** * Checks the requested schema name and returns a scaffolded {@link Form}. An exception is thrown * if an unexpected value is provided. * * @param string $formName * @param HTTPRequest $request * @return HTTPResponse * @throws InvalidArgumentException */ protected function generateSchemaForForm($formName, HTTPRequest $request) { switch ($formName) { // Get schema for history form case self::FORM_NAME_VERSION: $form = $this->getVersionForm([ 'RecordClass' => $request->getVar('RecordClass'), 'RecordID' => $request->getVar('RecordID'), 'RecordVersion' => $request->getVar('RecordVersion'), 'RecordDate' => $request->getVar('RecordDate'), ]); break; case self::FORM_NAME_COMPARE: $form = $this->getCompareForm([ 'RecordClass' => $request->getVar('RecordClass'), 'RecordID' => $request->getVar('RecordID'), 'RecordVersionFrom' => $request->getVar('RecordVersionFrom'), 'RecordVersionTo' => $request->getVar('RecordVersionTo'), ]); break; default: throw new InvalidArgumentException('Invalid form name passed to generate schema: ' . $formName); } // Respond with this schema $response = $this->getResponse(); $response->addHeader('Content-Type', 'application/json'); $schemaID = $this->getRequest()->getURL(); return $this->getSchemaResponse($schemaID, $form); } /** * Returns a {@link Form} showing the version details for a given version of a record * * @param array $context * @return Form */ public function getVersionForm(array $context) { // Attempt to parse a date if given in case we're fetching a version form for a specific timestamp. try { $specifiesDate = !empty($context['RecordDate']) && DBDatetime::create()->setValue($context['RecordDate']); } catch (InvalidArgumentException $e) { $specifiesDate = false; } return $specifiesDate ? $this->getVersionFormByDate($context) : $this->getVersionFormByVersion($context); } /** * @param array $context * @return Form|null */ protected function getVersionFormByDate(array $context) { $required = ['RecordClass', 'RecordID', 'RecordDate']; $this->validateInput($context, $required); $recordClass = $context['RecordClass']; $recordId = $context['RecordID']; $form = null; Versioned::withVersionedMode(function () use ($context, $recordClass, $recordId, &$form) { Versioned::reading_archived_date($context['RecordDate']); $record = DataList::create(DataObject::getSchema()->baseDataClass($recordClass)) ->byID($recordId); if ($record) { $effectiveContext = array_merge($context, ['Record' => $record]); // Ensure the form is scaffolded with archive date enabled. $form = $this->scaffoldForm(self::FORM_NAME_VERSION, $effectiveContext, [ $recordClass, $recordId, ]); } }); return $form; } /** * @param array $context * @return Form */ protected function getVersionFormByVersion(array $context) { $required = ['RecordClass', 'RecordID', 'RecordVersion']; $this->validateInput($context, $required); $recordClass = $context['RecordClass']; $recordId = $context['RecordID']; $recordVersion = $context['RecordVersion']; // Load record and perform a canView check $record = $this->getRecordVersion($recordClass, $recordId, $recordVersion); $effectiveContext = array_merge($context, ['Record' => $record]); return $this->scaffoldForm(self::FORM_NAME_VERSION, $effectiveContext, [ $recordClass, $recordId, ]); } /** * Fetches record version and checks canView permission for result * * @param string $recordClass * @param int $recordId * @param int $recordVersion * @return DataObject|null */ protected function getRecordVersion($recordClass, $recordId, $recordVersion) { $record = Versioned::get_version($recordClass, $recordId, $recordVersion); if (!$record) { $this->jsonError(404); return null; } if (!$record->canView()) { $this->jsonError(403, _t( __CLASS__.'.ErrorItemViewPermissionDenied', "You don't have the necessary permissions to view {ObjectTitle}", ['ObjectTitle' => $record->i18n_singular_name()] )); return null; } return $record; } /** * Returns a {@link Form} containing the comparison {@link DiffTransformation} view for a record * between two specified versions. * * @param array $context * @return Form */ public function getCompareForm(array $context) { $this->validateInput($context, ['RecordClass', 'RecordID', 'RecordVersionFrom', 'RecordVersionTo']); $recordClass = $context['RecordClass']; $recordId = $context['RecordID']; $recordVersionFrom = $context['RecordVersionFrom']; $recordVersionTo = $context['RecordVersionTo']; // Load record and perform a canView check $recordFrom = $this->getRecordVersion($recordClass, $recordId, $recordVersionFrom); $recordTo = $this->getRecordVersion($recordClass, $recordId, $recordVersionTo); if (!$recordFrom || !$recordTo) { return null; } $effectiveContext = array_merge($context, ['Record' => $recordTo]); $form = $this->scaffoldForm(self::FORM_NAME_COMPARE, $effectiveContext, [ $recordClass, $recordId, $recordVersionFrom, $recordVersionTo, ]); // Enable the "compare mode" diff view $comparisonTransformation = DiffTransformation::create(); $form->transform($comparisonTransformation); $form->loadDataFrom($recordFrom); return $form; } public function versionForm(HTTPRequest $request = null) { if (!$request) { $this->jsonError(400); return null; } try { return $this->getVersionForm([ 'RecordClass' => $request->getVar('RecordClass'), 'RecordID' => $request->getVar('RecordID'), 'RecordVersion' => $request->getVar('RecordVersion'), ]); } catch (InvalidArgumentException $ex) { $this->jsonError(400); } } public function compareForm(HTTPRequest $request = null) { if (!$request) { $this->jsonError(400); return null; } try { return $this->getCompareForm([ 'RecordClass' => $request->getVar('RecordClass'), 'RecordID' => $request->getVar('RecordID'), 'RecordVersionFrom' => $request->getVar('RecordVersionFrom'), 'RecordVersionTo' => $request->getVar('RecordVersionTo'), ]); } catch (InvalidArgumentException $ex) { $this->jsonError(400); } } /** * Perform some centralised validation checks on the input request and data within it * * @param array $context * @param string[] $requiredFields * @return bool * @throws InvalidArgumentException */ protected function validateInput(array $context, array $requiredFields = []) { foreach ($requiredFields as $requiredField) { if (empty($context[$requiredField])) { throw new InvalidArgumentException('Missing required field ' . $requiredField); } } return true; } /** * Given some context, scaffold a form using the FormFactory and return it * * @param string $formName The name for the returned {@link Form} * @param array $context Context arguments for the {@link FormFactory} * @param array $extra Context arguments for the {@link LeftAndMainFormRequestHandler} * @return Form */ protected function scaffoldForm($formName, array $context = [], array $extra = []) { /** @var FormFactory $scaffolder */ $scaffolder = Injector::inst()->get(DataObjectVersionFormFactory::class); $form = $scaffolder->getForm($this, $formName, $context); // Set form handler with class name, ID and VersionID return $form->setRequestHandler( LeftAndMainFormRequestHandler::create($form, $extra) ); } } |