mirror of
https://we.phorge.it/source/phorge.git
synced 2025-04-01 06:58:14 +02:00
(stable) Promote 2019 Week 45
This commit is contained in:
commit
cf6df55dbe
21 changed files with 334 additions and 76 deletions
|
@ -10,7 +10,7 @@ return array(
|
|||
'conpherence.pkg.css' => '3c8a0668',
|
||||
'conpherence.pkg.js' => '020aebcf',
|
||||
'core.pkg.css' => '77de226f',
|
||||
'core.pkg.js' => '6e5c894f',
|
||||
'core.pkg.js' => '705aec2c',
|
||||
'differential.pkg.css' => '607c84be',
|
||||
'differential.pkg.js' => '1b97518d',
|
||||
'diffusion.pkg.css' => '42c75c37',
|
||||
|
@ -448,7 +448,7 @@ return array(
|
|||
'rsrc/js/application/uiexample/notification-example.js' => '29819b75',
|
||||
'rsrc/js/core/Busy.js' => '5202e831',
|
||||
'rsrc/js/core/DragAndDropFileUpload.js' => '4370900d',
|
||||
'rsrc/js/core/DraggableList.js' => 'c9ad6f70',
|
||||
'rsrc/js/core/DraggableList.js' => '0169e425',
|
||||
'rsrc/js/core/Favicon.js' => '7930776a',
|
||||
'rsrc/js/core/FileUpload.js' => 'ab85e184',
|
||||
'rsrc/js/core/Hovercard.js' => '074f0783',
|
||||
|
@ -777,7 +777,7 @@ return array(
|
|||
'phabricator-diff-changeset-list' => '0f5c016d',
|
||||
'phabricator-diff-inline' => 'a4a14a94',
|
||||
'phabricator-drag-and-drop-file-upload' => '4370900d',
|
||||
'phabricator-draggable-list' => 'c9ad6f70',
|
||||
'phabricator-draggable-list' => '0169e425',
|
||||
'phabricator-fatal-config-template-css' => '20babf50',
|
||||
'phabricator-favicon' => '7930776a',
|
||||
'phabricator-feed-css' => 'd8b6e3f8',
|
||||
|
@ -920,6 +920,14 @@ return array(
|
|||
'javelin-uri',
|
||||
'phabricator-notification',
|
||||
),
|
||||
'0169e425' => array(
|
||||
'javelin-install',
|
||||
'javelin-dom',
|
||||
'javelin-stratcom',
|
||||
'javelin-util',
|
||||
'javelin-vector',
|
||||
'javelin-magical-init',
|
||||
),
|
||||
'022516b4' => array(
|
||||
'javelin-install',
|
||||
'javelin-util',
|
||||
|
@ -2032,14 +2040,6 @@ return array(
|
|||
'javelin-util',
|
||||
'phabricator-keyboard-shortcut-manager',
|
||||
),
|
||||
'c9ad6f70' => array(
|
||||
'javelin-install',
|
||||
'javelin-dom',
|
||||
'javelin-stratcom',
|
||||
'javelin-util',
|
||||
'javelin-vector',
|
||||
'javelin-magical-init',
|
||||
),
|
||||
'cf32921f' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
|
|
|
@ -4648,6 +4648,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorSearchEngineExtensionModule' => 'applications/search/engineextension/PhabricatorSearchEngineExtensionModule.php',
|
||||
'PhabricatorSearchFerretNgramGarbageCollector' => 'applications/search/garbagecollector/PhabricatorSearchFerretNgramGarbageCollector.php',
|
||||
'PhabricatorSearchField' => 'applications/search/field/PhabricatorSearchField.php',
|
||||
'PhabricatorSearchHandleController' => 'applications/search/controller/PhabricatorSearchHandleController.php',
|
||||
'PhabricatorSearchHost' => 'infrastructure/cluster/search/PhabricatorSearchHost.php',
|
||||
'PhabricatorSearchHovercardController' => 'applications/search/controller/PhabricatorSearchHovercardController.php',
|
||||
'PhabricatorSearchIndexVersion' => 'applications/search/storage/PhabricatorSearchIndexVersion.php',
|
||||
|
@ -4872,6 +4873,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorSystemActionRateLimitException' => 'applications/system/exception/PhabricatorSystemActionRateLimitException.php',
|
||||
'PhabricatorSystemApplication' => 'applications/system/application/PhabricatorSystemApplication.php',
|
||||
'PhabricatorSystemDAO' => 'applications/system/storage/PhabricatorSystemDAO.php',
|
||||
'PhabricatorSystemDebugUIEventListener' => 'applications/system/events/PhabricatorSystemDebugUIEventListener.php',
|
||||
'PhabricatorSystemDestructionGarbageCollector' => 'applications/system/garbagecollector/PhabricatorSystemDestructionGarbageCollector.php',
|
||||
'PhabricatorSystemDestructionLog' => 'applications/system/storage/PhabricatorSystemDestructionLog.php',
|
||||
'PhabricatorSystemObjectController' => 'applications/system/controller/PhabricatorSystemObjectController.php',
|
||||
|
@ -11270,6 +11272,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorSearchEngineExtensionModule' => 'PhabricatorConfigModule',
|
||||
'PhabricatorSearchFerretNgramGarbageCollector' => 'PhabricatorGarbageCollector',
|
||||
'PhabricatorSearchField' => 'Phobject',
|
||||
'PhabricatorSearchHandleController' => 'PhabricatorSearchBaseController',
|
||||
'PhabricatorSearchHost' => 'Phobject',
|
||||
'PhabricatorSearchHovercardController' => 'PhabricatorSearchBaseController',
|
||||
'PhabricatorSearchIndexVersion' => 'PhabricatorSearchDAO',
|
||||
|
@ -11511,6 +11514,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorSystemActionRateLimitException' => 'Exception',
|
||||
'PhabricatorSystemApplication' => 'PhabricatorApplication',
|
||||
'PhabricatorSystemDAO' => 'PhabricatorLiskDAO',
|
||||
'PhabricatorSystemDebugUIEventListener' => 'PhabricatorEventListener',
|
||||
'PhabricatorSystemDestructionGarbageCollector' => 'PhabricatorGarbageCollector',
|
||||
'PhabricatorSystemDestructionLog' => 'PhabricatorSystemDAO',
|
||||
'PhabricatorSystemObjectController' => 'PhabricatorController',
|
||||
|
|
|
@ -14,7 +14,9 @@ final class PhutilAsanaAuthAdapter extends PhutilOAuthAuthAdapter {
|
|||
}
|
||||
|
||||
public function getAccountID() {
|
||||
return $this->getOAuthAccountData('id');
|
||||
// See T13453. The Asana API has changed to string IDs and now returns a
|
||||
// "gid" field (previously, it returned an "id" field).
|
||||
return $this->getOAuthAccountData('gid');
|
||||
}
|
||||
|
||||
public function getAccountEmail() {
|
||||
|
|
|
@ -329,9 +329,16 @@ final class PhabricatorDashboardPanelRenderingEngine extends Phobject {
|
|||
$actions = array();
|
||||
|
||||
if ($panel) {
|
||||
$panel_actions = $panel->newHeaderEditActions(
|
||||
$viewer,
|
||||
$context_phid);
|
||||
try {
|
||||
$panel_actions = $panel->newHeaderEditActions(
|
||||
$viewer,
|
||||
$context_phid);
|
||||
} catch (Exception $ex) {
|
||||
$error_action = id(new PhabricatorActionView())
|
||||
->setIcon('fa-exclamation-triangle red')
|
||||
->setName(pht('<Rendering Exception>'));
|
||||
$panel_actions[] = $error_action;
|
||||
}
|
||||
|
||||
if ($panel_actions) {
|
||||
foreach ($panel_actions as $panel_action) {
|
||||
|
|
|
@ -22,6 +22,10 @@ final class DifferentialChangesetSearchEngine
|
|||
return 'PhabricatorDifferentialApplication';
|
||||
}
|
||||
|
||||
public function canUseInPanelContext() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function newQuery() {
|
||||
$query = id(new DifferentialChangesetQuery());
|
||||
|
||||
|
|
|
@ -371,7 +371,7 @@ final class DifferentialChangesetOneUpRenderer
|
|||
$cell_classes = $block_diff->getNewClasses();
|
||||
}
|
||||
} else if ($row_type === 'old') {
|
||||
if (!$old_ref) {
|
||||
if (!$old_ref || !$old) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -384,7 +384,7 @@ final class DifferentialChangesetOneUpRenderer
|
|||
|
||||
$new_key = null;
|
||||
} else if ($row_type === 'new') {
|
||||
if (!$new_ref) {
|
||||
if (!$new_ref || !$new) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ final class DiffusionRepositoryBranchesManagementPanel
|
|||
|
||||
$has_any =
|
||||
$repository->getDetail('default-branch') ||
|
||||
$repository->getFetchRules() ||
|
||||
$repository->getTrackOnlyRules() ||
|
||||
$repository->getPermanentRefRules();
|
||||
|
||||
|
|
|
@ -123,8 +123,11 @@ final class DoorkeeperBridgeAsana extends DoorkeeperBridge {
|
|||
}
|
||||
|
||||
public function fillObjectFromData(DoorkeeperExternalObject $obj, $result) {
|
||||
$id = $result['id'];
|
||||
$uri = "https://app.asana.com/0/{$id}/{$id}";
|
||||
$gid = $result['gid'];
|
||||
$uri = urisprintf(
|
||||
'https://app.asana.com/0/%s/%s',
|
||||
$gid,
|
||||
$gid);
|
||||
$obj->setObjectURI($uri);
|
||||
}
|
||||
|
||||
|
|
|
@ -102,7 +102,10 @@ final class PhabricatorAsanaConfigOptions
|
|||
pht('Workspace Name'));
|
||||
$out[] = '| ------------ | -------------- |';
|
||||
foreach ($workspaces as $workspace) {
|
||||
$out[] = sprintf('| `%s` | `%s` |', $workspace['id'], $workspace['name']);
|
||||
$out[] = sprintf(
|
||||
'| `%s` | `%s` |',
|
||||
$workspace['gid'],
|
||||
$workspace['name']);
|
||||
}
|
||||
|
||||
$out = implode("\n", $out);
|
||||
|
|
|
@ -358,7 +358,7 @@ final class DoorkeeperAsanaFeedWorker extends DoorkeeperFeedWorker {
|
|||
'POST',
|
||||
$subtask_data + array(
|
||||
'assignee' => $phid_aid_map[$user_phid],
|
||||
'completed' => $is_completed,
|
||||
'completed' => (int)$is_completed,
|
||||
'parent' => $parent_ref->getObjectID(),
|
||||
));
|
||||
|
||||
|
@ -393,7 +393,7 @@ final class DoorkeeperAsanaFeedWorker extends DoorkeeperFeedWorker {
|
|||
'PUT',
|
||||
$subtask_data + array(
|
||||
'assignee' => $phid_aid_map[$user_phid],
|
||||
'completed' => $is_completed,
|
||||
'completed' => (int)$is_completed,
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -484,7 +484,7 @@ final class DoorkeeperAsanaFeedWorker extends DoorkeeperFeedWorker {
|
|||
return array(
|
||||
'name' => $title,
|
||||
'notes' => $notes,
|
||||
'completed' => $is_completed,
|
||||
'completed' => (int)$is_completed,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -632,7 +632,7 @@ final class DoorkeeperAsanaFeedWorker extends DoorkeeperFeedWorker {
|
|||
->setApplicationType(DoorkeeperBridgeAsana::APPTYPE_ASANA)
|
||||
->setApplicationDomain(DoorkeeperBridgeAsana::APPDOMAIN_ASANA)
|
||||
->setObjectType($type)
|
||||
->setObjectID($result['id'])
|
||||
->setObjectID($result['gid'])
|
||||
->setIsVisible(true);
|
||||
|
||||
$xobj = $ref->newExternalObject();
|
||||
|
|
|
@ -399,7 +399,7 @@ abstract class HeraldAdapter extends Phobject {
|
|||
self::CONDITION_IS_NOT_ANY => pht('is not any of'),
|
||||
self::CONDITION_INCLUDE_ALL => pht('include all of'),
|
||||
self::CONDITION_INCLUDE_ANY => pht('include any of'),
|
||||
self::CONDITION_INCLUDE_NONE => pht('do not include'),
|
||||
self::CONDITION_INCLUDE_NONE => pht('include none of'),
|
||||
self::CONDITION_IS_ME => pht('is myself'),
|
||||
self::CONDITION_IS_NOT_ME => pht('is not myself'),
|
||||
self::CONDITION_REGEXP => pht('matches regexp'),
|
||||
|
|
|
@ -71,21 +71,30 @@ final class PhabricatorUserUsernameTransaction
|
|||
}
|
||||
|
||||
if (!strlen($new)) {
|
||||
$errors[] = $this->newRequiredError(
|
||||
pht('New username is required.'), $xaction);
|
||||
$errors[] = $this->newInvalidError(
|
||||
pht('New username is required.'),
|
||||
$xaction);
|
||||
} else if (!PhabricatorUser::validateUsername($new)) {
|
||||
$errors[] = $this->newInvalidError(
|
||||
PhabricatorUser::describeValidUsername(), $xaction);
|
||||
PhabricatorUser::describeValidUsername(),
|
||||
$xaction);
|
||||
}
|
||||
|
||||
$user = id(new PhabricatorPeopleQuery())
|
||||
->setViewer(PhabricatorUser::getOmnipotentUser())
|
||||
->withUsernames(array($new))
|
||||
->executeOne();
|
||||
|
||||
if ($user) {
|
||||
$errors[] = $this->newInvalidError(
|
||||
pht('Another user already has that username.'), $xaction);
|
||||
// See T13446. We may be changing the letter case of a username, which
|
||||
// is a perfectly fine edit.
|
||||
$is_self = ($user->getPHID() === $object->getPHID());
|
||||
if (!$is_self) {
|
||||
$errors[] = $this->newInvalidError(
|
||||
pht(
|
||||
'Another user already has the username "%s".',
|
||||
$new),
|
||||
$xaction);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -257,16 +257,15 @@ final class PhabricatorRepositoryPullEngine
|
|||
|
||||
$path = rtrim($repository->getLocalPath(), '/');
|
||||
|
||||
if ($repository->isHosted()) {
|
||||
$repository->execxRemoteCommand(
|
||||
'init --bare -- %s',
|
||||
$path);
|
||||
} else {
|
||||
$repository->execxRemoteCommand(
|
||||
'clone --bare -- %P %s',
|
||||
$repository->getRemoteURIEnvelope(),
|
||||
$path);
|
||||
}
|
||||
// See T13448. In all cases, we create repositories by using "git init"
|
||||
// to build a bare, empty working copy. If we try to use "git clone"
|
||||
// instead, we'll pull in too many refs if "Fetch Refs" is also
|
||||
// configured. There's no apparent way to make "git clone" behave narrowly
|
||||
// and no apparent reason to bother.
|
||||
|
||||
$repository->execxRemoteCommand(
|
||||
'init --bare -- %s',
|
||||
$path);
|
||||
}
|
||||
|
||||
|
||||
|
@ -290,29 +289,27 @@ final class PhabricatorRepositoryPullEngine
|
|||
$files = Filesystem::listDirectory($path, $include_hidden = true);
|
||||
if (!$files) {
|
||||
$message = pht(
|
||||
"Expected to find a git repository at '%s', but there ".
|
||||
"is an empty directory there. Remove the directory: the daemon ".
|
||||
"will run '%s' for you.",
|
||||
$path,
|
||||
'git clone');
|
||||
'Expected to find a Git repository at "%s", but there is an '.
|
||||
'empty directory there. Remove the directory. A daemon will '.
|
||||
'construct the working copy for you.',
|
||||
$path);
|
||||
} else {
|
||||
$message = pht(
|
||||
"Expected to find a git repository at '%s', but there is ".
|
||||
"a non-repository directory (with other stuff in it) there. Move ".
|
||||
"or remove this directory (or reconfigure the repository to use a ".
|
||||
"different directory), and then either clone a repository ".
|
||||
"yourself or let the daemon do it.",
|
||||
'Expected to find a Git repository at "%s", but there is '.
|
||||
'a non-repository directory (with other stuff in it) there. '.
|
||||
'Move or remove this directory. A daemon will construct '.
|
||||
'the working copy for you.',
|
||||
$path);
|
||||
}
|
||||
} else if (is_file($path)) {
|
||||
$message = pht(
|
||||
"Expected to find a git repository at '%s', but there is a ".
|
||||
"file there instead. Remove it and let the daemon clone a ".
|
||||
"repository for you.",
|
||||
'Expected to find a Git repository at "%s", but there is a '.
|
||||
'file there instead. Move or remove this file. A daemon will '.
|
||||
'construct the working copy for you.',
|
||||
$path);
|
||||
} else {
|
||||
$message = pht(
|
||||
"Expected to find a git repository at '%s', but did not.",
|
||||
'Expected to find a git repository at "%s", but did not.',
|
||||
$path);
|
||||
}
|
||||
} else {
|
||||
|
@ -327,11 +324,10 @@ final class PhabricatorRepositoryPullEngine
|
|||
} else if (!Filesystem::pathsAreEquivalent($repo_path, $path)) {
|
||||
$err = true;
|
||||
$message = pht(
|
||||
"Expected to find repo at '%s', but the actual git repository root ".
|
||||
"for this directory is '%s'. Something is misconfigured. ".
|
||||
"The repository's 'Local Path' should be set to some place where ".
|
||||
"the daemon can check out a working copy, ".
|
||||
"and should not be inside another git repository.",
|
||||
'Expected to find a Git repository at "%s", but the actual Git '.
|
||||
'repository root for this directory is "%s". Something is '.
|
||||
'misconfigured. This directory should be writable by the daemons '.
|
||||
'and not inside another Git repository.',
|
||||
$path,
|
||||
$repo_path);
|
||||
}
|
||||
|
@ -353,13 +349,56 @@ final class PhabricatorRepositoryPullEngine
|
|||
// Load the refs we're planning to fetch from the remote repository.
|
||||
$remote_refs = $this->loadGitRemoteRefs(
|
||||
$repository,
|
||||
$repository->getRemoteURIEnvelope());
|
||||
$repository->getRemoteURIEnvelope(),
|
||||
$is_local = false);
|
||||
|
||||
// Load the refs we're planning to fetch from the local repository, by
|
||||
// using the local working copy path as the "remote" repository URI.
|
||||
$local_refs = $this->loadGitRemoteRefs(
|
||||
$repository,
|
||||
new PhutilOpaqueEnvelope($path));
|
||||
new PhutilOpaqueEnvelope($path),
|
||||
$is_local = true);
|
||||
|
||||
// See T13448. The "git fetch --prune ..." flag only prunes local refs
|
||||
// matching the refspecs we pass it. If "Fetch Refs" is configured, we'll
|
||||
// pass it a very narrow list of refspecs, and it won't prune older refs
|
||||
// that aren't currently subject to fetching.
|
||||
|
||||
// Since we want to prune everything that isn't (a) on the fetch list and
|
||||
// (b) in the remote, handle pruning of any surplus leftover refs ourselves
|
||||
// before we fetch anything.
|
||||
|
||||
// (We don't have to do this if "Fetch Refs" isn't set up, since "--prune"
|
||||
// will work in that case, but it's a little simpler to always go down the
|
||||
// same code path.)
|
||||
|
||||
$surplus_refs = array();
|
||||
foreach ($local_refs as $local_ref => $local_hash) {
|
||||
$remote_hash = idx($remote_refs, $local_ref);
|
||||
if ($remote_hash === null) {
|
||||
$surplus_refs[] = $local_ref;
|
||||
}
|
||||
}
|
||||
|
||||
if ($surplus_refs) {
|
||||
$this->log(
|
||||
pht(
|
||||
'Found %s surplus local ref(s) to delete.',
|
||||
phutil_count($surplus_refs)));
|
||||
foreach ($surplus_refs as $surplus_ref) {
|
||||
$this->log(
|
||||
pht(
|
||||
'Deleting surplus local ref "%s" ("%s").',
|
||||
$surplus_ref,
|
||||
$local_refs[$surplus_ref]));
|
||||
|
||||
$repository->execLocalCommand(
|
||||
'update-ref -d %R --',
|
||||
$surplus_ref);
|
||||
|
||||
unset($local_refs[$surplus_ref]);
|
||||
}
|
||||
}
|
||||
|
||||
if ($remote_refs === $local_refs) {
|
||||
$this->log(
|
||||
|
@ -378,7 +417,7 @@ final class PhabricatorRepositoryPullEngine
|
|||
// checked out. See T13280.
|
||||
|
||||
$future = $repository->getRemoteCommandFuture(
|
||||
'fetch --prune --update-head-ok -- %P %Ls',
|
||||
'fetch --no-tags --update-head-ok -- %P %Ls',
|
||||
$repository->getRemoteURIEnvelope(),
|
||||
$fetch_rules);
|
||||
|
||||
|
@ -474,21 +513,32 @@ final class PhabricatorRepositoryPullEngine
|
|||
|
||||
private function loadGitRemoteRefs(
|
||||
PhabricatorRepository $repository,
|
||||
PhutilOpaqueEnvelope $remote_envelope) {
|
||||
PhutilOpaqueEnvelope $remote_envelope,
|
||||
$is_local) {
|
||||
|
||||
$ref_rules = $this->getGitRefRules($repository);
|
||||
// See T13448. When listing local remotes, we want to list everything,
|
||||
// not just refs we expect to fetch. This allows us to detect that we have
|
||||
// undesirable refs (which have been deleted in the remote, but are still
|
||||
// present locally) so we can update our state to reflect the correct
|
||||
// remote state.
|
||||
|
||||
// NOTE: "git ls-remote" does not support "--" until circa January 2016.
|
||||
// See T12416. None of the flags to "ls-remote" appear dangerous, but
|
||||
// refuse to list any refs beginning with "-" just in case.
|
||||
if ($is_local) {
|
||||
$ref_rules = array();
|
||||
} else {
|
||||
$ref_rules = $this->getGitRefRules($repository);
|
||||
|
||||
foreach ($ref_rules as $ref_rule) {
|
||||
if (preg_match('/^-/', $ref_rule)) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Refusing to list potentially dangerous ref ("%s") beginning '.
|
||||
'with "-".',
|
||||
$ref_rule));
|
||||
// NOTE: "git ls-remote" does not support "--" until circa January 2016.
|
||||
// See T12416. None of the flags to "ls-remote" appear dangerous, but
|
||||
// refuse to list any refs beginning with "-" just in case.
|
||||
|
||||
foreach ($ref_rules as $ref_rule) {
|
||||
if (preg_match('/^-/', $ref_rule)) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Refusing to list potentially dangerous ref ("%s") beginning '.
|
||||
'with "-".',
|
||||
$ref_rule));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -33,6 +33,8 @@ final class PhabricatorSearchApplication extends PhabricatorApplication {
|
|||
'index/(?P<phid>[^/]+)/' => 'PhabricatorSearchIndexController',
|
||||
'hovercard/'
|
||||
=> 'PhabricatorSearchHovercardController',
|
||||
'handle/(?P<phid>[^/]+)/'
|
||||
=> 'PhabricatorSearchHandleController',
|
||||
'edit/' => array(
|
||||
'key/(?P<queryKey>[^/]+)/' => 'PhabricatorSearchEditController',
|
||||
'id/(?P<id>[^/]+)/' => 'PhabricatorSearchEditController',
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorSearchHandleController
|
||||
extends PhabricatorSearchBaseController {
|
||||
|
||||
public function shouldAllowPublic() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $this->getViewer();
|
||||
$phid = $request->getURIData('phid');
|
||||
|
||||
$handles = $viewer->loadHandles(array($phid));
|
||||
$handle = $handles[$phid];
|
||||
|
||||
$cancel_uri = $handle->getURI();
|
||||
if (!$cancel_uri) {
|
||||
$cancel_uri = '/';
|
||||
}
|
||||
|
||||
$rows = array();
|
||||
|
||||
$rows[] = array(
|
||||
pht('PHID'),
|
||||
$phid,
|
||||
);
|
||||
|
||||
$rows[] = array(
|
||||
pht('PHID Type'),
|
||||
phid_get_type($phid),
|
||||
);
|
||||
|
||||
$rows[] = array(
|
||||
pht('URI'),
|
||||
$handle->getURI(),
|
||||
);
|
||||
|
||||
$icon = $handle->getIcon();
|
||||
if ($icon !== null) {
|
||||
$icon = id(new PHUIIconView())
|
||||
->setIcon($handle->getIcon());
|
||||
}
|
||||
|
||||
$rows[] = array(
|
||||
pht('Icon'),
|
||||
$icon,
|
||||
);
|
||||
|
||||
$rows[] = array(
|
||||
pht('Object Name'),
|
||||
$handle->getObjectName(),
|
||||
);
|
||||
|
||||
$rows[] = array(
|
||||
pht('Name'),
|
||||
$handle->getName(),
|
||||
);
|
||||
|
||||
$rows[] = array(
|
||||
pht('Full Name'),
|
||||
$handle->getFullName(),
|
||||
);
|
||||
|
||||
$rows[] = array(
|
||||
pht('Tag'),
|
||||
$handle->renderTag(),
|
||||
);
|
||||
|
||||
$rows[] = array(
|
||||
pht('Link'),
|
||||
$handle->renderLink(),
|
||||
);
|
||||
|
||||
$table = id(new AphrontTableView($rows))
|
||||
->setColumnClasses(
|
||||
array(
|
||||
'header',
|
||||
'wide',
|
||||
));
|
||||
|
||||
return $this->newDialog()
|
||||
->setTitle(pht('Handle: %s', $phid))
|
||||
->setWidth(AphrontDialogView::WIDTH_FORM)
|
||||
->appendChild($table)
|
||||
->addCancelButton($cancel_uri, pht('Done'));
|
||||
}
|
||||
|
||||
}
|
|
@ -14,6 +14,12 @@ final class PhabricatorSystemApplication extends PhabricatorApplication {
|
|||
return true;
|
||||
}
|
||||
|
||||
public function getEventListeners() {
|
||||
return array(
|
||||
new PhabricatorSystemDebugUIEventListener(),
|
||||
);
|
||||
}
|
||||
|
||||
public function getRoutes() {
|
||||
return array(
|
||||
'/status/' => 'PhabricatorStatusController',
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorSystemDebugUIEventListener
|
||||
extends PhabricatorEventListener {
|
||||
|
||||
public function register() {
|
||||
$this->listen(PhabricatorEventType::TYPE_UI_DIDRENDERACTIONS);
|
||||
}
|
||||
|
||||
public function handleEvent(PhutilEvent $event) {
|
||||
switch ($event->getType()) {
|
||||
case PhabricatorEventType::TYPE_UI_DIDRENDERACTIONS:
|
||||
$this->handleActionEvent($event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private function handleActionEvent($event) {
|
||||
$viewer = $event->getUser();
|
||||
$object = $event->getValue('object');
|
||||
|
||||
if (!PhabricatorEnv::getEnvConfig('phabricator.developer-mode')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$object || !$object->getPHID()) {
|
||||
// If we have no object, or the object doesn't have a PHID, we can't
|
||||
// do anything useful.
|
||||
return;
|
||||
}
|
||||
|
||||
$phid = $object->getPHID();
|
||||
|
||||
$submenu = array();
|
||||
|
||||
$submenu[] = id(new PhabricatorActionView())
|
||||
->setIcon('fa-asterisk')
|
||||
->setName(pht('View Handle'))
|
||||
->setHref(urisprintf('/search/handle/%s/', $phid))
|
||||
->setWorkflow(true);
|
||||
|
||||
$submenu[] = id(new PhabricatorActionView())
|
||||
->setIcon('fa-address-card-o')
|
||||
->setName(pht('View Hovercard'))
|
||||
->setHref(urisprintf('/search/hovercard/?phids[]=%s', $phid));
|
||||
|
||||
$developer_action = id(new PhabricatorActionView())
|
||||
->setName(pht('Advanced/Developer...'))
|
||||
->setIcon('fa-magic')
|
||||
->setOrder(9001)
|
||||
->setSubmenu($submenu);
|
||||
|
||||
$actions = $event->getValue('actions');
|
||||
$actions[] = $developer_action;
|
||||
$event->setValue('actions', $actions);
|
||||
}
|
||||
|
||||
}
|
|
@ -1133,6 +1133,8 @@ abstract class PhabricatorApplicationTransaction
|
|||
} else {
|
||||
$fragments = array();
|
||||
foreach ($moves as $move) {
|
||||
$to_column = $move['columnPHID'];
|
||||
$board_phid = $move['boardPHID'];
|
||||
$fragments[] = pht(
|
||||
'%s (%s)',
|
||||
$this->renderHandleLink($board_phid),
|
||||
|
|
|
@ -52,6 +52,14 @@ final class PhabricatorActionListView extends AphrontTagView {
|
|||
$action->setViewer($viewer);
|
||||
}
|
||||
|
||||
$sort = array();
|
||||
foreach ($actions as $key => $action) {
|
||||
$sort[$key] = id(new PhutilSortVector())
|
||||
->addInt($action->getOrder());
|
||||
}
|
||||
$sort = msortv($sort, 'getSelf');
|
||||
$actions = array_select_keys($actions, array_keys($sort));
|
||||
|
||||
require_celerity_resource('phabricator-action-list-view-css');
|
||||
|
||||
$items = array();
|
||||
|
|
|
@ -379,10 +379,11 @@ final class PHUIObjectItemView extends AphrontTagView {
|
|||
|
||||
if ($this->objectName) {
|
||||
$header_name[] = array(
|
||||
phutil_tag(
|
||||
javelin_tag(
|
||||
'span',
|
||||
array(
|
||||
'class' => 'phui-oi-objname',
|
||||
'sigil' => 'ungrabbable',
|
||||
),
|
||||
$this->objectName),
|
||||
' ',
|
||||
|
|
|
@ -181,6 +181,15 @@ JX.install('DraggableList', {
|
|||
return;
|
||||
}
|
||||
|
||||
// See T13452. If this is an ungrabble part of the item, don't start a
|
||||
// drag. We use this to allow users to select text on cards.
|
||||
var target = e.getTarget();
|
||||
if (target) {
|
||||
if (JX.Stratcom.hasSigil(target, 'ungrabbable')) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (JX.Stratcom.pass()) {
|
||||
// Let other handlers deal with this event before we do.
|
||||
return;
|
||||
|
|
Loading…
Add table
Reference in a new issue