public function handleRequest(AphrontRequest $request)
 {
     $viewer = $request->getViewer();
     $response = $this->loadPropertyObject();
     if ($response) {
         return $response;
     }
     $object = $this->getPropertyObject();
     $cancel_uri = $object->getURI();
     $property_key = $request->getStr('key');
     if (!strlen($property_key)) {
         return $this->buildPropertyKeyResponse($cancel_uri, null);
     } else {
         $error = null;
         try {
             AlmanacNames::validateName($property_key);
         } catch (Exception $ex) {
             $error = $ex->getMessage();
         }
         // NOTE: If you enter an existing name, we're just treating it as an
         // edit operation. This might be a little confusing.
         if ($error !== null) {
             if ($request->isFormPost()) {
                 // The user is creating a new property and picked a bad name. Give
                 // them an opportunity to fix it.
                 return $this->buildPropertyKeyResponse($cancel_uri, $error);
             } else {
                 // The user is editing an invalid property.
                 return $this->newDialog()->setTitle(pht('Invalid Property'))->appendParagraph(pht('The property name "%s" is invalid. This property can not ' . 'be edited.', $property_key))->appendParagraph($error)->addCancelButton($cancel_uri);
             }
         }
     }
     return $object->newAlmanacPropertyEditEngine()->addContextParameter('objectPHID')->addContextParameter('key')->setTargetObject($object)->setPropertyKey($property_key)->setController($this)->buildResponse();
 }
