1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-25 16:22:43 +01:00

(stable) Promote 2016 Week 8

This commit is contained in:
epriestley 2016-02-19 15:46:30 -08:00
commit ad53db0979
155 changed files with 2077 additions and 945 deletions

View file

@ -7,8 +7,8 @@
*/
return array(
'names' => array(
'core.pkg.css' => 'b59766ad',
'core.pkg.js' => 'd7daa6d8',
'core.pkg.css' => '7935f211',
'core.pkg.js' => '7d8faf57',
'darkconsole.pkg.js' => 'e7393ebb',
'differential.pkg.css' => '2de124c9',
'differential.pkg.js' => 'd0cd0df6',
@ -17,19 +17,19 @@ return array(
'maniphest.pkg.css' => '4845691a',
'maniphest.pkg.js' => '949a7498',
'rsrc/css/aphront/aphront-bars.css' => '231ac33c',
'rsrc/css/aphront/dark-console.css' => '6378ef3d',
'rsrc/css/aphront/dark-console.css' => 'f54bf286',
'rsrc/css/aphront/dialog-view.css' => 'b4334e08',
'rsrc/css/aphront/lightbox-attachment.css' => '7acac05d',
'rsrc/css/aphront/list-filter-view.css' => '5d6f0526',
'rsrc/css/aphront/multi-column.css' => 'fd18389d',
'rsrc/css/aphront/notification.css' => '9c279160',
'rsrc/css/aphront/notification.css' => '7f684b62',
'rsrc/css/aphront/panel-view.css' => '8427b78d',
'rsrc/css/aphront/phabricator-nav-view.css' => 'a24cb589',
'rsrc/css/aphront/phabricator-nav-view.css' => 'ac79a758',
'rsrc/css/aphront/table-view.css' => '6d01d468',
'rsrc/css/aphront/tokenizer.css' => '056da01b',
'rsrc/css/aphront/tooltip.css' => '7672b60f',
'rsrc/css/aphront/tooltip.css' => '1a07aea8',
'rsrc/css/aphront/typeahead-browse.css' => 'd8581d2c',
'rsrc/css/aphront/typeahead.css' => '0e403212',
'rsrc/css/aphront/typeahead.css' => 'd4f16145',
'rsrc/css/application/almanac/almanac.css' => 'dbb9b3af',
'rsrc/css/application/auth/auth.css' => '0877ed6e',
'rsrc/css/application/base/main-menu-view.css' => 'd00a795a',
@ -65,10 +65,10 @@ return array(
'rsrc/css/application/differential/revision-list.css' => 'f3c47d33',
'rsrc/css/application/differential/table-of-contents.css' => 'ae4b7a55',
'rsrc/css/application/diffusion/diffusion-icons.css' => '2941baf1',
'rsrc/css/application/diffusion/diffusion-readme.css' => '2106ea08',
'rsrc/css/application/diffusion/diffusion-readme.css' => '356a4f3c',
'rsrc/css/application/diffusion/diffusion-source.css' => '075ba788',
'rsrc/css/application/feed/feed.css' => 'ecd4ec57',
'rsrc/css/application/files/global-drag-and-drop.css' => '697324ad',
'rsrc/css/application/files/global-drag-and-drop.css' => '5c1b47c2',
'rsrc/css/application/flag/flag.css' => '5337623f',
'rsrc/css/application/harbormaster/harbormaster.css' => 'b0758ca5',
'rsrc/css/application/herald/herald-test.css' => 'a52e323e',
@ -81,10 +81,10 @@ return array(
'rsrc/css/application/owners/owners-path-editor.css' => '2f00933b',
'rsrc/css/application/paste/paste.css' => 'a5157c48',
'rsrc/css/application/people/people-profile.css' => '2473d929',
'rsrc/css/application/phame/phame.css' => '1dbbacf9',
'rsrc/css/application/phame/phame.css' => '4ca6fd6c',
'rsrc/css/application/pholio/pholio-edit.css' => '3ad9d1ee',
'rsrc/css/application/pholio/pholio-inline-comments.css' => '8e545e49',
'rsrc/css/application/pholio/pholio.css' => '95174bdd',
'rsrc/css/application/pholio/pholio.css' => 'ca89d380',
'rsrc/css/application/phortune/phortune-credit-card-form.css' => '8391eb02',
'rsrc/css/application/phortune/phortune.css' => '9149f103',
'rsrc/css/application/phrequent/phrequent.css' => 'ffc185ad',
@ -92,9 +92,9 @@ return array(
'rsrc/css/application/policy/policy-edit.css' => '815c66f7',
'rsrc/css/application/policy/policy-transaction-detail.css' => '82100a43',
'rsrc/css/application/policy/policy.css' => '957ea14c',
'rsrc/css/application/ponder/ponder-view.css' => '7b0df4da',
'rsrc/css/application/ponder/ponder-view.css' => 'b40dc156',
'rsrc/css/application/project/project-card-view.css' => '9418c97d',
'rsrc/css/application/project/project-view.css' => '4693497c',
'rsrc/css/application/project/project-view.css' => '83bb6654',
'rsrc/css/application/releeph/releeph-core.css' => '9b3c5733',
'rsrc/css/application/releeph/releeph-preview-branch.css' => 'b7a6f4a5',
'rsrc/css/application/releeph/releeph-request-differential-create-dialog.css' => '8d8b92cd',
@ -104,7 +104,7 @@ return array(
'rsrc/css/application/tokens/tokens.css' => '3d0f239e',
'rsrc/css/application/uiexample/example.css' => '528b19de',
'rsrc/css/core/core.css' => '5b3563c8',
'rsrc/css/core/remarkup.css' => 'e1c8b32f',
'rsrc/css/core/remarkup.css' => 'fc228f08',
'rsrc/css/core/syntax.css' => '9fd11da8',
'rsrc/css/core/z-index.css' => '5b6fcf3f',
'rsrc/css/diviner/diviner-shared.css' => 'aa3656aa',
@ -123,30 +123,30 @@ return array(
'rsrc/css/phui/phui-action-panel.css' => '91c7b835',
'rsrc/css/phui/phui-badge.css' => 'f25c3476',
'rsrc/css/phui/phui-big-info-view.css' => 'bd903741',
'rsrc/css/phui/phui-box.css' => '6e8ac7fd',
'rsrc/css/phui/phui-button.css' => 'd6ac72db',
'rsrc/css/phui/phui-box.css' => 'dd1294d3',
'rsrc/css/phui/phui-button.css' => 'edf464e9',
'rsrc/css/phui/phui-chart.css' => '6bf6f78e',
'rsrc/css/phui/phui-crumbs-view.css' => '79d536e5',
'rsrc/css/phui/phui-document-pro.css' => '8799acf7',
'rsrc/css/phui/phui-document-pro.css' => 'a8872307',
'rsrc/css/phui/phui-document-summary.css' => '9ca48bdf',
'rsrc/css/phui/phui-document.css' => '9c71d2bf',
'rsrc/css/phui/phui-feed-story.css' => '04aec08f',
'rsrc/css/phui/phui-fontkit.css' => '9cda225e',
'rsrc/css/phui/phui-form-view.css' => '4a1a0f5e',
'rsrc/css/phui/phui-form.css' => '0b98e572',
'rsrc/css/phui/phui-header-view.css' => 'd53cc835',
'rsrc/css/phui/phui-form.css' => 'aac1d51d',
'rsrc/css/phui/phui-header-view.css' => '50c5cb6a',
'rsrc/css/phui/phui-hovercard.css' => 'de1a2119',
'rsrc/css/phui/phui-icon-set-selector.css' => '1ab67aad',
'rsrc/css/phui/phui-icon.css' => '3f33ab57',
'rsrc/css/phui/phui-image-mask.css' => '5a8b09c8',
'rsrc/css/phui/phui-image-mask.css' => 'a8498f9c',
'rsrc/css/phui/phui-info-panel.css' => '27ea50a1',
'rsrc/css/phui/phui-info-view.css' => '6d7c3509',
'rsrc/css/phui/phui-list.css' => '9da2aa00',
'rsrc/css/phui/phui-object-box.css' => '407eaf5a',
'rsrc/css/phui/phui-object-item-list-view.css' => 'be31c3a7',
'rsrc/css/phui/phui-object-item-list-view.css' => '18b2ce8e',
'rsrc/css/phui/phui-pager.css' => 'bea33d23',
'rsrc/css/phui/phui-pinboard-view.css' => '2495140e',
'rsrc/css/phui/phui-profile-menu.css' => 'f709256c',
'rsrc/css/phui/phui-profile-menu.css' => '7e92a89a',
'rsrc/css/phui/phui-property-list-view.css' => '27b2849e',
'rsrc/css/phui/phui-remarkup-preview.css' => '1a8f2591',
'rsrc/css/phui/phui-segment-bar-view.css' => '46342871',
@ -154,10 +154,11 @@ return array(
'rsrc/css/phui/phui-status.css' => '888cedb8',
'rsrc/css/phui/phui-tag-view.css' => '9d5d4400',
'rsrc/css/phui/phui-timeline-view.css' => '2efceff8',
'rsrc/css/phui/phui-two-column-view.css' => 'c75bfc5b',
'rsrc/css/phui/workboards/phui-workboard.css' => 'e9e56029',
'rsrc/css/phui/phui-two-column-view.css' => '0763177e',
'rsrc/css/phui/workboards/phui-workboard-color.css' => 'ac6fe6a7',
'rsrc/css/phui/workboards/phui-workboard.css' => 'e6d89647',
'rsrc/css/phui/workboards/phui-workcard.css' => '3646fb96',
'rsrc/css/phui/workboards/phui-workpanel.css' => 'a78c0661',
'rsrc/css/phui/workboards/phui-workpanel.css' => '92197373',
'rsrc/css/sprite-login.css' => '60e8560e',
'rsrc/css/sprite-menu.css' => '9dd65b92',
'rsrc/css/sprite-tokens.css' => '4f399012',
@ -461,7 +462,7 @@ return array(
'rsrc/js/core/Notification.js' => 'ccf1cbf8',
'rsrc/js/core/Prefab.js' => 'e67df814',
'rsrc/js/core/ShapedRequest.js' => '7cbe244b',
'rsrc/js/core/TextAreaUtils.js' => '9e54692d',
'rsrc/js/core/TextAreaUtils.js' => '5813016a',
'rsrc/js/core/Title.js' => 'df5e11d2',
'rsrc/js/core/ToolTip.js' => '6323f942',
'rsrc/js/core/behavior-active-nav.js' => 'e379b58e',
@ -500,7 +501,7 @@ return array(
'rsrc/js/core/behavior-time-typeahead.js' => 'f80d6bf0',
'rsrc/js/core/behavior-toggle-class.js' => '5d7c9f33',
'rsrc/js/core/behavior-tokenizer.js' => 'b3a4b884',
'rsrc/js/core/behavior-tooltip.js' => '3ee3408b',
'rsrc/js/core/behavior-tooltip.js' => '42fcb747',
'rsrc/js/core/behavior-watch-anchor.js' => '9f36c42d',
'rsrc/js/core/behavior-workflow.js' => '0a3f3021',
'rsrc/js/core/phtize.js' => 'd254d646',
@ -517,15 +518,15 @@ return array(
'symbols' => array(
'almanac-css' => 'dbb9b3af',
'aphront-bars' => '231ac33c',
'aphront-dark-console-css' => '6378ef3d',
'aphront-dark-console-css' => 'f54bf286',
'aphront-dialog-view-css' => 'b4334e08',
'aphront-list-filter-view-css' => '5d6f0526',
'aphront-multi-column-view-css' => 'fd18389d',
'aphront-panel-view-css' => '8427b78d',
'aphront-table-view-css' => '6d01d468',
'aphront-tokenizer-control-css' => '056da01b',
'aphront-tooltip-css' => '7672b60f',
'aphront-typeahead-control-css' => '0e403212',
'aphront-tooltip-css' => '1a07aea8',
'aphront-typeahead-control-css' => 'd4f16145',
'auth-css' => '0877ed6e',
'bulk-job-css' => 'df9c1d4a',
'changeset-view-manager' => 'a2828756',
@ -550,13 +551,13 @@ return array(
'differential-revision-list-css' => 'f3c47d33',
'differential-table-of-contents-css' => 'ae4b7a55',
'diffusion-icons-css' => '2941baf1',
'diffusion-readme-css' => '2106ea08',
'diffusion-readme-css' => '356a4f3c',
'diffusion-source-css' => '075ba788',
'diviner-shared-css' => 'aa3656aa',
'font-aleo' => '8bdb2835',
'font-fontawesome' => 'c43323c5',
'font-lato' => 'c7ccd872',
'global-drag-and-drop-css' => '697324ad',
'global-drag-and-drop-css' => '5c1b47c2',
'harbormaster-css' => 'b0758ca5',
'herald-css' => '826075fa',
'herald-rule-editor' => '746ca158',
@ -647,7 +648,7 @@ return array(
'javelin-behavior-phabricator-reveal-content' => '60821bc7',
'javelin-behavior-phabricator-search-typeahead' => '06c32383',
'javelin-behavior-phabricator-show-older-transactions' => 'dbbf48b6',
'javelin-behavior-phabricator-tooltips' => '3ee3408b',
'javelin-behavior-phabricator-tooltips' => '42fcb747',
'javelin-behavior-phabricator-transaction-comment-form' => 'b23b49e6',
'javelin-behavior-phabricator-transaction-list' => '13c739ea',
'javelin-behavior-phabricator-watch-anchor' => '9f36c42d',
@ -760,21 +761,21 @@ return array(
'phabricator-keyboard-shortcut' => '1ae869f2',
'phabricator-keyboard-shortcut-manager' => 'c1700f6f',
'phabricator-main-menu-view' => 'd00a795a',
'phabricator-nav-view-css' => 'a24cb589',
'phabricator-nav-view-css' => 'ac79a758',
'phabricator-notification' => 'ccf1cbf8',
'phabricator-notification-css' => '9c279160',
'phabricator-notification-css' => '7f684b62',
'phabricator-notification-menu-css' => 'f31c0bde',
'phabricator-object-selector-css' => '85ee8ce6',
'phabricator-phtize' => 'd254d646',
'phabricator-prefab' => 'e67df814',
'phabricator-remarkup-css' => 'e1c8b32f',
'phabricator-remarkup-css' => 'fc228f08',
'phabricator-search-results-css' => '7dea472c',
'phabricator-shaped-request' => '7cbe244b',
'phabricator-side-menu-view-css' => '3a3d9f41',
'phabricator-slowvote-css' => 'da0afb1b',
'phabricator-source-code-view-css' => 'cbeef983',
'phabricator-standard-page-view' => 'e709f6d0',
'phabricator-textareautils' => '9e54692d',
'phabricator-textareautils' => '5813016a',
'phabricator-title' => 'df5e11d2',
'phabricator-tooltip' => '6323f942',
'phabricator-ui-example-css' => '528b19de',
@ -789,8 +790,8 @@ return array(
'phabricator-uiexample-reactor-sendclass' => '1def2711',
'phabricator-uiexample-reactor-sendproperties' => 'b1f0ccee',
'phabricator-zindex-css' => '5b6fcf3f',
'phame-css' => '1dbbacf9',
'pholio-css' => '95174bdd',
'phame-css' => '4ca6fd6c',
'pholio-css' => 'ca89d380',
'pholio-edit-css' => '3ad9d1ee',
'pholio-inline-comments-css' => '8e545e49',
'phortune-credit-card-form' => '2290aeef',
@ -801,8 +802,8 @@ return array(
'phui-action-panel-css' => '91c7b835',
'phui-badge-view-css' => 'f25c3476',
'phui-big-info-view-css' => 'bd903741',
'phui-box-css' => '6e8ac7fd',
'phui-button-css' => 'd6ac72db',
'phui-box-css' => 'dd1294d3',
'phui-button-css' => 'edf464e9',
'phui-calendar-css' => 'ccabe893',
'phui-calendar-day-css' => 'd1cf6f93',
'phui-calendar-list-css' => 'c1c7f338',
@ -811,27 +812,27 @@ return array(
'phui-crumbs-view-css' => '79d536e5',
'phui-document-summary-view-css' => '9ca48bdf',
'phui-document-view-css' => '9c71d2bf',
'phui-document-view-pro-css' => '8799acf7',
'phui-document-view-pro-css' => 'a8872307',
'phui-feed-story-css' => '04aec08f',
'phui-font-icon-base-css' => 'ecbbb4c2',
'phui-fontkit-css' => '9cda225e',
'phui-form-css' => '0b98e572',
'phui-form-css' => 'aac1d51d',
'phui-form-view-css' => '4a1a0f5e',
'phui-header-view-css' => 'd53cc835',
'phui-header-view-css' => '50c5cb6a',
'phui-hovercard' => '1bd28176',
'phui-hovercard-view-css' => 'de1a2119',
'phui-icon-set-selector-css' => '1ab67aad',
'phui-icon-view-css' => '3f33ab57',
'phui-image-mask-css' => '5a8b09c8',
'phui-image-mask-css' => 'a8498f9c',
'phui-info-panel-css' => '27ea50a1',
'phui-info-view-css' => '6d7c3509',
'phui-inline-comment-view-css' => '0fdb3667',
'phui-list-view-css' => '9da2aa00',
'phui-object-box-css' => '407eaf5a',
'phui-object-item-list-view-css' => 'be31c3a7',
'phui-object-item-list-view-css' => '18b2ce8e',
'phui-pager-css' => 'bea33d23',
'phui-pinboard-view-css' => '2495140e',
'phui-profile-menu-css' => 'f709256c',
'phui-profile-menu-css' => '7e92a89a',
'phui-property-list-view-css' => '27b2849e',
'phui-remarkup-preview-css' => '1a8f2591',
'phui-segment-bar-view-css' => '46342871',
@ -840,10 +841,11 @@ return array(
'phui-tag-view-css' => '9d5d4400',
'phui-theme-css' => 'ab7b848c',
'phui-timeline-view-css' => '2efceff8',
'phui-two-column-view-css' => 'c75bfc5b',
'phui-workboard-view-css' => 'e9e56029',
'phui-two-column-view-css' => '0763177e',
'phui-workboard-color-css' => 'ac6fe6a7',
'phui-workboard-view-css' => 'e6d89647',
'phui-workcard-view-css' => '3646fb96',
'phui-workpanel-view-css' => 'a78c0661',
'phui-workpanel-view-css' => '92197373',
'phuix-action-list-view' => 'b5c256b8',
'phuix-action-view' => '8cf6d262',
'phuix-autocomplete' => '9196fb06',
@ -853,9 +855,9 @@ return array(
'policy-css' => '957ea14c',
'policy-edit-css' => '815c66f7',
'policy-transaction-detail-css' => '82100a43',
'ponder-view-css' => '7b0df4da',
'ponder-view-css' => 'b40dc156',
'project-card-view-css' => '9418c97d',
'project-view-css' => '4693497c',
'project-view-css' => '83bb6654',
'releeph-core' => '9b3c5733',
'releeph-preview-branch' => 'b7a6f4a5',
'releeph-request-differential-create-dialog' => '8d8b92cd',
@ -1117,12 +1119,6 @@ return array(
'javelin-util',
'javelin-uri',
),
'3ee3408b' => array(
'javelin-behavior',
'javelin-behavior-device',
'javelin-stratcom',
'phabricator-tooltip',
),
'3f5d6dbf' => array(
'javelin-behavior',
'javelin-dom',
@ -1138,6 +1134,12 @@ return array(
'javelin-dom',
'javelin-request',
),
'42fcb747' => array(
'javelin-behavior',
'javelin-behavior-device',
'javelin-stratcom',
'phabricator-tooltip',
),
'44959b73' => array(
'javelin-util',
'javelin-uri',
@ -1273,6 +1275,11 @@ return array(
'javelin-request',
'javelin-util',
),
'5813016a' => array(
'javelin-install',
'javelin-dom',
'javelin-vector',
),
'59a7976a' => array(
'javelin-install',
'javelin-dom',
@ -1573,6 +1580,9 @@ return array(
'phuix-icon-view',
'phabricator-prefab',
),
92197373 => array(
'phui-workcard-view-css',
),
'93d0c9e3' => array(
'javelin-behavior',
'javelin-stratcom',
@ -1616,11 +1626,6 @@ return array(
'phabricator-phtize',
'changeset-view-manager',
),
'9e54692d' => array(
'javelin-install',
'javelin-dom',
'javelin-vector',
),
'9f36c42d' => array(
'javelin-behavior',
'javelin-stratcom',
@ -1659,9 +1664,6 @@ return array(
'javelin-install',
'javelin-dom',
),
'a78c0661' => array(
'phui-workcard-view-css',
),
'a80d0378' => array(
'javelin-behavior',
'javelin-stratcom',

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_owners.owners_package
ADD viewPolicy VARBINARY(64) NOT NULL;

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_owners.owners_package
ADD editPolicy VARBINARY(64) NOT NULL;

View file

@ -0,0 +1,2 @@
UPDATE {$NAMESPACE}_owners.owners_package
SET viewPolicy = 'users' WHERE viewPolicy = '';

View file

@ -0,0 +1,2 @@
UPDATE {$NAMESPACE}_owners.owners_package
SET editPolicy = 'users' WHERE editPolicy = '';

View file

@ -0,0 +1,4 @@
/* Make callsigns nullable, and thus optional. */
ALTER TABLE {$NAMESPACE}_repository.repository
CHANGE callsign callsign VARCHAR(32) COLLATE {$COLLATE_SORT};

View file

@ -32,14 +32,14 @@ $root = dirname(dirname(dirname(__FILE__)));
require_once $root.'/scripts/__init_script__.php';
if ($argc < 2) {
throw new Exception(pht('usage: commit-hook <callsign>'));
throw new Exception(pht('usage: commit-hook <repository>'));
}
$engine = new DiffusionCommitHookEngine();
$repository = id(new PhabricatorRepositoryQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withCallsigns(array($argv[1]))
->withIdentifiers(array($argv[1]))
->needProjectPHIDs(true)
->executeOne();
@ -62,8 +62,9 @@ if ($repository->isGit() || $repository->isHg()) {
if (!strlen($username)) {
throw new Exception(
pht(
'Usage: %s should be defined!',
DiffusionCommitHookEngine::ENV_USER));
'No Direct Pushes: You are pushing directly to a repository hosted '.
'by Phabricator. This will not work. See "No Direct Pushes" in the '.
'documentation for more information.'));
}
if ($repository->isHg()) {
@ -77,7 +78,7 @@ if ($repository->isGit() || $repository->isHg()) {
// specify the correct user; read this user out of the commit log.
if ($argc < 4) {
throw new Exception(pht('usage: commit-hook <callsign> <repo> <txn>'));
throw new Exception(pht('usage: commit-hook <repository> <repo> <txn>'));
}
$svn_repo = $argv[2];

View file

@ -6,7 +6,7 @@ require_once $root.'/scripts/__init_script__.php';
$args = new PhutilArgumentParser($argv);
$args->setSynopsis(<<<EOSYNOPSIS
**clear_repository_symbols.php** [__options__] __callsign__
**clear_repository_symbols.php** [__options__] __repository__
Clear repository symbols.
EOSYNOPSIS
@ -15,24 +15,26 @@ $args->parseStandardArguments();
$args->parse(
array(
array(
'name' => 'callsign',
'name' => 'repository',
'wildcard' => true,
),
));
$callsigns = $args->getArg('callsign');
if (count($callsigns) !== 1) {
$identifiers = $args->getArg('repository');
if (count($identifiers) !== 1) {
$args->printHelpAndExit();
}
$callsign = head($callsigns);
$identifier = head($identifiers);
$repository = id(new PhabricatorRepositoryQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withCallsigns($callsigns)
->withIdentifiers($identifiers)
->executeOne();
if (!$repository) {
echo pht("Repository '%s' does not exist.", $callsign);
echo tsprintf(
"%s\n",
pht('Repository "%s" does not exist.', $identifier));
exit(1);
}

View file

@ -6,7 +6,7 @@ require_once $root.'/scripts/__init_script__.php';
$args = new PhutilArgumentParser($argv);
$args->setSynopsis(<<<EOSYNOPSIS
**import_repository_symbols.php** [__options__] __callsign__ < symbols
**import_repository_symbols.php** [__options__] __repository__ < symbols
Import repository symbols (symbols are read from stdin).
EOSYNOPSIS
@ -35,24 +35,26 @@ $args->parse(
'be part of a single transaction.'),
),
array(
'name' => 'callsign',
'name' => 'repository',
'wildcard' => true,
),
));
$callsigns = $args->getArg('callsign');
if (count($callsigns) !== 1) {
$identifiers = $args->getArg('repository');
if (count($identifiers) !== 1) {
$args->printHelpAndExit();
}
$callsign = head($callsigns);
$identifier = head($identifiers);
$repository = id(new PhabricatorRepositoryQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withCallsigns($callsigns)
->withIdentifiers($identifiers)
->executeOne();
if (!$repository) {
echo pht("Repository '%s' does not exist.", $callsign);
echo tsprintf(
"%s\n",
pht('Repository "%s" does not exist.', $identifier));
exit(1);
}

View file

@ -689,7 +689,7 @@ phutil_register_library_map(array(
'DiffusionPreCommitRefRepositoryHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitRefRepositoryHeraldField.php',
'DiffusionPreCommitRefRepositoryProjectsHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitRefRepositoryProjectsHeraldField.php',
'DiffusionPreCommitRefTypeHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitRefTypeHeraldField.php',
'DiffusionPullEventGarbageCollector' => 'applications/diffusion/DiffusionPullEventGarbageCollector.php',
'DiffusionPullEventGarbageCollector' => 'applications/diffusion/garbagecollector/DiffusionPullEventGarbageCollector.php',
'DiffusionPushCapability' => 'applications/diffusion/capability/DiffusionPushCapability.php',
'DiffusionPushEventViewController' => 'applications/diffusion/controller/DiffusionPushEventViewController.php',
'DiffusionPushLogController' => 'applications/diffusion/controller/DiffusionPushLogController.php',
@ -2665,6 +2665,8 @@ phutil_register_library_map(array(
'PhabricatorOwnersCustomFieldStorage' => 'applications/owners/storage/PhabricatorOwnersCustomFieldStorage.php',
'PhabricatorOwnersCustomFieldStringIndex' => 'applications/owners/storage/PhabricatorOwnersCustomFieldStringIndex.php',
'PhabricatorOwnersDAO' => 'applications/owners/storage/PhabricatorOwnersDAO.php',
'PhabricatorOwnersDefaultEditCapability' => 'applications/owners/capability/PhabricatorOwnersDefaultEditCapability.php',
'PhabricatorOwnersDefaultViewCapability' => 'applications/owners/capability/PhabricatorOwnersDefaultViewCapability.php',
'PhabricatorOwnersDetailController' => 'applications/owners/controller/PhabricatorOwnersDetailController.php',
'PhabricatorOwnersEditController' => 'applications/owners/controller/PhabricatorOwnersEditController.php',
'PhabricatorOwnersListController' => 'applications/owners/controller/PhabricatorOwnersListController.php',
@ -2867,9 +2869,11 @@ phutil_register_library_map(array(
'PhabricatorProjectAddHeraldAction' => 'applications/project/herald/PhabricatorProjectAddHeraldAction.php',
'PhabricatorProjectApplication' => 'applications/project/application/PhabricatorProjectApplication.php',
'PhabricatorProjectArchiveController' => 'applications/project/controller/PhabricatorProjectArchiveController.php',
'PhabricatorProjectBoardBackgroundController' => 'applications/project/controller/PhabricatorProjectBoardBackgroundController.php',
'PhabricatorProjectBoardController' => 'applications/project/controller/PhabricatorProjectBoardController.php',
'PhabricatorProjectBoardDisableController' => 'applications/project/controller/PhabricatorProjectBoardDisableController.php',
'PhabricatorProjectBoardImportController' => 'applications/project/controller/PhabricatorProjectBoardImportController.php',
'PhabricatorProjectBoardManageController' => 'applications/project/controller/PhabricatorProjectBoardManageController.php',
'PhabricatorProjectBoardReorderController' => 'applications/project/controller/PhabricatorProjectBoardReorderController.php',
'PhabricatorProjectBoardViewController' => 'applications/project/controller/PhabricatorProjectBoardViewController.php',
'PhabricatorProjectCardView' => 'applications/project/view/PhabricatorProjectCardView.php',
@ -2967,6 +2971,7 @@ phutil_register_library_map(array(
'PhabricatorProjectViewController' => 'applications/project/controller/PhabricatorProjectViewController.php',
'PhabricatorProjectWatchController' => 'applications/project/controller/PhabricatorProjectWatchController.php',
'PhabricatorProjectWatcherListView' => 'applications/project/view/PhabricatorProjectWatcherListView.php',
'PhabricatorProjectWorkboardBackgroundColor' => 'applications/project/constants/PhabricatorProjectWorkboardBackgroundColor.php',
'PhabricatorProjectWorkboardProfilePanel' => 'applications/project/profilepanel/PhabricatorProjectWorkboardProfilePanel.php',
'PhabricatorProjectsEditEngineExtension' => 'applications/project/engineextension/PhabricatorProjectsEditEngineExtension.php',
'PhabricatorProjectsEditField' => 'applications/transactions/editfield/PhabricatorProjectsEditField.php',
@ -5706,7 +5711,7 @@ phutil_register_library_map(array(
'PHUIListView' => 'AphrontTagView',
'PHUIListViewTestCase' => 'PhabricatorTestCase',
'PHUIMainMenuView' => 'AphrontView',
'PHUIObjectBoxView' => 'AphrontView',
'PHUIObjectBoxView' => 'AphrontTagView',
'PHUIObjectItemListExample' => 'PhabricatorUIExample',
'PHUIObjectItemListView' => 'AphrontTagView',
'PHUIObjectItemView' => 'AphrontTagView',
@ -7036,6 +7041,8 @@ phutil_register_library_map(array(
'PhabricatorOwnersCustomFieldStorage' => 'PhabricatorCustomFieldStorage',
'PhabricatorOwnersCustomFieldStringIndex' => 'PhabricatorCustomFieldStringIndexStorage',
'PhabricatorOwnersDAO' => 'PhabricatorLiskDAO',
'PhabricatorOwnersDefaultEditCapability' => 'PhabricatorPolicyCapability',
'PhabricatorOwnersDefaultViewCapability' => 'PhabricatorPolicyCapability',
'PhabricatorOwnersDetailController' => 'PhabricatorOwnersController',
'PhabricatorOwnersEditController' => 'PhabricatorOwnersController',
'PhabricatorOwnersListController' => 'PhabricatorOwnersController',
@ -7293,9 +7300,11 @@ phutil_register_library_map(array(
'PhabricatorProjectAddHeraldAction' => 'PhabricatorProjectHeraldAction',
'PhabricatorProjectApplication' => 'PhabricatorApplication',
'PhabricatorProjectArchiveController' => 'PhabricatorProjectController',
'PhabricatorProjectBoardBackgroundController' => 'PhabricatorProjectBoardController',
'PhabricatorProjectBoardController' => 'PhabricatorProjectController',
'PhabricatorProjectBoardDisableController' => 'PhabricatorProjectBoardController',
'PhabricatorProjectBoardImportController' => 'PhabricatorProjectBoardController',
'PhabricatorProjectBoardManageController' => 'PhabricatorProjectBoardController',
'PhabricatorProjectBoardReorderController' => 'PhabricatorProjectBoardController',
'PhabricatorProjectBoardViewController' => 'PhabricatorProjectBoardController',
'PhabricatorProjectCardView' => 'AphrontTagView',
@ -7305,6 +7314,7 @@ phutil_register_library_map(array(
'PhabricatorApplicationTransactionInterface',
'PhabricatorPolicyInterface',
'PhabricatorDestructibleInterface',
'PhabricatorExtendedPolicyInterface',
),
'PhabricatorProjectColumnDetailController' => 'PhabricatorProjectBoardController',
'PhabricatorProjectColumnEditController' => 'PhabricatorProjectBoardController',
@ -7406,6 +7416,7 @@ phutil_register_library_map(array(
'PhabricatorProjectViewController' => 'PhabricatorProjectController',
'PhabricatorProjectWatchController' => 'PhabricatorProjectController',
'PhabricatorProjectWatcherListView' => 'PhabricatorProjectUserListView',
'PhabricatorProjectWorkboardBackgroundColor' => 'Phobject',
'PhabricatorProjectWorkboardProfilePanel' => 'PhabricatorProfilePanel',
'PhabricatorProjectsEditEngineExtension' => 'PhabricatorEditEngineExtension',
'PhabricatorProjectsEditField' => 'PhabricatorTokenizerEditField',

View file

@ -363,11 +363,7 @@ final class PhabricatorCalendarEventViewController
->getIconLabel($event->getIcon()));
if (strlen($event->getDescription())) {
$description = PhabricatorMarkupEngine::renderOneObject(
id(new PhabricatorMarkupOneOff())->setContent($event->getDescription()),
'default',
$viewer);
$description = new PHUIRemarkupView($viewer, $event->getDescription());
$properties->addSectionHeader(
pht('Description'),
PHUIPropertyListView::ICON_SUMMARY);

View file

@ -72,6 +72,12 @@ final class CelerityDefaultPostprocessor
'hoverselectedblue' => '#e6e9ee',
'borderinset' => 'inset 0 0 0 1px rgba(55,55,55,.15)',
// Alphas
'alphawhite' => '255,255,255',
'alphagrey' => '55,55,55',
'alphablue' => '71,87,120',
'alphablack' => '0,0,0',
// Base Greys
'lightgreyborder' => '#C7CCD9',
'greyborder' => '#A1A6B0',

View file

@ -37,7 +37,7 @@ final class PhabricatorConduitLogSearchEngine
return array(
id(new PhabricatorUsersSearchField())
->setKey('callerPHIDs')
->setLabel(pht('Methods'))
->setLabel(pht('Callers'))
->setAliases(array('caller', 'callers'))
->setDescription(pht('Find calls by specific users.')),
id(new PhabricatorSearchStringListField())

View file

@ -341,23 +341,14 @@ final class PhabricatorConfigWelcomeController
$header = id(new PHUIHeaderView())
->setHeader(pht('Welcome to Phabricator'));
$setup_header = PhabricatorMarkupEngine::renderOneObject(
id(new PhabricatorMarkupOneOff())
->setContent(pht('=Setup and Configuration')),
'default',
$viewer);
$setup_header = new PHUIRemarkupView(
$viewer, pht('=Setup and Configuration'));
$explore_header = PhabricatorMarkupEngine::renderOneObject(
id(new PhabricatorMarkupOneOff())
->setContent(pht('=Explore Phabricator')),
'default',
$viewer);
$explore_header = new PHUIRemarkupView(
$viewer, pht('=Explore Phabricator'));
$quick_header = PhabricatorMarkupEngine::renderOneObject(
id(new PhabricatorMarkupOneOff())
->setContent(pht('=Quick Start Guides')),
'default',
$viewer);
$quick_header = new PHUIRemarkupView(
$viewer, pht('=Quick Start Guide'));
return id(new PHUIDocumentView())
->setHeader($header)
@ -376,10 +367,7 @@ final class PhabricatorConfigWelcomeController
$icon = id(new PHUIIconView())
->setIcon($icon.' fa-2x');
$content = PhabricatorMarkupEngine::renderOneObject(
id(new PhabricatorMarkupOneOff())->setContent($content),
'default',
$viewer);
$content = new PHUIRemarkupView($viewer, $content);
$icon = phutil_tag(
'div',

View file

@ -132,11 +132,7 @@ final class PhabricatorCountdownViewController
$description = $countdown->getDescription();
if (strlen($description)) {
$description = PhabricatorMarkupEngine::renderOneObject(
id(new PhabricatorMarkupOneOff())->setContent($description),
'default',
$viewer);
$description = new PHUIRemarkupView($viewer, $description);
$view->addSectionHeader(
pht('Description'),
PHUIPropertyListView::ICON_SUMMARY);

View file

@ -44,12 +44,7 @@ final class DifferentialRevertPlanField
return null;
}
return PhabricatorMarkupEngine::renderOneObject(
id(new PhabricatorMarkupOneOff())
->setPreserveLinebreaks(true)
->setContent($this->getValue()),
'default',
$this->getViewer());
return new PHUIRemarkupView($this->getViewer(), $this->getValue());
}
public function shouldAppearInGlobalSearch() {

View file

@ -122,12 +122,7 @@ final class DifferentialSummaryField
return null;
}
return PhabricatorMarkupEngine::renderOneObject(
id(new PhabricatorMarkupOneOff())
->setPreserveLinebreaks(true)
->setContent($this->getValue()),
'default',
$this->getViewer());
return new PHUIRemarkupView($this->getViewer(), $this->getValue());
}
public function getApplicationTransactionRemarkupBlocks(

View file

@ -136,12 +136,7 @@ final class DifferentialTestPlanField
return null;
}
return PhabricatorMarkupEngine::renderOneObject(
id(new PhabricatorMarkupOneOff())
->setPreserveLinebreaks(true)
->setContent($this->getValue()),
'default',
$this->getViewer());
return new PHUIRemarkupView($this->getViewer(), $this->getValue());
}
public function getApplicationTransactionRemarkupBlocks(

View file

@ -64,7 +64,11 @@ final class PhabricatorDiffusionApplication extends PhabricatorApplication {
'(?:query/(?P<queryKey>[^/]+)/)?' => 'DiffusionPushLogListController',
'view/(?P<id>\d+)/' => 'DiffusionPushEventViewController',
),
'(?P<repositoryCallsign>[A-Z]+)/' => array(
'(?:'.
'(?P<repositoryCallsign>[A-Z]+)'.
'|'.
'(?P<repositoryID>[1-9]\d*)'.
')/' => array(
'' => 'DiffusionRepositoryController',
'repository/(?P<dblob>.*)' => 'DiffusionRepositoryController',
@ -115,8 +119,9 @@ final class PhabricatorDiffusionApplication extends PhabricatorApplication {
// catch-all for serving repositories over HTTP. We must accept
// requests without the trailing "/" because SVN commands don't
// necessarily include it.
'(?P<repositoryCallsign>[A-Z]+)(?:/.*)?' =>
'DiffusionRepositoryDefaultController',
'(?:(?P<repositoryCallsign>[A-Z]+)|(?P<repositoryID>[1-9]\d*))'.
'(?:/.*)?'
=> 'DiffusionRepositoryDefaultController',
'inline/' => array(
'edit/(?P<phid>[^/]+)/' => 'DiffusionInlineCommentController',

View file

@ -246,7 +246,16 @@ final class DiffusionBrowseQueryConduitAPIMethod
DiffusionBrowseResultSet::REASON_IS_FILE);
return $result;
}
$parts = explode('/', $remainder);
$name = reset($parts);
// If we've already seen this path component, we're looking at a file
// inside a directory we already processed. Just move on.
if (isset($results[$name])) {
continue;
}
if (count($parts) == 1) {
$type = DifferentialChangeType::FILE_NORMAL;
} else {
@ -254,7 +263,7 @@ final class DiffusionBrowseQueryConduitAPIMethod
}
if ($count >= $offset) {
$results[reset($parts)] = $type;
$results[$name] = $type;
}
$count++;

View file

@ -19,13 +19,19 @@ final class DiffusionBranchTableController extends DiffusionController {
$pager = id(new PHUIPagerView())
->readFromRequest($request);
// TODO: Add support for branches that contain commit
$branches = $this->callConduitWithDiffusionRequest(
'diffusion.branchquery',
array(
$params = array(
'offset' => $pager->getOffset(),
'limit' => $pager->getPageSize() + 1,
));
);
$contains = $drequest->getSymbolicCommit();
if (strlen($contains)) {
$params['contains'] = $contains;
}
$branches = $this->callConduitWithDiffusionRequest(
'diffusion.branchquery',
$params);
$branches = $pager->sliceResults($branches);
$branches = DiffusionRepositoryRef::loadAllFromDictionaries($branches);

View file

@ -15,20 +15,18 @@ final class DiffusionCommitBranchesController extends DiffusionController {
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
switch ($repository->getVersionControlSystem()) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
$branches = array();
break;
default:
$branches = $this->callConduitWithDiffusionRequest(
$branch_limit = 10;
$branches = DiffusionRepositoryRef::loadAllFromDictionaries(
$this->callConduitWithDiffusionRequest(
'diffusion.branchquery',
array(
'contains' => $drequest->getCommit(),
));
break;
}
'limit' => $branch_limit + 1,
)));
$has_more_branches = (count($branches) > $branch_limit);
$branches = array_slice($branches, 0, $branch_limit);
$branches = DiffusionRepositoryRef::loadAllFromDictionaries($branches);
$branch_links = array();
foreach ($branches as $branch) {
$branch_links[] = phutil_tag(
@ -43,6 +41,18 @@ final class DiffusionCommitBranchesController extends DiffusionController {
$branch->getShortName());
}
if ($has_more_branches) {
$branch_links[] = phutil_tag(
'a',
array(
'href' => $drequest->generateURI(
array(
'action' => 'branches',
)),
),
pht("More Branches\xE2\x80\xA6"));
}
return id(new AphrontAjaxResponse())
->setContent($branch_links ? implode(', ', $branch_links) : pht('None'));
}

View file

@ -16,11 +16,6 @@ final class DiffusionCommitTagsController extends DiffusionController {
$repository = $drequest->getRepository();
$tag_limit = 10;
switch ($repository->getVersionControlSystem()) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
$tags = array();
break;
default:
$tags = DiffusionRepositoryTag::newFromConduit(
$this->callConduitWithDiffusionRequest(
'diffusion.tagsquery',
@ -28,8 +23,7 @@ final class DiffusionCommitTagsController extends DiffusionController {
'commit' => $drequest->getCommit(),
'limit' => $tag_limit + 1,
)));
break;
}
$has_more_tags = (count($tags) > $tag_limit);
$tags = array_slice($tags, 0, $tag_limit);

View file

@ -64,6 +64,20 @@ abstract class DiffusionController extends PhabricatorController {
return new Aphront404Response();
}
// If the client is making a request like "/diffusion/1/...", but the
// repository has a different canonical path like "/diffusion/XYZ/...",
// redirect them to the canonical path.
$request_path = $request->getPath();
$repository = $drequest->getRepository();
$canonical_path = $repository->getCanonicalPath($request_path);
if ($canonical_path !== null) {
if ($canonical_path != $request_path) {
return id(new AphrontRedirectResponse())->setURI($canonical_path);
}
}
$this->diffusionRequest = $drequest;
return null;

View file

@ -290,10 +290,7 @@ final class DiffusionRepositoryController extends DiffusionController {
$description = $repository->getDetail('description');
if (strlen($description)) {
$description = PhabricatorMarkupEngine::renderOneObject(
$repository,
'description',
$user);
$description = new PHUIRemarkupView($user, $description);
$view->addSectionHeader(
pht('Description'), PHUIPropertyListView::ICON_SUMMARY);
$view->addTextContent($description);

View file

@ -134,7 +134,6 @@ final class DiffusionRepositoryCreateController
$type_name = PhabricatorRepositoryTransaction::TYPE_NAME;
$type_vcs = PhabricatorRepositoryTransaction::TYPE_VCS;
$type_activate = PhabricatorRepositoryTransaction::TYPE_ACTIVATE;
$type_local_path = PhabricatorRepositoryTransaction::TYPE_LOCAL_PATH;
$type_remote_uri = PhabricatorRepositoryTransaction::TYPE_REMOTE_URI;
$type_hosting = PhabricatorRepositoryTransaction::TYPE_HOSTING;
$type_http = PhabricatorRepositoryTransaction::TYPE_PROTOCOL_HTTP;
@ -150,14 +149,6 @@ final class DiffusionRepositoryCreateController
// If we're creating a new repository, set all this core stuff.
if ($is_create) {
$callsign = $form->getPage('name')
->getControl('callsign')->getValue();
// We must set this to a unique value to save the repository
// initially, and it's immutable, so we don't bother using
// transactions to apply this change.
$repository->setCallsign($callsign);
$xactions[] = id(clone $template)
->setTransactionType($type_name)
->setNewValue(
@ -179,16 +170,6 @@ final class DiffusionRepositoryCreateController
->setTransactionType($type_service)
->setNewValue($service->getPHID());
}
$default_local_path = PhabricatorEnv::getEnvConfig(
'repository.default-local-path');
$default_local_path = rtrim($default_local_path, '/');
$default_local_path = $default_local_path.'/'.$callsign.'/';
$xactions[] = id(clone $template)
->setTransactionType($type_local_path)
->setNewValue($default_local_path);
}
if ($is_init) {
@ -354,7 +335,7 @@ final class DiffusionRepositoryCreateController
}
/* -( Page: Name and Callsign )-------------------------------------------- */
/* -( Page: Name )--------------------------------------------------------- */
private function buildNamePage() {
@ -370,23 +351,7 @@ final class DiffusionRepositoryCreateController
->addControl(
id(new AphrontFormTextControl())
->setName('name')
->setLabel(pht('Name'))
->setCaption(pht('Human-readable repository name.')))
->addRemarkupInstructions(
pht(
'**Choose a "Callsign" for the repository.** This is a short, '.
'unique string which identifies commits elsewhere in Phabricator. '.
'For example, you might use `M` for your mobile app repository '.
'and `B` for your backend repository.'.
"\n\n".
'**Callsigns must be UPPERCASE**, and can not be edited after the '.
'repository is created. Generally, you should choose short '.
'callsigns.'))
->addControl(
id(new AphrontFormTextControl())
->setName('callsign')
->setLabel(pht('Callsign'))
->setCaption(pht('Short UPPERCASE identifier.')));
->setLabel(pht('Name')));
}
public function validateNamePage(PHUIFormPageView $page) {
@ -398,38 +363,7 @@ final class DiffusionRepositoryCreateController
pht('You must choose a name for this repository.'));
}
$c_call = $page->getControl('callsign');
$v_call = $c_call->getValue();
if (!strlen($v_call)) {
$c_call->setError(pht('Required'));
$page->addPageError(
pht('You must choose a callsign for this repository.'));
} else if (!preg_match('/^[A-Z]+\z/', $v_call)) {
$c_call->setError(pht('Invalid'));
$page->addPageError(
pht('The callsign must contain only UPPERCASE letters.'));
} else {
$exists = false;
try {
$repo = id(new PhabricatorRepositoryQuery())
->setViewer($this->getRequest()->getUser())
->withCallsigns(array($v_call))
->executeOne();
$exists = (bool)$repo;
} catch (PhabricatorPolicyException $ex) {
$exists = true;
}
if ($exists) {
$c_call->setError(pht('Not Unique'));
$page->addPageError(
pht(
'Another repository already uses that callsign. You must choose '.
'a unique callsign.'));
}
}
return $c_name->isValid() &&
$c_call->isValid();
return $c_name->isValid();
}

View file

@ -18,11 +18,13 @@ final class DiffusionRepositoryEditBasicController
$v_name = $repository->getName();
$v_desc = $repository->getDetail('description');
$v_slug = $repository->getRepositorySlug();
$v_callsign = $repository->getCallsign();
$v_projects = PhabricatorEdgeQuery::loadDestinationPHIDs(
$repository->getPHID(),
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST);
$e_name = true;
$e_slug = null;
$e_callsign = null;
$errors = array();
$validation_exception = null;
@ -31,6 +33,7 @@ final class DiffusionRepositoryEditBasicController
$v_desc = $request->getStr('description');
$v_projects = $request->getArr('projectPHIDs');
$v_slug = $request->getStr('slug');
$v_callsign = $request->getStr('callsign');
if (!strlen($v_name)) {
$e_name = pht('Required');
@ -47,6 +50,7 @@ final class DiffusionRepositoryEditBasicController
$type_desc = PhabricatorRepositoryTransaction::TYPE_DESCRIPTION;
$type_edge = PhabricatorTransactions::TYPE_EDGE;
$type_slug = PhabricatorRepositoryTransaction::TYPE_SLUG;
$type_callsign = PhabricatorRepositoryTransaction::TYPE_CALLSIGN;
$xactions[] = id(clone $template)
->setTransactionType($type_name)
@ -60,6 +64,10 @@ final class DiffusionRepositoryEditBasicController
->setTransactionType($type_slug)
->setNewValue($v_slug);
$xactions[] = id(clone $template)
->setTransactionType($type_callsign)
->setNewValue($v_callsign);
$xactions[] = id(clone $template)
->setTransactionType($type_edge)
->setMetadataValue(
@ -78,11 +86,16 @@ final class DiffusionRepositoryEditBasicController
try {
$editor->applyTransactions($repository, $xactions);
// The preferred edit URI may have changed if the callsign or slug
// were adjusted, so grab a fresh copy.
$edit_uri = $this->getRepositoryControllerURI($repository, 'edit/');
return id(new AphrontRedirectResponse())->setURI($edit_uri);
} catch (PhabricatorApplicationTransactionValidationException $ex) {
$validation_exception = $ex;
$e_slug = $ex->getShortMessage($type_slug);
$e_callsign = $ex->getShortMessage($type_callsign);
}
}
}
@ -106,6 +119,12 @@ final class DiffusionRepositoryEditBasicController
->setLabel(pht('Short Name'))
->setValue($v_slug)
->setError($e_slug))
->appendChild(
id(new AphrontFormTextControl())
->setName('callsign')
->setLabel(pht('Callsign'))
->setValue($v_callsign)
->setError($e_callsign))
->appendChild(
id(new PhabricatorRemarkupControl())
->setUser($viewer)

View file

@ -284,7 +284,12 @@ final class DiffusionRepositoryEditMainController
$repository->getVersionControlSystem());
$view->addProperty(pht('Type'), $type);
$view->addProperty(pht('Callsign'), $repository->getCallsign());
$callsign = $repository->getCallsign();
if (!strlen($callsign)) {
$callsign = phutil_tag('em', array(), pht('No Callsign'));
}
$view->addProperty(pht('Callsign'), $callsign);
$short_name = $repository->getRepositorySlug();
if ($short_name === null) {
@ -309,10 +314,7 @@ final class DiffusionRepositoryEditMainController
if (!strlen($description)) {
$description = phutil_tag('em', array(), pht('No description provided.'));
} else {
$description = PhabricatorMarkupEngine::renderOneObject(
$repository,
'description',
$viewer);
$description = new PHUIRemarkupView($viewer, $description);
}
$view->addTextContent($description);

View file

@ -262,17 +262,23 @@ final class DiffusionServeController extends DiffusionController {
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
$result = new PhabricatorVCSResponse(
500,
pht('This is not a Git repository.'));
pht(
'This repository ("%s") is not a Git repository.',
$repository->getDisplayName()));
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
$result = new PhabricatorVCSResponse(
500,
pht('This is not a Mercurial repository.'));
pht(
'This repository ("%s") is not a Mercurial repository.',
$repository->getDisplayName()));
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
$result = new PhabricatorVCSResponse(
500,
pht('This is not a Subversion repository.'));
pht(
'This repository ("%s") is not a Subversion repository.',
$repository->getDisplayName()));
break;
default:
$result = new PhabricatorVCSResponse(
@ -480,7 +486,9 @@ final class DiffusionServeController extends DiffusionController {
private function getRequestDirectoryPath(PhabricatorRepository $repository) {
$request = $this->getRequest();
$request_path = $request->getRequestURI()->getPath();
$base_path = preg_replace('@^/diffusion/[A-Z]+@', '', $request_path);
$info = PhabricatorRepository::parseRepositoryServicePath($request_path);
$base_path = $info['path'];
// For Git repositories, strip an optional directory component if it
// isn't the name of a known Git resource. This allows users to clone

View file

@ -10,6 +10,7 @@
*/
final class DiffusionCommitHookEngine extends Phobject {
const ENV_REPOSITORY = 'PHABRICATOR_REPOSITORY';
const ENV_USER = 'PHABRICATOR_USER';
const ENV_REMOTE_ADDRESS = 'PHABRICATOR_REMOTE_ADDRESS';
const ENV_REMOTE_PROTOCOL = 'PHABRICATOR_REMOTE_PROTOCOL';
@ -610,7 +611,7 @@ final class DiffusionCommitHookEngine extends Phobject {
$console = PhutilConsole::getConsole();
$env = array(
'PHABRICATOR_REPOSITORY' => $this->getRepository()->getCallsign(),
self::ENV_REPOSITORY => $this->getRepository()->getPHID(),
self::ENV_USER => $this->getViewer()->getUsername(),
self::ENV_REMOTE_PROTOCOL => $this->getRemoteProtocol(),
self::ENV_REMOTE_ADDRESS => $this->getRemoteAddress(),

View file

@ -243,10 +243,6 @@ abstract class DiffusionRequest extends Phobject {
return $this->repository;
}
public function getCallsign() {
return $this->getRepository()->getCallsign();
}
public function setPath($path) {
$this->path = $path;
return $this;

View file

@ -6,6 +6,7 @@ abstract class DiffusionSSHWorkflow extends PhabricatorSSHWorkflow {
private $repository;
private $hasWriteAccess;
private $proxyURI;
private $baseRequestPath;
public function getRepository() {
if (!$this->repository) {
@ -45,6 +46,10 @@ abstract class DiffusionSSHWorkflow extends PhabricatorSSHWorkflow {
abstract protected function identifyRepository();
abstract protected function executeRepositoryOperations();
protected function getBaseRequestPath() {
return $this->baseRequestPath;
}
protected function writeError($message) {
$this->getErrorChannel()->write($message);
return $this;
@ -149,25 +154,29 @@ abstract class DiffusionSSHWorkflow extends PhabricatorSSHWorkflow {
protected function loadRepositoryWithPath($path) {
$viewer = $this->getUser();
$regex = '@^/?diffusion/(?P<callsign>[A-Z]+)(?:/|\z)@';
$matches = null;
if (!preg_match($regex, $path, $matches)) {
$info = PhabricatorRepository::parseRepositoryServicePath($path);
if ($info === null) {
throw new Exception(
pht(
'Unrecognized repository path "%s". Expected a path like "%s".',
'Unrecognized repository path "%s". Expected a path like "%s" '.
'or "%s".',
$path,
'/diffusion/X/'));
'/diffusion/X/',
'/diffusion/123/'));
}
$callsign = $matches[1];
$identifier = $info['identifier'];
$base = $info['base'];
$this->baseRequestPath = $base;
$repository = id(new PhabricatorRepositoryQuery())
->setViewer($viewer)
->withCallsigns(array($callsign))
->withIdentifiers(array($identifier))
->executeOne();
if (!$repository) {
throw new Exception(
pht('No repository "%s" exists!', $callsign));
pht('No repository "%s" exists!', $identifier));
}
switch ($repository->getServeOverSSH()) {
@ -179,7 +188,9 @@ abstract class DiffusionSSHWorkflow extends PhabricatorSSHWorkflow {
case PhabricatorRepository::SERVE_OFF:
default:
throw new Exception(
pht('This repository is not available over SSH.'));
pht(
'This repository ("%s") is not available over SSH.',
$repository->getDisplayName()));
}
return $repository;

View file

@ -377,14 +377,12 @@ final class DiffusionSubversionServeSSHWorkflow
$repository = $this->getRepository();
$path = $this->getPathFromSubversionURI($uri_string);
$path = preg_replace(
'(^/diffusion/[A-Z]+)',
rtrim($repository->getLocalPath(), '/'),
$path);
$external_base = $this->getBaseRequestPath();
if (preg_match('(^/diffusion/[A-Z]+/\z)', $path)) {
$path = rtrim($path, '/');
}
// Replace "/diffusion/X" in the request with the repository local path,
// so "/diffusion/X/master/" becomes "/path/to/repository/X/master/".
$local_path = rtrim($repository->getLocalPath(), '/');
$path = $local_path.substr($path, strlen($external_base));
// NOTE: We are intentionally NOT removing username information from the
// URI. Subversion retains it over the course of the request and considers
@ -398,7 +396,7 @@ final class DiffusionSubversionServeSSHWorkflow
if ($this->externalBaseURI === null) {
$pre = (string)id(clone $uri)->setPath('');
$external_path = '/diffusion/'.$repository->getCallsign();
$external_path = $external_base;
$external_path = $this->normalizeSVNPath($external_path);
$this->externalBaseURI = $pre.$external_path;

View file

@ -109,11 +109,14 @@ final class DiffusionReadmeView extends DiffusionView {
$header = id(new PHUIHeaderView())
->setHeader($readme_name);
return id(new PHUIDocumentView())
$document = id(new PHUIDocumentViewPro())
->setFluid(true)
->appendChild($readme_content)
->addClass('diffusion-readme-view')
->setHeader($header);
->appendChild($readme_content);
return id(new PHUIObjectBoxView())
->setHeader($header)
->appendChild($document)
->addClass('diffusion-readme-view');
}
}

View file

@ -29,20 +29,12 @@ final class DivinerBookController extends DivinerController {
$book->getShortTitle(),
'/book/'.$book->getName().'/');
$action_button = id(new PHUIButtonView())
->setTag('a')
->setText(pht('Actions'))
->setHref('#')
->setIcon('fa-bars')
->addClass('phui-mobile-menu')
->setDropdownMenu($actions);
$header = id(new PHUIHeaderView())
->setHeader($book->getTitle())
->setUser($viewer)
->setPolicyObject($book)
->setEpoch($book->getDateModified())
->addActionLink($action_button);
->setActionList($actions);
// TODO: This could probably look better.
if ($book->getRepositoryPHID()) {
@ -94,11 +86,7 @@ final class DivinerBookController extends DivinerController {
$preface = $book->getPreface();
$preface_view = null;
if (strlen($preface)) {
$preface_view =
PhabricatorMarkupEngine::renderOneObject(
id(new PhabricatorMarkupOneOff())->setContent($preface),
'default',
$viewer);
$preface_view = new PHUIRemarkupView($viewer, $preface);
}
$document->appendChild($preface_view);

View file

@ -61,11 +61,7 @@ final class DivinerMainController extends DivinerController {
" %s\n\n",
'phabricator/ $ ./bin/diviner generate');
$text = PhabricatorMarkupEngine::renderOneObject(
id(new PhabricatorMarkupOneOff())->setContent($text),
'default',
$viewer);
$text = new PHUIRemarkupView($viewer, $text);
$document->appendChild($text);
}

View file

@ -27,7 +27,7 @@ final class DivinerGenerateWorkflow extends DivinerWorkflow {
),
array(
'name' => 'repository',
'param' => 'callsign',
'param' => 'identifier',
'help' => pht('Repository that the documentation belongs to.'),
),
));
@ -192,19 +192,19 @@ final class DivinerGenerateWorkflow extends DivinerWorkflow {
}
$publisher = newv($publisher_class, array());
$callsign = $args->getArg('repository');
$identifier = $args->getArg('repository');
$repository = null;
if ($callsign) {
if (strlen($identifier)) {
$repository = id(new PhabricatorRepositoryQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withCallsigns(array($callsign))
->withIdentifiers(array($identifier))
->executeOne();
if (!$repository) {
throw new PhutilArgumentUsageException(
pht(
"Repository '%s' does not exist.",
$callsign));
'Repository "%s" does not exist.',
$identifier));
}
$publisher->setRepositoryPHID($repository->getPHID());

View file

@ -111,10 +111,7 @@ final class PhabricatorAsanaConfigOptions
"The Asana Workspaces your linked account has access to are:\n\n%s",
$out);
return PhabricatorMarkupEngine::renderOneObject(
id(new PhabricatorMarkupOneOff())->setContent($out),
'default',
$viewer);
return new PHUIRemarkupView($viewer, $out);
}
private function renderContextualProjectDescription(
@ -155,10 +152,7 @@ final class PhabricatorAsanaConfigOptions
$out = implode("\n", $out);
return PhabricatorMarkupEngine::renderOneObject(
id(new PhabricatorMarkupOneOff())->setContent($out),
'default',
$viewer);
return new PHUIRemarkupView($viewer, $out);
}
}

View file

@ -98,11 +98,7 @@ final class FundInitiativeViewController
$description = $initiative->getDescription();
if (strlen($description)) {
$description = PhabricatorMarkupEngine::renderOneObject(
id(new PhabricatorMarkupOneOff())->setContent($description),
'default',
$viewer);
$description = new PHUIRemarkupView($viewer, $description);
$view->addSectionHeader(
pht('Description'), PHUIPropertyListView::ICON_SUMMARY);
$view->addTextContent($description);
@ -110,11 +106,7 @@ final class FundInitiativeViewController
$risks = $initiative->getRisks();
if (strlen($risks)) {
$risks = PhabricatorMarkupEngine::renderOneObject(
id(new PhabricatorMarkupOneOff())->setContent($risks),
'default',
$viewer);
$risks = new PHUIRemarkupView($viewer, $risks);
$view->addSectionHeader(
pht('Risks/Challenges'), 'fa-ambulance');
$view->addTextContent($risks);

View file

@ -181,16 +181,10 @@ final class HarbormasterBuildViewController
if ($step) {
$description = $step->getDescription();
if ($description) {
$rendered = PhabricatorMarkupEngine::renderOneObject(
id(new PhabricatorMarkupOneOff())
->setContent($description)
->setPreserveLinebreaks(true),
'default',
$viewer);
$description = new PHUIRemarkupView($viewer, $description);
$properties->addSectionHeader(
pht('Description'), PHUIPropertyListView::ICON_SUMMARY);
$properties->addTextContent($rendered);
$properties->addTextContent($description);
}
} else {
$target_box->setFormErrors(

View file

@ -258,11 +258,7 @@ final class LegalpadDocumentSignController extends LegalpadController {
$preamble_box = null;
if (strlen($document->getPreamble())) {
$preamble_text = PhabricatorMarkupEngine::renderOneObject(
id(new PhabricatorMarkupOneOff())->setContent(
$document->getPreamble()),
'default',
$viewer);
$preamble_text = new PHUIRemarkupView($viewer, $document->getPreamble());
// NOTE: We're avoiding `setObject()` here so we don't pick up extra UI
// elements like "Subscribers". This information is available on the

View file

@ -42,13 +42,6 @@ final class PhabricatorMacroApplication extends PhabricatorApplication {
);
}
public function getRemarkupRules() {
return array(
new PhabricatorIconRemarkupRule(),
new PhabricatorEmojiRemarkupRule(),
);
}
protected function getCustomCapabilities() {
return array(
PhabricatorMacroManageCapability::CAPABILITY => array(

View file

@ -289,8 +289,14 @@ final class ManiphestTransactionEditor
array($select_phids))
->execute();
$object_phids = mpull($board_tasks, 'getPHID');
$object_phids[] = $object_phid;
$board_tasks = mpull($board_tasks, null, 'getPHID');
$board_tasks[$object_phid] = $object;
// Make sure tasks are sorted by ID, so we lay out new positions in
// a consistent way.
$board_tasks = msort($board_tasks, 'getID');
$object_phids = array_keys($board_tasks);
$engine = id(new PhabricatorBoardLayoutEngine())
->setViewer($omnipotent_viewer)

View file

@ -104,14 +104,11 @@ final class PhabricatorApplicationDetailViewController
}
$overview = $application->getOverview();
if ($overview) {
if (strlen($overview)) {
$overview = new PHUIRemarkupView($viewer, $overview);
$properties->addSectionHeader(
pht('Overview'), PHUIPropertyListView::ICON_SUMMARY);
$properties->addTextContent(
PhabricatorMarkupEngine::renderOneObject(
id(new PhabricatorMarkupOneOff())->setContent($overview),
'default',
$viewer));
$properties->addTextContent($overview);
}
$descriptions = PhabricatorPolicyQuery::renderPolicyDescriptions(

View file

@ -117,10 +117,7 @@ final class PhabricatorApplicationEmailCommandsController
$crumbs->addTextCrumb($title);
$crumbs->setBorder(true);
$content_box = PhabricatorMarkupEngine::renderOneObject(
id(new PhabricatorMarkupOneOff())->setContent($content),
'default',
$viewer);
$content_box = new PHUIRemarkupView($viewer, $content);
$info_view = null;
if (!PhabricatorEnv::getEnvConfig('metamta.reply-handler-domain')) {

View file

@ -120,11 +120,7 @@ final class NuancePhabricatorFormSourceDefinition
PHUIPropertyListView $view) {
$complaint = $item->getNuanceProperty('complaint');
$complaint = PhabricatorMarkupEngine::renderOneObject(
id(new PhabricatorMarkupOneOff())->setContent($complaint),
'default',
$viewer);
$complaint = new PHUIRemarkupView($viewer, $complaint);
$view->addSectionHeader(
pht('Complaint'), 'fa-exclamation-circle');
$view->addTextContent($complaint);

View file

@ -54,4 +54,19 @@ final class PhabricatorOwnersApplication extends PhabricatorApplication {
);
}
protected function getCustomCapabilities() {
return array(
PhabricatorOwnersDefaultViewCapability::CAPABILITY => array(
'caption' => pht('Default view policy for newly created packages.'),
'template' => PhabricatorOwnersPackagePHIDType::TYPECONST,
'capability' => PhabricatorPolicyCapability::CAN_VIEW,
),
PhabricatorOwnersDefaultEditCapability::CAPABILITY => array(
'caption' => pht('Default edit policy for newly created packages.'),
'template' => PhabricatorOwnersPackagePHIDType::TYPECONST,
'capability' => PhabricatorPolicyCapability::CAN_EDIT,
),
);
}
}

View file

@ -0,0 +1,12 @@
<?php
final class PhabricatorOwnersDefaultEditCapability
extends PhabricatorPolicyCapability {
const CAPABILITY = 'owners.default.edit';
public function getCapabilityName() {
return pht('Default Edit Policy');
}
}

View file

@ -0,0 +1,16 @@
<?php
final class PhabricatorOwnersDefaultViewCapability
extends PhabricatorPolicyCapability {
const CAPABILITY = 'owners.default.view';
public function getCapabilityName() {
return pht('Default View Policy');
}
public function shouldAllowPublicPolicySetting() {
return true;
}
}

View file

@ -189,13 +189,10 @@ final class PhabricatorOwnersDetailController
$description = $package->getDescription();
if (strlen($description)) {
$description = new PHUIRemarkupView($viewer, $description);
$view->addSectionHeader(
pht('Description'), PHUIPropertyListView::ICON_SUMMARY);
$view->addTextContent(
$output = PhabricatorMarkupEngine::renderOneObject(
id(new PhabricatorMarkupOneOff())->setContent($description),
'default',
$viewer));
$view->addTextContent($description);
}
$view->invokeWillRenderEvent();
@ -211,8 +208,10 @@ final class PhabricatorOwnersDetailController
private function buildPackageActionView(PhabricatorOwnersPackage $package) {
$viewer = $this->getViewer();
// TODO: Implement this capability.
$can_edit = true;
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$package,
PhabricatorPolicyCapability::CAN_EDIT);
$id = $package->getID();
$edit_uri = $this->getApplicationURI("/edit/{$id}/");

View file

@ -12,8 +12,7 @@ final class PhabricatorOwnersPathsController
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
// TODO: Support this capability.
// PhabricatorPolicyCapability::CAN_EDIT,
PhabricatorPolicyCapability::CAN_EDIT,
))
->needPaths(true)
->executeOne();

View file

@ -21,6 +21,9 @@ final class PhabricatorOwnersPackageTransactionEditor
$types[] = PhabricatorOwnersPackageTransaction::TYPE_PATHS;
$types[] = PhabricatorOwnersPackageTransaction::TYPE_STATUS;
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
$types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
return $types;
}

View file

@ -18,6 +18,8 @@ final class PhabricatorOwnersPackage
protected $primaryOwnerPHID;
protected $mailKey;
protected $status;
protected $viewPolicy;
protected $editPolicy;
private $paths = self::ATTACHABLE;
private $owners = self::ATTACHABLE;
@ -27,8 +29,20 @@ final class PhabricatorOwnersPackage
const STATUS_ARCHIVED = 'archived';
public static function initializeNewPackage(PhabricatorUser $actor) {
$app = id(new PhabricatorApplicationQuery())
->setViewer($actor)
->withClasses(array('PhabricatorOwnersApplication'))
->executeOne();
$view_policy = $app->getPolicy(
PhabricatorOwnersDefaultViewCapability::CAPABILITY);
$edit_policy = $app->getPolicy(
PhabricatorOwnersDefaultEditCapability::CAPABILITY);
return id(new PhabricatorOwnersPackage())
->setAuditingEnabled(0)
->setViewPolicy($view_policy)
->setEditPolicy($edit_policy)
->attachPaths(array())
->setStatus(self::STATUS_ACTIVE)
->attachOwners(array())
@ -287,8 +301,12 @@ final class PhabricatorOwnersPackage
}
public function getPolicy($capability) {
// TODO: Implement proper policies.
return PhabricatorPolicies::POLICY_USER;
switch ($capability) {
case PhabricatorPolicyCapability::CAN_VIEW:
return $this->getViewPolicy();
case PhabricatorPolicyCapability::CAN_EDIT:
return $this->getEditPolicy();
}
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {

View file

@ -56,9 +56,11 @@ final class PhabricatorPeopleProfileViewController
$projects = $this->buildProjectsView($user);
$badges = $this->buildBadgesView($user);
require_celerity_resource('project-view-css');
$columns = id(new PHUITwoColumnView())
->addClass('project-view-badges')
$home = id(new PHUITwoColumnView())
->setHeader($header)
->addClass('project-view-home')
->setMainColumn(
array(
$properties,
@ -76,17 +78,6 @@ final class PhabricatorPeopleProfileViewController
$crumbs = $this->buildApplicationCrumbs();
$crumbs->setBorder(true);
require_celerity_resource('project-view-css');
$home = phutil_tag(
'div',
array(
'class' => 'project-view-home',
),
array(
$header,
$columns,
));
return $this->newPage()
->setTitle($user->getUsername())
->setNavigation($nav)
@ -115,7 +106,7 @@ final class PhabricatorPeopleProfileViewController
}
$view = id(new PHUIBoxView())
->setColor(PHUIBoxView::GREY)
->setBorder(true)
->appendChild($view)
->addClass('project-view-properties');
@ -174,7 +165,7 @@ final class PhabricatorPeopleProfileViewController
$box = id(new PHUIObjectBoxView())
->setHeader($header)
->appendChild($list)
->setBackground(PHUIBoxView::GREY);
->setBackground(PHUIObjectBoxView::GREY);
return $box;
}
@ -217,8 +208,9 @@ final class PhabricatorPeopleProfileViewController
$box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Badges'))
->addClass('project-view-badges')
->appendChild($flex)
->setBackground(PHUIBoxView::GREY);
->setBackground(PHUIObjectBoxView::GREY);
return $box;
}

View file

@ -118,10 +118,7 @@ final class PhameBlogManageController extends PhameBlogController {
$properties->invokeWillRenderEvent();
if (strlen($blog->getDescription())) {
$description = PhabricatorMarkupEngine::renderOneObject(
id(new PhabricatorMarkupOneOff())->setContent($blog->getDescription()),
'default',
$viewer);
$description = new PHUIRemarkupView($viewer, $description);
$properties->addSectionHeader(
pht('Description'),
PHUIPropertyListView::ICON_SUMMARY);

View file

@ -44,16 +44,7 @@ final class PhameBlogViewController extends PhameLiveController {
$header->setStatus($header_icon, $header_color, $header_name);
$actions = $this->renderActions($blog);
$action_button = id(new PHUIButtonView())
->setTag('a')
->setText(pht('Actions'))
->setHref('#')
->setIcon('fa-bars')
->addClass('phui-mobile-menu')
->setDropdownMenu($actions);
$header->addActionLink($action_button);
$header->setActionList($actions);
$header->setPolicyObject($blog);
}

View file

@ -24,17 +24,8 @@ final class PhamePostViewController
if (!$is_external) {
$actions = $this->renderActions($post);
$action_button = id(new PHUIButtonView())
->setTag('a')
->setText(pht('Actions'))
->setHref('#')
->setIcon('fa-bars')
->addClass('phui-mobile-menu')
->setDropdownMenu($actions);
$header->setPolicyObject($post);
$header->addActionLink($action_button);
$header->setActionList($actions);
}
$document = id(new PHUIDocumentViewPro())

View file

@ -52,6 +52,13 @@ final class PHUIHandleTagListView extends AphrontTagView {
protected function getTagContent() {
$handles = $this->handles;
// Remove any archived projects from the list.
foreach ($handles as $key => $handle) {
if ($handle->getStatus() == PhabricatorObjectHandle::STATUS_CLOSED) {
unset($handles[$key]);
}
}
// If the list is empty, we may render a "No Projects" tag.
if (!$handles) {
if (strlen($this->noDataString)) {

View file

@ -86,16 +86,13 @@ final class PholioInlineController extends PholioController {
),
$author_handle->renderLink());
$inline_content = new PHUIRemarkupView($viewer, $inline->getContent());
$comment_body = phutil_tag(
'div',
array(
'class' => 'pholio-inline-comment-body',
),
PhabricatorMarkupEngine::renderOneObject(
id(new PhabricatorMarkupOneOff())
->setContent($inline->getContent()),
'default',
$viewer));
$inline_content);
return $this->newDialog()
->setTitle(pht('Inline Comment'))

View file

@ -48,12 +48,7 @@ abstract class PhortuneCartController
return null;
}
$output = PhabricatorMarkupEngine::renderOneObject(
id(new PhabricatorMarkupOneOff())
->setPreserveLinebreaks(true)
->setContent($description),
'default',
$this->getViewer());
$output = new PHUIRemarkupView($this->getUser(), $description);
$box = id(new PHUIBoxView())
->addMargin(PHUI::MARGIN_LARGE)

View file

@ -136,11 +136,7 @@ final class PhortuneMerchantViewController
$description = $merchant->getDescription();
if (strlen($description)) {
$description = PhabricatorMarkupEngine::renderOneObject(
id(new PhabricatorMarkupOneOff())->setContent($description),
'default',
$viewer);
$description = new PHUIRemarkupView($viewer, $description);
$view->addSectionHeader(
pht('Description'), PHUIPropertyListView::ICON_SUMMARY);
$view->addTextContent($description);

View file

@ -203,19 +203,11 @@ final class PhrictionDocumentController
$crumbs->addCrumb($view);
}
$action_button = id(new PHUIButtonView())
->setTag('a')
->setText(pht('Actions'))
->setHref('#')
->setIcon('fa-bars')
->addClass('phui-mobile-menu')
->setDropdownMenu($actions);
$header = id(new PHUIHeaderView())
->setUser($viewer)
->setPolicyObject($document)
->setHeader($page_title)
->addActionLink($action_button);
->setActionList($actions);
if ($content) {
$header->setEpoch($content->getDateCreated());

View file

@ -134,16 +134,12 @@ final class PhabricatorPhurlURLViewController
$properties->invokeWillRenderEvent();
if (strlen($url->getDescription())) {
$description = PhabricatorMarkupEngine::renderOneObject(
id(new PhabricatorMarkupOneOff())->setContent($url->getDescription()),
'default',
$viewer);
$description = $url->getDescription();
if (strlen($description)) {
$description = new PHUIRemarkupView($viewer, $description);
$properties->addSectionHeader(
pht('Description'),
PHUIPropertyListView::ICON_SUMMARY);
$properties->addTextContent($description);
}

View file

@ -101,6 +101,9 @@ final class PhabricatorPolicyEditEngineExtension
if ($capability == PhabricatorPolicyCapability::CAN_VIEW) {
$type_space = PhabricatorTransactions::TYPE_SPACE;
if (isset($types[$type_space])) {
$space_phid = PhabricatorSpacesNamespaceQuery::getObjectSpacePHID(
$object);
$space_field = id(new PhabricatorSpaceEditField())
->setKey('spacePHID')
->setLabel(pht('Space'))
@ -114,7 +117,7 @@ final class PhabricatorPolicyEditEngineExtension
->setConduitDescription(
pht('Shift the object between spaces.'))
->setConduitTypeDescription(pht('New space PHID.'))
->setValue($object->getSpacePHID());
->setValue($space_phid);
$fields[] = $space_field;
$space_field->setPolicyField($policy_field);

View file

@ -44,7 +44,6 @@ final class PonderQuestionViewController extends PonderController {
$actions = $this->buildActionListView($question);
$properties = $this->buildPropertyListView($question, $actions);
$sidebar = $this->buildSidebar($question);
$content_id = celerity_generate_unique_node_id();
$timeline = $this->buildTransactionTimeline(
@ -81,20 +80,6 @@ final class PonderQuestionViewController extends PonderController {
->addPropertyList($properties)
->appendChild($footer);
if ($viewer->getPHID() == $question->getAuthorPHID()) {
$status = $question->getStatus();
$answers_list = $question->getAnswers();
if ($answers_list && ($status == PonderQuestionStatus::STATUS_OPEN)) {
$info_view = id(new PHUIInfoView())
->setSeverity(PHUIInfoView::SEVERITY_WARNING)
->appendChild(
pht(
'If this question has been resolved, please consider closing
the question and marking the answer as helpful.'));
$object_box->setInfoView($info_view);
}
}
$crumbs = $this->buildApplicationCrumbs($this->buildSideNavView());
$crumbs->addTextCrumb('Q'.$id, '/Q'.$id);
@ -107,21 +92,14 @@ final class PonderQuestionViewController extends PonderController {
->appendChild($answer);
}
$ponder_view = id(new PHUITwoColumnView())
->setMainColumn(array(
return $this->buildApplicationPage(
array(
$crumbs,
$object_box,
$comment_view,
$answer_wiki,
$answers,
$answer_add_panel,
))
->setSideColumn($sidebar)
->addClass('ponder-question-view');
return $this->buildApplicationPage(
array(
$crumbs,
$ponder_view,
),
array(
'title' => 'Q'.$question->getID().' '.$question->getTitle(),
@ -261,48 +239,4 @@ final class PonderQuestionViewController extends PonderController {
return $view;
}
private function buildSidebar(PonderQuestion $question) {
$viewer = $this->getViewer();
$status = $question->getStatus();
$id = $question->getID();
$questions = id(new PonderQuestionQuery())
->setViewer($viewer)
->withStatuses(array($status))
->withEdgeLogicPHIDs(
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST,
PhabricatorQueryConstraint::OPERATOR_OR,
$question->getProjectPHIDs())
->setLimit(10)
->execute();
$list = id(new PHUIObjectItemListView())
->setUser($viewer)
->setNoDataString(pht('No similar questions found.'));
foreach ($questions as $question) {
if ($id == $question->getID()) {
continue;
}
$item = new PHUIObjectItemView();
$item->setObjectName('Q'.$question->getID());
$item->setHeader($question->getTitle());
$item->setHref('/Q'.$question->getID());
$item->setObject($question);
$item->addAttribute(
pht(
'%s Answer(s)',
new PhutilNumber($question->getAnswerCount())));
$list->addItem($item);
}
$box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Similar Questions'))
->setObjectList($list);
return $box;
}
}

View file

@ -1013,6 +1013,51 @@ final class PhabricatorProjectCoreTestCase extends PhabricatorTestCase {
$this->assertColumns($expect, $user, $board, $task);
}
public function testColumnExtendedPolicies() {
$user = $this->createUser();
$user->save();
$board = $this->createProject($user);
$column = $this->addColumn($user, $board, 0);
// At first, the user should be able to view and edit the column.
$column = $this->refreshColumn($user, $column);
$this->assertTrue((bool)$column);
$can_edit = PhabricatorPolicyFilter::hasCapability(
$user,
$column,
PhabricatorPolicyCapability::CAN_EDIT);
$this->assertTrue($can_edit);
// Now, set the project edit policy to "Members of Project". This should
// disable editing.
$members_policy = id(new PhabricatorProjectMembersPolicyRule())
->getObjectPolicyFullKey();
$board->setEditPolicy($members_policy)->save();
$column = $this->refreshColumn($user, $column);
$this->assertTrue((bool)$column);
$can_edit = PhabricatorPolicyFilter::hasCapability(
$user,
$column,
PhabricatorPolicyCapability::CAN_EDIT);
$this->assertFalse($can_edit);
// Now, join the project. This should make the column editable again.
$this->joinProject($board, $user);
$column = $this->refreshColumn($user, $column);
$this->assertTrue((bool)$column);
$can_edit = PhabricatorPolicyFilter::hasCapability(
$user,
$column,
PhabricatorPolicyCapability::CAN_EDIT);
$this->assertTrue($can_edit);
}
private function moveToColumn(
PhabricatorUser $viewer,
PhabricatorProject $board,
@ -1251,6 +1296,22 @@ final class PhabricatorProjectCoreTestCase extends PhabricatorTestCase {
}
}
private function refreshColumn(
PhabricatorUser $viewer,
PhabricatorProjectColumn $column) {
$results = id(new PhabricatorProjectColumnQuery())
->setViewer($viewer)
->withIDs(array($column->getID()))
->execute();
if ($results) {
return head($results);
} else {
return null;
}
}
private function createProject(
PhabricatorUser $user,
PhabricatorProject $parent = null,

View file

@ -84,6 +84,10 @@ final class PhabricatorProjectApplication extends PhabricatorApplication {
=> 'PhabricatorProjectBoardReorderController',
'disable/'
=> 'PhabricatorProjectBoardDisableController',
'manage/'
=> 'PhabricatorProjectBoardManageController',
'background/'
=> 'PhabricatorProjectBoardBackgroundController',
),
'update/(?P<id>[1-9]\d*)/(?P<action>[^/]+)/'
=> 'PhabricatorProjectUpdateController',

View file

@ -0,0 +1,124 @@
<?php
final class PhabricatorProjectWorkboardBackgroundColor extends Phobject {
public static function getOptions() {
$options = array(
array(
'key' => '',
'name' => pht('Use Parent Background (Default)'),
'special' => 'parent',
'icon' => 'fa-chevron-circle-up',
'group' => 'basic',
),
array(
'key' => 'none',
'name' => pht('No Background'),
'special' => 'none',
'icon' => 'fa-ban',
'group' => 'basic',
),
array(
'key' => 'red',
'name' => pht('Red'),
),
array(
'key' => 'orange',
'name' => pht('Orange'),
),
array(
'key' => 'yellow',
'name' => pht('Yellow'),
),
array(
'key' => 'green',
'name' => pht('Green'),
),
array(
'key' => 'blue',
'name' => pht('Blue'),
),
array(
'key' => 'indigo',
'name' => pht('Indigo'),
),
array(
'key' => 'violet',
'name' => pht('Violet'),
),
array(
'key' => 'sky',
'name' => pht('Sky'),
),
array(
'key' => 'pink',
'name' => pht('Pink'),
),
array(
'key' => 'fire',
'name' => pht('Fire'),
),
array(
'key' => 'grey',
'name' => pht('Grey'),
),
array(
'key' => 'gradient-red',
'name' => pht('Ripe Peach'),
),
array(
'key' => 'gradient-orange',
'name' => pht('Ripe Orange'),
),
array(
'key' => 'gradient-yellow',
'name' => pht('Ripe Mango'),
),
array(
'key' => 'gradient-green',
'name' => pht('Shallows'),
),
array(
'key' => 'gradient-blue',
'name' => pht('Reef'),
),
array(
'key' => 'gradient-bluegrey',
'name' => pht('Depths'),
),
array(
'key' => 'gradient-indigo',
'name' => pht('This One Is Purple'),
),
array(
'key' => 'gradient-violet',
'name' => pht('Unripe Plum'),
),
array(
'key' => 'gradient-sky',
'name' => pht('Blue Sky'),
),
array(
'key' => 'gradient-pink',
'name' => pht('Intensity'),
),
array(
'key' => 'gradient-grey',
'name' => pht('Into The Expanse'),
),
);
foreach ($options as $key => $option) {
if (empty($option['group'])) {
if (preg_match('/^gradient/', $option['key'])) {
$option['group'] = 'gradient';
} else {
$option['group'] = 'solid';
}
}
$options[$key] = $option;
}
return ipull($options, null, 'key');
}
}

View file

@ -0,0 +1,168 @@
<?php
final class PhabricatorProjectBoardBackgroundController
extends PhabricatorProjectBoardController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getUser();
$board_id = $request->getURIData('projectID');
$board = id(new PhabricatorProjectQuery())
->setViewer($viewer)
->withIDs(array($board_id))
->needImages(true)
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$board) {
return new Aphront404Response();
}
if (!$board->getHasWorkboard()) {
return new Aphront404Response();
}
$this->setProject($board);
$id = $board->getID();
$manage_uri = $this->getApplicationURI("board/{$id}/manage/");
if ($request->isFormPost()) {
$background_key = $request->getStr('backgroundKey');
$xactions = array();
$xactions[] = id(new PhabricatorProjectTransaction())
->setTransactionType(PhabricatorProjectTransaction::TYPE_BACKGROUND)
->setNewValue($background_key);
id(new PhabricatorProjectTransactionEditor())
->setActor($viewer)
->setContentSourceFromRequest($request)
->setContinueOnNoEffect(true)
->setContinueOnMissingFields(true)
->applyTransactions($board, $xactions);
return id(new AphrontRedirectResponse())
->setURI($manage_uri);
}
$nav = $this->getProfileMenu();
$crumbs = id($this->buildApplicationCrumbs())
->addTextCrumb(pht('Workboard'), "/project/board/{$board_id}/")
->addTextCrumb(pht('Manage'), $manage_uri)
->addTextCrumb(pht('Background Color'));
$form = id(new AphrontFormView())
->setUser($viewer);
$group_info = array(
'basic' => array(
'label' => pht('Basics'),
),
'solid' => array(
'label' => pht('Solid Colors'),
),
'gradient' => array(
'label' => pht('Gradients'),
),
);
$groups = array();
$options = PhabricatorProjectWorkboardBackgroundColor::getOptions();
$option_groups = igroup($options, 'group');
require_celerity_resource('people-profile-css');
require_celerity_resource('phui-workboard-color-css');
Javelin::initBehavior('phabricator-tooltips', array());
foreach ($group_info as $group_key => $spec) {
$buttons = array();
$available_options = idx($option_groups, $group_key, array());
foreach ($available_options as $option) {
$buttons[] = $this->renderOptionButton($option);
}
$form->appendControl(
id(new AphrontFormMarkupControl())
->setLabel($spec['label'])
->setValue($buttons));
}
// NOTE: Each button is its own form, so we can't wrap them in a normal
// form.
$layout_view = $form->buildLayoutView();
$form_box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Edit Background Color'))
->appendChild($layout_view);
return $this->newPage()
->setTitle(
array(
pht('Edit Background Color'),
$board->getDisplayName(),
))
->setCrumbs($crumbs)
->setNavigation($nav)
->appendChild($form_box);
}
private function renderOptionButton(array $option) {
$viewer = $this->getViewer();
$icon = idx($option, 'icon');
if ($icon) {
$preview_class = null;
$preview_content = id(new PHUIIconView())
->setIcon($icon, 'lightbluetext');
} else {
$preview_class = 'phui-workboard-'.$option['key'];
$preview_content = null;
}
$preview = phutil_tag(
'div',
array(
'class' => 'phui-workboard-color-preview '.$preview_class,
),
$preview_content);
$button = javelin_tag(
'button',
array(
'class' => 'grey profile-image-button',
'sigil' => 'has-tooltip',
'meta' => array(
'tip' => $option['name'],
'size' => 300,
),
),
$preview);
$input = phutil_tag(
'input',
array(
'type' => 'hidden',
'name' => 'backgroundKey',
'value' => $option['key'],
));
return phabricator_form(
$viewer,
array(
'class' => 'profile-image-form',
'method' => 'POST',
),
array(
$button,
$input,
));
}
}

View file

@ -0,0 +1,174 @@
<?php
final class PhabricatorProjectBoardManageController
extends PhabricatorProjectBoardController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$board_id = $request->getURIData('projectID');
$board = id(new PhabricatorProjectQuery())
->setViewer($viewer)
->withIDs(array($board_id))
->needImages(true)
->executeOne();
if (!$board) {
return new Aphront404Response();
}
$this->setProject($board);
// Perform layout of no tasks to load and populate the columns in the
// correct order.
$layout_engine = id(new PhabricatorBoardLayoutEngine())
->setViewer($viewer)
->setBoardPHIDs(array($board->getPHID()))
->setObjectPHIDs(array())
->setFetchAllBoards(true)
->executeLayout();
$columns = $layout_engine->getColumns($board->getPHID());
$board_id = $board->getID();
$header = $this->buildHeaderView($board);
$actions = $this->buildActionView($board);
$properties = $this->buildPropertyView($board);
$properties->setActionList($actions);
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Workboard'), "/project/board/{$board_id}/");
$crumbs->addTextCrumb(pht('Manage'));
$box = id(new PHUIObjectBoxView())
->setHeader($header)
->addPropertyList($properties);
$nav = $this->getProfileMenu();
$title = array(
pht('Manage Workboard'),
$board->getDisplayName(),
);
$columns_list = $this->buildColumnsList($board, $columns);
return $this->newPage()
->setTitle($title)
->setNavigation($nav)
->setCrumbs($crumbs)
->appendChild(
array(
$box,
$columns_list,
));
}
private function buildHeaderView(PhabricatorProject $board) {
$viewer = $this->getRequest()->getUser();
$header = id(new PHUIHeaderView())
->setUser($viewer)
->setHeader(pht('Workboard: %s', $board->getDisplayName()));
return $header;
}
private function buildActionView(PhabricatorProject $board) {
$viewer = $this->getRequest()->getUser();
$id = $board->getID();
$actions = id(new PhabricatorActionListView())
->setUser($viewer);
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$board,
PhabricatorPolicyCapability::CAN_EDIT);
$reorder_uri = $this->getApplicationURI("board/{$id}/reorder/");
$actions->addAction(
id(new PhabricatorActionView())
->setIcon('fa-exchange')
->setName(pht('Reorder Columns'))
->setHref($reorder_uri)
->setDisabled(!$can_edit)
->setWorkflow(true));
$background_uri = $this->getApplicationURI("board/{$id}/background/");
$actions->addAction(
id(new PhabricatorActionView())
->setIcon('fa-paint-brush')
->setName(pht('Change Background Color'))
->setHref($background_uri)
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
$disable_uri = $this->getApplicationURI("board/{$id}/disable/");
$actions->addAction(
id(new PhabricatorActionView())
->setIcon('fa-ban')
->setName(pht('Disable Board'))
->setHref($disable_uri)
->setDisabled(!$can_edit)
->setWorkflow(true));
return $actions;
}
private function buildPropertyView(
PhabricatorProject $board) {
$viewer = $this->getRequest()->getUser();
$properties = id(new PHUIPropertyListView())
->setUser($viewer)
->setObject($board);
$background = $board->getDisplayWorkboardBackgroundColor();
if ($background !== null) {
$map = PhabricatorProjectWorkboardBackgroundColor::getOptions();
$map = ipull($map, 'name');
$name = idx($map, $background, $background);
$properties->addProperty(pht('Background Color'), $name);
}
return $properties;
}
private function buildColumnsList(
PhabricatorProject $board,
array $columns) {
assert_instances_of($columns, 'PhabricatorProjectColumn');
$board_id = $board->getID();
$view = id(new PHUIObjectItemListView())
->setNoDataString(pht('This board has no columns.'));
foreach ($columns as $column) {
$column_id = $column->getID();
$detail_uri = "/project/board/{$board_id}/column/{$column_id}/";
$item = id(new PHUIObjectItemView())
->setHeader($column->getDisplayName())
->setHref($detail_uri);
if ($column->isHidden()) {
$item->setDisabled(true);
}
$view->addItem($item);
}
return id(new PHUIObjectBoxView())
->setHeaderText(pht('Columns'))
->setObjectList($view);
}
}

View file

@ -23,13 +23,13 @@ final class PhabricatorProjectBoardReorderController
$this->setProject($project);
$project_id = $project->getID();
$board_uri = $this->getApplicationURI("board/{$project_id}/");
$manage_uri = $this->getApplicationURI("board/{$project_id}/manage/");
$reorder_uri = $this->getApplicationURI("board/{$project_id}/reorder/");
if ($request->isFormPost()) {
// User clicked "Done", make sure the page reloads to show the new
// column order.
return id(new AphrontRedirectResponse())->setURI($board_uri);
return id(new AphrontRedirectResponse())->setURI($manage_uri);
}
$columns = id(new PhabricatorProjectColumnQuery())

View file

@ -124,10 +124,14 @@ final class PhabricatorProjectBoardViewController
$board_phid = $project->getPHID();
// Regardless of display order, pass tasks to the layout engine in ID order
// so layout is consistent.
$board_tasks = msort($tasks, 'getID');
$layout_engine = id(new PhabricatorBoardLayoutEngine())
->setViewer($viewer)
->setBoardPHIDs(array($board_phid))
->setObjectPHIDs(array_keys($tasks))
->setObjectPHIDs(array_keys($board_tasks))
->setFetchAllBoards(true)
->executeLayout();
@ -427,7 +431,7 @@ final class PhabricatorProjectBoardViewController
$crumbs->addAction($manage_menu);
$crumbs->addAction($fullscreen);
return $this->newPage()
$page = $this->newPage()
->setTitle(
array(
$project->getDisplayName(),
@ -445,6 +449,17 @@ final class PhabricatorProjectBoardViewController
array(
$board_box,
));
$background = $project->getDisplayWorkboardBackgroundColor();
if ($background !== null) {
require_celerity_resource('phui-workboard-color-css');
$background_color_class = "phui-workboard-{$background}";
$page->addClass('phui-workboard-color');
$page->addClass($background_color_class);
}
return $page;
}
private function readRequestState() {
@ -671,9 +686,8 @@ final class PhabricatorProjectBoardViewController
$id = $project->getID();
$disable_uri = $this->getApplicationURI("board/{$id}/disable/");
$manage_uri = $this->getApplicationURI("board/{$id}/manage/");
$add_uri = $this->getApplicationURI("board/{$id}/edit/");
$reorder_uri = $this->getApplicationURI("board/{$id}/reorder/");
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
@ -687,14 +701,12 @@ final class PhabricatorProjectBoardViewController
->setName(pht('Add Column'))
->setHref($add_uri)
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit);
->setWorkflow(true);
$manage_items[] = id(new PhabricatorActionView())
->setIcon('fa-exchange')
->setName(pht('Reorder Columns'))
->setHref($reorder_uri)
->setDisabled(!$can_edit)
->setWorkflow(true);
->setIcon('fa-pencil')
->setName(pht('Manage Board'))
->setHref($manage_uri);
if ($show_hidden) {
$hidden_uri = $this->getURIWithState()
@ -726,13 +738,6 @@ final class PhabricatorProjectBoardViewController
->setHref($batch_edit_uri)
->setDisabled(!$can_batch_edit);
$manage_items[] = id(new PhabricatorActionView())
->setIcon('fa-ban')
->setName(pht('Disable Workboard'))
->setHref($disable_uri)
->setWorkflow(true)
->setDisabled(!$can_edit);
$manage_menu = id(new PhabricatorActionListView())
->setUser($viewer);
foreach ($manage_items as $item) {
@ -817,14 +822,6 @@ final class PhabricatorProjectBoardViewController
->setHref($batch_edit_uri)
->setDisabled(!$can_batch_edit);
$detail_uri = $this->getApplicationURI(
'board/'.$this->id.'/column/'.$column->getID().'/');
$column_items[] = id(new PhabricatorActionView())
->setIcon('fa-columns')
->setName(pht('Column Details'))
->setHref($detail_uri);
$can_hide = ($can_edit && !$column->isDefaultColumn());
$hide_uri = 'board/'.$this->id.'/hide/'.$column->getID().'/';
$hide_uri = $this->getApplicationURI($hide_uri);

View file

@ -73,8 +73,7 @@ final class PhabricatorProjectColumnDetailController
$header = id(new PHUIHeaderView())
->setUser($viewer)
->setHeader($column->getDisplayName())
->setPolicyObject($column);
->setHeader($column->getDisplayName());
if ($column->isHidden()) {
$header->setStatus('fa-ban', 'dark', pht('Hidden'));

View file

@ -136,21 +136,13 @@ final class PhabricatorProjectColumnEditController
$submit = pht('Save Column');
}
$form->appendChild(
id(new AphrontFormSubmitControl())
->setValue($submit)
->addCancelButton($view_uri));
$form_box = id(new PHUIObjectBoxView())
->setHeaderText($title)
->setValidationException($validation_exception)
->setForm($form);
$nav = $this->getProfileMenu();
return $this->newPage()
return $this->newDialog()
->setWidth(AphrontDialogView::WIDTH_FORM)
->setTitle($title)
->setNavigation($nav)
->appendChild($form_box);
->appendForm($form)
->setValidationException($validation_exception)
->addCancelButton($view_uri)
->addSubmitButton($submit);
}
}

View file

@ -24,6 +24,11 @@ final class PhabricatorProjectEditController
$id = $request->getURIData('id');
if (!$id) {
// This capability is checked again later, but checking it here
// explicitly gives us a better error message.
$this->requireApplicationCapability(
ProjectCreateProjectsCapability::CAPABILITY);
$parent_id = head($request->getArr('parent'));
if (!$parent_id) {
$parent_id = $request->getStr('parent');

View file

@ -59,14 +59,14 @@ final class PhabricatorProjectProfileController
->setUser($viewer)
->setProject($project)
->setLimit(5)
->setBackground(PHUIBoxView::GREY)
->setBackground(PHUIObjectBoxView::GREY)
->setUserPHIDs($project->getMemberPHIDs());
$watcher_list = id(new PhabricatorProjectWatcherListView())
->setUser($viewer)
->setProject($project)
->setLimit(5)
->setBackground(PHUIBoxView::GREY)
->setBackground(PHUIObjectBoxView::GREY)
->setUserPHIDs($project->getWatcherPHIDs());
$nav = $this->getProfileMenu();
@ -83,8 +83,11 @@ final class PhabricatorProjectProfileController
$feed = $this->renderStories($stories);
$feed = phutil_tag_div('project-view-feed', $feed);
require_celerity_resource('project-view-css');
$columns = id(new PHUITwoColumnView())
$home = id(new PHUITwoColumnView())
->setHeader($header)
->addClass('project-view-home')
->setMainColumn(
array(
$properties,
@ -101,17 +104,6 @@ final class PhabricatorProjectProfileController
$crumbs = $this->buildApplicationCrumbs();
$crumbs->setBorder(true);
require_celerity_resource('project-view-css');
$home = phutil_tag(
'div',
array(
'class' => 'project-view-home',
),
array(
$header,
$columns,
));
return $this->newPage()
->setNavigation($nav)
->setCrumbs($crumbs)
@ -142,7 +134,7 @@ final class PhabricatorProjectProfileController
}
$view = id(new PHUIBoxView())
->setColor(PHUIBoxView::GREY)
->setBorder(true)
->appendChild($view)
->addClass('project-view-properties');
@ -162,19 +154,32 @@ final class PhabricatorProjectProfileController
private function renderWatchAction(PhabricatorProject $project) {
$viewer = $this->getViewer();
$viewer_phid = $viewer->getPHID();
$id = $project->getID();
$is_watcher = ($viewer_phid && $project->isUserWatcher($viewer_phid));
if (!$viewer->isLoggedIn()) {
$is_watcher = false;
$is_ancestor = false;
} else {
$viewer_phid = $viewer->getPHID();
$is_watcher = $project->isUserWatcher($viewer_phid);
$is_ancestor = $project->isUserAncestorWatcher($viewer_phid);
}
if (!$is_watcher) {
if ($is_ancestor && !$is_watcher) {
$watch_icon = 'fa-eye';
$watch_text = pht('Watching Ancestor');
$watch_href = "/project/watch/{$id}/?via=profile";
$watch_disabled = true;
} else if (!$is_watcher) {
$watch_icon = 'fa-eye';
$watch_text = pht('Watch Project');
$watch_href = "/project/watch/{$id}/?via=profile";
$watch_disabled = false;
} else {
$watch_icon = 'fa-eye-slash';
$watch_text = pht('Unwatch Project');
$watch_href = "/project/unwatch/{$id}/?via=profile";
$watch_disabled = false;
}
$watch_icon = id(new PHUIIconView())
@ -185,7 +190,8 @@ final class PhabricatorProjectProfileController
->setWorkflow(true)
->setIcon($watch_icon)
->setText($watch_text)
->setHref($watch_href);
->setHref($watch_href)
->setDisabled($watch_disabled);
}
private function buildMilestoneList(PhabricatorProject $project) {
@ -205,7 +211,7 @@ final class PhabricatorProjectProfileController
array(
PhabricatorProjectStatus::STATUS_ACTIVE,
))
->setOrder('newest')
->setOrderVector(array('milestoneNumber', 'id'))
->execute();
if (!$milestones) {
return null;
@ -230,7 +236,7 @@ final class PhabricatorProjectProfileController
return id(new PHUIObjectBoxView())
->setHeader($header)
->setBackground(PHUIBoxView::GREY)
->setBackground(PHUIObjectBoxView::GREY)
->setObjectList($milestone_list);
}
@ -278,7 +284,7 @@ final class PhabricatorProjectProfileController
return id(new PHUIObjectBoxView())
->setHeader($header)
->setBackground(PHUIBoxView::GREY)
->setBackground(PHUIObjectBoxView::GREY)
->setObjectList($subproject_list);
}

View file

@ -43,7 +43,7 @@ final class PhabricatorProjectSubprojectsController
->withParentProjectPHIDs(array($project->getPHID()))
->needImages(true)
->withIsMilestone(true)
->setOrder('newest')
->setOrderVector(array('milestoneNumber', 'id'))
->execute();
} else {
$milestones = array();
@ -181,6 +181,9 @@ final class PhabricatorProjectSubprojectsController
$viewer = $this->getViewer();
$id = $project->getID();
$can_create = $this->hasApplicationCapability(
ProjectCreateProjectsCapability::CAPABILITY);
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$project,
@ -198,7 +201,7 @@ final class PhabricatorProjectSubprojectsController
$milestone_text = pht('Create Milestone');
}
$can_milestone = ($can_edit && $allows_milestones);
$can_milestone = ($can_create && $can_edit && $allows_milestones);
$milestone_href = "/project/edit/?milestone={$id}";
$view->addAction(
@ -209,7 +212,7 @@ final class PhabricatorProjectSubprojectsController
->setDisabled(!$can_milestone)
->setWorkflow(!$can_milestone));
$can_subproject = ($can_edit && $allows_subprojects);
$can_subproject = ($can_create && $can_edit && $allows_subprojects);
// If we're offering to create the first subproject, we're going to warn
// the user about the effects before moving forward.

View file

@ -25,6 +25,23 @@ final class PhabricatorProjectWatchController
$done_uri = "/project/members/{$id}/";
}
$is_watcher = $project->isUserWatcher($viewer->getPHID());
$is_ancestor = $project->isUserAncestorWatcher($viewer->getPHID());
if ($is_ancestor && !$is_watcher) {
$ancestor_phid = $project->getWatchedAncestorPHID($viewer->getPHID());
$handles = $viewer->loadHandles(array($ancestor_phid));
$ancestor_handle = $handles[$ancestor_phid];
return $this->newDialog()
->setTitle(pht('Watching Ancestor'))
->appendParagraph(
pht(
'You are already watching %s, an ancestor of this project, and '.
'are thus watching all of its subprojects.',
$ancestor_handle->renderTag()->render()))
->addCancelbutton($done_uri);
}
if ($request->isDialogFormPost()) {
$edge_action = null;
switch ($action) {
@ -61,10 +78,14 @@ final class PhabricatorProjectWatchController
switch ($action) {
case 'watch':
$title = pht('Watch Project?');
$body = pht(
$body = array();
$body[] = pht(
'Watching a project will let you monitor it closely. You will '.
'receive email and notifications about changes to every object '.
'associated with projects you watch.');
'tagged with projects you watch.');
$body[] = pht(
'Watching a project also watches all subprojects and milestones of '.
'that project.');
$submit = pht('Watch Project');
break;
case 'unwatch':
@ -78,12 +99,17 @@ final class PhabricatorProjectWatchController
return new Aphront404Response();
}
return $this->newDialog()
$dialog = $this->newDialog()
->setTitle($title)
->addHiddenInput('via', $via)
->appendParagraph($body)
->addCancelButton($done_uri)
->addSubmitButton($submit);
foreach ((array)$body as $paragraph) {
$dialog->appendParagraph($paragraph);
}
return $dialog;
}
}

View file

@ -42,6 +42,7 @@ final class PhabricatorProjectTransactionEditor
$types[] = PhabricatorProjectTransaction::TYPE_HASWORKBOARD;
$types[] = PhabricatorProjectTransaction::TYPE_DEFAULT_SORT;
$types[] = PhabricatorProjectTransaction::TYPE_DEFAULT_FILTER;
$types[] = PhabricatorProjectTransaction::TYPE_BACKGROUND;
return $types;
}
@ -77,6 +78,8 @@ final class PhabricatorProjectTransactionEditor
return $object->getDefaultWorkboardSort();
case PhabricatorProjectTransaction::TYPE_DEFAULT_FILTER:
return $object->getDefaultWorkboardFilter();
case PhabricatorProjectTransaction::TYPE_BACKGROUND:
return $object->getWorkboardBackgroundColor();
}
return parent::getCustomTransactionOldValue($object, $xaction);
@ -100,6 +103,12 @@ final class PhabricatorProjectTransactionEditor
return $xaction->getNewValue();
case PhabricatorProjectTransaction::TYPE_HASWORKBOARD:
return (int)$xaction->getNewValue();
case PhabricatorProjectTransaction::TYPE_BACKGROUND:
$value = $xaction->getNewValue();
if (!strlen($value)) {
return null;
}
return $value;
case PhabricatorProjectTransaction::TYPE_SLUGS:
return $this->normalizeSlugs($xaction->getNewValue());
}
@ -153,6 +162,9 @@ final class PhabricatorProjectTransactionEditor
case PhabricatorProjectTransaction::TYPE_DEFAULT_FILTER:
$object->setDefaultWorkboardFilter($xaction->getNewValue());
return;
case PhabricatorProjectTransaction::TYPE_BACKGROUND:
$object->setWorkboardBackgroundColor($xaction->getNewValue());
return;
}
return parent::applyCustomInternalTransaction($object, $xaction);
@ -198,6 +210,7 @@ final class PhabricatorProjectTransactionEditor
case PhabricatorProjectTransaction::TYPE_HASWORKBOARD:
case PhabricatorProjectTransaction::TYPE_DEFAULT_SORT:
case PhabricatorProjectTransaction::TYPE_DEFAULT_FILTER:
case PhabricatorProjectTransaction::TYPE_BACKGROUND:
return;
}

View file

@ -506,6 +506,7 @@ final class PhabricatorBoardLayoutEngine extends Phobject {
}
}
$view_sequence = 1;
foreach ($object_phids as $object_phid) {
$positions = idx($position_groups, $object_phid, array());
@ -554,7 +555,8 @@ final class PhabricatorBoardLayoutEngine extends Phobject {
->setBoardPHID($board_phid)
->setColumnPHID($proxy_hit)
->setObjectPHID($object_phid)
->setSequence(0);
->setSequence(0)
->setViewSequence($view_sequence++);
$this->addQueue[] = $new_position;
@ -578,7 +580,8 @@ final class PhabricatorBoardLayoutEngine extends Phobject {
->setBoardPHID($board_phid)
->setColumnPHID($default_phid)
->setObjectPHID($object_phid)
->setSequence(0);
->setSequence(0)
->setViewSequence($view_sequence++);
$this->addQueue[] = $new_position;

View file

@ -6,6 +6,7 @@ final class PhabricatorProjectQuery
private $ids;
private $phids;
private $memberPHIDs;
private $watcherPHIDs;
private $slugs;
private $slugNormals;
private $slugMap;
@ -62,6 +63,11 @@ final class PhabricatorProjectQuery
return $this;
}
public function withWatcherPHIDs(array $watcher_phids) {
$this->watcherPHIDs = $watcher_phids;
return $this;
}
public function withSlugs(array $slugs) {
$this->slugs = $slugs;
return $this;
@ -170,6 +176,11 @@ final class PhabricatorProjectQuery
'type' => 'string',
'unique' => true,
),
'milestoneNumber' => array(
'table' => $this->getPrimaryTableAlias(),
'column' => 'milestoneNumber',
'type' => 'int',
),
);
}
@ -242,7 +253,7 @@ final class PhabricatorProjectQuery
$all_graph = $this->getAllReachableAncestors($projects);
if ($this->needAncestorMembers) {
if ($this->needAncestorMembers || $this->needWatchers) {
$src_projects = $all_graph;
} else {
$src_projects = $projects;
@ -250,11 +261,13 @@ final class PhabricatorProjectQuery
$all_sources = array();
foreach ($src_projects as $project) {
// For milestones, we need parent members.
if ($project->isMilestone()) {
$phid = $project->getParentProjectPHID();
} else {
$phid = $project->getPHID();
$parent_phid = $project->getParentProjectPHID();
$all_sources[$parent_phid] = $parent_phid;
}
$phid = $project->getPHID();
$all_sources[$phid] = $phid;
}
@ -313,7 +326,7 @@ final class PhabricatorProjectQuery
if ($this->needWatchers) {
$watcher_phids = $edge_query->getDestinationPHIDs(
$source_phids,
array($project_phid),
array($watcher_type));
$project->attachWatcherPHIDs($watcher_phids);
$project->setIsUserWatcher(
@ -429,6 +442,13 @@ final class PhabricatorProjectQuery
$this->memberPHIDs);
}
if ($this->watcherPHIDs !== null) {
$where[] = qsprintf(
$conn,
'w.dst IN (%Ls)',
$this->watcherPHIDs);
}
if ($this->slugs !== null) {
$where[] = qsprintf(
$conn,
@ -542,7 +562,7 @@ final class PhabricatorProjectQuery
}
protected function shouldGroupQueryResultRows() {
if ($this->memberPHIDs || $this->nameTokens) {
if ($this->memberPHIDs || $this->watcherPHIDs || $this->nameTokens) {
return true;
}
return parent::shouldGroupQueryResultRows();
@ -559,6 +579,14 @@ final class PhabricatorProjectQuery
PhabricatorProjectMaterializedMemberEdgeType::EDGECONST);
}
if ($this->watcherPHIDs !== null) {
$joins[] = qsprintf(
$conn,
'JOIN %T w ON w.src = p.phid AND w.type = %d',
PhabricatorEdgeConfig::TABLE_NAME_EDGE,
PhabricatorObjectHasWatcherEdgeType::EDGECONST);
}
if ($this->slugs !== null) {
$joins[] = qsprintf(
$conn,

View file

@ -26,6 +26,10 @@ final class PhabricatorProjectSearchEngine
->setLabel(pht('Members'))
->setKey('memberPHIDs')
->setAliases(array('member', 'members')),
id(new PhabricatorUsersSearchField())
->setLabel(pht('Watchers'))
->setKey('watcherPHIDs')
->setAliases(array('watcher', 'watchers')),
id(new PhabricatorSearchSelectField())
->setLabel(pht('Status'))
->setKey('status')
@ -54,6 +58,10 @@ final class PhabricatorProjectSearchEngine
$query->withMemberPHIDs($map['memberPHIDs']);
}
if ($map['watcherPHIDs']) {
$query->withWatcherPHIDs($map['watcherPHIDs']);
}
if ($map['status']) {
$status = idx($this->getStatusValues(), $map['status']);
if ($status) {

View file

@ -287,6 +287,32 @@ final class PhabricatorProject extends PhabricatorProjectDAO
return $this->assertAttachedKey($this->sparseWatchers, $user_phid);
}
public function isUserAncestorWatcher($user_phid) {
$is_watcher = $this->isUserWatcher($user_phid);
if (!$is_watcher) {
$parent = $this->getParentProject();
if ($parent) {
return $parent->isUserWatcher($user_phid);
}
}
return $is_watcher;
}
public function getWatchedAncestorPHID($user_phid) {
if ($this->isUserWatcher($user_phid)) {
return $this->getPHID();
}
$parent = $this->getParentProject();
if ($parent) {
return $parent->getWatchedAncestorPHID($user_phid);
}
return null;
}
public function setIsUserWatcher($user_phid, $is_watcher) {
if ($this->sparseWatchers === self::ATTACHABLE) {
$this->sparseWatchers = array();
@ -304,6 +330,21 @@ final class PhabricatorProject extends PhabricatorProjectDAO
return $this->assertAttached($this->watcherPHIDs);
}
public function getAllAncestorWatcherPHIDs() {
$parent = $this->getParentProject();
if ($parent) {
$watchers = $parent->getAllAncestorWatcherPHIDs();
} else {
$watchers = array();
}
foreach ($this->getWatcherPHIDs() as $phid) {
$watchers[$phid] = $phid;
}
return $watchers;
}
public function attachSlugs(array $slugs) {
$this->slugs = $slugs;
return $this;
@ -579,6 +620,31 @@ final class PhabricatorProject extends PhabricatorProjectDAO
return $this->setProperty('workboard.filter.default', $filter);
}
public function getWorkboardBackgroundColor() {
return $this->getProperty('workboard.background');
}
public function setWorkboardBackgroundColor($color) {
return $this->setProperty('workboard.background', $color);
}
public function getDisplayWorkboardBackgroundColor() {
$color = $this->getWorkboardBackgroundColor();
if ($color === null) {
$parent = $this->getParentProject();
if ($parent) {
return $parent->getDisplayWorkboardBackgroundColor();
}
}
if ($color === 'none') {
$color = null;
}
return $color;
}
/* -( PhabricatorCustomFieldInterface )------------------------------------ */

View file

@ -5,7 +5,8 @@ final class PhabricatorProjectColumn
implements
PhabricatorApplicationTransactionInterface,
PhabricatorPolicyInterface,
PhabricatorDestructibleInterface {
PhabricatorDestructibleInterface,
PhabricatorExtendedPolicyInterface {
const STATUS_ACTIVE = 0;
const STATUS_HIDDEN = 1;
@ -169,14 +170,16 @@ final class PhabricatorProjectColumn
// Normal columns and subproject columns go first, in a user-controlled
// order.
// All the milestone columns go last, in their sequential order.
// All the milestone columns go last, in reverse order (newest on the
// left) so that you don't have to scroll across older milestones to get
// to the newest ones.
if (!$proxy || !$proxy->isMilestone()) {
$group = 'A';
$sequence = $this->getSequence();
} else {
$group = 'B';
$sequence = $proxy->getMilestoneNumber();
$sequence = (10000000 - $proxy->getMilestoneNumber());
}
return sprintf('%s%012d', $group, $sequence);
@ -217,7 +220,14 @@ final class PhabricatorProjectColumn
}
public function getPolicy($capability) {
return $this->getProject()->getPolicy($capability);
// NOTE: Column policies are enforced as an extended policy which makes
// them the same as the project's policies.
switch ($capability) {
case PhabricatorPolicyCapability::CAN_VIEW:
return PhabricatorPolicies::getMostOpenPolicy();
case PhabricatorPolicyCapability::CAN_EDIT:
return PhabricatorPolicies::POLICY_USER;
}
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
@ -231,6 +241,16 @@ final class PhabricatorProjectColumn
}
/* -( PhabricatorExtendedPolicyInterface )--------------------------------- */
public function getExtendedPolicy($capability, PhabricatorUser $viewer) {
return array(
array($this->getProject(), $capability),
);
}
/* -( PhabricatorDestructibleInterface )----------------------------------- */
public function destroyObjectPermanently(

View file

@ -9,6 +9,7 @@ final class PhabricatorProjectColumnPosition extends PhabricatorProjectDAO
protected $sequence;
private $column = self::ATTACHABLE;
private $viewSequence = 0;
protected function getConfiguration() {
return array(
@ -40,18 +41,30 @@ final class PhabricatorProjectColumnPosition extends PhabricatorProjectDAO
return $this;
}
public function getOrderingKey() {
if (!$this->getID() && !$this->getSequence()) {
return 0;
public function setViewSequence($view_sequence) {
$this->viewSequence = $view_sequence;
return $this;
}
// Low sequence numbers go above high sequence numbers.
// High position IDs go above low position IDs.
// Broadly, this makes newly added stuff float to the top.
public function getOrderingKey() {
// We're ordering both real positions and "virtual" positions which we have
// created but not saved yet.
// Low sequence numbers go above high sequence numbers. Virtual positions
// will have sequence number 0.
// High virtual sequence numbers go above low virtual sequence numbers.
// The layout engine gets objects in ID order, and this puts them in
// reverse ID order.
// High IDs go above low IDs.
// Broadly, this collectively makes newly added stuff float to the top.
return sprintf(
'~%012d%012d',
'~%012d%012d%012d',
$this->getSequence(),
((1 << 31) - $this->viewSequence),
((1 << 31) - $this->getID()));
}

View file

@ -15,6 +15,7 @@ final class PhabricatorProjectTransaction
const TYPE_HASWORKBOARD = 'project:hasworkboard';
const TYPE_DEFAULT_SORT = 'project:sort';
const TYPE_DEFAULT_FILTER = 'project:filter';
const TYPE_BACKGROUND = 'project:background';
// NOTE: This is deprecated, members are just a normal edge now.
const TYPE_MEMBERS = 'project:members';
@ -73,6 +74,7 @@ final class PhabricatorProjectTransaction
case self::TYPE_HASWORKBOARD:
case self::TYPE_DEFAULT_SORT:
case self::TYPE_DEFAULT_FILTER:
case self::TYPE_BACKGROUND:
return true;
}
@ -84,6 +86,7 @@ final class PhabricatorProjectTransaction
case self::TYPE_HASWORKBOARD:
case self::TYPE_DEFAULT_SORT:
case self::TYPE_DEFAULT_FILTER:
case self::TYPE_BACKGROUND:
return true;
}
@ -291,6 +294,11 @@ final class PhabricatorProjectTransaction
return pht(
'%s changed the default filter for the project workboard.',
$author_handle);
case self::TYPE_BACKGROUND:
return pht(
'%s changed the background color of the project workboard.',
$author_handle);
}
return parent::getTitle();

View file

@ -45,6 +45,7 @@ final class PhabricatorRepositoryEditor
$types[] = PhabricatorRepositoryTransaction::TYPE_SYMBOLS_SOURCES;
$types[] = PhabricatorRepositoryTransaction::TYPE_STAGING_URI;
$types[] = PhabricatorRepositoryTransaction::TYPE_AUTOMATION_BLUEPRINTS;
$types[] = PhabricatorRepositoryTransaction::TYPE_CALLSIGN;
$types[] = PhabricatorTransactions::TYPE_EDGE;
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
@ -110,6 +111,8 @@ final class PhabricatorRepositoryEditor
return $object->getDetail('staging-uri');
case PhabricatorRepositoryTransaction::TYPE_AUTOMATION_BLUEPRINTS:
return $object->getDetail('automation.blueprintPHIDs', array());
case PhabricatorRepositoryTransaction::TYPE_CALLSIGN:
return $object->getCallsign();
}
}
@ -148,6 +151,7 @@ final class PhabricatorRepositoryEditor
case PhabricatorRepositoryTransaction::TYPE_AUTOMATION_BLUEPRINTS:
return $xaction->getNewValue();
case PhabricatorRepositoryTransaction::TYPE_SLUG:
case PhabricatorRepositoryTransaction::TYPE_CALLSIGN:
$name = $xaction->getNewValue();
if (strlen($name)) {
return $name;
@ -240,6 +244,9 @@ final class PhabricatorRepositoryEditor
'automation.blueprintPHIDs',
$xaction->getNewValue());
return;
case PhabricatorRepositoryTransaction::TYPE_CALLSIGN:
$object->setCallsign($xaction->getNewValue());
return;
case PhabricatorRepositoryTransaction::TYPE_ENCODING:
// Make sure the encoding is valid by converting to UTF-8. This tests
// that the user has mbstring installed, and also that they didn't type
@ -468,7 +475,7 @@ final class PhabricatorRepositoryEditor
}
try {
PhabricatorRepository::asssertValidRepositorySlug($new);
PhabricatorRepository::assertValidRepositorySlug($new);
} catch (Exception $ex) {
$errors[] = new PhabricatorApplicationTransactionValidationError(
$type,
@ -495,6 +502,47 @@ final class PhabricatorRepositoryEditor
}
break;
case PhabricatorRepositoryTransaction::TYPE_CALLSIGN:
foreach ($xactions as $xaction) {
$old = $xaction->getOldValue();
$new = $xaction->getNewValue();
if (!strlen($new)) {
continue;
}
if ($new === $old) {
continue;
}
try {
PhabricatorRepository::assertValidCallsign($new);
} catch (Exception $ex) {
$errors[] = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Invalid'),
$ex->getMessage(),
$xaction);
continue;
}
$other = id(new PhabricatorRepositoryQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withCallsigns(array($new))
->executeOne();
if ($other && ($other->getID() !== $object->getID())) {
$errors[] = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Duplicate'),
pht(
'The selected callsign ("%s") is already in use by another '.
'repository. Choose a unique callsign.',
$new),
$xaction);
continue;
}
}
break;
}
return $errors;
@ -522,4 +570,27 @@ final class PhabricatorRepositoryEditor
return true;
}
protected function applyFinalEffects(
PhabricatorLiskDAO $object,
array $xactions) {
// If the repository does not have a local path yet, assign it one based
// on its ID. We can't do this earlier because we won't have an ID yet.
$local_path = $object->getDetail('local-path');
if (!strlen($local_path)) {
$local_key = 'repository.default-local-path';
$local_root = PhabricatorEnv::getEnvConfig($local_key);
$local_root = rtrim($local_root, '/');
$id = $object->getID();
$local_path = "{$local_root}/{$id}/";
$object->setDetail('local-path', $local_path);
$object->save();
}
return $xactions;
}
}

View file

@ -192,7 +192,7 @@ final class PhabricatorRepositoryPullEngine
}
private function getHookContextIdentifier(PhabricatorRepository $repository) {
$identifier = $repository->getCallsign();
$identifier = $repository->getPHID();
$instance = PhabricatorEnv::getEnvConfig('cluster.instance');
if (strlen($instance)) {

View file

@ -93,7 +93,7 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
),
self::CONFIG_COLUMN_SCHEMA => array(
'name' => 'sort255',
'callsign' => 'sort32',
'callsign' => 'sort32?',
'repositorySlug' => 'sort64?',
'versionControlSystem' => 'text32',
'uuid' => 'text64?',
@ -149,13 +149,21 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
}
public function getMonogram() {
return 'r'.$this->getCallsign();
$callsign = $this->getCallsign();
if (strlen($callsign)) {
return "r{$callsign}";
}
$id = $this->getID();
return "R{$id}";
}
public function getDisplayName() {
// TODO: This is intended to produce a human-readable name that is not
// necessarily a global, unique identifier. Eventually, it may just return
// a string like "skynet" instead of "rSKYNET".
$slug = $this->getRepositorySlug();
if (strlen($slug)) {
return $slug;
}
return $this->getMonogram();
}
@ -317,14 +325,14 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
public static function isValidRepositorySlug($slug) {
try {
self::asssertValidRepositorySlug($slug);
self::assertValidRepositorySlug($slug);
return true;
} catch (Exception $ex) {
return false;
}
}
public static function asssertValidRepositorySlug($slug) {
public static function assertValidRepositorySlug($slug) {
if (!strlen($slug)) {
throw new Exception(
pht(
@ -391,6 +399,30 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
}
}
public static function assertValidCallsign($callsign) {
if (!strlen($callsign)) {
throw new Exception(
pht(
'A repository callsign must be at least one character long.'));
}
if (strlen($callsign) > 32) {
throw new Exception(
pht(
'The callsign "%s" is not a valid repository callsign. Callsigns '.
'must be no more than 32 bytes long.',
$callsign));
}
if (!preg_match('/^[A-Z]+\z/', $callsign)) {
throw new Exception(
pht(
'The callsign "%s" is not a valid repository callsign. Callsigns '.
'may only contain UPPERCASE letters.',
$callsign));
}
}
/* -( Remote Command Execution )------------------------------------------- */
@ -675,7 +707,13 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
}
public function getURI() {
return '/diffusion/'.$this->getCallsign().'/';
$callsign = $this->getCallsign();
if (strlen($callsign)) {
return "/diffusion/{$callsign}/";
}
$id = $this->getID();
return "/diffusion/{$id}/";
}
public function getPathURI($path) {
@ -684,9 +722,98 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
public function getCommitURI($identifier) {
$callsign = $this->getCallsign();
if (strlen($callsign)) {
return "/r{$callsign}{$identifier}";
}
$id = $this->getID();
return "/R{$id}:{$identifier}";
}
public static function parseRepositoryServicePath($request_path) {
// NOTE: In Mercurial over SSH, the path will begin without a leading "/",
// so we're matching it optionally.
$patterns = array(
'(^'.
'(?P<base>/?diffusion/(?P<identifier>[A-Z]+|[0-9]\d*))'.
'(?P<path>(?:/.*)?)'.
'\z)',
);
$identifier = null;
foreach ($patterns as $pattern) {
$matches = null;
if (!preg_match($pattern, $request_path, $matches)) {
continue;
}
$identifier = $matches['identifier'];
$base = $matches['base'];
$path = $matches['path'];
break;
}
if ($identifier === null) {
return null;
}
return array(
'identifier' => $identifier,
'base' => $base,
'path' => $path,
);
}
public function getCanonicalPath($request_path) {
$standard_pattern =
'(^'.
'(?P<prefix>/diffusion/)'.
'(?P<identifier>[^/]+)'.
'(?P<suffix>(?:/.*)?)'.
'\z)';
$matches = null;
if (preg_match($standard_pattern, $request_path, $matches)) {
$prefix = $matches['prefix'];
$callsign = $this->getCallsign();
if ($callsign) {
$identifier = $callsign;
} else {
$identifier = $this->getID();
}
$suffix = $matches['suffix'];
if (!strlen($suffix)) {
$suffix = '/';
}
return $prefix.$identifier.$suffix;
}
$commit_pattern =
'(^'.
'(?P<prefix>/)'.
'(?P<monogram>'.
'(?:'.
'r(?P<repositoryCallsign>[A-Z]+)'.
'|'.
'R(?P<repositoryID>[1-9]\d*):'.
')'.
'(?P<commit>[a-f0-9]+)'.
')'.
'\z)';
$matches = null;
if (preg_match($commit_pattern, $request_path, $matches)) {
$commit = $matches['commit'];
return $this->getCommitURI($commit);
}
return null;
}
public function generateURI(array $params) {
$req_branch = false;
$req_commit = false;
@ -955,7 +1082,13 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
}
if ($need_scope) {
$scope = 'r'.$this->getCallsign();
$callsign = $this->getCallsign();
if ($callsign) {
$scope = "r{$callsign}";
} else {
$id = $this->getID();
$scope = "R{$id}:";
}
$name = $scope.$name;
}

View file

@ -29,6 +29,7 @@ final class PhabricatorRepositoryTransaction
const TYPE_SYMBOLS_LANGUAGE = 'repo:symbol-language';
const TYPE_STAGING_URI = 'repo:staging-uri';
const TYPE_AUTOMATION_BLUEPRINTS = 'repo:automation-blueprints';
const TYPE_CALLSIGN = 'repo:callsign';
// TODO: Clean up these legacy transaction types.
const TYPE_SSH_LOGIN = 'repo:ssh-login';
@ -466,6 +467,26 @@ final class PhabricatorRepositoryTransaction
new PhutilNumber(count($rem)),
$this->renderHandleList($rem));
}
case self::TYPE_CALLSIGN:
if ($old === null) {
return pht(
'%s set the callsign for this repository to "%s".',
$this->renderHandleLink($author_phid),
$new);
} else if ($new === null) {
return pht(
'%s removed the callsign ("%s") for this repository.',
$this->renderHandleLink($author_phid),
$old);
} else {
return pht(
'%s changed the callsign for this repository from "%s" to "%s".',
$this->renderHandleLink($author_phid),
$old,
$new);
}
}
return parent::getTitle();

View file

@ -9,6 +9,47 @@ final class PhabricatorRepositoryURITestCase
);
}
public function testRepositoryURICanonicalization() {
$repo = id(new PhabricatorRepository())
->makeEphemeral()
->setID(123);
$tests = array(
'/diffusion/123' => '/diffusion/123/',
'/diffusion/123/' => '/diffusion/123/',
'/diffusion/123/browse/master/' => '/diffusion/123/browse/master/',
'/kangaroo/' => null,
);
foreach ($tests as $input => $expect) {
$this->assertEqual(
$expect,
$repo->getCanonicalPath($input),
pht('Canonical Path (ID, No Callsign): %s', $input));
}
$repo->setCallsign('XYZ');
$tests = array(
'/diffusion/123' => '/diffusion/XYZ/',
'/diffusion/123/' => '/diffusion/XYZ/',
'/diffusion/123/browse/master/' => '/diffusion/XYZ/browse/master/',
'/diffusion/XYZ' => '/diffusion/XYZ/',
'/diffusion/XYZ/' => '/diffusion/XYZ/',
'/diffusion/XYZ/browse/master/' => '/diffusion/XYZ/browse/master/',
'/diffusion/ABC/' => '/diffusion/XYZ/',
'/kangaroo/' => null,
'/R1:abcdef' => '/rXYZabcdef',
);
foreach ($tests as $input => $expect) {
$this->assertEqual(
$expect,
$repo->getCanonicalPath($input),
pht('Canonical Path (ID, Callsign): %s', $input));
}
}
public function testURIGeneration() {
$svn = PhabricatorRepositoryType::REPOSITORY_TYPE_SVN;
$git = PhabricatorRepositoryType::REPOSITORY_TYPE_GIT;

View file

@ -122,13 +122,13 @@ final class PhabricatorSlowvotePollController
$view->invokeWillRenderEvent();
if (strlen($poll->getDescription())) {
$view->addTextContent(
$output = PhabricatorMarkupEngine::renderOneObject(
id(new PhabricatorMarkupOneOff())->setContent(
$poll->getDescription()),
'default',
$viewer));
$description = $poll->getDescription();
if (strlen($description)) {
$description = new PHUIRemarkupView($viewer, $description);
$view->addSectionHeader(
pht('Description'),
PHUIPropertyListView::ICON_SUMMARY);
$view->addTextContent($description);
}
return $view;

View file

@ -97,6 +97,19 @@ final class PhabricatorSlowvoteTransaction
return parent::getTitle();
}
public function getRemarkupBlocks() {
$blocks = parent::getRemarkupBlocks();
$type = $this->getTransactionType();
switch ($type) {
case self::TYPE_DESCRIPTION:
$blocks[] = $this->getNewValue();
break;
}
return $blocks;
}
public function getTitleForFeed() {
$author_phid = $this->getAuthorPHID();
$object_phid = $this->getObjectPHID();

View file

@ -74,13 +74,9 @@ final class SlowvoteEmbedView extends AphrontView {
$header = id(new PHUIHeaderView())
->setHeader($link_to_slowvote);
$description = null;
if ($poll->getDescription()) {
$description = PhabricatorMarkupEngine::renderOneObject(
id(new PhabricatorMarkupOneOff())->setContent(
$poll->getDescription()),
'default',
$this->getUser());
$description = $poll->getDescription();
if (strlen($description)) {
$description = new PHUIRemarkupView($this->getUser(), $description);
$description = phutil_tag(
'div',
array(

View file

@ -83,15 +83,10 @@ final class PhabricatorSpacesViewController
$description = $space->getDescription();
if (strlen($description)) {
$description = PhabricatorMarkupEngine::renderOneObject(
id(new PhabricatorMarkupOneOff())->setContent($description),
'default',
$viewer);
$description = new PHUIRemarkupView($viewer, $description);
$list->addSectionHeader(
pht('Description'),
PHUIPropertyListView::ICON_SUMMARY);
$list->addTextContent($description);
}

View file

@ -28,24 +28,10 @@ final class PhabricatorSubscriptionsEditController
->withPHIDs(array($phid))
->executeOne();
if (phid_get_type($phid) == PhabricatorProjectProjectPHIDType::TYPECONST) {
// TODO: This is a big hack, but a weak argument for adding some kind
// of "load for role" feature to ObjectQuery, and also not a really great
// argument for adding some kind of "load extra stuff" feature to
// SubscriberInterface. Do this for now and wait for the best way forward
// to become more clear?
$object = id(new PhabricatorProjectQuery())
->setViewer($viewer)
->withPHIDs(array($phid))
->needWatchers(true)
->executeOne();
} else {
$object = id(new PhabricatorObjectQuery())
->setViewer($viewer)
->withPHIDs(array($phid))
->executeOne();
}
if (!($object instanceof PhabricatorSubscribableInterface)) {
return $this->buildErrorResponse(

View file

@ -50,10 +50,7 @@ final class PhabricatorApplicationTransactionCommentRawController
$details_text = pht(
'For full details, run `/bin/mail show-outbound --id %d`',
$source_id);
$addendum = PhabricatorMarkupEngine::renderOneObject(
id(new PhabricatorMarkupOneOff())->setContent($details_text),
'default',
$viewer);
$addendum = new PHUIRemarkupView($viewer, $details_text);
}
}
}

View file

@ -70,6 +70,8 @@ abstract class PhabricatorEditEngineController
->executeOne();
if ($config) {
$engine = $config->getEngine();
} else {
return null;
}
if (!$engine->isEngineConfigurable()) {

View file

@ -2607,14 +2607,19 @@ abstract class PhabricatorApplicationTransactionEditor
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST);
if ($project_phids) {
$watcher_type = PhabricatorObjectHasWatcherEdgeType::EDGECONST;
$projects = id(new PhabricatorProjectQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withPHIDs($project_phids)
->needWatchers(true)
->execute();
$query = id(new PhabricatorEdgeQuery())
->withSourcePHIDs($project_phids)
->withEdgeTypes(array($watcher_type));
$query->execute();
$watcher_phids = array();
foreach ($projects as $project) {
foreach ($project->getAllAncestorWatcherPHIDs() as $phid) {
$watcher_phids[$phid] = $phid;
}
}
$watcher_phids = $query->getDestinationPHIDs();
if ($watcher_phids) {
// We need to do a visibility check for all the watchers, as
// watching a project is not a guarantee that you can see objects

Some files were not shown because too many files have changed in this diff Show more