mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-24 15:52:41 +01:00
(stable) Promote 2016 Week 26
This commit is contained in:
commit
cadac75b82
95 changed files with 2285 additions and 526 deletions
|
@ -7,18 +7,18 @@
|
|||
*/
|
||||
return array(
|
||||
'names' => array(
|
||||
'core.pkg.css' => 'c7fc5aec',
|
||||
'core.pkg.js' => '10275c16',
|
||||
'core.pkg.css' => 'b6b40555',
|
||||
'core.pkg.js' => 'f2139810',
|
||||
'darkconsole.pkg.js' => 'e7393ebb',
|
||||
'differential.pkg.css' => 'b3eea3f5',
|
||||
'differential.pkg.js' => '4b7d8f19',
|
||||
'differential.pkg.js' => '01a010d6',
|
||||
'diffusion.pkg.css' => '91c5d3a6',
|
||||
'diffusion.pkg.js' => '3a9a8bfa',
|
||||
'maniphest.pkg.css' => '4845691a',
|
||||
'maniphest.pkg.js' => '949a7498',
|
||||
'rsrc/css/aphront/aphront-bars.css' => '231ac33c',
|
||||
'rsrc/css/aphront/dark-console.css' => 'f54bf286',
|
||||
'rsrc/css/aphront/dialog-view.css' => 'b4334e08',
|
||||
'rsrc/css/aphront/dialog-view.css' => '913c172e',
|
||||
'rsrc/css/aphront/lightbox-attachment.css' => '7acac05d',
|
||||
'rsrc/css/aphront/list-filter-view.css' => '5d6f0526',
|
||||
'rsrc/css/aphront/multi-column.css' => 'fd18389d',
|
||||
|
@ -28,7 +28,7 @@ return array(
|
|||
'rsrc/css/aphront/table-view.css' => '9258e19f',
|
||||
'rsrc/css/aphront/tokenizer.css' => '056da01b',
|
||||
'rsrc/css/aphront/tooltip.css' => '1a07aea8',
|
||||
'rsrc/css/aphront/typeahead-browse.css' => 'd8581d2c',
|
||||
'rsrc/css/aphront/typeahead-browse.css' => '8904346a',
|
||||
'rsrc/css/aphront/typeahead.css' => 'd4f16145',
|
||||
'rsrc/css/application/almanac/almanac.css' => 'dbb9b3af',
|
||||
'rsrc/css/application/auth/auth.css' => '0877ed6e',
|
||||
|
@ -124,7 +124,7 @@ return array(
|
|||
'rsrc/css/phui/phui-badge.css' => '3baef8db',
|
||||
'rsrc/css/phui/phui-big-info-view.css' => 'bd903741',
|
||||
'rsrc/css/phui/phui-box.css' => '5c8387cf',
|
||||
'rsrc/css/phui/phui-button.css' => 'a64a8de6',
|
||||
'rsrc/css/phui/phui-button.css' => '4a5fbe3d',
|
||||
'rsrc/css/phui/phui-chart.css' => '6bf6f78e',
|
||||
'rsrc/css/phui/phui-crumbs-view.css' => '6b813619',
|
||||
'rsrc/css/phui/phui-curtain-view.css' => '7148ae25',
|
||||
|
@ -149,13 +149,13 @@ return array(
|
|||
'rsrc/css/phui/phui-pager.css' => 'bea33d23',
|
||||
'rsrc/css/phui/phui-pinboard-view.css' => '2495140e',
|
||||
'rsrc/css/phui/phui-profile-menu.css' => 'c8557f33',
|
||||
'rsrc/css/phui/phui-property-list-view.css' => 'd4bbd0cb',
|
||||
'rsrc/css/phui/phui-property-list-view.css' => '6d8e58ac',
|
||||
'rsrc/css/phui/phui-remarkup-preview.css' => '1a8f2591',
|
||||
'rsrc/css/phui/phui-segment-bar-view.css' => '46342871',
|
||||
'rsrc/css/phui/phui-spacing.css' => '042804d6',
|
||||
'rsrc/css/phui/phui-status.css' => 'd5263e49',
|
||||
'rsrc/css/phui/phui-tag-view.css' => '6bbd83e2',
|
||||
'rsrc/css/phui/phui-timeline-view.css' => '8ea41b25',
|
||||
'rsrc/css/phui/phui-timeline-view.css' => 'c3782437',
|
||||
'rsrc/css/phui/phui-two-column-view.css' => '9fb86c85',
|
||||
'rsrc/css/phui/workboards/phui-workboard-color.css' => 'ac6fe6a7',
|
||||
'rsrc/css/phui/workboards/phui-workboard.css' => 'e6d89647',
|
||||
|
@ -245,7 +245,7 @@ return array(
|
|||
'rsrc/externals/javelin/lib/URI.js' => 'c989ade3',
|
||||
'rsrc/externals/javelin/lib/Vector.js' => '2caa8fb8',
|
||||
'rsrc/externals/javelin/lib/WebSocket.js' => 'e292eaf4',
|
||||
'rsrc/externals/javelin/lib/Workflow.js' => '0eb34d1d',
|
||||
'rsrc/externals/javelin/lib/Workflow.js' => '1e911d0f',
|
||||
'rsrc/externals/javelin/lib/__tests__/Cookie.js' => '5ed109e8',
|
||||
'rsrc/externals/javelin/lib/__tests__/DOM.js' => 'c984504b',
|
||||
'rsrc/externals/javelin/lib/__tests__/JSON.js' => '837a7d68',
|
||||
|
@ -339,6 +339,7 @@ return array(
|
|||
'rsrc/image/people/washington.png' => '40dd301c',
|
||||
'rsrc/image/phrequent_active.png' => 'a466a8ed',
|
||||
'rsrc/image/phrequent_inactive.png' => 'bfc15a69',
|
||||
'rsrc/image/resize.png' => 'fd476de4',
|
||||
'rsrc/image/sprite-login-X2.png' => 'e3991e37',
|
||||
'rsrc/image/sprite-login.png' => '03d5af29',
|
||||
'rsrc/image/sprite-menu-X2.png' => 'cfd8fca5',
|
||||
|
@ -494,7 +495,7 @@ return array(
|
|||
'rsrc/js/core/behavior-lightbox-attachments.js' => 'f8ba29d7',
|
||||
'rsrc/js/core/behavior-line-linker.js' => '1499a8cb',
|
||||
'rsrc/js/core/behavior-more.js' => 'a80d0378',
|
||||
'rsrc/js/core/behavior-object-selector.js' => '49b73b36',
|
||||
'rsrc/js/core/behavior-object-selector.js' => '9030ebef',
|
||||
'rsrc/js/core/behavior-oncopy.js' => '2926fff2',
|
||||
'rsrc/js/core/behavior-phabricator-nav.js' => '56a1ca03',
|
||||
'rsrc/js/core/behavior-phabricator-remarkup-assist.js' => '116cf19b',
|
||||
|
@ -515,14 +516,15 @@ return array(
|
|||
'rsrc/js/core/behavior-watch-anchor.js' => '9f36c42d',
|
||||
'rsrc/js/core/behavior-workflow.js' => '0a3f3021',
|
||||
'rsrc/js/core/phtize.js' => 'd254d646',
|
||||
'rsrc/js/phui/behavior-phui-dropdown-menu.js' => '54733475',
|
||||
'rsrc/js/phui/behavior-phui-dropdown-menu.js' => '1aa4c968',
|
||||
'rsrc/js/phui/behavior-phui-file-upload.js' => 'b003d4fb',
|
||||
'rsrc/js/phui/behavior-phui-object-box-tabs.js' => '2bfa2836',
|
||||
'rsrc/js/phui/behavior-phui-profile-menu.js' => '12884df9',
|
||||
'rsrc/js/phui/behavior-phui-submenu.js' => 'a6f7a73b',
|
||||
'rsrc/js/phuix/PHUIXActionListView.js' => 'b5c256b8',
|
||||
'rsrc/js/phuix/PHUIXActionView.js' => '8cf6d262',
|
||||
'rsrc/js/phuix/PHUIXAutocomplete.js' => '9196fb06',
|
||||
'rsrc/js/phuix/PHUIXDropdownMenu.js' => 'bd4c8dca',
|
||||
'rsrc/js/phuix/PHUIXDropdownMenu.js' => '82e270da',
|
||||
'rsrc/js/phuix/PHUIXFormControl.js' => 'e15869a8',
|
||||
'rsrc/js/phuix/PHUIXIconView.js' => 'bff6884b',
|
||||
),
|
||||
|
@ -530,7 +532,7 @@ return array(
|
|||
'almanac-css' => 'dbb9b3af',
|
||||
'aphront-bars' => '231ac33c',
|
||||
'aphront-dark-console-css' => 'f54bf286',
|
||||
'aphront-dialog-view-css' => 'b4334e08',
|
||||
'aphront-dialog-view-css' => '913c172e',
|
||||
'aphront-list-filter-view-css' => '5d6f0526',
|
||||
'aphront-multi-column-view-css' => 'fd18389d',
|
||||
'aphront-panel-view-css' => '8427b78d',
|
||||
|
@ -656,7 +658,7 @@ return array(
|
|||
'javelin-behavior-phabricator-line-linker' => '1499a8cb',
|
||||
'javelin-behavior-phabricator-nav' => '56a1ca03',
|
||||
'javelin-behavior-phabricator-notification-example' => '8ce821c5',
|
||||
'javelin-behavior-phabricator-object-selector' => '49b73b36',
|
||||
'javelin-behavior-phabricator-object-selector' => '9030ebef',
|
||||
'javelin-behavior-phabricator-oncopy' => '2926fff2',
|
||||
'javelin-behavior-phabricator-remarkup-assist' => '116cf19b',
|
||||
'javelin-behavior-phabricator-reveal-content' => '60821bc7',
|
||||
|
@ -668,11 +670,12 @@ return array(
|
|||
'javelin-behavior-phabricator-watch-anchor' => '9f36c42d',
|
||||
'javelin-behavior-pholio-mock-edit' => 'bee502c8',
|
||||
'javelin-behavior-pholio-mock-view' => 'fbe497e7',
|
||||
'javelin-behavior-phui-dropdown-menu' => '54733475',
|
||||
'javelin-behavior-phui-dropdown-menu' => '1aa4c968',
|
||||
'javelin-behavior-phui-file-upload' => 'b003d4fb',
|
||||
'javelin-behavior-phui-hovercards' => 'bcaccd64',
|
||||
'javelin-behavior-phui-object-box-tabs' => '2bfa2836',
|
||||
'javelin-behavior-phui-profile-menu' => '12884df9',
|
||||
'javelin-behavior-phui-submenu' => 'a6f7a73b',
|
||||
'javelin-behavior-policy-control' => 'd0c516d5',
|
||||
'javelin-behavior-policy-rule-editor' => '5e9f347c',
|
||||
'javelin-behavior-project-boards' => '14a1faae',
|
||||
|
@ -749,7 +752,7 @@ return array(
|
|||
'javelin-workboard-card' => 'c587b80f',
|
||||
'javelin-workboard-column' => 'bae58312',
|
||||
'javelin-workboard-controller' => '55baf5ed',
|
||||
'javelin-workflow' => '0eb34d1d',
|
||||
'javelin-workflow' => '1e911d0f',
|
||||
'lightbox-attachment-css' => '7acac05d',
|
||||
'maniphest-batch-editor' => 'b0f0b6d5',
|
||||
'maniphest-report-css' => '9b9580b7',
|
||||
|
@ -821,7 +824,7 @@ return array(
|
|||
'phui-badge-view-css' => '3baef8db',
|
||||
'phui-big-info-view-css' => 'bd903741',
|
||||
'phui-box-css' => '5c8387cf',
|
||||
'phui-button-css' => 'a64a8de6',
|
||||
'phui-button-css' => '4a5fbe3d',
|
||||
'phui-calendar-css' => 'ccabe893',
|
||||
'phui-calendar-day-css' => 'd1cf6f93',
|
||||
'phui-calendar-list-css' => '56e6381a',
|
||||
|
@ -853,14 +856,14 @@ return array(
|
|||
'phui-pager-css' => 'bea33d23',
|
||||
'phui-pinboard-view-css' => '2495140e',
|
||||
'phui-profile-menu-css' => 'c8557f33',
|
||||
'phui-property-list-view-css' => 'd4bbd0cb',
|
||||
'phui-property-list-view-css' => '6d8e58ac',
|
||||
'phui-remarkup-preview-css' => '1a8f2591',
|
||||
'phui-segment-bar-view-css' => '46342871',
|
||||
'phui-spacing-css' => '042804d6',
|
||||
'phui-status-list-view-css' => 'd5263e49',
|
||||
'phui-tag-view-css' => '6bbd83e2',
|
||||
'phui-theme-css' => '027ba77e',
|
||||
'phui-timeline-view-css' => '8ea41b25',
|
||||
'phui-timeline-view-css' => 'c3782437',
|
||||
'phui-two-column-view-css' => '9fb86c85',
|
||||
'phui-workboard-color-css' => 'ac6fe6a7',
|
||||
'phui-workboard-view-css' => 'e6d89647',
|
||||
|
@ -869,7 +872,7 @@ return array(
|
|||
'phuix-action-list-view' => 'b5c256b8',
|
||||
'phuix-action-view' => '8cf6d262',
|
||||
'phuix-autocomplete' => '9196fb06',
|
||||
'phuix-dropdown-menu' => 'bd4c8dca',
|
||||
'phuix-dropdown-menu' => '82e270da',
|
||||
'phuix-form-control-view' => 'e15869a8',
|
||||
'phuix-icon-view' => 'bff6884b',
|
||||
'policy-css' => '957ea14c',
|
||||
|
@ -889,7 +892,7 @@ return array(
|
|||
'syntax-default-css' => '9923583c',
|
||||
'syntax-highlighting-css' => '9fc496d5',
|
||||
'tokens-css' => '3d0f239e',
|
||||
'typeahead-browse-css' => 'd8581d2c',
|
||||
'typeahead-browse-css' => '8904346a',
|
||||
'unhandled-exception-css' => '4c96257a',
|
||||
),
|
||||
'requires' => array(
|
||||
|
@ -981,17 +984,6 @@ return array(
|
|||
'javelin-dom',
|
||||
'javelin-router',
|
||||
),
|
||||
'0eb34d1d' => array(
|
||||
'javelin-stratcom',
|
||||
'javelin-request',
|
||||
'javelin-dom',
|
||||
'javelin-vector',
|
||||
'javelin-install',
|
||||
'javelin-util',
|
||||
'javelin-mask',
|
||||
'javelin-uri',
|
||||
'javelin-routable',
|
||||
),
|
||||
'0f764c35' => array(
|
||||
'javelin-install',
|
||||
'javelin-util',
|
||||
|
@ -1034,6 +1026,12 @@ return array(
|
|||
'javelin-workflow',
|
||||
'javelin-workboard-controller',
|
||||
),
|
||||
'1aa4c968' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-stratcom',
|
||||
'javelin-dom',
|
||||
'phuix-dropdown-menu',
|
||||
),
|
||||
'1ad0a787' => array(
|
||||
'javelin-install',
|
||||
'javelin-reactor',
|
||||
|
@ -1071,6 +1069,17 @@ return array(
|
|||
'javelin-dom',
|
||||
'javelin-reactor-dom',
|
||||
),
|
||||
'1e911d0f' => array(
|
||||
'javelin-stratcom',
|
||||
'javelin-request',
|
||||
'javelin-dom',
|
||||
'javelin-vector',
|
||||
'javelin-install',
|
||||
'javelin-util',
|
||||
'javelin-mask',
|
||||
'javelin-uri',
|
||||
'javelin-routable',
|
||||
),
|
||||
'21ba5861' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
|
@ -1205,12 +1214,6 @@ return array(
|
|||
'javelin-uri',
|
||||
'phabricator-notification',
|
||||
),
|
||||
'49b73b36' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
'javelin-request',
|
||||
'javelin-util',
|
||||
),
|
||||
'4b700e9e' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
|
@ -1274,12 +1277,6 @@ return array(
|
|||
'javelin-leader',
|
||||
'javelin-json',
|
||||
),
|
||||
54733475 => array(
|
||||
'javelin-behavior',
|
||||
'javelin-stratcom',
|
||||
'javelin-dom',
|
||||
'phuix-dropdown-menu',
|
||||
),
|
||||
'54b612ba' => array(
|
||||
'javelin-color',
|
||||
'javelin-install',
|
||||
|
@ -1528,6 +1525,13 @@ return array(
|
|||
'javelin-vector',
|
||||
'javelin-stratcom',
|
||||
),
|
||||
'82e270da' => array(
|
||||
'javelin-install',
|
||||
'javelin-util',
|
||||
'javelin-dom',
|
||||
'javelin-vector',
|
||||
'javelin-stratcom',
|
||||
),
|
||||
'834a1173' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-scrollbar',
|
||||
|
@ -1598,6 +1602,12 @@ return array(
|
|||
'javelin-dom',
|
||||
'javelin-request',
|
||||
),
|
||||
'9030ebef' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
'javelin-request',
|
||||
'javelin-util',
|
||||
),
|
||||
'9196fb06' => array(
|
||||
'javelin-install',
|
||||
'javelin-dom',
|
||||
|
@ -1692,6 +1702,11 @@ return array(
|
|||
'javelin-uri',
|
||||
'phabricator-notification',
|
||||
),
|
||||
'a6f7a73b' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-stratcom',
|
||||
'javelin-dom',
|
||||
),
|
||||
'a80d0378' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-stratcom',
|
||||
|
@ -1845,13 +1860,6 @@ return array(
|
|||
'javelin-vector',
|
||||
'phui-hovercard',
|
||||
),
|
||||
'bd4c8dca' => array(
|
||||
'javelin-install',
|
||||
'javelin-util',
|
||||
'javelin-dom',
|
||||
'javelin-vector',
|
||||
'javelin-stratcom',
|
||||
),
|
||||
'bdaf4d04' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_phame.phame_blog
|
||||
ADD parentDomain VARCHAR(128) NOT NULL COLLATE {$COLLATE_TEXT};
|
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_phame.phame_blog
|
||||
ADD parentSite VARCHAR(128) NOT NULL COLLATE {$COLLATE_TEXT};
|
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_phame.phame_blog
|
||||
ADD domainFullURI VARCHAR(128) COLLATE {$COLLATE_TEXT};
|
|
@ -0,0 +1,3 @@
|
|||
UPDATE {$NAMESPACE}_phame.phame_blog
|
||||
SET domainFullURI = CONCAT('http://', domain, '/')
|
||||
WHERE domain IS NOT NULL;
|
|
@ -0,0 +1,3 @@
|
|||
UPDATE {$NAMESPACE}_phame.phame_blogtransaction
|
||||
SET transactionType = 'phame.blog.full.domain'
|
||||
WHERE transactionType = 'phame.blog.domain';
|
File diff suppressed because one or more lines are too long
|
@ -54,10 +54,62 @@ if (!$repository->isHosted()) {
|
|||
|
||||
$engine->setRepository($repository);
|
||||
|
||||
$args = new PhutilArgumentParser($argv);
|
||||
$args->parsePartial(
|
||||
array(
|
||||
array(
|
||||
'name' => 'hook-mode',
|
||||
'param' => 'mode',
|
||||
'help' => pht('Hook execution mode.'),
|
||||
),
|
||||
));
|
||||
|
||||
$argv = array_merge(
|
||||
array($argv[0]),
|
||||
$args->getUnconsumedArgumentVector());
|
||||
|
||||
// Figure out which user is writing the commit.
|
||||
$hook_mode = $args->getArg('hook-mode');
|
||||
if ($hook_mode !== null) {
|
||||
$known_modes = array(
|
||||
'svn-revprop' => true,
|
||||
);
|
||||
|
||||
if ($repository->isGit() || $repository->isHg()) {
|
||||
if (empty($known_modes[$hook_mode])) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Invalid Hook Mode: This hook was invoked in "%s" mode, but this '.
|
||||
'is not a recognized hook mode. Valid modes are: %s.',
|
||||
$hook_mode,
|
||||
implode(', ', array_keys($known_modes))));
|
||||
}
|
||||
}
|
||||
|
||||
$is_svnrevprop = ($hook_mode == 'svn-revprop');
|
||||
|
||||
if ($is_svnrevprop) {
|
||||
// For now, we let these through if the repository allows dangerous changes
|
||||
// and prevent them if it doesn't. See T11208 for discussion.
|
||||
|
||||
$revprop_key = $argv[5];
|
||||
|
||||
if ($repository->shouldAllowDangerousChanges()) {
|
||||
$err = 0;
|
||||
} else {
|
||||
$err = 1;
|
||||
|
||||
$console = PhutilConsole::getConsole();
|
||||
$console->writeErr(
|
||||
pht(
|
||||
"DANGEROUS CHANGE: Dangerous change protection is enabled for this ".
|
||||
"repository, so you can not change revision properties (you are ".
|
||||
"attempting to edit \"%s\").\n".
|
||||
"Edit the repository configuration before making dangerous changes.",
|
||||
$revprop_key));
|
||||
}
|
||||
|
||||
exit($err);
|
||||
} else if ($repository->isGit() || $repository->isHg()) {
|
||||
$username = getenv(DiffusionCommitHookEngine::ENV_USER);
|
||||
if (!strlen($username)) {
|
||||
throw new Exception(
|
||||
|
|
|
@ -88,7 +88,22 @@ if ($as_device) {
|
|||
$arguments[] = AlmanacKeys::getKeyPath('device.key');
|
||||
}
|
||||
|
||||
// Subversion passes us a host in the form "domain.com:port", which is not
|
||||
// valid for normal SSH but which we can parse into a valid "-p" flag.
|
||||
|
||||
$passthru_args = $unconsumed_argv;
|
||||
$host = array_shift($passthru_args);
|
||||
$parts = explode(':', $host, 2);
|
||||
$host = $parts[0];
|
||||
|
||||
$port = $args->getArg('port');
|
||||
|
||||
if (!$port) {
|
||||
if (count($parts) == 2) {
|
||||
$port = $parts[1];
|
||||
}
|
||||
}
|
||||
|
||||
if ($port) {
|
||||
$pattern[] = '-p %d';
|
||||
$arguments[] = $port;
|
||||
|
@ -96,7 +111,9 @@ if ($port) {
|
|||
|
||||
$pattern[] = '--';
|
||||
|
||||
$passthru_args = $unconsumed_argv;
|
||||
$pattern[] = '%s';
|
||||
$arguments[] = $host;
|
||||
|
||||
foreach ($passthru_args as $passthru_arg) {
|
||||
$pattern[] = '%s';
|
||||
$arguments[] = $passthru_arg;
|
||||
|
|
|
@ -190,12 +190,14 @@ try {
|
|||
'P' => $user->getPHID(),
|
||||
));
|
||||
|
||||
if (!$user->canEstablishSSHSessions()) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Your account ("%s") does not have permission to establish SSH '.
|
||||
'sessions. Visit the web interface for more information.',
|
||||
$user_name));
|
||||
if (!$device) {
|
||||
if (!$user->canEstablishSSHSessions()) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Your account ("%s") does not have permission to establish SSH '.
|
||||
'sessions. Visit the web interface for more information.',
|
||||
$user_name));
|
||||
}
|
||||
}
|
||||
|
||||
$workflows = id(new PhutilClassMapQuery())
|
||||
|
|
|
@ -350,7 +350,6 @@ phutil_register_library_map(array(
|
|||
'DefaultDatabaseConfigurationProvider' => 'infrastructure/storage/configuration/DefaultDatabaseConfigurationProvider.php',
|
||||
'DifferentialAction' => 'applications/differential/constants/DifferentialAction.php',
|
||||
'DifferentialActionEmailCommand' => 'applications/differential/command/DifferentialActionEmailCommand.php',
|
||||
'DifferentialActionMenuEventListener' => 'applications/differential/event/DifferentialActionMenuEventListener.php',
|
||||
'DifferentialAddCommentView' => 'applications/differential/view/DifferentialAddCommentView.php',
|
||||
'DifferentialAdjustmentMapTestCase' => 'applications/differential/storage/__tests__/DifferentialAdjustmentMapTestCase.php',
|
||||
'DifferentialAffectedPath' => 'applications/differential/storage/DifferentialAffectedPath.php',
|
||||
|
@ -1420,8 +1419,13 @@ phutil_register_library_map(array(
|
|||
'ManiphestTaskEditController' => 'applications/maniphest/controller/ManiphestTaskEditController.php',
|
||||
'ManiphestTaskFulltextEngine' => 'applications/maniphest/search/ManiphestTaskFulltextEngine.php',
|
||||
'ManiphestTaskHasCommitEdgeType' => 'applications/maniphest/edge/ManiphestTaskHasCommitEdgeType.php',
|
||||
'ManiphestTaskHasCommitRelationship' => 'applications/maniphest/relationship/ManiphestTaskHasCommitRelationship.php',
|
||||
'ManiphestTaskHasMockEdgeType' => 'applications/maniphest/edge/ManiphestTaskHasMockEdgeType.php',
|
||||
'ManiphestTaskHasMockRelationship' => 'applications/maniphest/relationship/ManiphestTaskHasMockRelationship.php',
|
||||
'ManiphestTaskHasParentRelationship' => 'applications/maniphest/relationship/ManiphestTaskHasParentRelationship.php',
|
||||
'ManiphestTaskHasRevisionEdgeType' => 'applications/maniphest/edge/ManiphestTaskHasRevisionEdgeType.php',
|
||||
'ManiphestTaskHasRevisionRelationship' => 'applications/maniphest/relationship/ManiphestTaskHasRevisionRelationship.php',
|
||||
'ManiphestTaskHasSubtaskRelationship' => 'applications/maniphest/relationship/ManiphestTaskHasSubtaskRelationship.php',
|
||||
'ManiphestTaskHeraldField' => 'applications/maniphest/herald/ManiphestTaskHeraldField.php',
|
||||
'ManiphestTaskHeraldFieldGroup' => 'applications/maniphest/herald/ManiphestTaskHeraldFieldGroup.php',
|
||||
'ManiphestTaskListController' => 'applications/maniphest/controller/ManiphestTaskListController.php',
|
||||
|
@ -1437,6 +1441,7 @@ phutil_register_library_map(array(
|
|||
'ManiphestTaskPriorityHeraldAction' => 'applications/maniphest/herald/ManiphestTaskPriorityHeraldAction.php',
|
||||
'ManiphestTaskPriorityHeraldField' => 'applications/maniphest/herald/ManiphestTaskPriorityHeraldField.php',
|
||||
'ManiphestTaskQuery' => 'applications/maniphest/query/ManiphestTaskQuery.php',
|
||||
'ManiphestTaskRelationship' => 'applications/maniphest/relationship/ManiphestTaskRelationship.php',
|
||||
'ManiphestTaskResultListView' => 'applications/maniphest/view/ManiphestTaskResultListView.php',
|
||||
'ManiphestTaskSearchEngine' => 'applications/maniphest/query/ManiphestTaskSearchEngine.php',
|
||||
'ManiphestTaskStatus' => 'applications/maniphest/constants/ManiphestTaskStatus.php',
|
||||
|
@ -2860,6 +2865,8 @@ phutil_register_library_map(array(
|
|||
'PhabricatorObjectMentionedByObjectEdgeType' => 'applications/transactions/edges/PhabricatorObjectMentionedByObjectEdgeType.php',
|
||||
'PhabricatorObjectMentionsObjectEdgeType' => 'applications/transactions/edges/PhabricatorObjectMentionsObjectEdgeType.php',
|
||||
'PhabricatorObjectQuery' => 'applications/phid/query/PhabricatorObjectQuery.php',
|
||||
'PhabricatorObjectRelationship' => 'applications/search/relationship/PhabricatorObjectRelationship.php',
|
||||
'PhabricatorObjectRelationshipList' => 'applications/search/relationship/PhabricatorObjectRelationshipList.php',
|
||||
'PhabricatorObjectRemarkupRule' => 'infrastructure/markup/rule/PhabricatorObjectRemarkupRule.php',
|
||||
'PhabricatorObjectSelectorDialog' => 'view/control/PhabricatorObjectSelectorDialog.php',
|
||||
'PhabricatorOffsetPagedQuery' => 'infrastructure/query/PhabricatorOffsetPagedQuery.php',
|
||||
|
@ -3389,6 +3396,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorSearchOrderController' => 'applications/search/controller/PhabricatorSearchOrderController.php',
|
||||
'PhabricatorSearchOrderField' => 'applications/search/field/PhabricatorSearchOrderField.php',
|
||||
'PhabricatorSearchRelationship' => 'applications/search/constants/PhabricatorSearchRelationship.php',
|
||||
'PhabricatorSearchRelationshipController' => 'applications/search/controller/PhabricatorSearchRelationshipController.php',
|
||||
'PhabricatorSearchResultBucket' => 'applications/search/buckets/PhabricatorSearchResultBucket.php',
|
||||
'PhabricatorSearchResultBucketGroup' => 'applications/search/buckets/PhabricatorSearchResultBucketGroup.php',
|
||||
'PhabricatorSearchResultView' => 'applications/search/view/PhabricatorSearchResultView.php',
|
||||
|
@ -3834,7 +3842,6 @@ phutil_register_library_map(array(
|
|||
'PhluxVariablePHIDType' => 'applications/phlux/phid/PhluxVariablePHIDType.php',
|
||||
'PhluxVariableQuery' => 'applications/phlux/query/PhluxVariableQuery.php',
|
||||
'PhluxViewController' => 'applications/phlux/controller/PhluxViewController.php',
|
||||
'PholioActionMenuEventListener' => 'applications/pholio/event/PholioActionMenuEventListener.php',
|
||||
'PholioController' => 'applications/pholio/controller/PholioController.php',
|
||||
'PholioDAO' => 'applications/pholio/storage/PholioDAO.php',
|
||||
'PholioDefaultEditCapability' => 'applications/pholio/capability/PholioDefaultEditCapability.php',
|
||||
|
@ -4662,7 +4669,6 @@ phutil_register_library_map(array(
|
|||
),
|
||||
'DifferentialAction' => 'Phobject',
|
||||
'DifferentialActionEmailCommand' => 'MetaMTAEmailTransactionCommand',
|
||||
'DifferentialActionMenuEventListener' => 'PhabricatorEventListener',
|
||||
'DifferentialAddCommentView' => 'AphrontView',
|
||||
'DifferentialAdjustmentMapTestCase' => 'PhutilTestCase',
|
||||
'DifferentialAffectedPath' => 'DifferentialDAO',
|
||||
|
@ -5906,8 +5912,13 @@ phutil_register_library_map(array(
|
|||
'ManiphestTaskEditController' => 'ManiphestController',
|
||||
'ManiphestTaskFulltextEngine' => 'PhabricatorFulltextEngine',
|
||||
'ManiphestTaskHasCommitEdgeType' => 'PhabricatorEdgeType',
|
||||
'ManiphestTaskHasCommitRelationship' => 'ManiphestTaskRelationship',
|
||||
'ManiphestTaskHasMockEdgeType' => 'PhabricatorEdgeType',
|
||||
'ManiphestTaskHasMockRelationship' => 'ManiphestTaskRelationship',
|
||||
'ManiphestTaskHasParentRelationship' => 'ManiphestTaskRelationship',
|
||||
'ManiphestTaskHasRevisionEdgeType' => 'PhabricatorEdgeType',
|
||||
'ManiphestTaskHasRevisionRelationship' => 'ManiphestTaskRelationship',
|
||||
'ManiphestTaskHasSubtaskRelationship' => 'ManiphestTaskRelationship',
|
||||
'ManiphestTaskHeraldField' => 'HeraldField',
|
||||
'ManiphestTaskHeraldFieldGroup' => 'HeraldFieldGroup',
|
||||
'ManiphestTaskListController' => 'ManiphestController',
|
||||
|
@ -5923,6 +5934,7 @@ phutil_register_library_map(array(
|
|||
'ManiphestTaskPriorityHeraldAction' => 'HeraldAction',
|
||||
'ManiphestTaskPriorityHeraldField' => 'ManiphestTaskHeraldField',
|
||||
'ManiphestTaskQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'ManiphestTaskRelationship' => 'PhabricatorObjectRelationship',
|
||||
'ManiphestTaskResultListView' => 'ManiphestView',
|
||||
'ManiphestTaskSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
||||
'ManiphestTaskStatus' => 'ManiphestConstants',
|
||||
|
@ -7547,6 +7559,8 @@ phutil_register_library_map(array(
|
|||
'PhabricatorObjectMentionedByObjectEdgeType' => 'PhabricatorEdgeType',
|
||||
'PhabricatorObjectMentionsObjectEdgeType' => 'PhabricatorEdgeType',
|
||||
'PhabricatorObjectQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'PhabricatorObjectRelationship' => 'Phobject',
|
||||
'PhabricatorObjectRelationshipList' => 'Phobject',
|
||||
'PhabricatorObjectRemarkupRule' => 'PhutilRemarkupRule',
|
||||
'PhabricatorObjectSelectorDialog' => 'Phobject',
|
||||
'PhabricatorOffsetPagedQuery' => 'PhabricatorQuery',
|
||||
|
@ -8204,6 +8218,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorSearchOrderController' => 'PhabricatorSearchBaseController',
|
||||
'PhabricatorSearchOrderField' => 'PhabricatorSearchField',
|
||||
'PhabricatorSearchRelationship' => 'Phobject',
|
||||
'PhabricatorSearchRelationshipController' => 'PhabricatorSearchBaseController',
|
||||
'PhabricatorSearchResultBucket' => 'Phobject',
|
||||
'PhabricatorSearchResultBucketGroup' => 'Phobject',
|
||||
'PhabricatorSearchResultView' => 'AphrontView',
|
||||
|
@ -8730,7 +8745,6 @@ phutil_register_library_map(array(
|
|||
'PhluxVariablePHIDType' => 'PhabricatorPHIDType',
|
||||
'PhluxVariableQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'PhluxViewController' => 'PhluxController',
|
||||
'PholioActionMenuEventListener' => 'PhabricatorEventListener',
|
||||
'PholioController' => 'PhabricatorController',
|
||||
'PholioDAO' => 'PhabricatorLiskDAO',
|
||||
'PholioDefaultEditCapability' => 'PhabricatorPolicyCapability',
|
||||
|
|
|
@ -225,10 +225,10 @@ final class PhabricatorAuthStartController
|
|||
}
|
||||
|
||||
// Often, users end up here by clicking a disabled action link in the UI
|
||||
// (for example, they might click "Edit Blocking Tasks" on a Maniphest
|
||||
// task page). After they log in we want to send them back to that main
|
||||
// object page if we can, since it's confusing to end up on a standalone
|
||||
// page with only a dialog (particularly if that dialog is another error,
|
||||
// (for example, they might click "Edit Subtasks" on a Maniphest task
|
||||
// page). After they log in we want to send them back to that main object
|
||||
// page if we can, since it's confusing to end up on a standalone page with
|
||||
// only a dialog (particularly if that dialog is another error,
|
||||
// like a policy exception).
|
||||
|
||||
$via_header = AphrontRequest::getViaHeaderName();
|
||||
|
|
|
@ -47,7 +47,7 @@ final class PhabricatorBadgesEditor
|
|||
case PhabricatorBadgesTransaction::TYPE_ICON:
|
||||
return $object->getIcon();
|
||||
case PhabricatorBadgesTransaction::TYPE_QUALITY:
|
||||
return $object->getQuality();
|
||||
return (int)$object->getQuality();
|
||||
case PhabricatorBadgesTransaction::TYPE_STATUS:
|
||||
return $object->getStatus();
|
||||
case PhabricatorBadgesTransaction::TYPE_AWARD:
|
||||
|
|
|
@ -105,14 +105,14 @@ final class PhabricatorBadgesTransaction
|
|||
}
|
||||
break;
|
||||
case self::TYPE_QUALITY:
|
||||
$qual_new = PhabricatorBadgesQuality::getQualityName($new);
|
||||
$qual_old = PhabricatorBadgesQuality::getQualityName($old);
|
||||
if ($old === null) {
|
||||
return pht(
|
||||
'%s set the quality for this badge as "%s".',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$new);
|
||||
$qual_new);
|
||||
} else {
|
||||
$qual_new = PhabricatorBadgesQuality::getQualityName($new);
|
||||
$qual_old = PhabricatorBadgesQuality::getQualityName($old);
|
||||
return pht(
|
||||
'%s updated the quality for this badge from "%s" to "%s".',
|
||||
$this->renderHandleLink($author_phid),
|
||||
|
|
|
@ -474,8 +474,11 @@ abstract class PhabricatorController extends AphrontController {
|
|||
public function newCurtainView($object) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$action_id = celerity_generate_unique_node_id();
|
||||
|
||||
$action_list = id(new PhabricatorActionListView())
|
||||
->setViewer($viewer);
|
||||
->setViewer($viewer)
|
||||
->setID($action_id);
|
||||
|
||||
// NOTE: Applications (objects of class PhabricatorApplication) can't
|
||||
// currently be set here, although they don't need any of the extensions
|
||||
|
|
|
@ -13,6 +13,7 @@ final class CelerityStaticResourceResponse extends Phobject {
|
|||
private $packaged;
|
||||
private $metadata = array();
|
||||
private $metadataBlock = 0;
|
||||
private $metadataLocked;
|
||||
private $behaviors = array();
|
||||
private $hasRendered = array();
|
||||
private $postprocessorKey;
|
||||
|
@ -24,6 +25,13 @@ final class CelerityStaticResourceResponse extends Phobject {
|
|||
}
|
||||
|
||||
public function addMetadata($metadata) {
|
||||
if ($this->metadataLocked) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Attempting to add more metadata after metadata has been '.
|
||||
'locked.'));
|
||||
}
|
||||
|
||||
$id = count($this->metadata);
|
||||
$this->metadata[$id] = $metadata;
|
||||
return $this->metadataBlock.'_'.$id;
|
||||
|
@ -189,6 +197,8 @@ final class CelerityStaticResourceResponse extends Phobject {
|
|||
}
|
||||
|
||||
public function renderHTMLFooter() {
|
||||
$this->metadataLocked = true;
|
||||
|
||||
$data = array();
|
||||
if ($this->metadata) {
|
||||
$json_metadata = AphrontResponse::encodeJSONForHTTPResponse(
|
||||
|
|
|
@ -43,7 +43,6 @@ final class PhabricatorDifferentialApplication extends PhabricatorApplication {
|
|||
|
||||
public function getEventListeners() {
|
||||
return array(
|
||||
new DifferentialActionMenuEventListener(),
|
||||
new DifferentialLandingActionMenuEventListener(),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class DifferentialActionMenuEventListener
|
||||
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->handleActionsEvent($event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private function handleActionsEvent(PhutilEvent $event) {
|
||||
$object = $event->getValue('object');
|
||||
|
||||
$actions = null;
|
||||
if ($object instanceof ManiphestTask) {
|
||||
$actions = $this->renderTaskItems($event);
|
||||
$this->addActionMenuItems($event, $actions);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private function renderTaskItems(PhutilEvent $event) {
|
||||
if (!$this->canUseApplication($event->getUser())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$task = $event->getValue('object');
|
||||
$phid = $task->getPHID();
|
||||
|
||||
$can_edit = PhabricatorPolicyFilter::hasCapability(
|
||||
$event->getUser(),
|
||||
$task,
|
||||
PhabricatorPolicyCapability::CAN_EDIT);
|
||||
|
||||
return id(new PhabricatorActionView())
|
||||
->setName(pht('Edit Differential Revisions'))
|
||||
->setHref("/search/attach/{$phid}/DREV/")
|
||||
->setIcon('fa-cog')
|
||||
->setDisabled(!$can_edit)
|
||||
->setWorkflow(true);
|
||||
}
|
||||
|
||||
}
|
|
@ -18,25 +18,14 @@ final class DiffusionRepositoryEditDangerousController
|
|||
->getPanelURI();
|
||||
|
||||
if (!$repository->canAllowDangerousChanges()) {
|
||||
if ($repository->isSVN()) {
|
||||
return $this->newDialog()
|
||||
->setTitle(pht('Not in Danger'))
|
||||
->appendParagraph(
|
||||
pht(
|
||||
'It is not possible for users to push any dangerous changes '.
|
||||
'to a Subversion repository. Pushes to a Subversion repository '.
|
||||
'can always be reverted and never destroy data.'))
|
||||
->addCancelButton($panel_uri);
|
||||
} else {
|
||||
return $this->newDialog()
|
||||
->setTitle(pht('Unprotectable Repository'))
|
||||
->appendParagraph(
|
||||
pht(
|
||||
'This repository can not be protected from dangerous changes '.
|
||||
'because Phabricator does not control what users are allowed '.
|
||||
'to push to it.'))
|
||||
->addCancelButton($panel_uri);
|
||||
}
|
||||
return $this->newDialog()
|
||||
->setTitle(pht('Unprotectable Repository'))
|
||||
->appendParagraph(
|
||||
pht(
|
||||
'This repository can not be protected from dangerous changes '.
|
||||
'because Phabricator does not control what users are allowed '.
|
||||
'to push to it.'))
|
||||
->addCancelButton($panel_uri);
|
||||
}
|
||||
|
||||
if ($request->isFormPost()) {
|
||||
|
@ -57,18 +46,34 @@ final class DiffusionRepositoryEditDangerousController
|
|||
|
||||
if ($repository->shouldAllowDangerousChanges()) {
|
||||
$title = pht('Prevent Dangerous Changes');
|
||||
$body = pht(
|
||||
'It will no longer be possible to delete branches from this '.
|
||||
'repository, or %s push to this repository.',
|
||||
$force);
|
||||
|
||||
if ($repository->isSVN()) {
|
||||
$body = pht(
|
||||
'It will no longer be possible to edit revprops in this '.
|
||||
'repository.');
|
||||
} else {
|
||||
$body = pht(
|
||||
'It will no longer be possible to delete branches from this '.
|
||||
'repository, or %s push to this repository.',
|
||||
$force);
|
||||
}
|
||||
|
||||
$submit = pht('Prevent Dangerous Changes');
|
||||
} else {
|
||||
$title = pht('Allow Dangerous Changes');
|
||||
$body = pht(
|
||||
'If you allow dangerous changes, it will be possible to delete '.
|
||||
'branches and %s push this repository. These operations can '.
|
||||
'alter a repository in a way that is difficult to recover from.',
|
||||
$force);
|
||||
if ($repository->isSVN()) {
|
||||
$body = pht(
|
||||
'If you allow dangerous changes, it will be possible to edit '.
|
||||
'reprops in this repository, including arbitrarily rewriting '.
|
||||
'commit messages. These operations can alter a repository in a '.
|
||||
'way that is difficult to recover from.');
|
||||
} else {
|
||||
$body = pht(
|
||||
'If you allow dangerous changes, it will be possible to delete '.
|
||||
'branches and %s push this repository. These operations can '.
|
||||
'alter a repository in a way that is difficult to recover from.',
|
||||
$force);
|
||||
}
|
||||
$submit = pht('Allow Dangerous Changes');
|
||||
}
|
||||
|
||||
|
|
|
@ -31,24 +31,50 @@ final class DiffusionRepositoryDatasource
|
|||
"phabricator-search-icon phui-font-fa phui-icon-view {$type_icon}";
|
||||
|
||||
$results = array();
|
||||
foreach ($repos as $repo) {
|
||||
$display_name = $repo->getMonogram().' '.$repo->getName();
|
||||
foreach ($repos as $repository) {
|
||||
$monogram = $repository->getMonogram();
|
||||
$name = $repository->getName();
|
||||
|
||||
$name = $display_name;
|
||||
$slug = $repo->getRepositorySlug();
|
||||
$display_name = "{$monogram} {$name}";
|
||||
|
||||
$parts = array();
|
||||
$parts[] = $name;
|
||||
|
||||
$slug = $repository->getRepositorySlug();
|
||||
if (strlen($slug)) {
|
||||
$name = "{$name} {$slug}";
|
||||
$parts[] = $slug;
|
||||
}
|
||||
|
||||
$results[] = id(new PhabricatorTypeaheadResult())
|
||||
$callsign = $repository->getCallsign();
|
||||
if ($callsign) {
|
||||
$parts[] = $callsign;
|
||||
}
|
||||
|
||||
foreach ($repository->getAllMonograms() as $monogram) {
|
||||
$parts[] = $monogram;
|
||||
}
|
||||
|
||||
$name = implode(' ', $parts);
|
||||
|
||||
$vcs = $repository->getVersionControlSystem();
|
||||
$vcs_type = PhabricatorRepositoryType::getNameForRepositoryType($vcs);
|
||||
|
||||
$result = id(new PhabricatorTypeaheadResult())
|
||||
->setName($name)
|
||||
->setDisplayName($display_name)
|
||||
->setURI($repo->getURI())
|
||||
->setPHID($repo->getPHID())
|
||||
->setPriorityString($repo->getMonogram())
|
||||
->setURI($repository->getURI())
|
||||
->setPHID($repository->getPHID())
|
||||
->setPriorityString($repository->getMonogram())
|
||||
->setPriorityType('repo')
|
||||
->setImageSprite($image_sprite)
|
||||
->setDisplayType(pht('Repository'));
|
||||
->setDisplayType(pht('Repository'))
|
||||
->addAttribute($vcs_type);
|
||||
|
||||
if (!$repository->isTracked()) {
|
||||
$result->setClosed(pht('Inactive'));
|
||||
}
|
||||
|
||||
$results[] = $result;
|
||||
}
|
||||
|
||||
return $results;
|
||||
|
|
|
@ -43,7 +43,10 @@ final class DiffusionTaggedRepositoriesFunctionDatasource
|
|||
->setColor(null)
|
||||
->setPHID('tagged('.$result->getPHID().')')
|
||||
->setDisplayName(pht('Tagged: %s', $result->getDisplayName()))
|
||||
->setName('tagged '.$result->getName());
|
||||
->setName('tagged '.$result->getName())
|
||||
->resetAttributes()
|
||||
->addAttribute(pht('Function'))
|
||||
->addAttribute(pht('Select repositories tagged with this project.'));
|
||||
}
|
||||
|
||||
return $results;
|
||||
|
|
|
@ -7,6 +7,10 @@ final class DrydockBlueprintDatasource
|
|||
return pht('Type a blueprint name...');
|
||||
}
|
||||
|
||||
public function getBrowseTitle() {
|
||||
return pht('Browse Blueprints');
|
||||
}
|
||||
|
||||
public function getDatasourceApplicationClass() {
|
||||
return 'PhabricatorDrydockApplication';
|
||||
}
|
||||
|
@ -37,6 +41,9 @@ final class DrydockBlueprintDatasource
|
|||
$result->setClosed(pht('Disabled'));
|
||||
}
|
||||
|
||||
$result->addAttribute(
|
||||
$blueprint->getImplementation()->getBlueprintName());
|
||||
|
||||
$results[] = $result;
|
||||
}
|
||||
|
||||
|
|
|
@ -80,6 +80,8 @@ abstract class HeraldField extends Phobject {
|
|||
HeraldAdapter::CONDITION_NOT_CONTAINS,
|
||||
HeraldAdapter::CONDITION_REGEXP,
|
||||
HeraldAdapter::CONDITION_NOT_REGEXP,
|
||||
HeraldAdapter::CONDITION_EXISTS,
|
||||
HeraldAdapter::CONDITION_NOT_EXISTS,
|
||||
);
|
||||
case self::STANDARD_TEXT_MAP:
|
||||
return array(
|
||||
|
@ -107,7 +109,13 @@ abstract class HeraldField extends Phobject {
|
|||
case self::STANDARD_TEXT:
|
||||
case self::STANDARD_TEXT_LIST:
|
||||
case self::STANDARD_TEXT_MAP:
|
||||
return new HeraldTextFieldValue();
|
||||
switch ($condition) {
|
||||
case HeraldAdapter::CONDITION_EXISTS:
|
||||
case HeraldAdapter::CONDITION_NOT_EXISTS:
|
||||
return new HeraldEmptyFieldValue();
|
||||
default:
|
||||
return new HeraldTextFieldValue();
|
||||
}
|
||||
case self::STANDARD_PHID:
|
||||
case self::STANDARD_PHID_NULLABLE:
|
||||
case self::STANDARD_PHID_LIST:
|
||||
|
|
|
@ -166,15 +166,6 @@ final class ManiphestTaskDetailController extends ManiphestController {
|
|||
->setDisabled(!$can_edit)
|
||||
->setWorkflow(!$can_edit));
|
||||
|
||||
$curtain->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setName(pht('Merge Duplicates In'))
|
||||
->setHref("/search/attach/{$phid}/TASK/merge/")
|
||||
->setWorkflow(true)
|
||||
->setIcon('fa-compress')
|
||||
->setDisabled(!$can_edit)
|
||||
->setWorkflow(true));
|
||||
|
||||
$edit_config = $edit_engine->loadDefaultEditConfiguration();
|
||||
$can_create = (bool)$edit_config;
|
||||
|
||||
|
@ -195,23 +186,45 @@ final class ManiphestTaskDetailController extends ManiphestController {
|
|||
$edit_uri = $this->getApplicationURI($edit_uri);
|
||||
}
|
||||
|
||||
$curtain->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setName(pht('Create Subtask'))
|
||||
->setHref($edit_uri)
|
||||
->setIcon('fa-level-down')
|
||||
->setDisabled(!$can_create)
|
||||
->setWorkflow(!$can_create));
|
||||
$task_submenu = array();
|
||||
|
||||
$task_submenu[] = id(new PhabricatorActionView())
|
||||
->setName(pht('Create Subtask'))
|
||||
->setHref($edit_uri)
|
||||
->setIcon('fa-level-down')
|
||||
->setDisabled(!$can_create)
|
||||
->setWorkflow(!$can_create);
|
||||
|
||||
$relationship_list = PhabricatorObjectRelationshipList::newForObject(
|
||||
$viewer,
|
||||
$task);
|
||||
|
||||
$parent_key = ManiphestTaskHasParentRelationship::RELATIONSHIPKEY;
|
||||
$subtask_key = ManiphestTaskHasSubtaskRelationship::RELATIONSHIPKEY;
|
||||
|
||||
$task_submenu[] = $relationship_list->getRelationship($parent_key)
|
||||
->newAction($task);
|
||||
|
||||
$task_submenu[] = $relationship_list->getRelationship($subtask_key)
|
||||
->newAction($task);
|
||||
|
||||
$task_submenu[] = id(new PhabricatorActionView())
|
||||
->setName(pht('Merge Duplicates In'))
|
||||
->setHref("/search/attach/{$phid}/TASK/merge/")
|
||||
->setIcon('fa-compress')
|
||||
->setDisabled(!$can_edit)
|
||||
->setWorkflow(true);
|
||||
|
||||
$curtain->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setName(pht('Edit Blocking Tasks'))
|
||||
->setHref("/search/attach/{$phid}/TASK/blocks/")
|
||||
->setWorkflow(true)
|
||||
->setIcon('fa-link')
|
||||
->setDisabled(!$can_edit)
|
||||
->setWorkflow(true));
|
||||
->setName(pht('Edit Related Tasks...'))
|
||||
->setIcon('fa-anchor')
|
||||
->setSubmenu($task_submenu));
|
||||
|
||||
$relationship_submenu = $relationship_list->newActionMenu();
|
||||
if ($relationship_submenu) {
|
||||
$curtain->addAction($relationship_submenu);
|
||||
}
|
||||
|
||||
$owner_phid = $task->getOwnerPHID();
|
||||
$author_phid = $task->getAuthorPHID();
|
||||
|
@ -277,9 +290,9 @@ final class ManiphestTaskDetailController extends ManiphestController {
|
|||
|
||||
$edge_types = array(
|
||||
ManiphestTaskDependedOnByTaskEdgeType::EDGECONST
|
||||
=> pht('Blocks'),
|
||||
=> pht('Parent Tasks'),
|
||||
ManiphestTaskDependsOnTaskEdgeType::EDGECONST
|
||||
=> pht('Blocked By'),
|
||||
=> pht('Subtasks'),
|
||||
ManiphestTaskHasRevisionEdgeType::EDGECONST
|
||||
=> pht('Differential Revisions'),
|
||||
ManiphestTaskHasMockEdgeType::EDGECONST
|
||||
|
|
|
@ -17,7 +17,7 @@ final class ManiphestTaskDependedOnByTaskEdgeType extends PhabricatorEdgeType {
|
|||
$add_edges) {
|
||||
|
||||
return pht(
|
||||
'%s added %s blocked task(s): %s.',
|
||||
'%s added %s parent task(s): %s.',
|
||||
$actor,
|
||||
$add_count,
|
||||
$add_edges);
|
||||
|
@ -29,7 +29,7 @@ final class ManiphestTaskDependedOnByTaskEdgeType extends PhabricatorEdgeType {
|
|||
$rem_edges) {
|
||||
|
||||
return pht(
|
||||
'%s removed %s blocked task(s): %s.',
|
||||
'%s removed %s parent task(s): %s.',
|
||||
$actor,
|
||||
$rem_count,
|
||||
$rem_edges);
|
||||
|
@ -44,7 +44,7 @@ final class ManiphestTaskDependedOnByTaskEdgeType extends PhabricatorEdgeType {
|
|||
$rem_edges) {
|
||||
|
||||
return pht(
|
||||
'%s edited blocked task(s), added %s: %s; removed %s: %s.',
|
||||
'%s edited parent task(s), added %s: %s; removed %s: %s.',
|
||||
$actor,
|
||||
$add_count,
|
||||
$add_edges,
|
||||
|
@ -59,7 +59,7 @@ final class ManiphestTaskDependedOnByTaskEdgeType extends PhabricatorEdgeType {
|
|||
$add_edges) {
|
||||
|
||||
return pht(
|
||||
'%s added %s blocked task(s) for %s: %s.',
|
||||
'%s added %s parent task(s) for %s: %s.',
|
||||
$actor,
|
||||
$add_count,
|
||||
$object,
|
||||
|
@ -73,7 +73,7 @@ final class ManiphestTaskDependedOnByTaskEdgeType extends PhabricatorEdgeType {
|
|||
$rem_edges) {
|
||||
|
||||
return pht(
|
||||
'%s removed %s blocked task(s) for %s: %s.',
|
||||
'%s removed %s parent task(s) for %s: %s.',
|
||||
$actor,
|
||||
$rem_count,
|
||||
$object,
|
||||
|
@ -90,7 +90,7 @@ final class ManiphestTaskDependedOnByTaskEdgeType extends PhabricatorEdgeType {
|
|||
$rem_edges) {
|
||||
|
||||
return pht(
|
||||
'%s edited blocked task(s) for %s, added %s: %s; removed %s: %s.',
|
||||
'%s edited parent task(s) for %s, added %s: %s; removed %s: %s.',
|
||||
$actor,
|
||||
$object,
|
||||
$add_count,
|
||||
|
|
|
@ -22,7 +22,7 @@ final class ManiphestTaskDependsOnTaskEdgeType extends PhabricatorEdgeType {
|
|||
$add_edges) {
|
||||
|
||||
return pht(
|
||||
'%s added %s blocking task(s): %s.',
|
||||
'%s added %s subtask(s): %s.',
|
||||
$actor,
|
||||
$add_count,
|
||||
$add_edges);
|
||||
|
@ -34,7 +34,7 @@ final class ManiphestTaskDependsOnTaskEdgeType extends PhabricatorEdgeType {
|
|||
$rem_edges) {
|
||||
|
||||
return pht(
|
||||
'%s removed %s blocking task(s): %s.',
|
||||
'%s removed %s subtask(s): %s.',
|
||||
$actor,
|
||||
$rem_count,
|
||||
$rem_edges);
|
||||
|
@ -49,7 +49,7 @@ final class ManiphestTaskDependsOnTaskEdgeType extends PhabricatorEdgeType {
|
|||
$rem_edges) {
|
||||
|
||||
return pht(
|
||||
'%s edited blocking task(s), added %s: %s; removed %s: %s.',
|
||||
'%s edited subtask(s), added %s: %s; removed %s: %s.',
|
||||
$actor,
|
||||
$add_count,
|
||||
$add_edges,
|
||||
|
@ -64,7 +64,7 @@ final class ManiphestTaskDependsOnTaskEdgeType extends PhabricatorEdgeType {
|
|||
$add_edges) {
|
||||
|
||||
return pht(
|
||||
'%s added %s blocking task(s) for %s: %s.',
|
||||
'%s added %s subtask(s) for %s: %s.',
|
||||
$actor,
|
||||
$add_count,
|
||||
$object,
|
||||
|
@ -78,7 +78,7 @@ final class ManiphestTaskDependsOnTaskEdgeType extends PhabricatorEdgeType {
|
|||
$rem_edges) {
|
||||
|
||||
return pht(
|
||||
'%s removed %s blocking task(s) for %s: %s.',
|
||||
'%s removed %s subtask(s) for %s: %s.',
|
||||
$actor,
|
||||
$rem_count,
|
||||
$object,
|
||||
|
@ -95,7 +95,7 @@ final class ManiphestTaskDependsOnTaskEdgeType extends PhabricatorEdgeType {
|
|||
$rem_edges) {
|
||||
|
||||
return pht(
|
||||
'%s edited blocking task(s) for %s, added %s: %s; removed %s: %s.',
|
||||
'%s edited subtask(s) for %s, added %s: %s; removed %s: %s.',
|
||||
$actor,
|
||||
$object,
|
||||
$add_count,
|
||||
|
|
|
@ -325,7 +325,7 @@ final class ManiphestTransactionEditor
|
|||
ManiphestTransaction::MAILTAG_PROJECTS =>
|
||||
pht("A task's associated projects change."),
|
||||
ManiphestTransaction::MAILTAG_UNBLOCK =>
|
||||
pht('One of the tasks a task is blocked by changes status.'),
|
||||
pht("One of a task's subtasks changes status."),
|
||||
ManiphestTransaction::MAILTAG_COLUMN =>
|
||||
pht('A task is moved between columns on a workboard.'),
|
||||
ManiphestTransaction::MAILTAG_COMMENT =>
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
final class ManiphestTaskHasCommitRelationship
|
||||
extends ManiphestTaskRelationship {
|
||||
|
||||
const RELATIONSHIPKEY = 'task.has-commit';
|
||||
|
||||
public function getEdgeConstant() {
|
||||
return ManiphestTaskHasCommitEdgeType::EDGECONST;
|
||||
}
|
||||
|
||||
protected function getActionName() {
|
||||
return pht('Edit Commits');
|
||||
}
|
||||
|
||||
protected function getActionIcon() {
|
||||
return 'fa-code';
|
||||
}
|
||||
|
||||
public function shouldAppearInActionMenu() {
|
||||
// TODO: For now, the default search for commits is not very good, so
|
||||
// it is hard to find objects to link to. Until that works better, just
|
||||
// hide this item.
|
||||
return false;
|
||||
}
|
||||
|
||||
public function canRelateObjects($src, $dst) {
|
||||
return ($dst instanceof PhabricatorRepositoryCommit);
|
||||
}
|
||||
|
||||
public function getDialogTitleText() {
|
||||
return pht('Edit Related Commits');
|
||||
}
|
||||
|
||||
public function getDialogHeaderText() {
|
||||
return pht('Current Commits');
|
||||
}
|
||||
|
||||
public function getDialogButtonText() {
|
||||
return pht('Save Related Commits');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
final class ManiphestTaskHasMockRelationship
|
||||
extends ManiphestTaskRelationship {
|
||||
|
||||
const RELATIONSHIPKEY = 'task.has-mock';
|
||||
|
||||
public function getEdgeConstant() {
|
||||
return ManiphestTaskHasMockEdgeType::EDGECONST;
|
||||
}
|
||||
|
||||
protected function getActionName() {
|
||||
return pht('Edit Mocks');
|
||||
}
|
||||
|
||||
protected function getActionIcon() {
|
||||
return 'fa-camera-retro';
|
||||
}
|
||||
|
||||
public function canRelateObjects($src, $dst) {
|
||||
return ($dst instanceof PholioMock);
|
||||
}
|
||||
|
||||
public function getDialogTitleText() {
|
||||
return pht('Edit Related Mocks');
|
||||
}
|
||||
|
||||
public function getDialogHeaderText() {
|
||||
return pht('Current Mocks');
|
||||
}
|
||||
|
||||
public function getDialogButtonText() {
|
||||
return pht('Save Related Mocks');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
final class ManiphestTaskHasParentRelationship
|
||||
extends ManiphestTaskRelationship {
|
||||
|
||||
const RELATIONSHIPKEY = 'task.has-parent';
|
||||
|
||||
public function getEdgeConstant() {
|
||||
return ManiphestTaskDependedOnByTaskEdgeType::EDGECONST;
|
||||
}
|
||||
|
||||
protected function getActionName() {
|
||||
return pht('Edit Parent Tasks');
|
||||
}
|
||||
|
||||
protected function getActionIcon() {
|
||||
return 'fa-chevron-circle-up';
|
||||
}
|
||||
|
||||
public function canRelateObjects($src, $dst) {
|
||||
return ($dst instanceof ManiphestTask);
|
||||
}
|
||||
|
||||
public function shouldAppearInActionMenu() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getDialogTitleText() {
|
||||
return pht('Edit Parent Tasks');
|
||||
}
|
||||
|
||||
public function getDialogHeaderText() {
|
||||
return pht('Current Parent Tasks');
|
||||
}
|
||||
|
||||
public function getDialogButtonText() {
|
||||
return pht('Save Parent Tasks');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
final class ManiphestTaskHasRevisionRelationship
|
||||
extends ManiphestTaskRelationship {
|
||||
|
||||
const RELATIONSHIPKEY = 'task.has-revision';
|
||||
|
||||
public function getEdgeConstant() {
|
||||
return ManiphestTaskHasRevisionEdgeType::EDGECONST;
|
||||
}
|
||||
|
||||
protected function getActionName() {
|
||||
return pht('Edit Revisions');
|
||||
}
|
||||
|
||||
protected function getActionIcon() {
|
||||
return 'fa-cog';
|
||||
}
|
||||
|
||||
public function canRelateObjects($src, $dst) {
|
||||
return ($dst instanceof DifferentialRevision);
|
||||
}
|
||||
|
||||
public function getDialogTitleText() {
|
||||
return pht('Edit Related Revisions');
|
||||
}
|
||||
|
||||
public function getDialogHeaderText() {
|
||||
return pht('Current Revisions');
|
||||
}
|
||||
|
||||
public function getDialogButtonText() {
|
||||
return pht('Save Related Revisions');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
final class ManiphestTaskHasSubtaskRelationship
|
||||
extends ManiphestTaskRelationship {
|
||||
|
||||
const RELATIONSHIPKEY = 'task.has-subtask';
|
||||
|
||||
public function getEdgeConstant() {
|
||||
return ManiphestTaskDependsOnTaskEdgeType::EDGECONST;
|
||||
}
|
||||
|
||||
protected function getActionName() {
|
||||
return pht('Edit Subtasks');
|
||||
}
|
||||
|
||||
protected function getActionIcon() {
|
||||
return 'fa-chevron-circle-down';
|
||||
}
|
||||
|
||||
public function canRelateObjects($src, $dst) {
|
||||
return ($dst instanceof ManiphestTask);
|
||||
}
|
||||
|
||||
public function shouldAppearInActionMenu() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getDialogTitleText() {
|
||||
return pht('Edit Subtasks');
|
||||
}
|
||||
|
||||
public function getDialogHeaderText() {
|
||||
return pht('Current Subtasks');
|
||||
}
|
||||
|
||||
public function getDialogButtonText() {
|
||||
return pht('Save Subtasks');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
abstract class ManiphestTaskRelationship
|
||||
extends PhabricatorObjectRelationship {
|
||||
|
||||
public function isEnabledForObject($object) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$has_app = PhabricatorApplication::isClassInstalledForViewer(
|
||||
'PhabricatorManiphestApplication',
|
||||
$viewer);
|
||||
if (!$has_app) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ($object instanceof ManiphestTask);
|
||||
}
|
||||
|
||||
}
|
|
@ -500,24 +500,24 @@ final class ManiphestTransaction
|
|||
|
||||
if ($this->getMetadataValue('blocker.new')) {
|
||||
return pht(
|
||||
'%s created blocking task %s.',
|
||||
'%s created subtask %s.',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$this->renderHandleLink($blocker_phid));
|
||||
} else if ($old_closed && !$new_closed) {
|
||||
return pht(
|
||||
'%s reopened blocking task %s as "%s".',
|
||||
'%s reopened subtask %s as "%s".',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$this->renderHandleLink($blocker_phid),
|
||||
$new_name);
|
||||
} else if (!$old_closed && $new_closed) {
|
||||
return pht(
|
||||
'%s closed blocking task %s as "%s".',
|
||||
'%s closed subtask %s as "%s".',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$this->renderHandleLink($blocker_phid),
|
||||
$new_name);
|
||||
} else {
|
||||
return pht(
|
||||
'%s changed the status of blocking task %s from "%s" to "%s".',
|
||||
'%s changed the status of subtask %s from "%s" to "%s".',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$this->renderHandleLink($blocker_phid),
|
||||
$old_name,
|
||||
|
@ -753,21 +753,21 @@ final class ManiphestTransaction
|
|||
|
||||
if ($old_closed && !$new_closed) {
|
||||
return pht(
|
||||
'%s reopened %s, a task blocking %s, as "%s".',
|
||||
'%s reopened %s, a subtask of %s, as "%s".',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$this->renderHandleLink($blocker_phid),
|
||||
$this->renderHandleLink($object_phid),
|
||||
$new_name);
|
||||
} else if (!$old_closed && $new_closed) {
|
||||
return pht(
|
||||
'%s closed %s, a task blocking %s, as "%s".',
|
||||
'%s closed %s, a subtask of %s, as "%s".',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$this->renderHandleLink($blocker_phid),
|
||||
$this->renderHandleLink($object_phid),
|
||||
$new_name);
|
||||
} else {
|
||||
return pht(
|
||||
'%s changed the status of %s, a task blocking %s, '.
|
||||
'%s changed the status of %s, a subtasktask of %s, '.
|
||||
'from "%s" to "%s".',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$this->renderHandleLink($blocker_phid),
|
||||
|
|
|
@ -67,7 +67,8 @@ final class ManiphestTaskClosedStatusDatasource
|
|||
->setName($name.' closed')
|
||||
->setDisplayName($name)
|
||||
->setPHID(self::FUNCTION_TOKEN)
|
||||
->setUnique(true);
|
||||
->setUnique(true)
|
||||
->addAttribute(pht('Select any closed status.'));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -67,7 +67,8 @@ final class ManiphestTaskOpenStatusDatasource
|
|||
->setName($name.' open')
|
||||
->setDisplayName($name)
|
||||
->setPHID(self::FUNCTION_TOKEN)
|
||||
->setUnique(true);
|
||||
->setUnique(true)
|
||||
->addAttribute(pht('Select any open status.'));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -32,7 +32,8 @@ final class ManiphestTaskPriorityDatasource
|
|||
$result = id(new PhabricatorTypeaheadResult())
|
||||
->setIcon(ManiphestTaskPriority::getTaskPriorityIcon($value))
|
||||
->setPHID($value)
|
||||
->setName($name);
|
||||
->setName($name)
|
||||
->addAttribute(pht('Priority'));
|
||||
|
||||
if (ManiphestTaskPriority::isDisabledPriority($value)) {
|
||||
$result->setClosed(pht('Disabled'));
|
||||
|
|
|
@ -35,6 +35,12 @@ final class ManiphestTaskStatusDatasource
|
|||
->setPHID($value)
|
||||
->setName($name);
|
||||
|
||||
if (ManiphestTaskStatus::isOpenStatus($value)) {
|
||||
$result->addAttribute(pht('Open Status'));
|
||||
} else {
|
||||
$result->addAttribute(pht('Closed Status'));
|
||||
}
|
||||
|
||||
if (ManiphestTaskStatus::isDisabledStatus($value)) {
|
||||
$result->setClosed(pht('Disabled'));
|
||||
}
|
||||
|
|
|
@ -94,6 +94,10 @@ final class PhabricatorUser
|
|||
* @return bool True if this is a standard, usable account.
|
||||
*/
|
||||
public function isUserActivated() {
|
||||
if (!$this->isLoggedIn()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->isOmnipotent()) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -62,7 +62,8 @@ final class PhabricatorPeopleAnyOwnerDatasource
|
|||
->setDisplayName($name)
|
||||
->setIcon('fa-certificate')
|
||||
->setPHID(self::FUNCTION_TOKEN)
|
||||
->setUnique(true);
|
||||
->setUnique(true)
|
||||
->addAttribute(pht('Select results with any owner.'));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -3,18 +3,6 @@
|
|||
final class PhabricatorPeopleDatasource
|
||||
extends PhabricatorTypeaheadDatasource {
|
||||
|
||||
private $enrichResults;
|
||||
|
||||
/**
|
||||
* Controls enriched rendering, for global search. This is a bit hacky and
|
||||
* should probably be handled in a more general way, but is fairly reasonable
|
||||
* for now.
|
||||
*/
|
||||
public function setEnrichResults($enrich) {
|
||||
$this->enrichResults = $enrich;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getBrowseTitle() {
|
||||
return pht('Browse Users');
|
||||
}
|
||||
|
@ -40,7 +28,9 @@ final class PhabricatorPeopleDatasource
|
|||
|
||||
$users = $this->executeQuery($query);
|
||||
|
||||
if ($this->enrichResults && $users) {
|
||||
$is_browse = $this->getIsBrowse();
|
||||
|
||||
if ($is_browse && $users) {
|
||||
$phids = mpull($users, 'getPHID');
|
||||
$handles = id(new PhabricatorHandleQuery())
|
||||
->setViewer($viewer)
|
||||
|
@ -50,6 +40,8 @@ final class PhabricatorPeopleDatasource
|
|||
|
||||
$results = array();
|
||||
foreach ($users as $user) {
|
||||
$phid = $user->getPHID();
|
||||
|
||||
$closed = null;
|
||||
if ($user->getIsDisabled()) {
|
||||
$closed = pht('Disabled');
|
||||
|
@ -64,7 +56,7 @@ final class PhabricatorPeopleDatasource
|
|||
$result = id(new PhabricatorTypeaheadResult())
|
||||
->setName($user->getFullName())
|
||||
->setURI('/p/'.$username.'/')
|
||||
->setPHID($user->getPHID())
|
||||
->setPHID($phid)
|
||||
->setPriorityString($username)
|
||||
->setPriorityType('user')
|
||||
->setAutocomplete('@'.$username)
|
||||
|
@ -74,13 +66,29 @@ final class PhabricatorPeopleDatasource
|
|||
$result->setIcon('fa-envelope-o');
|
||||
}
|
||||
|
||||
if ($this->enrichResults) {
|
||||
$display_type = pht('User');
|
||||
if ($is_browse) {
|
||||
$handle = $handles[$phid];
|
||||
|
||||
$result
|
||||
->setIcon($handle->getIcon())
|
||||
->setImageURI($handle->getImageURI())
|
||||
->addAttribute($handle->getSubtitle());
|
||||
|
||||
if ($user->getIsAdmin()) {
|
||||
$result->addAttribute(
|
||||
array(
|
||||
id(new PHUIIconView())->setIcon('fa-star'),
|
||||
' ',
|
||||
pht('Administrator'),
|
||||
));
|
||||
}
|
||||
|
||||
if ($user->getIsAdmin()) {
|
||||
$display_type = pht('Administrator');
|
||||
} else {
|
||||
$display_type = pht('User');
|
||||
}
|
||||
$result->setDisplayType($display_type);
|
||||
$result->setImageURI($handles[$user->getPHID()]->getImageURI());
|
||||
}
|
||||
|
||||
$results[] = $result;
|
||||
|
|
|
@ -69,7 +69,8 @@ final class PhabricatorPeopleNoOwnerDatasource
|
|||
->setDisplayName($name)
|
||||
->setIcon('fa-ban')
|
||||
->setPHID('none()')
|
||||
->setUnique(true);
|
||||
->setUnique(true)
|
||||
->addAttribute(pht('Select results with no owner.'));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -74,7 +74,8 @@ final class PhabricatorViewerDatasource
|
|||
->setName(pht('Current Viewer'))
|
||||
->setPHID('viewer()')
|
||||
->setIcon('fa-user')
|
||||
->setUnique(true);
|
||||
->setUnique(true)
|
||||
->addAttribute(pht('Select current viewer.'));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -159,6 +159,12 @@ abstract class PhameLiveController extends PhameController {
|
|||
// "Blogs" crumb into the crumbs list.
|
||||
if ($is_external) {
|
||||
$crumbs = new PHUICrumbsView();
|
||||
// Link back to parent site
|
||||
if ($blog->getParentSite() && $blog->getParentDomain()) {
|
||||
$crumbs->addTextCrumb(
|
||||
$blog->getParentSite(),
|
||||
$blog->getExternalParentURI());
|
||||
}
|
||||
} else {
|
||||
$crumbs = parent::buildApplicationCrumbs();
|
||||
$crumbs->addTextCrumb(
|
||||
|
|
|
@ -95,15 +95,27 @@ final class PhameBlogManageController extends PhameBlogController {
|
|||
Javelin::initBehavior('phabricator-tooltips');
|
||||
|
||||
$properties = id(new PHUIPropertyListView())
|
||||
->setUser($viewer)
|
||||
->setObject($blog);
|
||||
->setUser($viewer);
|
||||
|
||||
$domain = $blog->getDomain();
|
||||
if (!$domain) {
|
||||
$domain = phutil_tag('em', array(), pht('No external domain'));
|
||||
$full_domain = $blog->getDomainFullURI();
|
||||
if (!$full_domain) {
|
||||
$full_domain = phutil_tag('em', array(), pht('No external domain'));
|
||||
}
|
||||
$properties->addProperty(pht('Full Domain'), $full_domain);
|
||||
|
||||
$parent_site = $blog->getParentSite();
|
||||
if (!$parent_site) {
|
||||
$parent_site = phutil_tag('em', array(), pht('No parent site'));
|
||||
}
|
||||
|
||||
$properties->addProperty(pht('Domain'), $domain);
|
||||
$properties->addProperty(pht('Parent Site'), $parent_site);
|
||||
|
||||
$parent_domain = $blog->getParentDomain();
|
||||
if (!$parent_domain) {
|
||||
$parent_domain = phutil_tag('em', array(), pht('No parent domain'));
|
||||
}
|
||||
|
||||
$properties->addProperty(pht('Parent Domain'), $parent_domain);
|
||||
|
||||
$feed_uri = PhabricatorEnv::getProductionURI(
|
||||
$this->getApplicationURI('blog/feed/'.$blog->getID().'/'));
|
||||
|
@ -133,8 +145,6 @@ final class PhameBlogManageController extends PhameBlogController {
|
|||
->addObject($blog, PhameBlog::MARKUP_FIELD_DESCRIPTION)
|
||||
->process();
|
||||
|
||||
$properties->invokeWillRenderEvent();
|
||||
|
||||
$description = $blog->getDescription();
|
||||
if (strlen($description)) {
|
||||
$description = new PHUIRemarkupView($viewer, $description);
|
||||
|
@ -150,7 +160,7 @@ final class PhameBlogManageController extends PhameBlogController {
|
|||
private function buildCurtain(PhameBlog $blog) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$curtain = $this->newCurtainView($viewer);
|
||||
$curtain = $this->newCurtainView($blog);
|
||||
|
||||
$actions = id(new PhabricatorActionListView())
|
||||
->setObject($blog)
|
||||
|
|
|
@ -94,13 +94,29 @@ final class PhameBlogEditEngine
|
|||
->setTransactionType(PhameBlogTransaction::TYPE_DESCRIPTION)
|
||||
->setValue($object->getDescription()),
|
||||
id(new PhabricatorTextEditField())
|
||||
->setKey('domain')
|
||||
->setLabel(pht('Custom Domain'))
|
||||
->setDescription(pht('Blog domain name.'))
|
||||
->setConduitDescription(pht('Change the blog domain.'))
|
||||
->setConduitTypeDescription(pht('New blog domain.'))
|
||||
->setValue($object->getDomain())
|
||||
->setTransactionType(PhameBlogTransaction::TYPE_DOMAIN),
|
||||
->setKey('domainFullURI')
|
||||
->setLabel(pht('Full Domain URI'))
|
||||
->setDescription(pht('Blog full domain URI.'))
|
||||
->setConduitDescription(pht('Change the blog full domain URI.'))
|
||||
->setConduitTypeDescription(pht('New blog full domain URI.'))
|
||||
->setValue($object->getDomainFullURI())
|
||||
->setTransactionType(PhameBlogTransaction::TYPE_FULLDOMAIN),
|
||||
id(new PhabricatorTextEditField())
|
||||
->setKey('parentSite')
|
||||
->setLabel(pht('Parent Site'))
|
||||
->setDescription(pht('Blog parent site name.'))
|
||||
->setConduitDescription(pht('Change the blog parent site name.'))
|
||||
->setConduitTypeDescription(pht('New blog parent site name.'))
|
||||
->setValue($object->getParentSite())
|
||||
->setTransactionType(PhameBlogTransaction::TYPE_PARENTSITE),
|
||||
id(new PhabricatorTextEditField())
|
||||
->setKey('parentDomain')
|
||||
->setLabel(pht('Parent Domain'))
|
||||
->setDescription(pht('Blog parent domain name.'))
|
||||
->setConduitDescription(pht('Change the blog parent domain.'))
|
||||
->setConduitTypeDescription(pht('New blog parent domain.'))
|
||||
->setValue($object->getParentDomain())
|
||||
->setTransactionType(PhameBlogTransaction::TYPE_PARENTDOMAIN),
|
||||
id(new PhabricatorSelectEditField())
|
||||
->setKey('status')
|
||||
->setLabel(pht('Status'))
|
||||
|
|
|
@ -17,7 +17,9 @@ final class PhameBlogEditor
|
|||
$types[] = PhameBlogTransaction::TYPE_NAME;
|
||||
$types[] = PhameBlogTransaction::TYPE_SUBTITLE;
|
||||
$types[] = PhameBlogTransaction::TYPE_DESCRIPTION;
|
||||
$types[] = PhameBlogTransaction::TYPE_DOMAIN;
|
||||
$types[] = PhameBlogTransaction::TYPE_FULLDOMAIN;
|
||||
$types[] = PhameBlogTransaction::TYPE_PARENTSITE;
|
||||
$types[] = PhameBlogTransaction::TYPE_PARENTDOMAIN;
|
||||
$types[] = PhameBlogTransaction::TYPE_STATUS;
|
||||
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
|
||||
$types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
|
||||
|
@ -36,8 +38,12 @@ final class PhameBlogEditor
|
|||
return $object->getSubtitle();
|
||||
case PhameBlogTransaction::TYPE_DESCRIPTION:
|
||||
return $object->getDescription();
|
||||
case PhameBlogTransaction::TYPE_DOMAIN:
|
||||
return $object->getDomain();
|
||||
case PhameBlogTransaction::TYPE_FULLDOMAIN:
|
||||
return $object->getDomainFullURI();
|
||||
case PhameBlogTransaction::TYPE_PARENTSITE:
|
||||
return $object->getParentSite();
|
||||
case PhameBlogTransaction::TYPE_PARENTDOMAIN:
|
||||
return $object->getParentDomain();
|
||||
case PhameBlogTransaction::TYPE_STATUS:
|
||||
return $object->getStatus();
|
||||
}
|
||||
|
@ -52,8 +58,10 @@ final class PhameBlogEditor
|
|||
case PhameBlogTransaction::TYPE_SUBTITLE:
|
||||
case PhameBlogTransaction::TYPE_DESCRIPTION:
|
||||
case PhameBlogTransaction::TYPE_STATUS:
|
||||
case PhameBlogTransaction::TYPE_PARENTSITE:
|
||||
case PhameBlogTransaction::TYPE_PARENTDOMAIN:
|
||||
return $xaction->getNewValue();
|
||||
case PhameBlogTransaction::TYPE_DOMAIN:
|
||||
case PhameBlogTransaction::TYPE_FULLDOMAIN:
|
||||
$domain = $xaction->getNewValue();
|
||||
if (!strlen($xaction->getNewValue())) {
|
||||
return null;
|
||||
|
@ -73,10 +81,23 @@ final class PhameBlogEditor
|
|||
return $object->setSubtitle($xaction->getNewValue());
|
||||
case PhameBlogTransaction::TYPE_DESCRIPTION:
|
||||
return $object->setDescription($xaction->getNewValue());
|
||||
case PhameBlogTransaction::TYPE_DOMAIN:
|
||||
return $object->setDomain($xaction->getNewValue());
|
||||
case PhameBlogTransaction::TYPE_FULLDOMAIN:
|
||||
$new_value = $xaction->getNewValue();
|
||||
if (strlen($new_value)) {
|
||||
$uri = new PhutilURI($new_value);
|
||||
$domain = $uri->getDomain();
|
||||
$object->setDomain($domain);
|
||||
} else {
|
||||
$object->setDomain(null);
|
||||
}
|
||||
$object->setDomainFullURI($new_value);
|
||||
return;
|
||||
case PhameBlogTransaction::TYPE_STATUS:
|
||||
return $object->setStatus($xaction->getNewValue());
|
||||
case PhameBlogTransaction::TYPE_PARENTSITE:
|
||||
return $object->setParentSite($xaction->getNewValue());
|
||||
case PhameBlogTransaction::TYPE_PARENTDOMAIN:
|
||||
return $object->setParentDomain($xaction->getNewValue());
|
||||
}
|
||||
|
||||
return parent::applyCustomInternalTransaction($object, $xaction);
|
||||
|
@ -90,7 +111,9 @@ final class PhameBlogEditor
|
|||
case PhameBlogTransaction::TYPE_NAME:
|
||||
case PhameBlogTransaction::TYPE_SUBTITLE:
|
||||
case PhameBlogTransaction::TYPE_DESCRIPTION:
|
||||
case PhameBlogTransaction::TYPE_DOMAIN:
|
||||
case PhameBlogTransaction::TYPE_FULLDOMAIN:
|
||||
case PhameBlogTransaction::TYPE_PARENTSITE:
|
||||
case PhameBlogTransaction::TYPE_PARENTDOMAIN:
|
||||
case PhameBlogTransaction::TYPE_STATUS:
|
||||
return;
|
||||
}
|
||||
|
@ -123,7 +146,26 @@ final class PhameBlogEditor
|
|||
$errors[] = $error;
|
||||
}
|
||||
break;
|
||||
case PhameBlogTransaction::TYPE_DOMAIN:
|
||||
case PhameBlogTransaction::TYPE_PARENTDOMAIN:
|
||||
if (!$xactions) {
|
||||
continue;
|
||||
}
|
||||
$parent_domain = last($xactions)->getNewValue();
|
||||
if (empty($parent_domain)) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
PhabricatorEnv::requireValidRemoteURIForLink($parent_domain);
|
||||
} catch (Exception $ex) {
|
||||
$error = new PhabricatorApplicationTransactionValidationError(
|
||||
$type,
|
||||
pht('Invalid URI'),
|
||||
pht('Parent Domain must be set to a valid Remote URI.'),
|
||||
nonempty(last($xactions), null));
|
||||
$errors[] = $error;
|
||||
}
|
||||
break;
|
||||
case PhameBlogTransaction::TYPE_FULLDOMAIN:
|
||||
if (!$xactions) {
|
||||
continue;
|
||||
}
|
||||
|
@ -152,9 +194,11 @@ final class PhameBlogEditor
|
|||
nonempty(last($xactions), null));
|
||||
$errors[] = $error;
|
||||
}
|
||||
$domain = new PhutilURI($custom_domain);
|
||||
$domain = $domain->getDomain();
|
||||
$duplicate_blog = id(new PhameBlogQuery())
|
||||
->setViewer(PhabricatorUser::getOmnipotentUser())
|
||||
->withDomain($custom_domain)
|
||||
->withDomain($domain)
|
||||
->executeOne();
|
||||
if ($duplicate_blog && $duplicate_blog->getID() != $object->getID()) {
|
||||
$error = new PhabricatorApplicationTransactionValidationError(
|
||||
|
|
|
@ -39,6 +39,7 @@ final class PhameBlogSite extends PhameSite {
|
|||
->setViewer(new PhabricatorUser())
|
||||
->withDomain($host)
|
||||
->needProfileImage(true)
|
||||
->needHeaderImage(true)
|
||||
->withStatuses(
|
||||
array(
|
||||
PhameBlog::STATUS_ACTIVE,
|
||||
|
|
|
@ -18,6 +18,9 @@ final class PhameBlog extends PhameDAO
|
|||
protected $subtitle;
|
||||
protected $description;
|
||||
protected $domain;
|
||||
protected $domainFullURI;
|
||||
protected $parentSite;
|
||||
protected $parentDomain;
|
||||
protected $configData;
|
||||
protected $creatorPHID;
|
||||
protected $viewPolicy;
|
||||
|
@ -44,6 +47,9 @@ final class PhameBlog extends PhameDAO
|
|||
'subtitle' => 'text64',
|
||||
'description' => 'text',
|
||||
'domain' => 'text128?',
|
||||
'domainFullURI' => 'text128?',
|
||||
'parentSite' => 'text128',
|
||||
'parentDomain' => 'text128',
|
||||
'status' => 'text32',
|
||||
'mailKey' => 'bytes20',
|
||||
'profileImagePHID' => 'phid?',
|
||||
|
@ -108,34 +114,29 @@ final class PhameBlog extends PhameDAO
|
|||
*
|
||||
* @return string
|
||||
*/
|
||||
public function validateCustomDomain($custom_domain) {
|
||||
$example_domain = 'blog.example.com';
|
||||
public function validateCustomDomain($domain_full_uri) {
|
||||
$example_domain = 'http://blog.example.com/';
|
||||
$label = pht('Invalid');
|
||||
|
||||
// note this "uri" should be pretty busted given the desired input
|
||||
// so just use it to test if there's a protocol specified
|
||||
$uri = new PhutilURI($custom_domain);
|
||||
if ($uri->getProtocol()) {
|
||||
$uri = new PhutilURI($domain_full_uri);
|
||||
$domain = $uri->getDomain();
|
||||
$protocol = $uri->getProtocol();
|
||||
$path = $uri->getPath();
|
||||
$supported_protocols = array('http', 'https');
|
||||
|
||||
if (!in_array($protocol, $supported_protocols)) {
|
||||
return array(
|
||||
$label,
|
||||
pht(
|
||||
'The custom domain should not include a protocol. Just provide '.
|
||||
'the bare domain name (for example, "%s").',
|
||||
'The custom domain should include a valid protocol in the URI '.
|
||||
'(for example, "%s"). Valid protocols are "http" or "https".',
|
||||
$example_domain),
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
if ($uri->getPort()) {
|
||||
return array(
|
||||
$label,
|
||||
pht(
|
||||
'The custom domain should not include a port number. Just provide '.
|
||||
'the bare domain name (for example, "%s").',
|
||||
$example_domain),
|
||||
);
|
||||
}
|
||||
|
||||
if (strpos($custom_domain, '/') !== false) {
|
||||
if (strlen($path) && $path != '/') {
|
||||
return array(
|
||||
$label,
|
||||
pht(
|
||||
|
@ -146,7 +147,7 @@ final class PhameBlog extends PhameDAO
|
|||
);
|
||||
}
|
||||
|
||||
if (strpos($custom_domain, '.') === false) {
|
||||
if (strpos($domain, '.') === false) {
|
||||
return array(
|
||||
$label,
|
||||
pht(
|
||||
|
@ -187,8 +188,14 @@ final class PhameBlog extends PhameDAO
|
|||
}
|
||||
|
||||
public function getExternalLiveURI() {
|
||||
$domain = $this->getDomain();
|
||||
$uri = new PhutilURI('http://'.$this->getDomain().'/');
|
||||
$uri = new PhutilURI($this->getDomainFullURI());
|
||||
PhabricatorEnv::requireValidRemoteURIForLink($uri);
|
||||
return (string)$uri;
|
||||
}
|
||||
|
||||
public function getExternalParentURI() {
|
||||
$uri = $this->getParentDomain();
|
||||
PhabricatorEnv::requireValidRemoteURIForLink($uri);
|
||||
return (string)$uri;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,8 +6,10 @@ final class PhameBlogTransaction
|
|||
const TYPE_NAME = 'phame.blog.name';
|
||||
const TYPE_SUBTITLE = 'phame.blog.subtitle';
|
||||
const TYPE_DESCRIPTION = 'phame.blog.description';
|
||||
const TYPE_DOMAIN = 'phame.blog.domain';
|
||||
const TYPE_FULLDOMAIN = 'phame.blog.full.domain';
|
||||
const TYPE_STATUS = 'phame.blog.status';
|
||||
const TYPE_PARENTSITE = 'phame.blog.parent.site';
|
||||
const TYPE_PARENTDOMAIN = 'phame.blog.parent.domain';
|
||||
|
||||
const MAILTAG_DETAILS = 'phame-blog-details';
|
||||
const MAILTAG_SUBSCRIBERS = 'phame-blog-subscribers';
|
||||
|
@ -44,7 +46,7 @@ final class PhameBlogTransaction
|
|||
}
|
||||
break;
|
||||
case self::TYPE_DESCRIPTION:
|
||||
case self::TYPE_DOMAIN:
|
||||
case self::TYPE_FULLDOMAIN:
|
||||
return 'fa-pencil';
|
||||
case self::TYPE_STATUS:
|
||||
if ($new == PhameBlog::STATUS_ARCHIVED) {
|
||||
|
@ -65,7 +67,7 @@ final class PhameBlogTransaction
|
|||
switch ($this->getTransactionType()) {
|
||||
case self::TYPE_STATUS:
|
||||
if ($new == PhameBlog::STATUS_ARCHIVED) {
|
||||
return 'red';
|
||||
return 'violet';
|
||||
} else {
|
||||
return 'green';
|
||||
}
|
||||
|
@ -83,7 +85,9 @@ final class PhameBlogTransaction
|
|||
case self::TYPE_NAME:
|
||||
case self::TYPE_SUBTITLE:
|
||||
case self::TYPE_DESCRIPTION:
|
||||
case self::TYPE_DOMAIN:
|
||||
case self::TYPE_FULLDOMAIN:
|
||||
case self::TYPE_PARENTSITE:
|
||||
case self::TYPE_PARENTDOMAIN:
|
||||
$tags[] = self::MAILTAG_DETAILS;
|
||||
break;
|
||||
default:
|
||||
|
@ -136,12 +140,38 @@ final class PhameBlogTransaction
|
|||
'%s updated the blog\'s description.',
|
||||
$this->renderHandleLink($author_phid));
|
||||
break;
|
||||
case self::TYPE_DOMAIN:
|
||||
case self::TYPE_FULLDOMAIN:
|
||||
return pht(
|
||||
'%s updated the blog\'s domain to "%s".',
|
||||
'%s updated the blog\'s full domain to "%s".',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$new);
|
||||
break;
|
||||
case self::TYPE_PARENTSITE:
|
||||
if ($old === null) {
|
||||
return pht(
|
||||
'%s set this blog\'s parent site to "%s".',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$new);
|
||||
} else {
|
||||
return pht(
|
||||
'%s updated the blog\'s parent site to "%s".',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$new);
|
||||
}
|
||||
break;
|
||||
case self::TYPE_PARENTDOMAIN:
|
||||
if ($old === null) {
|
||||
return pht(
|
||||
'%s set this blog\'s parent domain to "%s".',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$new);
|
||||
} else {
|
||||
return pht(
|
||||
'%s updated the blog\'s parent domain to "%s".',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$new);
|
||||
}
|
||||
break;
|
||||
case self::TYPE_STATUS:
|
||||
switch ($new) {
|
||||
case PhameBlog::STATUS_ACTIVE:
|
||||
|
@ -200,9 +230,21 @@ final class PhameBlogTransaction
|
|||
$this->renderHandleLink($author_phid),
|
||||
$this->renderHandleLink($object_phid));
|
||||
break;
|
||||
case self::TYPE_DOMAIN:
|
||||
case self::TYPE_FULLDOMAIN:
|
||||
return pht(
|
||||
'%s updated the domain for %s.',
|
||||
'%s updated the full domain for %s.',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$this->renderHandleLink($object_phid));
|
||||
break;
|
||||
case self::TYPE_PARENTSITE:
|
||||
return pht(
|
||||
'%s updated the parent site for %s.',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$this->renderHandleLink($object_phid));
|
||||
break;
|
||||
case self::TYPE_PARENTDOMAIN:
|
||||
return pht(
|
||||
'%s updated the parent domain for %s.',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$this->renderHandleLink($object_phid));
|
||||
break;
|
||||
|
|
|
@ -26,12 +26,6 @@ final class PhabricatorPholioApplication extends PhabricatorApplication {
|
|||
return pht('Things before they were cool.');
|
||||
}
|
||||
|
||||
public function getEventListeners() {
|
||||
return array(
|
||||
new PholioActionMenuEventListener(),
|
||||
);
|
||||
}
|
||||
|
||||
public function getRemarkupRules() {
|
||||
return array(
|
||||
new PholioRemarkupRule(),
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class PholioActionMenuEventListener
|
||||
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->handleActionsEvent($event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private function handleActionsEvent(PhutilEvent $event) {
|
||||
$object = $event->getValue('object');
|
||||
|
||||
$actions = null;
|
||||
if ($object instanceof ManiphestTask) {
|
||||
$actions = $this->renderTaskItems($event);
|
||||
}
|
||||
|
||||
$this->addActionMenuItems($event, $actions);
|
||||
}
|
||||
|
||||
private function renderTaskItems(PhutilEvent $event) {
|
||||
if (!$this->canUseApplication($event->getUser())) {
|
||||
return;
|
||||
}
|
||||
|
||||
$task = $event->getValue('object');
|
||||
$phid = $task->getPHID();
|
||||
|
||||
$can_edit = PhabricatorPolicyFilter::hasCapability(
|
||||
$event->getUser(),
|
||||
$task,
|
||||
PhabricatorPolicyCapability::CAN_EDIT);
|
||||
|
||||
return id(new PhabricatorActionView())
|
||||
->setName(pht('Edit Pholio Mocks'))
|
||||
->setHref("/search/attach/{$phid}/MOCK/edge/")
|
||||
->setWorkflow(true)
|
||||
->setIcon('fa-camera-retro')
|
||||
->setDisabled(!$can_edit)
|
||||
->setWorkflow(true);
|
||||
}
|
||||
|
||||
}
|
|
@ -55,9 +55,25 @@ final class PhabricatorProjectDatasource
|
|||
$has_cols = array_fill_keys(array_keys($projs), true);
|
||||
}
|
||||
|
||||
$is_browse = $this->getIsBrowse();
|
||||
if ($is_browse && $projs) {
|
||||
// TODO: This is a little ad-hoc, but we don't currently have
|
||||
// infrastructure for bulk querying custom fields efficiently.
|
||||
$table = new PhabricatorProjectCustomFieldStorage();
|
||||
$descriptions = $table->loadAllWhere(
|
||||
'objectPHID IN (%Ls) AND fieldIndex = %s',
|
||||
array_keys($projs),
|
||||
PhabricatorHash::digestForIndex('std:project:internal:description'));
|
||||
$descriptions = mpull($descriptions, 'getFieldValue', 'getObjectPHID');
|
||||
} else {
|
||||
$descriptions = array();
|
||||
}
|
||||
|
||||
$results = array();
|
||||
foreach ($projs as $proj) {
|
||||
if (!isset($has_cols[$proj->getPHID()])) {
|
||||
$phid = $proj->getPHID();
|
||||
|
||||
if (!isset($has_cols[$phid])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -99,7 +115,7 @@ final class PhabricatorProjectDatasource
|
|||
->setDisplayName($proj->getDisplayName())
|
||||
->setDisplayType($proj->getDisplayIconName())
|
||||
->setURI($proj->getURI())
|
||||
->setPHID($proj->getPHID())
|
||||
->setPHID($phid)
|
||||
->setIcon($proj->getDisplayIconIcon())
|
||||
->setColor($proj->getColor())
|
||||
->setPriorityType('proj')
|
||||
|
@ -111,6 +127,16 @@ final class PhabricatorProjectDatasource
|
|||
|
||||
$proj_result->setImageURI($proj->getProfileImageURI());
|
||||
|
||||
if ($is_browse) {
|
||||
$proj_result->addAttribute($proj->getDisplayIconName());
|
||||
|
||||
$description = idx($descriptions, $phid);
|
||||
if (strlen($description)) {
|
||||
$summary = PhabricatorMarkupEngine::summarize($description);
|
||||
$proj_result->addAttribute($summary);
|
||||
}
|
||||
}
|
||||
|
||||
$results[] = $proj_result;
|
||||
}
|
||||
|
||||
|
|
|
@ -86,20 +86,24 @@ final class PhabricatorProjectLogicalOrNotDatasource
|
|||
$result
|
||||
->setTokenType(PhabricatorTypeaheadTokenView::TYPE_FUNCTION)
|
||||
->setIcon('fa-asterisk')
|
||||
->setColor(null);
|
||||
->setColor(null)
|
||||
->resetAttributes()
|
||||
->addAttribute(pht('Function'));
|
||||
|
||||
if ($return_any) {
|
||||
$return[] = id(clone $result)
|
||||
->setPHID('any('.$result->getPHID().')')
|
||||
->setDisplayName(pht('In Any: %s', $result->getDisplayName()))
|
||||
->setName('any '.$result->getName());
|
||||
->setName('any '.$result->getName())
|
||||
->addAttribute(pht('Include results tagged with this project.'));
|
||||
}
|
||||
|
||||
if ($return_not) {
|
||||
$return[] = id(clone $result)
|
||||
->setPHID('not('.$result->getPHID().')')
|
||||
->setDisplayName(pht('Not In: %s', $result->getDisplayName()))
|
||||
->setName('not '.$result->getName());
|
||||
->setName('not '.$result->getName())
|
||||
->addAttribute(pht('Exclude results tagged with this project.'));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -96,7 +96,8 @@ final class PhabricatorProjectLogicalViewerDatasource
|
|||
->setName(pht('Current Viewer\'s Projects'))
|
||||
->setPHID('viewerprojects()')
|
||||
->setIcon('fa-asterisk')
|
||||
->setUnique(true);
|
||||
->setUnique(true)
|
||||
->addAttribute(pht('Select projects current viewer is a member of.'));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -44,7 +44,10 @@ final class PhabricatorProjectMembersDatasource
|
|||
->setColor(null)
|
||||
->setPHID('members('.$result->getPHID().')')
|
||||
->setDisplayName(pht('Members: %s', $result->getDisplayName()))
|
||||
->setName($result->getName().' members');
|
||||
->setName($result->getName().' members')
|
||||
->resetAttributes()
|
||||
->addAttribute(pht('Function'))
|
||||
->addAttribute(pht('Select project members.'));
|
||||
}
|
||||
|
||||
return $results;
|
||||
|
|
|
@ -68,7 +68,8 @@ final class PhabricatorProjectNoProjectsDatasource
|
|||
->setPHID('null()')
|
||||
->setIcon('fa-ban')
|
||||
->setName('null '.$name)
|
||||
->setDisplayName($name);
|
||||
->setDisplayName($name)
|
||||
->addAttribute(pht('Select results with no tags.'));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -191,7 +191,7 @@ final class PhabricatorRepositoryPullEngine
|
|||
));
|
||||
}
|
||||
|
||||
private function installHook($path) {
|
||||
private function installHook($path, array $hook_argv = array()) {
|
||||
$this->log('%s', pht('Installing commit hook to "%s"...', $path));
|
||||
|
||||
$repository = $this->getRepository();
|
||||
|
@ -202,10 +202,11 @@ final class PhabricatorRepositoryPullEngine
|
|||
|
||||
$full_php_path = Filesystem::resolveBinary('php');
|
||||
$cmd = csprintf(
|
||||
'exec %s -f %s -- %s "$@"',
|
||||
'exec %s -f %s -- %s %Ls "$@"',
|
||||
$full_php_path,
|
||||
$bin,
|
||||
$identifier);
|
||||
$identifier,
|
||||
$hook_argv);
|
||||
|
||||
$hook = "#!/bin/sh\nexport TERM=dumb\n{$cmd}\n";
|
||||
|
||||
|
@ -585,8 +586,16 @@ final class PhabricatorRepositoryPullEngine
|
|||
$root = $repository->getLocalPath();
|
||||
|
||||
$path = '/hooks/pre-commit';
|
||||
|
||||
$this->installHook($root.$path);
|
||||
|
||||
$revprop_path = '/hooks/pre-revprop-change';
|
||||
|
||||
$revprop_argv = array(
|
||||
'--hook-mode',
|
||||
'svn-revprop',
|
||||
);
|
||||
|
||||
$this->installHook($root.$revprop_path, $revprop_argv);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1623,11 +1623,10 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
|
|||
return false;
|
||||
}
|
||||
|
||||
if ($this->isGit() || $this->isHg()) {
|
||||
return true;
|
||||
}
|
||||
// In Git and Mercurial, ref deletions and rewrites are dangerous.
|
||||
// In Subversion, editing revprops is dangerous.
|
||||
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public function shouldAllowDangerousChanges() {
|
||||
|
|
|
@ -41,6 +41,8 @@ final class PhabricatorSearchApplication extends PhabricatorApplication {
|
|||
'delete/(?P<queryKey>[^/]+)/(?P<engine>[^/]+)/'
|
||||
=> 'PhabricatorSearchDeleteController',
|
||||
'order/(?P<engine>[^/]+)/' => 'PhabricatorSearchOrderController',
|
||||
'rel/(?P<relationshipKey>[^/]+)/(?P<sourcePHID>[^/]+)/'
|
||||
=> 'PhabricatorSearchRelationshipController',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -18,6 +18,11 @@ final class PhabricatorSearchAttachController
|
|||
|
||||
$object = id(new PhabricatorObjectQuery())
|
||||
->setViewer($user)
|
||||
->requireCapabilities(
|
||||
array(
|
||||
PhabricatorPolicyCapability::CAN_VIEW,
|
||||
PhabricatorPolicyCapability::CAN_EDIT,
|
||||
))
|
||||
->withPHIDs(array($phid))
|
||||
->executeOne();
|
||||
|
||||
|
|
|
@ -0,0 +1,205 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorSearchRelationshipController
|
||||
extends PhabricatorSearchBaseController {
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$phid = $request->getURIData('sourcePHID');
|
||||
$object = id(new PhabricatorObjectQuery())
|
||||
->setViewer($viewer)
|
||||
->withPHIDs(array($phid))
|
||||
->requireCapabilities(
|
||||
array(
|
||||
PhabricatorPolicyCapability::CAN_VIEW,
|
||||
PhabricatorPolicyCapability::CAN_EDIT,
|
||||
))
|
||||
->executeOne();
|
||||
if (!$object) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$list = PhabricatorObjectRelationshipList::newForObject(
|
||||
$viewer,
|
||||
$object);
|
||||
|
||||
$relationship_key = $request->getURIData('relationshipKey');
|
||||
$relationship = $list->getRelationship($relationship_key);
|
||||
if (!$relationship) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$src_phid = $object->getPHID();
|
||||
$edge_type = $relationship->getEdgeConstant();
|
||||
|
||||
$dst_phids = PhabricatorEdgeQuery::loadDestinationPHIDs(
|
||||
$src_phid,
|
||||
$edge_type);
|
||||
|
||||
$all_phids = $dst_phids;
|
||||
$all_phids[] = $src_phid;
|
||||
|
||||
$handles = $viewer->loadHandles($all_phids);
|
||||
$src_handle = $handles[$src_phid];
|
||||
|
||||
$done_uri = $src_handle->getURI();
|
||||
$initial_phids = $dst_phids;
|
||||
|
||||
if ($request->isFormPost()) {
|
||||
$phids = explode(';', $request->getStr('phids'));
|
||||
$phids = array_filter($phids);
|
||||
$phids = array_values($phids);
|
||||
|
||||
$initial_phids = $request->getStrList('initialPHIDs');
|
||||
|
||||
// Apply the changes as adds and removes relative to the original state
|
||||
// of the object when the dialog was rendered so that two users adding
|
||||
// relationships at the same time don't race and overwrite one another.
|
||||
$add_phids = array_diff($phids, $initial_phids);
|
||||
$rem_phids = array_diff($initial_phids, $phids);
|
||||
|
||||
if ($add_phids) {
|
||||
$dst_objects = id(new PhabricatorObjectQuery())
|
||||
->setViewer($viewer)
|
||||
->withPHIDs($phids)
|
||||
->setRaisePolicyExceptions(true)
|
||||
->execute();
|
||||
$dst_objects = mpull($dst_objects, null, 'getPHID');
|
||||
} else {
|
||||
$dst_objects = array();
|
||||
}
|
||||
|
||||
try {
|
||||
foreach ($add_phids as $add_phid) {
|
||||
$dst_object = idx($dst_objects, $add_phid);
|
||||
if (!$dst_object) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'You can not create a relationship to object "%s" because '.
|
||||
'the object does not exist or could not be loaded.',
|
||||
$add_phid));
|
||||
}
|
||||
|
||||
if (!$relationship->canRelateObjects($object, $dst_object)) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'You can not create a relationship (of type "%s") to object '.
|
||||
'"%s" because it is not the right type of object for this '.
|
||||
'relationship.',
|
||||
$relationship->getRelationshipConstant(),
|
||||
$add_phid));
|
||||
}
|
||||
}
|
||||
} catch (Exception $ex) {
|
||||
return $this->newUnrelatableObjectResponse($ex, $done_uri);
|
||||
}
|
||||
|
||||
$editor = $object->getApplicationTransactionEditor()
|
||||
->setActor($viewer)
|
||||
->setContentSourceFromRequest($request)
|
||||
->setContinueOnMissingFields(true)
|
||||
->setContinueOnNoEffect(true);
|
||||
|
||||
$xactions = array();
|
||||
$xactions[] = $object->getApplicationTransactionTemplate()
|
||||
->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
|
||||
->setMetadataValue('edge:type', $edge_type)
|
||||
->setNewValue(array(
|
||||
'+' => array_fuse($add_phids),
|
||||
'-' => array_fuse($rem_phids),
|
||||
));
|
||||
|
||||
try {
|
||||
$editor->applyTransactions($object, $xactions);
|
||||
|
||||
return id(new AphrontRedirectResponse())->setURI($done_uri);
|
||||
} catch (PhabricatorEdgeCycleException $ex) {
|
||||
return $this->newGraphCycleResponse($ex, $done_uri);
|
||||
}
|
||||
}
|
||||
|
||||
$handles = iterator_to_array($handles);
|
||||
$handles = array_select_keys($handles, $dst_phids);
|
||||
|
||||
// TODO: These are hard-coded for now.
|
||||
$filters = array(
|
||||
'assigned' => pht('Assigned to Me'),
|
||||
'created' => pht('Created By Me'),
|
||||
'open' => pht('All Open Objects'),
|
||||
'all' => pht('All Objects'),
|
||||
);
|
||||
|
||||
$dialog_title = $relationship->getDialogTitleText();
|
||||
$dialog_header = $relationship->getDialogHeaderText();
|
||||
$dialog_button = $relationship->getDialogButtonText();
|
||||
$dialog_instructions = $relationship->getDialogInstructionsText();
|
||||
|
||||
// TODO: Remove this, this is just legacy support.
|
||||
$legacy_kinds = array(
|
||||
ManiphestTaskHasCommitEdgeType::EDGECONST => 'CMIT',
|
||||
ManiphestTaskHasMockEdgeType::EDGECONST => 'MOCK',
|
||||
ManiphestTaskHasRevisionEdgeType::EDGECONST => 'DREV',
|
||||
ManiphestTaskDependsOnTaskEdgeType::EDGECONST => 'TASK',
|
||||
ManiphestTaskDependedOnByTaskEdgeType::EDGECONST => 'TASK',
|
||||
);
|
||||
|
||||
$edge_type = $relationship->getEdgeConstant();
|
||||
$legacy_kind = idx($legacy_kinds, $edge_type);
|
||||
if (!$legacy_kind) {
|
||||
throw new Exception(
|
||||
pht('Only specific legacy relationships are supported!'));
|
||||
}
|
||||
|
||||
return id(new PhabricatorObjectSelectorDialog())
|
||||
->setUser($viewer)
|
||||
->setInitialPHIDs($initial_phids)
|
||||
->setHandles($handles)
|
||||
->setFilters($filters)
|
||||
->setSelectedFilter('created')
|
||||
->setExcluded($phid)
|
||||
->setCancelURI($done_uri)
|
||||
->setSearchURI("/search/select/{$legacy_kind}/edge/")
|
||||
->setTitle($dialog_title)
|
||||
->setHeader($dialog_header)
|
||||
->setButtonText($dialog_button)
|
||||
->setInstructions($dialog_instructions)
|
||||
->buildDialog();
|
||||
}
|
||||
|
||||
private function newGraphCycleResponse(
|
||||
PhabricatorEdgeCycleException $ex,
|
||||
$done_uri) {
|
||||
|
||||
$viewer = $this->getViewer();
|
||||
$cycle = $ex->getCycle();
|
||||
|
||||
$handles = $this->loadViewerHandles($cycle);
|
||||
$names = array();
|
||||
foreach ($cycle as $cycle_phid) {
|
||||
$names[] = $handles[$cycle_phid]->getFullName();
|
||||
}
|
||||
|
||||
$message = pht(
|
||||
'You can not create that relationship because it would create a '.
|
||||
'circular dependency:');
|
||||
|
||||
$list = implode(" \xE2\x86\x92 ", $names);
|
||||
|
||||
return $this->newDialog()
|
||||
->setTitle(pht('Circular Dependency'))
|
||||
->appendParagraph($message)
|
||||
->appendParagraph($list)
|
||||
->addCancelButton($done_uri);
|
||||
}
|
||||
|
||||
private function newUnrelatableObjectResponse(Exception $ex, $done_uri) {
|
||||
$message = $ex->getMessage();
|
||||
|
||||
return $this->newDialog()
|
||||
->setTitle(pht('Invalid Relationship'))
|
||||
->appendParagraph($message)
|
||||
->addCancelButton($done_uri);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
|
||||
abstract class PhabricatorObjectRelationship extends Phobject {
|
||||
|
||||
private $viewer;
|
||||
|
||||
public function setViewer(PhabricatorUser $viewer) {
|
||||
$this->viewer = $viewer;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getViewer() {
|
||||
return $this->viewer;
|
||||
}
|
||||
|
||||
final public function getRelationshipConstant() {
|
||||
return $this->getPhobjectClassConstant('RELATIONSHIPKEY');
|
||||
}
|
||||
|
||||
abstract public function isEnabledForObject($object);
|
||||
|
||||
abstract public function getEdgeConstant();
|
||||
|
||||
abstract protected function getActionName();
|
||||
abstract protected function getActionIcon();
|
||||
|
||||
abstract public function canRelateObjects($src, $dst);
|
||||
|
||||
abstract public function getDialogTitleText();
|
||||
abstract public function getDialogHeaderText();
|
||||
abstract public function getDialogButtonText();
|
||||
|
||||
public function getDialogInstructionsText() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public function shouldAppearInActionMenu() {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function isActionEnabled($object) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
return PhabricatorPolicyFilter::hasCapability(
|
||||
$viewer,
|
||||
$object,
|
||||
PhabricatorPolicyCapability::CAN_EDIT);
|
||||
}
|
||||
|
||||
final public function newAction($object) {
|
||||
$is_enabled = $this->isActionEnabled($object);
|
||||
$action_uri = $this->getActionURI($object);
|
||||
|
||||
return id(new PhabricatorActionView())
|
||||
->setName($this->getActionName())
|
||||
->setHref($action_uri)
|
||||
->setIcon($this->getActionIcon())
|
||||
->setDisabled(!$is_enabled)
|
||||
->setWorkflow(true);
|
||||
}
|
||||
|
||||
final public static function getAllRelationships() {
|
||||
return id(new PhutilClassMapQuery())
|
||||
->setAncestorClass(__CLASS__)
|
||||
->setUniqueMethod('getRelationshipConstant')
|
||||
->execute();
|
||||
}
|
||||
|
||||
private function getActionURI($object) {
|
||||
$phid = $object->getPHID();
|
||||
$type = $this->getRelationshipConstant();
|
||||
return "/search/rel/{$type}/{$phid}/";
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorObjectRelationshipList extends Phobject {
|
||||
|
||||
private $viewer;
|
||||
private $object;
|
||||
private $relationships;
|
||||
|
||||
public function setViewer(PhabricatorUser $viewer) {
|
||||
$this->viewer = $viewer;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getViewer() {
|
||||
if ($this->viewer === null) {
|
||||
throw new PhutilInvalidStateException('setViewer');
|
||||
}
|
||||
|
||||
return $this->viewer;
|
||||
}
|
||||
|
||||
public function setObject($object) {
|
||||
$this->object = $object;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getObject() {
|
||||
if ($this->object === null) {
|
||||
throw new PhutilInvalidStateException('setObject');
|
||||
}
|
||||
|
||||
return $this->object;
|
||||
}
|
||||
|
||||
public function setRelationships(array $relationships) {
|
||||
assert_instances_of($relationships, 'PhabricatorObjectRelationship');
|
||||
$this->relationships = $relationships;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getRelationships() {
|
||||
if ($this->relationships === null) {
|
||||
throw new PhutilInvalidStateException('setRelationships');
|
||||
}
|
||||
|
||||
return $this->relationships;
|
||||
}
|
||||
|
||||
public function newActionMenu() {
|
||||
$relationships = $this->getRelationships();
|
||||
$object = $this->getObject();
|
||||
|
||||
$actions = array();
|
||||
foreach ($relationships as $key => $relationship) {
|
||||
if (!$relationship->shouldAppearInActionMenu()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$actions[$key] = $relationship->newAction($object);
|
||||
}
|
||||
|
||||
if (!$actions) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$actions = msort($actions, 'getName');
|
||||
|
||||
return id(new PhabricatorActionView())
|
||||
->setName(pht('Edit Related Objects...'))
|
||||
->setIcon('fa-link')
|
||||
->setSubmenu($actions);
|
||||
}
|
||||
|
||||
public function getRelationship($key) {
|
||||
return idx($this->relationships, $key);
|
||||
}
|
||||
|
||||
public static function newForObject(PhabricatorUser $viewer, $object) {
|
||||
$relationships = PhabricatorObjectRelationship::getAllRelationships();
|
||||
|
||||
$results = array();
|
||||
foreach ($relationships as $key => $relationship) {
|
||||
$relationship = clone $relationship;
|
||||
|
||||
$relationship->setViewer($viewer);
|
||||
if (!$relationship->isEnabledForObject($object)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$results[$key] = $relationship;
|
||||
}
|
||||
|
||||
return id(new self())
|
||||
->setViewer($viewer)
|
||||
->setObject($object)
|
||||
->setRelationships($results);
|
||||
}
|
||||
|
||||
}
|
|
@ -16,14 +16,22 @@ final class PhabricatorSearchDatasource
|
|||
}
|
||||
|
||||
public function getComponentDatasources() {
|
||||
return array(
|
||||
id(new PhabricatorPeopleDatasource())->setEnrichResults(true),
|
||||
$sources = array(
|
||||
new PhabricatorPeopleDatasource(),
|
||||
new PhabricatorProjectDatasource(),
|
||||
new PhabricatorApplicationDatasource(),
|
||||
new PhabricatorTypeaheadMonogramDatasource(),
|
||||
new DiffusionRepositoryDatasource(),
|
||||
new DiffusionSymbolDatasource(),
|
||||
);
|
||||
|
||||
// These results are always rendered in the full browse display mode, so
|
||||
// set the browse flag on all component sources.
|
||||
foreach ($sources as $source) {
|
||||
$source->setIsBrowse(true);
|
||||
}
|
||||
|
||||
return $sources;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -931,6 +931,7 @@ abstract class PhabricatorApplicationTransactionEditor
|
|||
$object->openTransaction();
|
||||
}
|
||||
|
||||
try {
|
||||
foreach ($xactions as $xaction) {
|
||||
$this->applyInternalEffects($object, $xaction);
|
||||
}
|
||||
|
@ -940,8 +941,6 @@ abstract class PhabricatorApplicationTransactionEditor
|
|||
try {
|
||||
$object->save();
|
||||
} catch (AphrontDuplicateKeyQueryException $ex) {
|
||||
$object->killTransaction();
|
||||
|
||||
// This callback has an opportunity to throw a better exception,
|
||||
// so execution may end here.
|
||||
$this->didCatchDuplicateKeyException($object, $xactions, $ex);
|
||||
|
@ -973,7 +972,11 @@ abstract class PhabricatorApplicationTransactionEditor
|
|||
$read_locking = false;
|
||||
}
|
||||
|
||||
$object->saveTransaction();
|
||||
$object->saveTransaction();
|
||||
} catch (Exception $ex) {
|
||||
$object->killTransaction();
|
||||
throw $ex;
|
||||
}
|
||||
|
||||
// Now that we've completely applied the core transaction set, try to apply
|
||||
// Herald rules. Herald rules are allowed to either take direct actions on
|
||||
|
|
|
@ -343,6 +343,7 @@ class PhabricatorApplicationTransactionCommentView extends AphrontView {
|
|||
array(
|
||||
'id' => $this->getPreviewPanelID(),
|
||||
'style' => 'display: none',
|
||||
'class' => 'phui-comment-preview-view',
|
||||
),
|
||||
$preview);
|
||||
}
|
||||
|
|
|
@ -65,7 +65,8 @@ final class PhabricatorTypeaheadModularDatasourceController
|
|||
}
|
||||
|
||||
$composite
|
||||
->setOffset($offset);
|
||||
->setOffset($offset)
|
||||
->setIsBrowse(true);
|
||||
}
|
||||
|
||||
$results = $composite->loadResults();
|
||||
|
@ -142,9 +143,6 @@ final class PhabricatorTypeaheadModularDatasourceController
|
|||
|
||||
$items = array();
|
||||
foreach ($results as $result) {
|
||||
$token = PhabricatorTypeaheadTokenView::newFromTypeaheadResult(
|
||||
$result);
|
||||
|
||||
// Disable already-selected tokens.
|
||||
$disabled = isset($exclude[$result->getPHID()]);
|
||||
|
||||
|
@ -161,15 +159,14 @@ final class PhabricatorTypeaheadModularDatasourceController
|
|||
),
|
||||
pht('Select'));
|
||||
|
||||
$information = $this->renderBrowseResult($result, $button);
|
||||
|
||||
$items[] = phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'typeahead-browse-item grouped',
|
||||
),
|
||||
array(
|
||||
$token,
|
||||
$button,
|
||||
));
|
||||
$information);
|
||||
}
|
||||
|
||||
$markup = array(
|
||||
|
@ -250,6 +247,8 @@ final class PhabricatorTypeaheadModularDatasourceController
|
|||
->setRenderDialogAsDiv(true)
|
||||
->setTitle($source->getBrowseTitle())
|
||||
->appendChild($browser)
|
||||
->setResizeX(true)
|
||||
->setResizeY($frame_id)
|
||||
->addFooter($function_help)
|
||||
->addCancelButton('/', pht('Close'));
|
||||
}
|
||||
|
@ -350,4 +349,60 @@ final class PhabricatorTypeaheadModularDatasourceController
|
|||
->appendChild($view);
|
||||
}
|
||||
|
||||
private function renderBrowseResult(
|
||||
PhabricatorTypeaheadResult $result,
|
||||
$button) {
|
||||
|
||||
$class = array();
|
||||
$style = array();
|
||||
$separator = " \xC2\xB7 ";
|
||||
|
||||
$class[] = 'phabricator-main-search-typeahead-result';
|
||||
|
||||
$name = phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'result-name',
|
||||
),
|
||||
$result->getDisplayName());
|
||||
|
||||
$icon = $result->getIcon();
|
||||
$icon = id(new PHUIIconView())->setIcon($icon);
|
||||
|
||||
$attributes = $result->getAttributes();
|
||||
$attributes = phutil_implode_html($separator, $attributes);
|
||||
$attributes = array($icon, ' ', $attributes);
|
||||
|
||||
$closed = $result->getClosed();
|
||||
if ($closed) {
|
||||
$class[] = 'result-closed';
|
||||
$attributes = array($closed, $separator, $attributes);
|
||||
}
|
||||
|
||||
$attributes = phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'result-type',
|
||||
),
|
||||
$attributes);
|
||||
|
||||
$image = $result->getImageURI();
|
||||
if ($image) {
|
||||
$style[] = 'background-image: url('.$image.');';
|
||||
$class[] = 'has-image';
|
||||
}
|
||||
|
||||
return phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => implode(' ', $class),
|
||||
'style' => implode(' ', $style),
|
||||
),
|
||||
array(
|
||||
$button,
|
||||
$name,
|
||||
$attributes,
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -37,6 +37,7 @@ abstract class PhabricatorTypeaheadCompositeDatasource
|
|||
}
|
||||
|
||||
$stack = $this->getFunctionStack();
|
||||
$is_browse = $this->getIsBrowse();
|
||||
|
||||
$results = array();
|
||||
foreach ($this->getUsableDatasources() as $source) {
|
||||
|
@ -70,6 +71,10 @@ abstract class PhabricatorTypeaheadCompositeDatasource
|
|||
$source->setLimit($offset + $limit);
|
||||
}
|
||||
|
||||
if ($is_browse) {
|
||||
$source->setIsBrowse(true);
|
||||
}
|
||||
|
||||
$source_results = $source->loadResults();
|
||||
$source_results = $source->didLoadResults($source_results);
|
||||
$results[] = $source_results;
|
||||
|
|
|
@ -12,6 +12,7 @@ abstract class PhabricatorTypeaheadDatasource extends Phobject {
|
|||
private $limit;
|
||||
private $parameters = array();
|
||||
private $functionStack = array();
|
||||
private $isBrowse;
|
||||
|
||||
public function setLimit($limit) {
|
||||
$this->limit = $limit;
|
||||
|
@ -71,6 +72,15 @@ abstract class PhabricatorTypeaheadDatasource extends Phobject {
|
|||
return idx($this->parameters, $name, $default);
|
||||
}
|
||||
|
||||
public function setIsBrowse($is_browse) {
|
||||
$this->isBrowse = $is_browse;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getIsBrowse() {
|
||||
return $this->isBrowse;
|
||||
}
|
||||
|
||||
public function getDatasourceURI() {
|
||||
$uri = new PhutilURI('/typeahead/class/'.get_class($this).'/');
|
||||
$uri->setQueryParams($this->parameters);
|
||||
|
@ -199,7 +209,8 @@ abstract class PhabricatorTypeaheadDatasource extends Phobject {
|
|||
protected function newFunctionResult() {
|
||||
return id(new PhabricatorTypeaheadResult())
|
||||
->setTokenType(PhabricatorTypeaheadTokenView::TYPE_FUNCTION)
|
||||
->setIcon('fa-asterisk');
|
||||
->setIcon('fa-asterisk')
|
||||
->addAttribute(pht('Function'));
|
||||
}
|
||||
|
||||
public function newInvalidToken($name) {
|
||||
|
|
|
@ -17,6 +17,7 @@ final class PhabricatorTypeaheadResult extends Phobject {
|
|||
private $tokenType;
|
||||
private $unique;
|
||||
private $autocomplete;
|
||||
private $attributes = array();
|
||||
|
||||
public function setIcon($icon) {
|
||||
$this->icon = $icon;
|
||||
|
@ -188,4 +189,26 @@ final class PhabricatorTypeaheadResult extends Phobject {
|
|||
return null;
|
||||
}
|
||||
|
||||
public function getImageURI() {
|
||||
return $this->imageURI;
|
||||
}
|
||||
|
||||
public function getClosed() {
|
||||
return $this->closed;
|
||||
}
|
||||
|
||||
public function resetAttributes() {
|
||||
$this->attributes = array();
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAttributes() {
|
||||
return $this->attributes;
|
||||
}
|
||||
|
||||
public function addAttribute($attribute) {
|
||||
$this->attributes[] = $attribute;
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -134,7 +134,6 @@ final class PhabricatorEdgeEditor extends Phobject {
|
|||
$caught = $ex;
|
||||
}
|
||||
|
||||
|
||||
if ($caught) {
|
||||
$this->killTransactions();
|
||||
}
|
||||
|
|
|
@ -28,10 +28,20 @@ final class PhabricatorEdgesDestructionEngineExtension
|
|||
foreach ($edges as $type => $type_edges) {
|
||||
foreach ($type_edges as $src => $src_edges) {
|
||||
foreach ($src_edges as $dst => $edge) {
|
||||
$editor->removeEdge($edge['src'], $edge['type'], $edge['dst']);
|
||||
try {
|
||||
$editor->removeEdge($edge['src'], $edge['type'], $edge['dst']);
|
||||
} catch (Exception $ex) {
|
||||
// We can run into an exception while removing the edge if the
|
||||
// edge type no longer exists. This prevents us from figuring out
|
||||
// if there's an inverse type. Just ignore any errors here and
|
||||
// continue, since the best we can do is clean up all the edges
|
||||
// we still have information about. See T11201.
|
||||
phlog($ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$editor->save();
|
||||
}
|
||||
|
||||
|
|
|
@ -206,73 +206,73 @@ final class PhabricatorUSEnglishTranslation
|
|||
),
|
||||
),
|
||||
|
||||
'%s added %s blocking task(s): %s.' => array(
|
||||
'%s added %s subtask(s): %s.' => array(
|
||||
array(
|
||||
'%s added a blocking task: %3$s.',
|
||||
'%s added blocking tasks: %3$s.',
|
||||
'%s added a subtask: %3$s.',
|
||||
'%s added subtasks: %3$s.',
|
||||
),
|
||||
),
|
||||
|
||||
'%s added %s blocked task(s): %s.' => array(
|
||||
'%s added %s parent task(s): %s.' => array(
|
||||
array(
|
||||
'%s added a blocked task: %3$s.',
|
||||
'%s added blocked tasks: %3$s.',
|
||||
'%s added a parent task: %3$s.',
|
||||
'%s added parent tasks: %3$s.',
|
||||
),
|
||||
),
|
||||
|
||||
'%s removed %s blocking task(s): %s.' => array(
|
||||
'%s removed %s subtask(s): %s.' => array(
|
||||
array(
|
||||
'%s removed a blocking task: %3$s.',
|
||||
'%s removed blocking tasks: %3$s.',
|
||||
'%s removed a subtask: %3$s.',
|
||||
'%s removed subtasks: %3$s.',
|
||||
),
|
||||
),
|
||||
|
||||
'%s removed %s blocked task(s): %s.' => array(
|
||||
'%s removed %s parent task(s): %s.' => array(
|
||||
array(
|
||||
'%s removed a blocked task: %3$s.',
|
||||
'%s removed blocked tasks: %3$s.',
|
||||
'%s removed a parent task: %3$s.',
|
||||
'%s removed parent tasks: %3$s.',
|
||||
),
|
||||
),
|
||||
|
||||
'%s added %s blocking task(s) for %s: %s.' => array(
|
||||
'%s added %s subtask(s) for %s: %s.' => array(
|
||||
array(
|
||||
'%s added a blocking task for %3$s: %4$s.',
|
||||
'%s added blocking tasks for %3$s: %4$s.',
|
||||
'%s added a subtask for %3$s: %4$s.',
|
||||
'%s added subtasks for %3$s: %4$s.',
|
||||
),
|
||||
),
|
||||
|
||||
'%s added %s blocked task(s) for %s: %s.' => array(
|
||||
'%s added %s parent task(s) for %s: %s.' => array(
|
||||
array(
|
||||
'%s added a blocked task for %3$s: %4$s.',
|
||||
'%s added blocked tasks for %3$s: %4$s.',
|
||||
'%s added a parent task for %3$s: %4$s.',
|
||||
'%s added parent tasks for %3$s: %4$s.',
|
||||
),
|
||||
),
|
||||
|
||||
'%s removed %s blocking task(s) for %s: %s.' => array(
|
||||
'%s removed %s subtask(s) for %s: %s.' => array(
|
||||
array(
|
||||
'%s removed a blocking task for %3$s: %4$s.',
|
||||
'%s removed blocking tasks for %3$s: %4$s.',
|
||||
'%s removed a subtask for %3$s: %4$s.',
|
||||
'%s removed subtasks for %3$s: %4$s.',
|
||||
),
|
||||
),
|
||||
|
||||
'%s removed %s blocked task(s) for %s: %s.' => array(
|
||||
'%s removed %s parent task(s) for %s: %s.' => array(
|
||||
array(
|
||||
'%s removed a blocked task for %3$s: %4$s.',
|
||||
'%s removed blocked tasks for %3$s: %4$s.',
|
||||
'%s removed a parent task for %3$s: %4$s.',
|
||||
'%s removed parent tasks for %3$s: %4$s.',
|
||||
),
|
||||
),
|
||||
|
||||
'%s edited blocking task(s), added %s: %s; removed %s: %s.' =>
|
||||
'%s edited blocking tasks, added: %3$s; removed: %5$s.',
|
||||
'%s edited subtask(s), added %s: %s; removed %s: %s.' =>
|
||||
'%s edited subtasks, added: %3$s; removed: %5$s.',
|
||||
|
||||
'%s edited blocking task(s) for %s, added %s: %s; removed %s: %s.' =>
|
||||
'%s edited blocking tasks for %s, added: %4$s; removed: %6$s.',
|
||||
'%s edited subtask(s) for %s, added %s: %s; removed %s: %s.' =>
|
||||
'%s edited subtasks for %s, added: %4$s; removed: %6$s.',
|
||||
|
||||
'%s edited blocked task(s), added %s: %s; removed %s: %s.' =>
|
||||
'%s edited blocked tasks, added: %3$s; removed: %5$s.',
|
||||
'%s edited parent task(s), added %s: %s; removed %s: %s.' =>
|
||||
'%s edited parent tasks, added: %3$s; removed: %5$s.',
|
||||
|
||||
'%s edited blocked task(s) for %s, added %s: %s; removed %s: %s.' =>
|
||||
'%s edited blocked tasks for %s, added: %4$s; removed: %6$s.',
|
||||
'%s edited parent task(s) for %s, added %s: %s; removed %s: %s.' =>
|
||||
'%s edited parent tasks for %s, added: %4$s; removed: %6$s.',
|
||||
|
||||
'%s edited answer(s), added %s: %s; removed %d: %s.' =>
|
||||
'%s edited answers, added: %3$s; removed: %5$s.',
|
||||
|
|
|
@ -24,6 +24,8 @@ final class AphrontDialogView
|
|||
private $flush;
|
||||
private $validationException;
|
||||
private $objectList;
|
||||
private $resizeX;
|
||||
private $resizeY;
|
||||
|
||||
|
||||
const WIDTH_DEFAULT = 'default';
|
||||
|
@ -72,6 +74,24 @@ final class AphrontDialogView
|
|||
return $this->shortTitle;
|
||||
}
|
||||
|
||||
public function setResizeY($resize_y) {
|
||||
$this->resizeY = $resize_y;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getResizeY() {
|
||||
return $this->resizeY;
|
||||
}
|
||||
|
||||
public function setResizeX($resize_x) {
|
||||
$this->resizeX = $resize_x;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getResizeX() {
|
||||
return $this->resizeX;
|
||||
}
|
||||
|
||||
public function addSubmitButton($text = null) {
|
||||
if (!$text) {
|
||||
$text = pht('Okay');
|
||||
|
@ -347,6 +367,20 @@ final class AphrontDialogView
|
|||
$this->footers);
|
||||
}
|
||||
|
||||
$resize = null;
|
||||
if ($this->resizeX || $this->resizeY) {
|
||||
$resize = javelin_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'aphront-dialog-resize',
|
||||
'sigil' => 'jx-dialog-resize',
|
||||
'meta' => array(
|
||||
'resizeX' => $this->resizeX,
|
||||
'resizeY' => $this->resizeY,
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
$tail = null;
|
||||
if ($buttons || $footer) {
|
||||
$tail = phutil_tag(
|
||||
|
@ -357,6 +391,7 @@ final class AphrontDialogView
|
|||
array(
|
||||
$buttons,
|
||||
$footer,
|
||||
$resize,
|
||||
));
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ final class PhabricatorObjectSelectorDialog extends Phobject {
|
|||
private $searchURI;
|
||||
private $selectedFilter;
|
||||
private $excluded;
|
||||
private $initialPHIDs;
|
||||
|
||||
private $title;
|
||||
private $header;
|
||||
|
@ -77,6 +78,15 @@ final class PhabricatorObjectSelectorDialog extends Phobject {
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function setInitialPHIDs(array $initial_phids) {
|
||||
$this->initialPHIDs = $initial_phids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getInitialPHIDs() {
|
||||
return $this->initialPHIDs;
|
||||
}
|
||||
|
||||
public function buildDialog() {
|
||||
$user = $this->user;
|
||||
|
||||
|
@ -171,8 +181,14 @@ final class PhabricatorObjectSelectorDialog extends Phobject {
|
|||
$view = new PhabricatorHandleObjectSelectorDataView($handle);
|
||||
$handle_views[$phid] = $view->renderData();
|
||||
}
|
||||
|
||||
$dialog->addHiddenInput('phids', implode(';', array_keys($this->handles)));
|
||||
|
||||
$initial_phids = $this->getInitialPHIDs();
|
||||
if ($initial_phids) {
|
||||
$initial_phids = implode(', ', $initial_phids);
|
||||
$dialog->addHiddenInput('initialPHIDs', $initial_phids);
|
||||
}
|
||||
|
||||
Javelin::initBehavior(
|
||||
'phabricator-object-selector',
|
||||
|
@ -188,7 +204,10 @@ final class PhabricatorObjectSelectorDialog extends Phobject {
|
|||
'handles' => $handle_views,
|
||||
));
|
||||
|
||||
return $dialog;
|
||||
$dialog->setResizeX(true);
|
||||
$dialog->setResizeY($results_id);
|
||||
|
||||
return $dialog;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -21,6 +21,10 @@ final class PhabricatorActionListView extends AphrontView {
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function getID() {
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function render() {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
|
@ -44,13 +48,26 @@ final class PhabricatorActionListView extends AphrontView {
|
|||
|
||||
require_celerity_resource('phabricator-action-list-view-css');
|
||||
|
||||
$items = array();
|
||||
foreach ($actions as $action) {
|
||||
foreach ($action->getItems() as $item) {
|
||||
$items[] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
return phutil_tag(
|
||||
'ul',
|
||||
array(
|
||||
'class' => 'phabricator-action-list-view',
|
||||
'id' => $this->id,
|
||||
),
|
||||
$actions);
|
||||
$items);
|
||||
}
|
||||
|
||||
public function getDropdownMenuMetadata() {
|
||||
return array(
|
||||
'items' => (string)hsprintf('%s', $this),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -14,6 +14,10 @@ final class PhabricatorActionView extends AphrontView {
|
|||
private $metadata;
|
||||
private $selected;
|
||||
private $openInNewWindow;
|
||||
private $submenu = array();
|
||||
private $hidden;
|
||||
private $depth;
|
||||
private $id;
|
||||
|
||||
public function setSelected($selected) {
|
||||
$this->selected = $selected;
|
||||
|
@ -66,6 +70,10 @@ final class PhabricatorActionView extends AphrontView {
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function getName() {
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function setLabel($label) {
|
||||
$this->label = $label;
|
||||
return $this;
|
||||
|
@ -95,7 +103,60 @@ final class PhabricatorActionView extends AphrontView {
|
|||
return $this->openInNewWindow;
|
||||
}
|
||||
|
||||
public function getID() {
|
||||
if (!$this->id) {
|
||||
$this->id = celerity_generate_unique_node_id();
|
||||
}
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function setSubmenu(array $submenu) {
|
||||
$this->submenu = $submenu;
|
||||
|
||||
if (!$this->getHref()) {
|
||||
$this->setHref('#');
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getItems($depth = 0) {
|
||||
$items = array();
|
||||
|
||||
$items[] = $this;
|
||||
foreach ($this->submenu as $action) {
|
||||
foreach ($action->getItems($depth + 1) as $item) {
|
||||
$item
|
||||
->setHidden(true)
|
||||
->setDepth($depth + 1);
|
||||
|
||||
$items[] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
public function setHidden($hidden) {
|
||||
$this->hidden = $hidden;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getHidden() {
|
||||
return $this->hidden;
|
||||
}
|
||||
|
||||
public function setDepth($depth) {
|
||||
$this->depth = $depth;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDepth() {
|
||||
return $this->depth;
|
||||
}
|
||||
|
||||
public function render() {
|
||||
$caret_id = celerity_generate_unique_node_id();
|
||||
|
||||
$icon = null;
|
||||
if ($this->icon) {
|
||||
|
@ -155,6 +216,18 @@ final class PhabricatorActionView extends AphrontView {
|
|||
$target = null;
|
||||
}
|
||||
|
||||
if ($this->submenu) {
|
||||
$caret = javelin_tag(
|
||||
'span',
|
||||
array(
|
||||
'class' => 'caret-right',
|
||||
'id' => $caret_id,
|
||||
),
|
||||
'');
|
||||
} else {
|
||||
$caret = null;
|
||||
}
|
||||
|
||||
$item = javelin_tag(
|
||||
'a',
|
||||
array(
|
||||
|
@ -164,7 +237,7 @@ final class PhabricatorActionView extends AphrontView {
|
|||
'sigil' => $sigils,
|
||||
'meta' => $this->metadata,
|
||||
),
|
||||
array($icon, $this->name));
|
||||
array($icon, $this->name, $caret));
|
||||
}
|
||||
} else {
|
||||
$item = phutil_tag(
|
||||
|
@ -190,10 +263,47 @@ final class PhabricatorActionView extends AphrontView {
|
|||
$classes[] = 'phabricator-action-view-selected';
|
||||
}
|
||||
|
||||
return phutil_tag(
|
||||
if ($this->submenu) {
|
||||
$classes[] = 'phabricator-action-view-submenu';
|
||||
}
|
||||
|
||||
$style = array();
|
||||
|
||||
if ($this->hidden) {
|
||||
$style[] = 'display: none;';
|
||||
}
|
||||
|
||||
if ($this->depth) {
|
||||
$indent = ($this->depth * 16);
|
||||
$style[] = "margin-left: {$indent}px;";
|
||||
}
|
||||
|
||||
$sigil = null;
|
||||
$meta = null;
|
||||
|
||||
if ($this->submenu) {
|
||||
Javelin::initBehavior('phui-submenu');
|
||||
$sigil = 'phui-submenu';
|
||||
|
||||
$item_ids = array();
|
||||
foreach ($this->submenu as $subitem) {
|
||||
$item_ids[] = $subitem->getID();
|
||||
}
|
||||
|
||||
$meta = array(
|
||||
'itemIDs' => $item_ids,
|
||||
'caretID' => $caret_id,
|
||||
);
|
||||
}
|
||||
|
||||
return javelin_tag(
|
||||
'li',
|
||||
array(
|
||||
'id' => $this->getID(),
|
||||
'class' => implode(' ', $classes),
|
||||
'style' => implode(' ', $style),
|
||||
'sigil' => $sigil,
|
||||
'meta' => $meta,
|
||||
),
|
||||
$item);
|
||||
}
|
||||
|
|
|
@ -222,7 +222,7 @@ final class PhabricatorStandardPageView extends PhabricatorBarePageView
|
|||
}
|
||||
|
||||
if ($user) {
|
||||
if ($user->isLoggedIn()) {
|
||||
if ($user->isUserActivated()) {
|
||||
$offset = $user->getTimeZoneOffset();
|
||||
|
||||
$ignore_key = PhabricatorTimezoneIgnoreOffsetSetting::SETTINGKEY;
|
||||
|
|
|
@ -104,10 +104,19 @@ final class PHUIButtonView extends AphrontTagView {
|
|||
public function setDropdownMenu(PhabricatorActionListView $actions) {
|
||||
Javelin::initBehavior('phui-dropdown-menu');
|
||||
|
||||
$this->addSigil('phui-dropdown-menu');
|
||||
$this->setMetadata($actions->getDropdownMenuMetadata());
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setDropdownMenuID($id) {
|
||||
Javelin::initBehavior('phui-dropdown-menu');
|
||||
|
||||
$this->addSigil('phui-dropdown-menu');
|
||||
$this->setMetadata(
|
||||
array(
|
||||
'items' => $actions,
|
||||
'menuID' => $id,
|
||||
));
|
||||
|
||||
return $this;
|
||||
|
|
|
@ -24,6 +24,7 @@ final class PHUIHeaderView extends AphrontTagView {
|
|||
private $badges = array();
|
||||
private $href;
|
||||
private $actionList;
|
||||
private $actionListID;
|
||||
|
||||
public function setHeader($header) {
|
||||
$this->header = $header;
|
||||
|
@ -90,6 +91,11 @@ final class PHUIHeaderView extends AphrontTagView {
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function setActionListID($action_list_id) {
|
||||
$this->actionListID = $action_list_id;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setPolicyObject(PhabricatorPolicyInterface $object) {
|
||||
$this->policyObject = $object;
|
||||
return $this;
|
||||
|
@ -189,14 +195,20 @@ final class PHUIHeaderView extends AphrontTagView {
|
|||
|
||||
protected function getTagContent() {
|
||||
|
||||
if ($this->actionList) {
|
||||
if ($this->actionList || $this->actionListID) {
|
||||
$action_button = id(new PHUIButtonView())
|
||||
->setTag('a')
|
||||
->setText(pht('Actions'))
|
||||
->setHref('#')
|
||||
->setIcon('fa-bars')
|
||||
->addClass('phui-mobile-menu')
|
||||
->setDropdownMenu($this->actionList);
|
||||
->addClass('phui-mobile-menu');
|
||||
|
||||
if ($this->actionList) {
|
||||
$action_button->setDropdownMenu($this->actionList);
|
||||
} else if ($this->actionListID) {
|
||||
$action_button->setDropdownMenuID($this->actionListID);
|
||||
}
|
||||
|
||||
$this->addActionLink($action_button);
|
||||
}
|
||||
|
||||
|
|
|
@ -43,10 +43,7 @@ final class PHUIListItemView extends AphrontTagView {
|
|||
Javelin::initBehavior('phui-dropdown-menu');
|
||||
|
||||
$this->addSigil('phui-dropdown-menu');
|
||||
$this->setMetadata(
|
||||
array(
|
||||
'items' => $actions,
|
||||
));
|
||||
$this->setMetadata($actions->getDropdownMenuMetadata());
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
|
|
@ -327,9 +327,7 @@ final class PHUITimelineEventView extends AphrontView {
|
|||
'sigil' => $sigil,
|
||||
'aria-haspopup' => 'true',
|
||||
'aria-expanded' => 'false',
|
||||
'meta' => array(
|
||||
'items' => hsprintf('%s', $action_list),
|
||||
),
|
||||
'meta' => $action_list->getDropdownMenuMetadata(),
|
||||
),
|
||||
array(
|
||||
$aural,
|
||||
|
|
|
@ -114,7 +114,7 @@ final class PHUITwoColumnView extends AphrontTagView {
|
|||
$curtain = $this->getCurtain();
|
||||
if ($curtain) {
|
||||
$action_list = $curtain->getActionList();
|
||||
$this->header->setActionList($action_list);
|
||||
$this->header->setActionListID($action_list->getID());
|
||||
}
|
||||
|
||||
$header = phutil_tag_div(
|
||||
|
|
|
@ -48,6 +48,7 @@
|
|||
|
||||
.aphront-dialog-tail {
|
||||
border: none;
|
||||
position: relative;
|
||||
background: {$lightgreybackground};
|
||||
padding: 8px 16px;
|
||||
border-top: 1px solid {$thinblueborder};
|
||||
|
@ -55,6 +56,23 @@
|
|||
border-bottom-right-radius: 3px;
|
||||
}
|
||||
|
||||
.device .aphront-dialog-resize {
|
||||
/* No resizing on devices. */
|
||||
display: none;
|
||||
}
|
||||
|
||||
.aphront-dialog-resize {
|
||||
position: absolute;
|
||||
right: -4px;
|
||||
bottom: -4px;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
background-image: url(/rsrc/image/resize.png);
|
||||
background-size: 100%;
|
||||
cursor: nwse-resize;
|
||||
pointer-events: all;
|
||||
}
|
||||
|
||||
.aphront-dialog-foot {
|
||||
padding: 6px 0;
|
||||
float: left;
|
||||
|
|
|
@ -57,10 +57,26 @@ input.typeahead-browse-input {
|
|||
|
||||
.typeahead-browse-item button {
|
||||
float: right;
|
||||
margin: 2px 6px;
|
||||
margin: 8px 6px 0;
|
||||
}
|
||||
|
||||
.typeahead-browse-item a.jx-tokenizer-token {
|
||||
margin-top: 1px;
|
||||
margin-left: 6px;
|
||||
}
|
||||
|
||||
.typeahead-browse-item .phabricator-main-search-typeahead-result {
|
||||
margin: 2px 0;
|
||||
padding: 0 8px;
|
||||
}
|
||||
|
||||
.typeahead-browse-item .phabricator-main-search-typeahead-result.has-image {
|
||||
padding-left: 48px;
|
||||
}
|
||||
|
||||
.typeahead-browse-item
|
||||
.phabricator-main-search-typeahead-result.result-closed
|
||||
.result-name {
|
||||
text-decoration: line-through;
|
||||
color: {$lightgreytext};
|
||||
}
|
||||
|
|
|
@ -264,6 +264,32 @@ a.policy-control .caret {
|
|||
border-top-color: #000;
|
||||
}
|
||||
|
||||
.phabricator-action-view-submenu .caret-right {
|
||||
float: right;
|
||||
margin-top: 4px;
|
||||
margin-right: 6px;
|
||||
border-left-color: {$alphablue};
|
||||
}
|
||||
|
||||
.phabricator-action-view-submenu .caret {
|
||||
float: right;
|
||||
margin-top: 5px;
|
||||
margin-right: 4px;
|
||||
border-top: 7px solid {$lightgreytext};
|
||||
}
|
||||
|
||||
.phabricator-action-list-view .phabricator-action-view-submenu.phui-submenu-open
|
||||
.phabricator-action-view-item {
|
||||
background-color: rgba({$alphablue}, 0.07);
|
||||
color: {$sky};
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.phabricator-action-list-view .phabricator-action-view-submenu.phui-submenu-open
|
||||
.phabricator-action-view-item .phui-icon-view {
|
||||
color: {$sky};
|
||||
}
|
||||
|
||||
/* Icons */
|
||||
.button.has-icon {
|
||||
position: relative;
|
||||
|
|
|
@ -169,8 +169,9 @@
|
|||
|
||||
/* When tags appear in property lists, give them a little more vertical
|
||||
spacing. */
|
||||
.phui-property-list-view .phui-tag-view {
|
||||
.phui-property-list-value .phui-tag-view {
|
||||
margin: 2px 0;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.phui-property-list-has-actions .phui-property-list-properties-wrap {
|
||||
|
|
|
@ -417,3 +417,7 @@ a.phui-timeline-menu .phui-icon-view {
|
|||
.phui-timeline-badges .phui-badge-mini .phui-icon-view {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.phui-comment-preview-view {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
|
83
webroot/rsrc/externals/javelin/lib/Workflow.js
vendored
83
webroot/rsrc/externals/javelin/lib/Workflow.js
vendored
|
@ -157,6 +157,80 @@ JX.install('Workflow', {
|
|||
_getActiveWorkflow : function() {
|
||||
var stack = JX.Workflow._stack;
|
||||
return stack[stack.length - 1];
|
||||
},
|
||||
|
||||
_onresizestart: function(e) {
|
||||
var self = JX.Workflow;
|
||||
if (self._resizing) {
|
||||
return;
|
||||
}
|
||||
|
||||
var workflow = self._getActiveWorkflow();
|
||||
if (!workflow) {
|
||||
return;
|
||||
}
|
||||
|
||||
e.kill();
|
||||
|
||||
var form = JX.DOM.find(workflow._root, 'div', 'jx-dialog');
|
||||
var resize = e.getNodeData('jx-dialog-resize');
|
||||
var node_y = JX.$(resize.resizeY);
|
||||
|
||||
var dim = JX.Vector.getDim(form);
|
||||
dim.y = JX.Vector.getDim(node_y).y;
|
||||
|
||||
if (!form._minimumSize) {
|
||||
form._minimumSize = dim;
|
||||
}
|
||||
|
||||
self._resizing = {
|
||||
min: form._minimumSize,
|
||||
form: form,
|
||||
startPos: JX.$V(e),
|
||||
startDim: dim,
|
||||
resizeY: node_y,
|
||||
resizeX: resize.resizeX
|
||||
};
|
||||
},
|
||||
|
||||
_onmousemove: function(e) {
|
||||
var self = JX.Workflow;
|
||||
if (!self._resizing) {
|
||||
return;
|
||||
}
|
||||
|
||||
var spec = self._resizing;
|
||||
var form = spec.form;
|
||||
var min = spec.min;
|
||||
|
||||
var delta = JX.$V(e).add(-spec.startPos.x, -spec.startPos.y);
|
||||
var src_dim = spec.startDim;
|
||||
var dst_dim = JX.$V(src_dim.x + delta.x, src_dim.y + delta.y);
|
||||
|
||||
if (dst_dim.x < min.x) {
|
||||
dst_dim.x = min.x;
|
||||
}
|
||||
|
||||
if (dst_dim.y < min.y) {
|
||||
dst_dim.y = min.y;
|
||||
}
|
||||
|
||||
if (spec.resizeX) {
|
||||
JX.$V(dst_dim.x, null).setDim(form);
|
||||
}
|
||||
|
||||
if (spec.resizeY) {
|
||||
JX.$V(null, dst_dim.y).setDim(spec.resizeY);
|
||||
}
|
||||
},
|
||||
|
||||
_onmouseup: function() {
|
||||
var self = JX.Workflow;
|
||||
if (!self._resizing) {
|
||||
return;
|
||||
}
|
||||
|
||||
self._resizing = false;
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -220,6 +294,12 @@ JX.install('Workflow', {
|
|||
[],
|
||||
JX.Workflow._onsyntheticsubmit);
|
||||
|
||||
JX.DOM.listen(
|
||||
this._root,
|
||||
'mousedown',
|
||||
'jx-dialog-resize',
|
||||
JX.Workflow._onresizestart);
|
||||
|
||||
// Note that even in the presence of a content frame, we're doing
|
||||
// everything here at top level: dialogs are fully modal and cover
|
||||
// the entire window.
|
||||
|
@ -413,6 +493,9 @@ JX.install('Workflow', {
|
|||
}
|
||||
|
||||
JX.Stratcom.listen('keydown', null, close_dialog_when_user_presses_escape);
|
||||
|
||||
JX.Stratcom.listen('mousemove', null, JX.Workflow._onmousemove);
|
||||
JX.Stratcom.listen('mouseup', null, JX.Workflow._onmouseup);
|
||||
}
|
||||
|
||||
});
|
||||
|
|
BIN
webroot/rsrc/image/resize.png
Normal file
BIN
webroot/rsrc/image/resize.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 288 B |
|
@ -18,10 +18,21 @@ JX.behavior('phabricator-object-selector', function(config) {
|
|||
var query_timer = null;
|
||||
var query_delay = 50;
|
||||
|
||||
var phid_input = JX.DOM.find(
|
||||
// TODO: This is fairly grotesque, but the dialog has two different forms
|
||||
// inside it and there's no way to sigil the inputs in the "real" form right
|
||||
// now. Clean this up when the dialog as a whole gets cleaned up.
|
||||
|
||||
var inputs = JX.DOM.scry(
|
||||
JX.$(config.form),
|
||||
'input',
|
||||
'aphront-dialog-application-input');
|
||||
var phid_input;
|
||||
for (var ii = 0; ii < inputs.length; ii++) {
|
||||
if (inputs[ii].name == 'phids') {
|
||||
phid_input = inputs[ii];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var last_value = JX.$(config.query).value;
|
||||
|
||||
|
|
|
@ -16,17 +16,42 @@ JX.behavior('phui-dropdown-menu', function() {
|
|||
|
||||
e.kill();
|
||||
|
||||
var list = JX.$H(data.items).getFragment().firstChild;
|
||||
var list;
|
||||
var placeholder;
|
||||
if (data.items) {
|
||||
list = JX.$H(data.items).getFragment().firstChild;
|
||||
} else {
|
||||
list = JX.$(data.menuID);
|
||||
placeholder = JX.$N('span');
|
||||
}
|
||||
|
||||
var icon = e.getNode('phui-dropdown-menu');
|
||||
data.menu = new JX.PHUIXDropdownMenu(icon);
|
||||
data.menu.setContent(list);
|
||||
|
||||
data.menu.listen('open', function() {
|
||||
if (placeholder) {
|
||||
JX.DOM.replace(list, placeholder);
|
||||
}
|
||||
data.menu.setContent(list);
|
||||
});
|
||||
|
||||
data.menu.listen('close', function() {
|
||||
if (placeholder) {
|
||||
JX.DOM.replace(placeholder, list);
|
||||
}
|
||||
});
|
||||
|
||||
data.menu.open();
|
||||
|
||||
JX.DOM.listen(list, 'click', 'tag:a', function(e) {
|
||||
if (!e.isNormalClick()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (JX.Stratcom.pass()) {
|
||||
return;
|
||||
}
|
||||
|
||||
data.menu.close();
|
||||
});
|
||||
});
|
||||
|
|
44
webroot/rsrc/js/phui/behavior-phui-submenu.js
Normal file
44
webroot/rsrc/js/phui/behavior-phui-submenu.js
Normal file
|
@ -0,0 +1,44 @@
|
|||
/**
|
||||
* @provides javelin-behavior-phui-submenu
|
||||
* @requires javelin-behavior
|
||||
* javelin-stratcom
|
||||
* javelin-dom
|
||||
*/
|
||||
|
||||
JX.behavior('phui-submenu', function() {
|
||||
|
||||
JX.Stratcom.listen('click', 'phui-submenu', function(e) {
|
||||
if (!e.isNormalClick()) {
|
||||
return;
|
||||
}
|
||||
|
||||
var node = e.getNode('phui-submenu');
|
||||
var data = e.getNodeData('phui-submenu');
|
||||
|
||||
e.kill();
|
||||
|
||||
data.open = !data.open;
|
||||
|
||||
for (var ii = 0; ii < data.itemIDs.length; ii++) {
|
||||
var id = data.itemIDs[ii];
|
||||
var item = JX.$(id);
|
||||
if (data.open) {
|
||||
JX.DOM.show(item);
|
||||
} else {
|
||||
JX.DOM.hide(item);
|
||||
}
|
||||
|
||||
// Add a class so we can animate zany effects.
|
||||
JX.DOM.alterClass(item, 'phui-submenu-animate', data.open);
|
||||
}
|
||||
|
||||
JX.DOM.alterClass(node, 'phui-submenu-open', data.open);
|
||||
|
||||
// Toggle the caret from ">" to "V" when opening the menu, and back again
|
||||
// when closing it.
|
||||
var caret = JX.$(data.caretID);
|
||||
JX.DOM.alterClass(caret, 'caret', data.open);
|
||||
JX.DOM.alterClass(caret, 'caret-right', !data.open);
|
||||
});
|
||||
|
||||
});
|
|
@ -42,7 +42,7 @@ JX.install('PHUIXDropdownMenu', {
|
|||
JX.Stratcom.listen('keydown', null, JX.bind(this, this._onkey));
|
||||
},
|
||||
|
||||
events: ['open'],
|
||||
events: ['open', 'close'],
|
||||
|
||||
properties: {
|
||||
width: null,
|
||||
|
@ -83,6 +83,8 @@ JX.install('PHUIXDropdownMenu', {
|
|||
this._open = false;
|
||||
this._hide();
|
||||
|
||||
this.invoke('close');
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
|
|
Loading…
Reference in a new issue