1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-18 19:40:55 +01:00

Allow ApplicationEditor forms to be reconfigured

Summary:
Ref T9132. This diff doesn't do anything interesting, it just lays the groundwork for more interesting future diffs.

Broadly, the idea here is to let you create multiple views of each edit form. For example, we might create several different "Create Task" forms, like:

  - "New Bug Report"
  - "New Feature Request"

These would be views of the "Create Task" form, but with various adjustments:

  - A form might have additional instructions ("how to file a good bug report").
  - A form might have prefilled values for some fields (like particular projects, subscribers, or policies).
  - A form might have some fields locked (so they can not be edited) or hidden.
  - A form might have a different field order.
  - A form might have a limited visibility policy, so only some users can access it.

This diff adds a new storage object (`EditEngineConfiguration`) to keep track of all those customizations and represent "a form which has been configured to look and work a certain way".

This doesn't let these configurations do anything useful/interesting, and you can't access them directly yet, it's just all the boring plumbing to enable more interesting behavior in the future.

Test Plan:
ApplicationEditor forms now let you manage available forms and edit the current form:

{F959025}

There's a new (bare bones) list of all available engines:

{F959030}

And if you jump into an engine, you can see all the forms for it:

{F959038}

The actual form configurations have standard detail/edit pages. The edit pages are themselves driven by ApplicationEditor, of course, so you can edit the form for editing forms.

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T9132

Differential Revision: https://secure.phabricator.com/D14453
This commit is contained in:
epriestley 2015-11-04 12:52:52 -08:00
parent b3d3130b71
commit 0398097498
29 changed files with 1536 additions and 31 deletions

View file

@ -0,0 +1,17 @@
CREATE TABLE {$NAMESPACE}_search.search_editengineconfiguration (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
phid VARBINARY(64) NOT NULL,
engineKey VARCHAR(64) NOT NULL COLLATE {$COLLATE_TEXT},
builtinKey VARCHAR(64) COLLATE {$COLLATE_TEXT},
name VARCHAR(255) NOT NULL COLLATE {$COLLATE_TEXT},
viewPolicy VARBINARY(64) NOT NULL,
editPolicy VARBINARY(64) NOT NULL,
properties LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT},
isDisabled BOOL NOT NULL DEFAULT 0,
isDefault BOOL NOT NULL DEFAULT 0,
dateCreated INT UNSIGNED NOT NULL,
dateModified INT UNSIGNED NOT NULL,
UNIQUE KEY `key_phid` (phid),
UNIQUE KEY `key_engine` (engineKey, builtinKey),
KEY `key_default` (engineKey, isDefault, isDisabled)
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};

View file

@ -0,0 +1,19 @@
CREATE TABLE {$NAMESPACE}_search.search_editengineconfigurationtransaction (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
phid VARBINARY(64) NOT NULL,
authorPHID VARBINARY(64) NOT NULL,
objectPHID VARBINARY(64) NOT NULL,
viewPolicy VARBINARY(64) NOT NULL,
editPolicy VARBINARY(64) NOT NULL,
commentPHID VARBINARY(64) DEFAULT NULL,
commentVersion INT UNSIGNED NOT NULL,
transactionType VARCHAR(32) COLLATE {$COLLATE_TEXT} NOT NULL,
oldValue LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
newValue LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
contentSource LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
metadata LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
dateCreated INT UNSIGNED NOT NULL,
dateModified INT UNSIGNED NOT NULL,
UNIQUE KEY `key_phid` (`phid`),
KEY `key_object` (`objectPHID`)
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};

View file

