@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator

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

+1536 -31
+17
resources/sql/autopatches/20151106.editengine.1.table.sql
··· 1 + CREATE TABLE {$NAMESPACE}_search.search_editengineconfiguration ( 2 + id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, 3 + phid VARBINARY(64) NOT NULL, 4 + engineKey VARCHAR(64) NOT NULL COLLATE {$COLLATE_TEXT}, 5 + builtinKey VARCHAR(64) COLLATE {$COLLATE_TEXT}, 6 + name VARCHAR(255) NOT NULL COLLATE {$COLLATE_TEXT}, 7 + viewPolicy VARBINARY(64) NOT NULL, 8 + editPolicy VARBINARY(64) NOT NULL, 9 + properties LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT}, 10 + isDisabled BOOL NOT NULL DEFAULT 0, 11 + isDefault BOOL NOT NULL DEFAULT 0, 12 + dateCreated INT UNSIGNED NOT NULL, 13 + dateModified INT UNSIGNED NOT NULL, 14 + UNIQUE KEY `key_phid` (phid), 15 + UNIQUE KEY `key_engine` (engineKey, builtinKey), 16 + KEY `key_default` (engineKey, isDefault, isDisabled) 17 + ) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
+19
resources/sql/autopatches/20151106.editengine.2.xactions.sql
··· 1 + CREATE TABLE {$NAMESPACE}_search.search_editengineconfigurationtransaction ( 2 + id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, 3 + phid VARBINARY(64) NOT NULL, 4 + authorPHID VARBINARY(64) NOT NULL, 5 + objectPHID VARBINARY(64) NOT NULL, 6 + viewPolicy VARBINARY(64) NOT NULL, 7 + editPolicy VARBINARY(64) NOT NULL, 8 + commentPHID VARBINARY(64) DEFAULT NULL, 9 + commentVersion INT UNSIGNED NOT NULL, 10 + transactionType VARCHAR(32) COLLATE {$COLLATE_TEXT} NOT NULL, 11 + oldValue LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL, 12 + newValue LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL, 13 + contentSource LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL, 14 + metadata LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL, 15 + dateCreated INT UNSIGNED NOT NULL, 16 + dateModified INT UNSIGNED NOT NULL, 17 + UNIQUE KEY `key_phid` (`phid`), 18 + KEY `key_object` (`objectPHID`) 19 + ) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
+47 -6
src/__phutil_library_map__.php
··· 1579 1579 'PhabricatorApplicationDatasource' => 'applications/meta/typeahead/PhabricatorApplicationDatasource.php', 1580 1580 'PhabricatorApplicationDetailViewController' => 'applications/meta/controller/PhabricatorApplicationDetailViewController.php', 1581 1581 'PhabricatorApplicationEditController' => 'applications/meta/controller/PhabricatorApplicationEditController.php', 1582 - 'PhabricatorApplicationEditEngine' => 'applications/transactions/editengine/PhabricatorApplicationEditEngine.php', 1583 - 'PhabricatorApplicationEditEngineAPIMethod' => 'applications/transactions/editengine/PhabricatorApplicationEditEngineAPIMethod.php', 1584 1582 'PhabricatorApplicationEditHTTPParameterHelpView' => 'applications/transactions/view/PhabricatorApplicationEditHTTPParameterHelpView.php', 1585 1583 'PhabricatorApplicationEmailCommandsController' => 'applications/meta/controller/PhabricatorApplicationEmailCommandsController.php', 1586 1584 'PhabricatorApplicationLaunchView' => 'applications/meta/view/PhabricatorApplicationLaunchView.php', ··· 2110 2108 'PhabricatorEdgeTestCase' => 'infrastructure/edges/__tests__/PhabricatorEdgeTestCase.php', 2111 2109 'PhabricatorEdgeType' => 'infrastructure/edges/type/PhabricatorEdgeType.php', 2112 2110 'PhabricatorEdgeTypeTestCase' => 'infrastructure/edges/type/__tests__/PhabricatorEdgeTypeTestCase.php', 2111 + 'PhabricatorEditEngine' => 'applications/transactions/editengine/PhabricatorEditEngine.php', 2112 + 'PhabricatorEditEngineAPIMethod' => 'applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php', 2113 + 'PhabricatorEditEngineConfiguration' => 'applications/transactions/storage/PhabricatorEditEngineConfiguration.php', 2114 + 'PhabricatorEditEngineConfigurationEditController' => 'applications/transactions/controller/PhabricatorEditEngineConfigurationEditController.php', 2115 + 'PhabricatorEditEngineConfigurationEditEngine' => 'applications/transactions/editor/PhabricatorEditEngineConfigurationEditEngine.php', 2116 + 'PhabricatorEditEngineConfigurationEditor' => 'applications/transactions/editor/PhabricatorEditEngineConfigurationEditor.php', 2117 + 'PhabricatorEditEngineConfigurationListController' => 'applications/transactions/controller/PhabricatorEditEngineConfigurationListController.php', 2118 + 'PhabricatorEditEngineConfigurationPHIDType' => 'applications/transactions/phid/PhabricatorEditEngineConfigurationPHIDType.php', 2119 + 'PhabricatorEditEngineConfigurationQuery' => 'applications/transactions/query/PhabricatorEditEngineConfigurationQuery.php', 2120 + 'PhabricatorEditEngineConfigurationSaveController' => 'applications/transactions/controller/PhabricatorEditEngineConfigurationSaveController.php', 2121 + 'PhabricatorEditEngineConfigurationSearchEngine' => 'applications/transactions/query/PhabricatorEditEngineConfigurationSearchEngine.php', 2122 + 'PhabricatorEditEngineConfigurationTransaction' => 'applications/transactions/storage/PhabricatorEditEngineConfigurationTransaction.php', 2123 + 'PhabricatorEditEngineConfigurationTransactionQuery' => 'applications/transactions/query/PhabricatorEditEngineConfigurationTransactionQuery.php', 2124 + 'PhabricatorEditEngineConfigurationViewController' => 'applications/transactions/controller/PhabricatorEditEngineConfigurationViewController.php', 2125 + 'PhabricatorEditEngineController' => 'applications/transactions/controller/PhabricatorEditEngineController.php', 2126 + 'PhabricatorEditEngineListController' => 'applications/transactions/controller/PhabricatorEditEngineListController.php', 2127 + 'PhabricatorEditEngineQuery' => 'applications/transactions/query/PhabricatorEditEngineQuery.php', 2128 + 'PhabricatorEditEngineSearchEngine' => 'applications/transactions/query/PhabricatorEditEngineSearchEngine.php', 2113 2129 'PhabricatorEditField' => 'applications/transactions/editfield/PhabricatorEditField.php', 2114 2130 'PhabricatorEditType' => 'applications/transactions/edittype/PhabricatorEditType.php', 2115 2131 'PhabricatorEditor' => 'infrastructure/PhabricatorEditor.php', ··· 2294 2310 'PhabricatorInlineCommentInterface' => 'infrastructure/diff/interface/PhabricatorInlineCommentInterface.php', 2295 2311 'PhabricatorInlineCommentPreviewController' => 'infrastructure/diff/PhabricatorInlineCommentPreviewController.php', 2296 2312 'PhabricatorInlineSummaryView' => 'infrastructure/diff/view/PhabricatorInlineSummaryView.php', 2313 + 'PhabricatorInstructionsEditField' => 'applications/transactions/editfield/PhabricatorInstructionsEditField.php', 2297 2314 'PhabricatorInternationalizationManagementExtractWorkflow' => 'infrastructure/internationalization/management/PhabricatorInternationalizationManagementExtractWorkflow.php', 2298 2315 'PhabricatorInternationalizationManagementWorkflow' => 'infrastructure/internationalization/management/PhabricatorInternationalizationManagementWorkflow.php', 2299 2316 'PhabricatorInvalidConfigSetupCheck' => 'applications/config/check/PhabricatorInvalidConfigSetupCheck.php', ··· 5500 5517 'PasteCreateMailReceiver' => 'PhabricatorMailReceiver', 5501 5518 'PasteDefaultEditCapability' => 'PhabricatorPolicyCapability', 5502 5519 'PasteDefaultViewCapability' => 'PhabricatorPolicyCapability', 5503 - 'PasteEditConduitAPIMethod' => 'PhabricatorApplicationEditEngineAPIMethod', 5520 + 'PasteEditConduitAPIMethod' => 'PhabricatorEditEngineAPIMethod', 5504 5521 'PasteEmbedView' => 'AphrontView', 5505 5522 'PasteInfoConduitAPIMethod' => 'PasteConduitAPIMethod', 5506 5523 'PasteMailReceiver' => 'PhabricatorObjectMailReceiver', ··· 5544 5561 'PhabricatorApplicationDatasource' => 'PhabricatorTypeaheadDatasource', 5545 5562 'PhabricatorApplicationDetailViewController' => 'PhabricatorApplicationsController', 5546 5563 'PhabricatorApplicationEditController' => 'PhabricatorApplicationsController', 5547 - 'PhabricatorApplicationEditEngine' => 'Phobject', 5548 - 'PhabricatorApplicationEditEngineAPIMethod' => 'ConduitAPIMethod', 5549 5564 'PhabricatorApplicationEditHTTPParameterHelpView' => 'AphrontView', 5550 5565 'PhabricatorApplicationEmailCommandsController' => 'PhabricatorApplicationsController', 5551 5566 'PhabricatorApplicationLaunchView' => 'AphrontTagView', ··· 6172 6187 'PhabricatorEdgeTestCase' => 'PhabricatorTestCase', 6173 6188 'PhabricatorEdgeType' => 'Phobject', 6174 6189 'PhabricatorEdgeTypeTestCase' => 'PhabricatorTestCase', 6190 + 'PhabricatorEditEngine' => array( 6191 + 'Phobject', 6192 + 'PhabricatorPolicyInterface', 6193 + ), 6194 + 'PhabricatorEditEngineAPIMethod' => 'ConduitAPIMethod', 6195 + 'PhabricatorEditEngineConfiguration' => array( 6196 + 'PhabricatorSearchDAO', 6197 + 'PhabricatorApplicationTransactionInterface', 6198 + 'PhabricatorPolicyInterface', 6199 + ), 6200 + 'PhabricatorEditEngineConfigurationEditController' => 'PhabricatorEditEngineController', 6201 + 'PhabricatorEditEngineConfigurationEditEngine' => 'PhabricatorEditEngine', 6202 + 'PhabricatorEditEngineConfigurationEditor' => 'PhabricatorApplicationTransactionEditor', 6203 + 'PhabricatorEditEngineConfigurationListController' => 'PhabricatorEditEngineController', 6204 + 'PhabricatorEditEngineConfigurationPHIDType' => 'PhabricatorPHIDType', 6205 + 'PhabricatorEditEngineConfigurationQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 6206 + 'PhabricatorEditEngineConfigurationSaveController' => 'PhabricatorEditEngineController', 6207 + 'PhabricatorEditEngineConfigurationSearchEngine' => 'PhabricatorApplicationSearchEngine', 6208 + 'PhabricatorEditEngineConfigurationTransaction' => 'PhabricatorApplicationTransaction', 6209 + 'PhabricatorEditEngineConfigurationTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 6210 + 'PhabricatorEditEngineConfigurationViewController' => 'PhabricatorEditEngineController', 6211 + 'PhabricatorEditEngineController' => 'PhabricatorApplicationTransactionController', 6212 + 'PhabricatorEditEngineListController' => 'PhabricatorEditEngineController', 6213 + 'PhabricatorEditEngineQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 6214 + 'PhabricatorEditEngineSearchEngine' => 'PhabricatorApplicationSearchEngine', 6175 6215 'PhabricatorEditField' => 'Phobject', 6176 6216 'PhabricatorEditType' => 'Phobject', 6177 6217 'PhabricatorEditor' => 'Phobject', ··· 6392 6432 'PhabricatorInlineCommentInterface' => 'PhabricatorMarkupInterface', 6393 6433 'PhabricatorInlineCommentPreviewController' => 'PhabricatorController', 6394 6434 'PhabricatorInlineSummaryView' => 'AphrontView', 6435 + 'PhabricatorInstructionsEditField' => 'PhabricatorEditField', 6395 6436 'PhabricatorInternationalizationManagementExtractWorkflow' => 'PhabricatorInternationalizationManagementWorkflow', 6396 6437 'PhabricatorInternationalizationManagementWorkflow' => 'PhabricatorManagementWorkflow', 6397 6438 'PhabricatorInvalidConfigSetupCheck' => 'PhabricatorSetupCheck', ··· 6710 6751 'PhabricatorPasteController' => 'PhabricatorController', 6711 6752 'PhabricatorPasteDAO' => 'PhabricatorLiskDAO', 6712 6753 'PhabricatorPasteEditController' => 'PhabricatorPasteController', 6713 - 'PhabricatorPasteEditEngine' => 'PhabricatorApplicationEditEngine', 6754 + 'PhabricatorPasteEditEngine' => 'PhabricatorEditEngine', 6714 6755 'PhabricatorPasteEditor' => 'PhabricatorApplicationTransactionEditor', 6715 6756 'PhabricatorPasteListController' => 'PhabricatorPasteController', 6716 6757 'PhabricatorPastePastePHIDType' => 'PhabricatorPHIDType',
+5 -1
src/applications/base/PhabricatorApplication.php
··· 635 635 return array(); 636 636 } 637 637 638 - protected function getEditRoutePattern($base) { 638 + protected function getEditRoutePattern($base = null) { 639 639 return $base.'(?:(?P<id>[0-9]\d*)/)?(?:(?P<editAction>parameters)/)?'; 640 + } 641 + 642 + protected function getQueryRoutePattern($base = null) { 643 + return $base.'(?:query/(?P<queryKey>[^/]+)/)?'; 640 644 } 641 645 642 646 }
+1 -1
src/applications/paste/conduit/PasteEditConduitAPIMethod.php
··· 1 1 <?php 2 2 3 3 final class PasteEditConduitAPIMethod 4 - extends PhabricatorApplicationEditEngineAPIMethod { 4 + extends PhabricatorEditEngineAPIMethod { 5 5 6 6 public function getAPIMethodName() { 7 7 return 'paste.edit';
+8 -2
src/applications/paste/editor/PhabricatorPasteEditEngine.php
··· 1 1 <?php 2 2 3 3 final class PhabricatorPasteEditEngine 4 - extends PhabricatorApplicationEditEngine { 4 + extends PhabricatorEditEngine { 5 + 6 + const ENGINECONST = 'paste.paste'; 7 + 8 + public function getEngineName() { 9 + return pht('Pastes'); 10 + } 5 11 6 12 protected function newEditableObject() { 7 13 return PhabricatorPaste::initializeNewPaste($this->getViewer()); ··· 24 30 return $object->getMonogram(); 25 31 } 26 32 27 - protected function getObjectCreateShortText($object) { 33 + protected function getObjectCreateShortText() { 28 34 return pht('Create Paste'); 29 35 } 30 36
+14
src/applications/transactions/application/PhabricatorTransactionsApplication.php
··· 33 33 => 'PhabricatorApplicationTransactionShowOlderController', 34 34 '(?P<value>old|new)/(?<phid>[^/]+)/' 35 35 => 'PhabricatorApplicationTransactionValueController', 36 + 'editengine/' => array( 37 + $this->getQueryRoutePattern() 38 + => 'PhabricatorEditEngineListController', 39 + '(?P<engineKey>[^/]+)/' => array( 40 + $this->getQueryRoutePattern() => 41 + 'PhabricatorEditEngineConfigurationListController', 42 + $this->getEditRoutePattern('edit/') => 43 + 'PhabricatorEditEngineConfigurationEditController', 44 + 'view/(?P<key>[^/]+)/' => 45 + 'PhabricatorEditEngineConfigurationViewController', 46 + 'save/(?P<key>[^/]+)/' => 47 + 'PhabricatorEditEngineConfigurationSaveController', 48 + ), 49 + ), 36 50 ), 37 51 ); 38 52 }
+26
src/applications/transactions/controller/PhabricatorEditEngineConfigurationEditController.php
··· 1 + <?php 2 + 3 + final class PhabricatorEditEngineConfigurationEditController 4 + extends PhabricatorEditEngineController { 5 + 6 + public function handleRequest(AphrontRequest $request) { 7 + $viewer = $this->getViewer(); 8 + 9 + $target_engine_key = $request->getURIData('engineKey'); 10 + 11 + $target_engine = PhabricatorEditEngine::getByKey( 12 + $viewer, 13 + $target_engine_key); 14 + if (!$target_engine) { 15 + return new Aphront404Response(); 16 + } 17 + 18 + $this->setEngineKey($target_engine->getEngineKey()); 19 + 20 + return id(new PhabricatorEditEngineConfigurationEditEngine()) 21 + ->setTargetEngine($target_engine) 22 + ->setController($this) 23 + ->buildResponse(); 24 + } 25 + 26 + }
+34
src/applications/transactions/controller/PhabricatorEditEngineConfigurationListController.php
··· 1 + <?php 2 + 3 + final class PhabricatorEditEngineConfigurationListController 4 + extends PhabricatorEditEngineController { 5 + 6 + public function shouldAllowPublic() { 7 + return true; 8 + } 9 + 10 + public function handleRequest(AphrontRequest $request) { 11 + $this->setEngineKey($request->getURIData('engineKey')); 12 + 13 + return id(new PhabricatorEditEngineConfigurationSearchEngine()) 14 + ->setController($this) 15 + ->setEngineKey($this->getEngineKey()) 16 + ->buildResponse(); 17 + } 18 + 19 + protected function buildApplicationCrumbs() { 20 + $crumbs = parent::buildApplicationCrumbs(); 21 + 22 + $engine_key = $this->getEngineKey(); 23 + $edit_uri = "/transactions/editengine/{$engine_key}/edit/"; 24 + 25 + $crumbs->addAction( 26 + id(new PHUIListItemView()) 27 + ->setName(pht('New Form')) 28 + ->setHref($edit_uri) 29 + ->setIcon('fa-plus-square')); 30 + 31 + return $crumbs; 32 + } 33 + 34 + }
+55
src/applications/transactions/controller/PhabricatorEditEngineConfigurationSaveController.php
··· 1 + <?php 2 + 3 + final class PhabricatorEditEngineConfigurationSaveController 4 + extends PhabricatorEditEngineController { 5 + 6 + public function handleRequest(AphrontRequest $request) { 7 + $engine_key = $request->getURIData('engineKey'); 8 + $this->setEngineKey($engine_key); 9 + 10 + $key = $request->getURIData('key'); 11 + $viewer = $this->getViewer(); 12 + 13 + $config = id(new PhabricatorEditEngineConfigurationQuery()) 14 + ->setViewer($viewer) 15 + ->withEngineKeys(array($engine_key)) 16 + ->withIdentifiers(array($key)) 17 + ->executeOne(); 18 + if (!$config) { 19 + return id(new Aphront404Response()); 20 + } 21 + 22 + $view_uri = $config->getURI(); 23 + 24 + if ($config->getID()) { 25 + return $this->newDialog() 26 + ->setTitle(pht('Already Editable')) 27 + ->appendParagraph( 28 + pht('This form configuration is already editable.')) 29 + ->addCancelButton($view_uri); 30 + } 31 + 32 + if ($request->isFormPost()) { 33 + $editor = id(new PhabricatorEditEngineConfigurationEditor()) 34 + ->setActor($viewer) 35 + ->setContentSourceFromRequest($request) 36 + ->setContinueOnNoEffect(true); 37 + 38 + $editor->applyTransactions($config, array()); 39 + 40 + return id(new AphrontRedirectResponse()) 41 + ->setURI($config->getURI()); 42 + } 43 + 44 + // TODO: Explain what this means in more detail once the implications are 45 + // more clear, or just link to some docs or something. 46 + 47 + return $this->newDialog() 48 + ->setTitle(pht('Make Builtin Editable')) 49 + ->appendParagraph( 50 + pht('Make this builtin form editable?')) 51 + ->addSubmitButton(pht('Make Editable')) 52 + ->addCancelButton($view_uri); 53 + } 54 + 55 + }
+125
src/applications/transactions/controller/PhabricatorEditEngineConfigurationViewController.php
··· 1 + <?php 2 + 3 + final class PhabricatorEditEngineConfigurationViewController 4 + extends PhabricatorEditEngineController { 5 + 6 + public function shouldAllowPublic() { 7 + return true; 8 + } 9 + 10 + public function handleRequest(AphrontRequest $request) { 11 + $engine_key = $request->getURIData('engineKey'); 12 + $this->setEngineKey($engine_key); 13 + 14 + $key = $request->getURIData('key'); 15 + $viewer = $this->getViewer(); 16 + 17 + $config = id(new PhabricatorEditEngineConfigurationQuery()) 18 + ->setViewer($viewer) 19 + ->withEngineKeys(array($engine_key)) 20 + ->withIdentifiers(array($key)) 21 + ->executeOne(); 22 + if (!$config) { 23 + return id(new Aphront404Response()); 24 + } 25 + 26 + $is_concrete = (bool)$config->getID(); 27 + 28 + $actions = $this->buildActionView($config); 29 + 30 + $properties = $this->buildPropertyView($config) 31 + ->setActionList($actions); 32 + 33 + $header = id(new PHUIHeaderView()) 34 + ->setUser($viewer) 35 + ->setPolicyObject($config) 36 + ->setHeader(pht('Edit Form: %s', $config->getDisplayName())); 37 + 38 + $box = id(new PHUIObjectBoxView()) 39 + ->setHeader($header) 40 + ->addPropertyList($properties); 41 + 42 + $crumbs = $this->buildApplicationCrumbs(); 43 + 44 + if ($is_concrete) { 45 + $crumbs->addTextCrumb(pht('Form %d', $config->getID())); 46 + } else { 47 + $crumbs->addTextCrumb(pht('Builtin')); 48 + } 49 + 50 + if ($is_concrete) { 51 + $timeline = $this->buildTransactionTimeline( 52 + $config, 53 + new PhabricatorEditEngineConfigurationTransactionQuery()); 54 + 55 + $timeline->setShouldTerminate(true); 56 + } else { 57 + $timeline = null; 58 + } 59 + 60 + return $this->newPage() 61 + ->setCrumbs($crumbs) 62 + ->appendChild( 63 + array( 64 + $box, 65 + $timeline, 66 + )); 67 + } 68 + 69 + private function buildActionView( 70 + PhabricatorEditEngineConfiguration $config) { 71 + $viewer = $this->getViewer(); 72 + $engine_key = $this->getEngineKey(); 73 + 74 + $can_edit = PhabricatorPolicyFilter::hasCapability( 75 + $viewer, 76 + $config, 77 + PhabricatorPolicyCapability::CAN_EDIT); 78 + 79 + $view = id(new PhabricatorActionListView()) 80 + ->setUser($viewer); 81 + 82 + $key = $config->getIdentifier(); 83 + 84 + $base_uri = "/transactions/editengine/{$engine_key}"; 85 + 86 + $is_concrete = (bool)$config->getID(); 87 + if (!$is_concrete) { 88 + $save_uri = "{$base_uri}/save/{$key}/"; 89 + 90 + $view->addAction( 91 + id(new PhabricatorActionView()) 92 + ->setName(pht('Make Editable')) 93 + ->setIcon('fa-pencil') 94 + ->setDisabled(!$can_edit) 95 + ->setWorkflow(true) 96 + ->setHref($save_uri)); 97 + 98 + $can_edit = false; 99 + } else { 100 + $edit_uri = "{$base_uri}/edit/{$key}/"; 101 + $view->addAction( 102 + id(new PhabricatorActionView()) 103 + ->setName(pht('Edit Form Configuration')) 104 + ->setIcon('fa-pencil') 105 + ->setDisabled(!$can_edit) 106 + ->setWorkflow(!$can_edit) 107 + ->setHref($edit_uri)); 108 + } 109 + 110 + return $view; 111 + } 112 + 113 + private function buildPropertyView( 114 + PhabricatorEditEngineConfiguration $config) { 115 + $viewer = $this->getViewer(); 116 + 117 + $properties = id(new PHUIPropertyListView()) 118 + ->setUser($viewer) 119 + ->setObject($config); 120 + 121 + return $properties; 122 + } 123 + 124 + 125 + }
+37
src/applications/transactions/controller/PhabricatorEditEngineController.php
··· 1 + <?php 2 + 3 + abstract class PhabricatorEditEngineController 4 + extends PhabricatorApplicationTransactionController { 5 + 6 + private $engineKey; 7 + 8 + public function setEngineKey($engine_key) { 9 + $this->engineKey = $engine_key; 10 + return $this; 11 + } 12 + 13 + public function getEngineKey() { 14 + return $this->engineKey; 15 + } 16 + 17 + protected function buildApplicationCrumbs() { 18 + $crumbs = parent::buildApplicationCrumbs(); 19 + 20 + $crumbs->addTextCrumb(pht('Edit Engines'), '/transactions/editengine/'); 21 + 22 + $engine_key = $this->getEngineKey(); 23 + if ($engine_key !== null) { 24 + $engine = PhabricatorEditEngine::getByKey( 25 + $this->getViewer(), 26 + $engine_key); 27 + if ($engine) { 28 + $crumbs->addTextCrumb( 29 + $engine->getEngineName(), 30 + "/transactions/editengine/{$engine_key}/"); 31 + } 32 + } 33 + 34 + return $crumbs; 35 + } 36 + 37 + }
+16
src/applications/transactions/controller/PhabricatorEditEngineListController.php
··· 1 + <?php 2 + 3 + final class PhabricatorEditEngineListController 4 + extends PhabricatorEditEngineController { 5 + 6 + public function shouldAllowPublic() { 7 + return true; 8 + } 9 + 10 + public function handleRequest(AphrontRequest $request) { 11 + return id(new PhabricatorEditEngineSearchEngine()) 12 + ->setController($this) 13 + ->buildResponse(); 14 + } 15 + 16 + }
+240 -5
src/applications/transactions/editengine/PhabricatorApplicationEditEngine.php src/applications/transactions/editengine/PhabricatorEditEngine.php
··· 4 4 /** 5 5 * @task fields Managing Fields 6 6 * @task text Display Text 7 + * @task config Edit Engine Configuration 7 8 * @task uri Managing URIs 8 9 * @task load Creating and Loading Objects 9 10 * @task web Responding to Web Requests ··· 11 12 * @task http Responding to HTTP Parameter Requests 12 13 * @task conduit Responding to Conduit Requests 13 14 */ 14 - abstract class PhabricatorApplicationEditEngine extends Phobject { 15 + abstract class PhabricatorEditEngine 16 + extends Phobject 17 + implements PhabricatorPolicyInterface { 18 + 19 + const EDITENGINECONFIG_DEFAULT = 'default'; 15 20 16 21 private $viewer; 17 22 private $controller; 18 23 private $isCreate; 24 + private $editEngineConfiguration; 19 25 20 26 final public function setViewer(PhabricatorUser $viewer) { 21 27 $this->viewer = $viewer; ··· 34 40 35 41 final public function getController() { 36 42 return $this->controller; 43 + } 44 + 45 + final public function getEngineKey() { 46 + return $this->getPhobjectClassConstant('ENGINECONST', 64); 37 47 } 38 48 39 49 ··· 184 194 } 185 195 } 186 196 197 + $config = $this->getEditEngineConfiguration(); 198 + $fields = $config->applyConfigurationToFields($this, $fields); 199 + 187 200 foreach ($fields as $field) { 188 201 $field 189 202 ->setViewer($viewer) ··· 200 213 /** 201 214 * @task text 202 215 */ 216 + abstract public function getEngineName(); 217 + 218 + 219 + /** 220 + * @task text 221 + */ 203 222 abstract protected function getObjectCreateTitleText($object); 204 223 205 224 ··· 212 231 /** 213 232 * @task text 214 233 */ 215 - abstract protected function getObjectCreateShortText($object); 234 + abstract protected function getObjectCreateShortText(); 216 235 217 236 218 237 /** ··· 237 256 } 238 257 239 258 259 + /* -( Edit Engine Configuration )------------------------------------------ */ 260 + 261 + 262 + protected function supportsEditEngineConfiguration() { 263 + return true; 264 + } 265 + 266 + final protected function getEditEngineConfiguration() { 267 + return $this->editEngineConfiguration; 268 + } 269 + 270 + private function loadEditEngineConfiguration($key) { 271 + if ($key === null) { 272 + $key = self::EDITENGINECONFIG_DEFAULT; 273 + } 274 + 275 + $config = id(new PhabricatorEditEngineConfigurationQuery()) 276 + ->setViewer($this->getViewer()) 277 + ->withEngineKeys(array($this->getEngineKey())) 278 + ->withIdentifiers(array($key)) 279 + ->executeOne(); 280 + if (!$config) { 281 + return null; 282 + } 283 + 284 + $this->editEngineConfiguration = $config; 285 + 286 + return $config; 287 + } 288 + 289 + final public function getBuiltinEngineConfigurations() { 290 + $configurations = $this->newBuiltinEngineConfigurations(); 291 + 292 + if (!$configurations) { 293 + throw new Exception( 294 + pht( 295 + 'EditEngine ("%s") returned no builtin engine configurations, but '. 296 + 'an edit engine must have at least one configuration.', 297 + get_class($this))); 298 + } 299 + 300 + assert_instances_of($configurations, 'PhabricatorEditEngineConfiguration'); 301 + 302 + $has_default = false; 303 + foreach ($configurations as $config) { 304 + if ($config->getBuiltinKey() == self::EDITENGINECONFIG_DEFAULT) { 305 + $has_default = true; 306 + } 307 + } 308 + 309 + if (!$has_default) { 310 + $first = head($configurations); 311 + if (!$first->getBuiltinKey()) { 312 + $first->setBuiltinKey(self::EDITENGINECONFIG_DEFAULT); 313 + 314 + if (!strlen($first->getName())) { 315 + $first->setName($this->getObjectCreateShortText()); 316 + } 317 + } else { 318 + throw new Exception( 319 + pht( 320 + 'EditEngine ("%s") returned builtin engine configurations, '. 321 + 'but none are marked as default and the first configuration has '. 322 + 'a different builtin key already. Mark a builtin as default or '. 323 + 'omit the key from the first configuration', 324 + get_class($this))); 325 + } 326 + } 327 + 328 + $builtins = array(); 329 + foreach ($configurations as $key => $config) { 330 + $builtin_key = $config->getBuiltinKey(); 331 + 332 + if ($builtin_key === null) { 333 + throw new Exception( 334 + pht( 335 + 'EditEngine ("%s") returned builtin engine configurations, '. 336 + 'but one (with key "%s") is missing a builtin key. Provide a '. 337 + 'builtin key for each configuration (you can omit it from the '. 338 + 'first configuration in the list to automatically assign the '. 339 + 'default key).', 340 + get_class($this), 341 + $key)); 342 + } 343 + 344 + if (isset($builtins[$builtin_key])) { 345 + throw new Exception( 346 + pht( 347 + 'EditEngine ("%s") returned builtin engine configurations, '. 348 + 'but at least two specify the same builtin key ("%s"). Engines '. 349 + 'must have unique builtin keys.', 350 + get_class($this), 351 + $builtin_key)); 352 + } 353 + 354 + $builtins[$builtin_key] = $config; 355 + } 356 + 357 + 358 + return $builtins; 359 + } 360 + 361 + protected function newBuiltinEngineConfigurations() { 362 + return array( 363 + $this->newConfiguration(), 364 + ); 365 + } 366 + 367 + final protected function newConfiguration() { 368 + return PhabricatorEditEngineConfiguration::initializeNewConfiguration( 369 + $this->getViewer(), 370 + $this); 371 + } 372 + 373 + 240 374 /* -( Managing URIs )------------------------------------------------------ */ 241 375 242 376 ··· 317 451 * @return bool True if a new object is being created. 318 452 * @task load 319 453 */ 320 - final protected function getIsCreate() { 454 + final public function getIsCreate() { 321 455 return $this->isCreate; 322 456 } 323 457 ··· 391 525 } 392 526 393 527 528 + /** 529 + * Verify that an object is appropriate for editing. 530 + * 531 + * @param wild Loaded value. 532 + * @return void 533 + * @task load 534 + */ 535 + private function validateObject($object) { 536 + if (!$object || !is_object($object)) { 537 + throw new Exception( 538 + pht( 539 + 'EditEngine "%s" created or loaded an invalid object: object must '. 540 + 'actually be an object, but is of some other type ("%s").', 541 + get_class($this), 542 + gettype($object))); 543 + } 544 + 545 + if (!($object instanceof PhabricatorApplicationTransactionInterface)) { 546 + throw new Exception( 547 + pht( 548 + 'EditEngine "%s" created or loaded an invalid object: object (of '. 549 + 'class "%s") must implement "%s", but does not.', 550 + get_class($this), 551 + get_class($object), 552 + 'PhabricatorApplicationTransactionInterface')); 553 + } 554 + } 555 + 556 + 394 557 /* -( Responding to Web Requests )----------------------------------------- */ 395 558 396 559 ··· 399 562 $controller = $this->getController(); 400 563 $request = $controller->getRequest(); 401 564 565 + $config = $this->loadEditEngineConfiguration($request->getURIData('form')); 566 + if (!$config) { 567 + return new Aphront404Response(); 568 + } 569 + 402 570 $id = $request->getURIData('id'); 403 571 if ($id) { 404 572 $this->setIsCreate(false); ··· 411 579 $object = $this->newEditableObject(); 412 580 } 413 581 582 + $this->validateObject($object); 583 + 414 584 $action = $request->getURIData('editAction'); 415 585 switch ($action) { 416 586 case 'parameters': ··· 425 595 426 596 $crumbs = $controller->buildApplicationCrumbsForEditEngine(); 427 597 if ($this->getIsCreate()) { 428 - $create_text = $this->getObjectCreateShortText($object); 598 + $create_text = $this->getObjectCreateShortText(); 429 599 if ($final) { 430 600 $crumbs->addTextCrumb($create_text); 431 601 } else { ··· 570 740 private function buildEditFormActions($object) { 571 741 $actions = array(); 572 742 743 + if ($this->supportsEditEngineConfiguration()) { 744 + $engine_key = $this->getEngineKey(); 745 + $config = $this->getEditEngineConfiguration(); 746 + 747 + $actions[] = id(new PhabricatorActionView()) 748 + ->setName(pht('Manage Form Configurations')) 749 + ->setIcon('fa-list-ul') 750 + ->setHref("/transactions/editengine/{$engine_key}/"); 751 + $actions[] = id(new PhabricatorActionView()) 752 + ->setName(pht('Edit Form Configuration')) 753 + ->setIcon('fa-pencil') 754 + ->setHref($config->getURI()); 755 + } 756 + 573 757 $actions[] = id(new PhabricatorActionView()) 574 758 ->setName(pht('Show HTTP Parameters')) 575 759 ->setIcon('fa-crosshairs') ··· 601 785 602 786 $header_text = pht( 603 787 'HTTP Parameters: %s', 604 - $this->getObjectCreateShortText($object)); 788 + $this->getObjectCreateShortText()); 605 789 606 790 $header = id(new PHUIHeaderView()) 607 791 ->setHeader($header_text); ··· 637 821 final public function buildConduitResponse(ConduitAPIRequest $request) { 638 822 $viewer = $this->getViewer(); 639 823 824 + $config = $this->loadEditEngineConfiguration(null); 825 + if (!$config) { 826 + throw new Exception( 827 + pht( 828 + 'Unable to load configuration for this EditEngine ("%s").', 829 + get_class($this))); 830 + } 831 + 640 832 $phid = $request->getValue('objectPHID'); 641 833 if ($phid) { 642 834 $this->setIsCreate(false); ··· 648 840 $this->setIsCreate(true); 649 841 $object = $this->newEditableObject(); 650 842 } 843 + 844 + $this->validateObject($object); 651 845 652 846 $fields = $this->buildEditFields($object); 653 847 ··· 772 966 return $this->getAllEditTypesFromFields($fields); 773 967 } 774 968 969 + final public static function getAllEditEngines() { 970 + return id(new PhutilClassMapQuery()) 971 + ->setAncestorClass(__CLASS__) 972 + ->setUniqueMethod('getEngineKey') 973 + ->execute(); 974 + } 775 975 976 + final public static function getByKey(PhabricatorUser $viewer, $key) { 977 + return id(new PhabricatorEditEngineQuery()) 978 + ->setViewer($viewer) 979 + ->withEngineKeys(array($key)) 980 + ->executeOne(); 981 + } 982 + 983 + 984 + /* -( PhabricatorPolicyInterface )----------------------------------------- */ 985 + 986 + 987 + public function getPHID() { 988 + return get_class($this); 989 + } 990 + 991 + public function getCapabilities() { 992 + return array( 993 + PhabricatorPolicyCapability::CAN_VIEW, 994 + ); 995 + } 996 + 997 + public function getPolicy($capability) { 998 + switch ($capability) { 999 + case PhabricatorPolicyCapability::CAN_VIEW: 1000 + return PhabricatorPolicies::getMostOpenPolicy(); 1001 + } 1002 + } 1003 + 1004 + public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { 1005 + return false; 1006 + } 1007 + 1008 + public function describeAutomaticCapability($capability) { 1009 + return null; 1010 + } 776 1011 }
+1 -1
src/applications/transactions/editengine/PhabricatorApplicationEditEngineAPIMethod.php src/applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php
··· 1 1 <?php 2 2 3 - abstract class PhabricatorApplicationEditEngineAPIMethod 3 + abstract class PhabricatorEditEngineAPIMethod 4 4 extends ConduitAPIMethod { 5 5 6 6 abstract public function newEditEngine();
+26 -2
src/applications/transactions/editfield/PhabricatorEditField.php
··· 13 13 private $metadata = array(); 14 14 private $description; 15 15 private $editTypeKey; 16 - 16 + private $isLocked; 17 17 18 18 public function setKey($key) { 19 19 $this->key = $key; ··· 69 69 return $this->description; 70 70 } 71 71 72 - abstract protected function newControl(); 72 + public function setIsLocked($is_locked) { 73 + $this->isLocked = $is_locked; 74 + return $this; 75 + } 76 + 77 + public function getIsLocked() { 78 + return $this->isLocked; 79 + } 80 + 81 + protected function newControl() { 82 + throw new PhutilMethodNotImplementedException(); 83 + } 73 84 74 85 protected function renderControl() { 75 86 $control = $this->newControl(); ··· 83 94 84 95 if (!$control->getLabel()) { 85 96 $control->setLabel($this->getLabel()); 97 + } 98 + 99 + if ($this->getIsLocked()) { 100 + $control->setDisabled(true); 86 101 } 87 102 88 103 return $control; ··· 164 179 public function readValueFromObject($object) { 165 180 $this->value = $this->getValueFromObject($object); 166 181 return $this; 182 + } 183 + 184 + public function readDefaultValueFromConfiguration($value) { 185 + $this->value = $this->getDefaultValueFromConfiguration($value); 186 + return $this; 187 + } 188 + 189 + protected function getDefaultValueFromConfiguration($value) { 190 + return $value; 167 191 } 168 192 169 193 protected function getValueFromObject($object) {
+10
src/applications/transactions/editfield/PhabricatorInstructionsEditField.php
··· 1 + <?php 2 + 3 + final class PhabricatorInstructionsEditField 4 + extends PhabricatorEditField { 5 + 6 + public function appendToForm(AphrontFormView $form) { 7 + return $form->appendRemarkupInstructions($this->getValue()); 8 + } 9 + 10 + }
-12
src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php
··· 815 815 816 816 $xactions = $this->filterTransactions($object, $xactions); 817 817 818 - if (!$xactions) { 819 - if ($read_locking) { 820 - $object->endReadLocking(); 821 - $read_locking = false; 822 - } 823 - if ($transaction_open) { 824 - $object->killTransaction(); 825 - $transaction_open = false; 826 - } 827 - return array(); 828 - } 829 - 830 818 // Now that we've merged, filtered, and combined transactions, check for 831 819 // required capabilities. 832 820 foreach ($xactions as $xaction) {
+78
src/applications/transactions/editor/PhabricatorEditEngineConfigurationEditEngine.php
··· 1 + <?php 2 + 3 + final class PhabricatorEditEngineConfigurationEditEngine 4 + extends PhabricatorEditEngine { 5 + 6 + const ENGINECONST = 'transactions.editengine.config'; 7 + 8 + private $targetEngine; 9 + 10 + public function setTargetEngine(PhabricatorEditEngine $target_engine) { 11 + $this->targetEngine = $target_engine; 12 + return $this; 13 + } 14 + 15 + public function getTargetEngine() { 16 + return $this->targetEngine; 17 + } 18 + 19 + public function getEngineName() { 20 + return pht('Edit Configurations'); 21 + } 22 + 23 + protected function newEditableObject() { 24 + return PhabricatorEditEngineConfiguration::initializeNewConfiguration( 25 + $this->getViewer(), 26 + $this->getTargetEngine()); 27 + } 28 + 29 + protected function newObjectQuery() { 30 + return id(new PhabricatorEditEngineConfigurationQuery()); 31 + } 32 + 33 + protected function getObjectCreateTitleText($object) { 34 + return pht('Create New Form'); 35 + } 36 + 37 + protected function getObjectEditTitleText($object) { 38 + return pht('Edit Form %d: %s', $object->getID(), $object->getDisplayName()); 39 + } 40 + 41 + protected function getObjectEditShortText($object) { 42 + return pht('Form %d', $object->getID()); 43 + } 44 + 45 + protected function getObjectCreateShortText() { 46 + return pht('Create Form'); 47 + } 48 + 49 + protected function getObjectViewURI($object) { 50 + $engine_key = $this->getTargetEngine()->getEngineKey(); 51 + $id = $object->getID(); 52 + return "/transactions/editengine/{$engine_key}/view/{$id}/"; 53 + } 54 + 55 + protected function getObjectEditURI($object) { 56 + $engine_key = $this->getTargetEngine()->getEngineKey(); 57 + $id = $object->getID(); 58 + return "/transactions/editengine/{$engine_key}/edit/{$id}/"; 59 + } 60 + 61 + protected function getObjectCreateCancelURI($object) { 62 + $engine_key = $this->getTargetEngine()->getEngineKey(); 63 + return "/transactions/editengine/{$engine_key}/"; 64 + } 65 + 66 + protected function buildCustomEditFields($object) { 67 + return array( 68 + id(new PhabricatorTextEditField()) 69 + ->setKey('name') 70 + ->setLabel(pht('Name')) 71 + ->setDescription(pht('Name of the form.')) 72 + ->setTransactionType( 73 + PhabricatorEditEngineConfigurationTransaction::TYPE_NAME) 74 + ->setValue($object->getName()), 75 + ); 76 + } 77 + 78 + }
+98
src/applications/transactions/editor/PhabricatorEditEngineConfigurationEditor.php
··· 1 + <?php 2 + 3 + final class PhabricatorEditEngineConfigurationEditor 4 + extends PhabricatorApplicationTransactionEditor { 5 + 6 + public function getEditorApplicationClass() { 7 + return 'PhabricatorTransactionsApplication'; 8 + } 9 + 10 + public function getEditorObjectsDescription() { 11 + return pht('Edit Configurations'); 12 + } 13 + 14 + public function getTransactionTypes() { 15 + $types = parent::getTransactionTypes(); 16 + 17 + $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY; 18 + $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY; 19 + 20 + $types[] = PhabricatorEditEngineConfigurationTransaction::TYPE_NAME; 21 + 22 + return $types; 23 + } 24 + 25 + protected function validateTransaction( 26 + PhabricatorLiskDAO $object, 27 + $type, 28 + array $xactions) { 29 + 30 + $errors = parent::validateTransaction($object, $type, $xactions); 31 + switch ($type) { 32 + case PhabricatorEditEngineConfigurationTransaction::TYPE_NAME: 33 + $missing = $this->validateIsEmptyTextField( 34 + $object->getName(), 35 + $xactions); 36 + 37 + if ($missing) { 38 + $error = new PhabricatorApplicationTransactionValidationError( 39 + $type, 40 + pht('Required'), 41 + pht('Form name is required.'), 42 + nonempty(last($xactions), null)); 43 + 44 + $error->setIsMissingFieldError(true); 45 + $errors[] = $error; 46 + } 47 + break; 48 + } 49 + 50 + return $errors; 51 + } 52 + 53 + protected function getCustomTransactionOldValue( 54 + PhabricatorLiskDAO $object, 55 + PhabricatorApplicationTransaction $xaction) { 56 + 57 + switch ($xaction->getTransactionType()) { 58 + case PhabricatorEditEngineConfigurationTransaction::TYPE_NAME: 59 + return $object->getName(); 60 + } 61 + } 62 + 63 + protected function getCustomTransactionNewValue( 64 + PhabricatorLiskDAO $object, 65 + PhabricatorApplicationTransaction $xaction) { 66 + 67 + switch ($xaction->getTransactionType()) { 68 + case PhabricatorEditEngineConfigurationTransaction::TYPE_NAME: 69 + return $xaction->getNewValue(); 70 + } 71 + } 72 + 73 + protected function applyCustomInternalTransaction( 74 + PhabricatorLiskDAO $object, 75 + PhabricatorApplicationTransaction $xaction) { 76 + 77 + switch ($xaction->getTransactionType()) { 78 + case PhabricatorEditEngineConfigurationTransaction::TYPE_NAME: 79 + $object->setName($xaction->getNewValue()); 80 + return; 81 + } 82 + 83 + return parent::applyCustomInternalTransaction($object, $xaction); 84 + } 85 + 86 + protected function applyCustomExternalTransaction( 87 + PhabricatorLiskDAO $object, 88 + PhabricatorApplicationTransaction $xaction) { 89 + 90 + switch ($xaction->getTransactionType()) { 91 + case PhabricatorEditEngineConfigurationTransaction::TYPE_NAME: 92 + return; 93 + } 94 + 95 + return parent::applyCustomExternalTransaction($object, $xaction); 96 + } 97 + 98 + }
+43
src/applications/transactions/phid/PhabricatorEditEngineConfigurationPHIDType.php
··· 1 + <?php 2 + 3 + final class PhabricatorEditEngineConfigurationPHIDType 4 + extends PhabricatorPHIDType { 5 + 6 + const TYPECONST = 'FORM'; 7 + 8 + public function getTypeName() { 9 + return pht('Edit Configuration'); 10 + } 11 + 12 + public function newObject() { 13 + return new PhabricatorEditEngineConfiguration(); 14 + } 15 + 16 + public function getPHIDTypeApplicationClass() { 17 + return 'PhabricatorTransactionsApplication'; 18 + } 19 + 20 + protected function buildQueryForObjects( 21 + PhabricatorObjectQuery $object_query, 22 + array $phids) { 23 + return id(new PhabricatorEditEngineConfigurationQuery()) 24 + ->withPHIDs($phids); 25 + } 26 + 27 + public function loadHandles( 28 + PhabricatorHandleQuery $query, 29 + array $handles, 30 + array $objects) { 31 + 32 + foreach ($handles as $phid => $handle) { 33 + $config = $objects[$phid]; 34 + 35 + $id = $config->getID(); 36 + $name = $config->getName(); 37 + 38 + $handle->setName($name); 39 + $handle->setURI($config->getURI()); 40 + } 41 + } 42 + 43 + }
+183
src/applications/transactions/query/PhabricatorEditEngineConfigurationQuery.php
··· 1 + <?php 2 + 3 + final class PhabricatorEditEngineConfigurationQuery 4 + extends PhabricatorCursorPagedPolicyAwareQuery { 5 + 6 + private $ids; 7 + private $phids; 8 + private $engineKeys; 9 + private $builtinKeys; 10 + private $identifiers; 11 + 12 + public function withIDs(array $ids) { 13 + $this->ids = $ids; 14 + return $this; 15 + } 16 + 17 + public function withPHIDs(array $phids) { 18 + $this->phids = $phids; 19 + return $this; 20 + } 21 + 22 + public function withEngineKeys(array $engine_keys) { 23 + $this->engineKeys = $engine_keys; 24 + return $this; 25 + } 26 + 27 + public function withBuiltinKeys(array $builtin_keys) { 28 + $this->builtinKeys = $builtin_keys; 29 + return $this; 30 + } 31 + 32 + public function withIdentifiers(array $identifiers) { 33 + $this->identifiers = $identifiers; 34 + return $this; 35 + } 36 + 37 + public function newResultObject() { 38 + return new PhabricatorEditEngineConfiguration(); 39 + } 40 + 41 + protected function loadPage() { 42 + // TODO: The logic here is a little flimsy and won't survive pagination. 43 + // For now, I'm just not bothering with pagination since I believe it will 44 + // take some time before any install manages to produce a large enough 45 + // number of edit forms for any particular engine for the lack of UI 46 + // pagination to become a problem. 47 + 48 + $page = $this->loadStandardPage($this->newResultObject()); 49 + 50 + // Now that we've loaded the real results from the database, we're going 51 + // to load builtins from the edit engines and add them to the list. 52 + 53 + $engines = PhabricatorEditEngine::getAllEditEngines(); 54 + 55 + if ($this->engineKeys) { 56 + $engines = array_select_keys($engines, $this->engineKeys); 57 + } 58 + 59 + foreach ($engines as $engine) { 60 + $engine->setViewer($this->getViewer()); 61 + } 62 + 63 + // List all the builtins which have already been saved to the database as 64 + // real objects. 65 + $concrete = array(); 66 + foreach ($page as $config) { 67 + $builtin_key = $config->getBuiltinKey(); 68 + if ($builtin_key !== null) { 69 + $engine_key = $config->getEngineKey(); 70 + $concrete[$engine_key][$builtin_key] = $config; 71 + } 72 + } 73 + 74 + $builtins = array(); 75 + foreach ($engines as $engine_key => $engine) { 76 + $engine_builtins = $engine->getBuiltinEngineConfigurations(); 77 + foreach ($engine_builtins as $engine_builtin) { 78 + $builtin_key = $engine_builtin->getBuiltinKey(); 79 + if (isset($concrete[$engine_key][$builtin_key])) { 80 + continue; 81 + } else { 82 + $builtins[] = $engine_builtin; 83 + } 84 + } 85 + } 86 + 87 + foreach ($builtins as $builtin) { 88 + $page[] = $builtin; 89 + } 90 + 91 + // Now we have to do some extra filtering to make sure everything we're 92 + // about to return really satisfies the query. 93 + 94 + if ($this->ids !== null) { 95 + $ids = array_fuse($this->ids); 96 + foreach ($page as $key => $config) { 97 + if (empty($ids[$config->getID()])) { 98 + unset($page[$key]); 99 + } 100 + } 101 + } 102 + 103 + if ($this->phids !== null) { 104 + $phids = array_fuse($this->phids); 105 + foreach ($page as $key => $config) { 106 + if (empty($phids[$config->getPHID()])) { 107 + unset($page[$key]); 108 + } 109 + } 110 + } 111 + 112 + if ($this->builtinKeys !== null) { 113 + $builtin_keys = array_fuse($this->builtinKeys); 114 + foreach ($page as $key => $config) { 115 + if (empty($builtin_keys[$config->getBuiltinKey()])) { 116 + unset($page[$key]); 117 + } 118 + } 119 + } 120 + 121 + if ($this->identifiers !== null) { 122 + $identifiers = array_fuse($this->identifiers); 123 + foreach ($page as $key => $config) { 124 + if (isset($identifiers[$config->getBuiltinKey()])) { 125 + continue; 126 + } 127 + if (isset($identifiers[$config->getID()])) { 128 + continue; 129 + } 130 + unset($page[$key]); 131 + } 132 + } 133 + 134 + return $page; 135 + } 136 + 137 + protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { 138 + $where = parent::buildWhereClauseParts($conn); 139 + 140 + if ($this->ids !== null) { 141 + $where[] = qsprintf( 142 + $conn, 143 + 'id IN (%Ld)', 144 + $this->ids); 145 + } 146 + 147 + if ($this->phids !== null) { 148 + $where[] = qsprintf( 149 + $conn, 150 + 'phid IN (%Ls)', 151 + $this->phids); 152 + } 153 + 154 + if ($this->engineKeys !== null) { 155 + $where[] = qsprintf( 156 + $conn, 157 + 'engineKey IN (%Ls)', 158 + $this->engineKeys); 159 + } 160 + 161 + if ($this->builtinKeys !== null) { 162 + $where[] = qsprintf( 163 + $conn, 164 + 'builtinKey IN (%Ls)', 165 + $this->builtinKeys); 166 + } 167 + 168 + if ($this->identifiers !== null) { 169 + $where[] = qsprintf( 170 + $conn, 171 + '(id IN (%Ls) OR builtinKey IN (%Ls))', 172 + $this->identifiers, 173 + $this->identifiers); 174 + } 175 + 176 + return $where; 177 + } 178 + 179 + public function getQueryApplicationClass() { 180 + return 'PhabricatorTransactionsApplication'; 181 + } 182 + 183 + }
+102
src/applications/transactions/query/PhabricatorEditEngineConfigurationSearchEngine.php
··· 1 + <?php 2 + 3 + final class PhabricatorEditEngineConfigurationSearchEngine 4 + extends PhabricatorApplicationSearchEngine { 5 + 6 + private $engineKey; 7 + 8 + public function setEngineKey($engine_key) { 9 + $this->engineKey = $engine_key; 10 + return $this; 11 + } 12 + 13 + public function getEngineKey() { 14 + return $this->engineKey; 15 + } 16 + 17 + public function canUseInPanelContext() { 18 + return false; 19 + } 20 + 21 + public function getResultTypeDescription() { 22 + return pht('Forms'); 23 + } 24 + 25 + public function getApplicationClassName() { 26 + return 'PhabricatorTransactionsApplication'; 27 + } 28 + 29 + public function newQuery() { 30 + return id(new PhabricatorEditEngineConfigurationQuery()) 31 + ->withEngineKeys(array($this->getEngineKey())); 32 + } 33 + 34 + protected function buildQueryFromParameters(array $map) { 35 + $query = $this->newQuery(); 36 + return $query; 37 + } 38 + 39 + protected function buildCustomSearchFields() { 40 + return array(); 41 + } 42 + 43 + protected function getDefaultFieldOrder() { 44 + return array(); 45 + } 46 + 47 + protected function getURI($path) { 48 + return '/transactions/editengine/'.$this->getEngineKey().'/'.$path; 49 + } 50 + 51 + protected function getBuiltinQueryNames() { 52 + $names = array( 53 + 'all' => pht('All Forms'), 54 + ); 55 + 56 + return $names; 57 + } 58 + 59 + public function buildSavedQueryFromBuiltin($query_key) { 60 + $query = $this->newSavedQuery(); 61 + $query->setQueryKey($query_key); 62 + 63 + switch ($query_key) { 64 + case 'all': 65 + return $query; 66 + } 67 + 68 + return parent::buildSavedQueryFromBuiltin($query_key); 69 + } 70 + 71 + protected function renderResultList( 72 + array $configs, 73 + PhabricatorSavedQuery $query, 74 + array $handles) { 75 + assert_instances_of($configs, 'PhabricatorEditEngineConfiguration'); 76 + $viewer = $this->requireViewer(); 77 + $engine_key = $this->getEngineKey(); 78 + 79 + $list = id(new PHUIObjectItemListView()) 80 + ->setUser($viewer); 81 + foreach ($configs as $config) { 82 + $item = id(new PHUIObjectItemView()) 83 + ->setHeader($config->getDisplayName()); 84 + 85 + $id = $config->getID(); 86 + if ($id) { 87 + $item->setObjectName(pht('Form %d', $id)); 88 + $key = $id; 89 + } else { 90 + $item->setObjectName(pht('Builtin')); 91 + $key = $config->getBuiltinKey(); 92 + } 93 + $item->setHref("/transactions/editengine/{$engine_key}/view/{$key}/"); 94 + 95 + 96 + $list->addItem($item); 97 + } 98 + 99 + return id(new PhabricatorApplicationSearchResultView()) 100 + ->setObjectList($list); 101 + } 102 + }
+10
src/applications/transactions/query/PhabricatorEditEngineConfigurationTransactionQuery.php
··· 1 + <?php 2 + 3 + final class PhabricatorEditEngineConfigurationTransactionQuery 4 + extends PhabricatorApplicationTransactionQuery { 5 + 6 + public function getTemplateApplicationTransaction() { 7 + return new PhabricatorEditEngineConfigurationTransaction(); 8 + } 9 + 10 + }
+31
src/applications/transactions/query/PhabricatorEditEngineQuery.php
··· 1 + <?php 2 + 3 + final class PhabricatorEditEngineQuery 4 + extends PhabricatorCursorPagedPolicyAwareQuery { 5 + 6 + private $engineKeys; 7 + 8 + public function withEngineKeys(array $keys) { 9 + $this->engineKeys = $keys; 10 + return $this; 11 + } 12 + 13 + protected function loadPage() { 14 + $engines = PhabricatorEditEngine::getAllEditEngines(); 15 + 16 + if ($this->engineKeys !== null) { 17 + $engines = array_select_keys($engines, $this->engineKeys); 18 + } 19 + 20 + return $engines; 21 + } 22 + 23 + public function getQueryApplicationClass() { 24 + return 'PhabricatorTransactionsApplication'; 25 + } 26 + 27 + protected function getResultCursor($object) { 28 + return null; 29 + } 30 + 31 + }
+78
src/applications/transactions/query/PhabricatorEditEngineSearchEngine.php
··· 1 + <?php 2 + 3 + final class PhabricatorEditEngineSearchEngine 4 + extends PhabricatorApplicationSearchEngine { 5 + 6 + public function getResultTypeDescription() { 7 + return pht('Edit Engines'); 8 + } 9 + 10 + public function getApplicationClassName() { 11 + return 'PhabricatorTransactionsApplication'; 12 + } 13 + 14 + public function newQuery() { 15 + return id(new PhabricatorEditEngineQuery()); 16 + } 17 + 18 + protected function buildQueryFromParameters(array $map) { 19 + $query = $this->newQuery(); 20 + return $query; 21 + } 22 + 23 + protected function buildCustomSearchFields() { 24 + return array(); 25 + } 26 + 27 + protected function getDefaultFieldOrder() { 28 + return array(); 29 + } 30 + 31 + protected function getURI($path) { 32 + return '/transactions/editengine/'.$path; 33 + } 34 + 35 + protected function getBuiltinQueryNames() { 36 + $names = array( 37 + 'all' => pht('All Edit Engines'), 38 + ); 39 + 40 + return $names; 41 + } 42 + 43 + public function buildSavedQueryFromBuiltin($query_key) { 44 + $query = $this->newSavedQuery(); 45 + $query->setQueryKey($query_key); 46 + 47 + switch ($query_key) { 48 + case 'all': 49 + return $query; 50 + } 51 + 52 + return parent::buildSavedQueryFromBuiltin($query_key); 53 + } 54 + 55 + protected function renderResultList( 56 + array $engines, 57 + PhabricatorSavedQuery $query, 58 + array $handles) { 59 + assert_instances_of($engines, 'PhabricatorEditEngine'); 60 + $viewer = $this->requireViewer(); 61 + 62 + $list = id(new PHUIObjectItemListView()) 63 + ->setUser($viewer); 64 + foreach ($engines as $engine) { 65 + $engine_key = $engine->getEngineKey(); 66 + $query_uri = "/transactions/editengine/{$engine_key}/"; 67 + 68 + $item = id(new PHUIObjectItemView()) 69 + ->setHeader($engine->getEngineName()) 70 + ->setHref($query_uri); 71 + 72 + $list->addItem($item); 73 + } 74 + 75 + return id(new PhabricatorApplicationSearchResultView()) 76 + ->setObjectList($list); 77 + } 78 + }
+211
src/applications/transactions/storage/PhabricatorEditEngineConfiguration.php
··· 1 + <?php 2 + 3 + final class PhabricatorEditEngineConfiguration 4 + extends PhabricatorSearchDAO 5 + implements 6 + PhabricatorApplicationTransactionInterface, 7 + PhabricatorPolicyInterface { 8 + 9 + protected $engineKey; 10 + protected $builtinKey; 11 + protected $name; 12 + protected $viewPolicy; 13 + protected $editPolicy; 14 + protected $properties = array(); 15 + protected $isDisabled = 0; 16 + protected $isDefault = 0; 17 + 18 + private $engine = self::ATTACHABLE; 19 + 20 + public function getTableName() { 21 + return 'search_editengineconfiguration'; 22 + } 23 + 24 + public static function initializeNewConfiguration( 25 + PhabricatorUser $actor, 26 + PhabricatorEditEngine $engine) { 27 + 28 + // TODO: This should probably be controlled by a new defualt capability. 29 + $edit_policy = PhabricatorPolicies::POLICY_ADMIN; 30 + 31 + return id(new PhabricatorEditEngineConfiguration()) 32 + ->setEngineKey($engine->getEngineKey()) 33 + ->attachEngine($engine) 34 + ->setViewPolicy(PhabricatorPolicies::getMostOpenPolicy()) 35 + ->setEditPolicy($edit_policy); 36 + } 37 + 38 + public function generatePHID() { 39 + return PhabricatorPHID::generateNewPHID( 40 + PhabricatorEditEngineConfigurationPHIDType::TYPECONST); 41 + } 42 + 43 + protected function getConfiguration() { 44 + return array( 45 + self::CONFIG_AUX_PHID => true, 46 + self::CONFIG_SERIALIZATION => array( 47 + 'properties' => self::SERIALIZATION_JSON, 48 + ), 49 + self::CONFIG_COLUMN_SCHEMA => array( 50 + 'engineKey' => 'text64', 51 + 'builtinKey' => 'text64?', 52 + 'name' => 'text255', 53 + 'isDisabled' => 'bool', 54 + 'isDefault' => 'bool', 55 + ), 56 + self::CONFIG_KEY_SCHEMA => array( 57 + 'key_engine' => array( 58 + 'columns' => array('engineKey', 'builtinKey'), 59 + 'unique' => true, 60 + ), 61 + 'key_default' => array( 62 + 'columns' => array('engineKey', 'isDefault', 'isDisabled'), 63 + ), 64 + ), 65 + ) + parent::getConfiguration(); 66 + } 67 + 68 + public function getProperty($key, $default = null) { 69 + return idx($this->properties, $key, $default); 70 + } 71 + 72 + public function setProperty($key, $value) { 73 + $this->properties[$key] = $value; 74 + return $this; 75 + } 76 + 77 + public function attachEngine(PhabricatorEditEngine $engine) { 78 + $this->engine = $engine; 79 + return $this; 80 + } 81 + 82 + public function getEngine() { 83 + return $this->assertAttached($this->engine); 84 + } 85 + 86 + public function applyConfigurationToFields( 87 + PhabricatorEditEngine $engine, 88 + array $fields) { 89 + $fields = mpull($fields, null, 'getKey'); 90 + 91 + $values = $this->getProperty('defaults', array()); 92 + foreach ($fields as $key => $field) { 93 + if ($engine->getIsCreate()) { 94 + if (array_key_exists($key, $values)) { 95 + $field->readDefaultValueFromConfiguration($values[$key]); 96 + } 97 + } 98 + } 99 + 100 + $fields = $this->reorderFields($fields); 101 + 102 + $head_instructions = $this->getProperty('instructions.head'); 103 + if (strlen($head_instructions)) { 104 + $fields = array( 105 + 'config.instructions.head' => id(new PhabricatorInstructionsEditField()) 106 + ->setKey('config.instructions.head') 107 + ->setValue($head_instructions), 108 + ) + $fields; 109 + } 110 + 111 + return $fields; 112 + } 113 + 114 + private function reorderFields(array $fields) { 115 + $keys = array(); 116 + $fields = array_select_keys($fields, $keys) + $fields; 117 + 118 + // Now, move locked fields to the bottom. 119 + $head = array(); 120 + $tail = array(); 121 + foreach ($fields as $key => $field) { 122 + if (!$field->getIsLocked()) { 123 + $head[$key] = $field; 124 + } else { 125 + $tail[$key] = $field; 126 + } 127 + } 128 + 129 + return $head + $tail; 130 + } 131 + 132 + public function getURI() { 133 + $engine_key = $this->getEngineKey(); 134 + $key = $this->getIdentifier(); 135 + 136 + return "/transactions/editengine/{$engine_key}/view/{$key}/"; 137 + } 138 + 139 + public function getIdentifier() { 140 + $key = $this->getID(); 141 + if (!$key) { 142 + $key = $this->getBuiltinKey(); 143 + } 144 + return $key; 145 + } 146 + 147 + public function getDisplayName() { 148 + $name = $this->getName(); 149 + if (strlen($name)) { 150 + return $name; 151 + } 152 + 153 + $builtin = $this->getBuiltinKey(); 154 + if ($builtin !== null) { 155 + return pht('Builtin Form "%s"', $builtin); 156 + } 157 + 158 + return pht('Untitled Form'); 159 + } 160 + 161 + 162 + /* -( PhabricatorPolicyInterface )----------------------------------------- */ 163 + 164 + 165 + public function getCapabilities() { 166 + return array( 167 + PhabricatorPolicyCapability::CAN_VIEW, 168 + PhabricatorPolicyCapability::CAN_EDIT, 169 + ); 170 + } 171 + 172 + public function getPolicy($capability) { 173 + switch ($capability) { 174 + case PhabricatorPolicyCapability::CAN_VIEW: 175 + return $this->getViewPolicy(); 176 + case PhabricatorPolicyCapability::CAN_EDIT: 177 + return $this->getEditPolicy(); 178 + } 179 + } 180 + 181 + public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { 182 + return false; 183 + } 184 + 185 + public function describeAutomaticCapability($capability) { 186 + return null; 187 + } 188 + 189 + 190 + /* -( PhabricatorApplicationTransactionInterface )------------------------- */ 191 + 192 + 193 + public function getApplicationTransactionEditor() { 194 + return new PhabricatorEditEngineConfigurationEditor(); 195 + } 196 + 197 + public function getApplicationTransactionObject() { 198 + return $this; 199 + } 200 + 201 + public function getApplicationTransactionTemplate() { 202 + return new PhabricatorEditEngineConfigurationTransaction(); 203 + } 204 + 205 + public function willRenderTimeline( 206 + PhabricatorApplicationTransactionView $timeline, 207 + AphrontRequest $request) { 208 + return $timeline; 209 + } 210 + 211 + }
+20
src/applications/transactions/storage/PhabricatorEditEngineConfigurationTransaction.php
··· 1 + <?php 2 + 3 + final class PhabricatorEditEngineConfigurationTransaction 4 + extends PhabricatorApplicationTransaction { 5 + 6 + const TYPE_NAME = 'editengine.config.name'; 7 + 8 + public function getApplicationName() { 9 + return 'search'; 10 + } 11 + 12 + public function getApplicationTransactionType() { 13 + return PhabricatorEditEngineConfigurationPHIDType::TYPECONST; 14 + } 15 + 16 + public function getApplicationTransactionCommentObject() { 17 + return null; 18 + } 19 + 20 + }
+1 -1
src/applications/transactions/view/PhabricatorApplicationEditHTTPParameterHelpView.php
··· 4 4 * Renders the "HTTP Parameters" help page for edit engines. 5 5 * 6 6 * This page has a ton of text and specialized rendering on it, this class 7 - * just pulls it out of the main @{class:PhabricatorApplicationEditEngine}. 7 + * just pulls it out of the main @{class:PhabricatorEditEngine}. 8 8 */ 9 9 final class PhabricatorApplicationEditHTTPParameterHelpView 10 10 extends AphrontView {