protected function validateTransaction(PhabricatorLiskDAO $object, $type, array $xactions) { $errors = parent::validateTransaction($object, $type, $xactions); switch ($type) { case PhortuneAccountTransaction::TYPE_NAME: $missing = $this->validateIsEmptyTextField($object->getName(), $xactions); if ($missing) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Required'), pht('Account name is required.'), nonempty(last($xactions), null)); $error->setIsMissingFieldError(true); $errors[] = $error; } break; case PhabricatorTransactions::TYPE_EDGE: foreach ($xactions as $xaction) { switch ($xaction->getMetadataValue('edge:type')) { case PhortuneAccountHasMemberEdgeType::EDGECONST: // TODO: This is a bit cumbersome, but validation happens before // transaction normalization. Maybe provide a cleaner attack on // this eventually? There's no way to generate "+" or "-" // transactions right now. $new = $xaction->getNewValue(); $set = idx($new, '=', array()); if (empty($set[$this->requireActor()->getPHID()])) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('You can not remove yourself as an account member.'), $xaction); $errors[] = $error; } break; } } break; } return $errors; }
protected function validateTransaction(PhabricatorLiskDAO $object, $type, array $xactions) { $errors = parent::validateTransaction($object, $type, $xactions); switch ($type) { case PhabricatorPhurlURLTransaction::TYPE_NAME: $missing = $this->validateIsEmptyTextField($object->getName(), $xactions); if ($missing) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Required'), pht('URL name is required.'), nonempty(last($xactions), null)); $error->setIsMissingFieldError(true); $errors[] = $error; } break; case PhabricatorPhurlURLTransaction::TYPE_URL: $missing = $this->validateIsEmptyTextField($object->getLongURL(), $xactions); if ($missing) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Required'), pht('URL path is required.'), nonempty(last($xactions), null)); $error->setIsMissingFieldError(true); $errors[] = $error; } foreach ($xactions as $xaction) { if ($xaction->getOldValue() != $xaction->getNewValue()) { $protocols = PhabricatorEnv::getEnvConfig('uri.allowed-protocols'); $uri = new PhutilURI($xaction->getNewValue()); if (!isset($protocols[$uri->getProtocol()])) { $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid URL'), pht('The protocol of the URL is invalid.'), null); } } } break; } return $errors; }
protected function validateTransaction(PhabricatorLiskDAO $object, $type, array $xactions) { $errors = parent::validateTransaction($object, $type, $xactions); switch ($type) { case PhabricatorOAuthServerTransaction::TYPE_NAME: $missing = $this->validateIsEmptyTextField($object->getName(), $xactions); if ($missing) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Required'), pht('OAuth applications must have a name.'), nonempty(last($xactions), null)); $error->setIsMissingFieldError(true); $errors[] = $error; } break; case PhabricatorOAuthServerTransaction::TYPE_REDIRECT_URI: $missing = $this->validateIsEmptyTextField($object->getRedirectURI(), $xactions); if ($missing) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Required'), pht('OAuth applications must have a valid redirect URI.'), nonempty(last($xactions), null)); $error->setIsMissingFieldError(true); $errors[] = $error; } else { foreach ($xactions as $xaction) { $redirect_uri = $xaction->getNewValue(); try { $server = new PhabricatorOAuthServer(); $server->assertValidRedirectURI($redirect_uri); } catch (Exception $ex) { $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), $ex->getMessage(), $xaction); } } } break; } return $errors; }
protected function validateTransaction(PhabricatorLiskDAO $object, $type, array $xactions) { $errors = parent::validateTransaction($object, $type, $xactions); switch ($type) { case PhabricatorCountdownTransaction::TYPE_TITLE: $missing = $this->validateIsEmptyTextField($object->getTitle(), $xactions); if ($missing) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Required'), pht('You must give the countdown a name.'), nonempty(last($xactions), null)); $error->setIsMissingFieldError(true); $errors[] = $error; } break; case PhabricatorCountdownTransaction::TYPE_EPOCH: if (!$object->getEpoch() && !$xactions) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Required'), pht('You must give the countdown an end date.'), null); $error->setIsMissingFieldError(true); $errors[] = $error; } foreach ($xactions as $xaction) { $value = $xaction->getNewValue(); if (!$value->isValid()) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('You must give the countdown a valid end date.'), $xaction); $errors[] = $error; } } break; } return $errors; }
protected function validateTransaction(PhabricatorLiskDAO $object, $type, array $xactions) { $errors = parent::validateTransaction($object, $type, $xactions); switch ($type) { case PhamePostTransaction::TYPE_TITLE: $missing = $this->validateIsEmptyTextField($object->getTitle(), $xactions); if ($missing) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Required'), pht('Title is required.'), nonempty(last($xactions), null)); $error->setIsMissingFieldError(true); $errors[] = $error; } break; case PhamePostTransaction::TYPE_PHAME_TITLE: if (!$xactions) { continue; } $missing = $this->validateIsEmptyTextField($object->getPhameTitle(), $xactions); $phame_title = last($xactions)->getNewValue(); if ($missing || $phame_title == '/') { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Required'), pht('Phame title is required.'), nonempty(last($xactions), null)); $error->setIsMissingFieldError(true); $errors[] = $error; } $duplicate_post = id(new PhamePostQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withPhameTitles(array($phame_title))->executeOne(); if ($duplicate_post && $duplicate_post->getID() != $object->getID()) { $error_text = pht('Phame title must be unique; another post already has this phame ' . 'title.'); $error = new PhabricatorApplicationTransactionValidationError($type, pht('Not Unique'), $error_text, nonempty(last($xactions), null)); $errors[] = $error; } break; } return $errors; }
protected function validateTransaction(PhabricatorLiskDAO $object, $type, array $xactions) { $errors = parent::validateTransaction($object, $type, $xactions); switch ($type) { case AlmanacBindingTransaction::TYPE_INTERFACE: $missing = $this->validateIsEmptyTextField($object->getInterfacePHID(), $xactions); if ($missing) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Required'), pht('Bindings must specify an interface.'), nonempty(last($xactions), null)); $error->setIsMissingFieldError(true); $errors[] = $error; } else { if ($xactions) { foreach ($xactions as $xaction) { $interfaces = id(new AlmanacInterfaceQuery())->setViewer($this->requireActor())->withPHIDs(array($xaction->getNewValue()))->execute(); if (!$interfaces) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('You can not bind a service to an invalid or restricted ' . 'interface.'), $xaction); $errors[] = $error; } } $final_value = last($xactions)->getNewValue(); $binding = id(new AlmanacBindingQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withServicePHIDs(array($object->getServicePHID()))->withInterfacePHIDs(array($final_value))->executeOne(); if ($binding && $binding->getID() != $object->getID()) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Already Bound'), pht('You can not bind a service to the same interface multiple ' . 'times.'), last($xactions)); $errors[] = $error; } } } break; } return $errors; }
protected function validateTransaction(PhabricatorLiskDAO $object, $type, array $xactions) { $errors = parent::validateTransaction($object, $type, $xactions); switch ($type) { case PhamePostTransaction::TYPE_TITLE: $missing = $this->validateIsEmptyTextField($object->getTitle(), $xactions); if ($missing) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Required'), pht('Title is required.'), nonempty(last($xactions), null)); $error->setIsMissingFieldError(true); $errors[] = $error; } break; case PhamePostTransaction::TYPE_BLOG: if ($this->getIsNewObject()) { if (!$xactions) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Required'), pht('When creating a post, you must specify which blog it ' . 'should belong to.'), null); $error->setIsMissingFieldError(true); $errors[] = $error; break; } } foreach ($xactions as $xaction) { $new_phid = $xaction->getNewValue(); $blog = id(new PhameBlogQuery())->setViewer($this->getActor())->withPHIDs(array($new_phid))->requireCapabilities(array(PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT))->execute(); if ($blog) { continue; } $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('The specified blog PHID ("%s") is not valid. You can only ' . 'create a post on (or move a post into) a blog which you ' . 'have permission to see and edit.', $new_phid), $xaction); } break; } return $errors; }
protected function validateTransaction(PhabricatorLiskDAO $object, $type, array $xactions) { $errors = parent::validateTransaction($object, $type, $xactions); switch ($type) { case PhabricatorProjectColumnTransaction::TYPE_LIMIT: foreach ($xactions as $xaction) { $value = $xaction->getNewValue(); if (strlen($value) && !preg_match('/^\\d+\\z/', $value)) { $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('Column point limit must either be empty or a nonnegative ' . 'integer.'), $xaction); } } break; case PhabricatorProjectColumnTransaction::TYPE_NAME: $missing = $this->validateIsEmptyTextField($object->getName(), $xactions); // The default "Backlog" column is allowed to be unnamed, which // means we use the default name. if ($missing && !$object->isDefaultColumn()) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Required'), pht('Column name is required.'), nonempty(last($xactions), null)); $error->setIsMissingFieldError(true); $errors[] = $error; } break; } return $errors; }
/** * We run Herald as part of transaction validation because Herald can * block diff creation for Differential diffs. Its important to do this * separately so no Herald logs are saved; these logs could expose * information the Herald rules are inteneded to block. */ protected function validateTransaction(PhabricatorLiskDAO $object, $type, array $xactions) { $errors = parent::validateTransaction($object, $type, $xactions); foreach ($xactions as $xaction) { switch ($type) { case DifferentialDiffTransaction::TYPE_DIFF_CREATE: $diff = clone $object; $diff = $this->updateDiffFromDict($diff, $xaction->getNewValue()); $adapter = $this->buildHeraldAdapter($diff, $xactions); $adapter->setContentSource($this->getContentSource()); $adapter->setIsNewObject($this->getIsNewObject()); $engine = new HeraldEngine(); $rules = $engine->loadRulesForAdapter($adapter); $rules = mpull($rules, null, 'getID'); $effects = $engine->applyRules($rules, $adapter); $blocking_effect = null; foreach ($effects as $effect) { if ($effect->getAction() == HeraldAdapter::ACTION_BLOCK) { $blocking_effect = $effect; break; } } if ($blocking_effect) { $rule = $blocking_effect->getRule(); $message = $effect->getTarget(); if (!strlen($message)) { $message = pht('(None.)'); } $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Rejected by Herald'), pht("Creation of this diff was rejected by Herald rule %s.\n" . " Rule: %s\n" . "Reason: %s", $rule->getMonogram(), $rule->getName(), $message)); } break; } } return $errors; }
protected function validateTransaction(PhabricatorLiskDAO $object, $type, array $xactions) { $errors = parent::validateTransaction($object, $type, $xactions); switch ($type) { case PhabricatorMetaMTAApplicationEmailTransaction::TYPE_ADDRESS: foreach ($xactions as $xaction) { $email = $xaction->getNewValue(); if (!strlen($email)) { // We'll deal with this below. continue; } if (!PhabricatorUserEmail::isValidAddress($email)) { $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('Email address is not formatted properly.')); } } $missing = $this->validateIsEmptyTextField($object->getAddress(), $xactions); if ($missing) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Required'), pht('You must provide an email address.'), nonempty(last($xactions), null)); $error->setIsMissingFieldError(true); $errors[] = $error; } break; } return $errors; }
protected function validateTransaction(PhabricatorLiskDAO $object, $type, array $xactions) { $errors = parent::validateTransaction($object, $type, $xactions); switch ($type) { case PhabricatorPasteTransaction::TYPE_CONTENT: if (!$object->getFilePHID() && !$xactions) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Required'), pht('You must provide content to create a paste.'), null); $error->setIsMissingFieldError(true); $errors[] = $error; } break; } return $errors; }
protected function validateTransaction(PhabricatorLiskDAO $object, $type, array $xactions) { $errors = parent::validateTransaction($object, $type, $xactions); switch ($type) { case PhabricatorPhurlURLTransaction::TYPE_NAME: $missing = $this->validateIsEmptyTextField($object->getName(), $xactions); if ($missing) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Required'), pht('URL name is required.'), nonempty(last($xactions), null)); $error->setIsMissingFieldError(true); $errors[] = $error; } break; case PhabricatorPhurlURLTransaction::TYPE_ALIAS: $overdrawn = $this->validateIsTextFieldTooLong($object->getName(), $xactions, 64); if ($overdrawn) { $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Alias Too Long'), pht('The alias can be no longer than 64 characters.'), nonempty(last($xactions), null)); } foreach ($xactions as $xaction) { if ($xaction->getOldValue() != $xaction->getNewValue()) { $new_alias = $xaction->getNewValue(); if (!preg_match('/[a-zA-Z]/', $new_alias)) { $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid Alias'), pht('The alias must contain at least one letter.'), $xaction); } if (preg_match('/[^a-z0-9]/i', $new_alias)) { $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid Alias'), pht('The alias may only contain letters and numbers.'), $xaction); } } } break; case PhabricatorPhurlURLTransaction::TYPE_URL: $missing = $this->validateIsEmptyTextField($object->getLongURL(), $xactions); if ($missing) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Required'), pht('URL path is required.'), nonempty(last($xactions), null)); $error->setIsMissingFieldError(true); $errors[] = $error; } foreach ($xactions as $xaction) { if ($xaction->getOldValue() != $xaction->getNewValue()) { $protocols = PhabricatorEnv::getEnvConfig('uri.allowed-protocols'); $uri = new PhutilURI($xaction->getNewValue()); if (!isset($protocols[$uri->getProtocol()])) { $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid URL'), pht('The protocol of the URL is invalid.'), null); } } } break; } return $errors; }
protected function validateTransaction(PhabricatorLiskDAO $object, $type, array $xactions) { $errors = parent::validateTransaction($object, $type, $xactions); switch ($type) { case HarbormasterBuildPlanTransaction::TYPE_NAME: $missing = $this->validateIsEmptyTextField($object->getName(), $xactions); if ($missing) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Required'), pht('You must choose a name for your build plan.'), last($xactions)); $error->setIsMissingFieldError(true); $errors[] = $error; } break; } return $errors; }
protected function validateTransaction(PhabricatorLiskDAO $object, $type, array $xactions) { $errors = parent::validateTransaction($object, $type, $xactions); switch ($type) { case FundInitiativeTransaction::TYPE_NAME: $missing = $this->validateIsEmptyTextField($object->getName(), $xactions); if ($missing) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Required'), pht('Initiative name is required.'), nonempty(last($xactions), null)); $error->setIsMissingFieldError(true); $errors[] = $error; } break; } return $errors; }
protected function validateTransaction(PhabricatorLiskDAO $object, $type, array $xactions) { $errors = parent::validateTransaction($object, $type, $xactions); switch ($type) { case DrydockBlueprintTransaction::TYPE_NAME: $missing = $this->validateIsEmptyTextField($object->getBlueprintName(), $xactions); if ($missing) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Required'), pht('You must choose a name for this blueprint.'), nonempty(last($xactions), null)); $error->setIsMissingFieldError(true); $errors[] = $error; continue; } break; } return $errors; }
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 PhameBlogTransaction::TYPE_NAME: $missing = $this->validateIsEmptyTextField($object->getName(), $xactions); if ($missing) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Required'), pht('Name is required.'), nonempty(last($xactions), null)); $error->setIsMissingFieldError(true); $errors[] = $error; } break; case PhameBlogTransaction::TYPE_DOMAIN: if (!$xactions) { continue; } $custom_domain = last($xactions)->getNewValue(); if (empty($custom_domain)) { continue; } list($error_label, $error_text) = $object->validateCustomDomain($custom_domain); if ($error_label) { $error = new PhabricatorApplicationTransactionValidationError($type, $error_label, $error_text, nonempty(last($xactions), null)); $errors[] = $error; } if ($object->getViewPolicy() != PhabricatorPolicies::POLICY_PUBLIC) { $error_text = pht('For custom domains to work, the blog must have a view policy of ' . 'public.'); $error = new PhabricatorApplicationTransactionValidationError(PhabricatorTransactions::TYPE_VIEW_POLICY, pht('Invalid Policy'), $error_text, nonempty(last($xactions), null)); $errors[] = $error; } $duplicate_blog = id(new PhameBlogQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withDomain($custom_domain)->executeOne(); if ($duplicate_blog && $duplicate_blog->getID() != $object->getID()) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Not Unique'), pht('Domain must be unique; another blog already has this domain.'), nonempty(last($xactions), null)); $errors[] = $error; } break; } return $errors; }
protected function validateTransaction(PhabricatorLiskDAO $object, $type, array $xactions) { $errors = parent::validateTransaction($object, $type, $xactions); switch ($type) { case PhabricatorAuthSSHKeyTransaction::TYPE_NAME: $missing = $this->validateIsEmptyTextField($object->getName(), $xactions); if ($missing) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Required'), pht('SSH key name is required.'), nonempty(last($xactions), null)); $error->setIsMissingFieldError(true); $errors[] = $error; } break; case PhabricatorAuthSSHKeyTransaction::TYPE_KEY: $missing = $this->validateIsEmptyTextField($object->getName(), $xactions); if ($missing) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Required'), pht('SSH key material is required.'), nonempty(last($xactions), null)); $error->setIsMissingFieldError(true); $errors[] = $error; } else { foreach ($xactions as $xaction) { $new = $xaction->getNewValue(); try { $public_key = PhabricatorAuthSSHPublicKey::newFromRawKey($new); } catch (Exception $ex) { $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), $ex->getMessage(), $xaction); } } } break; case PhabricatorAuthSSHKeyTransaction::TYPE_DEACTIVATE: foreach ($xactions as $xaction) { if (!$xaction->getNewValue()) { $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('SSH keys can not be reactivated.'), $xaction); } } 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); $settings = $this->getSettings(); switch ($type) { case PhabricatorUserPreferencesTransaction::TYPE_SETTING: foreach ($xactions as $xaction) { $setting_key = $xaction->getMetadataValue(PhabricatorUserPreferencesTransaction::PROPERTY_SETTING); $setting = idx($settings, $setting_key); if (!$setting) { $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('There is no known application setting with key "%s".', $setting_key), $xaction); continue; } try { $setting->validateTransactionValue($xaction->getNewValue()); } catch (Exception $ex) { $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), $ex->getMessage(), $xaction); } } 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 NuanceSourceTransaction::TYPE_NAME: $missing = $this->validateIsEmptyTextField($object->getName(), $xactions); if ($missing) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Required'), pht('Source name is required.'), nonempty(last($xactions), null)); $error->setIsMissingFieldError(true); $errors[] = $error; } break; case NuanceSourceTransaction::TYPE_DEFAULT_QUEUE: foreach ($xactions as $xaction) { if (!$xaction->getNewValue()) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Required'), pht('Sources must have a default queue.'), $xaction); $error->setIsMissingFieldError(true); $errors[] = $error; } } break; } return $errors; }
protected function validateTransaction(PhabricatorLiskDAO $object, $type, array $xactions) { $errors = parent::validateTransaction($object, $type, $xactions); switch ($type) { case PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE: case PhabricatorRepositoryTransaction::TYPE_TRACK_ONLY: foreach ($xactions as $xaction) { foreach ($xaction->getNewValue() as $pattern) { // Check for invalid regular expressions. $regexp = PhabricatorRepository::extractBranchRegexp($pattern); if ($regexp !== null) { $ok = @preg_match($regexp, ''); if ($ok === false) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('Expression "%s" is not a valid regular expression. Note ' . 'that you must include delimiters.', $regexp), $xaction); $errors[] = $error; continue; } } // Check for formatting mistakes like `regex(...)` instead of // `regexp(...)`. $matches = null; if (preg_match('/^([^(]+)\\(.*\\)\\z/', $pattern, $matches)) { switch ($matches[1]) { case 'regexp': break; default: $error = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('Matching function "%s(...)" is not recognized. Valid ' . 'functions are: regexp(...).', $matches[1]), $xaction); $errors[] = $error; break; } } } } break; case PhabricatorRepositoryTransaction::TYPE_REMOTE_URI: foreach ($xactions as $xaction) { $new_uri = $xaction->getNewValue(); try { PhabricatorRepository::assertValidRemoteURI($new_uri); } catch (Exception $ex) { $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), $ex->getMessage(), $xaction); } } break; case PhabricatorRepositoryTransaction::TYPE_CREDENTIAL: $ok = PassphraseCredentialControl::validateTransactions($this->getActor(), $xactions); if (!$ok) { foreach ($xactions as $xaction) { $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('The selected credential does not exist, or you do not have ' . 'permission to use it.'), $xaction); } } break; case PhabricatorRepositoryTransaction::TYPE_AUTOMATION_BLUEPRINTS: foreach ($xactions as $xaction) { $old = nonempty($xaction->getOldValue(), array()); $new = nonempty($xaction->getNewValue(), array()); $add = array_diff($new, $old); $invalid = PhabricatorObjectQuery::loadInvalidPHIDsForViewer($this->getActor(), $add); if ($invalid) { $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('Some of the selected automation blueprints are invalid ' . 'or restricted: %s.', implode(', ', $invalid)), $xaction); } } break; case PhabricatorRepositoryTransaction::TYPE_SLUG: foreach ($xactions as $xaction) { $old = $xaction->getOldValue(); $new = $xaction->getNewValue(); if (!strlen($new)) { continue; } if ($new === $old) { continue; } try { PhabricatorRepository::asssertValidRepositorySlug($new); } catch (Exception $ex) { $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), $ex->getMessage(), $xaction); continue; } $other = id(new PhabricatorRepositoryQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withSlugs(array($new))->executeOne(); if ($other && $other->getID() !== $object->getID()) { $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Duplicate'), pht('The selected repository short name is already in use by ' . 'another repository. Choose a unique short name.'), $xaction); continue; } } break; } return $errors; }
protected function validateTransaction(PhabricatorLiskDAO $object, $type, array $xactions) { $errors = parent::validateTransaction($object, $type, $xactions); $config_self_accept_key = 'differential.allow-self-accept'; $allow_self_accept = PhabricatorEnv::getEnvConfig($config_self_accept_key); foreach ($xactions as $xaction) { switch ($type) { case PhabricatorTransactions::TYPE_EDGE: switch ($xaction->getMetadataValue('edge:type')) { case DifferentialRevisionHasReviewerEdgeType::EDGECONST: // Prevent the author from becoming a reviewer. // NOTE: This is pretty gross, but this restriction is unusual. // If we end up with too much more of this, we should try to clean // this up -- maybe by moving validation to after transactions // are adjusted (so we can just examine the final value) or adding // a second phase there? $author_phid = $object->getAuthorPHID(); $new = $xaction->getNewValue(); $add = idx($new, '+', array()); $eq = idx($new, '=', array()); $phids = array_keys($add + $eq); foreach ($phids as $phid) { if ($phid == $author_phid && !$allow_self_accept && !$xaction->getIsCommandeerSideEffect()) { $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('The author of a revision can not be a reviewer.'), $xaction); } } break; } break; case DifferentialTransaction::TYPE_UPDATE: $diff = $this->loadDiff($xaction->getNewValue()); if (!$diff) { $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('The specified diff does not exist.'), $xaction); } else { if ($diff->getRevisionID() && $diff->getRevisionID() != $object->getID()) { $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('You can not update this revision to the specified diff, ' . 'because the diff is already attached to another revision.'), $xaction); } } break; case DifferentialTransaction::TYPE_ACTION: $error = $this->validateDifferentialAction($object, $type, $xaction, $xaction->getNewValue()); if ($error) { $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), $error, $xaction); } break; } } return $errors; }
protected function validateTransaction(PhabricatorLiskDAO $object, $type, array $xactions) { $errors = parent::validateTransaction($object, $type, $xactions); switch ($type) { case PhabricatorProjectTransaction::TYPE_NAME: $missing = $this->validateIsEmptyTextField($object->getName(), $xactions); if ($missing) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Required'), pht('Project name is required.'), nonempty(last($xactions), null)); $error->setIsMissingFieldError(true); $errors[] = $error; } if (!$xactions) { break; } if ($this->getIsMilestone()) { break; } $name = last($xactions)->getNewValue(); if (!PhabricatorSlug::isValidProjectSlug($name)) { $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('Project names must contain at least one letter or number.'), last($xactions)); break; } $slug = PhabricatorSlug::normalizeProjectSlug($name); $slug_used_already = id(new PhabricatorProjectSlug())->loadOneWhere('slug = %s', $slug); if ($slug_used_already && $slug_used_already->getProjectPHID() != $object->getPHID()) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Duplicate'), pht('Project name generates the same hashtag ("%s") as another ' . 'existing project. Choose a unique name.', '#' . $slug), nonempty(last($xactions), null)); $errors[] = $error; } break; case PhabricatorProjectTransaction::TYPE_SLUGS: if (!$xactions) { break; } $slug_xaction = last($xactions); $new = $slug_xaction->getNewValue(); $invalid = array(); foreach ($new as $slug) { if (!PhabricatorSlug::isValidProjectSlug($slug)) { $invalid[] = $slug; } } if ($invalid) { $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('Hashtags must contain at least one letter or number. %s ' . 'project hashtag(s) are invalid: %s.', phutil_count($invalid), implode(', ', $invalid)), $slug_xaction); break; } $new = $this->normalizeSlugs($new); if ($new) { $slugs_used_already = id(new PhabricatorProjectSlug())->loadAllWhere('slug IN (%Ls)', $new); } else { // The project doesn't have any extra slugs. $slugs_used_already = array(); } $slugs_used_already = mgroup($slugs_used_already, 'getProjectPHID'); foreach ($slugs_used_already as $project_phid => $used_slugs) { if ($project_phid == $object->getPHID()) { continue; } $used_slug_strs = mpull($used_slugs, 'getSlug'); $error = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('%s project hashtag(s) are already used by other projects: %s.', phutil_count($used_slug_strs), implode(', ', $used_slug_strs)), $slug_xaction); $errors[] = $error; } break; case PhabricatorProjectTransaction::TYPE_PARENT: case PhabricatorProjectTransaction::TYPE_MILESTONE: if (!$xactions) { break; } $xaction = last($xactions); $parent_phid = $xaction->getNewValue(); if (!$parent_phid) { continue; } if (!$this->getIsNewObject()) { $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('You can only set a parent or milestone project when creating a ' . 'project for the first time.'), $xaction); break; } $projects = id(new PhabricatorProjectQuery())->setViewer($this->requireActor())->withPHIDs(array($parent_phid))->requireCapabilities(array(PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT))->execute(); if (!$projects) { $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('Parent or milestone project PHID ("%s") must be the PHID of a ' . 'valid, visible project which you have permission to edit.', $parent_phid), $xaction); break; } $project = head($projects); if ($project->isMilestone()) { $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('Parent or milestone project PHID ("%s") must not be a ' . 'milestone. Milestones may not have subprojects or milestones.', $parent_phid), $xaction); break; } $limit = PhabricatorProject::getProjectDepthLimit(); if ($project->getProjectDepth() >= $limit - 1) { $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('You can not create a subproject or mielstone under this parent ' . 'because it would nest projects too deeply. The maximum ' . 'nesting depth of projects is %s.', new PhutilNumber($limit)), $xaction); break; } $object->attachParentProject($project); break; } return $errors; }
protected function validateTransaction(PhabricatorLiskDAO $object, $type, array $xactions) { $errors = parent::validateTransaction($object, $type, $xactions); switch ($type) { case ManiphestTransaction::TYPE_TITLE: $missing = $this->validateIsEmptyTextField($object->getTitle(), $xactions); if ($missing) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Required'), pht('Task title is required.'), nonempty(last($xactions), null)); $error->setIsMissingFieldError(true); $errors[] = $error; } break; case ManiphestTransaction::TYPE_PARENT: $with_effect = array(); foreach ($xactions as $xaction) { $task_phid = $xaction->getNewValue(); if (!$task_phid) { continue; } $with_effect[] = $xaction; $task = id(new ManiphestTaskQuery())->setViewer($this->getActor())->withPHIDs(array($task_phid))->executeOne(); if (!$task) { $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('Parent task identifier "%s" does not identify a visible ' . 'task.', $task_phid), $xaction); } } if ($with_effect && !$this->getIsNewObject()) { $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('You can only select a parent task when creating a ' . 'transaction for the first time.'), last($with_effect)); } break; case ManiphestTransaction::TYPE_COLUMN: $with_effect = array(); foreach ($xactions as $xaction) { $column_phid = $xaction->getNewValue(); if (!$column_phid) { continue; } $with_effect[] = $xaction; $column = $this->loadProjectColumn($column_phid); if (!$column) { $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('Column PHID "%s" does not identify a visible column.', $column_phid), $xaction); } } if ($with_effect && !$this->getIsNewObject()) { $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('You can only put a task into an initial column during task ' . 'creation.'), last($with_effect)); } break; } return $errors; }
protected function validateTransaction(PhabricatorLiskDAO $object, $type, array $xactions) { $errors = parent::validateTransaction($object, $type, $xactions); foreach ($xactions as $xaction) { switch ($type) { case PhabricatorAuditActionConstants::ACTION: $error = $this->validateAuditAction($object, $type, $xaction, $xaction->getNewValue()); if ($error) { $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), $error, $xaction); } break; } } return $errors; }
protected function validateTransaction(PhabricatorLiskDAO $object, $type, array $xactions) { $errors = parent::validateTransaction($object, $type, $xactions); switch ($type) { case ConpherenceTransaction::TYPE_TITLE: if (empty($xactions)) { break; } $missing = $this->validateIsEmptyTextField($object->getTitle(), $xactions); if ($missing) { $detail = pht('Room title is required.'); $error = new PhabricatorApplicationTransactionValidationError($type, pht('Required'), $detail, last($xactions)); $error->setIsMissingFieldError(true); $errors[] = $error; } break; case ConpherenceTransaction::TYPE_PICTURE: foreach ($xactions as $xaction) { $file = $xaction->getNewValue(); if (!$file->isTransformableImage()) { $detail = pht('This server only supports these image formats: %s.', implode(', ', PhabricatorFile::getTransformableImageFormats())); $error = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), $detail, last($xactions)); $errors[] = $error; } } break; case ConpherenceTransaction::TYPE_PARTICIPANTS: foreach ($xactions as $xaction) { $new_phids = $this->getPHIDTransactionNewValue($xaction, array()); $old_phids = nonempty($object->getParticipantPHIDs(), array()); $phids = array_diff($new_phids, $old_phids); if (!$phids) { continue; } $users = id(new PhabricatorPeopleQuery())->setViewer($this->requireActor())->withPHIDs($phids)->execute(); $users = mpull($users, null, 'getPHID'); foreach ($phids as $phid) { if (isset($users[$phid])) { continue; } $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('New room participant "%s" is not a valid user.', $phid), $xaction); } } break; } return $errors; }
protected function validateTransaction(PhabricatorLiskDAO $object, $type, array $xactions) { $errors = parent::validateTransaction($object, $type, $xactions); switch ($type) { case ManiphestTransaction::TYPE_TITLE: $missing = $this->validateIsEmptyTextField($object->getTitle(), $xactions); if ($missing) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Required'), pht('Task title is required.'), nonempty(last($xactions), null)); $error->setIsMissingFieldError(true); $errors[] = $error; } break; case ManiphestTransaction::TYPE_PARENT: $with_effect = array(); foreach ($xactions as $xaction) { $task_phid = $xaction->getNewValue(); if (!$task_phid) { continue; } $with_effect[] = $xaction; $task = id(new ManiphestTaskQuery())->setViewer($this->getActor())->withPHIDs(array($task_phid))->executeOne(); if (!$task) { $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('Parent task identifier "%s" does not identify a visible ' . 'task.', $task_phid), $xaction); } } if ($with_effect && !$this->getIsNewObject()) { $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('You can only select a parent task when creating a ' . 'transaction for the first time.'), last($with_effect)); } break; case ManiphestTransaction::TYPE_OWNER: foreach ($xactions as $xaction) { $old = $xaction->getOldValue(); $new = $xaction->getNewValue(); if (!strlen($new)) { continue; } if ($new === $old) { continue; } $assignee_list = id(new PhabricatorPeopleQuery())->setViewer($this->getActor())->withPHIDs(array($new))->execute(); if (!$assignee_list) { $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('User "%s" is not a valid user.', $new), $xaction); } } break; case ManiphestTransaction::TYPE_COVER_IMAGE: foreach ($xactions as $xaction) { $old = $xaction->getOldValue(); $new = $xaction->getNewValue(); if (!$new) { continue; } if ($new === $old) { continue; } $file = id(new PhabricatorFileQuery())->setViewer($this->getActor())->withPHIDs(array($new))->executeOne(); if (!$file) { $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('File "%s" is not valid.', $new), $xaction); continue; } if (!$file->isTransformableImage()) { $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('File "%s" is not a valid image file.', $new), $xaction); continue; } } break; case ManiphestTransaction::TYPE_POINTS: foreach ($xactions as $xaction) { $new = $xaction->getNewValue(); if (strlen($new) && !is_numeric($new)) { $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('Points value must be numeric or empty.'), $xaction); continue; } if ((double) $new < 0) { $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('Points value must be nonnegative.'), $xaction); continue; } } break; } return $errors; }
protected function validateTransaction(PhabricatorLiskDAO $object, $type, array $xactions) { $errors = parent::validateTransaction($object, $type, $xactions); switch ($type) { case PassphraseCredentialTransaction::TYPE_NAME: $missing = $this->validateIsEmptyTextField($object->getName(), $xactions); if ($missing) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Required'), pht('Credential name is required.'), nonempty(last($xactions), null)); $error->setIsMissingFieldError(true); $errors[] = $error; } break; case PassphraseCredentialTransaction::TYPE_USERNAME: $credential_type = $object->getImplementation(); if (!$credential_type->shouldRequireUsername()) { break; } $missing = $this->validateIsEmptyTextField($object->getUsername(), $xactions); if ($missing) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Required'), pht('Username is required.'), nonempty(last($xactions), null)); $error->setIsMissingFieldError(true); $errors[] = $error; } break; } return $errors; }