mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-24 22:40:55 +01:00
Provide an Editor extension point for transaction validation
Summary: Depends on D20040. Ref T13242. See PHI1039. See PHI873. Two reasonable cases have arisen recently where extending validation rules would help solve problems. We can do this in a pretty straightforward way with a standard extension pattern. Test Plan: Used this extension to keep ducks away from projects: ```lang=php <?php final class NoDucksEditorExtension extends PhabricatorEditorExtension { const EXTENSIONKEY = 'no.ducks'; public function getExtensionName() { return pht('No Ducks!'); } public function supportsObject( PhabricatorApplicationTransactionEditor $editor, PhabricatorApplicationTransactionInterface $object) { return ($object instanceof PhabricatorProject); } public function validateTransactions($object, array $xactions) { $errors = array(); $name_type = PhabricatorProjectNameTransaction::TRANSACTIONTYPE; $old_value = $object->getName(); foreach ($xactions as $xaction) { if ($xaction->getTransactionType() !== $name_type) { continue; } $new_value = $xaction->getNewValue(); if ($old_value === $new_value) { continue; } if (preg_match('/duck/i', $new_value)) { $errors[] = $this->newInvalidTransactionError( $xaction, pht('Project names must not contain the substring "duck".')); } } return $errors; } } ``` {F6162585} Reviewers: amckinley Reviewed By: amckinley Maniphest Tasks: T13242 Differential Revision: https://secure.phabricator.com/D20041
This commit is contained in:
parent
c9760e8d64
commit
f7e8fa0764
4 changed files with 185 additions and 0 deletions
|
@ -3061,6 +3061,8 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorEditPage' => 'applications/transactions/editengine/PhabricatorEditPage.php',
|
'PhabricatorEditPage' => 'applications/transactions/editengine/PhabricatorEditPage.php',
|
||||||
'PhabricatorEditType' => 'applications/transactions/edittype/PhabricatorEditType.php',
|
'PhabricatorEditType' => 'applications/transactions/edittype/PhabricatorEditType.php',
|
||||||
'PhabricatorEditor' => 'infrastructure/PhabricatorEditor.php',
|
'PhabricatorEditor' => 'infrastructure/PhabricatorEditor.php',
|
||||||
|
'PhabricatorEditorExtension' => 'applications/transactions/engineextension/PhabricatorEditorExtension.php',
|
||||||
|
'PhabricatorEditorExtensionModule' => 'applications/transactions/engineextension/PhabricatorEditorExtensionModule.php',
|
||||||
'PhabricatorEditorMailEngineExtension' => 'applications/transactions/engineextension/PhabricatorEditorMailEngineExtension.php',
|
'PhabricatorEditorMailEngineExtension' => 'applications/transactions/engineextension/PhabricatorEditorMailEngineExtension.php',
|
||||||
'PhabricatorEditorMultipleSetting' => 'applications/settings/setting/PhabricatorEditorMultipleSetting.php',
|
'PhabricatorEditorMultipleSetting' => 'applications/settings/setting/PhabricatorEditorMultipleSetting.php',
|
||||||
'PhabricatorEditorSetting' => 'applications/settings/setting/PhabricatorEditorSetting.php',
|
'PhabricatorEditorSetting' => 'applications/settings/setting/PhabricatorEditorSetting.php',
|
||||||
|
@ -8927,6 +8929,8 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorEditPage' => 'Phobject',
|
'PhabricatorEditPage' => 'Phobject',
|
||||||
'PhabricatorEditType' => 'Phobject',
|
'PhabricatorEditType' => 'Phobject',
|
||||||
'PhabricatorEditor' => 'Phobject',
|
'PhabricatorEditor' => 'Phobject',
|
||||||
|
'PhabricatorEditorExtension' => 'Phobject',
|
||||||
|
'PhabricatorEditorExtensionModule' => 'PhabricatorConfigModule',
|
||||||
'PhabricatorEditorMailEngineExtension' => 'PhabricatorMailEngineExtension',
|
'PhabricatorEditorMailEngineExtension' => 'PhabricatorMailEngineExtension',
|
||||||
'PhabricatorEditorMultipleSetting' => 'PhabricatorSelectSetting',
|
'PhabricatorEditorMultipleSetting' => 'PhabricatorSelectSetting',
|
||||||
'PhabricatorEditorSetting' => 'PhabricatorStringSetting',
|
'PhabricatorEditorSetting' => 'PhabricatorStringSetting',
|
||||||
|
|
|
@ -88,6 +88,7 @@ abstract class PhabricatorApplicationTransactionEditor
|
||||||
private $hasRequiredMFA = false;
|
private $hasRequiredMFA = false;
|
||||||
private $request;
|
private $request;
|
||||||
private $cancelURI;
|
private $cancelURI;
|
||||||
|
private $extensions;
|
||||||
|
|
||||||
const STORAGE_ENCODING_BINARY = 'binary';
|
const STORAGE_ENCODING_BINARY = 'binary';
|
||||||
|
|
||||||
|
@ -1013,6 +1014,7 @@ abstract class PhabricatorApplicationTransactionEditor
|
||||||
}
|
}
|
||||||
|
|
||||||
$errors[] = $this->validateAllTransactions($object, $xactions);
|
$errors[] = $this->validateAllTransactions($object, $xactions);
|
||||||
|
$errors[] = $this->validateTransactionsWithExtensions($object, $xactions);
|
||||||
$errors = array_mergev($errors);
|
$errors = array_mergev($errors);
|
||||||
|
|
||||||
$continue_on_missing = $this->getContinueOnMissingFields();
|
$continue_on_missing = $this->getContinueOnMissingFields();
|
||||||
|
@ -4975,4 +4977,60 @@ abstract class PhabricatorApplicationTransactionEditor
|
||||||
return $xactions;
|
return $xactions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* -( Extensions )--------------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
private function validateTransactionsWithExtensions(
|
||||||
|
PhabricatorLiskDAO $object,
|
||||||
|
array $xactions) {
|
||||||
|
$errors = array();
|
||||||
|
|
||||||
|
$extensions = $this->getEditorExtensions();
|
||||||
|
foreach ($extensions as $extension) {
|
||||||
|
$extension_errors = $extension
|
||||||
|
->setObject($object)
|
||||||
|
->validateTransactions($object, $xactions);
|
||||||
|
|
||||||
|
assert_instances_of(
|
||||||
|
$extension_errors,
|
||||||
|
'PhabricatorApplicationTransactionValidationError');
|
||||||
|
|
||||||
|
$errors[] = $extension_errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_mergev($errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getEditorExtensions() {
|
||||||
|
if ($this->extensions === null) {
|
||||||
|
$this->extensions = $this->newEditorExtensions();
|
||||||
|
}
|
||||||
|
return $this->extensions;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function newEditorExtensions() {
|
||||||
|
$extensions = PhabricatorEditorExtension::getAllExtensions();
|
||||||
|
|
||||||
|
$actor = $this->getActor();
|
||||||
|
$object = $this->object;
|
||||||
|
foreach ($extensions as $key => $extension) {
|
||||||
|
|
||||||
|
$extension = id(clone $extension)
|
||||||
|
->setViewer($actor)
|
||||||
|
->setEditor($this)
|
||||||
|
->setObject($object);
|
||||||
|
|
||||||
|
if (!$extension->supportsObject($this, $object)) {
|
||||||
|
unset($extensions[$key]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$extensions[$key] = $extension;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $extensions;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
abstract class PhabricatorEditorExtension
|
||||||
|
extends Phobject {
|
||||||
|
|
||||||
|
private $viewer;
|
||||||
|
private $editor;
|
||||||
|
private $object;
|
||||||
|
|
||||||
|
final public function getExtensionKey() {
|
||||||
|
return $this->getPhobjectClassConstant('EXTENSIONKEY');
|
||||||
|
}
|
||||||
|
|
||||||
|
final public function setEditor(
|
||||||
|
PhabricatorApplicationTransactionEditor $editor) {
|
||||||
|
$this->editor = $editor;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
final public function getEditor() {
|
||||||
|
return $this->editor;
|
||||||
|
}
|
||||||
|
|
||||||
|
final public function setViewer(PhabricatorUser $viewer) {
|
||||||
|
$this->viewer = $viewer;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
final public function getViewer() {
|
||||||
|
return $this->viewer;
|
||||||
|
}
|
||||||
|
|
||||||
|
final public function setObject(
|
||||||
|
PhabricatorApplicationTransactionInterface $object) {
|
||||||
|
$this->object = $object;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
final public static function getAllExtensions() {
|
||||||
|
return id(new PhutilClassMapQuery())
|
||||||
|
->setAncestorClass(__CLASS__)
|
||||||
|
->setUniqueMethod('getExtensionKey')
|
||||||
|
->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract public function getExtensionName();
|
||||||
|
|
||||||
|
public function supportsObject(
|
||||||
|
PhabricatorApplicationTransactionEditor $editor,
|
||||||
|
PhabricatorApplicationTransactionInterface $object) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function validateTransactions($object, array $xactions) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
final protected function newTransactionError(
|
||||||
|
PhabricatorApplicationTransaction $xaction,
|
||||||
|
$title,
|
||||||
|
$message) {
|
||||||
|
return new PhabricatorApplicationTransactionValidationError(
|
||||||
|
$xaction->getTransactionType(),
|
||||||
|
$title,
|
||||||
|
$message,
|
||||||
|
$xaction);
|
||||||
|
}
|
||||||
|
|
||||||
|
final protected function newRequiredTransasctionError(
|
||||||
|
PhabricatorApplicationTransaction $xaction,
|
||||||
|
$message) {
|
||||||
|
return $this->newError($xaction, pht('Required'), $message)
|
||||||
|
->setIsMissingFieldError(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
final protected function newInvalidTransactionError(
|
||||||
|
PhabricatorApplicationTransaction $xaction,
|
||||||
|
$message) {
|
||||||
|
return $this->newTransactionError($xaction, pht('Invalid'), $message);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorEditorExtensionModule
|
||||||
|
extends PhabricatorConfigModule {
|
||||||
|
|
||||||
|
public function getModuleKey() {
|
||||||
|
return 'editor';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getModuleName() {
|
||||||
|
return pht('Engine: Editor');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function renderModuleStatus(AphrontRequest $request) {
|
||||||
|
$viewer = $request->getViewer();
|
||||||
|
|
||||||
|
$extensions = PhabricatorEditorExtension::getAllExtensions();
|
||||||
|
|
||||||
|
$rows = array();
|
||||||
|
foreach ($extensions as $extension) {
|
||||||
|
$rows[] = array(
|
||||||
|
get_class($extension),
|
||||||
|
$extension->getExtensionName(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return id(new AphrontTableView($rows))
|
||||||
|
->setHeaders(
|
||||||
|
array(
|
||||||
|
pht('Class'),
|
||||||
|
pht('Name'),
|
||||||
|
))
|
||||||
|
->setColumnClasses(
|
||||||
|
array(
|
||||||
|
null,
|
||||||
|
'wide pri',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue