Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F15420342
D10718.id25730.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
29 KB
Referenced Files
None
Subscribers
None
D10718.id25730.diff
View Options
diff --git a/resources/sql/autopatches/20141016.almanac.interface.sql b/resources/sql/autopatches/20141016.almanac.interface.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20141016.almanac.interface.sql
@@ -0,0 +1,13 @@
+CREATE TABLE {$NAMESPACE}_almanac.almanac_interface (
+ id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ phid VARBINARY(64) NOT NULL,
+ devicePHID VARBINARY(64) NOT NULL,
+ networkPHID VARBINARY(64) NOT NULL,
+ address VARCHAR(128) NOT NULL COLLATE utf8_bin,
+ port INT UNSIGNED NOT NULL,
+ dateCreated INT UNSIGNED NOT NULL,
+ dateModified INT UNSIGNED NOT NULL,
+ UNIQUE KEY `key_phid` (phid),
+ KEY `key_location` (networkPHID, address, port),
+ KEY `key_device` (devicePHID)
+) ENGINE=InnoDB, COLLATE utf8_general_ci;
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
@@ -9,6 +9,7 @@
phutil_register_library_map(array(
'__library_version__' => 2,
'class' => array(
+ 'AlmanacAddress' => 'applications/almanac/util/AlmanacAddress.php',
'AlmanacConduitUtil' => 'applications/almanac/util/AlmanacConduitUtil.php',
'AlmanacConsoleController' => 'applications/almanac/controller/AlmanacConsoleController.php',
'AlmanacController' => 'applications/almanac/controller/AlmanacController.php',
@@ -28,6 +29,11 @@
'AlmanacDeviceTransaction' => 'applications/almanac/storage/AlmanacDeviceTransaction.php',
'AlmanacDeviceTransactionQuery' => 'applications/almanac/query/AlmanacDeviceTransactionQuery.php',
'AlmanacDeviceViewController' => 'applications/almanac/controller/AlmanacDeviceViewController.php',
+ 'AlmanacInterface' => 'applications/almanac/storage/AlmanacInterface.php',
+ 'AlmanacInterfaceEditController' => 'applications/almanac/controller/AlmanacInterfaceEditController.php',
+ 'AlmanacInterfacePHIDType' => 'applications/almanac/phid/AlmanacInterfacePHIDType.php',
+ 'AlmanacInterfaceQuery' => 'applications/almanac/query/AlmanacInterfaceQuery.php',
+ 'AlmanacInterfaceTableView' => 'applications/almanac/view/AlmanacInterfaceTableView.php',
'AlmanacManagementRegisterWorkflow' => 'applications/almanac/management/AlmanacManagementRegisterWorkflow.php',
'AlmanacManagementWorkflow' => 'applications/almanac/management/AlmanacManagementWorkflow.php',
'AlmanacNames' => 'applications/almanac/util/AlmanacNames.php',
@@ -2939,6 +2945,7 @@
'require_celerity_resource' => 'applications/celerity/api.php',
),
'xmap' => array(
+ 'AlmanacAddress' => 'Phobject',
'AlmanacConduitUtil' => 'Phobject',
'AlmanacConsoleController' => 'AlmanacController',
'AlmanacController' => 'PhabricatorController',
@@ -2961,6 +2968,14 @@
'AlmanacDeviceTransaction' => 'PhabricatorApplicationTransaction',
'AlmanacDeviceTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'AlmanacDeviceViewController' => 'AlmanacDeviceController',
+ 'AlmanacInterface' => array(
+ 'AlmanacDAO',
+ 'PhabricatorPolicyInterface',
+ ),
+ 'AlmanacInterfaceEditController' => 'AlmanacDeviceController',
+ 'AlmanacInterfacePHIDType' => 'PhabricatorPHIDType',
+ 'AlmanacInterfaceQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
+ 'AlmanacInterfaceTableView' => 'AphrontView',
'AlmanacManagementRegisterWorkflow' => 'AlmanacManagementWorkflow',
'AlmanacManagementWorkflow' => 'PhabricatorManagementWorkflow',
'AlmanacNames' => 'Phobject',
diff --git a/src/applications/almanac/application/PhabricatorAlmanacApplication.php b/src/applications/almanac/application/PhabricatorAlmanacApplication.php
--- a/src/applications/almanac/application/PhabricatorAlmanacApplication.php
+++ b/src/applications/almanac/application/PhabricatorAlmanacApplication.php
@@ -44,6 +44,9 @@
'edit/(?:(?P<id>\d+)/)?' => 'AlmanacDeviceEditController',
'view/(?P<name>[^/]+)/' => 'AlmanacDeviceViewController',
),
+ 'interface/' => array(
+ 'edit/(?:(?P<id>\d+)/)?' => 'AlmanacInterfaceEditController',
+ ),
'network/' => array(
'(?:query/(?P<queryKey>[^/]+)/)?' => 'AlmanacNetworkListController',
'edit/(?:(?P<id>\d+)/)?' => 'AlmanacNetworkEditController',
diff --git a/src/applications/almanac/controller/AlmanacDeviceViewController.php b/src/applications/almanac/controller/AlmanacDeviceViewController.php
--- a/src/applications/almanac/controller/AlmanacDeviceViewController.php
+++ b/src/applications/almanac/controller/AlmanacDeviceViewController.php
@@ -35,6 +35,8 @@
->setHeader($header)
->addPropertyList($property_list);
+ $interfaces = $this->buildInterfaceList($device);
+
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb($device->getName());
@@ -53,6 +55,7 @@
array(
$crumbs,
$box,
+ $interfaces,
$xaction_view,
),
array(
@@ -92,4 +95,48 @@
return $actions;
}
+ private function buildInterfaceList(AlmanacDevice $device) {
+ $viewer = $this->getViewer();
+ $id = $device->getID();
+
+ $can_edit = PhabricatorPolicyFilter::hasCapability(
+ $viewer,
+ $device,
+ PhabricatorPolicyCapability::CAN_EDIT);
+
+ $interfaces = id(new AlmanacInterfaceQuery())
+ ->setViewer($viewer)
+ ->withDevicePHIDs(array($device->getPHID()))
+ ->execute();
+
+ $phids = array();
+ foreach ($interfaces as $interface) {
+ $phids[] = $interface->getNetworkPHID();
+ $phids[] = $interface->getDevicePHID();
+ }
+ $handles = $this->loadViewerHandles($phids);
+
+ $table = id(new AlmanacInterfaceTableView())
+ ->setUser($viewer)
+ ->setInterfaces($interfaces)
+ ->setHandles($handles);
+
+ $header = id(new PHUIHeaderView())
+ ->setHeader(pht('Device Interfaces'))
+ ->addActionLink(
+ id(new PHUIButtonView())
+ ->setTag('a')
+ ->setHref($this->getApplicationURI("interface/edit/?deviceID={$id}"))
+ ->setWorkflow(!$can_edit)
+ ->setDisabled(!$can_edit)
+ ->setText(pht('Add Interface'))
+ ->setIcon(
+ id(new PHUIIconView())
+ ->setIconFont('fa-plus')));
+
+ return id(new PHUIObjectBoxView())
+ ->setHeader($header)
+ ->appendChild($table);
+ }
+
}
diff --git a/src/applications/almanac/controller/AlmanacInterfaceEditController.php b/src/applications/almanac/controller/AlmanacInterfaceEditController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/almanac/controller/AlmanacInterfaceEditController.php
@@ -0,0 +1,155 @@
+<?php
+
+final class AlmanacInterfaceEditController
+ extends AlmanacDeviceController {
+
+ public function handleRequest(AphrontRequest $request) {
+ $viewer = $request->getViewer();
+
+ $id = $request->getURIData('id');
+ if ($id) {
+ $interface = id(new AlmanacInterfaceQuery())
+ ->setViewer($viewer)
+ ->withIDs(array($id))
+ ->requireCapabilities(
+ array(
+ PhabricatorPolicyCapability::CAN_VIEW,
+ PhabricatorPolicyCapability::CAN_EDIT,
+ ))
+ ->executeOne();
+ if (!$interface) {
+ return new Aphront404Response();
+ }
+
+ $device = $interface->getDevice();
+
+ $is_new = false;
+ $title = pht('Edit Interface');
+ $save_button = pht('Save Changes');
+ } else {
+ $device = id(new AlmanacDeviceQuery())
+ ->setViewer($viewer)
+ ->withIDs(array($request->getStr('deviceID')))
+ ->requireCapabilities(
+ array(
+ PhabricatorPolicyCapability::CAN_VIEW,
+ PhabricatorPolicyCapability::CAN_EDIT,
+ ))
+ ->executeOne();
+ if (!$device) {
+ return new Aphront404Response();
+ }
+
+ $interface = AlmanacInterface::initializeNewInterface();
+ $is_new = true;
+
+ $title = pht('Create Interface');
+ $save_button = pht('Create Interface');
+ }
+
+ $device_uri = $device->getURI();
+ $cancel_uri = $device_uri;
+
+ $v_network = $interface->getNetworkPHID();
+
+ $v_address = $interface->getAddress();
+ $e_address = true;
+
+ $v_port = $interface->getPort();
+
+ $validation_exception = null;
+
+ if ($request->isFormPost()) {
+ $v_network = $request->getStr('networkPHID');
+ $v_address = $request->getStr('address');
+ $v_port = $request->getStr('port');
+
+ $type_interface = AlmanacDeviceTransaction::TYPE_INTERFACE;
+
+ $address = AlmanacAddress::newFromParts($v_network, $v_address, $v_port);
+
+ $xaction = id(new AlmanacDeviceTransaction())
+ ->setTransactionType($type_interface)
+ ->setNewValue($address->toDictionary());
+
+ if ($interface->getID()) {
+ $xaction->setOldValue(array(
+ 'id' => $interface->getID(),
+ ) + $interface->toAddress()->toDictionary());
+ } else {
+ $xaction->setOldValue(array());
+ }
+
+ $xactions = array();
+ $xactions[] = $xaction;
+
+ $editor = id(new AlmanacDeviceEditor())
+ ->setActor($viewer)
+ ->setContentSourceFromRequest($request)
+ ->setContinueOnNoEffect(true)
+ ->setContinueOnMissingFields(true);
+
+ try {
+ $editor->applyTransactions($device, $xactions);
+
+ $device_uri = $device->getURI();
+ return id(new AphrontRedirectResponse())->setURI($device_uri);
+ } catch (PhabricatorApplicationTransactionValidationException $ex) {
+ $validation_exception = $ex;
+ $e_address = $ex->getShortMessage($type_interface);
+ }
+ }
+
+ $networks = id(new AlmanacNetworkQuery())
+ ->setViewer($viewer)
+ ->execute();
+
+ $form = id(new AphrontFormView())
+ ->setUser($viewer)
+ ->appendChild(
+ id(new AphrontFormSelectControl())
+ ->setLabel(pht('Network'))
+ ->setName('networkPHID')
+ ->setValue($v_network)
+ ->setOptions(mpull($networks, 'getName', 'getPHID')))
+ ->appendChild(
+ id(new AphrontFormTextControl())
+ ->setLabel(pht('Address'))
+ ->setName('address')
+ ->setValue($v_address)
+ ->setError($e_address))
+ ->appendChild(
+ id(new AphrontFormTextControl())
+ ->setLabel(pht('Port'))
+ ->setName('port')
+ ->setValue($v_port)
+ ->setError($e_address))
+ ->appendChild(
+ id(new AphrontFormSubmitControl())
+ ->addCancelButton($cancel_uri)
+ ->setValue($save_button));
+
+ $box = id(new PHUIObjectBoxView())
+ ->setValidationException($validation_exception)
+ ->setHeaderText($title)
+ ->appendChild($form);
+
+ $crumbs = $this->buildApplicationCrumbs();
+ $crumbs->addTextCrumb($device->getName(), $device_uri);
+ if ($is_new) {
+ $crumbs->addTextCrumb(pht('Create Interface'));
+ } else {
+ $crumbs->addTextCrumb(pht('Edit Interface'));
+ }
+
+ return $this->buildApplicationPage(
+ array(
+ $crumbs,
+ $box,
+ ),
+ array(
+ 'title' => $title,
+ ));
+ }
+
+}
diff --git a/src/applications/almanac/editor/AlmanacDeviceEditor.php b/src/applications/almanac/editor/AlmanacDeviceEditor.php
--- a/src/applications/almanac/editor/AlmanacDeviceEditor.php
+++ b/src/applications/almanac/editor/AlmanacDeviceEditor.php
@@ -15,6 +15,7 @@
$types = parent::getTransactionTypes();
$types[] = AlmanacDeviceTransaction::TYPE_NAME;
+ $types[] = AlmanacDeviceTransaction::TYPE_INTERFACE;
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
$types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
@@ -38,6 +39,7 @@
switch ($xaction->getTransactionType()) {
case AlmanacDeviceTransaction::TYPE_NAME:
+ case AlmanacDeviceTransaction::TYPE_INTERFACE:
return $xaction->getNewValue();
}
@@ -52,6 +54,7 @@
case AlmanacDeviceTransaction::TYPE_NAME:
$object->setName($xaction->getNewValue());
return;
+ case AlmanacDeviceTransaction::TYPE_INTERFACE:
case PhabricatorTransactions::TYPE_VIEW_POLICY:
case PhabricatorTransactions::TYPE_EDIT_POLICY:
return;
@@ -69,6 +72,32 @@
case PhabricatorTransactions::TYPE_VIEW_POLICY:
case PhabricatorTransactions::TYPE_EDIT_POLICY:
return;
+ case AlmanacDeviceTransaction::TYPE_INTERFACE:
+ $old = $xaction->getOldValue();
+ if ($old) {
+ $interface = id(new AlmanacInterfaceQuery())
+ ->setViewer($this->requireActor())
+ ->withIDs(array($old['id']))
+ ->executeOne();
+ if (!$interface) {
+ throw new Exception(pht('Unable to load interface!'));
+ }
+ } else {
+ $interface = AlmanacInterface::initializeNewInterface()
+ ->setDevicePHID($object->getPHID());
+ }
+
+ $new = $xaction->getNewValue();
+ if ($new) {
+ $interface
+ ->setNetworkPHID($new['networkPHID'])
+ ->setAddress($new['address'])
+ ->setPort((int)$new['port'])
+ ->save();
+ } else {
+ $interface->delete();
+ }
+ return;
}
return parent::applyCustomExternalTransaction($object, $xaction);
@@ -134,6 +163,125 @@
}
break;
+ case AlmanacDeviceTransaction::TYPE_INTERFACE:
+ // We want to make sure that all the affected networks are visible to
+ // the actor, any edited interfaces exist, and that the actual address
+ // components are valid.
+
+ $network_phids = array();
+ foreach ($xactions as $xaction) {
+ $old = $xaction->getOldValue();
+ $new = $xaction->getNewValue();
+ if ($old) {
+ $network_phids[] = $old['networkPHID'];
+ }
+ if ($new) {
+ $network_phids[] = $new['networkPHID'];
+
+ $address = $new['address'];
+ if (!strlen($address)) {
+ $error = new PhabricatorApplicationTransactionValidationError(
+ $type,
+ pht('Invalid'),
+ pht('Interfaces must have an address.'),
+ $xaction);
+ $errors[] = $error;
+ } else {
+ // TODO: Validate addresses, but IPv6 addresses are not trival
+ // to validate.
+ }
+
+ $port = $new['port'];
+ if (!strlen($port)) {
+ $error = new PhabricatorApplicationTransactionValidationError(
+ $type,
+ pht('Invalid'),
+ pht('Interfaces must have a port.'),
+ $xaction);
+ $errors[] = $error;
+ } else if ((int)$port < 1 || (int)$port > 65535) {
+ $error = new PhabricatorApplicationTransactionValidationError(
+ $type,
+ pht('Invalid'),
+ pht(
+ 'Port numbers must be between 1 and 65535, inclusive.'),
+ $xaction);
+ $errors[] = $error;
+ }
+ }
+ }
+
+ if ($network_phids) {
+ $networks = id(new AlmanacNetworkQuery())
+ ->setViewer($this->requireActor())
+ ->withPHIDs($network_phids)
+ ->execute();
+ $networks = mpull($networks, null, 'getPHID');
+ } else {
+ $networks = array();
+ }
+
+ $addresses = array();
+ foreach ($xactions as $xaction) {
+ $old = $xaction->getOldValue();
+ if ($old) {
+ $network = idx($networks, $old['networkPHID']);
+ if (!$network) {
+ $error = new PhabricatorApplicationTransactionValidationError(
+ $type,
+ pht('Invalid'),
+ pht(
+ 'You can not edit an interface which belongs to a '.
+ 'nonexistent or restricted network.'),
+ $xaction);
+ $errors[] = $error;
+ }
+
+ $addresses[] = $old['id'];
+ }
+
+ $new = $xaction->getNewValue();
+ if ($new) {
+ $network = idx($networks, $new['networkPHID']);
+ if (!$network) {
+ $error = new PhabricatorApplicationTransactionValidationError(
+ $type,
+ pht('Invalid'),
+ pht(
+ 'You can not add an interface on a nonexistent or '.
+ 'restricted network.'),
+ $xaction);
+ $errors[] = $error;
+ }
+ }
+ }
+
+ if ($addresses) {
+ $interfaces = id(new AlmanacInterfaceQuery())
+ ->setViewer($this->requireActor())
+ ->withDevicePHIDs(array($object->getPHID()))
+ ->withIDs($addresses)
+ ->execute();
+ $interfaces = mpull($interfaces, null, 'getID');
+ } else {
+ $interfaces = array();
+ }
+
+ foreach ($xactions as $xaction) {
+ $old = $xaction->getOldValue();
+ if ($old) {
+ $interface = idx($interfaces, $old['id']);
+ if (!$interface) {
+ $error = new PhabricatorApplicationTransactionValidationError(
+ $type,
+ pht('Invalid'),
+ pht('You can not edit an invalid or restricted interface.'),
+ $xaction);
+ $errors[] = $error;
+ }
+ }
+ }
+ break;
}
return $errors;
diff --git a/src/applications/almanac/phid/AlmanacInterfacePHIDType.php b/src/applications/almanac/phid/AlmanacInterfacePHIDType.php
new file mode 100644
--- /dev/null
+++ b/src/applications/almanac/phid/AlmanacInterfacePHIDType.php
@@ -0,0 +1,38 @@
+<?php
+
+final class AlmanacInterfacePHIDType extends PhabricatorPHIDType {
+
+ const TYPECONST = 'AINT';
+
+ public function getTypeName() {
+ return pht('Almanac Interface');
+ }
+
+ public function newObject() {
+ return new AlmanacInterface();
+ }
+
+ protected function buildQueryForObjects(
+ PhabricatorObjectQuery $query,
+ array $phids) {
+
+ return id(new AlmanacInterfaceQuery())
+ ->withPHIDs($phids);
+ }
+
+ public function loadHandles(
+ PhabricatorHandleQuery $query,
+ array $handles,
+ array $objects) {
+
+ foreach ($handles as $phid => $handle) {
+ $interface = $objects[$phid];
+
+ $id = $interface->getID();
+
+ $handle->setObjectName(pht('Interface %d', $id));
+ $handle->setName(pht('Interface %d', $id));
+ }
+ }
+
+}
diff --git a/src/applications/almanac/query/AlmanacInterfaceQuery.php b/src/applications/almanac/query/AlmanacInterfaceQuery.php
new file mode 100644
--- /dev/null
+++ b/src/applications/almanac/query/AlmanacInterfaceQuery.php
@@ -0,0 +1,139 @@
+<?php
+
+final class AlmanacInterfaceQuery
+ extends PhabricatorCursorPagedPolicyAwareQuery {
+
+ private $ids;
+ private $phids;
+ private $networkPHIDs;
+ private $devicePHIDs;
+ private $addresses;
+
+ public function withIDs(array $ids) {
+ $this->ids = $ids;
+ return $this;
+ }
+
+ public function withPHIDs(array $phids) {
+ $this->phids = $phids;
+ return $this;
+ }
+
+ public function withNetworkPHIDs(array $phids) {
+ $this->networkPHIDs = $phids;
+ return $this;
+ }
+
+ public function withDevicePHIDs(array $phids) {
+ $this->devicePHIDs = $phids;
+ return $this;
+ }
+
+ public function withAddresses(array $addresses) {
+ $this->addresses = $addresses;
+ return $this;
+ }
+
+ protected function loadPage() {
+ $table = new AlmanacInterface();
+ $conn_r = $table->establishConnection('r');
+
+ $data = queryfx_all(
+ $conn_r,
+ 'SELECT * FROM %T %Q %Q %Q',
+ $table->getTableName(),
+ $this->buildWhereClause($conn_r),
+ $this->buildOrderClause($conn_r),
+ $this->buildLimitClause($conn_r));
+
+ return $table->loadAllFromArray($data);
+ }
+
+ protected function willFilterPage(array $interfaces) {
+ $network_phids = mpull($interfaces, 'getNetworkPHID');
+ $device_phids = mpull($interfaces, 'getDevicePHID');
+
+ $networks = id(new AlmanacNetworkQuery())
+ ->setParentQuery($this)
+ ->setViewer($this->getViewer())
+ ->withPHIDs($network_phids)
+ ->execute();
+ $networks = mpull($networks, null, 'getPHID');
+
+ $devices = id(new AlmanacDeviceQuery())
+ ->setParentQuery($this)
+ ->setViewer($this->getViewer())
+ ->withPHIDs($device_phids)
+ ->execute();
+ $devices = mpull($devices, null, 'getPHID');
+
+ foreach ($interfaces as $key => $interface) {
+ $network = idx($networks, $interface->getNetworkPHID());
+ $device = idx($devices, $interface->getDevicePHID());
+ if (!$network || !$device) {
+ $this->didRejectResult($interface);
+ unset($interfaces[$key]);
+ continue;
+ }
+
+ $interface->attachNetwork($network);
+ $interface->attachDevice($device);
+ }
+
+ return $interfaces;
+ }
+
+ protected function buildWhereClause($conn_r) {
+ $where = array();
+
+ if ($this->ids !== null) {
+ $where[] = qsprintf(
+ $conn_r,
+ 'id IN (%Ld)',
+ $this->ids);
+ }
+
+ if ($this->phids !== null) {
+ $where[] = qsprintf(
+ $conn_r,
+ 'phid IN (%Ls)',
+ $this->phids);
+ }
+
+ if ($this->networkPHIDs !== null) {
+ $where[] = qsprintf(
+ $conn_r,
+ 'networkPHID IN (%Ls)',
+ $this->networkPHIDs);
+ }
+
+ if ($this->devicePHIDs !== null) {
+ $where[] = qsprintf(
+ $conn_r,
+ 'devicePHID IN (%Ls)',
+ $this->devicePHIDs);
+ }
+
+ if ($this->addresses !== null) {
+ $parts = array();
+ foreach ($this->addresses as $address) {
+ $parts[] = qsprintf(
+ $conn_r,
+ '(networkPHID = %s AND address = %s AND port = %d)',
+ $address->getNetworkPHID(),
+ $address->getAddress(),
+ $address->getPort());
+ }
+ $where[] = implode(' OR ', $parts);
+ }
+
+ $where[] = $this->buildPagingClause($conn_r);
+
+ return $this->formatWhereClause($where);
+ }
+
+ public function getQueryApplicationClass() {
+ return 'PhabricatorAlmanacApplication';
+ }
+
+}
diff --git a/src/applications/almanac/storage/AlmanacDeviceTransaction.php b/src/applications/almanac/storage/AlmanacDeviceTransaction.php
--- a/src/applications/almanac/storage/AlmanacDeviceTransaction.php
+++ b/src/applications/almanac/storage/AlmanacDeviceTransaction.php
@@ -4,6 +4,7 @@
extends PhabricatorApplicationTransaction {
const TYPE_NAME = 'almanac:device:name';
+ const TYPE_INTERFACE = 'almanac:device:interface';
public function getApplicationName() {
return 'almanac';
@@ -17,6 +18,26 @@
return null;
}
+ public function getRequiredHandlePHIDs() {
+ $phids = parent::getRequiredHandlePHIDs();
+
+ $old = $this->getOldValue();
+ $new = $this->getNewValue();
+
+ switch ($this->getTransactionType()) {
+ case self::TYPE_INTERFACE:
+ if ($old) {
+ $phids[] = $old['networkPHID'];
+ }
+ if ($new) {
+ $phids[] = $new['networkPHID'];
+ }
+ break;
+ }
+
+ return $phids;
+ }
+
public function getTitle() {
$author_phid = $this->getAuthorPHID();
@@ -37,9 +58,43 @@
$new);
}
break;
+ case self::TYPE_INTERFACE:
+ if ($old && $new) {
+ return pht(
+ '%s changed interface %s on this device to %s.',
+ $this->renderHandleLink($author_phid),
+ $this->describeInterface($old),
+ $this->describeInterface($new));
+ } else if ($old) {
+ return pht(
+ '%s removed the interface %s from this device.',
+ $this->renderHandleLink($author_phid),
+ $this->describeInterface($new));
+ } else if ($new) {
+ return pht(
+ '%s added the interface %s to this device.',
+ $this->renderHandleLink($author_phid),
+ $this->describeInterface($new));
+ }
}
return parent::getTitle();
}
+ public function shouldGenerateOldValue() {
+ switch ($this->getTransactionType()) {
+ case self::TYPE_INTERFACE:
+ return false;
+ }
+ return parent::shouldGenerateOldValue();
+ }
+
+ private function describeInterface(array $info) {
+ return pht(
+ '%s:%s (%s)',
+ $info['address'],
+ $info['port'],
+ $this->renderHandleLink($info['networkPHID']));
+ }
+
}
diff --git a/src/applications/almanac/storage/AlmanacInterface.php b/src/applications/almanac/storage/AlmanacInterface.php
new file mode 100644
--- /dev/null
+++ b/src/applications/almanac/storage/AlmanacInterface.php
@@ -0,0 +1,99 @@
+<?php
+
+final class AlmanacInterface
+ extends AlmanacDAO
+ implements PhabricatorPolicyInterface {
+
+ protected $devicePHID;
+ protected $networkPHID;
+ protected $address;
+ protected $port;
+
+ private $device = self::ATTACHABLE;
+ private $network = self::ATTACHABLE;
+
+ public static function initializeNewInterface() {
+ return id(new AlmanacInterface());
+ }
+
+ public function getConfiguration() {
+ return array(
+ self::CONFIG_AUX_PHID => true,
+ self::CONFIG_COLUMN_SCHEMA => array(
+ 'address' => 'text64',
+ 'port' => 'uint32',
+ ),
+ self::CONFIG_KEY_SCHEMA => array(
+ 'key_location' => array(
+ 'columns' => array('networkPHID', 'address', 'port'),
+ ),
+ 'key_device' => array(
+ 'columns' => array('devicePHID'),
+ ),
+ ),
+ ) + parent::getConfiguration();
+ }
+
+ public function generatePHID() {
+ return PhabricatorPHID::generateNewPHID(
+ AlmanacInterfacePHIDType::TYPECONST);
+ }
+
+ public function getDevice() {
+ return $this->assertAttached($this->device);
+ }
+
+ public function attachDevice(AlmanacDevice $device) {
+ $this->device = $device;
+ return $this;
+ }
+
+ public function getNetwork() {
+ return $this->assertAttached($this->network);
+ }
+
+ public function attachNetwork(AlmanacNetwork $device) {
+ $this->device = $device;
+ return $this;
+ }
+
+ public function toAddress() {
+ return AlmanacAddress::newFromParts(
+ $this->getNetworkPHID(),
+ $this->getAddress(),
+ $this->getPort());
+ }
+
+ public function getAddressHash() {
+ return $this->toAddress()->toHash();
+ }
+
+
+/* -( PhabricatorPolicyInterface )----------------------------------------- */
+
+
+ public function getCapabilities() {
+ return array(
+ PhabricatorPolicyCapability::CAN_VIEW,
+ PhabricatorPolicyCapability::CAN_EDIT,
+ );
+ }
+
+ public function getPolicy($capability) {
+ return $this->getDevice()->getPolicy($capability);
+ }
+
+ public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
+ return $this->getDevice()->hasAutomaticCapability($capability, $viewer);
+ }
+
+ public function describeAutomaticCapability($capability) {
+ return array(
+ pht('An interface inherits the policies of the device it belongs to.'),
+ pht(
+ 'You must be able to view the network an interface resides on to '.
+ 'view the interface.'),
+ );
+ }
+
+}
diff --git a/src/applications/almanac/util/AlmanacAddress.php b/src/applications/almanac/util/AlmanacAddress.php
new file mode 100644
--- /dev/null
+++ b/src/applications/almanac/util/AlmanacAddress.php
@@ -0,0 +1,54 @@
+<?php
+
+final class AlmanacAddress extends Phobject {
+
+ private $networkPHID;
+ private $address;
+ private $port;
+
+ private function __construct() {
+ // <private>
+ }
+
+ public function getNetworkPHID() {
+ return $this->networkPHID;
+ }
+
+ public function getAddress() {
+ return $this->address;
+ }
+
+ public function getPort() {
+ return $this->port;
+ }
+
+ public static function newFromDictionary(array $dictionary) {
+ return self::newFromParts(
+ $dictionary['networkPHID'],
+ $dictionary['address'],
+ $dictionary['port']);
+ }
+
+ public static function newFromParts($network_phid, $address, $port) {
+ $addr = new AlmanacAddress();
+
+ $addr->networkPHID = $network_phid;
+ $addr->address = $address;
+ $addr->port = (int)$port;
+
+ return $addr;
+ }
+
+ public function toDictionary() {
+ return array(
+ 'networkPHID' => $this->getNetworkPHID(),
+ 'address' => $this->getAddress(),
+ 'port' => $this->getPort(),
+ );
+ }
+
+ public function toHash() {
+ return PhabricatorHash::digestForIndex(json_encode($this->toDictionary()));
+ }
+
+}
diff --git a/src/applications/almanac/view/AlmanacInterfaceTableView.php b/src/applications/almanac/view/AlmanacInterfaceTableView.php
new file mode 100644
--- /dev/null
+++ b/src/applications/almanac/view/AlmanacInterfaceTableView.php
@@ -0,0 +1,69 @@
+<?php
+
+final class AlmanacInterfaceTableView extends AphrontView {
+
+ private $interfaces;
+ private $handles;
+
+ public function setHandles(array $handles) {
+ $this->handles = $handles;
+ return $this;
+ }
+
+ public function getHandles() {
+ return $this->handles;
+ }
+
+ public function setInterfaces(array $interfaces) {
+ $this->interfaces = $interfaces;
+ return $this;
+ }
+
+ public function getInterfaces() {
+ return $this->interfaces;
+ }
+
+ public function render() {
+ $interfaces = $this->getInterfaces();
+ $handles = $this->getHandles();
+ $viewer = $this->getUser();
+
+ $rows = array();
+ foreach ($interfaces as $interface) {
+ $rows[] = array(
+ $interface->getID(),
+ $handles[$interface->getNetworkPHID()]->renderLink(),
+ $interface->getAddress(),
+ $interface->getPort(),
+ phutil_tag(
+ 'a',
+ array(
+ 'class' => 'small grey button',
+ 'href' => '/almanac/interface/edit/'.$interface->getID().'/',
+ ),
+ pht('Edit')),
+ );
+ }
+
+ $table = id(new AphrontTableView($rows))
+ ->setHeaders(
+ array(
+ pht('ID'),
+ pht('Network'),
+ pht('Address'),
+ pht('Port'),
+ null,
+ ))
+ ->setColumnClasses(
+ array(
+ '',
+ 'wide',
+ '',
+ '',
+ 'action',
+ ));
+
+ return $table;
+ }
+
+}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Mar 22, 1:12 PM (2 h, 36 m)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7700270
Default Alt Text
D10718.id25730.diff (29 KB)
Attached To
Mode
D10718: Build AlmanacInterface
Attached
Detach File
Event Timeline
Log In to Comment