getTransactionType()) { case AlmanacDeviceTransaction::TYPE_NAME: return $object->getName(); } return parent::getCustomTransactionOldValue($object, $xaction); } protected function getCustomTransactionNewValue( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { case AlmanacDeviceTransaction::TYPE_NAME: case AlmanacDeviceTransaction::TYPE_INTERFACE: return $xaction->getNewValue(); } return parent::getCustomTransactionNewValue($object, $xaction); } protected function applyCustomInternalTransaction( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { case AlmanacDeviceTransaction::TYPE_NAME: $object->setName($xaction->getNewValue()); return; case AlmanacDeviceTransaction::TYPE_INTERFACE: return; } return parent::applyCustomInternalTransaction($object, $xaction); } protected function applyCustomExternalTransaction( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { case AlmanacDeviceTransaction::TYPE_NAME: 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']); if (idx($new, 'phid')) { $interface->setPHID($new['phid']); } $interface->save(); } else { $interface->delete(); } return; } return parent::applyCustomExternalTransaction($object, $xaction); } protected function validateTransaction( PhabricatorLiskDAO $object, $type, array $xactions) { $errors = parent::validateTransaction($object, $type, $xactions); switch ($type) { case AlmanacDeviceTransaction::TYPE_NAME: $missing = $this->validateIsEmptyTextField( $object->getName(), $xactions); if ($missing) { $error = new PhabricatorApplicationTransactionValidationError( $type, pht('Required'), pht('Device name is required.'), nonempty(last($xactions), null)); $error->setIsMissingFieldError(true); $errors[] = $error; } else { foreach ($xactions as $xaction) { $message = null; $name = $xaction->getNewValue(); try { AlmanacNames::validateName($name); } catch (Exception $ex) { $message = $ex->getMessage(); } if ($message !== null) { $error = new PhabricatorApplicationTransactionValidationError( $type, pht('Invalid'), $message, $xaction); $errors[] = $error; continue; } $other = id(new AlmanacDeviceQuery()) ->setViewer(PhabricatorUser::getOmnipotentUser()) ->withNames(array($name)) ->executeOne(); if ($other && ($other->getID() != $object->getID())) { $error = new PhabricatorApplicationTransactionValidationError( $type, pht('Not Unique'), pht('Almanac devices must have unique names.'), $xaction); $errors[] = $error; continue; } if ($name === $object->getName()) { continue; } $namespace = AlmanacNamespace::loadRestrictedNamespace( $this->getActor(), $name); if ($namespace) { $error = new PhabricatorApplicationTransactionValidationError( $type, pht('Restricted'), pht( 'You do not have permission to create Almanac devices '. 'within the "%s" namespace.', $namespace->getName()), $xaction); $errors[] = $error; continue; } } } 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; } $phid = idx($new, 'phid'); if ($phid) { $interface_phid_type = AlmanacInterfacePHIDType::TYPECONST; if (phid_get_type($phid) !== $interface_phid_type) { $error = new PhabricatorApplicationTransactionValidationError( $type, pht('Invalid'), pht( 'Precomputed interface PHIDs must be of type '. 'AlmanacInterfacePHIDType.'), $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; continue; } $new = $xaction->getNewValue(); if (!$new) { if ($interface->loadIsInUse()) { $error = new PhabricatorApplicationTransactionValidationError( $type, pht('In Use'), pht('You can not delete an interface which is still in use.'), $xaction); $errors[] = $error; } } } } break; } return $errors; } }