Page MenuHomePhabricator

D10718.diff
No OneTemporary

D10718.diff

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',
@@ -2940,6 +2946,7 @@
'require_celerity_resource' => 'applications/celerity/api.php',
),
'xmap' => array(
+ 'AlmanacAddress' => 'Phobject',
'AlmanacConduitUtil' => 'Phobject',
'AlmanacConsoleController' => 'AlmanacController',
'AlmanacController' => 'PhabricatorController',
@@ -2962,6 +2969,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

Mime Type
text/plain
Expires
Thu, Mar 6, 2:34 AM (2 w, 2 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7228512
Default Alt Text
D10718.diff (29 KB)

Event Timeline