Exemple #2
0
 public function save()
 {
     AlmanacNames::validateName($this->getName());
     $this->nameIndex = PhabricatorHash::digestForIndex($this->getName());
     if (!$this->mailKey) {
         $this->mailKey = Filesystem::readRandomCharacters(20);
     }
     return parent::save();
 }
 public function testServiceOrDeviceNames()
 {
     $map = array('' => false, 'a' => false, 'ab' => false, '...' => false, 'ab.' => false, '.ab' => false, 'A-B' => false, 'A!B' => false, 'A.B' => false, 'a..b' => false, '1.2' => false, '127.0.0.1' => false, '1.b' => false, 'a.1' => false, 'a.1.b' => false, '-.a' => false, '-a.b' => false, 'a-.b' => false, 'a.-' => false, 'a.-b' => false, 'a.b-' => false, '-.-' => false, 'a--b' => false, 'abc' => true, 'a.b' => true, 'db.phacility.instance' => true, 'web002.useast.example.com' => true, 'master.example-corp.com' => true, str_repeat('a', 100) => true, str_repeat('a', 101) => false);
     foreach ($map as $input => $expect) {
         $caught = null;
         try {
             AlmanacNames::validateName($input);
         } catch (Exception $ex) {
             $caught = $ex;
         }
         $this->assertEqual($expect, !$caught instanceof Exception, $input);
     }
 }
 protected function validateTransaction(PhabricatorLiskDAO $object, $type, array $xactions)
 {
     $errors = parent::validateTransaction($object, $type, $xactions);
     switch ($type) {
         case AlmanacNamespaceTransaction::TYPE_NAME:
             $missing = $this->validateIsEmptyTextField($object->getName(), $xactions);
             if ($missing) {
                 $error = new PhabricatorApplicationTransactionValidationError($type, pht('Required'), pht('Namespace name is required.'), nonempty(last($xactions), null));
                 $error->setIsMissingFieldError(true);
                 $errors[] = $error;
             } else {
                 foreach ($xactions as $xaction) {
                     $name = $xaction->getNewValue();
                     $message = null;
                     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 AlmanacNamespaceQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withNames(array($name))->executeOne();
                     if ($other && $other->getID() != $object->getID()) {
                         $error = new PhabricatorApplicationTransactionValidationError($type, pht('Not Unique'), pht('The namespace name "%s" is already in use by another ' . 'namespace. Each namespace must have a unique name.', $name), $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 namespaces ' . 'within the "%s" namespace.', $namespace->getName()), $xaction);
                         $errors[] = $error;
                         continue;
                     }
                 }
             }
             break;
     }
     return $errors;
 }
 protected function validateTransaction(PhabricatorLiskDAO $object, $type, array $xactions)
 {
     $errors = parent::validateTransaction($object, $type, $xactions);
     switch ($type) {
         case AlmanacTransaction::TYPE_PROPERTY_UPDATE:
             foreach ($xactions as $xaction) {
                 $property_key = $xaction->getMetadataValue('almanac.property');
                 $message = null;
                 try {
                     AlmanacNames::validateName($property_key);
                 } catch (Exception $ex) {
                     $message = $ex->getMessage();
                 }
                 if ($message !== null) {
                     $error = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), $message, $xaction);
                     $errors[] = $error;
                     continue;
                 }
                 $new_value = $xaction->getNewValue();
                 try {
                     phutil_json_encode($new_value);
                 } catch (Exception $ex) {
                     $message = pht('Almanac property values must be representable in JSON. %s', $ex->getMessage());
                 }
                 if ($message !== null) {
                     $error = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), $message, $xaction);
                     $errors[] = $error;
                     continue;
                 }
             }
             break;
         case AlmanacTransaction::TYPE_PROPERTY_REMOVE:
             // NOTE: No name validation on removals since it's OK to delete
             // an invalid property that somehow came into existence.
             break;
     }
     return $errors;
 }
 protected function validateTransaction(PhabricatorLiskDAO $object, $type, array $xactions)
 {
     $errors = parent::validateTransaction($object, $type, $xactions);
     switch ($type) {
         case AlmanacServiceTransaction::TYPE_NAME:
             $missing = $this->validateIsEmptyTextField($object->getName(), $xactions);
             if ($missing) {
                 $error = new PhabricatorApplicationTransactionValidationError($type, pht('Required'), pht('Service name is required.'), nonempty(last($xactions), null));
                 $error->setIsMissingFieldError(true);
                 $errors[] = $error;
             } else {
                 foreach ($xactions as $xaction) {
                     $message = null;
                     $name = $xaction->getNewValue();
                     try {
                         AlmanacNames::validateServiceOrDeviceName($name);
                     } catch (Exception $ex) {
                         $message = $ex->getMessage();
                     }
                     if ($message !== null) {
                         $error = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), $message, $xaction);
                         $errors[] = $error;
                     }
                 }
             }
             if ($xactions) {
                 $duplicate = id(new AlmanacServiceQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withNames(array(last($xactions)->getNewValue()))->executeOne();
                 if ($duplicate && $duplicate->getID() != $object->getID()) {
                     $error = new PhabricatorApplicationTransactionValidationError($type, pht('Not Unique'), pht('Almanac services must have unique names.'), last($xactions));
                     $errors[] = $error;
                 }
             }
             break;
     }
     return $errors;
 }
 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::validateServiceOrDeviceName($name);
                     } catch (Exception $ex) {
                         $message = $ex->getMessage();
                     }
                     if ($message !== null) {
                         $error = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), $message, $xaction);
                         $errors[] = $error;
                     }
                 }
             }
             if ($xactions) {
                 $duplicate = id(new AlmanacDeviceQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withNames(array(last($xactions)->getNewValue()))->executeOne();
                 if ($duplicate && $duplicate->getID() != $object->getID()) {
                     $error = new PhabricatorApplicationTransactionValidationError($type, pht('Not Unique'), pht('Almanac devices must have unique names.'), last($xactions));
                     $errors[] = $error;
                 }
             }
             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;
                     }
                 }
             }
             break;
     }
     return $errors;
 }
 public function handleRequest(AphrontRequest $request)
 {
     $viewer = $request->getViewer();
     $object = id(new PhabricatorObjectQuery())->setViewer($viewer)->withPHIDs(array($request->getStr('objectPHID')))->requireCapabilities(array(PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT))->executeOne();
     if (!$object) {
         return new Aphront404Response();
     }
     if (!$object instanceof AlmanacPropertyInterface) {
         return new Aphront404Response();
     }
     $cancel_uri = $object->getURI();
     $key = $request->getStr('key');
     if ($key) {
         $property_key = $key;
         $is_new = false;
         $title = pht('Edit Property');
         $save_button = pht('Save Changes');
     } else {
         $property_key = null;
         $is_new = true;
         $title = pht('Add Property');
         $save_button = pht('Add Property');
     }
     if ($is_new) {
         $errors = array();
         $property = null;
         $v_name = null;
         $e_name = true;
         if ($request->isFormPost()) {
             $name = $request->getStr('name');
             if (!strlen($name)) {
                 $e_name = pht('Required');
                 $errors[] = pht('You must provide a property name.');
             } else {
                 $caught = null;
                 try {
                     AlmanacNames::validateServiceOrDeviceName($name);
                 } catch (Exception $ex) {
                     $caught = $ex;
                 }
                 if ($caught) {
                     $e_name = pht('Invalid');
                     $errors[] = $caught->getMessage();
                 }
             }
             if (!$errors) {
                 $property_key = $name;
             }
         }
         if ($property_key === null) {
             $form = id(new AphrontFormView())->setUser($viewer)->appendChild(id(new AphrontFormTextControl())->setName('name')->setLabel(pht('Name'))->setValue($v_name)->setError($e_name));
             return $this->newDialog()->setTitle($title)->setErrors($errors)->addHiddenInput('objectPHID', $request->getStr('objectPHID'))->appendForm($form)->addSubmitButton(pht('Continue'))->addCancelButton($cancel_uri);
         }
     }
     // Make sure property key is appropriate.
     // TODO: It would be cleaner to put this safety check in the Editor.
     AlmanacNames::validateServiceOrDeviceName($property_key);
     // If we're adding a new property, put a placeholder on the object so
     // that we can build a CustomField for it.
     if (!$object->hasAlmanacProperty($property_key)) {
         $temporary_property = id(new AlmanacProperty())->setObjectPHID($object->getPHID())->setFieldName($property_key);
         $object->attachAlmanacProperties(array($temporary_property));
     }
     $field_list = PhabricatorCustomField::getObjectFields($object, PhabricatorCustomField::ROLE_DEFAULT);
     // Select only the field being edited.
     $fields = $field_list->getFields();
     $fields = array_select_keys($fields, array($property_key));
     $field_list = new PhabricatorCustomFieldList($fields);
     $field_list->setViewer($viewer)->readFieldsFromStorage($object);
     $validation_exception = null;
     if ($request->isFormPost() && $request->getStr('isValueEdit')) {
         $xactions = $field_list->buildFieldTransactionsFromRequest($object->getApplicationTransactionTemplate(), $request);
         $editor = $object->getApplicationTransactionEditor()->setActor($viewer)->setContentSourceFromRequest($request)->setContinueOnNoEffect(true)->setContinueOnMissingFields(true);
         try {
             $editor->applyTransactions($object, $xactions);
             return id(new AphrontRedirectResponse())->setURI($cancel_uri);
         } catch (PhabricatorApplicationTransactionValidationException $ex) {
             $validation_exception = $ex;
         }
     }
     $form = id(new AphrontFormView())->setUser($viewer)->addHiddenInput('objectPHID', $request->getStr('objectPHID'))->addHiddenInput('key', $request->getStr('key'))->addHiddenInput('name', $property_key)->addHiddenInput('isValueEdit', true);
     $field_list->appendFieldsToForm($form);
     return $this->newDialog()->setTitle($title)->setValidationException($validation_exception)->appendForm($form)->addSubmitButton($save_button)->addCancelButton($cancel_uri);
 }