diff --git a/resources/sql/autopatches/20180410.almanac.01.iface.xaction.sql b/resources/sql/autopatches/20180410.almanac.01.iface.xaction.sql new file mode 100644 --- /dev/null +++ b/resources/sql/autopatches/20180410.almanac.01.iface.xaction.sql @@ -0,0 +1,19 @@ +CREATE TABLE {$NAMESPACE}_almanac.almanac_interfacetransaction ( + 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 @@ -57,12 +57,19 @@ 'AlmanacDrydockPoolServiceType' => 'applications/almanac/servicetype/AlmanacDrydockPoolServiceType.php', 'AlmanacEditor' => 'applications/almanac/editor/AlmanacEditor.php', 'AlmanacInterface' => 'applications/almanac/storage/AlmanacInterface.php', + 'AlmanacInterfaceAddressTransaction' => 'applications/almanac/xaction/AlmanacInterfaceAddressTransaction.php', 'AlmanacInterfaceDatasource' => 'applications/almanac/typeahead/AlmanacInterfaceDatasource.php', 'AlmanacInterfaceDeleteController' => 'applications/almanac/controller/AlmanacInterfaceDeleteController.php', + 'AlmanacInterfaceDeviceTransaction' => 'applications/almanac/xaction/AlmanacInterfaceDeviceTransaction.php', 'AlmanacInterfaceEditController' => 'applications/almanac/controller/AlmanacInterfaceEditController.php', + 'AlmanacInterfaceEditor' => 'applications/almanac/editor/AlmanacInterfaceEditor.php', + 'AlmanacInterfaceNetworkTransaction' => 'applications/almanac/xaction/AlmanacInterfaceNetworkTransaction.php', 'AlmanacInterfacePHIDType' => 'applications/almanac/phid/AlmanacInterfacePHIDType.php', + 'AlmanacInterfacePortTransaction' => 'applications/almanac/xaction/AlmanacInterfacePortTransaction.php', 'AlmanacInterfaceQuery' => 'applications/almanac/query/AlmanacInterfaceQuery.php', 'AlmanacInterfaceTableView' => 'applications/almanac/view/AlmanacInterfaceTableView.php', + 'AlmanacInterfaceTransaction' => 'applications/almanac/storage/AlmanacInterfaceTransaction.php', + 'AlmanacInterfaceTransactionType' => 'applications/almanac/xaction/AlmanacInterfaceTransactionType.php', 'AlmanacKeys' => 'applications/almanac/util/AlmanacKeys.php', 'AlmanacManageClusterServicesCapability' => 'applications/almanac/capability/AlmanacManageClusterServicesCapability.php', 'AlmanacManagementRegisterWorkflow' => 'applications/almanac/management/AlmanacManagementRegisterWorkflow.php', @@ -5243,13 +5250,21 @@ 'PhabricatorPolicyInterface', 'PhabricatorDestructibleInterface', 'PhabricatorExtendedPolicyInterface', + 'PhabricatorApplicationTransactionInterface', ), + 'AlmanacInterfaceAddressTransaction' => 'AlmanacNetworkTransactionType', 'AlmanacInterfaceDatasource' => 'PhabricatorTypeaheadDatasource', 'AlmanacInterfaceDeleteController' => 'AlmanacDeviceController', + 'AlmanacInterfaceDeviceTransaction' => 'AlmanacNetworkTransactionType', 'AlmanacInterfaceEditController' => 'AlmanacDeviceController', + 'AlmanacInterfaceEditor' => 'PhabricatorApplicationTransactionEditor', + 'AlmanacInterfaceNetworkTransaction' => 'AlmanacNetworkTransactionType', 'AlmanacInterfacePHIDType' => 'PhabricatorPHIDType', + 'AlmanacInterfacePortTransaction' => 'AlmanacNetworkTransactionType', 'AlmanacInterfaceQuery' => 'AlmanacQuery', 'AlmanacInterfaceTableView' => 'AphrontView', + 'AlmanacInterfaceTransaction' => 'PhabricatorModularTransaction', + 'AlmanacInterfaceTransactionType' => 'AlmanacTransactionType', 'AlmanacKeys' => 'Phobject', 'AlmanacManageClusterServicesCapability' => 'PhabricatorPolicyCapability', 'AlmanacManagementRegisterWorkflow' => 'AlmanacManagementWorkflow', diff --git a/src/applications/almanac/editor/AlmanacInterfaceEditor.php b/src/applications/almanac/editor/AlmanacInterfaceEditor.php new file mode 100644 --- /dev/null +++ b/src/applications/almanac/editor/AlmanacInterfaceEditor.php @@ -0,0 +1,22 @@ +delete(); } + +/* -( PhabricatorApplicationTransactionInterface )------------------------- */ + + + public function getApplicationTransactionEditor() { + return new AlmanacInterfaceEditor(); + } + + public function getApplicationTransactionObject() { + return $this; + } + + public function getApplicationTransactionTemplate() { + return new AlmanacInterfaceTransaction(); + } + + public function willRenderTimeline( + PhabricatorApplicationTransactionView $timeline, + AphrontRequest $request) { + return $timeline; + } + } diff --git a/src/applications/almanac/storage/AlmanacInterfaceTransaction.php b/src/applications/almanac/storage/AlmanacInterfaceTransaction.php new file mode 100644 --- /dev/null +++ b/src/applications/almanac/storage/AlmanacInterfaceTransaction.php @@ -0,0 +1,22 @@ +getAddress(); + } + + public function applyInternalEffects($object, $value) { + $object->setAddress($value); + } + + public function getTitle() { + return pht( + '%s changed the address for this interface from %s to %s.', + $this->renderAuthor(), + $this->renderOldValue(), + $this->renderNewValue()); + } + + public function validateTransactions($object, array $xactions) { + $errors = array(); + + if ($this->isEmptyTextTransaction($object->getAddress(), $xactions)) { + $errors[] = $this->newRequiredError( + pht('Interfaces must have an address.')); + } + + foreach ($xactions as $xaction) { + + // NOTE: For now, we don't validate addresses. We generally expect users + // to provide IPv4 addresses, but it's reasonable for them to provide + // IPv6 addresses, and some installs currently use DNS names. This is + // off-label but works today. + + } + + return $errors; + } + +} diff --git a/src/applications/almanac/xaction/AlmanacInterfaceDeviceTransaction.php b/src/applications/almanac/xaction/AlmanacInterfaceDeviceTransaction.php new file mode 100644 --- /dev/null +++ b/src/applications/almanac/xaction/AlmanacInterfaceDeviceTransaction.php @@ -0,0 +1,60 @@ +getDevicePHID(); + } + + public function applyInternalEffects($object, $value) { + $object->setDevicePHID($value); + } + + public function getTitle() { + return pht( + '%s changed the device for this interface from %s to %s.', + $this->renderAuthor(), + $this->renderOldHandle(), + $this->renderNewHandle()); + } + + public function validateTransactions($object, array $xactions) { + $errors = array(); + + if ($this->isEmptyTextTransaction($object->getAddress(), $xactions)) { + $errors[] = $this->newRequiredError( + pht('Interfaces must have a device.')); + } + + foreach ($xactions as $xaction) { + if (!$this->isNewObject()) { + $errors[] = $this->newInvalidError( + pht( + 'The device for an interface can not be changed once it has '. + 'been created.'), + $xaction); + continue; + } + + $device_phid = $xaction->getNewValue(); + $devices = id(new AlmanacDeviceQuery()) + ->setViewer($this->getActor()) + ->withPHIDs(array($device_phid)) + ->execute(); + if (!$devices) { + $errors[] = $this->newInvalidError( + pht( + 'You can not attach an interface to a nonexistent or restricted '. + 'device.'), + $xaction); + continue; + } + } + + return $errors; + } + +} diff --git a/src/applications/almanac/xaction/AlmanacInterfaceNetworkTransaction.php b/src/applications/almanac/xaction/AlmanacInterfaceNetworkTransaction.php new file mode 100644 --- /dev/null +++ b/src/applications/almanac/xaction/AlmanacInterfaceNetworkTransaction.php @@ -0,0 +1,53 @@ +getNetworkPHID(); + } + + public function applyInternalEffects($object, $value) { + $object->setNetworkPHID($value); + } + + public function getTitle() { + return pht( + '%s changed the network for this interface from %s to %s.', + $this->renderAuthor(), + $this->renderOldHandle(), + $this->renderNewHandle()); + } + + public function validateTransactions($object, array $xactions) { + $errors = array(); + + $network_phid = $object->getNetworkPHID(); + if ($this->isEmptyTextTransaction($network_phid, $xactions)) { + $errors[] = $this->newRequiredError( + pht('Interfaces must have a network.')); + } + + foreach ($xactions as $xaction) { + $network_phid = $xaction->getNewValue(); + + $networks = id(new AlmanacNetworkQuery()) + ->setViewer($this->getActor()) + ->withPHIDs(array($network_phid)) + ->execute(); + if (!$networks) { + $errors[] = $this->newInvalidError( + pht( + 'You can not put an interface on a nonexistent or restricted '. + 'network.'), + $xaction); + continue; + } + } + + return $errors; + } + +} diff --git a/src/applications/almanac/xaction/AlmanacInterfacePortTransaction.php b/src/applications/almanac/xaction/AlmanacInterfacePortTransaction.php new file mode 100644 --- /dev/null +++ b/src/applications/almanac/xaction/AlmanacInterfacePortTransaction.php @@ -0,0 +1,53 @@ +getPort(); + + if ($port !== null) { + $port = (int)$port; + } + + return $port; + } + + public function applyInternalEffects($object, $value) { + $object->setPort((int)$value); + } + + public function getTitle() { + return pht( + '%s changed the port for this interface from %s to %s.', + $this->renderAuthor(), + $this->renderOldValue(), + $this->renderNewValue()); + } + + public function validateTransactions($object, array $xactions) { + $errors = array(); + + if ($this->isEmptyTextTransaction($object->getName(), $xactions)) { + $errors[] = $this->newRequiredError( + pht('Interfaces must have a port number.')); + } + + foreach ($xactions as $xaction) { + $port = $xaction->getNewValue(); + + $port = (int)$port; + if ($port < 1 || $port > 65535) { + $errors[] = $this->newInvalidError( + pht('Port numbers must be between 1 and 65535, inclusive.'), + $xaction); + continue; + } + } + + return $errors; + } + +} diff --git a/src/applications/almanac/xaction/AlmanacInterfaceTransactionType.php b/src/applications/almanac/xaction/AlmanacInterfaceTransactionType.php new file mode 100644 --- /dev/null +++ b/src/applications/almanac/xaction/AlmanacInterfaceTransactionType.php @@ -0,0 +1,4 @@ +