@ -1579,8 +1579,6 @@ phutil_register_library_map(array(
'PhabricatorApplicationDatasource' => 'applications/meta/typeahead/PhabricatorApplicationDatasource.php', 'PhabricatorApplicationDatasource' => 'applications/meta/typeahead/PhabricatorApplicationDatasource.php',
'PhabricatorApplicationDetailViewController' => 'applications/meta/controller/PhabricatorApplicationDetailViewController.php', 'PhabricatorApplicationDetailViewController' => 'applications/meta/controller/PhabricatorApplicationDetailViewController.php',
'PhabricatorApplicationEditController' => 'applications/meta/controller/PhabricatorApplicationEditController.php', 'PhabricatorApplicationEditController' => 'applications/meta/controller/PhabricatorApplicationEditController.php',
'PhabricatorApplicationEditEngine' => 'applications/transactions/editengine/PhabricatorApplicationEditEngine.php',
'PhabricatorApplicationEditEngineAPIMethod' => 'applications/transactions/editengine/PhabricatorApplicationEditEngineAPIMethod.php',
'PhabricatorApplicationEditHTTPParameterHelpView' => 'applications/transactions/view/PhabricatorApplicationEditHTTPParameterHelpView.php', 'PhabricatorApplicationEditHTTPParameterHelpView' => 'applications/transactions/view/PhabricatorApplicationEditHTTPParameterHelpView.php',
'PhabricatorApplicationEmailCommandsController' => 'applications/meta/controller/PhabricatorApplicationEmailCommandsController.php', 'PhabricatorApplicationEmailCommandsController' => 'applications/meta/controller/PhabricatorApplicationEmailCommandsController.php',
'PhabricatorApplicationLaunchView' => 'applications/meta/view/PhabricatorApplicationLaunchView.php', 'PhabricatorApplicationLaunchView' => 'applications/meta/view/PhabricatorApplicationLaunchView.php',
@ -2110,6 +2108,24 @@ phutil_register_library_map(array(
'PhabricatorEdgeTestCase' => 'infrastructure/edges/__tests__/PhabricatorEdgeTestCase.php', 'PhabricatorEdgeTestCase' => 'infrastructure/edges/__tests__/PhabricatorEdgeTestCase.php',
'PhabricatorEdgeType' => 'infrastructure/edges/type/PhabricatorEdgeType.php', 'PhabricatorEdgeType' => 'infrastructure/edges/type/PhabricatorEdgeType.php',
'PhabricatorEdgeTypeTestCase' => 'infrastructure/edges/type/__tests__/PhabricatorEdgeTypeTestCase.php', 'PhabricatorEdgeTypeTestCase' => 'infrastructure/edges/type/__tests__/PhabricatorEdgeTypeTestCase.php',
'PhabricatorEditEngine' => 'applications/transactions/editengine/PhabricatorEditEngine.php',
'PhabricatorEditEngineAPIMethod' => 'applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php',
'PhabricatorEditEngineConfiguration' => 'applications/transactions/storage/PhabricatorEditEngineConfiguration.php',
'PhabricatorEditEngineConfigurationEditController' => 'applications/transactions/controller/PhabricatorEditEngineConfigurationEditController.php',
'PhabricatorEditEngineConfigurationEditEngine' => 'applications/transactions/editor/PhabricatorEditEngineConfigurationEditEngine.php',
'PhabricatorEditEngineConfigurationEditor' => 'applications/transactions/editor/PhabricatorEditEngineConfigurationEditor.php',
'PhabricatorEditEngineConfigurationListController' => 'applications/transactions/controller/PhabricatorEditEngineConfigurationListController.php',
'PhabricatorEditEngineConfigurationPHIDType' => 'applications/transactions/phid/PhabricatorEditEngineConfigurationPHIDType.php',
'PhabricatorEditEngineConfigurationQuery' => 'applications/transactions/query/PhabricatorEditEngineConfigurationQuery.php',
'PhabricatorEditEngineConfigurationSaveController' => 'applications/transactions/controller/PhabricatorEditEngineConfigurationSaveController.php',
'PhabricatorEditEngineConfigurationSearchEngine' => 'applications/transactions/query/PhabricatorEditEngineConfigurationSearchEngine.php',
'PhabricatorEditEngineConfigurationTransaction' => 'applications/transactions/storage/PhabricatorEditEngineConfigurationTransaction.php',
'PhabricatorEditEngineConfigurationTransactionQuery' => 'applications/transactions/query/PhabricatorEditEngineConfigurationTransactionQuery.php',
'PhabricatorEditEngineConfigurationViewController' => 'applications/transactions/controller/PhabricatorEditEngineConfigurationViewController.php',
'PhabricatorEditEngineController' => 'applications/transactions/controller/PhabricatorEditEngineController.php',
'PhabricatorEditEngineListController' => 'applications/transactions/controller/PhabricatorEditEngineListController.php',
'PhabricatorEditEngineQuery' => 'applications/transactions/query/PhabricatorEditEngineQuery.php',
'PhabricatorEditEngineSearchEngine' => 'applications/transactions/query/PhabricatorEditEngineSearchEngine.php',
'PhabricatorEditField' => 'applications/transactions/editfield/PhabricatorEditField.php', 'PhabricatorEditField' => 'applications/transactions/editfield/PhabricatorEditField.php',
'PhabricatorEditType' => 'applications/transactions/edittype/PhabricatorEditType.php', 'PhabricatorEditType' => 'applications/transactions/edittype/PhabricatorEditType.php',
'PhabricatorEditor' => 'infrastructure/PhabricatorEditor.php', 'PhabricatorEditor' => 'infrastructure/PhabricatorEditor.php',
@ -2294,6 +2310,7 @@ phutil_register_library_map(array(
'PhabricatorInlineCommentInterface' => 'infrastructure/diff/interface/PhabricatorInlineCommentInterface.php', 'PhabricatorInlineCommentInterface' => 'infrastructure/diff/interface/PhabricatorInlineCommentInterface.php',
'PhabricatorInlineCommentPreviewController' => 'infrastructure/diff/PhabricatorInlineCommentPreviewController.php', 'PhabricatorInlineCommentPreviewController' => 'infrastructure/diff/PhabricatorInlineCommentPreviewController.php',
'PhabricatorInlineSummaryView' => 'infrastructure/diff/view/PhabricatorInlineSummaryView.php', 'PhabricatorInlineSummaryView' => 'infrastructure/diff/view/PhabricatorInlineSummaryView.php',
'PhabricatorInstructionsEditField' => 'applications/transactions/editfield/PhabricatorInstructionsEditField.php',
'PhabricatorInternationalizationManagementExtractWorkflow' => 'infrastructure/internationalization/management/PhabricatorInternationalizationManagementExtractWorkflow.php', 'PhabricatorInternationalizationManagementExtractWorkflow' => 'infrastructure/internationalization/management/PhabricatorInternationalizationManagementExtractWorkflow.php',
'PhabricatorInternationalizationManagementWorkflow' => 'infrastructure/internationalization/management/PhabricatorInternationalizationManagementWorkflow.php', 'PhabricatorInternationalizationManagementWorkflow' => 'infrastructure/internationalization/management/PhabricatorInternationalizationManagementWorkflow.php',
'PhabricatorInvalidConfigSetupCheck' => 'applications/config/check/PhabricatorInvalidConfigSetupCheck.php', 'PhabricatorInvalidConfigSetupCheck' => 'applications/config/check/PhabricatorInvalidConfigSetupCheck.php',
@ -5500,7 +5517,7 @@ phutil_register_library_map(array(
'PasteCreateMailReceiver' => 'PhabricatorMailReceiver', 'PasteCreateMailReceiver' => 'PhabricatorMailReceiver',
'PasteDefaultEditCapability' => 'PhabricatorPolicyCapability', 'PasteDefaultEditCapability' => 'PhabricatorPolicyCapability',
'PasteDefaultViewCapability' => 'PhabricatorPolicyCapability', 'PasteDefaultViewCapability' => 'PhabricatorPolicyCapability',
'PasteEditConduitAPIMethod' => 'PhabricatorApplicationEditEngineAPIMethod', 'PasteEditConduitAPIMethod' => 'PhabricatorEditEngineAPIMethod',
'PasteEmbedView' => 'AphrontView', 'PasteEmbedView' => 'AphrontView',
'PasteInfoConduitAPIMethod' => 'PasteConduitAPIMethod', 'PasteInfoConduitAPIMethod' => 'PasteConduitAPIMethod',
'PasteMailReceiver' => 'PhabricatorObjectMailReceiver', 'PasteMailReceiver' => 'PhabricatorObjectMailReceiver',
@ -5544,8 +5561,6 @@ phutil_register_library_map(array(
'PhabricatorApplicationDatasource' => 'PhabricatorTypeaheadDatasource', 'PhabricatorApplicationDatasource' => 'PhabricatorTypeaheadDatasource',
'PhabricatorApplicationDetailViewController' => 'PhabricatorApplicationsController', 'PhabricatorApplicationDetailViewController' => 'PhabricatorApplicationsController',
'PhabricatorApplicationEditController' => 'PhabricatorApplicationsController', 'PhabricatorApplicationEditController' => 'PhabricatorApplicationsController',
'PhabricatorApplicationEditEngine' => 'Phobject',
'PhabricatorApplicationEditEngineAPIMethod' => 'ConduitAPIMethod',
'PhabricatorApplicationEditHTTPParameterHelpView' => 'AphrontView', 'PhabricatorApplicationEditHTTPParameterHelpView' => 'AphrontView',
'PhabricatorApplicationEmailCommandsController' => 'PhabricatorApplicationsController', 'PhabricatorApplicationEmailCommandsController' => 'PhabricatorApplicationsController',
'PhabricatorApplicationLaunchView' => 'AphrontTagView', 'PhabricatorApplicationLaunchView' => 'AphrontTagView',
@ -6172,6 +6187,31 @@ phutil_register_library_map(array(
'PhabricatorEdgeTestCase' => 'PhabricatorTestCase', 'PhabricatorEdgeTestCase' => 'PhabricatorTestCase',
'PhabricatorEdgeType' => 'Phobject', 'PhabricatorEdgeType' => 'Phobject',
'PhabricatorEdgeTypeTestCase' => 'PhabricatorTestCase', 'PhabricatorEdgeTypeTestCase' => 'PhabricatorTestCase',
'PhabricatorEditEngine' => array(
'Phobject',
'PhabricatorPolicyInterface',
),
'PhabricatorEditEngineAPIMethod' => 'ConduitAPIMethod',
'PhabricatorEditEngineConfiguration' => array(
'PhabricatorSearchDAO',
'PhabricatorApplicationTransactionInterface',
'PhabricatorPolicyInterface',
),
'PhabricatorEditEngineConfigurationEditController' => 'PhabricatorEditEngineController',
'PhabricatorEditEngineConfigurationEditEngine' => 'PhabricatorEditEngine',
'PhabricatorEditEngineConfigurationEditor' => 'PhabricatorApplicationTransactionEditor',
'PhabricatorEditEngineConfigurationListController' => 'PhabricatorEditEngineController',
'PhabricatorEditEngineConfigurationPHIDType' => 'PhabricatorPHIDType',
'PhabricatorEditEngineConfigurationQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorEditEngineConfigurationSaveController' => 'PhabricatorEditEngineController',
'PhabricatorEditEngineConfigurationSearchEngine' => 'PhabricatorApplicationSearchEngine',
'PhabricatorEditEngineConfigurationTransaction' => 'PhabricatorApplicationTransaction',
'PhabricatorEditEngineConfigurationTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'PhabricatorEditEngineConfigurationViewController' => 'PhabricatorEditEngineController',
'PhabricatorEditEngineController' => 'PhabricatorApplicationTransactionController',
'PhabricatorEditEngineListController' => 'PhabricatorEditEngineController',
'PhabricatorEditEngineQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorEditEngineSearchEngine' => 'PhabricatorApplicationSearchEngine',
'PhabricatorEditField' => 'Phobject', 'PhabricatorEditField' => 'Phobject',
'PhabricatorEditType' => 'Phobject', 'PhabricatorEditType' => 'Phobject',
'PhabricatorEditor' => 'Phobject', 'PhabricatorEditor' => 'Phobject',
@ -6392,6 +6432,7 @@ phutil_register_library_map(array(
'PhabricatorInlineCommentInterface' => 'PhabricatorMarkupInterface', 'PhabricatorInlineCommentInterface' => 'PhabricatorMarkupInterface',
'PhabricatorInlineCommentPreviewController' => 'PhabricatorController', 'PhabricatorInlineCommentPreviewController' => 'PhabricatorController',
'PhabricatorInlineSummaryView' => 'AphrontView', 'PhabricatorInlineSummaryView' => 'AphrontView',
'PhabricatorInstructionsEditField' => 'PhabricatorEditField',
'PhabricatorInternationalizationManagementExtractWorkflow' => 'PhabricatorInternationalizationManagementWorkflow', 'PhabricatorInternationalizationManagementExtractWorkflow' => 'PhabricatorInternationalizationManagementWorkflow',
'PhabricatorInternationalizationManagementWorkflow' => 'PhabricatorManagementWorkflow', 'PhabricatorInternationalizationManagementWorkflow' => 'PhabricatorManagementWorkflow',
'PhabricatorInvalidConfigSetupCheck' => 'PhabricatorSetupCheck', 'PhabricatorInvalidConfigSetupCheck' => 'PhabricatorSetupCheck',
@ -6710,7 +6751,7 @@ phutil_register_library_map(array(
'PhabricatorPasteController' => 'PhabricatorController', 'PhabricatorPasteController' => 'PhabricatorController',
'PhabricatorPasteDAO' => 'PhabricatorLiskDAO', 'PhabricatorPasteDAO' => 'PhabricatorLiskDAO',
'PhabricatorPasteEditController' => 'PhabricatorPasteController', 'PhabricatorPasteEditController' => 'PhabricatorPasteController',
'PhabricatorPasteEditEngine' => 'PhabricatorApplicationEditEngine', 'PhabricatorPasteEditEngine' => 'PhabricatorEditEngine',
'PhabricatorPasteEditor' => 'PhabricatorApplicationTransactionEditor', 'PhabricatorPasteEditor' => 'PhabricatorApplicationTransactionEditor',
'PhabricatorPasteListController' => 'PhabricatorPasteController', 'PhabricatorPasteListController' => 'PhabricatorPasteController',
'PhabricatorPastePastePHIDType' => 'PhabricatorPHIDType', 'PhabricatorPastePastePHIDType' => 'PhabricatorPHIDType',

View file

@ -635,8 +635,12 @@ abstract class PhabricatorApplication
return array(); return array();
} }
protected function getEditRoutePattern($base) { protected function getEditRoutePattern($base = null) {
return $base.'(?:(?P<id>[0-9]\d*)/)?(?:(?P<editAction>parameters)/)?'; return $base.'(?:(?P<id>[0-9]\d*)/)?(?:(?P<editAction>parameters)/)?';
} }
protected function getQueryRoutePattern($base = null) {
return $base.'(?:query/(?P<queryKey>[^/]+)/)?';
}
} }

View file

@ -1,7 +1,7 @@
<?php <?php
final class PasteEditConduitAPIMethod final class PasteEditConduitAPIMethod
extends PhabricatorApplicationEditEngineAPIMethod { extends PhabricatorEditEngineAPIMethod {
public function getAPIMethodName() { public function getAPIMethodName() {
return 'paste.edit'; return 'paste.edit';

View file

@ -1,7 +1,13 @@
<?php <?php
final class PhabricatorPasteEditEngine final class PhabricatorPasteEditEngine
extends PhabricatorApplicationEditEngine { extends PhabricatorEditEngine {
const ENGINECONST = 'paste.paste';
public function getEngineName() {
return pht('Pastes');
}
protected function newEditableObject() { protected function newEditableObject() {
return PhabricatorPaste::initializeNewPaste($this->getViewer()); return PhabricatorPaste::initializeNewPaste($this->getViewer());
@ -24,7 +30,7 @@ final class PhabricatorPasteEditEngine
return $object->getMonogram(); return $object->getMonogram();
} }
protected function getObjectCreateShortText($object) { protected function getObjectCreateShortText() {
return pht('Create Paste'); return pht('Create Paste');
} }

View file

@ -33,6 +33,20 @@ final class PhabricatorTransactionsApplication extends PhabricatorApplication {
=> 'PhabricatorApplicationTransactionShowOlderController', => 'PhabricatorApplicationTransactionShowOlderController',
'(?P<value>old|new)/(?<phid>[^/]+)/' '(?P<value>old|new)/(?<phid>[^/]+)/'
=> 'PhabricatorApplicationTransactionValueController', => 'PhabricatorApplicationTransactionValueController',
'editengine/' => array(
$this->getQueryRoutePattern()
=> 'PhabricatorEditEngineListController',
'(?P<engineKey>[^/]+)/' => array(
$this->getQueryRoutePattern() =>
'PhabricatorEditEngineConfigurationListController',
$this->getEditRoutePattern('edit/') =>
'PhabricatorEditEngineConfigurationEditController',
'view/(?P<key>[^/]+)/' =>
'PhabricatorEditEngineConfigurationViewController',
'save/(?P<key>[^/]+)/' =>
'PhabricatorEditEngineConfigurationSaveController',
),
),
), ),
); );
} }

View file

@ -0,0 +1,26 @@
<?php
final class PhabricatorEditEngineConfigurationEditController
extends PhabricatorEditEngineController {
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$target_engine_key = $request->getURIData('engineKey');
$target_engine = PhabricatorEditEngine::getByKey(
$viewer,
$target_engine_key);
if (!$target_engine) {
return new Aphront404Response();
}
$this->setEngineKey($target_engine->getEngineKey());
return id(new PhabricatorEditEngineConfigurationEditEngine())
->setTargetEngine($target_engine)
->setController($this)
->buildResponse();
}
}

View file

@ -0,0 +1,34 @@
<?php
final class PhabricatorEditEngineConfigurationListController
extends PhabricatorEditEngineController {
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$this->setEngineKey($request->getURIData('engineKey'));
return id(new PhabricatorEditEngineConfigurationSearchEngine())
->setController($this)
->setEngineKey($this->getEngineKey())
->buildResponse();
}
protected function buildApplicationCrumbs() {
$crumbs = parent::buildApplicationCrumbs();
$engine_key = $this->getEngineKey();
$edit_uri = "/transactions/editengine/{$engine_key}/edit/";
$crumbs->addAction(
id(new PHUIListItemView())
->setName(pht('New Form'))
->setHref($edit_uri)
->setIcon('fa-plus-square'));
return $crumbs;
}
}

View file

@ -0,0 +1,55 @@
<?php
final class PhabricatorEditEngineConfigurationSaveController
extends PhabricatorEditEngineController {
public function handleRequest(AphrontRequest $request) {
$engine_key = $request->getURIData('engineKey');
$this->setEngineKey($engine_key);
$key = $request->getURIData('key');
$viewer = $this->getViewer();
$config = id(new PhabricatorEditEngineConfigurationQuery())
->setViewer($viewer)
->withEngineKeys(array($engine_key))
->withIdentifiers(array($key))
->executeOne();
if (!$config) {
return id(new Aphront404Response());
}
$view_uri = $config->getURI();
if ($config->getID()) {
return $this->newDialog()
->setTitle(pht('Already Editable'))
->appendParagraph(
pht('This form configuration is already editable.'))
->addCancelButton($view_uri);
}
if ($request->isFormPost()) {
$editor = id(new PhabricatorEditEngineConfigurationEditor())
->setActor($viewer)
->setContentSourceFromRequest($request)
->setContinueOnNoEffect(true);
$editor->applyTransactions($config, array());
return id(new AphrontRedirectResponse())
->setURI($config->getURI());
}
// TODO: Explain what this means in more detail once the implications are
// more clear, or just link to some docs or something.
return $this->newDialog()
->setTitle(pht('Make Builtin Editable'))
->appendParagraph(
pht('Make this builtin form editable?'))
->addSubmitButton(pht('Make Editable'))
->addCancelButton($view_uri);
}
}

View file

@ -0,0 +1,125 @@
<?php
final class PhabricatorEditEngineConfigurationViewController
extends PhabricatorEditEngineController {
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$engine_key = $request->getURIData('engineKey');
$this->setEngineKey($engine_key);
$key = $request->getURIData('key');
$viewer = $this->getViewer();
$config = id(new PhabricatorEditEngineConfigurationQuery())
->setViewer($viewer)
->withEngineKeys(array($engine_key))
->withIdentifiers(array($key))
->executeOne();
if (!$config) {
return id(new Aphront404Response());
}
$is_concrete = (bool)$config->getID();
$actions = $this->buildActionView($config);
$properties = $this->buildPropertyView($config)
->setActionList($actions);
$header = id(new PHUIHeaderView())
->setUser($viewer)
->setPolicyObject($config)
->setHeader(pht('Edit Form: %s', $config->getDisplayName()));
$box = id(new PHUIObjectBoxView())
->setHeader($header)
->addPropertyList($properties);
$crumbs = $this->buildApplicationCrumbs();
if ($is_concrete) {
$crumbs->addTextCrumb(pht('Form %d', $config->getID()));
} else {
$crumbs->addTextCrumb(pht('Builtin'));
}
if ($is_concrete) {
$timeline = $this->buildTransactionTimeline(
$config,
new PhabricatorEditEngineConfigurationTransactionQuery());
$timeline->setShouldTerminate(true);
} else {
$timeline = null;
}
return $this->newPage()
->setCrumbs($crumbs)
->appendChild(
array(
$box,
$timeline,
));
}
private function buildActionView(
PhabricatorEditEngineConfiguration $config) {
$viewer = $this->getViewer();
$engine_key = $this->getEngineKey();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$config,
PhabricatorPolicyCapability::CAN_EDIT);
$view = id(new PhabricatorActionListView())
->setUser($viewer);
$key = $config->getIdentifier();
$base_uri = "/transactions/editengine/{$engine_key}";
$is_concrete = (bool)$config->getID();
if (!$is_concrete) {
$save_uri = "{$base_uri}/save/{$key}/";
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('Make Editable'))
->setIcon('fa-pencil')
->setDisabled(!$can_edit)
->setWorkflow(true)
->setHref($save_uri));
$can_edit = false;
} else {
$edit_uri = "{$base_uri}/edit/{$key}/";
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Form Configuration'))
->setIcon('fa-pencil')
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit)
->setHref($edit_uri));
}
return $view;
}
private function buildPropertyView(
PhabricatorEditEngineConfiguration $config) {
$viewer = $this->getViewer();
$properties = id(new PHUIPropertyListView())
->setUser($viewer)
->setObject($config);
return $properties;
}
}

View file

@ -0,0 +1,37 @@
<?php
abstract class PhabricatorEditEngineController
extends PhabricatorApplicationTransactionController {
private $engineKey;
public function setEngineKey($engine_key) {
$this->engineKey = $engine_key;
return $this;
}
public function getEngineKey() {
return $this->engineKey;
}
protected function buildApplicationCrumbs() {
$crumbs = parent::buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Edit Engines'), '/transactions/editengine/');
$engine_key = $this->getEngineKey();
if ($engine_key !== null) {
$engine = PhabricatorEditEngine::getByKey(
$this->getViewer(),
$engine_key);
if ($engine) {
$crumbs->addTextCrumb(
$engine->getEngineName(),
"/transactions/editengine/{$engine_key}/");
}
}
return $crumbs;
}
}

View file

@ -0,0 +1,16 @@
<?php
final class PhabricatorEditEngineListController
extends PhabricatorEditEngineController {
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
return id(new PhabricatorEditEngineSearchEngine())
->setController($this)
->buildResponse();
}
}

View file

@ -4,6 +4,7 @@
/** /**
* @task fields Managing Fields * @task fields Managing Fields
* @task text Display Text * @task text Display Text
* @task config Edit Engine Configuration
* @task uri Managing URIs * @task uri Managing URIs
* @task load Creating and Loading Objects * @task load Creating and Loading Objects
* @task web Responding to Web Requests * @task web Responding to Web Requests
@ -11,11 +12,16 @@
* @task http Responding to HTTP Parameter Requests * @task http Responding to HTTP Parameter Requests
* @task conduit Responding to Conduit Requests * @task conduit Responding to Conduit Requests
*/ */
abstract class PhabricatorApplicationEditEngine extends Phobject { abstract class PhabricatorEditEngine
extends Phobject
implements PhabricatorPolicyInterface {
const EDITENGINECONFIG_DEFAULT = 'default';
private $viewer; private $viewer;
private $controller; private $controller;
private $isCreate; private $isCreate;
private $editEngineConfiguration;
final public function setViewer(PhabricatorUser $viewer) { final public function setViewer(PhabricatorUser $viewer) {
$this->viewer = $viewer; $this->viewer = $viewer;
@ -36,6 +42,10 @@ abstract class PhabricatorApplicationEditEngine extends Phobject {
return $this->controller; return $this->controller;
} }
final public function getEngineKey() {
return $this->getPhobjectClassConstant('ENGINECONST', 64);
}
/* -( Managing Fields )---------------------------------------------------- */ /* -( Managing Fields )---------------------------------------------------- */
@ -184,6 +194,9 @@ abstract class PhabricatorApplicationEditEngine extends Phobject {
} }
} }
$config = $this->getEditEngineConfiguration();
$fields = $config->applyConfigurationToFields($this, $fields);
foreach ($fields as $field) { foreach ($fields as $field) {
$field $field
->setViewer($viewer) ->setViewer($viewer)
@ -197,6 +210,12 @@ abstract class PhabricatorApplicationEditEngine extends Phobject {
/* -( Display Text )------------------------------------------------------- */ /* -( Display Text )------------------------------------------------------- */
/**
* @task text
*/
abstract public function getEngineName();
/** /**
* @task text * @task text
*/ */
@ -212,7 +231,7 @@ abstract class PhabricatorApplicationEditEngine extends Phobject {
/** /**
* @task text * @task text
*/ */
abstract protected function getObjectCreateShortText($object); abstract protected function getObjectCreateShortText();
/** /**
@ -237,6 +256,121 @@ abstract class PhabricatorApplicationEditEngine extends Phobject {
} }
/* -( Edit Engine Configuration )------------------------------------------ */
protected function supportsEditEngineConfiguration() {
return true;
}
final protected function getEditEngineConfiguration() {
return $this->editEngineConfiguration;
}
private function loadEditEngineConfiguration($key) {
if ($key === null) {
$key = self::EDITENGINECONFIG_DEFAULT;
}
$config = id(new PhabricatorEditEngineConfigurationQuery())
->setViewer($this->getViewer())
->withEngineKeys(array($this->getEngineKey()))
->withIdentifiers(array($key))
->executeOne();
if (!$config) {
return null;
}
$this->editEngineConfiguration = $config;
return $config;
}
final public function getBuiltinEngineConfigurations() {
$configurations = $this->newBuiltinEngineConfigurations();
if (!$configurations) {
throw new Exception(
pht(
'EditEngine ("%s") returned no builtin engine configurations, but '.
'an edit engine must have at least one configuration.',
get_class($this)));
}
assert_instances_of($configurations, 'PhabricatorEditEngineConfiguration');
$has_default = false;
foreach ($configurations as $config) {
if ($config->getBuiltinKey() == self::EDITENGINECONFIG_DEFAULT) {
$has_default = true;
}
}
if (!$has_default) {
$first = head($configurations);
if (!$first->getBuiltinKey()) {
$first->setBuiltinKey(self::EDITENGINECONFIG_DEFAULT);
if (!strlen($first->getName())) {
$first->setName($this->getObjectCreateShortText());
}
} else {
throw new Exception(
pht(
'EditEngine ("%s") returned builtin engine configurations, '.
'but none are marked as default and the first configuration has '.
'a different builtin key already. Mark a builtin as default or '.
'omit the key from the first configuration',
get_class($this)));
}
}
$builtins = array();
foreach ($configurations as $key => $config) {
$builtin_key = $config->getBuiltinKey();
if ($builtin_key === null) {
throw new Exception(
pht(
'EditEngine ("%s") returned builtin engine configurations, '.
'but one (with key "%s") is missing a builtin key. Provide a '.
'builtin key for each configuration (you can omit it from the '.
'first configuration in the list to automatically assign the '.
'default key).',
get_class($this),
$key));
}
if (isset($builtins[$builtin_key])) {
throw new Exception(
pht(
'EditEngine ("%s") returned builtin engine configurations, '.
'but at least two specify the same builtin key ("%s"). Engines '.
'must have unique builtin keys.',
get_class($this),
$builtin_key));
}
$builtins[$builtin_key] = $config;
}
return $builtins;
}
protected function newBuiltinEngineConfigurations() {
return array(
$this->newConfiguration(),
);
}
final protected function newConfiguration() {
return PhabricatorEditEngineConfiguration::initializeNewConfiguration(
$this->getViewer(),
$this);
}
/* -( Managing URIs )------------------------------------------------------ */ /* -( Managing URIs )------------------------------------------------------ */
@ -317,7 +451,7 @@ abstract class PhabricatorApplicationEditEngine extends Phobject {
* @return bool True if a new object is being created. * @return bool True if a new object is being created.
* @task load * @task load
*/ */
final protected function getIsCreate() { final public function getIsCreate() {
return $this->isCreate; return $this->isCreate;
} }
@ -391,6 +525,35 @@ abstract class PhabricatorApplicationEditEngine extends Phobject {
} }
/**
* Verify that an object is appropriate for editing.
*
* @param wild Loaded value.
* @return void
* @task load
*/
private function validateObject($object) {
if (!$object || !is_object($object)) {
throw new Exception(
pht(
'EditEngine "%s" created or loaded an invalid object: object must '.
'actually be an object, but is of some other type ("%s").',
get_class($this),
gettype($object)));
}
if (!($object instanceof PhabricatorApplicationTransactionInterface)) {
throw new Exception(
pht(
'EditEngine "%s" created or loaded an invalid object: object (of '.
'class "%s") must implement "%s", but does not.',
get_class($this),
get_class($object),
'PhabricatorApplicationTransactionInterface'));
}
}
/* -( Responding to Web Requests )----------------------------------------- */ /* -( Responding to Web Requests )----------------------------------------- */
@ -399,6 +562,11 @@ abstract class PhabricatorApplicationEditEngine extends Phobject {
$controller = $this->getController(); $controller = $this->getController();
$request = $controller->getRequest(); $request = $controller->getRequest();
$config = $this->loadEditEngineConfiguration($request->getURIData('form'));
if (!$config) {
return new Aphront404Response();
}
$id = $request->getURIData('id'); $id = $request->getURIData('id');
if ($id) { if ($id) {
$this->setIsCreate(false); $this->setIsCreate(false);
@ -411,6 +579,8 @@ abstract class PhabricatorApplicationEditEngine extends Phobject {
$object = $this->newEditableObject(); $object = $this->newEditableObject();
} }
$this->validateObject($object);
$action = $request->getURIData('editAction'); $action = $request->getURIData('editAction');
switch ($action) { switch ($action) {
case 'parameters': case 'parameters':
@ -425,7 +595,7 @@ abstract class PhabricatorApplicationEditEngine extends Phobject {
$crumbs = $controller->buildApplicationCrumbsForEditEngine(); $crumbs = $controller->buildApplicationCrumbsForEditEngine();
if ($this->getIsCreate()) { if ($this->getIsCreate()) {
$create_text = $this->getObjectCreateShortText($object); $create_text = $this->getObjectCreateShortText();
if ($final) { if ($final) {
$crumbs->addTextCrumb($create_text); $crumbs->addTextCrumb($create_text);
} else { } else {
@ -570,6 +740,20 @@ abstract class PhabricatorApplicationEditEngine extends Phobject {
private function buildEditFormActions($object) { private function buildEditFormActions($object) {
$actions = array(); $actions = array();
if ($this->supportsEditEngineConfiguration()) {
$engine_key = $this->getEngineKey();
$config = $this->getEditEngineConfiguration();
$actions[] = id(new PhabricatorActionView())
->setName(pht('Manage Form Configurations'))
->setIcon('fa-list-ul')
->setHref("/transactions/editengine/{$engine_key}/");
$actions[] = id(new PhabricatorActionView())
->setName(pht('Edit Form Configuration'))
->setIcon('fa-pencil')
->setHref($config->getURI());
}
$actions[] = id(new PhabricatorActionView()) $actions[] = id(new PhabricatorActionView())
->setName(pht('Show HTTP Parameters')) ->setName(pht('Show HTTP Parameters'))
->setIcon('fa-crosshairs') ->setIcon('fa-crosshairs')
@ -601,7 +785,7 @@ abstract class PhabricatorApplicationEditEngine extends Phobject {
$header_text = pht( $header_text = pht(
'HTTP Parameters: %s', 'HTTP Parameters: %s',
$this->getObjectCreateShortText($object)); $this->getObjectCreateShortText());
$header = id(new PHUIHeaderView()) $header = id(new PHUIHeaderView())
->setHeader($header_text); ->setHeader($header_text);
@ -637,6 +821,14 @@ abstract class PhabricatorApplicationEditEngine extends Phobject {
final public function buildConduitResponse(ConduitAPIRequest $request) { final public function buildConduitResponse(ConduitAPIRequest $request) {
$viewer = $this->getViewer(); $viewer = $this->getViewer();
$config = $this->loadEditEngineConfiguration(null);
if (!$config) {
throw new Exception(
pht(
'Unable to load configuration for this EditEngine ("%s").',
get_class($this)));
}
$phid = $request->getValue('objectPHID'); $phid = $request->getValue('objectPHID');
if ($phid) { if ($phid) {
$this->setIsCreate(false); $this->setIsCreate(false);
@ -649,6 +841,8 @@ abstract class PhabricatorApplicationEditEngine extends Phobject {
$object = $this->newEditableObject(); $object = $this->newEditableObject();
} }
$this->validateObject($object);
$fields = $this->buildEditFields($object); $fields = $this->buildEditFields($object);
$types = $this->getAllEditTypesFromFields($fields); $types = $this->getAllEditTypesFromFields($fields);
@ -772,5 +966,46 @@ abstract class PhabricatorApplicationEditEngine extends Phobject {
return $this->getAllEditTypesFromFields($fields); return $this->getAllEditTypesFromFields($fields);
} }
final public static function getAllEditEngines() {
return id(new PhutilClassMapQuery())
->setAncestorClass(__CLASS__)
->setUniqueMethod('getEngineKey')
->execute();
}
final public static function getByKey(PhabricatorUser $viewer, $key) {
return id(new PhabricatorEditEngineQuery())
->setViewer($viewer)
->withEngineKeys(array($key))
->executeOne();
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */
public function getPHID() {
return get_class($this);
}
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
);
}
public function getPolicy($capability) {
switch ($capability) {
case PhabricatorPolicyCapability::CAN_VIEW:
return PhabricatorPolicies::getMostOpenPolicy();
}
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
return false;
}
public function describeAutomaticCapability($capability) {
return null;
}
} }

View file

@ -1,6 +1,6 @@
<?php <?php
abstract class PhabricatorApplicationEditEngineAPIMethod abstract class PhabricatorEditEngineAPIMethod
extends ConduitAPIMethod { extends ConduitAPIMethod {
abstract public function newEditEngine(); abstract public function newEditEngine();

View file

@ -13,7 +13,7 @@ abstract class PhabricatorEditField extends Phobject {
private $metadata = array(); private $metadata = array();
private $description; private $description;
private $editTypeKey; private $editTypeKey;
private $isLocked;
public function setKey($key) { public function setKey($key) {
$this->key = $key; $this->key = $key;
@ -69,7 +69,18 @@ abstract class PhabricatorEditField extends Phobject {
return $this->description; return $this->description;
} }
abstract protected function newControl(); public function setIsLocked($is_locked) {
$this->isLocked = $is_locked;
return $this;
}
public function getIsLocked() {
return $this->isLocked;
}
protected function newControl() {
throw new PhutilMethodNotImplementedException();
}
protected function renderControl() { protected function renderControl() {
$control = $this->newControl(); $control = $this->newControl();
@ -85,6 +96,10 @@ abstract class PhabricatorEditField extends Phobject {
$control->setLabel($this->getLabel()); $control->setLabel($this->getLabel());
} }
if ($this->getIsLocked()) {
$control->setDisabled(true);
}
return $control; return $control;
} }
@ -166,6 +181,15 @@ abstract class PhabricatorEditField extends Phobject {
return $this; return $this;
} }
public function readDefaultValueFromConfiguration($value) {
$this->value = $this->getDefaultValueFromConfiguration($value);
return $this;
}
protected function getDefaultValueFromConfiguration($value) {
return $value;
}
protected function getValueFromObject($object) { protected function getValueFromObject($object) {
if ($this->hasValue) { if ($this->hasValue) {
return $this->value; return $this->value;

View file

@ -0,0 +1,10 @@
<?php
final class PhabricatorInstructionsEditField
extends PhabricatorEditField {
public function appendToForm(AphrontFormView $form) {
return $form->appendRemarkupInstructions($this->getValue());
}
}

View file

@ -815,18 +815,6 @@ abstract class PhabricatorApplicationTransactionEditor
$xactions = $this->filterTransactions($object, $xactions); $xactions = $this->filterTransactions($object, $xactions);
if (!$xactions) {
if ($read_locking) {
$object->endReadLocking();
$read_locking = false;
}
if ($transaction_open) {
$object->killTransaction();
$transaction_open = false;
}
return array();
}
// Now that we've merged, filtered, and combined transactions, check for // Now that we've merged, filtered, and combined transactions, check for
// required capabilities. // required capabilities.
foreach ($xactions as $xaction) { foreach ($xactions as $xaction) {

View file

@ -0,0 +1,78 @@
<?php
final class PhabricatorEditEngineConfigurationEditEngine
extends PhabricatorEditEngine {
const ENGINECONST = 'transactions.editengine.config';
private $targetEngine;
public function setTargetEngine(PhabricatorEditEngine $target_engine) {
$this->targetEngine = $target_engine;
return $this;
}
public function getTargetEngine() {
return $this->targetEngine;
}
public function getEngineName() {
return pht('Edit Configurations');
}
protected function newEditableObject() {
return PhabricatorEditEngineConfiguration::initializeNewConfiguration(
$this->getViewer(),
$this->getTargetEngine());
}
protected function newObjectQuery() {
return id(new PhabricatorEditEngineConfigurationQuery());
}
protected function getObjectCreateTitleText($object) {
return pht('Create New Form');
}
protected function getObjectEditTitleText($object) {
return pht('Edit Form %d: %s', $object->getID(), $object->getDisplayName());
}
protected function getObjectEditShortText($object) {
return pht('Form %d', $object->getID());
}
protected function getObjectCreateShortText() {
return pht('Create Form');
}
protected function getObjectViewURI($object) {
$engine_key = $this->getTargetEngine()->getEngineKey();
$id = $object->getID();
return "/transactions/editengine/{$engine_key}/view/{$id}/";
}
protected function getObjectEditURI($object) {
$engine_key = $this->getTargetEngine()->getEngineKey();
$id = $object->getID();
return "/transactions/editengine/{$engine_key}/edit/{$id}/";
}
protected function getObjectCreateCancelURI($object) {
$engine_key = $this->getTargetEngine()->getEngineKey();
return "/transactions/editengine/{$engine_key}/";
}
protected function buildCustomEditFields($object) {
return array(
id(new PhabricatorTextEditField())
->setKey('name')
->setLabel(pht('Name'))
->setDescription(pht('Name of the form.'))
->setTransactionType(
PhabricatorEditEngineConfigurationTransaction::TYPE_NAME)
->setValue($object->getName()),
);
}
}

View file

@ -0,0 +1,98 @@
<?php
final class PhabricatorEditEngineConfigurationEditor
extends PhabricatorApplicationTransactionEditor {
public function getEditorApplicationClass() {
return 'PhabricatorTransactionsApplication';
}
public function getEditorObjectsDescription() {
return pht('Edit Configurations');
}
public function getTransactionTypes() {
$types = parent::getTransactionTypes();
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
$types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
$types[] = PhabricatorEditEngineConfigurationTransaction::TYPE_NAME;
return $types;
}
protected function validateTransaction(
PhabricatorLiskDAO $object,
$type,
array $xactions) {
$errors = parent::validateTransaction($object, $type, $xactions);
switch ($type) {
case PhabricatorEditEngineConfigurationTransaction::TYPE_NAME:
$missing = $this->validateIsEmptyTextField(
$object->getName(),
$xactions);
if ($missing) {
$error = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Required'),
pht('Form name is required.'),
nonempty(last($xactions), null));
$error->setIsMissingFieldError(true);
$errors[] = $error;
}
break;
}
return $errors;
}
protected function getCustomTransactionOldValue(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case PhabricatorEditEngineConfigurationTransaction::TYPE_NAME:
return $object->getName();
}
}
protected function getCustomTransactionNewValue(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case PhabricatorEditEngineConfigurationTransaction::TYPE_NAME:
return $xaction->getNewValue();
}
}
protected function applyCustomInternalTransaction(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case PhabricatorEditEngineConfigurationTransaction::TYPE_NAME:
$object->setName($xaction->getNewValue());
return;
}
return parent::applyCustomInternalTransaction($object, $xaction);
}
protected function applyCustomExternalTransaction(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case PhabricatorEditEngineConfigurationTransaction::TYPE_NAME:
return;
}
return parent::applyCustomExternalTransaction($object, $xaction);
}
}

View file

@ -0,0 +1,43 @@
<?php
final class PhabricatorEditEngineConfigurationPHIDType
extends PhabricatorPHIDType {
const TYPECONST = 'FORM';
public function getTypeName() {
return pht('Edit Configuration');
}
public function newObject() {
return new PhabricatorEditEngineConfiguration();
}
public function getPHIDTypeApplicationClass() {
return 'PhabricatorTransactionsApplication';
}
protected function buildQueryForObjects(
PhabricatorObjectQuery $object_query,
array $phids) {
return id(new PhabricatorEditEngineConfigurationQuery())
->withPHIDs($phids);
}
public function loadHandles(
PhabricatorHandleQuery $query,
array $handles,
array $objects) {
foreach ($handles as $phid => $handle) {
$config = $objects[$phid];
$id = $config->getID();
$name = $config->getName();
$handle->setName($name);
$handle->setURI($config->getURI());
}
}
}

View file

@ -0,0 +1,183 @@
<?php
final class PhabricatorEditEngineConfigurationQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $phids;
private $engineKeys;
private $builtinKeys;
private $identifiers;
public function withIDs(array $ids) {
$this->ids = $ids;
return $this;
}
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
public function withEngineKeys(array $engine_keys) {
$this->engineKeys = $engine_keys;
return $this;
}
public function withBuiltinKeys(array $builtin_keys) {
$this->builtinKeys = $builtin_keys;
return $this;
}
public function withIdentifiers(array $identifiers) {
$this->identifiers = $identifiers;
return $this;
}
public function newResultObject() {
return new PhabricatorEditEngineConfiguration();
}
protected function loadPage() {
// TODO: The logic here is a little flimsy and won't survive pagination.
// For now, I'm just not bothering with pagination since I believe it will
// take some time before any install manages to produce a large enough
// number of edit forms for any particular engine for the lack of UI
// pagination to become a problem.
$page = $this->loadStandardPage($this->newResultObject());
// Now that we've loaded the real results from the database, we're going
// to load builtins from the edit engines and add them to the list.
$engines = PhabricatorEditEngine::getAllEditEngines();
if ($this->engineKeys) {
$engines = array_select_keys($engines, $this->engineKeys);
}
foreach ($engines as $engine) {
$engine->setViewer($this->getViewer());
}
// List all the builtins which have already been saved to the database as
// real objects.
$concrete = array();
foreach ($page as $config) {
$builtin_key = $config->getBuiltinKey();
if ($builtin_key !== null) {
$engine_key = $config->getEngineKey();
$concrete[$engine_key][$builtin_key] = $config;
}
}
$builtins = array();
foreach ($engines as $engine_key => $engine) {
$engine_builtins = $engine->getBuiltinEngineConfigurations();
foreach ($engine_builtins as $engine_builtin) {
$builtin_key = $engine_builtin->getBuiltinKey();
if (isset($concrete[$engine_key][$builtin_key])) {
continue;
} else {
$builtins[] = $engine_builtin;
}
}
}
foreach ($builtins as $builtin) {
$page[] = $builtin;
}
// Now we have to do some extra filtering to make sure everything we're
// about to return really satisfies the query.
if ($this->ids !== null) {
$ids = array_fuse($this->ids);
foreach ($page as $key => $config) {
if (empty($ids[$config->getID()])) {
unset($page[$key]);
}
}
}
if ($this->phids !== null) {
$phids = array_fuse($this->phids);
foreach ($page as $key => $config) {
if (empty($phids[$config->getPHID()])) {
unset($page[$key]);
}
}
}
if ($this->builtinKeys !== null) {
$builtin_keys = array_fuse($this->builtinKeys);
foreach ($page as $key => $config) {
if (empty($builtin_keys[$config->getBuiltinKey()])) {
unset($page[$key]);
}
}
}
if ($this->identifiers !== null) {
$identifiers = array_fuse($this->identifiers);
foreach ($page as $key => $config) {
if (isset($identifiers[$config->getBuiltinKey()])) {
continue;
}
if (isset($identifiers[$config->getID()])) {
continue;
}
unset($page[$key]);
}
}
return $page;
}
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
$where = parent::buildWhereClauseParts($conn);
if ($this->ids !== null) {
$where[] = qsprintf(
$conn,
'id IN (%Ld)',
$this->ids);
}
if ($this->phids !== null) {
$where[] = qsprintf(
$conn,
'phid IN (%Ls)',
$this->phids);
}
if ($this->engineKeys !== null) {
$where[] = qsprintf(
$conn,
'engineKey IN (%Ls)',
$this->engineKeys);
}
if ($this->builtinKeys !== null) {
$where[] = qsprintf(
$conn,
'builtinKey IN (%Ls)',
$this->builtinKeys);
}
if ($this->identifiers !== null) {
$where[] = qsprintf(
$conn,
'(id IN (%Ls) OR builtinKey IN (%Ls))',
$this->identifiers,
$this->identifiers);
}
return $where;
}
public function getQueryApplicationClass() {
return 'PhabricatorTransactionsApplication';
}
}

View file

@ -0,0 +1,102 @@
<?php
final class PhabricatorEditEngineConfigurationSearchEngine
extends PhabricatorApplicationSearchEngine {
private $engineKey;
public function setEngineKey($engine_key) {
$this->engineKey = $engine_key;
return $this;
}
public function getEngineKey() {
return $this->engineKey;
}
public function canUseInPanelContext() {
return false;
}
public function getResultTypeDescription() {
return pht('Forms');
}
public function getApplicationClassName() {
return 'PhabricatorTransactionsApplication';
}
public function newQuery() {
return id(new PhabricatorEditEngineConfigurationQuery())
->withEngineKeys(array($this->getEngineKey()));
}
protected function buildQueryFromParameters(array $map) {
$query = $this->newQuery();
return $query;
}
protected function buildCustomSearchFields() {
return array();
}
protected function getDefaultFieldOrder() {
return array();
}
protected function getURI($path) {
return '/transactions/editengine/'.$this->getEngineKey().'/'.$path;
}
protected function getBuiltinQueryNames() {
$names = array(
'all' => pht('All Forms'),
);
return $names;
}
public function buildSavedQueryFromBuiltin($query_key) {
$query = $this->newSavedQuery();
$query->setQueryKey($query_key);
switch ($query_key) {
case 'all':
return $query;
}
return parent::buildSavedQueryFromBuiltin($query_key);
}
protected function renderResultList(
array $configs,
PhabricatorSavedQuery $query,
array $handles) {
assert_instances_of($configs, 'PhabricatorEditEngineConfiguration');
$viewer = $this->requireViewer();
$engine_key = $this->getEngineKey();
$list = id(new PHUIObjectItemListView())
->setUser($viewer);
foreach ($configs as $config) {
$item = id(new PHUIObjectItemView())
->setHeader($config->getDisplayName());
$id = $config->getID();
if ($id) {
$item->setObjectName(pht('Form %d', $id));
$key = $id;
} else {
$item->setObjectName(pht('Builtin'));
$key = $config->getBuiltinKey();
}
$item->setHref("/transactions/editengine/{$engine_key}/view/{$key}/");
$list->addItem($item);
}
return id(new PhabricatorApplicationSearchResultView())
->setObjectList($list);
}
}

View file

@ -0,0 +1,10 @@
<?php
final class PhabricatorEditEngineConfigurationTransactionQuery
extends PhabricatorApplicationTransactionQuery {
public function getTemplateApplicationTransaction() {
return new PhabricatorEditEngineConfigurationTransaction();
}
}

View file

@ -0,0 +1,31 @@
<?php
final class PhabricatorEditEngineQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $engineKeys;
public function withEngineKeys(array $keys) {
$this->engineKeys = $keys;
return $this;
}
protected function loadPage() {
$engines = PhabricatorEditEngine::getAllEditEngines();
if ($this->engineKeys !== null) {
$engines = array_select_keys($engines, $this->engineKeys);
}
return $engines;
}
public function getQueryApplicationClass() {
return 'PhabricatorTransactionsApplication';
}
protected function getResultCursor($object) {
return null;
}
}

View file

@ -0,0 +1,78 @@
<?php
final class PhabricatorEditEngineSearchEngine
extends PhabricatorApplicationSearchEngine {
public function getResultTypeDescription() {
return pht('Edit Engines');
}
public function getApplicationClassName() {
return 'PhabricatorTransactionsApplication';
}
public function newQuery() {
return id(new PhabricatorEditEngineQuery());
}
protected function buildQueryFromParameters(array $map) {
$query = $this->newQuery();
return $query;
}
protected function buildCustomSearchFields() {
return array();
}
protected function getDefaultFieldOrder() {
return array();
}
protected function getURI($path) {
return '/transactions/editengine/'.$path;
}
protected function getBuiltinQueryNames() {
$names = array(
'all' => pht('All Edit Engines'),
);
return $names;
}
public function buildSavedQueryFromBuiltin($query_key) {
$query = $this->newSavedQuery();
$query->setQueryKey($query_key);
switch ($query_key) {
case 'all':
return $query;
}
return parent::buildSavedQueryFromBuiltin($query_key);
}
protected function renderResultList(
array $engines,
PhabricatorSavedQuery $query,
array $handles) {
assert_instances_of($engines, 'PhabricatorEditEngine');
$viewer = $this->requireViewer();
$list = id(new PHUIObjectItemListView())
->setUser($viewer);
foreach ($engines as $engine) {
$engine_key = $engine->getEngineKey();
$query_uri = "/transactions/editengine/{$engine_key}/";
$item = id(new PHUIObjectItemView())
->setHeader($engine->getEngineName())
->setHref($query_uri);
$list->addItem($item);
}
return id(new PhabricatorApplicationSearchResultView())
->setObjectList($list);
}
}

View file

@ -0,0 +1,211 @@
<?php
final class PhabricatorEditEngineConfiguration
extends PhabricatorSearchDAO
implements
PhabricatorApplicationTransactionInterface,
PhabricatorPolicyInterface {
protected $engineKey;
protected $builtinKey;
protected $name;
protected $viewPolicy;
protected $editPolicy;
protected $properties = array();
protected $isDisabled = 0;
protected $isDefault = 0;
private $engine = self::ATTACHABLE;
public function getTableName() {
return 'search_editengineconfiguration';
}
public static function initializeNewConfiguration(
PhabricatorUser $actor,
PhabricatorEditEngine $engine) {
// TODO: This should probably be controlled by a new defualt capability.
$edit_policy = PhabricatorPolicies::POLICY_ADMIN;
return id(new PhabricatorEditEngineConfiguration())
->setEngineKey($engine->getEngineKey())
->attachEngine($engine)
->setViewPolicy(PhabricatorPolicies::getMostOpenPolicy())
->setEditPolicy($edit_policy);
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(
PhabricatorEditEngineConfigurationPHIDType::TYPECONST);
}
protected function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
self::CONFIG_SERIALIZATION => array(
'properties' => self::SERIALIZATION_JSON,
),
self::CONFIG_COLUMN_SCHEMA => array(
'engineKey' => 'text64',
'builtinKey' => 'text64?',
'name' => 'text255',
'isDisabled' => 'bool',
'isDefault' => 'bool',
),
self::CONFIG_KEY_SCHEMA => array(
'key_engine' => array(
'columns' => array('engineKey', 'builtinKey'),
'unique' => true,
),
'key_default' => array(
'columns' => array('engineKey', 'isDefault', 'isDisabled'),
),
),
) + parent::getConfiguration();
}
public function getProperty($key, $default = null) {
return idx($this->properties, $key, $default);
}
public function setProperty($key, $value) {
$this->properties[$key] = $value;
return $this;
}
public function attachEngine(PhabricatorEditEngine $engine) {
$this->engine = $engine;
return $this;
}
public function getEngine() {
return $this->assertAttached($this->engine);
}
public function applyConfigurationToFields(
PhabricatorEditEngine $engine,
array $fields) {
$fields = mpull($fields, null, 'getKey');
$values = $this->getProperty('defaults', array());
foreach ($fields as $key => $field) {
if ($engine->getIsCreate()) {
if (array_key_exists($key, $values)) {
$field->readDefaultValueFromConfiguration($values[$key]);
}
}
}
$fields = $this->reorderFields($fields);
$head_instructions = $this->getProperty('instructions.head');
if (strlen($head_instructions)) {
$fields = array(
'config.instructions.head' => id(new PhabricatorInstructionsEditField())
->setKey('config.instructions.head')
->setValue($head_instructions),
) + $fields;
}
return $fields;
}
private function reorderFields(array $fields) {
$keys = array();
$fields = array_select_keys($fields, $keys) + $fields;
// Now, move locked fields to the bottom.
$head = array();
$tail = array();
foreach ($fields as $key => $field) {
if (!$field->getIsLocked()) {
$head[$key] = $field;
} else {
$tail[$key] = $field;
}
}
return $head + $tail;
}
public function getURI() {
$engine_key = $this->getEngineKey();
$key = $this->getIdentifier();
return "/transactions/editengine/{$engine_key}/view/{$key}/";
}
public function getIdentifier() {
$key = $this->getID();
if (!$key) {
$key = $this->getBuiltinKey();
}
return $key;
}
public function getDisplayName() {
$name = $this->getName();
if (strlen($name)) {
return $name;
}
$builtin = $this->getBuiltinKey();
if ($builtin !== null) {
return pht('Builtin Form "%s"', $builtin);
}
return pht('Untitled Form');
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
);
}
public function getPolicy($capability) {
switch ($capability) {
case PhabricatorPolicyCapability::CAN_VIEW:
return $this->getViewPolicy();
case PhabricatorPolicyCapability::CAN_EDIT:
return $this->getEditPolicy();
}
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
return false;
}
public function describeAutomaticCapability($capability) {
return null;
}
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
public function getApplicationTransactionEditor() {
return new PhabricatorEditEngineConfigurationEditor();
}
public function getApplicationTransactionObject() {
return $this;
}
public function getApplicationTransactionTemplate() {
return new PhabricatorEditEngineConfigurationTransaction();
}
public function willRenderTimeline(
PhabricatorApplicationTransactionView $timeline,
AphrontRequest $request) {
return $timeline;
}
}

View file

@ -0,0 +1,20 @@
<?php
final class PhabricatorEditEngineConfigurationTransaction
extends PhabricatorApplicationTransaction {
const TYPE_NAME = 'editengine.config.name';
public function getApplicationName() {
return 'search';
}
public function getApplicationTransactionType() {
return PhabricatorEditEngineConfigurationPHIDType::TYPECONST;
}
public function getApplicationTransactionCommentObject() {
return null;
}
}

View file

@ -4,7 +4,7 @@
* Renders the "HTTP Parameters" help page for edit engines. * Renders the "HTTP Parameters" help page for edit engines.
* *
* This page has a ton of text and specialized rendering on it, this class * This page has a ton of text and specialized rendering on it, this class
* just pulls it out of the main @{class:PhabricatorApplicationEditEngine}. * just pulls it out of the main @{class:PhabricatorEditEngine}.
*/ */
final class PhabricatorApplicationEditHTTPParameterHelpView final class PhabricatorApplicationEditHTTPParameterHelpView
extends AphrontView { extends AphrontView {