diff --git a/resources/sql/autopatches/20151106.editengine.1.table.sql b/resources/sql/autopatches/20151106.editengine.1.table.sql new file mode 100644 --- /dev/null +++ b/resources/sql/autopatches/20151106.editengine.1.table.sql @@ -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}; diff --git a/resources/sql/autopatches/20151106.editengine.2.xactions.sql b/resources/sql/autopatches/20151106.editengine.2.xactions.sql new file mode 100644 --- /dev/null +++ b/resources/sql/autopatches/20151106.editengine.2.xactions.sql @@ -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}; diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1579,8 +1579,6 @@ 'PhabricatorApplicationDatasource' => 'applications/meta/typeahead/PhabricatorApplicationDatasource.php', 'PhabricatorApplicationDetailViewController' => 'applications/meta/controller/PhabricatorApplicationDetailViewController.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', 'PhabricatorApplicationEmailCommandsController' => 'applications/meta/controller/PhabricatorApplicationEmailCommandsController.php', 'PhabricatorApplicationLaunchView' => 'applications/meta/view/PhabricatorApplicationLaunchView.php', @@ -2110,6 +2108,24 @@ 'PhabricatorEdgeTestCase' => 'infrastructure/edges/__tests__/PhabricatorEdgeTestCase.php', 'PhabricatorEdgeType' => 'infrastructure/edges/type/PhabricatorEdgeType.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', 'PhabricatorEditType' => 'applications/transactions/edittype/PhabricatorEditType.php', 'PhabricatorEditor' => 'infrastructure/PhabricatorEditor.php', @@ -2294,6 +2310,7 @@ 'PhabricatorInlineCommentInterface' => 'infrastructure/diff/interface/PhabricatorInlineCommentInterface.php', 'PhabricatorInlineCommentPreviewController' => 'infrastructure/diff/PhabricatorInlineCommentPreviewController.php', 'PhabricatorInlineSummaryView' => 'infrastructure/diff/view/PhabricatorInlineSummaryView.php', + 'PhabricatorInstructionsEditField' => 'applications/transactions/editfield/PhabricatorInstructionsEditField.php', 'PhabricatorInternationalizationManagementExtractWorkflow' => 'infrastructure/internationalization/management/PhabricatorInternationalizationManagementExtractWorkflow.php', 'PhabricatorInternationalizationManagementWorkflow' => 'infrastructure/internationalization/management/PhabricatorInternationalizationManagementWorkflow.php', 'PhabricatorInvalidConfigSetupCheck' => 'applications/config/check/PhabricatorInvalidConfigSetupCheck.php', @@ -5500,7 +5517,7 @@ 'PasteCreateMailReceiver' => 'PhabricatorMailReceiver', 'PasteDefaultEditCapability' => 'PhabricatorPolicyCapability', 'PasteDefaultViewCapability' => 'PhabricatorPolicyCapability', - 'PasteEditConduitAPIMethod' => 'PhabricatorApplicationEditEngineAPIMethod', + 'PasteEditConduitAPIMethod' => 'PhabricatorEditEngineAPIMethod', 'PasteEmbedView' => 'AphrontView', 'PasteInfoConduitAPIMethod' => 'PasteConduitAPIMethod', 'PasteMailReceiver' => 'PhabricatorObjectMailReceiver', @@ -5544,8 +5561,6 @@ 'PhabricatorApplicationDatasource' => 'PhabricatorTypeaheadDatasource', 'PhabricatorApplicationDetailViewController' => 'PhabricatorApplicationsController', 'PhabricatorApplicationEditController' => 'PhabricatorApplicationsController', - 'PhabricatorApplicationEditEngine' => 'Phobject', - 'PhabricatorApplicationEditEngineAPIMethod' => 'ConduitAPIMethod', 'PhabricatorApplicationEditHTTPParameterHelpView' => 'AphrontView', 'PhabricatorApplicationEmailCommandsController' => 'PhabricatorApplicationsController', 'PhabricatorApplicationLaunchView' => 'AphrontTagView', @@ -6172,6 +6187,31 @@ 'PhabricatorEdgeTestCase' => 'PhabricatorTestCase', 'PhabricatorEdgeType' => 'Phobject', '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', 'PhabricatorEditType' => 'Phobject', 'PhabricatorEditor' => 'Phobject', @@ -6392,6 +6432,7 @@ 'PhabricatorInlineCommentInterface' => 'PhabricatorMarkupInterface', 'PhabricatorInlineCommentPreviewController' => 'PhabricatorController', 'PhabricatorInlineSummaryView' => 'AphrontView', + 'PhabricatorInstructionsEditField' => 'PhabricatorEditField', 'PhabricatorInternationalizationManagementExtractWorkflow' => 'PhabricatorInternationalizationManagementWorkflow', 'PhabricatorInternationalizationManagementWorkflow' => 'PhabricatorManagementWorkflow', 'PhabricatorInvalidConfigSetupCheck' => 'PhabricatorSetupCheck', @@ -6710,7 +6751,7 @@ 'PhabricatorPasteController' => 'PhabricatorController', 'PhabricatorPasteDAO' => 'PhabricatorLiskDAO', 'PhabricatorPasteEditController' => 'PhabricatorPasteController', - 'PhabricatorPasteEditEngine' => 'PhabricatorApplicationEditEngine', + 'PhabricatorPasteEditEngine' => 'PhabricatorEditEngine', 'PhabricatorPasteEditor' => 'PhabricatorApplicationTransactionEditor', 'PhabricatorPasteListController' => 'PhabricatorPasteController', 'PhabricatorPastePastePHIDType' => 'PhabricatorPHIDType', diff --git a/src/applications/base/PhabricatorApplication.php b/src/applications/base/PhabricatorApplication.php --- a/src/applications/base/PhabricatorApplication.php +++ b/src/applications/base/PhabricatorApplication.php @@ -635,8 +635,12 @@ return array(); } - protected function getEditRoutePattern($base) { + protected function getEditRoutePattern($base = null) { return $base.'(?:(?P[0-9]\d*)/)?(?:(?Pparameters)/)?'; } + protected function getQueryRoutePattern($base = null) { + return $base.'(?:query/(?P[^/]+)/)?'; + } + } diff --git a/src/applications/paste/conduit/PasteEditConduitAPIMethod.php b/src/applications/paste/conduit/PasteEditConduitAPIMethod.php --- a/src/applications/paste/conduit/PasteEditConduitAPIMethod.php +++ b/src/applications/paste/conduit/PasteEditConduitAPIMethod.php @@ -1,7 +1,7 @@ getViewer()); @@ -24,7 +30,7 @@ return $object->getMonogram(); } - protected function getObjectCreateShortText($object) { + protected function getObjectCreateShortText() { return pht('Create Paste'); } diff --git a/src/applications/transactions/application/PhabricatorTransactionsApplication.php b/src/applications/transactions/application/PhabricatorTransactionsApplication.php --- a/src/applications/transactions/application/PhabricatorTransactionsApplication.php +++ b/src/applications/transactions/application/PhabricatorTransactionsApplication.php @@ -33,6 +33,20 @@ => 'PhabricatorApplicationTransactionShowOlderController', '(?Pold|new)/(?[^/]+)/' => 'PhabricatorApplicationTransactionValueController', + 'editengine/' => array( + $this->getQueryRoutePattern() + => 'PhabricatorEditEngineListController', + '(?P[^/]+)/' => array( + $this->getQueryRoutePattern() => + 'PhabricatorEditEngineConfigurationListController', + $this->getEditRoutePattern('edit/') => + 'PhabricatorEditEngineConfigurationEditController', + 'view/(?P[^/]+)/' => + 'PhabricatorEditEngineConfigurationViewController', + 'save/(?P[^/]+)/' => + 'PhabricatorEditEngineConfigurationSaveController', + ), + ), ), ); } diff --git a/src/applications/transactions/controller/PhabricatorEditEngineConfigurationEditController.php b/src/applications/transactions/controller/PhabricatorEditEngineConfigurationEditController.php new file mode 100644 --- /dev/null +++ b/src/applications/transactions/controller/PhabricatorEditEngineConfigurationEditController.php @@ -0,0 +1,26 @@ +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(); + } + +} diff --git a/src/applications/transactions/controller/PhabricatorEditEngineConfigurationListController.php b/src/applications/transactions/controller/PhabricatorEditEngineConfigurationListController.php new file mode 100644 --- /dev/null +++ b/src/applications/transactions/controller/PhabricatorEditEngineConfigurationListController.php @@ -0,0 +1,34 @@ +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; + } + +} diff --git a/src/applications/transactions/controller/PhabricatorEditEngineConfigurationSaveController.php b/src/applications/transactions/controller/PhabricatorEditEngineConfigurationSaveController.php new file mode 100644 --- /dev/null +++ b/src/applications/transactions/controller/PhabricatorEditEngineConfigurationSaveController.php @@ -0,0 +1,55 @@ +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); + } + +} diff --git a/src/applications/transactions/controller/PhabricatorEditEngineConfigurationViewController.php b/src/applications/transactions/controller/PhabricatorEditEngineConfigurationViewController.php new file mode 100644 --- /dev/null +++ b/src/applications/transactions/controller/PhabricatorEditEngineConfigurationViewController.php @@ -0,0 +1,125 @@ +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; + } + + +} diff --git a/src/applications/transactions/controller/PhabricatorEditEngineController.php b/src/applications/transactions/controller/PhabricatorEditEngineController.php new file mode 100644 --- /dev/null +++ b/src/applications/transactions/controller/PhabricatorEditEngineController.php @@ -0,0 +1,37 @@ +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; + } + +} diff --git a/src/applications/transactions/controller/PhabricatorEditEngineListController.php b/src/applications/transactions/controller/PhabricatorEditEngineListController.php new file mode 100644 --- /dev/null +++ b/src/applications/transactions/controller/PhabricatorEditEngineListController.php @@ -0,0 +1,16 @@ +setController($this) + ->buildResponse(); + } + +} diff --git a/src/applications/transactions/editengine/PhabricatorApplicationEditEngine.php b/src/applications/transactions/editengine/PhabricatorEditEngine.php rename from src/applications/transactions/editengine/PhabricatorApplicationEditEngine.php rename to src/applications/transactions/editengine/PhabricatorEditEngine.php --- a/src/applications/transactions/editengine/PhabricatorApplicationEditEngine.php +++ b/src/applications/transactions/editengine/PhabricatorEditEngine.php @@ -4,6 +4,7 @@ /** * @task fields Managing Fields * @task text Display Text + * @task config Edit Engine Configuration * @task uri Managing URIs * @task load Creating and Loading Objects * @task web Responding to Web Requests @@ -11,11 +12,16 @@ * @task http Responding to HTTP Parameter 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 $controller; private $isCreate; + private $editEngineConfiguration; final public function setViewer(PhabricatorUser $viewer) { $this->viewer = $viewer; @@ -36,6 +42,10 @@ return $this->controller; } + final public function getEngineKey() { + return $this->getPhobjectClassConstant('ENGINECONST', 64); + } + /* -( Managing Fields )---------------------------------------------------- */ @@ -184,6 +194,9 @@ } } + $config = $this->getEditEngineConfiguration(); + $fields = $config->applyConfigurationToFields($this, $fields); + foreach ($fields as $field) { $field ->setViewer($viewer) @@ -200,6 +213,12 @@ /** * @task text */ + abstract public function getEngineName(); + + + /** + * @task text + */ abstract protected function getObjectCreateTitleText($object); @@ -212,7 +231,7 @@ /** * @task text */ - abstract protected function getObjectCreateShortText($object); + abstract protected function getObjectCreateShortText(); /** @@ -237,6 +256,121 @@ } +/* -( 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 )------------------------------------------------------ */ @@ -317,7 +451,7 @@ * @return bool True if a new object is being created. * @task load */ - final protected function getIsCreate() { + final public function getIsCreate() { return $this->isCreate; } @@ -391,6 +525,35 @@ } + /** + * 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 )----------------------------------------- */ @@ -399,6 +562,11 @@ $controller = $this->getController(); $request = $controller->getRequest(); + $config = $this->loadEditEngineConfiguration($request->getURIData('form')); + if (!$config) { + return new Aphront404Response(); + } + $id = $request->getURIData('id'); if ($id) { $this->setIsCreate(false); @@ -411,6 +579,8 @@ $object = $this->newEditableObject(); } + $this->validateObject($object); + $action = $request->getURIData('editAction'); switch ($action) { case 'parameters': @@ -425,7 +595,7 @@ $crumbs = $controller->buildApplicationCrumbsForEditEngine(); if ($this->getIsCreate()) { - $create_text = $this->getObjectCreateShortText($object); + $create_text = $this->getObjectCreateShortText(); if ($final) { $crumbs->addTextCrumb($create_text); } else { @@ -570,6 +740,20 @@ private function buildEditFormActions($object) { $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()) ->setName(pht('Show HTTP Parameters')) ->setIcon('fa-crosshairs') @@ -601,7 +785,7 @@ $header_text = pht( 'HTTP Parameters: %s', - $this->getObjectCreateShortText($object)); + $this->getObjectCreateShortText()); $header = id(new PHUIHeaderView()) ->setHeader($header_text); @@ -637,6 +821,14 @@ final public function buildConduitResponse(ConduitAPIRequest $request) { $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'); if ($phid) { $this->setIsCreate(false); @@ -649,6 +841,8 @@ $object = $this->newEditableObject(); } + $this->validateObject($object); + $fields = $this->buildEditFields($object); $types = $this->getAllEditTypesFromFields($fields); @@ -772,5 +966,46 @@ 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; + } } diff --git a/src/applications/transactions/editengine/PhabricatorApplicationEditEngineAPIMethod.php b/src/applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php rename from src/applications/transactions/editengine/PhabricatorApplicationEditEngineAPIMethod.php rename to src/applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php --- a/src/applications/transactions/editengine/PhabricatorApplicationEditEngineAPIMethod.php +++ b/src/applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php @@ -1,6 +1,6 @@ key = $key; @@ -69,7 +69,18 @@ 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() { $control = $this->newControl(); @@ -85,6 +96,10 @@ $control->setLabel($this->getLabel()); } + if ($this->getIsLocked()) { + $control->setDisabled(true); + } + return $control; } @@ -166,6 +181,15 @@ return $this; } + public function readDefaultValueFromConfiguration($value) { + $this->value = $this->getDefaultValueFromConfiguration($value); + return $this; + } + + protected function getDefaultValueFromConfiguration($value) { + return $value; + } + protected function getValueFromObject($object) { if ($this->hasValue) { return $this->value; diff --git a/src/applications/transactions/editfield/PhabricatorInstructionsEditField.php b/src/applications/transactions/editfield/PhabricatorInstructionsEditField.php new file mode 100644 --- /dev/null +++ b/src/applications/transactions/editfield/PhabricatorInstructionsEditField.php @@ -0,0 +1,10 @@ +appendRemarkupInstructions($this->getValue()); + } + +} diff --git a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php --- a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php +++ b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php @@ -815,18 +815,6 @@ $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 // required capabilities. foreach ($xactions as $xaction) { diff --git a/src/applications/transactions/editor/PhabricatorEditEngineConfigurationEditEngine.php b/src/applications/transactions/editor/PhabricatorEditEngineConfigurationEditEngine.php new file mode 100644 --- /dev/null +++ b/src/applications/transactions/editor/PhabricatorEditEngineConfigurationEditEngine.php @@ -0,0 +1,78 @@ +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()), + ); + } + +} diff --git a/src/applications/transactions/editor/PhabricatorEditEngineConfigurationEditor.php b/src/applications/transactions/editor/PhabricatorEditEngineConfigurationEditor.php new file mode 100644 --- /dev/null +++ b/src/applications/transactions/editor/PhabricatorEditEngineConfigurationEditor.php @@ -0,0 +1,98 @@ +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); + } + +} diff --git a/src/applications/transactions/phid/PhabricatorEditEngineConfigurationPHIDType.php b/src/applications/transactions/phid/PhabricatorEditEngineConfigurationPHIDType.php new file mode 100644 --- /dev/null +++ b/src/applications/transactions/phid/PhabricatorEditEngineConfigurationPHIDType.php @@ -0,0 +1,43 @@ +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()); + } + } + +} diff --git a/src/applications/transactions/query/PhabricatorEditEngineConfigurationQuery.php b/src/applications/transactions/query/PhabricatorEditEngineConfigurationQuery.php new file mode 100644 --- /dev/null +++ b/src/applications/transactions/query/PhabricatorEditEngineConfigurationQuery.php @@ -0,0 +1,183 @@ +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'; + } + +} diff --git a/src/applications/transactions/query/PhabricatorEditEngineConfigurationSearchEngine.php b/src/applications/transactions/query/PhabricatorEditEngineConfigurationSearchEngine.php new file mode 100644 --- /dev/null +++ b/src/applications/transactions/query/PhabricatorEditEngineConfigurationSearchEngine.php @@ -0,0 +1,102 @@ +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); + } +} diff --git a/src/applications/transactions/query/PhabricatorEditEngineConfigurationTransactionQuery.php b/src/applications/transactions/query/PhabricatorEditEngineConfigurationTransactionQuery.php new file mode 100644 --- /dev/null +++ b/src/applications/transactions/query/PhabricatorEditEngineConfigurationTransactionQuery.php @@ -0,0 +1,10 @@ +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; + } + +} diff --git a/src/applications/transactions/query/PhabricatorEditEngineSearchEngine.php b/src/applications/transactions/query/PhabricatorEditEngineSearchEngine.php new file mode 100644 --- /dev/null +++ b/src/applications/transactions/query/PhabricatorEditEngineSearchEngine.php @@ -0,0 +1,78 @@ +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); + } +} diff --git a/src/applications/transactions/storage/PhabricatorEditEngineConfiguration.php b/src/applications/transactions/storage/PhabricatorEditEngineConfiguration.php new file mode 100644 --- /dev/null +++ b/src/applications/transactions/storage/PhabricatorEditEngineConfiguration.php @@ -0,0 +1,211 @@ +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; + } + +} diff --git a/src/applications/transactions/storage/PhabricatorEditEngineConfigurationTransaction.php b/src/applications/transactions/storage/PhabricatorEditEngineConfigurationTransaction.php new file mode 100644 --- /dev/null +++ b/src/applications/transactions/storage/PhabricatorEditEngineConfigurationTransaction.php @@ -0,0 +1,20 @@ +