mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-10 08:52:39 +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',
|
||||
'PhabricatorEditType' => 'applications/transactions/edittype/PhabricatorEditType.php',
|
||||
'PhabricatorEditor' => 'infrastructure/PhabricatorEditor.php',
|
||||
'PhabricatorEditorExtension' => 'applications/transactions/engineextension/PhabricatorEditorExtension.php',
|
||||
'PhabricatorEditorExtensionModule' => 'applications/transactions/engineextension/PhabricatorEditorExtensionModule.php',
|
||||
'PhabricatorEditorMailEngineExtension' => 'applications/transactions/engineextension/PhabricatorEditorMailEngineExtension.php',
|
||||
'PhabricatorEditorMultipleSetting' => 'applications/settings/setting/PhabricatorEditorMultipleSetting.php',
|
||||
'PhabricatorEditorSetting' => 'applications/settings/setting/PhabricatorEditorSetting.php',
|
||||
|
@ -8927,6 +8929,8 @@ phutil_register_library_map(array(
|
|||
'PhabricatorEditPage' => 'Phobject',
|
||||
'PhabricatorEditType' => 'Phobject',
|
||||
'PhabricatorEditor' => 'Phobject',
|
||||
'PhabricatorEditorExtension' => 'Phobject',
|
||||
'PhabricatorEditorExtensionModule' => 'PhabricatorConfigModule',
|
||||
'PhabricatorEditorMailEngineExtension' => 'PhabricatorMailEngineExtension',
|
||||
'PhabricatorEditorMultipleSetting' => 'PhabricatorSelectSetting',
|
||||
'PhabricatorEditorSetting' => 'PhabricatorStringSetting',
|
||||
|
|
|
@ -88,6 +88,7 @@ abstract class PhabricatorApplicationTransactionEditor
|
|||
private $hasRequiredMFA = false;
|
||||
private $request;
|
||||
private $cancelURI;
|
||||
private $extensions;
|
||||
|
||||
const STORAGE_ENCODING_BINARY = 'binary';
|
||||
|
||||
|
@ -1013,6 +1014,7 @@ abstract class PhabricatorApplicationTransactionEditor
|
|||
}
|
||||
|
||||
$errors[] = $this->validateAllTransactions($object, $xactions);
|
||||
$errors[] = $this->validateTransactionsWithExtensions($object, $xactions);
|
||||
$errors = array_mergev($errors);
|
||||
|
||||
$continue_on_missing = $this->getContinueOnMissingFields();
|
||||
|
@ -4975,4 +4977,60 @@ abstract class PhabricatorApplicationTransactionEditor
|
|||
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