Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/conduit/controller/PhabricatorConduitConsoleController.php b/src/applications/conduit/controller/PhabricatorConduitConsoleController.php
index b767ead4f0..b696645e55 100644
--- a/src/applications/conduit/controller/PhabricatorConduitConsoleController.php
+++ b/src/applications/conduit/controller/PhabricatorConduitConsoleController.php
@@ -1,206 +1,213 @@
<?php
final class PhabricatorConduitConsoleController
extends PhabricatorConduitController {
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$method_name = $request->getURIData('method');
$method = id(new PhabricatorConduitMethodQuery())
->setViewer($viewer)
->withMethods(array($method_name))
->executeOne();
if (!$method) {
return new Aphront404Response();
}
$method->setViewer($viewer);
$call_uri = '/api/'.$method->getAPIMethodName();
$errors = array();
$form = id(new AphrontFormView())
->setAction($call_uri)
->setUser($request->getUser())
->appendRemarkupInstructions(
pht(
'Enter parameters using **JSON**. For instance, to enter a '.
'list, type: `%s`',
'["apple", "banana", "cherry"]'));
$params = $method->getParamTypes();
foreach ($params as $param => $desc) {
$form->appendChild(
id(new AphrontFormTextControl())
->setLabel($param)
->setName("params[{$param}]")
->setCaption($desc));
}
$must_login = !$viewer->isLoggedIn() &&
$method->shouldRequireAuthentication();
if ($must_login) {
$errors[] = pht(
'Login Required: This method requires authentication. You must '.
'log in before you can make calls to it.');
} else {
$form
->appendChild(
id(new AphrontFormSelectControl())
->setLabel(pht('Output Format'))
->setName('output')
->setOptions(
array(
'human' => pht('Human Readable'),
'json' => pht('JSON'),
)))
->appendChild(
id(new AphrontFormSubmitControl())
->addCancelButton($this->getApplicationURI())
->setValue(pht('Call Method')));
}
$header = id(new PHUIHeaderView())
->setUser($viewer)
->setHeader($method->getAPIMethodName())
->setHeaderIcon('fa-tty');
$form_box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Call Method'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setForm($form);
$properties = $this->buildMethodProperties($method);
$info_box = id(new PHUIObjectBoxView())
->setHeaderText(pht('API Method: %s', $method->getAPIMethodName()))
->setFormErrors($errors)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->appendChild($properties);
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb($method->getAPIMethodName());
$crumbs->setBorder(true);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter(array(
$info_box,
$method->getMethodDocumentation(),
$form_box,
$this->renderExampleBox($method, null),
));
$title = $method->getAPIMethodName();
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild($view);
}
private function buildMethodProperties(ConduitAPIMethod $method) {
$viewer = $this->getViewer();
$view = id(new PHUIPropertyListView());
$status = $method->getMethodStatus();
$reason = $method->getMethodStatusDescription();
switch ($status) {
case ConduitAPIMethod::METHOD_STATUS_UNSTABLE:
$stability_icon = 'fa-exclamation-triangle yellow';
$stability_label = pht('Unstable Method');
$stability_info = nonempty(
$reason,
pht(
'This method is new and unstable. Its interface is subject '.
'to change.'));
break;
case ConduitAPIMethod::METHOD_STATUS_DEPRECATED:
$stability_icon = 'fa-exclamation-triangle red';
$stability_label = pht('Deprecated Method');
$stability_info = nonempty($reason, pht('This method is deprecated.'));
break;
+ case ConduitAPIMethod::METHOD_STATUS_FROZEN:
+ $stability_icon = 'fa-archive grey';
+ $stability_label = pht('Frozen Method');
+ $stability_info = nonempty(
+ $reason,
+ pht('This method is frozen and will eventually be deprecated.'));
+ break;
default:
$stability_label = null;
break;
}
if ($stability_label) {
$view->addProperty(
pht('Stability'),
array(
id(new PHUIIconView())->setIcon($stability_icon),
' ',
phutil_tag('strong', array(), $stability_label.':'),
' ',
$stability_info,
));
}
$view->addProperty(
pht('Returns'),
$method->getReturnType());
$error_types = $method->getErrorTypes();
$error_types['ERR-CONDUIT-CORE'] = pht('See error message for details.');
$error_description = array();
foreach ($error_types as $error => $meaning) {
$error_description[] = hsprintf(
'<li><strong>%s:</strong> %s</li>',
$error,
$meaning);
}
$error_description = phutil_tag('ul', array(), $error_description);
$view->addProperty(
pht('Errors'),
$error_description);
$scope = $method->getRequiredScope();
switch ($scope) {
case ConduitAPIMethod::SCOPE_ALWAYS:
$oauth_icon = 'fa-globe green';
$oauth_description = pht(
'OAuth clients may always call this method.');
break;
case ConduitAPIMethod::SCOPE_NEVER:
$oauth_icon = 'fa-ban red';
$oauth_description = pht(
'OAuth clients may never call this method.');
break;
default:
$oauth_icon = 'fa-unlock-alt blue';
$oauth_description = pht(
'OAuth clients may call this method after requesting access to '.
'the "%s" scope.',
$scope);
break;
}
$view->addProperty(
pht('OAuth Scope'),
array(
id(new PHUIIconView())->setIcon($oauth_icon),
' ',
$oauth_description,
));
$view->addSectionHeader(
pht('Description'), PHUIPropertyListView::ICON_SUMMARY);
$view->addTextContent(
new PHUIRemarkupView($viewer, $method->getMethodDescription()));
return $view;
}
}
diff --git a/src/applications/conduit/method/ConduitAPIMethod.php b/src/applications/conduit/method/ConduitAPIMethod.php
index 3a2cc6e30d..05831a782d 100644
--- a/src/applications/conduit/method/ConduitAPIMethod.php
+++ b/src/applications/conduit/method/ConduitAPIMethod.php
@@ -1,411 +1,412 @@
<?php
/**
* @task info Method Information
* @task status Method Status
* @task pager Paging Results
*/
abstract class ConduitAPIMethod
extends Phobject
implements PhabricatorPolicyInterface {
private $viewer;
- const METHOD_STATUS_STABLE = 'stable';
- const METHOD_STATUS_UNSTABLE = 'unstable';
- const METHOD_STATUS_DEPRECATED = 'deprecated';
+ const METHOD_STATUS_STABLE = 'stable';
+ const METHOD_STATUS_UNSTABLE = 'unstable';
+ const METHOD_STATUS_DEPRECATED = 'deprecated';
+ const METHOD_STATUS_FROZEN = 'frozen';
const SCOPE_NEVER = 'scope.never';
const SCOPE_ALWAYS = 'scope.always';
/**
* Get a short, human-readable text summary of the method.
*
* @return string Short summary of method.
* @task info
*/
public function getMethodSummary() {
return $this->getMethodDescription();
}
/**
* Get a detailed description of the method.
*
* This method should return remarkup.
*
* @return string Detailed description of the method.
* @task info
*/
abstract public function getMethodDescription();
public function getMethodDocumentation() {
return null;
}
abstract protected function defineParamTypes();
abstract protected function defineReturnType();
protected function defineErrorTypes() {
return array();
}
abstract protected function execute(ConduitAPIRequest $request);
public function isInternalAPI() {
return false;
}
public function getParamTypes() {
$types = $this->defineParamTypes();
$query = $this->newQueryObject();
if ($query) {
$types['order'] = 'optional order';
$types += $this->getPagerParamTypes();
}
return $types;
}
public function getReturnType() {
return $this->defineReturnType();
}
public function getErrorTypes() {
return $this->defineErrorTypes();
}
/**
* This is mostly for compatibility with
* @{class:PhabricatorCursorPagedPolicyAwareQuery}.
*/
public function getID() {
return $this->getAPIMethodName();
}
/**
* Get the status for this method (e.g., stable, unstable or deprecated).
* Should return a METHOD_STATUS_* constant. By default, methods are
* "stable".
*
* @return const METHOD_STATUS_* constant.
* @task status
*/
public function getMethodStatus() {
return self::METHOD_STATUS_STABLE;
}
/**
* Optional description to supplement the method status. In particular, if
* a method is deprecated, you can return a string here describing the reason
* for deprecation and stable alternatives.
*
* @return string|null Description of the method status, if available.
* @task status
*/
public function getMethodStatusDescription() {
return null;
}
public function getErrorDescription($error_code) {
return idx($this->getErrorTypes(), $error_code, pht('Unknown Error'));
}
public function getRequiredScope() {
return self::SCOPE_NEVER;
}
public function executeMethod(ConduitAPIRequest $request) {
$this->setViewer($request->getUser());
return $this->execute($request);
}
abstract public function getAPIMethodName();
/**
* Return a key which sorts methods by application name, then method status,
* then method name.
*/
public function getSortOrder() {
$name = $this->getAPIMethodName();
$map = array(
self::METHOD_STATUS_STABLE => 0,
self::METHOD_STATUS_UNSTABLE => 1,
self::METHOD_STATUS_DEPRECATED => 2,
);
$ord = idx($map, $this->getMethodStatus(), 0);
list($head, $tail) = explode('.', $name, 2);
return "{$head}.{$ord}.{$tail}";
}
public static function getMethodStatusMap() {
$map = array(
self::METHOD_STATUS_STABLE => pht('Stable'),
self::METHOD_STATUS_UNSTABLE => pht('Unstable'),
self::METHOD_STATUS_DEPRECATED => pht('Deprecated'),
);
return $map;
}
public function getApplicationName() {
return head(explode('.', $this->getAPIMethodName(), 2));
}
public static function loadAllConduitMethods() {
return self::newClassMapQuery()->execute();
}
private static function newClassMapQuery() {
return id(new PhutilClassMapQuery())
->setAncestorClass(__CLASS__)
->setUniqueMethod('getAPIMethodName');
}
public static function getConduitMethod($method_name) {
return id(new PhabricatorCachedClassMapQuery())
->setClassMapQuery(self::newClassMapQuery())
->setMapKeyMethod('getAPIMethodName')
->loadClass($method_name);
}
public function shouldRequireAuthentication() {
return true;
}
public function shouldAllowPublic() {
return false;
}
public function shouldAllowUnguardedWrites() {
return false;
}
/**
* Optionally, return a @{class:PhabricatorApplication} which this call is
* part of. The call will be disabled when the application is uninstalled.
*
* @return PhabricatorApplication|null Related application.
*/
public function getApplication() {
return null;
}
protected function formatStringConstants($constants) {
foreach ($constants as $key => $value) {
$constants[$key] = '"'.$value.'"';
}
$constants = implode(', ', $constants);
return 'string-constant<'.$constants.'>';
}
public static function getParameterMetadataKey($key) {
if (strncmp($key, 'api.', 4) === 0) {
// All keys passed beginning with "api." are always metadata keys.
return substr($key, 4);
} else {
switch ($key) {
// These are real keys which always belong to request metadata.
case 'access_token':
case 'scope':
case 'output':
// This is not a real metadata key; it is included here only to
// prevent Conduit methods from defining it.
case '__conduit__':
// This is prevented globally as a blanket defense against OAuth
// redirection attacks. It is included here to stop Conduit methods
// from defining it.
case 'code':
// This is not a real metadata key, but the presence of this
// parameter triggers an alternate request decoding pathway.
case 'params':
return $key;
}
}
return null;
}
final public function setViewer(PhabricatorUser $viewer) {
$this->viewer = $viewer;
return $this;
}
final public function getViewer() {
return $this->viewer;
}
/* -( Paging Results )----------------------------------------------------- */
/**
* @task pager
*/
protected function getPagerParamTypes() {
return array(
'before' => 'optional string',
'after' => 'optional string',
'limit' => 'optional int (default = 100)',
);
}
/**
* @task pager
*/
protected function newPager(ConduitAPIRequest $request) {
$limit = $request->getValue('limit', 100);
$limit = min(1000, $limit);
$limit = max(1, $limit);
$pager = id(new AphrontCursorPagerView())
->setPageSize($limit);
$before_id = $request->getValue('before');
if ($before_id !== null) {
$pager->setBeforeID($before_id);
}
$after_id = $request->getValue('after');
if ($after_id !== null) {
$pager->setAfterID($after_id);
}
return $pager;
}
/**
* @task pager
*/
protected function addPagerResults(
array $results,
AphrontCursorPagerView $pager) {
$results['cursor'] = array(
'limit' => $pager->getPageSize(),
'after' => $pager->getNextPageID(),
'before' => $pager->getPrevPageID(),
);
return $results;
}
/* -( Implementing Query Methods )----------------------------------------- */
public function newQueryObject() {
return null;
}
protected function newQueryForRequest(ConduitAPIRequest $request) {
$query = $this->newQueryObject();
if (!$query) {
throw new Exception(
pht(
'You can not call newQueryFromRequest() in this method ("%s") '.
'because it does not implement newQueryObject().',
get_class($this)));
}
if (!($query instanceof PhabricatorCursorPagedPolicyAwareQuery)) {
throw new Exception(
pht(
'Call to method newQueryObject() did not return an object of class '.
'"%s".',
'PhabricatorCursorPagedPolicyAwareQuery'));
}
$query->setViewer($request->getUser());
$order = $request->getValue('order');
if ($order !== null) {
if (is_scalar($order)) {
$query->setOrder($order);
} else {
$query->setOrderVector($order);
}
}
return $query;
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */
public function getPHID() {
return null;
}
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
);
}
public function getPolicy($capability) {
// Application methods get application visibility; other methods get open
// visibility.
$application = $this->getApplication();
if ($application) {
return $application->getPolicy($capability);
}
return PhabricatorPolicies::getMostOpenPolicy();
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
if (!$this->shouldRequireAuthentication()) {
// Make unauthenticated methods universally visible.
return true;
}
return false;
}
protected function hasApplicationCapability(
$capability,
PhabricatorUser $viewer) {
$application = $this->getApplication();
if (!$application) {
return false;
}
return PhabricatorPolicyFilter::hasCapability(
$viewer,
$application,
$capability);
}
protected function requireApplicationCapability(
$capability,
PhabricatorUser $viewer) {
$application = $this->getApplication();
if (!$application) {
return;
}
PhabricatorPolicyFilter::requireCapability(
$viewer,
$this->getApplication(),
$capability);
}
}
diff --git a/src/applications/conduit/query/PhabricatorConduitMethodQuery.php b/src/applications/conduit/query/PhabricatorConduitMethodQuery.php
index 0409e4ceb3..eb61d8c480 100644
--- a/src/applications/conduit/query/PhabricatorConduitMethodQuery.php
+++ b/src/applications/conduit/query/PhabricatorConduitMethodQuery.php
@@ -1,157 +1,158 @@
<?php
final class PhabricatorConduitMethodQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $isDeprecated;
private $isStable;
private $isUnstable;
private $applicationNames;
private $nameContains;
private $methods;
private $isInternal;
public function withMethods(array $methods) {
$this->methods = $methods;
return $this;
}
public function withNameContains($name_contains) {
$this->nameContains = $name_contains;
return $this;
}
public function withIsStable($is_stable) {
$this->isStable = $is_stable;
return $this;
}
public function withIsUnstable($is_unstable) {
$this->isUnstable = $is_unstable;
return $this;
}
public function withIsDeprecated($is_deprecated) {
$this->isDeprecated = $is_deprecated;
return $this;
}
public function withIsInternal($is_internal) {
$this->isInternal = $is_internal;
return $this;
}
protected function loadPage() {
$methods = $this->getAllMethods();
$methods = $this->filterMethods($methods);
return $methods;
}
private function getAllMethods() {
return id(new PhutilClassMapQuery())
->setAncestorClass('ConduitAPIMethod')
->setSortMethod('getSortOrder')
->execute();
}
private function filterMethods(array $methods) {
foreach ($methods as $key => $method) {
$application = $method->getApplication();
if (!$application) {
continue;
}
if (!$application->isInstalled()) {
unset($methods[$key]);
}
}
$status = array(
- ConduitAPIMethod::METHOD_STATUS_STABLE => $this->isStable,
+ ConduitAPIMethod::METHOD_STATUS_STABLE => $this->isStable,
+ ConduitAPIMethod::METHOD_STATUS_FROZEN => $this->isStable,
ConduitAPIMethod::METHOD_STATUS_DEPRECATED => $this->isDeprecated,
ConduitAPIMethod::METHOD_STATUS_UNSTABLE => $this->isUnstable,
);
// Only apply status filters if any of them are set.
if (array_filter($status)) {
foreach ($methods as $key => $method) {
$keep = idx($status, $method->getMethodStatus());
if (!$keep) {
unset($methods[$key]);
}
}
}
if ($this->nameContains) {
$needle = phutil_utf8_strtolower($this->nameContains);
foreach ($methods as $key => $method) {
$haystack = $method->getAPIMethodName();
$haystack = phutil_utf8_strtolower($haystack);
if (strpos($haystack, $needle) === false) {
unset($methods[$key]);
}
}
}
if ($this->methods) {
$map = array_fuse($this->methods);
foreach ($methods as $key => $method) {
$needle = $method->getAPIMethodName();
if (empty($map[$needle])) {
unset($methods[$key]);
}
}
}
if ($this->isInternal !== null) {
foreach ($methods as $key => $method) {
if ($method->isInternalAPI() !== $this->isInternal) {
unset($methods[$key]);
}
}
}
return $methods;
}
protected function willFilterPage(array $methods) {
$application_phids = array();
foreach ($methods as $method) {
$application = $method->getApplication();
if ($application === null) {
continue;
}
$application_phids[] = $application->getPHID();
}
if ($application_phids) {
$applications = id(new PhabricatorApplicationQuery())
->setParentQuery($this)
->setViewer($this->getViewer())
->withPHIDs($application_phids)
->execute();
$applications = mpull($applications, null, 'getPHID');
} else {
$applications = array();
}
// Remove methods which belong to an application the viewer can not see.
foreach ($methods as $key => $method) {
$application = $method->getApplication();
if ($application === null) {
continue;
}
if (empty($applications[$application->getPHID()])) {
$this->didRejectResult($method);
unset($methods[$key]);
}
}
return $methods;
}
public function getQueryApplicationClass() {
return 'PhabricatorConduitApplication';
}
}
diff --git a/src/applications/conduit/query/PhabricatorConduitSearchEngine.php b/src/applications/conduit/query/PhabricatorConduitSearchEngine.php
index 3c01005722..787c2154d5 100644
--- a/src/applications/conduit/query/PhabricatorConduitSearchEngine.php
+++ b/src/applications/conduit/query/PhabricatorConduitSearchEngine.php
@@ -1,184 +1,188 @@
<?php
final class PhabricatorConduitSearchEngine
extends PhabricatorApplicationSearchEngine {
public function getResultTypeDescription() {
return pht('Conduit Methods');
}
public function getApplicationClassName() {
return 'PhabricatorConduitApplication';
}
public function getPageSize(PhabricatorSavedQuery $saved) {
return PHP_INT_MAX - 1;
}
public function buildSavedQueryFromRequest(AphrontRequest $request) {
$saved = new PhabricatorSavedQuery();
$saved->setParameter('isStable', $request->getStr('isStable'));
$saved->setParameter('isUnstable', $request->getStr('isUnstable'));
$saved->setParameter('isDeprecated', $request->getStr('isDeprecated'));
$saved->setParameter('nameContains', $request->getStr('nameContains'));
return $saved;
}
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
$query = id(new PhabricatorConduitMethodQuery());
$query->withIsStable($saved->getParameter('isStable'));
$query->withIsUnstable($saved->getParameter('isUnstable'));
$query->withIsDeprecated($saved->getParameter('isDeprecated'));
$query->withIsInternal(false);
$contains = $saved->getParameter('nameContains');
if (strlen($contains)) {
$query->withNameContains($contains);
}
return $query;
}
public function buildSearchForm(
AphrontFormView $form,
PhabricatorSavedQuery $saved) {
$form
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Name Contains'))
->setName('nameContains')
->setValue($saved->getParameter('nameContains')));
$is_stable = $saved->getParameter('isStable');
$is_unstable = $saved->getParameter('isUnstable');
$is_deprecated = $saved->getParameter('isDeprecated');
$form
->appendChild(
id(new AphrontFormCheckboxControl())
->setLabel('Stability')
->addCheckbox(
'isStable',
1,
hsprintf(
'<strong>%s</strong>: %s',
pht('Stable Methods'),
pht('Show established API methods with stable interfaces.')),
$is_stable)
->addCheckbox(
'isUnstable',
1,
hsprintf(
'<strong>%s</strong>: %s',
pht('Unstable Methods'),
pht('Show new methods which are subject to change.')),
$is_unstable)
->addCheckbox(
'isDeprecated',
1,
hsprintf(
'<strong>%s</strong>: %s',
pht('Deprecated Methods'),
pht(
'Show old methods which will be deleted in a future '.
'version of Phabricator.')),
$is_deprecated));
}
protected function getURI($path) {
return '/conduit/'.$path;
}
protected function getBuiltinQueryNames() {
return array(
'modern' => pht('Modern Methods'),
'all' => pht('All Methods'),
);
}
public function buildSavedQueryFromBuiltin($query_key) {
$query = $this->newSavedQuery();
$query->setQueryKey($query_key);
switch ($query_key) {
case 'modern':
return $query
->setParameter('isStable', true)
->setParameter('isUnstable', true);
case 'all':
return $query
->setParameter('isStable', true)
->setParameter('isUnstable', true)
->setParameter('isDeprecated', true);
}
return parent::buildSavedQueryFromBuiltin($query_key);
}
protected function renderResultList(
array $methods,
PhabricatorSavedQuery $query,
array $handles) {
assert_instances_of($methods, 'ConduitAPIMethod');
$viewer = $this->requireViewer();
$out = array();
$last = null;
$list = null;
foreach ($methods as $method) {
$app = $method->getApplicationName();
if ($app !== $last) {
$last = $app;
if ($list) {
$out[] = $list;
}
$list = id(new PHUIObjectItemListView());
$list->setHeader($app);
$app_object = $method->getApplication();
if ($app_object) {
$app_name = $app_object->getName();
} else {
$app_name = $app;
}
}
$method_name = $method->getAPIMethodName();
$item = id(new PHUIObjectItemView())
->setHeader($method_name)
->setHref($this->getApplicationURI('method/'.$method_name.'/'))
->addAttribute($method->getMethodSummary());
switch ($method->getMethodStatus()) {
case ConduitAPIMethod::METHOD_STATUS_STABLE:
break;
case ConduitAPIMethod::METHOD_STATUS_UNSTABLE:
$item->addIcon('fa-warning', pht('Unstable'));
$item->setStatusIcon('fa-warning yellow');
break;
case ConduitAPIMethod::METHOD_STATUS_DEPRECATED:
$item->addIcon('fa-warning', pht('Deprecated'));
$item->setStatusIcon('fa-warning red');
break;
+ case ConduitAPIMethod::METHOD_STATUS_FROZEN:
+ $item->addIcon('fa-archive', pht('Frozen'));
+ $item->setStatusIcon('fa-archive grey');
+ break;
}
$list->addItem($item);
}
if ($list) {
$out[] = $list;
}
$result = new PhabricatorApplicationSearchResultView();
$result->setContent($out);
return $result;
}
}
diff --git a/src/applications/differential/conduit/DifferentialCloseConduitAPIMethod.php b/src/applications/differential/conduit/DifferentialCloseConduitAPIMethod.php
index e613647935..d71876961e 100644
--- a/src/applications/differential/conduit/DifferentialCloseConduitAPIMethod.php
+++ b/src/applications/differential/conduit/DifferentialCloseConduitAPIMethod.php
@@ -1,61 +1,71 @@
<?php
final class DifferentialCloseConduitAPIMethod
extends DifferentialConduitAPIMethod {
public function getAPIMethodName() {
return 'differential.close';
}
public function getMethodDescription() {
return pht('Close a Differential revision.');
}
+ public function getMethodStatus() {
+ return self::METHOD_STATUS_FROZEN;
+ }
+
+ public function getMethodStatusDescription() {
+ return pht(
+ 'This method is frozen and will eventually be deprecated. New code '.
+ 'should use "differential.revision.edit" instead.');
+ }
+
protected function defineParamTypes() {
return array(
'revisionID' => 'required int',
);
}
protected function defineReturnType() {
return 'void';
}
protected function defineErrorTypes() {
return array(
'ERR_NOT_FOUND' => pht('Revision was not found.'),
);
}
protected function execute(ConduitAPIRequest $request) {
$viewer = $request->getUser();
$id = $request->getValue('revisionID');
$revision = id(new DifferentialRevisionQuery())
->withIDs(array($id))
->setViewer($viewer)
->needReviewerStatus(true)
->executeOne();
if (!$revision) {
throw new ConduitException('ERR_NOT_FOUND');
}
$xactions = array();
$xactions[] = id(new DifferentialTransaction())
->setTransactionType(DifferentialTransaction::TYPE_ACTION)
->setNewValue(DifferentialAction::ACTION_CLOSE);
$content_source = $request->newContentSource();
$editor = id(new DifferentialTransactionEditor())
->setActor($viewer)
->setContentSource($request->newContentSource())
->setContinueOnMissingFields(true)
->setContinueOnNoEffect(true);
$editor->applyTransactions($revision, $xactions);
return;
}
}
diff --git a/src/applications/differential/conduit/DifferentialCreateCommentConduitAPIMethod.php b/src/applications/differential/conduit/DifferentialCreateCommentConduitAPIMethod.php
index 8f4b154876..459298c54f 100644
--- a/src/applications/differential/conduit/DifferentialCreateCommentConduitAPIMethod.php
+++ b/src/applications/differential/conduit/DifferentialCreateCommentConduitAPIMethod.php
@@ -1,92 +1,102 @@
<?php
final class DifferentialCreateCommentConduitAPIMethod
extends DifferentialConduitAPIMethod {
public function getAPIMethodName() {
return 'differential.createcomment';
}
public function getMethodDescription() {
return pht('Add a comment to a Differential revision.');
}
+ public function getMethodStatus() {
+ return self::METHOD_STATUS_FROZEN;
+ }
+
+ public function getMethodStatusDescription() {
+ return pht(
+ 'This method is frozen and will eventually be deprecated. New code '.
+ 'should use "differential.revision.edit" instead.');
+ }
+
protected function defineParamTypes() {
return array(
'revision_id' => 'required revisionid',
'message' => 'optional string',
'action' => 'optional string',
'silent' => 'optional bool',
'attach_inlines' => 'optional bool',
);
}
protected function defineReturnType() {
return 'nonempty dict';
}
protected function defineErrorTypes() {
return array(
'ERR_BAD_REVISION' => pht('Bad revision ID.'),
);
}
protected function execute(ConduitAPIRequest $request) {
$viewer = $request->getUser();
$revision = id(new DifferentialRevisionQuery())
->setViewer($viewer)
->withIDs(array($request->getValue('revision_id')))
->needReviewerStatus(true)
->needReviewerAuthority(true)
->executeOne();
if (!$revision) {
throw new ConduitException('ERR_BAD_REVISION');
}
$xactions = array();
$action = $request->getValue('action');
if ($action && ($action != 'comment') && ($action != 'none')) {
$xactions[] = id(new DifferentialTransaction())
->setTransactionType(DifferentialTransaction::TYPE_ACTION)
->setNewValue($action);
}
$content = $request->getValue('message');
if (strlen($content)) {
$xactions[] = id(new DifferentialTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_COMMENT)
->attachComment(
id(new DifferentialTransactionComment())
->setContent($content));
}
if ($request->getValue('attach_inlines')) {
$type_inline = DifferentialTransaction::TYPE_INLINE;
$inlines = DifferentialTransactionQuery::loadUnsubmittedInlineComments(
$viewer,
$revision);
foreach ($inlines as $inline) {
$xactions[] = id(new DifferentialTransaction())
->setTransactionType($type_inline)
->attachComment($inline);
}
}
$editor = id(new DifferentialTransactionEditor())
->setActor($viewer)
->setDisableEmail($request->getValue('silent'))
->setContentSource($request->newContentSource())
->setContinueOnNoEffect(true)
->setContinueOnMissingFields(true);
$editor->applyTransactions($revision, $xactions);
return array(
'revisionid' => $revision->getID(),
'uri' => PhabricatorEnv::getURI('/D'.$revision->getID()),
);
}
}
diff --git a/src/applications/differential/conduit/DifferentialCreateRevisionConduitAPIMethod.php b/src/applications/differential/conduit/DifferentialCreateRevisionConduitAPIMethod.php
index 371d57cd9c..532d63680b 100644
--- a/src/applications/differential/conduit/DifferentialCreateRevisionConduitAPIMethod.php
+++ b/src/applications/differential/conduit/DifferentialCreateRevisionConduitAPIMethod.php
@@ -1,63 +1,73 @@
<?php
final class DifferentialCreateRevisionConduitAPIMethod
extends DifferentialConduitAPIMethod {
public function getAPIMethodName() {
return 'differential.createrevision';
}
public function getMethodDescription() {
return pht('Create a new Differential revision.');
}
+ public function getMethodStatus() {
+ return self::METHOD_STATUS_FROZEN;
+ }
+
+ public function getMethodStatusDescription() {
+ return pht(
+ 'This method is frozen and will eventually be deprecated. New code '.
+ 'should use "differential.revision.edit" instead.');
+ }
+
protected function defineParamTypes() {
return array(
// TODO: Arcanist passes this; prevent fatals after D4191 until Conduit
// version 7 or newer.
'user' => 'ignored',
'diffid' => 'required diffid',
'fields' => 'required dict',
);
}
protected function defineReturnType() {
return 'nonempty dict';
}
protected function defineErrorTypes() {
return array(
'ERR_BAD_DIFF' => pht('Bad diff ID.'),
);
}
protected function execute(ConduitAPIRequest $request) {
$viewer = $request->getUser();
$diff = id(new DifferentialDiffQuery())
->setViewer($viewer)
->withIDs(array($request->getValue('diffid')))
->executeOne();
if (!$diff) {
throw new ConduitException('ERR_BAD_DIFF');
}
$revision = DifferentialRevision::initializeNewRevision($viewer);
$revision->attachReviewerStatus(array());
$result = $this->applyFieldEdit(
$request,
$revision,
$diff,
$request->getValue('fields', array()),
$message = null);
$revision_id = $result['object']['id'];
return array(
'revisionid' => $revision_id,
'uri' => PhabricatorEnv::getURI('/D'.$revision_id),
);
}
}
diff --git a/src/applications/differential/conduit/DifferentialQueryConduitAPIMethod.php b/src/applications/differential/conduit/DifferentialQueryConduitAPIMethod.php
index 1d4bcc2a8d..720361367a 100644
--- a/src/applications/differential/conduit/DifferentialQueryConduitAPIMethod.php
+++ b/src/applications/differential/conduit/DifferentialQueryConduitAPIMethod.php
@@ -1,236 +1,246 @@
<?php
final class DifferentialQueryConduitAPIMethod
extends DifferentialConduitAPIMethod {
public function getAPIMethodName() {
return 'differential.query';
}
public function getMethodDescription() {
return pht('Query Differential revisions which match certain criteria.');
}
+ public function getMethodStatus() {
+ return self::METHOD_STATUS_FROZEN;
+ }
+
+ public function getMethodStatusDescription() {
+ return pht(
+ 'This method is frozen and will eventually be deprecated. New code '.
+ 'should use "differential.revision.search" instead.');
+ }
+
protected function defineParamTypes() {
$hash_types = ArcanistDifferentialRevisionHash::getTypes();
$hash_const = $this->formatStringConstants($hash_types);
$status_types = array(
DifferentialRevisionQuery::STATUS_ANY,
DifferentialRevisionQuery::STATUS_OPEN,
DifferentialRevisionQuery::STATUS_ACCEPTED,
DifferentialRevisionQuery::STATUS_CLOSED,
);
$status_const = $this->formatStringConstants($status_types);
$order_types = array(
DifferentialRevisionQuery::ORDER_MODIFIED,
DifferentialRevisionQuery::ORDER_CREATED,
);
$order_const = $this->formatStringConstants($order_types);
return array(
'authors' => 'optional list<phid>',
'ccs' => 'optional list<phid>',
'reviewers' => 'optional list<phid>',
'paths' => 'optional list<pair<callsign, path>>',
'commitHashes' => 'optional list<pair<'.$hash_const.', string>>',
'status' => 'optional '.$status_const,
'order' => 'optional '.$order_const,
'limit' => 'optional uint',
'offset' => 'optional uint',
'ids' => 'optional list<uint>',
'phids' => 'optional list<phid>',
'subscribers' => 'optional list<phid>',
'responsibleUsers' => 'optional list<phid>',
'branches' => 'optional list<string>',
);
}
protected function defineReturnType() {
return 'list<dict>';
}
protected function defineErrorTypes() {
return array(
'ERR-INVALID-PARAMETER' => pht('Missing or malformed parameter.'),
);
}
protected function execute(ConduitAPIRequest $request) {
$authors = $request->getValue('authors');
$ccs = $request->getValue('ccs');
$reviewers = $request->getValue('reviewers');
$status = $request->getValue('status');
$order = $request->getValue('order');
$path_pairs = $request->getValue('paths');
$commit_hashes = $request->getValue('commitHashes');
$limit = $request->getValue('limit');
$offset = $request->getValue('offset');
$ids = $request->getValue('ids');
$phids = $request->getValue('phids');
$subscribers = $request->getValue('subscribers');
$responsible_users = $request->getValue('responsibleUsers');
$branches = $request->getValue('branches');
$query = id(new DifferentialRevisionQuery())
->setViewer($request->getUser());
if ($authors) {
$query->withAuthors($authors);
}
if ($ccs) {
$query->withCCs($ccs);
}
if ($reviewers) {
$query->withReviewers($reviewers);
}
if ($path_pairs) {
$paths = array();
foreach ($path_pairs as $pair) {
list($callsign, $path) = $pair;
$paths[] = $path;
}
$path_map = id(new DiffusionPathIDQuery($paths))->loadPathIDs();
if (count($path_map) != count($paths)) {
$unknown_paths = array();
foreach ($paths as $p) {
if (!idx($path_map, $p)) {
$unknown_paths[] = $p;
}
}
throw id(new ConduitException('ERR-INVALID-PARAMETER'))
->setErrorDescription(
pht(
'Unknown paths: %s',
implode(', ', $unknown_paths)));
}
$repos = array();
foreach ($path_pairs as $pair) {
list($callsign, $path) = $pair;
if (!idx($repos, $callsign)) {
$repos[$callsign] = id(new PhabricatorRepositoryQuery())
->setViewer($request->getUser())
->withCallsigns(array($callsign))
->executeOne();
if (!$repos[$callsign]) {
throw id(new ConduitException('ERR-INVALID-PARAMETER'))
->setErrorDescription(
pht(
'Unknown repo callsign: %s',
$callsign));
}
}
$repo = $repos[$callsign];
$query->withPath($repo->getID(), idx($path_map, $path));
}
}
if ($commit_hashes) {
$hash_types = ArcanistDifferentialRevisionHash::getTypes();
foreach ($commit_hashes as $info) {
list($type, $hash) = $info;
if (empty($type) ||
!in_array($type, $hash_types) ||
empty($hash)) {
throw new ConduitException('ERR-INVALID-PARAMETER');
}
}
$query->withCommitHashes($commit_hashes);
}
if ($status) {
$query->withStatus($status);
}
if ($order) {
$query->setOrder($order);
}
if ($limit) {
$query->setLimit($limit);
}
if ($offset) {
$query->setOffset($offset);
}
if ($ids) {
$query->withIDs($ids);
}
if ($phids) {
$query->withPHIDs($phids);
}
if ($responsible_users) {
$query->withResponsibleUsers($responsible_users);
}
if ($subscribers) {
$query->withCCs($subscribers);
}
if ($branches) {
$query->withBranches($branches);
}
$query->needRelationships(true);
$query->needCommitPHIDs(true);
$query->needDiffIDs(true);
$query->needActiveDiffs(true);
$query->needHashes(true);
$revisions = $query->execute();
$field_data = $this->loadCustomFieldsForRevisions(
$request->getUser(),
$revisions);
$results = array();
foreach ($revisions as $revision) {
$diff = $revision->getActiveDiff();
if (!$diff) {
continue;
}
$id = $revision->getID();
$phid = $revision->getPHID();
$result = array(
'id' => $id,
'phid' => $phid,
'title' => $revision->getTitle(),
'uri' => PhabricatorEnv::getProductionURI('/D'.$id),
'dateCreated' => $revision->getDateCreated(),
'dateModified' => $revision->getDateModified(),
'authorPHID' => $revision->getAuthorPHID(),
'status' => $revision->getStatus(),
'statusName' =>
ArcanistDifferentialRevisionStatus::getNameForRevisionStatus(
$revision->getStatus()),
'properties' => $revision->getProperties(),
'branch' => $diff->getBranch(),
'summary' => $revision->getSummary(),
'testPlan' => $revision->getTestPlan(),
'lineCount' => $revision->getLineCount(),
'activeDiffPHID' => $diff->getPHID(),
'diffs' => $revision->getDiffIDs(),
'commits' => $revision->getCommitPHIDs(),
'reviewers' => array_values($revision->getReviewers()),
'ccs' => array_values($revision->getCCPHIDs()),
'hashes' => $revision->getHashes(),
'auxiliary' => idx($field_data, $phid, array()),
'repositoryPHID' => $diff->getRepositoryPHID(),
);
// TODO: This is a hacky way to put permissions on this field until we
// have first-class support, see T838.
if ($revision->getAuthorPHID() == $request->getUser()->getPHID()) {
$result['sourcePath'] = $diff->getSourcePath();
}
$results[] = $result;
}
return $results;
}
}
diff --git a/src/applications/differential/conduit/DifferentialUpdateRevisionConduitAPIMethod.php b/src/applications/differential/conduit/DifferentialUpdateRevisionConduitAPIMethod.php
index 232dbf79b4..d45dc9749e 100644
--- a/src/applications/differential/conduit/DifferentialUpdateRevisionConduitAPIMethod.php
+++ b/src/applications/differential/conduit/DifferentialUpdateRevisionConduitAPIMethod.php
@@ -1,79 +1,89 @@
<?php
final class DifferentialUpdateRevisionConduitAPIMethod
extends DifferentialConduitAPIMethod {
public function getAPIMethodName() {
return 'differential.updaterevision';
}
public function getMethodDescription() {
return pht('Update a Differential revision.');
}
+ public function getMethodStatus() {
+ return self::METHOD_STATUS_FROZEN;
+ }
+
+ public function getMethodStatusDescription() {
+ return pht(
+ 'This method is frozen and will eventually be deprecated. New code '.
+ 'should use "differential.revision.edit" instead.');
+ }
+
protected function defineParamTypes() {
return array(
'id' => 'required revisionid',
'diffid' => 'required diffid',
'fields' => 'required dict',
'message' => 'required string',
);
}
protected function defineReturnType() {
return 'nonempty dict';
}
protected function defineErrorTypes() {
return array(
'ERR_BAD_DIFF' => pht('Bad diff ID.'),
'ERR_BAD_REVISION' => pht('Bad revision ID.'),
'ERR_WRONG_USER' => pht('You are not the author of this revision.'),
'ERR_CLOSED' => pht('This revision has already been closed.'),
);
}
protected function execute(ConduitAPIRequest $request) {
$viewer = $request->getUser();
$diff = id(new DifferentialDiffQuery())
->setViewer($viewer)
->withIDs(array($request->getValue('diffid')))
->executeOne();
if (!$diff) {
throw new ConduitException('ERR_BAD_DIFF');
}
$revision = id(new DifferentialRevisionQuery())
->setViewer($request->getUser())
->withIDs(array($request->getValue('id')))
->needReviewerStatus(true)
->needActiveDiffs(true)
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$revision) {
throw new ConduitException('ERR_BAD_REVISION');
}
if ($revision->getStatus() == ArcanistDifferentialRevisionStatus::CLOSED) {
throw new ConduitException('ERR_CLOSED');
}
$this->applyFieldEdit(
$request,
$revision,
$diff,
$request->getValue('fields', array()),
$request->getValue('message'));
return array(
'revisionid' => $revision->getID(),
'uri' => PhabricatorEnv::getURI('/D'.$revision->getID()),
);
}
}
diff --git a/src/applications/maniphest/conduit/ManiphestCreateTaskConduitAPIMethod.php b/src/applications/maniphest/conduit/ManiphestCreateTaskConduitAPIMethod.php
index 3a404774cf..fac96372fe 100644
--- a/src/applications/maniphest/conduit/ManiphestCreateTaskConduitAPIMethod.php
+++ b/src/applications/maniphest/conduit/ManiphestCreateTaskConduitAPIMethod.php
@@ -1,36 +1,46 @@
<?php
final class ManiphestCreateTaskConduitAPIMethod
extends ManiphestConduitAPIMethod {
public function getAPIMethodName() {
return 'maniphest.createtask';
}
public function getMethodDescription() {
return pht('Create a new Maniphest task.');
}
+ public function getMethodStatus() {
+ return self::METHOD_STATUS_FROZEN;
+ }
+
+ public function getMethodStatusDescription() {
+ return pht(
+ 'This method is frozen and will eventually be deprecated. New code '.
+ 'should use "maniphest.edit" instead.');
+ }
+
protected function defineParamTypes() {
return $this->getTaskFields($is_new = true);
}
protected function defineReturnType() {
return 'nonempty dict';
}
protected function defineErrorTypes() {
return array(
'ERR-INVALID-PARAMETER' => pht('Missing or malformed parameter.'),
);
}
protected function execute(ConduitAPIRequest $request) {
$task = ManiphestTask::initializeNewTask($request->getUser());
$task = $this->applyRequest($task, $request, $is_new = true);
return $this->buildTaskInfoDictionary($task);
}
}
diff --git a/src/applications/maniphest/conduit/ManiphestInfoConduitAPIMethod.php b/src/applications/maniphest/conduit/ManiphestInfoConduitAPIMethod.php
index 367feff41a..8bd439253f 100644
--- a/src/applications/maniphest/conduit/ManiphestInfoConduitAPIMethod.php
+++ b/src/applications/maniphest/conduit/ManiphestInfoConduitAPIMethod.php
@@ -1,45 +1,55 @@
<?php
final class ManiphestInfoConduitAPIMethod extends ManiphestConduitAPIMethod {
public function getAPIMethodName() {
return 'maniphest.info';
}
public function getMethodDescription() {
return pht('Retrieve information about a Maniphest task, given its ID.');
}
+ public function getMethodStatus() {
+ return self::METHOD_STATUS_FROZEN;
+ }
+
+ public function getMethodStatusDescription() {
+ return pht(
+ 'This method is frozen and will eventually be deprecated. New code '.
+ 'should use "maniphest.search" instead.');
+ }
+
protected function defineParamTypes() {
return array(
'task_id' => 'required id',
);
}
protected function defineReturnType() {
return 'nonempty dict';
}
protected function defineErrorTypes() {
return array(
'ERR_BAD_TASK' => pht('No such Maniphest task exists.'),
);
}
protected function execute(ConduitAPIRequest $request) {
$task_id = $request->getValue('task_id');
$task = id(new ManiphestTaskQuery())
->setViewer($request->getUser())
->withIDs(array($task_id))
->needSubscriberPHIDs(true)
->needProjectPHIDs(true)
->executeOne();
if (!$task) {
throw new ConduitException('ERR_BAD_TASK');
}
return $this->buildTaskInfoDictionary($task);
}
}
diff --git a/src/applications/maniphest/conduit/ManiphestQueryConduitAPIMethod.php b/src/applications/maniphest/conduit/ManiphestQueryConduitAPIMethod.php
index ea8aeb95ed..45373d2587 100644
--- a/src/applications/maniphest/conduit/ManiphestQueryConduitAPIMethod.php
+++ b/src/applications/maniphest/conduit/ManiphestQueryConduitAPIMethod.php
@@ -1,122 +1,132 @@
<?php
final class ManiphestQueryConduitAPIMethod extends ManiphestConduitAPIMethod {
public function getAPIMethodName() {
return 'maniphest.query';
}
public function getMethodDescription() {
return pht('Execute complex searches for Maniphest tasks.');
}
+ public function getMethodStatus() {
+ return self::METHOD_STATUS_FROZEN;
+ }
+
+ public function getMethodStatusDescription() {
+ return pht(
+ 'This method is frozen and will eventually be deprecated. New code '.
+ 'should use "maniphest.search" instead.');
+ }
+
protected function defineParamTypes() {
$statuses = array(
ManiphestTaskQuery::STATUS_ANY,
ManiphestTaskQuery::STATUS_OPEN,
ManiphestTaskQuery::STATUS_CLOSED,
ManiphestTaskQuery::STATUS_RESOLVED,
ManiphestTaskQuery::STATUS_WONTFIX,
ManiphestTaskQuery::STATUS_INVALID,
ManiphestTaskQuery::STATUS_SPITE,
ManiphestTaskQuery::STATUS_DUPLICATE,
);
$status_const = $this->formatStringConstants($statuses);
$orders = array(
ManiphestTaskQuery::ORDER_PRIORITY,
ManiphestTaskQuery::ORDER_CREATED,
ManiphestTaskQuery::ORDER_MODIFIED,
);
$order_const = $this->formatStringConstants($orders);
return array(
'ids' => 'optional list<uint>',
'phids' => 'optional list<phid>',
'ownerPHIDs' => 'optional list<phid>',
'authorPHIDs' => 'optional list<phid>',
'projectPHIDs' => 'optional list<phid>',
'ccPHIDs' => 'optional list<phid>',
'fullText' => 'optional string',
'status' => 'optional '.$status_const,
'order' => 'optional '.$order_const,
'limit' => 'optional int',
'offset' => 'optional int',
);
}
protected function defineReturnType() {
return 'list';
}
protected function execute(ConduitAPIRequest $request) {
$query = id(new ManiphestTaskQuery())
->setViewer($request->getUser())
->needProjectPHIDs(true)
->needSubscriberPHIDs(true);
$task_ids = $request->getValue('ids');
if ($task_ids) {
$query->withIDs($task_ids);
}
$task_phids = $request->getValue('phids');
if ($task_phids) {
$query->withPHIDs($task_phids);
}
$owners = $request->getValue('ownerPHIDs');
if ($owners) {
$query->withOwners($owners);
}
$authors = $request->getValue('authorPHIDs');
if ($authors) {
$query->withAuthors($authors);
}
$projects = $request->getValue('projectPHIDs');
if ($projects) {
$query->withEdgeLogicPHIDs(
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST,
PhabricatorQueryConstraint::OPERATOR_AND,
$projects);
}
$ccs = $request->getValue('ccPHIDs');
if ($ccs) {
$query->withSubscribers($ccs);
}
$full_text = $request->getValue('fullText');
if ($full_text) {
$query->withFullTextSearch($full_text);
}
$status = $request->getValue('status');
if ($status) {
$query->withStatus($status);
}
$order = $request->getValue('order');
if ($order) {
$query->setOrder($order);
}
$limit = $request->getValue('limit');
if ($limit) {
$query->setLimit($limit);
}
$offset = $request->getValue('offset');
if ($offset) {
$query->setOffset($offset);
}
$results = $query->execute();
return $this->buildTaskInfoDictionaries($results);
}
}
diff --git a/src/applications/maniphest/conduit/ManiphestUpdateConduitAPIMethod.php b/src/applications/maniphest/conduit/ManiphestUpdateConduitAPIMethod.php
index d4adee9570..d8d02becd7 100644
--- a/src/applications/maniphest/conduit/ManiphestUpdateConduitAPIMethod.php
+++ b/src/applications/maniphest/conduit/ManiphestUpdateConduitAPIMethod.php
@@ -1,69 +1,79 @@
<?php
final class ManiphestUpdateConduitAPIMethod extends ManiphestConduitAPIMethod {
public function getAPIMethodName() {
return 'maniphest.update';
}
public function getMethodDescription() {
return pht('Update an existing Maniphest task.');
}
+ public function getMethodStatus() {
+ return self::METHOD_STATUS_FROZEN;
+ }
+
+ public function getMethodStatusDescription() {
+ return pht(
+ 'This method is frozen and will eventually be deprecated. New code '.
+ 'should use "maniphest.edit" instead.');
+ }
+
protected function defineErrorTypes() {
return array(
'ERR-BAD-TASK' => pht('No such Maniphest task exists.'),
'ERR-INVALID-PARAMETER' => pht('Missing or malformed parameter.'),
'ERR-NO-EFFECT' => pht('Update has no effect.'),
);
}
protected function defineParamTypes() {
return $this->getTaskFields($is_new = false);
}
protected function defineReturnType() {
return 'nonempty dict';
}
protected function execute(ConduitAPIRequest $request) {
$id = $request->getValue('id');
$phid = $request->getValue('phid');
if (($id && $phid) || (!$id && !$phid)) {
throw new Exception(
pht(
"Specify exactly one of '%s' and '%s'.",
'id',
'phid'));
}
$query = id(new ManiphestTaskQuery())
->setViewer($request->getUser())
->needSubscriberPHIDs(true)
->needProjectPHIDs(true);
if ($id) {
$query->withIDs(array($id));
} else {
$query->withPHIDs(array($phid));
}
$task = $query->executeOne();
$params = $request->getAllParameters();
unset($params['id']);
unset($params['phid']);
if (call_user_func_array('coalesce', $params) === null) {
throw new ConduitException('ERR-NO-EFFECT');
}
if (!$task) {
throw new ConduitException('ERR-BAD-TASK');
}
$task = $this->applyRequest($task, $request, $is_new = false);
return $this->buildTaskInfoDictionary($task);
}
}
diff --git a/src/applications/paste/conduit/PasteCreateConduitAPIMethod.php b/src/applications/paste/conduit/PasteCreateConduitAPIMethod.php
index b6ccd151ba..3badd04da7 100644
--- a/src/applications/paste/conduit/PasteCreateConduitAPIMethod.php
+++ b/src/applications/paste/conduit/PasteCreateConduitAPIMethod.php
@@ -1,72 +1,82 @@
<?php
final class PasteCreateConduitAPIMethod extends PasteConduitAPIMethod {
public function getAPIMethodName() {
return 'paste.create';
}
public function getMethodDescription() {
return pht('Create a new paste.');
}
+ public function getMethodStatus() {
+ return self::METHOD_STATUS_FROZEN;
+ }
+
+ public function getMethodStatusDescription() {
+ return pht(
+ 'This method is frozen and will eventually be deprecated. New code '.
+ 'should use "paste.edit" instead.');
+ }
+
protected function defineParamTypes() {
return array(
'content' => 'required string',
'title' => 'optional string',
'language' => 'optional string',
);
}
protected function defineReturnType() {
return 'nonempty dict';
}
protected function defineErrorTypes() {
return array(
'ERR-NO-PASTE' => pht('Paste may not be empty.'),
);
}
protected function execute(ConduitAPIRequest $request) {
$content = $request->getValue('content');
$title = $request->getValue('title');
$language = $request->getValue('language');
if (!strlen($content)) {
throw new ConduitException('ERR-NO-PASTE');
}
$title = nonempty($title, pht('Masterwork From Distant Lands'));
$language = nonempty($language, '');
$viewer = $request->getUser();
$paste = PhabricatorPaste::initializeNewPaste($viewer);
$xactions = array();
$xactions[] = id(new PhabricatorPasteTransaction())
->setTransactionType(PhabricatorPasteContentTransaction::TRANSACTIONTYPE)
->setNewValue($content);
$xactions[] = id(new PhabricatorPasteTransaction())
->setTransactionType(PhabricatorPasteTitleTransaction::TRANSACTIONTYPE)
->setNewValue($title);
$xactions[] = id(new PhabricatorPasteTransaction())
->setTransactionType(PhabricatorPasteLanguageTransaction::TRANSACTIONTYPE)
->setNewValue($language);
$editor = id(new PhabricatorPasteEditor())
->setActor($viewer)
->setContinueOnNoEffect(true)
->setContentSource($request->newContentSource());
$xactions = $editor->applyTransactions($paste, $xactions);
$paste->attachRawContent($content);
return $this->buildPasteInfoDictionary($paste);
}
}
diff --git a/src/applications/paste/conduit/PasteQueryConduitAPIMethod.php b/src/applications/paste/conduit/PasteQueryConduitAPIMethod.php
index 6f3f87acf2..f20c18c06a 100644
--- a/src/applications/paste/conduit/PasteQueryConduitAPIMethod.php
+++ b/src/applications/paste/conduit/PasteQueryConduitAPIMethod.php
@@ -1,63 +1,73 @@
<?php
final class PasteQueryConduitAPIMethod extends PasteConduitAPIMethod {
public function getAPIMethodName() {
return 'paste.query';
}
public function getMethodDescription() {
return pht('Query Pastes.');
}
+ public function getMethodStatus() {
+ return self::METHOD_STATUS_FROZEN;
+ }
+
+ public function getMethodStatusDescription() {
+ return pht(
+ 'This method is frozen and will eventually be deprecated. New code '.
+ 'should use "paste.search" instead.');
+ }
+
protected function defineParamTypes() {
return array(
'ids' => 'optional list<int>',
'phids' => 'optional list<phid>',
'authorPHIDs' => 'optional list<phid>',
'after' => 'optional int',
'limit' => 'optional int, default = 100',
);
}
protected function defineReturnType() {
return 'list<dict>';
}
protected function execute(ConduitAPIRequest $request) {
$query = id(new PhabricatorPasteQuery())
->setViewer($request->getUser())
->needRawContent(true);
if ($request->getValue('ids')) {
$query->withIDs($request->getValue('ids'));
}
if ($request->getValue('phids')) {
$query->withPHIDs($request->getValue('phids'));
}
if ($request->getValue('authorPHIDs')) {
$query->withAuthorPHIDs($request->getValue('authorPHIDs'));
}
if ($request->getValue('after')) {
$query->setAfterID($request->getValue('after'));
}
$limit = $request->getValue('limit', 100);
if ($limit) {
$query->setLimit($limit);
}
$pastes = $query->execute();
$results = array();
foreach ($pastes as $paste) {
$results[$paste->getPHID()] = $this->buildPasteInfoDictionary($paste);
}
return $results;
}
}
diff --git a/src/applications/people/conduit/UserQueryConduitAPIMethod.php b/src/applications/people/conduit/UserQueryConduitAPIMethod.php
index c42414a6be..df0ec65841 100644
--- a/src/applications/people/conduit/UserQueryConduitAPIMethod.php
+++ b/src/applications/people/conduit/UserQueryConduitAPIMethod.php
@@ -1,82 +1,92 @@
<?php
final class UserQueryConduitAPIMethod extends UserConduitAPIMethod {
public function getAPIMethodName() {
return 'user.query';
}
public function getMethodDescription() {
return pht('Query users.');
}
+ public function getMethodStatus() {
+ return self::METHOD_STATUS_FROZEN;
+ }
+
+ public function getMethodStatusDescription() {
+ return pht(
+ 'This method is frozen and will eventually be deprecated. New code '.
+ 'should use "user.search" instead.');
+ }
+
protected function defineParamTypes() {
return array(
'usernames' => 'optional list<string>',
'emails' => 'optional list<string>',
'realnames' => 'optional list<string>',
'phids' => 'optional list<phid>',
'ids' => 'optional list<uint>',
'offset' => 'optional int',
'limit' => 'optional int (default = 100)',
);
}
protected function defineReturnType() {
return 'list<dict>';
}
protected function defineErrorTypes() {
return array(
'ERR-INVALID-PARAMETER' => pht('Missing or malformed parameter.'),
);
}
protected function execute(ConduitAPIRequest $request) {
$usernames = $request->getValue('usernames', array());
$emails = $request->getValue('emails', array());
$realnames = $request->getValue('realnames', array());
$phids = $request->getValue('phids', array());
$ids = $request->getValue('ids', array());
$offset = $request->getValue('offset', 0);
$limit = $request->getValue('limit', 100);
$query = id(new PhabricatorPeopleQuery())
->setViewer($request->getUser())
->needProfileImage(true)
->needAvailability(true);
if ($usernames) {
$query->withUsernames($usernames);
}
if ($emails) {
$query->withEmails($emails);
}
if ($realnames) {
$query->withRealnames($realnames);
}
if ($phids) {
$query->withPHIDs($phids);
}
if ($ids) {
$query->withIDs($ids);
}
if ($limit) {
$query->setLimit($limit);
}
if ($offset) {
$query->setOffset($offset);
}
$users = $query->execute();
$results = array();
foreach ($users as $user) {
$results[] = $this->buildUserInformationDictionary(
$user,
$with_email = false,
$with_availability = true);
}
return $results;
}
}
diff --git a/src/applications/project/conduit/ProjectCreateConduitAPIMethod.php b/src/applications/project/conduit/ProjectCreateConduitAPIMethod.php
index aefade63a2..66075e7248 100644
--- a/src/applications/project/conduit/ProjectCreateConduitAPIMethod.php
+++ b/src/applications/project/conduit/ProjectCreateConduitAPIMethod.php
@@ -1,81 +1,91 @@
<?php
final class ProjectCreateConduitAPIMethod extends ProjectConduitAPIMethod {
public function getAPIMethodName() {
return 'project.create';
}
public function getMethodDescription() {
return pht('Create a project.');
}
+ public function getMethodStatus() {
+ return self::METHOD_STATUS_FROZEN;
+ }
+
+ public function getMethodStatusDescription() {
+ return pht(
+ 'This method is frozen and will eventually be deprecated. New code '.
+ 'should use "project.edit" instead.');
+ }
+
protected function defineParamTypes() {
return array(
'name' => 'required string',
'members' => 'optional list<phid>',
'icon' => 'optional string',
'color' => 'optional string',
'tags' => 'optional list<string>',
);
}
protected function defineReturnType() {
return 'dict';
}
protected function execute(ConduitAPIRequest $request) {
$user = $request->getUser();
$this->requireApplicationCapability(
ProjectCreateProjectsCapability::CAPABILITY,
$user);
$project = PhabricatorProject::initializeNewProject($user);
$type_name = PhabricatorProjectTransaction::TYPE_NAME;
$members = $request->getValue('members');
$xactions = array();
$xactions[] = id(new PhabricatorProjectTransaction())
->setTransactionType($type_name)
->setNewValue($request->getValue('name'));
if ($request->getValue('icon')) {
$xactions[] = id(new PhabricatorProjectTransaction())
->setTransactionType(PhabricatorProjectTransaction::TYPE_ICON)
->setNewValue($request->getValue('icon'));
}
if ($request->getValue('color')) {
$xactions[] = id(new PhabricatorProjectTransaction())
->setTransactionType(PhabricatorProjectTransaction::TYPE_COLOR)
->setNewValue($request->getValue('color'));
}
if ($request->getValue('tags')) {
$xactions[] = id(new PhabricatorProjectTransaction())
->setTransactionType(PhabricatorProjectTransaction::TYPE_SLUGS)
->setNewValue($request->getValue('tags'));
}
$xactions[] = id(new PhabricatorProjectTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
->setMetadataValue(
'edge:type',
PhabricatorProjectProjectHasMemberEdgeType::EDGECONST)
->setNewValue(
array(
'+' => array_fuse($members),
));
$editor = id(new PhabricatorProjectTransactionEditor())
->setActor($user)
->setContinueOnNoEffect(true)
->setContentSource($request->newContentSource());
$editor->applyTransactions($project, $xactions);
return $this->buildProjectInfoDictionary($project);
}
}
diff --git a/src/applications/project/conduit/ProjectQueryConduitAPIMethod.php b/src/applications/project/conduit/ProjectQueryConduitAPIMethod.php
index 3116ecb7f9..0a02088413 100644
--- a/src/applications/project/conduit/ProjectQueryConduitAPIMethod.php
+++ b/src/applications/project/conduit/ProjectQueryConduitAPIMethod.php
@@ -1,127 +1,137 @@
<?php
final class ProjectQueryConduitAPIMethod extends ProjectConduitAPIMethod {
public function getAPIMethodName() {
return 'project.query';
}
public function getMethodDescription() {
return pht('Execute searches for Projects.');
}
+ public function getMethodStatus() {
+ return self::METHOD_STATUS_FROZEN;
+ }
+
+ public function getMethodStatusDescription() {
+ return pht(
+ 'This method is frozen and will eventually be deprecated. New code '.
+ 'should use "project.search" instead.');
+ }
+
protected function defineParamTypes() {
$statuses = array(
PhabricatorProjectQuery::STATUS_ANY,
PhabricatorProjectQuery::STATUS_OPEN,
PhabricatorProjectQuery::STATUS_CLOSED,
PhabricatorProjectQuery::STATUS_ACTIVE,
PhabricatorProjectQuery::STATUS_ARCHIVED,
);
$status_const = $this->formatStringConstants($statuses);
return array(
'ids' => 'optional list<int>',
'names' => 'optional list<string>',
'phids' => 'optional list<phid>',
'slugs' => 'optional list<string>',
'icons' => 'optional list<string>',
'colors' => 'optional list<string>',
'status' => 'optional '.$status_const,
'members' => 'optional list<phid>',
'limit' => 'optional int',
'offset' => 'optional int',
);
}
protected function defineReturnType() {
return 'list';
}
protected function execute(ConduitAPIRequest $request) {
$query = new PhabricatorProjectQuery();
$query->setViewer($request->getUser());
$query->needMembers(true);
$query->needSlugs(true);
$ids = $request->getValue('ids');
if ($ids) {
$query->withIDs($ids);
}
$names = $request->getValue('names');
if ($names) {
$query->withNames($names);
}
$status = $request->getValue('status');
if ($status) {
$query->withStatus($status);
}
$phids = $request->getValue('phids');
if ($phids) {
$query->withPHIDs($phids);
}
$slugs = $request->getValue('slugs');
if ($slugs) {
$query->withSlugs($slugs);
}
$request->getValue('icons');
if ($request->getValue('icons')) {
$icons = array();
$query->withIcons($icons);
}
$colors = $request->getValue('colors');
if ($colors) {
$query->withColors($colors);
}
$members = $request->getValue('members');
if ($members) {
$query->withMemberPHIDs($members);
}
$limit = $request->getValue('limit');
if ($limit) {
$query->setLimit($limit);
}
$offset = $request->getValue('offset');
if ($offset) {
$query->setOffset($offset);
}
$pager = $this->newPager($request);
$results = $query->executeWithCursorPager($pager);
$projects = $this->buildProjectInfoDictionaries($results);
// TODO: This is pretty hideous.
$slug_map = array();
if ($slugs) {
foreach ($slugs as $slug) {
$normal = PhabricatorSlug::normalizeProjectSlug($slug);
foreach ($projects as $project) {
if (in_array($normal, $project['slugs'])) {
$slug_map[$slug] = $project['phid'];
}
}
}
}
$result = array(
'data' => $projects,
'slugMap' => $slug_map,
);
return $this->addPagerResults($result, $pager);
}
}
diff --git a/src/applications/repository/conduit/RepositoryQueryConduitAPIMethod.php b/src/applications/repository/conduit/RepositoryQueryConduitAPIMethod.php
index a3c1061e6a..61435e2a1b 100644
--- a/src/applications/repository/conduit/RepositoryQueryConduitAPIMethod.php
+++ b/src/applications/repository/conduit/RepositoryQueryConduitAPIMethod.php
@@ -1,85 +1,87 @@
<?php
final class RepositoryQueryConduitAPIMethod
extends RepositoryConduitAPIMethod {
public function getAPIMethodName() {
return 'repository.query';
}
public function getMethodStatus() {
- return self::METHOD_STATUS_UNSTABLE;
+ return self::METHOD_STATUS_FROZEN;
}
public function getMethodStatusDescription() {
- return pht('Repository methods are new and subject to change.');
+ return pht(
+ 'This method is frozen and will eventually be deprecated. New code '.
+ 'should use "diffusion.repository.query" instead.');
}
public function getMethodDescription() {
return pht('Query repositories.');
}
public function newQueryObject() {
return new PhabricatorRepositoryQuery();
}
protected function defineParamTypes() {
return array(
'ids' => 'optional list<int>',
'phids' => 'optional list<phid>',
'callsigns' => 'optional list<string>',
'vcsTypes' => 'optional list<string>',
'remoteURIs' => 'optional list<string>',
'uuids' => 'optional list<string>',
);
}
protected function defineReturnType() {
return 'list<dict>';
}
protected function execute(ConduitAPIRequest $request) {
$query = $this->newQueryForRequest($request);
$ids = $request->getValue('ids', array());
if ($ids) {
$query->withIDs($ids);
}
$phids = $request->getValue('phids', array());
if ($phids) {
$query->withPHIDs($phids);
}
$callsigns = $request->getValue('callsigns', array());
if ($callsigns) {
$query->withCallsigns($callsigns);
}
$vcs_types = $request->getValue('vcsTypes', array());
if ($vcs_types) {
$query->withTypes($vcs_types);
}
$remote_uris = $request->getValue('remoteURIs', array());
if ($remote_uris) {
$query->withURIs($remote_uris);
}
$uuids = $request->getValue('uuids', array());
if ($uuids) {
$query->withUUIDs($uuids);
}
$pager = $this->newPager($request);
$repositories = $query->executeWithCursorPager($pager);
$results = array();
foreach ($repositories as $repository) {
$results[] = $repository->toDictionary();
}
return $results;
}
}
diff --git a/src/applications/search/engine/PhabricatorSearchEngineAPIMethod.php b/src/applications/search/engine/PhabricatorSearchEngineAPIMethod.php
index d050e48bff..3d13f2c3d9 100644
--- a/src/applications/search/engine/PhabricatorSearchEngineAPIMethod.php
+++ b/src/applications/search/engine/PhabricatorSearchEngineAPIMethod.php
@@ -1,610 +1,599 @@
<?php
abstract class PhabricatorSearchEngineAPIMethod
extends ConduitAPIMethod {
abstract public function newSearchEngine();
final public function getQueryMaps($query) {
$maps = $this->getCustomQueryMaps($query);
// Make sure we emit empty maps as objects, not lists.
foreach ($maps as $key => $map) {
if (!$map) {
$maps[$key] = (object)$map;
}
}
if (!$maps) {
$maps = (object)$maps;
}
return $maps;
}
protected function getCustomQueryMaps($query) {
return array();
}
public function getApplication() {
$engine = $this->newSearchEngine();
$class = $engine->getApplicationClassName();
return PhabricatorApplication::getByClass($class);
}
- public function getMethodStatus() {
- return self::METHOD_STATUS_UNSTABLE;
- }
-
- public function getMethodStatusDescription() {
- return pht(
- 'ApplicationSearch methods are fairly stable, but were introduced '.
- 'relatively recently and may continue to evolve as more applications '.
- 'adopt them.');
- }
-
final protected function defineParamTypes() {
return array(
'queryKey' => 'optional string',
'constraints' => 'optional map<string, wild>',
'attachments' => 'optional map<string, bool>',
'order' => 'optional order',
) + $this->getPagerParamTypes();
}
final protected function defineReturnType() {
return 'map<string, wild>';
}
final protected function execute(ConduitAPIRequest $request) {
$engine = $this->newSearchEngine()
->setViewer($request->getUser());
return $engine->buildConduitResponse($request, $this);
}
final public function getMethodDescription() {
return pht(
'This is a standard **ApplicationSearch** method which will let you '.
'list, query, or search for objects. For documentation on these '.
'endpoints, see **[[ %s | Conduit API: Using Search Endpoints ]]**.',
PhabricatorEnv::getDoclink('Conduit API: Using Search Endpoints'));
}
final public function getMethodDocumentation() {
$viewer = $this->getViewer();
$engine = $this->newSearchEngine()
->setViewer($viewer);
$query = $engine->newQuery();
$out = array();
$out[] = $this->buildQueriesBox($engine);
$out[] = $this->buildConstraintsBox($engine);
$out[] = $this->buildOrderBox($engine, $query);
$out[] = $this->buildFieldsBox($engine);
$out[] = $this->buildAttachmentsBox($engine);
$out[] = $this->buildPagingBox($engine);
return $out;
}
private function buildQueriesBox(
PhabricatorApplicationSearchEngine $engine) {
$viewer = $this->getViewer();
$info = pht(<<<EOTEXT
You can choose a builtin or saved query as a starting point for filtering
results by selecting it with `queryKey`. If you don't specify a `queryKey`,
the query will start with no constraints.
For example, many applications have builtin queries like `"active"` or
`"open"` to find only active or enabled results. To use a `queryKey`, specify
it like this:
```lang=json, name="Selecting a Builtin Query"
{
...
"queryKey": "active",
...
}
```
The table below shows the keys to use to select builtin queries and your
saved queries, but you can also use **any** query you run via the web UI as a
starting point. You can find the key for a query by examining the URI after
running a normal search.
You can use these keys to select builtin queries and your configured saved
queries:
EOTEXT
);
$named_queries = $engine->loadAllNamedQueries();
$rows = array();
foreach ($named_queries as $named_query) {
$builtin = $named_query->getIsBuiltin()
? pht('Builtin')
: pht('Custom');
$rows[] = array(
$named_query->getQueryKey(),
$named_query->getQueryName(),
$builtin,
);
}
$table = id(new AphrontTableView($rows))
->setHeaders(
array(
pht('Query Key'),
pht('Name'),
pht('Builtin'),
))
->setColumnClasses(
array(
'prewrap',
'pri wide',
null,
));
return id(new PHUIObjectBoxView())
->setHeaderText(pht('Builtin and Saved Queries'))
->setCollapsed(true)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->appendChild($this->buildRemarkup($info))
->appendChild($table);
}
private function buildConstraintsBox(
PhabricatorApplicationSearchEngine $engine) {
$info = pht(<<<EOTEXT
You can apply custom constraints by passing a dictionary in `constraints`.
This will let you search for specific sets of results (for example, you may
want show only results with a certain state, status, or owner).
If you specify both a `queryKey` and `constraints`, the builtin or saved query
will be applied first as a starting point, then any additional values in
`constraints` will be applied, overwriting the defaults from the original query.
Specify constraints like this:
```lang=json, name="Example Custom Constraints"
{
...
"constraints": {
"authors": ["PHID-USER-1111", "PHID-USER-2222"],
"statuses": ["open", "closed"],
...
},
...
}
```
This API endpoint supports these constraints:
EOTEXT
);
$fields = $engine->getSearchFieldsForConduit();
// As a convenience, put these fields at the very top, even if the engine
// specifies and alternate display order for the web UI. These fields are
// very important in the API and nearly useless in the web UI.
$fields = array_select_keys(
$fields,
array('ids', 'phids')) + $fields;
$rows = array();
foreach ($fields as $field) {
$key = $field->getConduitKey();
$label = $field->getLabel();
$type_object = $field->getConduitParameterType();
if ($type_object) {
$type = $type_object->getTypeName();
$description = $field->getDescription();
} else {
$type = null;
$description = phutil_tag('em', array(), pht('Not supported.'));
}
$rows[] = array(
$key,
$label,
$type,
$description,
);
}
$table = id(new AphrontTableView($rows))
->setHeaders(
array(
pht('Key'),
pht('Label'),
pht('Type'),
pht('Description'),
))
->setColumnClasses(
array(
'prewrap',
'pri',
'prewrap',
'wide',
));
return id(new PHUIObjectBoxView())
->setHeaderText(pht('Custom Query Constraints'))
->setCollapsed(true)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->appendChild($this->buildRemarkup($info))
->appendChild($table);
}
private function buildOrderBox(
PhabricatorApplicationSearchEngine $engine,
$query) {
$orders_info = pht(<<<EOTEXT
Use `order` to choose an ordering for the results.
Either specify a single key from the builtin orders (these are a set of
meaningful, high-level, human-readable orders) or specify a custom list of
low-level columns.
To use a high-level order, choose a builtin order from the table below
and specify it like this:
```lang=json, name="Choosing a Result Order"
{
...
"order": "newest",
...
}
```
These builtin orders are available:
EOTEXT
);
$orders = $query->getBuiltinOrders();
$rows = array();
foreach ($orders as $key => $order) {
$rows[] = array(
$key,
$order['name'],
implode(', ', $order['vector']),
);
}
$orders_table = id(new AphrontTableView($rows))
->setHeaders(
array(
pht('Key'),
pht('Description'),
pht('Columns'),
))
->setColumnClasses(
array(
'pri',
'',
'wide',
));
$columns_info = pht(<<<EOTEXT
You can choose a low-level column order instead. To do this, provide a list
of columns instead of a single key. This is an advanced feature.
In a custom column order:
- each column may only be specified once;
- each column may be prefixed with `-` to invert the order;
- the last column must be a unique column, usually `id`; and
- no column other than the last may be unique.
To use a low-level order, choose a sequence of columns and specify them like
this:
```lang=json, name="Using a Custom Order"
{
...
"order": ["color", "-name", "id"],
...
}
```
These low-level columns are available:
EOTEXT
);
$columns = $query->getOrderableColumns();
$rows = array();
foreach ($columns as $key => $column) {
$rows[] = array(
$key,
idx($column, 'unique') ? pht('Yes') : pht('No'),
);
}
$columns_table = id(new AphrontTableView($rows))
->setHeaders(
array(
pht('Key'),
pht('Unique'),
))
->setColumnClasses(
array(
'pri',
'wide',
));
return id(new PHUIObjectBoxView())
->setHeaderText(pht('Result Ordering'))
->setCollapsed(true)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->appendChild($this->buildRemarkup($orders_info))
->appendChild($orders_table)
->appendChild($this->buildRemarkup($columns_info))
->appendChild($columns_table);
}
private function buildFieldsBox(
PhabricatorApplicationSearchEngine $engine) {
$info = pht(<<<EOTEXT
Objects matching your query are returned as a list of dictionaries in the
`data` property of the results. Each dictionary has some metadata and a
`fields` key, which contains the information abou the object that most callers
will be interested in.
For example, the results may look something like this:
```lang=json, name="Example Results"
{
...
"data": [
{
"id": 123,
"phid": "PHID-WXYZ-1111",
"fields": {
"name": "First Example Object",
"authorPHID": "PHID-USER-2222"
}
},
{
"id": 124,
"phid": "PHID-WXYZ-3333",
"fields": {
"name": "Second Example Object",
"authorPHID": "PHID-USER-4444"
}
},
...
]
...
}
```
This result structure is standardized across all search methods, but the
available fields differ from application to application.
These are the fields available on this object type:
EOTEXT
);
$specs = $engine->getAllConduitFieldSpecifications();
$rows = array();
foreach ($specs as $key => $spec) {
$type = $spec->getType();
$description = $spec->getDescription();
$rows[] = array(
$key,
$type,
$description,
);
}
$table = id(new AphrontTableView($rows))
->setHeaders(
array(
pht('Key'),
pht('Type'),
pht('Description'),
))
->setColumnClasses(
array(
'pri',
'mono',
'wide',
));
return id(new PHUIObjectBoxView())
->setHeaderText(pht('Object Fields'))
->setCollapsed(true)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->appendChild($this->buildRemarkup($info))
->appendChild($table);
}
private function buildAttachmentsBox(
PhabricatorApplicationSearchEngine $engine) {
$info = pht(<<<EOTEXT
By default, only basic information about objects is returned. If you want
more extensive information, you can use available `attachments` to get more
information in the results (like subscribers and projects).
Generally, requesting more information means the query executes more slowly
and returns more data (in some cases, much more data). You should normally
request only the data you need.
To request extra data, specify which attachments you want in the `attachments`
parameter:
```lang=json, name="Example Attachments Request"
{
...
"attachments": {
"subscribers": true
},
...
}
```
This example specifies that results should include information about
subscribers. In the return value, each object will now have this information
filled out in the corresponding `attachments` value:
```lang=json, name="Example Attachments Result"
{
...
"data": [
{
...
"attachments": {
"subscribers": {
"subscriberPHIDs": [
"PHID-WXYZ-2222",
],
"subscriberCount": 1,
"viewerIsSubscribed": false
}
},
...
},
...
],
...
}
```
These attachments are available:
EOTEXT
);
$attachments = $engine->getConduitSearchAttachments();
$rows = array();
foreach ($attachments as $key => $attachment) {
$rows[] = array(
$key,
$attachment->getAttachmentName(),
$attachment->getAttachmentDescription(),
);
}
$table = id(new AphrontTableView($rows))
->setNoDataString(pht('This call does not support any attachments.'))
->setHeaders(
array(
pht('Key'),
pht('Name'),
pht('Description'),
))
->setColumnClasses(
array(
'prewrap',
'pri',
'wide',
));
return id(new PHUIObjectBoxView())
->setHeaderText(pht('Attachments'))
->setCollapsed(true)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->appendChild($this->buildRemarkup($info))
->appendChild($table);
}
private function buildPagingBox(
PhabricatorApplicationSearchEngine $engine) {
$info = pht(<<<EOTEXT
Queries are limited to returning 100 results at a time. If you want fewer
results than this, you can use `limit` to specify a smaller limit.
If you want more results, you'll need to make additional queries to retrieve
more pages of results.
The result structure contains a `cursor` key with information you'll need in
order to fetch the next page of results. After an initial query, it will
usually look something like this:
```lang=json, name="Example Cursor Result"
{
...
"cursor": {
"limit": 100,
"after": "1234",
"before": null,
"order": null
}
...
}
```
The `limit` and `order` fields are describing the effective limit and order the
query was executed with, and are usually not of much interest. The `after` and
`before` fields give you cursors which you can pass when making another API
call in order to get the next (or previous) page of results.
To get the next page of results, repeat your API call with all the same
parameters as the original call, but pass the `after` cursor you received from
the first call in the `after` parameter when making the second call.
If you do things correctly, you should get the second page of results, and
a cursor structure like this:
```lang=json, name="Second Result Page"
{
...
"cursor": {
"limit": 5,
"after": "4567",
"before": "7890",
"order": null
}
...
}
```
You can now continue to the third page of results by passing the new `after`
cursor to the `after` parameter in your third call, or return to the previous
page of results by passing the `before` cursor to the `before` parameter. This
might be useful if you are rendering a web UI for a user and want to provide
"Next Page" and "Previous Page" links.
If `after` is `null`, there is no next page of results available. Likewise,
if `before` is `null`, there are no previous results available.
EOTEXT
);
return id(new PHUIObjectBoxView())
->setHeaderText(pht('Paging and Limits'))
->setCollapsed(true)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->appendChild($this->buildRemarkup($info));
}
private function buildRemarkup($remarkup) {
$viewer = $this->getViewer();
$view = new PHUIRemarkupView($viewer, $remarkup);
$view->setRemarkupOptions(
array(
PHUIRemarkupView::OPTION_PRESERVE_LINEBREAKS => false,
));
return id(new PHUIBoxView())
->appendChild($view)
->addPadding(PHUI::PADDING_LARGE);
}
}
diff --git a/src/applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php b/src/applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php
index de693a4049..3a6dd4b70c 100644
--- a/src/applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php
+++ b/src/applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php
@@ -1,171 +1,160 @@
<?php
abstract class PhabricatorEditEngineAPIMethod
extends ConduitAPIMethod {
abstract public function newEditEngine();
public function getApplication() {
$engine = $this->newEditEngine();
$class = $engine->getEngineApplicationClass();
return PhabricatorApplication::getByClass($class);
}
- public function getMethodStatus() {
- return self::METHOD_STATUS_UNSTABLE;
- }
-
- public function getMethodStatusDescription() {
- return pht(
- 'ApplicationEditor methods are fairly stable, but were introduced '.
- 'relatively recently and may continue to evolve as more applications '.
- 'adopt them.');
- }
-
final protected function defineParamTypes() {
return array(
'transactions' => 'list<map<string, wild>>',
'objectIdentifier' => 'optional id|phid|string',
);
}
final protected function defineReturnType() {
return 'map<string, wild>';
}
final protected function execute(ConduitAPIRequest $request) {
$engine = $this->newEditEngine()
->setViewer($request->getUser());
return $engine->buildConduitResponse($request);
}
final public function getMethodDescription() {
return pht(
'This is a standard **ApplicationEditor** method which allows you to '.
'create and modify objects by applying transactions. For documentation '.
'on these endpoints, see '.
'**[[ %s | Conduit API: Using Edit Endpoints ]]**.',
PhabricatorEnv::getDoclink('Conduit API: Using Edit Endpoints'));
}
final public function getMethodDocumentation() {
$viewer = $this->getViewer();
$engine = $this->newEditEngine()
->setViewer($viewer);
$types = $engine->getConduitEditTypes();
$out = array();
$out[] = $this->buildEditTypesBoxes($engine, $types);
return $out;
}
private function buildEditTypesBoxes(
PhabricatorEditEngine $engine,
array $types) {
$boxes = array();
$summary_info = pht(
'This endpoint supports these types of transactions. See below for '.
'detailed information about each transaction type.');
$rows = array();
foreach ($types as $type) {
$rows[] = array(
$type->getEditType(),
$type->getConduitDescription(),
);
}
$summary_table = id(new AphrontTableView($rows))
->setHeaders(
array(
pht('Key'),
pht('Description'),
))
->setColumnClasses(
array(
'prewrap',
'wide',
));
$boxes[] = id(new PHUIObjectBoxView())
->setHeaderText(pht('Transaction Types'))
->setCollapsed(true)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->appendChild($this->buildRemarkup($summary_info))
->appendChild($summary_table);
foreach ($types as $type) {
$section = array();
$section[] = $type->getConduitDescription();
$type_documentation = $type->getConduitDocumentation();
if (strlen($type_documentation)) {
$section[] = $type_documentation;
}
$section = implode("\n\n", $section);
$rows = array();
$rows[] = array(
'type',
'const',
$type->getEditType(),
);
$rows[] = array(
'value',
$type->getConduitType(),
$type->getConduitTypeDescription(),
);
$type_table = id(new AphrontTableView($rows))
->setHeaders(
array(
pht('Key'),
pht('Type'),
pht('Description'),
))
->setColumnClasses(
array(
'prewrap',
'prewrap',
'wide',
));
$boxes[] = id(new PHUIObjectBoxView())
->setHeaderText(pht('Transaction Type: %s', $type->getEditType()))
->setCollapsed(true)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->appendChild($this->buildRemarkup($section))
->appendChild($type_table);
}
return $boxes;
}
private function buildRemarkup($remarkup) {
$viewer = $this->getViewer();
$view = new PHUIRemarkupView($viewer, $remarkup);
$view->setRemarkupOptions(
array(
PHUIRemarkupView::OPTION_PRESERVE_LINEBREAKS => false,
));
return id(new PHUIBoxView())
->appendChild($view)
->addPadding(PHUI::PADDING_LARGE);
}
}

File Metadata

Mime Type
text/x-diff
Expires
Tue, Jul 29, 6:56 AM (2 w, 5 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
188201
Default Alt Text
(94 KB)

Event Timeline