1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-28 09:42:41 +01:00

(stable) Promote 2016 Week 26

This commit is contained in:
epriestley 2016-06-24 15:30:20 -07:00
commit cadac75b82
95 changed files with 2285 additions and 526 deletions

View file

@ -7,18 +7,18 @@
*/ */
return array( return array(
'names' => array( 'names' => array(
'core.pkg.css' => 'c7fc5aec', 'core.pkg.css' => 'b6b40555',
'core.pkg.js' => '10275c16', 'core.pkg.js' => 'f2139810',
'darkconsole.pkg.js' => 'e7393ebb', 'darkconsole.pkg.js' => 'e7393ebb',
'differential.pkg.css' => 'b3eea3f5', 'differential.pkg.css' => 'b3eea3f5',
'differential.pkg.js' => '4b7d8f19', 'differential.pkg.js' => '01a010d6',
'diffusion.pkg.css' => '91c5d3a6', 'diffusion.pkg.css' => '91c5d3a6',
'diffusion.pkg.js' => '3a9a8bfa', 'diffusion.pkg.js' => '3a9a8bfa',
'maniphest.pkg.css' => '4845691a', 'maniphest.pkg.css' => '4845691a',
'maniphest.pkg.js' => '949a7498', 'maniphest.pkg.js' => '949a7498',
'rsrc/css/aphront/aphront-bars.css' => '231ac33c', 'rsrc/css/aphront/aphront-bars.css' => '231ac33c',
'rsrc/css/aphront/dark-console.css' => 'f54bf286', 'rsrc/css/aphront/dark-console.css' => 'f54bf286',
'rsrc/css/aphront/dialog-view.css' => 'b4334e08', 'rsrc/css/aphront/dialog-view.css' => '913c172e',
'rsrc/css/aphront/lightbox-attachment.css' => '7acac05d', 'rsrc/css/aphront/lightbox-attachment.css' => '7acac05d',
'rsrc/css/aphront/list-filter-view.css' => '5d6f0526', 'rsrc/css/aphront/list-filter-view.css' => '5d6f0526',
'rsrc/css/aphront/multi-column.css' => 'fd18389d', 'rsrc/css/aphront/multi-column.css' => 'fd18389d',
@ -28,7 +28,7 @@ return array(
'rsrc/css/aphront/table-view.css' => '9258e19f', 'rsrc/css/aphront/table-view.css' => '9258e19f',
'rsrc/css/aphront/tokenizer.css' => '056da01b', 'rsrc/css/aphront/tokenizer.css' => '056da01b',
'rsrc/css/aphront/tooltip.css' => '1a07aea8', 'rsrc/css/aphront/tooltip.css' => '1a07aea8',
'rsrc/css/aphront/typeahead-browse.css' => 'd8581d2c', 'rsrc/css/aphront/typeahead-browse.css' => '8904346a',
'rsrc/css/aphront/typeahead.css' => 'd4f16145', 'rsrc/css/aphront/typeahead.css' => 'd4f16145',
'rsrc/css/application/almanac/almanac.css' => 'dbb9b3af', 'rsrc/css/application/almanac/almanac.css' => 'dbb9b3af',
'rsrc/css/application/auth/auth.css' => '0877ed6e', 'rsrc/css/application/auth/auth.css' => '0877ed6e',
@ -124,7 +124,7 @@ return array(
'rsrc/css/phui/phui-badge.css' => '3baef8db', 'rsrc/css/phui/phui-badge.css' => '3baef8db',
'rsrc/css/phui/phui-big-info-view.css' => 'bd903741', 'rsrc/css/phui/phui-big-info-view.css' => 'bd903741',
'rsrc/css/phui/phui-box.css' => '5c8387cf', 'rsrc/css/phui/phui-box.css' => '5c8387cf',
'rsrc/css/phui/phui-button.css' => 'a64a8de6', 'rsrc/css/phui/phui-button.css' => '4a5fbe3d',
'rsrc/css/phui/phui-chart.css' => '6bf6f78e', 'rsrc/css/phui/phui-chart.css' => '6bf6f78e',
'rsrc/css/phui/phui-crumbs-view.css' => '6b813619', 'rsrc/css/phui/phui-crumbs-view.css' => '6b813619',
'rsrc/css/phui/phui-curtain-view.css' => '7148ae25', 'rsrc/css/phui/phui-curtain-view.css' => '7148ae25',
@ -149,13 +149,13 @@ return array(
'rsrc/css/phui/phui-pager.css' => 'bea33d23', 'rsrc/css/phui/phui-pager.css' => 'bea33d23',
'rsrc/css/phui/phui-pinboard-view.css' => '2495140e', 'rsrc/css/phui/phui-pinboard-view.css' => '2495140e',
'rsrc/css/phui/phui-profile-menu.css' => 'c8557f33', 'rsrc/css/phui/phui-profile-menu.css' => 'c8557f33',
'rsrc/css/phui/phui-property-list-view.css' => 'd4bbd0cb', 'rsrc/css/phui/phui-property-list-view.css' => '6d8e58ac',
'rsrc/css/phui/phui-remarkup-preview.css' => '1a8f2591', 'rsrc/css/phui/phui-remarkup-preview.css' => '1a8f2591',
'rsrc/css/phui/phui-segment-bar-view.css' => '46342871', 'rsrc/css/phui/phui-segment-bar-view.css' => '46342871',
'rsrc/css/phui/phui-spacing.css' => '042804d6', 'rsrc/css/phui/phui-spacing.css' => '042804d6',
'rsrc/css/phui/phui-status.css' => 'd5263e49', 'rsrc/css/phui/phui-status.css' => 'd5263e49',
'rsrc/css/phui/phui-tag-view.css' => '6bbd83e2', 'rsrc/css/phui/phui-tag-view.css' => '6bbd83e2',
'rsrc/css/phui/phui-timeline-view.css' => '8ea41b25', 'rsrc/css/phui/phui-timeline-view.css' => 'c3782437',
'rsrc/css/phui/phui-two-column-view.css' => '9fb86c85', 'rsrc/css/phui/phui-two-column-view.css' => '9fb86c85',
'rsrc/css/phui/workboards/phui-workboard-color.css' => 'ac6fe6a7', 'rsrc/css/phui/workboards/phui-workboard-color.css' => 'ac6fe6a7',
'rsrc/css/phui/workboards/phui-workboard.css' => 'e6d89647', 'rsrc/css/phui/workboards/phui-workboard.css' => 'e6d89647',
@ -245,7 +245,7 @@ return array(
'rsrc/externals/javelin/lib/URI.js' => 'c989ade3', 'rsrc/externals/javelin/lib/URI.js' => 'c989ade3',
'rsrc/externals/javelin/lib/Vector.js' => '2caa8fb8', 'rsrc/externals/javelin/lib/Vector.js' => '2caa8fb8',
'rsrc/externals/javelin/lib/WebSocket.js' => 'e292eaf4', 'rsrc/externals/javelin/lib/WebSocket.js' => 'e292eaf4',
'rsrc/externals/javelin/lib/Workflow.js' => '0eb34d1d', 'rsrc/externals/javelin/lib/Workflow.js' => '1e911d0f',
'rsrc/externals/javelin/lib/__tests__/Cookie.js' => '5ed109e8', 'rsrc/externals/javelin/lib/__tests__/Cookie.js' => '5ed109e8',
'rsrc/externals/javelin/lib/__tests__/DOM.js' => 'c984504b', 'rsrc/externals/javelin/lib/__tests__/DOM.js' => 'c984504b',
'rsrc/externals/javelin/lib/__tests__/JSON.js' => '837a7d68', 'rsrc/externals/javelin/lib/__tests__/JSON.js' => '837a7d68',
@ -339,6 +339,7 @@ return array(
'rsrc/image/people/washington.png' => '40dd301c', 'rsrc/image/people/washington.png' => '40dd301c',
'rsrc/image/phrequent_active.png' => 'a466a8ed', 'rsrc/image/phrequent_active.png' => 'a466a8ed',
'rsrc/image/phrequent_inactive.png' => 'bfc15a69', 'rsrc/image/phrequent_inactive.png' => 'bfc15a69',
'rsrc/image/resize.png' => 'fd476de4',
'rsrc/image/sprite-login-X2.png' => 'e3991e37', 'rsrc/image/sprite-login-X2.png' => 'e3991e37',
'rsrc/image/sprite-login.png' => '03d5af29', 'rsrc/image/sprite-login.png' => '03d5af29',
'rsrc/image/sprite-menu-X2.png' => 'cfd8fca5', 'rsrc/image/sprite-menu-X2.png' => 'cfd8fca5',
@ -494,7 +495,7 @@ return array(
'rsrc/js/core/behavior-lightbox-attachments.js' => 'f8ba29d7', 'rsrc/js/core/behavior-lightbox-attachments.js' => 'f8ba29d7',
'rsrc/js/core/behavior-line-linker.js' => '1499a8cb', 'rsrc/js/core/behavior-line-linker.js' => '1499a8cb',
'rsrc/js/core/behavior-more.js' => 'a80d0378', 'rsrc/js/core/behavior-more.js' => 'a80d0378',
'rsrc/js/core/behavior-object-selector.js' => '49b73b36', 'rsrc/js/core/behavior-object-selector.js' => '9030ebef',
'rsrc/js/core/behavior-oncopy.js' => '2926fff2', 'rsrc/js/core/behavior-oncopy.js' => '2926fff2',
'rsrc/js/core/behavior-phabricator-nav.js' => '56a1ca03', 'rsrc/js/core/behavior-phabricator-nav.js' => '56a1ca03',
'rsrc/js/core/behavior-phabricator-remarkup-assist.js' => '116cf19b', 'rsrc/js/core/behavior-phabricator-remarkup-assist.js' => '116cf19b',
@ -515,14 +516,15 @@ return array(
'rsrc/js/core/behavior-watch-anchor.js' => '9f36c42d', 'rsrc/js/core/behavior-watch-anchor.js' => '9f36c42d',
'rsrc/js/core/behavior-workflow.js' => '0a3f3021', 'rsrc/js/core/behavior-workflow.js' => '0a3f3021',
'rsrc/js/core/phtize.js' => 'd254d646', 'rsrc/js/core/phtize.js' => 'd254d646',
'rsrc/js/phui/behavior-phui-dropdown-menu.js' => '54733475', 'rsrc/js/phui/behavior-phui-dropdown-menu.js' => '1aa4c968',
'rsrc/js/phui/behavior-phui-file-upload.js' => 'b003d4fb', 'rsrc/js/phui/behavior-phui-file-upload.js' => 'b003d4fb',
'rsrc/js/phui/behavior-phui-object-box-tabs.js' => '2bfa2836', 'rsrc/js/phui/behavior-phui-object-box-tabs.js' => '2bfa2836',
'rsrc/js/phui/behavior-phui-profile-menu.js' => '12884df9', 'rsrc/js/phui/behavior-phui-profile-menu.js' => '12884df9',
'rsrc/js/phui/behavior-phui-submenu.js' => 'a6f7a73b',
'rsrc/js/phuix/PHUIXActionListView.js' => 'b5c256b8', 'rsrc/js/phuix/PHUIXActionListView.js' => 'b5c256b8',
'rsrc/js/phuix/PHUIXActionView.js' => '8cf6d262', 'rsrc/js/phuix/PHUIXActionView.js' => '8cf6d262',
'rsrc/js/phuix/PHUIXAutocomplete.js' => '9196fb06', 'rsrc/js/phuix/PHUIXAutocomplete.js' => '9196fb06',
'rsrc/js/phuix/PHUIXDropdownMenu.js' => 'bd4c8dca', 'rsrc/js/phuix/PHUIXDropdownMenu.js' => '82e270da',
'rsrc/js/phuix/PHUIXFormControl.js' => 'e15869a8', 'rsrc/js/phuix/PHUIXFormControl.js' => 'e15869a8',
'rsrc/js/phuix/PHUIXIconView.js' => 'bff6884b', 'rsrc/js/phuix/PHUIXIconView.js' => 'bff6884b',
), ),
@ -530,7 +532,7 @@ return array(
'almanac-css' => 'dbb9b3af', 'almanac-css' => 'dbb9b3af',
'aphront-bars' => '231ac33c', 'aphront-bars' => '231ac33c',
'aphront-dark-console-css' => 'f54bf286', 'aphront-dark-console-css' => 'f54bf286',
'aphront-dialog-view-css' => 'b4334e08', 'aphront-dialog-view-css' => '913c172e',
'aphront-list-filter-view-css' => '5d6f0526', 'aphront-list-filter-view-css' => '5d6f0526',
'aphront-multi-column-view-css' => 'fd18389d', 'aphront-multi-column-view-css' => 'fd18389d',
'aphront-panel-view-css' => '8427b78d', 'aphront-panel-view-css' => '8427b78d',
@ -656,7 +658,7 @@ return array(
'javelin-behavior-phabricator-line-linker' => '1499a8cb', 'javelin-behavior-phabricator-line-linker' => '1499a8cb',
'javelin-behavior-phabricator-nav' => '56a1ca03', 'javelin-behavior-phabricator-nav' => '56a1ca03',
'javelin-behavior-phabricator-notification-example' => '8ce821c5', 'javelin-behavior-phabricator-notification-example' => '8ce821c5',
'javelin-behavior-phabricator-object-selector' => '49b73b36', 'javelin-behavior-phabricator-object-selector' => '9030ebef',
'javelin-behavior-phabricator-oncopy' => '2926fff2', 'javelin-behavior-phabricator-oncopy' => '2926fff2',
'javelin-behavior-phabricator-remarkup-assist' => '116cf19b', 'javelin-behavior-phabricator-remarkup-assist' => '116cf19b',
'javelin-behavior-phabricator-reveal-content' => '60821bc7', 'javelin-behavior-phabricator-reveal-content' => '60821bc7',
@ -668,11 +670,12 @@ return array(
'javelin-behavior-phabricator-watch-anchor' => '9f36c42d', 'javelin-behavior-phabricator-watch-anchor' => '9f36c42d',
'javelin-behavior-pholio-mock-edit' => 'bee502c8', 'javelin-behavior-pholio-mock-edit' => 'bee502c8',
'javelin-behavior-pholio-mock-view' => 'fbe497e7', 'javelin-behavior-pholio-mock-view' => 'fbe497e7',
'javelin-behavior-phui-dropdown-menu' => '54733475', 'javelin-behavior-phui-dropdown-menu' => '1aa4c968',
'javelin-behavior-phui-file-upload' => 'b003d4fb', 'javelin-behavior-phui-file-upload' => 'b003d4fb',
'javelin-behavior-phui-hovercards' => 'bcaccd64', 'javelin-behavior-phui-hovercards' => 'bcaccd64',
'javelin-behavior-phui-object-box-tabs' => '2bfa2836', 'javelin-behavior-phui-object-box-tabs' => '2bfa2836',
'javelin-behavior-phui-profile-menu' => '12884df9', 'javelin-behavior-phui-profile-menu' => '12884df9',
'javelin-behavior-phui-submenu' => 'a6f7a73b',
'javelin-behavior-policy-control' => 'd0c516d5', 'javelin-behavior-policy-control' => 'd0c516d5',
'javelin-behavior-policy-rule-editor' => '5e9f347c', 'javelin-behavior-policy-rule-editor' => '5e9f347c',
'javelin-behavior-project-boards' => '14a1faae', 'javelin-behavior-project-boards' => '14a1faae',
@ -749,7 +752,7 @@ return array(
'javelin-workboard-card' => 'c587b80f', 'javelin-workboard-card' => 'c587b80f',
'javelin-workboard-column' => 'bae58312', 'javelin-workboard-column' => 'bae58312',
'javelin-workboard-controller' => '55baf5ed', 'javelin-workboard-controller' => '55baf5ed',
'javelin-workflow' => '0eb34d1d', 'javelin-workflow' => '1e911d0f',
'lightbox-attachment-css' => '7acac05d', 'lightbox-attachment-css' => '7acac05d',
'maniphest-batch-editor' => 'b0f0b6d5', 'maniphest-batch-editor' => 'b0f0b6d5',
'maniphest-report-css' => '9b9580b7', 'maniphest-report-css' => '9b9580b7',
@ -821,7 +824,7 @@ return array(
'phui-badge-view-css' => '3baef8db', 'phui-badge-view-css' => '3baef8db',
'phui-big-info-view-css' => 'bd903741', 'phui-big-info-view-css' => 'bd903741',
'phui-box-css' => '5c8387cf', 'phui-box-css' => '5c8387cf',
'phui-button-css' => 'a64a8de6', 'phui-button-css' => '4a5fbe3d',
'phui-calendar-css' => 'ccabe893', 'phui-calendar-css' => 'ccabe893',
'phui-calendar-day-css' => 'd1cf6f93', 'phui-calendar-day-css' => 'd1cf6f93',
'phui-calendar-list-css' => '56e6381a', 'phui-calendar-list-css' => '56e6381a',
@ -853,14 +856,14 @@ return array(
'phui-pager-css' => 'bea33d23', 'phui-pager-css' => 'bea33d23',
'phui-pinboard-view-css' => '2495140e', 'phui-pinboard-view-css' => '2495140e',
'phui-profile-menu-css' => 'c8557f33', 'phui-profile-menu-css' => 'c8557f33',
'phui-property-list-view-css' => 'd4bbd0cb', 'phui-property-list-view-css' => '6d8e58ac',
'phui-remarkup-preview-css' => '1a8f2591', 'phui-remarkup-preview-css' => '1a8f2591',
'phui-segment-bar-view-css' => '46342871', 'phui-segment-bar-view-css' => '46342871',
'phui-spacing-css' => '042804d6', 'phui-spacing-css' => '042804d6',
'phui-status-list-view-css' => 'd5263e49', 'phui-status-list-view-css' => 'd5263e49',
'phui-tag-view-css' => '6bbd83e2', 'phui-tag-view-css' => '6bbd83e2',
'phui-theme-css' => '027ba77e', 'phui-theme-css' => '027ba77e',
'phui-timeline-view-css' => '8ea41b25', 'phui-timeline-view-css' => 'c3782437',
'phui-two-column-view-css' => '9fb86c85', 'phui-two-column-view-css' => '9fb86c85',
'phui-workboard-color-css' => 'ac6fe6a7', 'phui-workboard-color-css' => 'ac6fe6a7',
'phui-workboard-view-css' => 'e6d89647', 'phui-workboard-view-css' => 'e6d89647',
@ -869,7 +872,7 @@ return array(
'phuix-action-list-view' => 'b5c256b8', 'phuix-action-list-view' => 'b5c256b8',
'phuix-action-view' => '8cf6d262', 'phuix-action-view' => '8cf6d262',
'phuix-autocomplete' => '9196fb06', 'phuix-autocomplete' => '9196fb06',
'phuix-dropdown-menu' => 'bd4c8dca', 'phuix-dropdown-menu' => '82e270da',
'phuix-form-control-view' => 'e15869a8', 'phuix-form-control-view' => 'e15869a8',
'phuix-icon-view' => 'bff6884b', 'phuix-icon-view' => 'bff6884b',
'policy-css' => '957ea14c', 'policy-css' => '957ea14c',
@ -889,7 +892,7 @@ return array(
'syntax-default-css' => '9923583c', 'syntax-default-css' => '9923583c',
'syntax-highlighting-css' => '9fc496d5', 'syntax-highlighting-css' => '9fc496d5',
'tokens-css' => '3d0f239e', 'tokens-css' => '3d0f239e',
'typeahead-browse-css' => 'd8581d2c', 'typeahead-browse-css' => '8904346a',
'unhandled-exception-css' => '4c96257a', 'unhandled-exception-css' => '4c96257a',
), ),
'requires' => array( 'requires' => array(
@ -981,17 +984,6 @@ return array(
'javelin-dom', 'javelin-dom',
'javelin-router', 'javelin-router',
), ),
'0eb34d1d' => array(
'javelin-stratcom',
'javelin-request',
'javelin-dom',
'javelin-vector',
'javelin-install',
'javelin-util',
'javelin-mask',
'javelin-uri',
'javelin-routable',
),
'0f764c35' => array( '0f764c35' => array(
'javelin-install', 'javelin-install',
'javelin-util', 'javelin-util',
@ -1034,6 +1026,12 @@ return array(
'javelin-workflow', 'javelin-workflow',
'javelin-workboard-controller', 'javelin-workboard-controller',
), ),
'1aa4c968' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
'phuix-dropdown-menu',
),
'1ad0a787' => array( '1ad0a787' => array(
'javelin-install', 'javelin-install',
'javelin-reactor', 'javelin-reactor',
@ -1071,6 +1069,17 @@ return array(
'javelin-dom', 'javelin-dom',
'javelin-reactor-dom', 'javelin-reactor-dom',
), ),
'1e911d0f' => array(
'javelin-stratcom',
'javelin-request',
'javelin-dom',
'javelin-vector',
'javelin-install',
'javelin-util',
'javelin-mask',
'javelin-uri',
'javelin-routable',
),
'21ba5861' => array( '21ba5861' => array(
'javelin-behavior', 'javelin-behavior',
'javelin-dom', 'javelin-dom',
@ -1205,12 +1214,6 @@ return array(
'javelin-uri', 'javelin-uri',
'phabricator-notification', 'phabricator-notification',
), ),
'49b73b36' => array(
'javelin-behavior',
'javelin-dom',
'javelin-request',
'javelin-util',
),
'4b700e9e' => array( '4b700e9e' => array(
'javelin-behavior', 'javelin-behavior',
'javelin-dom', 'javelin-dom',
@ -1274,12 +1277,6 @@ return array(
'javelin-leader', 'javelin-leader',
'javelin-json', 'javelin-json',
), ),
54733475 => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
'phuix-dropdown-menu',
),
'54b612ba' => array( '54b612ba' => array(
'javelin-color', 'javelin-color',
'javelin-install', 'javelin-install',
@ -1528,6 +1525,13 @@ return array(
'javelin-vector', 'javelin-vector',
'javelin-stratcom', 'javelin-stratcom',
), ),
'82e270da' => array(
'javelin-install',
'javelin-util',
'javelin-dom',
'javelin-vector',
'javelin-stratcom',
),
'834a1173' => array( '834a1173' => array(
'javelin-behavior', 'javelin-behavior',
'javelin-scrollbar', 'javelin-scrollbar',
@ -1598,6 +1602,12 @@ return array(
'javelin-dom', 'javelin-dom',
'javelin-request', 'javelin-request',
), ),
'9030ebef' => array(
'javelin-behavior',
'javelin-dom',
'javelin-request',
'javelin-util',
),
'9196fb06' => array( '9196fb06' => array(
'javelin-install', 'javelin-install',
'javelin-dom', 'javelin-dom',
@ -1692,6 +1702,11 @@ return array(
'javelin-uri', 'javelin-uri',
'phabricator-notification', 'phabricator-notification',
), ),
'a6f7a73b' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
),
'a80d0378' => array( 'a80d0378' => array(
'javelin-behavior', 'javelin-behavior',
'javelin-stratcom', 'javelin-stratcom',
@ -1845,13 +1860,6 @@ return array(
'javelin-vector', 'javelin-vector',
'phui-hovercard', 'phui-hovercard',
), ),
'bd4c8dca' => array(
'javelin-install',
'javelin-util',
'javelin-dom',
'javelin-vector',
'javelin-stratcom',
),
'bdaf4d04' => array( 'bdaf4d04' => array(
'javelin-behavior', 'javelin-behavior',
'javelin-dom', 'javelin-dom',

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_phame.phame_blog
ADD parentDomain VARCHAR(128) NOT NULL COLLATE {$COLLATE_TEXT};

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_phame.phame_blog
ADD parentSite VARCHAR(128) NOT NULL COLLATE {$COLLATE_TEXT};

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_phame.phame_blog
ADD domainFullURI VARCHAR(128) COLLATE {$COLLATE_TEXT};

View file

@ -0,0 +1,3 @@
UPDATE {$NAMESPACE}_phame.phame_blog
SET domainFullURI = CONCAT('http://', domain, '/')
WHERE domain IS NOT NULL;

View file

@ -0,0 +1,3 @@
UPDATE {$NAMESPACE}_phame.phame_blogtransaction
SET transactionType = 'phame.blog.full.domain'
WHERE transactionType = 'phame.blog.domain';

File diff suppressed because one or more lines are too long

View file

@ -54,10 +54,62 @@ if (!$repository->isHosted()) {
$engine->setRepository($repository); $engine->setRepository($repository);
$args = new PhutilArgumentParser($argv);
$args->parsePartial(
array(
array(
'name' => 'hook-mode',
'param' => 'mode',
'help' => pht('Hook execution mode.'),
),
));
$argv = array_merge(
array($argv[0]),
$args->getUnconsumedArgumentVector());
// Figure out which user is writing the commit. // Figure out which user is writing the commit.
$hook_mode = $args->getArg('hook-mode');
if ($hook_mode !== null) {
$known_modes = array(
'svn-revprop' => true,
);
if ($repository->isGit() || $repository->isHg()) { if (empty($known_modes[$hook_mode])) {
throw new Exception(
pht(
'Invalid Hook Mode: This hook was invoked in "%s" mode, but this '.
'is not a recognized hook mode. Valid modes are: %s.',
$hook_mode,
implode(', ', array_keys($known_modes))));
}
}
$is_svnrevprop = ($hook_mode == 'svn-revprop');
if ($is_svnrevprop) {
// For now, we let these through if the repository allows dangerous changes
// and prevent them if it doesn't. See T11208 for discussion.
$revprop_key = $argv[5];
if ($repository->shouldAllowDangerousChanges()) {
$err = 0;
} else {
$err = 1;
$console = PhutilConsole::getConsole();
$console->writeErr(
pht(
"DANGEROUS CHANGE: Dangerous change protection is enabled for this ".
"repository, so you can not change revision properties (you are ".
"attempting to edit \"%s\").\n".
"Edit the repository configuration before making dangerous changes.",
$revprop_key));
}
exit($err);
} else if ($repository->isGit() || $repository->isHg()) {
$username = getenv(DiffusionCommitHookEngine::ENV_USER); $username = getenv(DiffusionCommitHookEngine::ENV_USER);
if (!strlen($username)) { if (!strlen($username)) {
throw new Exception( throw new Exception(

View file

@ -88,7 +88,22 @@ if ($as_device) {
$arguments[] = AlmanacKeys::getKeyPath('device.key'); $arguments[] = AlmanacKeys::getKeyPath('device.key');
} }
// Subversion passes us a host in the form "domain.com:port", which is not
// valid for normal SSH but which we can parse into a valid "-p" flag.
$passthru_args = $unconsumed_argv;
$host = array_shift($passthru_args);
$parts = explode(':', $host, 2);
$host = $parts[0];
$port = $args->getArg('port'); $port = $args->getArg('port');
if (!$port) {
if (count($parts) == 2) {
$port = $parts[1];
}
}
if ($port) { if ($port) {
$pattern[] = '-p %d'; $pattern[] = '-p %d';
$arguments[] = $port; $arguments[] = $port;
@ -96,7 +111,9 @@ if ($port) {
$pattern[] = '--'; $pattern[] = '--';
$passthru_args = $unconsumed_argv; $pattern[] = '%s';
$arguments[] = $host;
foreach ($passthru_args as $passthru_arg) { foreach ($passthru_args as $passthru_arg) {
$pattern[] = '%s'; $pattern[] = '%s';
$arguments[] = $passthru_arg; $arguments[] = $passthru_arg;

View file

@ -190,12 +190,14 @@ try {
'P' => $user->getPHID(), 'P' => $user->getPHID(),
)); ));
if (!$user->canEstablishSSHSessions()) { if (!$device) {
throw new Exception( if (!$user->canEstablishSSHSessions()) {
pht( throw new Exception(
'Your account ("%s") does not have permission to establish SSH '. pht(
'sessions. Visit the web interface for more information.', 'Your account ("%s") does not have permission to establish SSH '.
$user_name)); 'sessions. Visit the web interface for more information.',
$user_name));
}
} }
$workflows = id(new PhutilClassMapQuery()) $workflows = id(new PhutilClassMapQuery())

View file

@ -350,7 +350,6 @@ phutil_register_library_map(array(
'DefaultDatabaseConfigurationProvider' => 'infrastructure/storage/configuration/DefaultDatabaseConfigurationProvider.php', 'DefaultDatabaseConfigurationProvider' => 'infrastructure/storage/configuration/DefaultDatabaseConfigurationProvider.php',
'DifferentialAction' => 'applications/differential/constants/DifferentialAction.php', 'DifferentialAction' => 'applications/differential/constants/DifferentialAction.php',
'DifferentialActionEmailCommand' => 'applications/differential/command/DifferentialActionEmailCommand.php', 'DifferentialActionEmailCommand' => 'applications/differential/command/DifferentialActionEmailCommand.php',
'DifferentialActionMenuEventListener' => 'applications/differential/event/DifferentialActionMenuEventListener.php',
'DifferentialAddCommentView' => 'applications/differential/view/DifferentialAddCommentView.php', 'DifferentialAddCommentView' => 'applications/differential/view/DifferentialAddCommentView.php',
'DifferentialAdjustmentMapTestCase' => 'applications/differential/storage/__tests__/DifferentialAdjustmentMapTestCase.php', 'DifferentialAdjustmentMapTestCase' => 'applications/differential/storage/__tests__/DifferentialAdjustmentMapTestCase.php',
'DifferentialAffectedPath' => 'applications/differential/storage/DifferentialAffectedPath.php', 'DifferentialAffectedPath' => 'applications/differential/storage/DifferentialAffectedPath.php',
@ -1420,8 +1419,13 @@ phutil_register_library_map(array(
'ManiphestTaskEditController' => 'applications/maniphest/controller/ManiphestTaskEditController.php', 'ManiphestTaskEditController' => 'applications/maniphest/controller/ManiphestTaskEditController.php',
'ManiphestTaskFulltextEngine' => 'applications/maniphest/search/ManiphestTaskFulltextEngine.php', 'ManiphestTaskFulltextEngine' => 'applications/maniphest/search/ManiphestTaskFulltextEngine.php',
'ManiphestTaskHasCommitEdgeType' => 'applications/maniphest/edge/ManiphestTaskHasCommitEdgeType.php', 'ManiphestTaskHasCommitEdgeType' => 'applications/maniphest/edge/ManiphestTaskHasCommitEdgeType.php',
'ManiphestTaskHasCommitRelationship' => 'applications/maniphest/relationship/ManiphestTaskHasCommitRelationship.php',
'ManiphestTaskHasMockEdgeType' => 'applications/maniphest/edge/ManiphestTaskHasMockEdgeType.php', 'ManiphestTaskHasMockEdgeType' => 'applications/maniphest/edge/ManiphestTaskHasMockEdgeType.php',
'ManiphestTaskHasMockRelationship' => 'applications/maniphest/relationship/ManiphestTaskHasMockRelationship.php',
'ManiphestTaskHasParentRelationship' => 'applications/maniphest/relationship/ManiphestTaskHasParentRelationship.php',
'ManiphestTaskHasRevisionEdgeType' => 'applications/maniphest/edge/ManiphestTaskHasRevisionEdgeType.php', 'ManiphestTaskHasRevisionEdgeType' => 'applications/maniphest/edge/ManiphestTaskHasRevisionEdgeType.php',
'ManiphestTaskHasRevisionRelationship' => 'applications/maniphest/relationship/ManiphestTaskHasRevisionRelationship.php',
'ManiphestTaskHasSubtaskRelationship' => 'applications/maniphest/relationship/ManiphestTaskHasSubtaskRelationship.php',
'ManiphestTaskHeraldField' => 'applications/maniphest/herald/ManiphestTaskHeraldField.php', 'ManiphestTaskHeraldField' => 'applications/maniphest/herald/ManiphestTaskHeraldField.php',
'ManiphestTaskHeraldFieldGroup' => 'applications/maniphest/herald/ManiphestTaskHeraldFieldGroup.php', 'ManiphestTaskHeraldFieldGroup' => 'applications/maniphest/herald/ManiphestTaskHeraldFieldGroup.php',
'ManiphestTaskListController' => 'applications/maniphest/controller/ManiphestTaskListController.php', 'ManiphestTaskListController' => 'applications/maniphest/controller/ManiphestTaskListController.php',
@ -1437,6 +1441,7 @@ phutil_register_library_map(array(
'ManiphestTaskPriorityHeraldAction' => 'applications/maniphest/herald/ManiphestTaskPriorityHeraldAction.php', 'ManiphestTaskPriorityHeraldAction' => 'applications/maniphest/herald/ManiphestTaskPriorityHeraldAction.php',
'ManiphestTaskPriorityHeraldField' => 'applications/maniphest/herald/ManiphestTaskPriorityHeraldField.php', 'ManiphestTaskPriorityHeraldField' => 'applications/maniphest/herald/ManiphestTaskPriorityHeraldField.php',
'ManiphestTaskQuery' => 'applications/maniphest/query/ManiphestTaskQuery.php', 'ManiphestTaskQuery' => 'applications/maniphest/query/ManiphestTaskQuery.php',
'ManiphestTaskRelationship' => 'applications/maniphest/relationship/ManiphestTaskRelationship.php',
'ManiphestTaskResultListView' => 'applications/maniphest/view/ManiphestTaskResultListView.php', 'ManiphestTaskResultListView' => 'applications/maniphest/view/ManiphestTaskResultListView.php',
'ManiphestTaskSearchEngine' => 'applications/maniphest/query/ManiphestTaskSearchEngine.php', 'ManiphestTaskSearchEngine' => 'applications/maniphest/query/ManiphestTaskSearchEngine.php',
'ManiphestTaskStatus' => 'applications/maniphest/constants/ManiphestTaskStatus.php', 'ManiphestTaskStatus' => 'applications/maniphest/constants/ManiphestTaskStatus.php',
@ -2860,6 +2865,8 @@ phutil_register_library_map(array(
'PhabricatorObjectMentionedByObjectEdgeType' => 'applications/transactions/edges/PhabricatorObjectMentionedByObjectEdgeType.php', 'PhabricatorObjectMentionedByObjectEdgeType' => 'applications/transactions/edges/PhabricatorObjectMentionedByObjectEdgeType.php',
'PhabricatorObjectMentionsObjectEdgeType' => 'applications/transactions/edges/PhabricatorObjectMentionsObjectEdgeType.php', 'PhabricatorObjectMentionsObjectEdgeType' => 'applications/transactions/edges/PhabricatorObjectMentionsObjectEdgeType.php',
'PhabricatorObjectQuery' => 'applications/phid/query/PhabricatorObjectQuery.php', 'PhabricatorObjectQuery' => 'applications/phid/query/PhabricatorObjectQuery.php',
'PhabricatorObjectRelationship' => 'applications/search/relationship/PhabricatorObjectRelationship.php',
'PhabricatorObjectRelationshipList' => 'applications/search/relationship/PhabricatorObjectRelationshipList.php',
'PhabricatorObjectRemarkupRule' => 'infrastructure/markup/rule/PhabricatorObjectRemarkupRule.php', 'PhabricatorObjectRemarkupRule' => 'infrastructure/markup/rule/PhabricatorObjectRemarkupRule.php',
'PhabricatorObjectSelectorDialog' => 'view/control/PhabricatorObjectSelectorDialog.php', 'PhabricatorObjectSelectorDialog' => 'view/control/PhabricatorObjectSelectorDialog.php',
'PhabricatorOffsetPagedQuery' => 'infrastructure/query/PhabricatorOffsetPagedQuery.php', 'PhabricatorOffsetPagedQuery' => 'infrastructure/query/PhabricatorOffsetPagedQuery.php',
@ -3389,6 +3396,7 @@ phutil_register_library_map(array(
'PhabricatorSearchOrderController' => 'applications/search/controller/PhabricatorSearchOrderController.php', 'PhabricatorSearchOrderController' => 'applications/search/controller/PhabricatorSearchOrderController.php',
'PhabricatorSearchOrderField' => 'applications/search/field/PhabricatorSearchOrderField.php', 'PhabricatorSearchOrderField' => 'applications/search/field/PhabricatorSearchOrderField.php',
'PhabricatorSearchRelationship' => 'applications/search/constants/PhabricatorSearchRelationship.php', 'PhabricatorSearchRelationship' => 'applications/search/constants/PhabricatorSearchRelationship.php',
'PhabricatorSearchRelationshipController' => 'applications/search/controller/PhabricatorSearchRelationshipController.php',
'PhabricatorSearchResultBucket' => 'applications/search/buckets/PhabricatorSearchResultBucket.php', 'PhabricatorSearchResultBucket' => 'applications/search/buckets/PhabricatorSearchResultBucket.php',
'PhabricatorSearchResultBucketGroup' => 'applications/search/buckets/PhabricatorSearchResultBucketGroup.php', 'PhabricatorSearchResultBucketGroup' => 'applications/search/buckets/PhabricatorSearchResultBucketGroup.php',
'PhabricatorSearchResultView' => 'applications/search/view/PhabricatorSearchResultView.php', 'PhabricatorSearchResultView' => 'applications/search/view/PhabricatorSearchResultView.php',
@ -3834,7 +3842,6 @@ phutil_register_library_map(array(
'PhluxVariablePHIDType' => 'applications/phlux/phid/PhluxVariablePHIDType.php', 'PhluxVariablePHIDType' => 'applications/phlux/phid/PhluxVariablePHIDType.php',
'PhluxVariableQuery' => 'applications/phlux/query/PhluxVariableQuery.php', 'PhluxVariableQuery' => 'applications/phlux/query/PhluxVariableQuery.php',
'PhluxViewController' => 'applications/phlux/controller/PhluxViewController.php', 'PhluxViewController' => 'applications/phlux/controller/PhluxViewController.php',
'PholioActionMenuEventListener' => 'applications/pholio/event/PholioActionMenuEventListener.php',
'PholioController' => 'applications/pholio/controller/PholioController.php', 'PholioController' => 'applications/pholio/controller/PholioController.php',
'PholioDAO' => 'applications/pholio/storage/PholioDAO.php', 'PholioDAO' => 'applications/pholio/storage/PholioDAO.php',
'PholioDefaultEditCapability' => 'applications/pholio/capability/PholioDefaultEditCapability.php', 'PholioDefaultEditCapability' => 'applications/pholio/capability/PholioDefaultEditCapability.php',
@ -4662,7 +4669,6 @@ phutil_register_library_map(array(
), ),
'DifferentialAction' => 'Phobject', 'DifferentialAction' => 'Phobject',
'DifferentialActionEmailCommand' => 'MetaMTAEmailTransactionCommand', 'DifferentialActionEmailCommand' => 'MetaMTAEmailTransactionCommand',
'DifferentialActionMenuEventListener' => 'PhabricatorEventListener',
'DifferentialAddCommentView' => 'AphrontView', 'DifferentialAddCommentView' => 'AphrontView',
'DifferentialAdjustmentMapTestCase' => 'PhutilTestCase', 'DifferentialAdjustmentMapTestCase' => 'PhutilTestCase',
'DifferentialAffectedPath' => 'DifferentialDAO', 'DifferentialAffectedPath' => 'DifferentialDAO',
@ -5906,8 +5912,13 @@ phutil_register_library_map(array(
'ManiphestTaskEditController' => 'ManiphestController', 'ManiphestTaskEditController' => 'ManiphestController',
'ManiphestTaskFulltextEngine' => 'PhabricatorFulltextEngine', 'ManiphestTaskFulltextEngine' => 'PhabricatorFulltextEngine',
'ManiphestTaskHasCommitEdgeType' => 'PhabricatorEdgeType', 'ManiphestTaskHasCommitEdgeType' => 'PhabricatorEdgeType',
'ManiphestTaskHasCommitRelationship' => 'ManiphestTaskRelationship',
'ManiphestTaskHasMockEdgeType' => 'PhabricatorEdgeType', 'ManiphestTaskHasMockEdgeType' => 'PhabricatorEdgeType',
'ManiphestTaskHasMockRelationship' => 'ManiphestTaskRelationship',
'ManiphestTaskHasParentRelationship' => 'ManiphestTaskRelationship',
'ManiphestTaskHasRevisionEdgeType' => 'PhabricatorEdgeType', 'ManiphestTaskHasRevisionEdgeType' => 'PhabricatorEdgeType',
'ManiphestTaskHasRevisionRelationship' => 'ManiphestTaskRelationship',
'ManiphestTaskHasSubtaskRelationship' => 'ManiphestTaskRelationship',
'ManiphestTaskHeraldField' => 'HeraldField', 'ManiphestTaskHeraldField' => 'HeraldField',
'ManiphestTaskHeraldFieldGroup' => 'HeraldFieldGroup', 'ManiphestTaskHeraldFieldGroup' => 'HeraldFieldGroup',
'ManiphestTaskListController' => 'ManiphestController', 'ManiphestTaskListController' => 'ManiphestController',
@ -5923,6 +5934,7 @@ phutil_register_library_map(array(
'ManiphestTaskPriorityHeraldAction' => 'HeraldAction', 'ManiphestTaskPriorityHeraldAction' => 'HeraldAction',
'ManiphestTaskPriorityHeraldField' => 'ManiphestTaskHeraldField', 'ManiphestTaskPriorityHeraldField' => 'ManiphestTaskHeraldField',
'ManiphestTaskQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'ManiphestTaskQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'ManiphestTaskRelationship' => 'PhabricatorObjectRelationship',
'ManiphestTaskResultListView' => 'ManiphestView', 'ManiphestTaskResultListView' => 'ManiphestView',
'ManiphestTaskSearchEngine' => 'PhabricatorApplicationSearchEngine', 'ManiphestTaskSearchEngine' => 'PhabricatorApplicationSearchEngine',
'ManiphestTaskStatus' => 'ManiphestConstants', 'ManiphestTaskStatus' => 'ManiphestConstants',
@ -7547,6 +7559,8 @@ phutil_register_library_map(array(
'PhabricatorObjectMentionedByObjectEdgeType' => 'PhabricatorEdgeType', 'PhabricatorObjectMentionedByObjectEdgeType' => 'PhabricatorEdgeType',
'PhabricatorObjectMentionsObjectEdgeType' => 'PhabricatorEdgeType', 'PhabricatorObjectMentionsObjectEdgeType' => 'PhabricatorEdgeType',
'PhabricatorObjectQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhabricatorObjectQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorObjectRelationship' => 'Phobject',
'PhabricatorObjectRelationshipList' => 'Phobject',
'PhabricatorObjectRemarkupRule' => 'PhutilRemarkupRule', 'PhabricatorObjectRemarkupRule' => 'PhutilRemarkupRule',
'PhabricatorObjectSelectorDialog' => 'Phobject', 'PhabricatorObjectSelectorDialog' => 'Phobject',
'PhabricatorOffsetPagedQuery' => 'PhabricatorQuery', 'PhabricatorOffsetPagedQuery' => 'PhabricatorQuery',
@ -8204,6 +8218,7 @@ phutil_register_library_map(array(
'PhabricatorSearchOrderController' => 'PhabricatorSearchBaseController', 'PhabricatorSearchOrderController' => 'PhabricatorSearchBaseController',
'PhabricatorSearchOrderField' => 'PhabricatorSearchField', 'PhabricatorSearchOrderField' => 'PhabricatorSearchField',
'PhabricatorSearchRelationship' => 'Phobject', 'PhabricatorSearchRelationship' => 'Phobject',
'PhabricatorSearchRelationshipController' => 'PhabricatorSearchBaseController',
'PhabricatorSearchResultBucket' => 'Phobject', 'PhabricatorSearchResultBucket' => 'Phobject',
'PhabricatorSearchResultBucketGroup' => 'Phobject', 'PhabricatorSearchResultBucketGroup' => 'Phobject',
'PhabricatorSearchResultView' => 'AphrontView', 'PhabricatorSearchResultView' => 'AphrontView',
@ -8730,7 +8745,6 @@ phutil_register_library_map(array(
'PhluxVariablePHIDType' => 'PhabricatorPHIDType', 'PhluxVariablePHIDType' => 'PhabricatorPHIDType',
'PhluxVariableQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhluxVariableQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhluxViewController' => 'PhluxController', 'PhluxViewController' => 'PhluxController',
'PholioActionMenuEventListener' => 'PhabricatorEventListener',
'PholioController' => 'PhabricatorController', 'PholioController' => 'PhabricatorController',
'PholioDAO' => 'PhabricatorLiskDAO', 'PholioDAO' => 'PhabricatorLiskDAO',
'PholioDefaultEditCapability' => 'PhabricatorPolicyCapability', 'PholioDefaultEditCapability' => 'PhabricatorPolicyCapability',

View file

@ -225,10 +225,10 @@ final class PhabricatorAuthStartController
} }
// Often, users end up here by clicking a disabled action link in the UI // Often, users end up here by clicking a disabled action link in the UI
// (for example, they might click "Edit Blocking Tasks" on a Maniphest // (for example, they might click "Edit Subtasks" on a Maniphest task
// task page). After they log in we want to send them back to that main // page). After they log in we want to send them back to that main object
// object page if we can, since it's confusing to end up on a standalone // page if we can, since it's confusing to end up on a standalone page with
// page with only a dialog (particularly if that dialog is another error, // only a dialog (particularly if that dialog is another error,
// like a policy exception). // like a policy exception).
$via_header = AphrontRequest::getViaHeaderName(); $via_header = AphrontRequest::getViaHeaderName();

View file

@ -47,7 +47,7 @@ final class PhabricatorBadgesEditor
case PhabricatorBadgesTransaction::TYPE_ICON: case PhabricatorBadgesTransaction::TYPE_ICON:
return $object->getIcon(); return $object->getIcon();
case PhabricatorBadgesTransaction::TYPE_QUALITY: case PhabricatorBadgesTransaction::TYPE_QUALITY:
return $object->getQuality(); return (int)$object->getQuality();
case PhabricatorBadgesTransaction::TYPE_STATUS: case PhabricatorBadgesTransaction::TYPE_STATUS:
return $object->getStatus(); return $object->getStatus();
case PhabricatorBadgesTransaction::TYPE_AWARD: case PhabricatorBadgesTransaction::TYPE_AWARD:

View file

@ -105,14 +105,14 @@ final class PhabricatorBadgesTransaction
} }
break; break;
case self::TYPE_QUALITY: case self::TYPE_QUALITY:
$qual_new = PhabricatorBadgesQuality::getQualityName($new);
$qual_old = PhabricatorBadgesQuality::getQualityName($old);
if ($old === null) { if ($old === null) {
return pht( return pht(
'%s set the quality for this badge as "%s".', '%s set the quality for this badge as "%s".',
$this->renderHandleLink($author_phid), $this->renderHandleLink($author_phid),
$new); $qual_new);
} else { } else {
$qual_new = PhabricatorBadgesQuality::getQualityName($new);
$qual_old = PhabricatorBadgesQuality::getQualityName($old);
return pht( return pht(
'%s updated the quality for this badge from "%s" to "%s".', '%s updated the quality for this badge from "%s" to "%s".',
$this->renderHandleLink($author_phid), $this->renderHandleLink($author_phid),

View file

@ -474,8 +474,11 @@ abstract class PhabricatorController extends AphrontController {
public function newCurtainView($object) { public function newCurtainView($object) {
$viewer = $this->getViewer(); $viewer = $this->getViewer();
$action_id = celerity_generate_unique_node_id();
$action_list = id(new PhabricatorActionListView()) $action_list = id(new PhabricatorActionListView())
->setViewer($viewer); ->setViewer($viewer)
->setID($action_id);
// NOTE: Applications (objects of class PhabricatorApplication) can't // NOTE: Applications (objects of class PhabricatorApplication) can't
// currently be set here, although they don't need any of the extensions // currently be set here, although they don't need any of the extensions

View file

@ -13,6 +13,7 @@ final class CelerityStaticResourceResponse extends Phobject {
private $packaged; private $packaged;
private $metadata = array(); private $metadata = array();
private $metadataBlock = 0; private $metadataBlock = 0;
private $metadataLocked;
private $behaviors = array(); private $behaviors = array();
private $hasRendered = array(); private $hasRendered = array();
private $postprocessorKey; private $postprocessorKey;
@ -24,6 +25,13 @@ final class CelerityStaticResourceResponse extends Phobject {
} }
public function addMetadata($metadata) { public function addMetadata($metadata) {
if ($this->metadataLocked) {
throw new Exception(
pht(
'Attempting to add more metadata after metadata has been '.
'locked.'));
}
$id = count($this->metadata); $id = count($this->metadata);
$this->metadata[$id] = $metadata; $this->metadata[$id] = $metadata;
return $this->metadataBlock.'_'.$id; return $this->metadataBlock.'_'.$id;
@ -189,6 +197,8 @@ final class CelerityStaticResourceResponse extends Phobject {
} }
public function renderHTMLFooter() { public function renderHTMLFooter() {
$this->metadataLocked = true;
$data = array(); $data = array();
if ($this->metadata) { if ($this->metadata) {
$json_metadata = AphrontResponse::encodeJSONForHTTPResponse( $json_metadata = AphrontResponse::encodeJSONForHTTPResponse(

View file

@ -43,7 +43,6 @@ final class PhabricatorDifferentialApplication extends PhabricatorApplication {
public function getEventListeners() { public function getEventListeners() {
return array( return array(
new DifferentialActionMenuEventListener(),
new DifferentialLandingActionMenuEventListener(), new DifferentialLandingActionMenuEventListener(),
); );
} }

View file

@ -1,50 +0,0 @@
<?php
final class DifferentialActionMenuEventListener
extends PhabricatorEventListener {
public function register() {
$this->listen(PhabricatorEventType::TYPE_UI_DIDRENDERACTIONS);
}
public function handleEvent(PhutilEvent $event) {
switch ($event->getType()) {
case PhabricatorEventType::TYPE_UI_DIDRENDERACTIONS:
$this->handleActionsEvent($event);
break;
}
}
private function handleActionsEvent(PhutilEvent $event) {
$object = $event->getValue('object');
$actions = null;
if ($object instanceof ManiphestTask) {
$actions = $this->renderTaskItems($event);
$this->addActionMenuItems($event, $actions);
}
}
private function renderTaskItems(PhutilEvent $event) {
if (!$this->canUseApplication($event->getUser())) {
return null;
}
$task = $event->getValue('object');
$phid = $task->getPHID();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$event->getUser(),
$task,
PhabricatorPolicyCapability::CAN_EDIT);
return id(new PhabricatorActionView())
->setName(pht('Edit Differential Revisions'))
->setHref("/search/attach/{$phid}/DREV/")
->setIcon('fa-cog')
->setDisabled(!$can_edit)
->setWorkflow(true);
}
}

View file

@ -18,25 +18,14 @@ final class DiffusionRepositoryEditDangerousController
->getPanelURI(); ->getPanelURI();
if (!$repository->canAllowDangerousChanges()) { if (!$repository->canAllowDangerousChanges()) {
if ($repository->isSVN()) { return $this->newDialog()
return $this->newDialog() ->setTitle(pht('Unprotectable Repository'))
->setTitle(pht('Not in Danger')) ->appendParagraph(
->appendParagraph( pht(
pht( 'This repository can not be protected from dangerous changes '.
'It is not possible for users to push any dangerous changes '. 'because Phabricator does not control what users are allowed '.
'to a Subversion repository. Pushes to a Subversion repository '. 'to push to it.'))
'can always be reverted and never destroy data.')) ->addCancelButton($panel_uri);
->addCancelButton($panel_uri);
} else {
return $this->newDialog()
->setTitle(pht('Unprotectable Repository'))
->appendParagraph(
pht(
'This repository can not be protected from dangerous changes '.
'because Phabricator does not control what users are allowed '.
'to push to it.'))
->addCancelButton($panel_uri);
}
} }
if ($request->isFormPost()) { if ($request->isFormPost()) {
@ -57,18 +46,34 @@ final class DiffusionRepositoryEditDangerousController
if ($repository->shouldAllowDangerousChanges()) { if ($repository->shouldAllowDangerousChanges()) {
$title = pht('Prevent Dangerous Changes'); $title = pht('Prevent Dangerous Changes');
$body = pht(
'It will no longer be possible to delete branches from this '. if ($repository->isSVN()) {
'repository, or %s push to this repository.', $body = pht(
$force); 'It will no longer be possible to edit revprops in this '.
'repository.');
} else {
$body = pht(
'It will no longer be possible to delete branches from this '.
'repository, or %s push to this repository.',
$force);
}
$submit = pht('Prevent Dangerous Changes'); $submit = pht('Prevent Dangerous Changes');
} else { } else {
$title = pht('Allow Dangerous Changes'); $title = pht('Allow Dangerous Changes');
$body = pht( if ($repository->isSVN()) {
'If you allow dangerous changes, it will be possible to delete '. $body = pht(
'branches and %s push this repository. These operations can '. 'If you allow dangerous changes, it will be possible to edit '.
'alter a repository in a way that is difficult to recover from.', 'reprops in this repository, including arbitrarily rewriting '.
$force); 'commit messages. These operations can alter a repository in a '.
'way that is difficult to recover from.');
} else {
$body = pht(
'If you allow dangerous changes, it will be possible to delete '.
'branches and %s push this repository. These operations can '.
'alter a repository in a way that is difficult to recover from.',
$force);
}
$submit = pht('Allow Dangerous Changes'); $submit = pht('Allow Dangerous Changes');
} }

View file

@ -31,24 +31,50 @@ final class DiffusionRepositoryDatasource
"phabricator-search-icon phui-font-fa phui-icon-view {$type_icon}"; "phabricator-search-icon phui-font-fa phui-icon-view {$type_icon}";
$results = array(); $results = array();
foreach ($repos as $repo) { foreach ($repos as $repository) {
$display_name = $repo->getMonogram().' '.$repo->getName(); $monogram = $repository->getMonogram();
$name = $repository->getName();
$name = $display_name; $display_name = "{$monogram} {$name}";
$slug = $repo->getRepositorySlug();
$parts = array();
$parts[] = $name;
$slug = $repository->getRepositorySlug();
if (strlen($slug)) { if (strlen($slug)) {
$name = "{$name} {$slug}"; $parts[] = $slug;
} }
$results[] = id(new PhabricatorTypeaheadResult()) $callsign = $repository->getCallsign();
if ($callsign) {
$parts[] = $callsign;
}
foreach ($repository->getAllMonograms() as $monogram) {
$parts[] = $monogram;
}
$name = implode(' ', $parts);
$vcs = $repository->getVersionControlSystem();
$vcs_type = PhabricatorRepositoryType::getNameForRepositoryType($vcs);
$result = id(new PhabricatorTypeaheadResult())
->setName($name) ->setName($name)
->setDisplayName($display_name) ->setDisplayName($display_name)
->setURI($repo->getURI()) ->setURI($repository->getURI())
->setPHID($repo->getPHID()) ->setPHID($repository->getPHID())
->setPriorityString($repo->getMonogram()) ->setPriorityString($repository->getMonogram())
->setPriorityType('repo') ->setPriorityType('repo')
->setImageSprite($image_sprite) ->setImageSprite($image_sprite)
->setDisplayType(pht('Repository')); ->setDisplayType(pht('Repository'))
->addAttribute($vcs_type);
if (!$repository->isTracked()) {
$result->setClosed(pht('Inactive'));
}
$results[] = $result;
} }
return $results; return $results;

View file

@ -43,7 +43,10 @@ final class DiffusionTaggedRepositoriesFunctionDatasource
->setColor(null) ->setColor(null)
->setPHID('tagged('.$result->getPHID().')') ->setPHID('tagged('.$result->getPHID().')')
->setDisplayName(pht('Tagged: %s', $result->getDisplayName())) ->setDisplayName(pht('Tagged: %s', $result->getDisplayName()))
->setName('tagged '.$result->getName()); ->setName('tagged '.$result->getName())
->resetAttributes()
->addAttribute(pht('Function'))
->addAttribute(pht('Select repositories tagged with this project.'));
} }
return $results; return $results;

View file

@ -7,6 +7,10 @@ final class DrydockBlueprintDatasource
return pht('Type a blueprint name...'); return pht('Type a blueprint name...');
} }
public function getBrowseTitle() {
return pht('Browse Blueprints');
}
public function getDatasourceApplicationClass() { public function getDatasourceApplicationClass() {
return 'PhabricatorDrydockApplication'; return 'PhabricatorDrydockApplication';
} }
@ -37,6 +41,9 @@ final class DrydockBlueprintDatasource
$result->setClosed(pht('Disabled')); $result->setClosed(pht('Disabled'));
} }
$result->addAttribute(
$blueprint->getImplementation()->getBlueprintName());
$results[] = $result; $results[] = $result;
} }

View file

@ -80,6 +80,8 @@ abstract class HeraldField extends Phobject {
HeraldAdapter::CONDITION_NOT_CONTAINS, HeraldAdapter::CONDITION_NOT_CONTAINS,
HeraldAdapter::CONDITION_REGEXP, HeraldAdapter::CONDITION_REGEXP,
HeraldAdapter::CONDITION_NOT_REGEXP, HeraldAdapter::CONDITION_NOT_REGEXP,
HeraldAdapter::CONDITION_EXISTS,
HeraldAdapter::CONDITION_NOT_EXISTS,
); );
case self::STANDARD_TEXT_MAP: case self::STANDARD_TEXT_MAP:
return array( return array(
@ -107,7 +109,13 @@ abstract class HeraldField extends Phobject {
case self::STANDARD_TEXT: case self::STANDARD_TEXT:
case self::STANDARD_TEXT_LIST: case self::STANDARD_TEXT_LIST:
case self::STANDARD_TEXT_MAP: case self::STANDARD_TEXT_MAP:
return new HeraldTextFieldValue(); switch ($condition) {
case HeraldAdapter::CONDITION_EXISTS:
case HeraldAdapter::CONDITION_NOT_EXISTS:
return new HeraldEmptyFieldValue();
default:
return new HeraldTextFieldValue();
}
case self::STANDARD_PHID: case self::STANDARD_PHID:
case self::STANDARD_PHID_NULLABLE: case self::STANDARD_PHID_NULLABLE:
case self::STANDARD_PHID_LIST: case self::STANDARD_PHID_LIST:

View file

@ -166,15 +166,6 @@ final class ManiphestTaskDetailController extends ManiphestController {
->setDisabled(!$can_edit) ->setDisabled(!$can_edit)
->setWorkflow(!$can_edit)); ->setWorkflow(!$can_edit));
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Merge Duplicates In'))
->setHref("/search/attach/{$phid}/TASK/merge/")
->setWorkflow(true)
->setIcon('fa-compress')
->setDisabled(!$can_edit)
->setWorkflow(true));
$edit_config = $edit_engine->loadDefaultEditConfiguration(); $edit_config = $edit_engine->loadDefaultEditConfiguration();
$can_create = (bool)$edit_config; $can_create = (bool)$edit_config;
@ -195,23 +186,45 @@ final class ManiphestTaskDetailController extends ManiphestController {
$edit_uri = $this->getApplicationURI($edit_uri); $edit_uri = $this->getApplicationURI($edit_uri);
} }
$curtain->addAction( $task_submenu = array();
id(new PhabricatorActionView())
->setName(pht('Create Subtask')) $task_submenu[] = id(new PhabricatorActionView())
->setHref($edit_uri) ->setName(pht('Create Subtask'))
->setIcon('fa-level-down') ->setHref($edit_uri)
->setDisabled(!$can_create) ->setIcon('fa-level-down')
->setWorkflow(!$can_create)); ->setDisabled(!$can_create)
->setWorkflow(!$can_create);
$relationship_list = PhabricatorObjectRelationshipList::newForObject(
$viewer,
$task);
$parent_key = ManiphestTaskHasParentRelationship::RELATIONSHIPKEY;
$subtask_key = ManiphestTaskHasSubtaskRelationship::RELATIONSHIPKEY;
$task_submenu[] = $relationship_list->getRelationship($parent_key)
->newAction($task);
$task_submenu[] = $relationship_list->getRelationship($subtask_key)
->newAction($task);
$task_submenu[] = id(new PhabricatorActionView())
->setName(pht('Merge Duplicates In'))
->setHref("/search/attach/{$phid}/TASK/merge/")
->setIcon('fa-compress')
->setDisabled(!$can_edit)
->setWorkflow(true);
$curtain->addAction( $curtain->addAction(
id(new PhabricatorActionView()) id(new PhabricatorActionView())
->setName(pht('Edit Blocking Tasks')) ->setName(pht('Edit Related Tasks...'))
->setHref("/search/attach/{$phid}/TASK/blocks/") ->setIcon('fa-anchor')
->setWorkflow(true) ->setSubmenu($task_submenu));
->setIcon('fa-link')
->setDisabled(!$can_edit)
->setWorkflow(true));
$relationship_submenu = $relationship_list->newActionMenu();
if ($relationship_submenu) {
$curtain->addAction($relationship_submenu);
}
$owner_phid = $task->getOwnerPHID(); $owner_phid = $task->getOwnerPHID();
$author_phid = $task->getAuthorPHID(); $author_phid = $task->getAuthorPHID();
@ -277,9 +290,9 @@ final class ManiphestTaskDetailController extends ManiphestController {
$edge_types = array( $edge_types = array(
ManiphestTaskDependedOnByTaskEdgeType::EDGECONST ManiphestTaskDependedOnByTaskEdgeType::EDGECONST
=> pht('Blocks'), => pht('Parent Tasks'),
ManiphestTaskDependsOnTaskEdgeType::EDGECONST ManiphestTaskDependsOnTaskEdgeType::EDGECONST
=> pht('Blocked By'), => pht('Subtasks'),
ManiphestTaskHasRevisionEdgeType::EDGECONST ManiphestTaskHasRevisionEdgeType::EDGECONST
=> pht('Differential Revisions'), => pht('Differential Revisions'),
ManiphestTaskHasMockEdgeType::EDGECONST ManiphestTaskHasMockEdgeType::EDGECONST

View file

@ -17,7 +17,7 @@ final class ManiphestTaskDependedOnByTaskEdgeType extends PhabricatorEdgeType {
$add_edges) { $add_edges) {
return pht( return pht(
'%s added %s blocked task(s): %s.', '%s added %s parent task(s): %s.',
$actor, $actor,
$add_count, $add_count,
$add_edges); $add_edges);
@ -29,7 +29,7 @@ final class ManiphestTaskDependedOnByTaskEdgeType extends PhabricatorEdgeType {
$rem_edges) { $rem_edges) {
return pht( return pht(
'%s removed %s blocked task(s): %s.', '%s removed %s parent task(s): %s.',
$actor, $actor,
$rem_count, $rem_count,
$rem_edges); $rem_edges);
@ -44,7 +44,7 @@ final class ManiphestTaskDependedOnByTaskEdgeType extends PhabricatorEdgeType {
$rem_edges) { $rem_edges) {
return pht( return pht(
'%s edited blocked task(s), added %s: %s; removed %s: %s.', '%s edited parent task(s), added %s: %s; removed %s: %s.',
$actor, $actor,
$add_count, $add_count,
$add_edges, $add_edges,
@ -59,7 +59,7 @@ final class ManiphestTaskDependedOnByTaskEdgeType extends PhabricatorEdgeType {
$add_edges) { $add_edges) {
return pht( return pht(
'%s added %s blocked task(s) for %s: %s.', '%s added %s parent task(s) for %s: %s.',
$actor, $actor,
$add_count, $add_count,
$object, $object,
@ -73,7 +73,7 @@ final class ManiphestTaskDependedOnByTaskEdgeType extends PhabricatorEdgeType {
$rem_edges) { $rem_edges) {
return pht( return pht(
'%s removed %s blocked task(s) for %s: %s.', '%s removed %s parent task(s) for %s: %s.',
$actor, $actor,
$rem_count, $rem_count,
$object, $object,
@ -90,7 +90,7 @@ final class ManiphestTaskDependedOnByTaskEdgeType extends PhabricatorEdgeType {
$rem_edges) { $rem_edges) {
return pht( return pht(
'%s edited blocked task(s) for %s, added %s: %s; removed %s: %s.', '%s edited parent task(s) for %s, added %s: %s; removed %s: %s.',
$actor, $actor,
$object, $object,
$add_count, $add_count,

View file

@ -22,7 +22,7 @@ final class ManiphestTaskDependsOnTaskEdgeType extends PhabricatorEdgeType {
$add_edges) { $add_edges) {
return pht( return pht(
'%s added %s blocking task(s): %s.', '%s added %s subtask(s): %s.',
$actor, $actor,
$add_count, $add_count,
$add_edges); $add_edges);
@ -34,7 +34,7 @@ final class ManiphestTaskDependsOnTaskEdgeType extends PhabricatorEdgeType {
$rem_edges) { $rem_edges) {
return pht( return pht(
'%s removed %s blocking task(s): %s.', '%s removed %s subtask(s): %s.',
$actor, $actor,
$rem_count, $rem_count,
$rem_edges); $rem_edges);
@ -49,7 +49,7 @@ final class ManiphestTaskDependsOnTaskEdgeType extends PhabricatorEdgeType {
$rem_edges) { $rem_edges) {
return pht( return pht(
'%s edited blocking task(s), added %s: %s; removed %s: %s.', '%s edited subtask(s), added %s: %s; removed %s: %s.',
$actor, $actor,
$add_count, $add_count,
$add_edges, $add_edges,
@ -64,7 +64,7 @@ final class ManiphestTaskDependsOnTaskEdgeType extends PhabricatorEdgeType {
$add_edges) { $add_edges) {
return pht( return pht(
'%s added %s blocking task(s) for %s: %s.', '%s added %s subtask(s) for %s: %s.',
$actor, $actor,
$add_count, $add_count,
$object, $object,
@ -78,7 +78,7 @@ final class ManiphestTaskDependsOnTaskEdgeType extends PhabricatorEdgeType {
$rem_edges) { $rem_edges) {
return pht( return pht(
'%s removed %s blocking task(s) for %s: %s.', '%s removed %s subtask(s) for %s: %s.',
$actor, $actor,
$rem_count, $rem_count,
$object, $object,
@ -95,7 +95,7 @@ final class ManiphestTaskDependsOnTaskEdgeType extends PhabricatorEdgeType {
$rem_edges) { $rem_edges) {
return pht( return pht(
'%s edited blocking task(s) for %s, added %s: %s; removed %s: %s.', '%s edited subtask(s) for %s, added %s: %s; removed %s: %s.',
$actor, $actor,
$object, $object,
$add_count, $add_count,

View file

@ -325,7 +325,7 @@ final class ManiphestTransactionEditor
ManiphestTransaction::MAILTAG_PROJECTS => ManiphestTransaction::MAILTAG_PROJECTS =>
pht("A task's associated projects change."), pht("A task's associated projects change."),
ManiphestTransaction::MAILTAG_UNBLOCK => ManiphestTransaction::MAILTAG_UNBLOCK =>
pht('One of the tasks a task is blocked by changes status.'), pht("One of a task's subtasks changes status."),
ManiphestTransaction::MAILTAG_COLUMN => ManiphestTransaction::MAILTAG_COLUMN =>
pht('A task is moved between columns on a workboard.'), pht('A task is moved between columns on a workboard.'),
ManiphestTransaction::MAILTAG_COMMENT => ManiphestTransaction::MAILTAG_COMMENT =>

View file

@ -0,0 +1,43 @@
<?php
final class ManiphestTaskHasCommitRelationship
extends ManiphestTaskRelationship {
const RELATIONSHIPKEY = 'task.has-commit';
public function getEdgeConstant() {
return ManiphestTaskHasCommitEdgeType::EDGECONST;
}
protected function getActionName() {
return pht('Edit Commits');
}
protected function getActionIcon() {
return 'fa-code';
}
public function shouldAppearInActionMenu() {
// TODO: For now, the default search for commits is not very good, so
// it is hard to find objects to link to. Until that works better, just
// hide this item.
return false;
}
public function canRelateObjects($src, $dst) {
return ($dst instanceof PhabricatorRepositoryCommit);
}
public function getDialogTitleText() {
return pht('Edit Related Commits');
}
public function getDialogHeaderText() {
return pht('Current Commits');
}
public function getDialogButtonText() {
return pht('Save Related Commits');
}
}

View file

@ -0,0 +1,36 @@
<?php
final class ManiphestTaskHasMockRelationship
extends ManiphestTaskRelationship {
const RELATIONSHIPKEY = 'task.has-mock';
public function getEdgeConstant() {
return ManiphestTaskHasMockEdgeType::EDGECONST;
}
protected function getActionName() {
return pht('Edit Mocks');
}
protected function getActionIcon() {
return 'fa-camera-retro';
}
public function canRelateObjects($src, $dst) {
return ($dst instanceof PholioMock);
}
public function getDialogTitleText() {
return pht('Edit Related Mocks');
}
public function getDialogHeaderText() {
return pht('Current Mocks');
}
public function getDialogButtonText() {
return pht('Save Related Mocks');
}
}

View file

@ -0,0 +1,40 @@
<?php
final class ManiphestTaskHasParentRelationship
extends ManiphestTaskRelationship {
const RELATIONSHIPKEY = 'task.has-parent';
public function getEdgeConstant() {
return ManiphestTaskDependedOnByTaskEdgeType::EDGECONST;
}
protected function getActionName() {
return pht('Edit Parent Tasks');
}
protected function getActionIcon() {
return 'fa-chevron-circle-up';
}
public function canRelateObjects($src, $dst) {
return ($dst instanceof ManiphestTask);
}
public function shouldAppearInActionMenu() {
return false;
}
public function getDialogTitleText() {
return pht('Edit Parent Tasks');
}
public function getDialogHeaderText() {
return pht('Current Parent Tasks');
}
public function getDialogButtonText() {
return pht('Save Parent Tasks');
}
}

View file

@ -0,0 +1,36 @@
<?php
final class ManiphestTaskHasRevisionRelationship
extends ManiphestTaskRelationship {
const RELATIONSHIPKEY = 'task.has-revision';
public function getEdgeConstant() {
return ManiphestTaskHasRevisionEdgeType::EDGECONST;
}
protected function getActionName() {
return pht('Edit Revisions');
}
protected function getActionIcon() {
return 'fa-cog';
}
public function canRelateObjects($src, $dst) {
return ($dst instanceof DifferentialRevision);
}
public function getDialogTitleText() {
return pht('Edit Related Revisions');
}
public function getDialogHeaderText() {
return pht('Current Revisions');
}
public function getDialogButtonText() {
return pht('Save Related Revisions');
}
}

View file

@ -0,0 +1,40 @@
<?php
final class ManiphestTaskHasSubtaskRelationship
extends ManiphestTaskRelationship {
const RELATIONSHIPKEY = 'task.has-subtask';
public function getEdgeConstant() {
return ManiphestTaskDependsOnTaskEdgeType::EDGECONST;
}
protected function getActionName() {
return pht('Edit Subtasks');
}
protected function getActionIcon() {
return 'fa-chevron-circle-down';
}
public function canRelateObjects($src, $dst) {
return ($dst instanceof ManiphestTask);
}
public function shouldAppearInActionMenu() {
return false;
}
public function getDialogTitleText() {
return pht('Edit Subtasks');
}
public function getDialogHeaderText() {
return pht('Current Subtasks');
}
public function getDialogButtonText() {
return pht('Save Subtasks');
}
}

View file

@ -0,0 +1,19 @@
<?php
abstract class ManiphestTaskRelationship
extends PhabricatorObjectRelationship {
public function isEnabledForObject($object) {
$viewer = $this->getViewer();
$has_app = PhabricatorApplication::isClassInstalledForViewer(
'PhabricatorManiphestApplication',
$viewer);
if (!$has_app) {
return false;
}
return ($object instanceof ManiphestTask);
}
}

View file

@ -500,24 +500,24 @@ final class ManiphestTransaction
if ($this->getMetadataValue('blocker.new')) { if ($this->getMetadataValue('blocker.new')) {
return pht( return pht(
'%s created blocking task %s.', '%s created subtask %s.',
$this->renderHandleLink($author_phid), $this->renderHandleLink($author_phid),
$this->renderHandleLink($blocker_phid)); $this->renderHandleLink($blocker_phid));
} else if ($old_closed && !$new_closed) { } else if ($old_closed && !$new_closed) {
return pht( return pht(
'%s reopened blocking task %s as "%s".', '%s reopened subtask %s as "%s".',
$this->renderHandleLink($author_phid), $this->renderHandleLink($author_phid),
$this->renderHandleLink($blocker_phid), $this->renderHandleLink($blocker_phid),
$new_name); $new_name);
} else if (!$old_closed && $new_closed) { } else if (!$old_closed && $new_closed) {
return pht( return pht(
'%s closed blocking task %s as "%s".', '%s closed subtask %s as "%s".',
$this->renderHandleLink($author_phid), $this->renderHandleLink($author_phid),
$this->renderHandleLink($blocker_phid), $this->renderHandleLink($blocker_phid),
$new_name); $new_name);
} else { } else {
return pht( return pht(
'%s changed the status of blocking task %s from "%s" to "%s".', '%s changed the status of subtask %s from "%s" to "%s".',
$this->renderHandleLink($author_phid), $this->renderHandleLink($author_phid),
$this->renderHandleLink($blocker_phid), $this->renderHandleLink($blocker_phid),
$old_name, $old_name,
@ -753,21 +753,21 @@ final class ManiphestTransaction
if ($old_closed && !$new_closed) { if ($old_closed && !$new_closed) {
return pht( return pht(
'%s reopened %s, a task blocking %s, as "%s".', '%s reopened %s, a subtask of %s, as "%s".',
$this->renderHandleLink($author_phid), $this->renderHandleLink($author_phid),
$this->renderHandleLink($blocker_phid), $this->renderHandleLink($blocker_phid),
$this->renderHandleLink($object_phid), $this->renderHandleLink($object_phid),
$new_name); $new_name);
} else if (!$old_closed && $new_closed) { } else if (!$old_closed && $new_closed) {
return pht( return pht(
'%s closed %s, a task blocking %s, as "%s".', '%s closed %s, a subtask of %s, as "%s".',
$this->renderHandleLink($author_phid), $this->renderHandleLink($author_phid),
$this->renderHandleLink($blocker_phid), $this->renderHandleLink($blocker_phid),
$this->renderHandleLink($object_phid), $this->renderHandleLink($object_phid),
$new_name); $new_name);
} else { } else {
return pht( return pht(
'%s changed the status of %s, a task blocking %s, '. '%s changed the status of %s, a subtasktask of %s, '.
'from "%s" to "%s".', 'from "%s" to "%s".',
$this->renderHandleLink($author_phid), $this->renderHandleLink($author_phid),
$this->renderHandleLink($blocker_phid), $this->renderHandleLink($blocker_phid),

View file

@ -67,7 +67,8 @@ final class ManiphestTaskClosedStatusDatasource
->setName($name.' closed') ->setName($name.' closed')
->setDisplayName($name) ->setDisplayName($name)
->setPHID(self::FUNCTION_TOKEN) ->setPHID(self::FUNCTION_TOKEN)
->setUnique(true); ->setUnique(true)
->addAttribute(pht('Select any closed status.'));
} }
} }

View file

@ -67,7 +67,8 @@ final class ManiphestTaskOpenStatusDatasource
->setName($name.' open') ->setName($name.' open')
->setDisplayName($name) ->setDisplayName($name)
->setPHID(self::FUNCTION_TOKEN) ->setPHID(self::FUNCTION_TOKEN)
->setUnique(true); ->setUnique(true)
->addAttribute(pht('Select any open status.'));
} }
} }

View file

@ -32,7 +32,8 @@ final class ManiphestTaskPriorityDatasource
$result = id(new PhabricatorTypeaheadResult()) $result = id(new PhabricatorTypeaheadResult())
->setIcon(ManiphestTaskPriority::getTaskPriorityIcon($value)) ->setIcon(ManiphestTaskPriority::getTaskPriorityIcon($value))
->setPHID($value) ->setPHID($value)
->setName($name); ->setName($name)
->addAttribute(pht('Priority'));
if (ManiphestTaskPriority::isDisabledPriority($value)) { if (ManiphestTaskPriority::isDisabledPriority($value)) {
$result->setClosed(pht('Disabled')); $result->setClosed(pht('Disabled'));

View file

@ -35,6 +35,12 @@ final class ManiphestTaskStatusDatasource
->setPHID($value) ->setPHID($value)
->setName($name); ->setName($name);
if (ManiphestTaskStatus::isOpenStatus($value)) {
$result->addAttribute(pht('Open Status'));
} else {
$result->addAttribute(pht('Closed Status'));
}
if (ManiphestTaskStatus::isDisabledStatus($value)) { if (ManiphestTaskStatus::isDisabledStatus($value)) {
$result->setClosed(pht('Disabled')); $result->setClosed(pht('Disabled'));
} }

View file

@ -94,6 +94,10 @@ final class PhabricatorUser
* @return bool True if this is a standard, usable account. * @return bool True if this is a standard, usable account.
*/ */
public function isUserActivated() { public function isUserActivated() {
if (!$this->isLoggedIn()) {
return false;
}
if ($this->isOmnipotent()) { if ($this->isOmnipotent()) {
return true; return true;
} }

View file

@ -62,7 +62,8 @@ final class PhabricatorPeopleAnyOwnerDatasource
->setDisplayName($name) ->setDisplayName($name)
->setIcon('fa-certificate') ->setIcon('fa-certificate')
->setPHID(self::FUNCTION_TOKEN) ->setPHID(self::FUNCTION_TOKEN)
->setUnique(true); ->setUnique(true)
->addAttribute(pht('Select results with any owner.'));
} }
} }

View file

@ -3,18 +3,6 @@
final class PhabricatorPeopleDatasource final class PhabricatorPeopleDatasource
extends PhabricatorTypeaheadDatasource { extends PhabricatorTypeaheadDatasource {
private $enrichResults;
/**
* Controls enriched rendering, for global search. This is a bit hacky and
* should probably be handled in a more general way, but is fairly reasonable
* for now.
*/
public function setEnrichResults($enrich) {
$this->enrichResults = $enrich;
return $this;
}
public function getBrowseTitle() { public function getBrowseTitle() {
return pht('Browse Users'); return pht('Browse Users');
} }
@ -40,7 +28,9 @@ final class PhabricatorPeopleDatasource
$users = $this->executeQuery($query); $users = $this->executeQuery($query);
if ($this->enrichResults && $users) { $is_browse = $this->getIsBrowse();
if ($is_browse && $users) {
$phids = mpull($users, 'getPHID'); $phids = mpull($users, 'getPHID');
$handles = id(new PhabricatorHandleQuery()) $handles = id(new PhabricatorHandleQuery())
->setViewer($viewer) ->setViewer($viewer)
@ -50,6 +40,8 @@ final class PhabricatorPeopleDatasource
$results = array(); $results = array();
foreach ($users as $user) { foreach ($users as $user) {
$phid = $user->getPHID();
$closed = null; $closed = null;
if ($user->getIsDisabled()) { if ($user->getIsDisabled()) {
$closed = pht('Disabled'); $closed = pht('Disabled');
@ -64,7 +56,7 @@ final class PhabricatorPeopleDatasource
$result = id(new PhabricatorTypeaheadResult()) $result = id(new PhabricatorTypeaheadResult())
->setName($user->getFullName()) ->setName($user->getFullName())
->setURI('/p/'.$username.'/') ->setURI('/p/'.$username.'/')
->setPHID($user->getPHID()) ->setPHID($phid)
->setPriorityString($username) ->setPriorityString($username)
->setPriorityType('user') ->setPriorityType('user')
->setAutocomplete('@'.$username) ->setAutocomplete('@'.$username)
@ -74,13 +66,29 @@ final class PhabricatorPeopleDatasource
$result->setIcon('fa-envelope-o'); $result->setIcon('fa-envelope-o');
} }
if ($this->enrichResults) { if ($is_browse) {
$display_type = pht('User'); $handle = $handles[$phid];
$result
->setIcon($handle->getIcon())
->setImageURI($handle->getImageURI())
->addAttribute($handle->getSubtitle());
if ($user->getIsAdmin()) {
$result->addAttribute(
array(
id(new PHUIIconView())->setIcon('fa-star'),
' ',
pht('Administrator'),
));
}
if ($user->getIsAdmin()) { if ($user->getIsAdmin()) {
$display_type = pht('Administrator'); $display_type = pht('Administrator');
} else {
$display_type = pht('User');
} }
$result->setDisplayType($display_type); $result->setDisplayType($display_type);
$result->setImageURI($handles[$user->getPHID()]->getImageURI());
} }
$results[] = $result; $results[] = $result;

View file

@ -69,7 +69,8 @@ final class PhabricatorPeopleNoOwnerDatasource
->setDisplayName($name) ->setDisplayName($name)
->setIcon('fa-ban') ->setIcon('fa-ban')
->setPHID('none()') ->setPHID('none()')
->setUnique(true); ->setUnique(true)
->addAttribute(pht('Select results with no owner.'));
} }
} }

View file

@ -74,7 +74,8 @@ final class PhabricatorViewerDatasource
->setName(pht('Current Viewer')) ->setName(pht('Current Viewer'))
->setPHID('viewer()') ->setPHID('viewer()')
->setIcon('fa-user') ->setIcon('fa-user')
->setUnique(true); ->setUnique(true)
->addAttribute(pht('Select current viewer.'));
} }
} }

View file

@ -159,6 +159,12 @@ abstract class PhameLiveController extends PhameController {
// "Blogs" crumb into the crumbs list. // "Blogs" crumb into the crumbs list.
if ($is_external) { if ($is_external) {
$crumbs = new PHUICrumbsView(); $crumbs = new PHUICrumbsView();
// Link back to parent site
if ($blog->getParentSite() && $blog->getParentDomain()) {
$crumbs->addTextCrumb(
$blog->getParentSite(),
$blog->getExternalParentURI());
}
} else { } else {
$crumbs = parent::buildApplicationCrumbs(); $crumbs = parent::buildApplicationCrumbs();
$crumbs->addTextCrumb( $crumbs->addTextCrumb(

View file

@ -95,15 +95,27 @@ final class PhameBlogManageController extends PhameBlogController {
Javelin::initBehavior('phabricator-tooltips'); Javelin::initBehavior('phabricator-tooltips');
$properties = id(new PHUIPropertyListView()) $properties = id(new PHUIPropertyListView())
->setUser($viewer) ->setUser($viewer);
->setObject($blog);
$domain = $blog->getDomain(); $full_domain = $blog->getDomainFullURI();
if (!$domain) { if (!$full_domain) {
$domain = phutil_tag('em', array(), pht('No external domain')); $full_domain = phutil_tag('em', array(), pht('No external domain'));
}
$properties->addProperty(pht('Full Domain'), $full_domain);
$parent_site = $blog->getParentSite();
if (!$parent_site) {
$parent_site = phutil_tag('em', array(), pht('No parent site'));
} }
$properties->addProperty(pht('Domain'), $domain); $properties->addProperty(pht('Parent Site'), $parent_site);
$parent_domain = $blog->getParentDomain();
if (!$parent_domain) {
$parent_domain = phutil_tag('em', array(), pht('No parent domain'));
}
$properties->addProperty(pht('Parent Domain'), $parent_domain);
$feed_uri = PhabricatorEnv::getProductionURI( $feed_uri = PhabricatorEnv::getProductionURI(
$this->getApplicationURI('blog/feed/'.$blog->getID().'/')); $this->getApplicationURI('blog/feed/'.$blog->getID().'/'));
@ -133,8 +145,6 @@ final class PhameBlogManageController extends PhameBlogController {
->addObject($blog, PhameBlog::MARKUP_FIELD_DESCRIPTION) ->addObject($blog, PhameBlog::MARKUP_FIELD_DESCRIPTION)
->process(); ->process();
$properties->invokeWillRenderEvent();
$description = $blog->getDescription(); $description = $blog->getDescription();
if (strlen($description)) { if (strlen($description)) {
$description = new PHUIRemarkupView($viewer, $description); $description = new PHUIRemarkupView($viewer, $description);
@ -150,7 +160,7 @@ final class PhameBlogManageController extends PhameBlogController {
private function buildCurtain(PhameBlog $blog) { private function buildCurtain(PhameBlog $blog) {
$viewer = $this->getViewer(); $viewer = $this->getViewer();
$curtain = $this->newCurtainView($viewer); $curtain = $this->newCurtainView($blog);
$actions = id(new PhabricatorActionListView()) $actions = id(new PhabricatorActionListView())
->setObject($blog) ->setObject($blog)

View file

@ -94,13 +94,29 @@ final class PhameBlogEditEngine
->setTransactionType(PhameBlogTransaction::TYPE_DESCRIPTION) ->setTransactionType(PhameBlogTransaction::TYPE_DESCRIPTION)
->setValue($object->getDescription()), ->setValue($object->getDescription()),
id(new PhabricatorTextEditField()) id(new PhabricatorTextEditField())
->setKey('domain') ->setKey('domainFullURI')
->setLabel(pht('Custom Domain')) ->setLabel(pht('Full Domain URI'))
->setDescription(pht('Blog domain name.')) ->setDescription(pht('Blog full domain URI.'))
->setConduitDescription(pht('Change the blog domain.')) ->setConduitDescription(pht('Change the blog full domain URI.'))
->setConduitTypeDescription(pht('New blog domain.')) ->setConduitTypeDescription(pht('New blog full domain URI.'))
->setValue($object->getDomain()) ->setValue($object->getDomainFullURI())
->setTransactionType(PhameBlogTransaction::TYPE_DOMAIN), ->setTransactionType(PhameBlogTransaction::TYPE_FULLDOMAIN),
id(new PhabricatorTextEditField())
->setKey('parentSite')
->setLabel(pht('Parent Site'))
->setDescription(pht('Blog parent site name.'))
->setConduitDescription(pht('Change the blog parent site name.'))
->setConduitTypeDescription(pht('New blog parent site name.'))
->setValue($object->getParentSite())
->setTransactionType(PhameBlogTransaction::TYPE_PARENTSITE),
id(new PhabricatorTextEditField())
->setKey('parentDomain')
->setLabel(pht('Parent Domain'))
->setDescription(pht('Blog parent domain name.'))
->setConduitDescription(pht('Change the blog parent domain.'))
->setConduitTypeDescription(pht('New blog parent domain.'))
->setValue($object->getParentDomain())
->setTransactionType(PhameBlogTransaction::TYPE_PARENTDOMAIN),
id(new PhabricatorSelectEditField()) id(new PhabricatorSelectEditField())
->setKey('status') ->setKey('status')
->setLabel(pht('Status')) ->setLabel(pht('Status'))

View file

@ -17,7 +17,9 @@ final class PhameBlogEditor
$types[] = PhameBlogTransaction::TYPE_NAME; $types[] = PhameBlogTransaction::TYPE_NAME;
$types[] = PhameBlogTransaction::TYPE_SUBTITLE; $types[] = PhameBlogTransaction::TYPE_SUBTITLE;
$types[] = PhameBlogTransaction::TYPE_DESCRIPTION; $types[] = PhameBlogTransaction::TYPE_DESCRIPTION;
$types[] = PhameBlogTransaction::TYPE_DOMAIN; $types[] = PhameBlogTransaction::TYPE_FULLDOMAIN;
$types[] = PhameBlogTransaction::TYPE_PARENTSITE;
$types[] = PhameBlogTransaction::TYPE_PARENTDOMAIN;
$types[] = PhameBlogTransaction::TYPE_STATUS; $types[] = PhameBlogTransaction::TYPE_STATUS;
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY; $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
$types[] = PhabricatorTransactions::TYPE_EDIT_POLICY; $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
@ -36,8 +38,12 @@ final class PhameBlogEditor
return $object->getSubtitle(); return $object->getSubtitle();
case PhameBlogTransaction::TYPE_DESCRIPTION: case PhameBlogTransaction::TYPE_DESCRIPTION:
return $object->getDescription(); return $object->getDescription();
case PhameBlogTransaction::TYPE_DOMAIN: case PhameBlogTransaction::TYPE_FULLDOMAIN:
return $object->getDomain(); return $object->getDomainFullURI();
case PhameBlogTransaction::TYPE_PARENTSITE:
return $object->getParentSite();
case PhameBlogTransaction::TYPE_PARENTDOMAIN:
return $object->getParentDomain();
case PhameBlogTransaction::TYPE_STATUS: case PhameBlogTransaction::TYPE_STATUS:
return $object->getStatus(); return $object->getStatus();
} }
@ -52,8 +58,10 @@ final class PhameBlogEditor
case PhameBlogTransaction::TYPE_SUBTITLE: case PhameBlogTransaction::TYPE_SUBTITLE:
case PhameBlogTransaction::TYPE_DESCRIPTION: case PhameBlogTransaction::TYPE_DESCRIPTION:
case PhameBlogTransaction::TYPE_STATUS: case PhameBlogTransaction::TYPE_STATUS:
case PhameBlogTransaction::TYPE_PARENTSITE:
case PhameBlogTransaction::TYPE_PARENTDOMAIN:
return $xaction->getNewValue(); return $xaction->getNewValue();
case PhameBlogTransaction::TYPE_DOMAIN: case PhameBlogTransaction::TYPE_FULLDOMAIN:
$domain = $xaction->getNewValue(); $domain = $xaction->getNewValue();
if (!strlen($xaction->getNewValue())) { if (!strlen($xaction->getNewValue())) {
return null; return null;
@ -73,10 +81,23 @@ final class PhameBlogEditor
return $object->setSubtitle($xaction->getNewValue()); return $object->setSubtitle($xaction->getNewValue());
case PhameBlogTransaction::TYPE_DESCRIPTION: case PhameBlogTransaction::TYPE_DESCRIPTION:
return $object->setDescription($xaction->getNewValue()); return $object->setDescription($xaction->getNewValue());
case PhameBlogTransaction::TYPE_DOMAIN: case PhameBlogTransaction::TYPE_FULLDOMAIN:
return $object->setDomain($xaction->getNewValue()); $new_value = $xaction->getNewValue();
if (strlen($new_value)) {
$uri = new PhutilURI($new_value);
$domain = $uri->getDomain();
$object->setDomain($domain);
} else {
$object->setDomain(null);
}
$object->setDomainFullURI($new_value);
return;
case PhameBlogTransaction::TYPE_STATUS: case PhameBlogTransaction::TYPE_STATUS:
return $object->setStatus($xaction->getNewValue()); return $object->setStatus($xaction->getNewValue());
case PhameBlogTransaction::TYPE_PARENTSITE:
return $object->setParentSite($xaction->getNewValue());
case PhameBlogTransaction::TYPE_PARENTDOMAIN:
return $object->setParentDomain($xaction->getNewValue());
} }
return parent::applyCustomInternalTransaction($object, $xaction); return parent::applyCustomInternalTransaction($object, $xaction);
@ -90,7 +111,9 @@ final class PhameBlogEditor
case PhameBlogTransaction::TYPE_NAME: case PhameBlogTransaction::TYPE_NAME:
case PhameBlogTransaction::TYPE_SUBTITLE: case PhameBlogTransaction::TYPE_SUBTITLE:
case PhameBlogTransaction::TYPE_DESCRIPTION: case PhameBlogTransaction::TYPE_DESCRIPTION:
case PhameBlogTransaction::TYPE_DOMAIN: case PhameBlogTransaction::TYPE_FULLDOMAIN:
case PhameBlogTransaction::TYPE_PARENTSITE:
case PhameBlogTransaction::TYPE_PARENTDOMAIN:
case PhameBlogTransaction::TYPE_STATUS: case PhameBlogTransaction::TYPE_STATUS:
return; return;
} }
@ -123,7 +146,26 @@ final class PhameBlogEditor
$errors[] = $error; $errors[] = $error;
} }
break; break;
case PhameBlogTransaction::TYPE_DOMAIN: case PhameBlogTransaction::TYPE_PARENTDOMAIN:
if (!$xactions) {
continue;
}
$parent_domain = last($xactions)->getNewValue();
if (empty($parent_domain)) {
continue;
}
try {
PhabricatorEnv::requireValidRemoteURIForLink($parent_domain);
} catch (Exception $ex) {
$error = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Invalid URI'),
pht('Parent Domain must be set to a valid Remote URI.'),
nonempty(last($xactions), null));
$errors[] = $error;
}
break;
case PhameBlogTransaction::TYPE_FULLDOMAIN:
if (!$xactions) { if (!$xactions) {
continue; continue;
} }
@ -152,9 +194,11 @@ final class PhameBlogEditor
nonempty(last($xactions), null)); nonempty(last($xactions), null));
$errors[] = $error; $errors[] = $error;
} }
$domain = new PhutilURI($custom_domain);
$domain = $domain->getDomain();
$duplicate_blog = id(new PhameBlogQuery()) $duplicate_blog = id(new PhameBlogQuery())
->setViewer(PhabricatorUser::getOmnipotentUser()) ->setViewer(PhabricatorUser::getOmnipotentUser())
->withDomain($custom_domain) ->withDomain($domain)
->executeOne(); ->executeOne();
if ($duplicate_blog && $duplicate_blog->getID() != $object->getID()) { if ($duplicate_blog && $duplicate_blog->getID() != $object->getID()) {
$error = new PhabricatorApplicationTransactionValidationError( $error = new PhabricatorApplicationTransactionValidationError(

View file

@ -39,6 +39,7 @@ final class PhameBlogSite extends PhameSite {
->setViewer(new PhabricatorUser()) ->setViewer(new PhabricatorUser())
->withDomain($host) ->withDomain($host)
->needProfileImage(true) ->needProfileImage(true)
->needHeaderImage(true)
->withStatuses( ->withStatuses(
array( array(
PhameBlog::STATUS_ACTIVE, PhameBlog::STATUS_ACTIVE,

View file

@ -18,6 +18,9 @@ final class PhameBlog extends PhameDAO
protected $subtitle; protected $subtitle;
protected $description; protected $description;
protected $domain; protected $domain;
protected $domainFullURI;
protected $parentSite;
protected $parentDomain;
protected $configData; protected $configData;
protected $creatorPHID; protected $creatorPHID;
protected $viewPolicy; protected $viewPolicy;
@ -44,6 +47,9 @@ final class PhameBlog extends PhameDAO
'subtitle' => 'text64', 'subtitle' => 'text64',
'description' => 'text', 'description' => 'text',
'domain' => 'text128?', 'domain' => 'text128?',
'domainFullURI' => 'text128?',
'parentSite' => 'text128',
'parentDomain' => 'text128',
'status' => 'text32', 'status' => 'text32',
'mailKey' => 'bytes20', 'mailKey' => 'bytes20',
'profileImagePHID' => 'phid?', 'profileImagePHID' => 'phid?',
@ -108,34 +114,29 @@ final class PhameBlog extends PhameDAO
* *
* @return string * @return string
*/ */
public function validateCustomDomain($custom_domain) { public function validateCustomDomain($domain_full_uri) {
$example_domain = 'blog.example.com'; $example_domain = 'http://blog.example.com/';
$label = pht('Invalid'); $label = pht('Invalid');
// note this "uri" should be pretty busted given the desired input // note this "uri" should be pretty busted given the desired input
// so just use it to test if there's a protocol specified // so just use it to test if there's a protocol specified
$uri = new PhutilURI($custom_domain); $uri = new PhutilURI($domain_full_uri);
if ($uri->getProtocol()) { $domain = $uri->getDomain();
$protocol = $uri->getProtocol();
$path = $uri->getPath();
$supported_protocols = array('http', 'https');
if (!in_array($protocol, $supported_protocols)) {
return array( return array(
$label, $label,
pht( pht(
'The custom domain should not include a protocol. Just provide '. 'The custom domain should include a valid protocol in the URI '.
'the bare domain name (for example, "%s").', '(for example, "%s"). Valid protocols are "http" or "https".',
$example_domain), $example_domain),
); );
} }
if ($uri->getPort()) { if (strlen($path) && $path != '/') {
return array(
$label,
pht(
'The custom domain should not include a port number. Just provide '.
'the bare domain name (for example, "%s").',
$example_domain),
);
}
if (strpos($custom_domain, '/') !== false) {
return array( return array(
$label, $label,
pht( pht(
@ -146,7 +147,7 @@ final class PhameBlog extends PhameDAO
); );
} }
if (strpos($custom_domain, '.') === false) { if (strpos($domain, '.') === false) {
return array( return array(
$label, $label,
pht( pht(
@ -187,8 +188,14 @@ final class PhameBlog extends PhameDAO
} }
public function getExternalLiveURI() { public function getExternalLiveURI() {
$domain = $this->getDomain(); $uri = new PhutilURI($this->getDomainFullURI());
$uri = new PhutilURI('http://'.$this->getDomain().'/'); PhabricatorEnv::requireValidRemoteURIForLink($uri);
return (string)$uri;
}
public function getExternalParentURI() {
$uri = $this->getParentDomain();
PhabricatorEnv::requireValidRemoteURIForLink($uri);
return (string)$uri; return (string)$uri;
} }

View file

@ -6,8 +6,10 @@ final class PhameBlogTransaction
const TYPE_NAME = 'phame.blog.name'; const TYPE_NAME = 'phame.blog.name';
const TYPE_SUBTITLE = 'phame.blog.subtitle'; const TYPE_SUBTITLE = 'phame.blog.subtitle';
const TYPE_DESCRIPTION = 'phame.blog.description'; const TYPE_DESCRIPTION = 'phame.blog.description';
const TYPE_DOMAIN = 'phame.blog.domain'; const TYPE_FULLDOMAIN = 'phame.blog.full.domain';
const TYPE_STATUS = 'phame.blog.status'; const TYPE_STATUS = 'phame.blog.status';
const TYPE_PARENTSITE = 'phame.blog.parent.site';
const TYPE_PARENTDOMAIN = 'phame.blog.parent.domain';
const MAILTAG_DETAILS = 'phame-blog-details'; const MAILTAG_DETAILS = 'phame-blog-details';
const MAILTAG_SUBSCRIBERS = 'phame-blog-subscribers'; const MAILTAG_SUBSCRIBERS = 'phame-blog-subscribers';
@ -44,7 +46,7 @@ final class PhameBlogTransaction
} }
break; break;
case self::TYPE_DESCRIPTION: case self::TYPE_DESCRIPTION:
case self::TYPE_DOMAIN: case self::TYPE_FULLDOMAIN:
return 'fa-pencil'; return 'fa-pencil';
case self::TYPE_STATUS: case self::TYPE_STATUS:
if ($new == PhameBlog::STATUS_ARCHIVED) { if ($new == PhameBlog::STATUS_ARCHIVED) {
@ -65,7 +67,7 @@ final class PhameBlogTransaction
switch ($this->getTransactionType()) { switch ($this->getTransactionType()) {
case self::TYPE_STATUS: case self::TYPE_STATUS:
if ($new == PhameBlog::STATUS_ARCHIVED) { if ($new == PhameBlog::STATUS_ARCHIVED) {
return 'red'; return 'violet';
} else { } else {
return 'green'; return 'green';
} }
@ -83,7 +85,9 @@ final class PhameBlogTransaction
case self::TYPE_NAME: case self::TYPE_NAME:
case self::TYPE_SUBTITLE: case self::TYPE_SUBTITLE:
case self::TYPE_DESCRIPTION: case self::TYPE_DESCRIPTION:
case self::TYPE_DOMAIN: case self::TYPE_FULLDOMAIN:
case self::TYPE_PARENTSITE:
case self::TYPE_PARENTDOMAIN:
$tags[] = self::MAILTAG_DETAILS; $tags[] = self::MAILTAG_DETAILS;
break; break;
default: default:
@ -136,12 +140,38 @@ final class PhameBlogTransaction
'%s updated the blog\'s description.', '%s updated the blog\'s description.',
$this->renderHandleLink($author_phid)); $this->renderHandleLink($author_phid));
break; break;
case self::TYPE_DOMAIN: case self::TYPE_FULLDOMAIN:
return pht( return pht(
'%s updated the blog\'s domain to "%s".', '%s updated the blog\'s full domain to "%s".',
$this->renderHandleLink($author_phid), $this->renderHandleLink($author_phid),
$new); $new);
break; break;
case self::TYPE_PARENTSITE:
if ($old === null) {
return pht(
'%s set this blog\'s parent site to "%s".',
$this->renderHandleLink($author_phid),
$new);
} else {
return pht(
'%s updated the blog\'s parent site to "%s".',
$this->renderHandleLink($author_phid),
$new);
}
break;
case self::TYPE_PARENTDOMAIN:
if ($old === null) {
return pht(
'%s set this blog\'s parent domain to "%s".',
$this->renderHandleLink($author_phid),
$new);
} else {
return pht(
'%s updated the blog\'s parent domain to "%s".',
$this->renderHandleLink($author_phid),
$new);
}
break;
case self::TYPE_STATUS: case self::TYPE_STATUS:
switch ($new) { switch ($new) {
case PhameBlog::STATUS_ACTIVE: case PhameBlog::STATUS_ACTIVE:
@ -200,9 +230,21 @@ final class PhameBlogTransaction
$this->renderHandleLink($author_phid), $this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid)); $this->renderHandleLink($object_phid));
break; break;
case self::TYPE_DOMAIN: case self::TYPE_FULLDOMAIN:
return pht( return pht(
'%s updated the domain for %s.', '%s updated the full domain for %s.',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid));
break;
case self::TYPE_PARENTSITE:
return pht(
'%s updated the parent site for %s.',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid));
break;
case self::TYPE_PARENTDOMAIN:
return pht(
'%s updated the parent domain for %s.',
$this->renderHandleLink($author_phid), $this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid)); $this->renderHandleLink($object_phid));
break; break;

View file

@ -26,12 +26,6 @@ final class PhabricatorPholioApplication extends PhabricatorApplication {
return pht('Things before they were cool.'); return pht('Things before they were cool.');
} }
public function getEventListeners() {
return array(
new PholioActionMenuEventListener(),
);
}
public function getRemarkupRules() { public function getRemarkupRules() {
return array( return array(
new PholioRemarkupRule(), new PholioRemarkupRule(),

View file

@ -1,51 +0,0 @@
<?php
final class PholioActionMenuEventListener
extends PhabricatorEventListener {
public function register() {
$this->listen(PhabricatorEventType::TYPE_UI_DIDRENDERACTIONS);
}
public function handleEvent(PhutilEvent $event) {
switch ($event->getType()) {
case PhabricatorEventType::TYPE_UI_DIDRENDERACTIONS:
$this->handleActionsEvent($event);
break;
}
}
private function handleActionsEvent(PhutilEvent $event) {
$object = $event->getValue('object');
$actions = null;
if ($object instanceof ManiphestTask) {
$actions = $this->renderTaskItems($event);
}
$this->addActionMenuItems($event, $actions);
}
private function renderTaskItems(PhutilEvent $event) {
if (!$this->canUseApplication($event->getUser())) {
return;
}
$task = $event->getValue('object');
$phid = $task->getPHID();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$event->getUser(),
$task,
PhabricatorPolicyCapability::CAN_EDIT);
return id(new PhabricatorActionView())
->setName(pht('Edit Pholio Mocks'))
->setHref("/search/attach/{$phid}/MOCK/edge/")
->setWorkflow(true)
->setIcon('fa-camera-retro')
->setDisabled(!$can_edit)
->setWorkflow(true);
}
}

View file

@ -55,9 +55,25 @@ final class PhabricatorProjectDatasource
$has_cols = array_fill_keys(array_keys($projs), true); $has_cols = array_fill_keys(array_keys($projs), true);
} }
$is_browse = $this->getIsBrowse();
if ($is_browse && $projs) {
// TODO: This is a little ad-hoc, but we don't currently have
// infrastructure for bulk querying custom fields efficiently.
$table = new PhabricatorProjectCustomFieldStorage();
$descriptions = $table->loadAllWhere(
'objectPHID IN (%Ls) AND fieldIndex = %s',
array_keys($projs),
PhabricatorHash::digestForIndex('std:project:internal:description'));
$descriptions = mpull($descriptions, 'getFieldValue', 'getObjectPHID');
} else {
$descriptions = array();
}
$results = array(); $results = array();
foreach ($projs as $proj) { foreach ($projs as $proj) {
if (!isset($has_cols[$proj->getPHID()])) { $phid = $proj->getPHID();
if (!isset($has_cols[$phid])) {
continue; continue;
} }
@ -99,7 +115,7 @@ final class PhabricatorProjectDatasource
->setDisplayName($proj->getDisplayName()) ->setDisplayName($proj->getDisplayName())
->setDisplayType($proj->getDisplayIconName()) ->setDisplayType($proj->getDisplayIconName())
->setURI($proj->getURI()) ->setURI($proj->getURI())
->setPHID($proj->getPHID()) ->setPHID($phid)
->setIcon($proj->getDisplayIconIcon()) ->setIcon($proj->getDisplayIconIcon())
->setColor($proj->getColor()) ->setColor($proj->getColor())
->setPriorityType('proj') ->setPriorityType('proj')
@ -111,6 +127,16 @@ final class PhabricatorProjectDatasource
$proj_result->setImageURI($proj->getProfileImageURI()); $proj_result->setImageURI($proj->getProfileImageURI());
if ($is_browse) {
$proj_result->addAttribute($proj->getDisplayIconName());
$description = idx($descriptions, $phid);
if (strlen($description)) {
$summary = PhabricatorMarkupEngine::summarize($description);
$proj_result->addAttribute($summary);
}
}
$results[] = $proj_result; $results[] = $proj_result;
} }

View file

@ -86,20 +86,24 @@ final class PhabricatorProjectLogicalOrNotDatasource
$result $result
->setTokenType(PhabricatorTypeaheadTokenView::TYPE_FUNCTION) ->setTokenType(PhabricatorTypeaheadTokenView::TYPE_FUNCTION)
->setIcon('fa-asterisk') ->setIcon('fa-asterisk')
->setColor(null); ->setColor(null)
->resetAttributes()
->addAttribute(pht('Function'));
if ($return_any) { if ($return_any) {
$return[] = id(clone $result) $return[] = id(clone $result)
->setPHID('any('.$result->getPHID().')') ->setPHID('any('.$result->getPHID().')')
->setDisplayName(pht('In Any: %s', $result->getDisplayName())) ->setDisplayName(pht('In Any: %s', $result->getDisplayName()))
->setName('any '.$result->getName()); ->setName('any '.$result->getName())
->addAttribute(pht('Include results tagged with this project.'));
} }
if ($return_not) { if ($return_not) {
$return[] = id(clone $result) $return[] = id(clone $result)
->setPHID('not('.$result->getPHID().')') ->setPHID('not('.$result->getPHID().')')
->setDisplayName(pht('Not In: %s', $result->getDisplayName())) ->setDisplayName(pht('Not In: %s', $result->getDisplayName()))
->setName('not '.$result->getName()); ->setName('not '.$result->getName())
->addAttribute(pht('Exclude results tagged with this project.'));
} }
} }

View file

@ -96,7 +96,8 @@ final class PhabricatorProjectLogicalViewerDatasource
->setName(pht('Current Viewer\'s Projects')) ->setName(pht('Current Viewer\'s Projects'))
->setPHID('viewerprojects()') ->setPHID('viewerprojects()')
->setIcon('fa-asterisk') ->setIcon('fa-asterisk')
->setUnique(true); ->setUnique(true)
->addAttribute(pht('Select projects current viewer is a member of.'));
} }
} }

View file

@ -44,7 +44,10 @@ final class PhabricatorProjectMembersDatasource
->setColor(null) ->setColor(null)
->setPHID('members('.$result->getPHID().')') ->setPHID('members('.$result->getPHID().')')
->setDisplayName(pht('Members: %s', $result->getDisplayName())) ->setDisplayName(pht('Members: %s', $result->getDisplayName()))
->setName($result->getName().' members'); ->setName($result->getName().' members')
->resetAttributes()
->addAttribute(pht('Function'))
->addAttribute(pht('Select project members.'));
} }
return $results; return $results;

View file

@ -68,7 +68,8 @@ final class PhabricatorProjectNoProjectsDatasource
->setPHID('null()') ->setPHID('null()')
->setIcon('fa-ban') ->setIcon('fa-ban')
->setName('null '.$name) ->setName('null '.$name)
->setDisplayName($name); ->setDisplayName($name)
->addAttribute(pht('Select results with no tags.'));
} }
} }

View file

@ -191,7 +191,7 @@ final class PhabricatorRepositoryPullEngine
)); ));
} }
private function installHook($path) { private function installHook($path, array $hook_argv = array()) {
$this->log('%s', pht('Installing commit hook to "%s"...', $path)); $this->log('%s', pht('Installing commit hook to "%s"...', $path));
$repository = $this->getRepository(); $repository = $this->getRepository();
@ -202,10 +202,11 @@ final class PhabricatorRepositoryPullEngine
$full_php_path = Filesystem::resolveBinary('php'); $full_php_path = Filesystem::resolveBinary('php');
$cmd = csprintf( $cmd = csprintf(
'exec %s -f %s -- %s "$@"', 'exec %s -f %s -- %s %Ls "$@"',
$full_php_path, $full_php_path,
$bin, $bin,
$identifier); $identifier,
$hook_argv);
$hook = "#!/bin/sh\nexport TERM=dumb\n{$cmd}\n"; $hook = "#!/bin/sh\nexport TERM=dumb\n{$cmd}\n";
@ -585,8 +586,16 @@ final class PhabricatorRepositoryPullEngine
$root = $repository->getLocalPath(); $root = $repository->getLocalPath();
$path = '/hooks/pre-commit'; $path = '/hooks/pre-commit';
$this->installHook($root.$path); $this->installHook($root.$path);
$revprop_path = '/hooks/pre-revprop-change';
$revprop_argv = array(
'--hook-mode',
'svn-revprop',
);
$this->installHook($root.$revprop_path, $revprop_argv);
} }

View file

@ -1623,11 +1623,10 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
return false; return false;
} }
if ($this->isGit() || $this->isHg()) { // In Git and Mercurial, ref deletions and rewrites are dangerous.
return true; // In Subversion, editing revprops is dangerous.
}
return false; return true;
} }
public function shouldAllowDangerousChanges() { public function shouldAllowDangerousChanges() {

View file

@ -41,6 +41,8 @@ final class PhabricatorSearchApplication extends PhabricatorApplication {
'delete/(?P<queryKey>[^/]+)/(?P<engine>[^/]+)/' 'delete/(?P<queryKey>[^/]+)/(?P<engine>[^/]+)/'
=> 'PhabricatorSearchDeleteController', => 'PhabricatorSearchDeleteController',
'order/(?P<engine>[^/]+)/' => 'PhabricatorSearchOrderController', 'order/(?P<engine>[^/]+)/' => 'PhabricatorSearchOrderController',
'rel/(?P<relationshipKey>[^/]+)/(?P<sourcePHID>[^/]+)/'
=> 'PhabricatorSearchRelationshipController',
), ),
); );
} }

View file

@ -18,6 +18,11 @@ final class PhabricatorSearchAttachController
$object = id(new PhabricatorObjectQuery()) $object = id(new PhabricatorObjectQuery())
->setViewer($user) ->setViewer($user)
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->withPHIDs(array($phid)) ->withPHIDs(array($phid))
->executeOne(); ->executeOne();

View file

@ -0,0 +1,205 @@
<?php
final class PhabricatorSearchRelationshipController
extends PhabricatorSearchBaseController {
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$phid = $request->getURIData('sourcePHID');
$object = id(new PhabricatorObjectQuery())
->setViewer($viewer)
->withPHIDs(array($phid))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$object) {
return new Aphront404Response();
}
$list = PhabricatorObjectRelationshipList::newForObject(
$viewer,
$object);
$relationship_key = $request->getURIData('relationshipKey');
$relationship = $list->getRelationship($relationship_key);
if (!$relationship) {
return new Aphront404Response();
}
$src_phid = $object->getPHID();
$edge_type = $relationship->getEdgeConstant();
$dst_phids = PhabricatorEdgeQuery::loadDestinationPHIDs(
$src_phid,
$edge_type);
$all_phids = $dst_phids;
$all_phids[] = $src_phid;
$handles = $viewer->loadHandles($all_phids);
$src_handle = $handles[$src_phid];
$done_uri = $src_handle->getURI();
$initial_phids = $dst_phids;
if ($request->isFormPost()) {
$phids = explode(';', $request->getStr('phids'));
$phids = array_filter($phids);
$phids = array_values($phids);
$initial_phids = $request->getStrList('initialPHIDs');
// Apply the changes as adds and removes relative to the original state
// of the object when the dialog was rendered so that two users adding
// relationships at the same time don't race and overwrite one another.
$add_phids = array_diff($phids, $initial_phids);
$rem_phids = array_diff($initial_phids, $phids);
if ($add_phids) {
$dst_objects = id(new PhabricatorObjectQuery())
->setViewer($viewer)
->withPHIDs($phids)
->setRaisePolicyExceptions(true)
->execute();
$dst_objects = mpull($dst_objects, null, 'getPHID');
} else {
$dst_objects = array();
}
try {
foreach ($add_phids as $add_phid) {
$dst_object = idx($dst_objects, $add_phid);
if (!$dst_object) {
throw new Exception(
pht(
'You can not create a relationship to object "%s" because '.
'the object does not exist or could not be loaded.',
$add_phid));
}
if (!$relationship->canRelateObjects($object, $dst_object)) {
throw new Exception(
pht(
'You can not create a relationship (of type "%s") to object '.
'"%s" because it is not the right type of object for this '.
'relationship.',
$relationship->getRelationshipConstant(),
$add_phid));
}
}
} catch (Exception $ex) {
return $this->newUnrelatableObjectResponse($ex, $done_uri);
}
$editor = $object->getApplicationTransactionEditor()
->setActor($viewer)
->setContentSourceFromRequest($request)
->setContinueOnMissingFields(true)
->setContinueOnNoEffect(true);
$xactions = array();
$xactions[] = $object->getApplicationTransactionTemplate()
->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
->setMetadataValue('edge:type', $edge_type)
->setNewValue(array(
'+' => array_fuse($add_phids),
'-' => array_fuse($rem_phids),
));
try {
$editor->applyTransactions($object, $xactions);
return id(new AphrontRedirectResponse())->setURI($done_uri);
} catch (PhabricatorEdgeCycleException $ex) {
return $this->newGraphCycleResponse($ex, $done_uri);
}
}
$handles = iterator_to_array($handles);
$handles = array_select_keys($handles, $dst_phids);
// TODO: These are hard-coded for now.
$filters = array(
'assigned' => pht('Assigned to Me'),
'created' => pht('Created By Me'),
'open' => pht('All Open Objects'),
'all' => pht('All Objects'),
);
$dialog_title = $relationship->getDialogTitleText();
$dialog_header = $relationship->getDialogHeaderText();
$dialog_button = $relationship->getDialogButtonText();
$dialog_instructions = $relationship->getDialogInstructionsText();
// TODO: Remove this, this is just legacy support.
$legacy_kinds = array(
ManiphestTaskHasCommitEdgeType::EDGECONST => 'CMIT',
ManiphestTaskHasMockEdgeType::EDGECONST => 'MOCK',
ManiphestTaskHasRevisionEdgeType::EDGECONST => 'DREV',
ManiphestTaskDependsOnTaskEdgeType::EDGECONST => 'TASK',
ManiphestTaskDependedOnByTaskEdgeType::EDGECONST => 'TASK',
);
$edge_type = $relationship->getEdgeConstant();
$legacy_kind = idx($legacy_kinds, $edge_type);
if (!$legacy_kind) {
throw new Exception(
pht('Only specific legacy relationships are supported!'));
}
return id(new PhabricatorObjectSelectorDialog())
->setUser($viewer)
->setInitialPHIDs($initial_phids)
->setHandles($handles)
->setFilters($filters)
->setSelectedFilter('created')
->setExcluded($phid)
->setCancelURI($done_uri)
->setSearchURI("/search/select/{$legacy_kind}/edge/")
->setTitle($dialog_title)
->setHeader($dialog_header)
->setButtonText($dialog_button)
->setInstructions($dialog_instructions)
->buildDialog();
}
private function newGraphCycleResponse(
PhabricatorEdgeCycleException $ex,
$done_uri) {
$viewer = $this->getViewer();
$cycle = $ex->getCycle();
$handles = $this->loadViewerHandles($cycle);
$names = array();
foreach ($cycle as $cycle_phid) {
$names[] = $handles[$cycle_phid]->getFullName();
}
$message = pht(
'You can not create that relationship because it would create a '.
'circular dependency:');
$list = implode(" \xE2\x86\x92 ", $names);
return $this->newDialog()
->setTitle(pht('Circular Dependency'))
->appendParagraph($message)
->appendParagraph($list)
->addCancelButton($done_uri);
}
private function newUnrelatableObjectResponse(Exception $ex, $done_uri) {
$message = $ex->getMessage();
return $this->newDialog()
->setTitle(pht('Invalid Relationship'))
->appendParagraph($message)
->addCancelButton($done_uri);
}
}

View file

@ -0,0 +1,75 @@
<?php
abstract class PhabricatorObjectRelationship extends Phobject {
private $viewer;
public function setViewer(PhabricatorUser $viewer) {
$this->viewer = $viewer;
return $this;
}
public function getViewer() {
return $this->viewer;
}
final public function getRelationshipConstant() {
return $this->getPhobjectClassConstant('RELATIONSHIPKEY');
}
abstract public function isEnabledForObject($object);
abstract public function getEdgeConstant();
abstract protected function getActionName();
abstract protected function getActionIcon();
abstract public function canRelateObjects($src, $dst);
abstract public function getDialogTitleText();
abstract public function getDialogHeaderText();
abstract public function getDialogButtonText();
public function getDialogInstructionsText() {
return null;
}
public function shouldAppearInActionMenu() {
return true;
}
protected function isActionEnabled($object) {
$viewer = $this->getViewer();
return PhabricatorPolicyFilter::hasCapability(
$viewer,
$object,
PhabricatorPolicyCapability::CAN_EDIT);
}
final public function newAction($object) {
$is_enabled = $this->isActionEnabled($object);
$action_uri = $this->getActionURI($object);
return id(new PhabricatorActionView())
->setName($this->getActionName())
->setHref($action_uri)
->setIcon($this->getActionIcon())
->setDisabled(!$is_enabled)
->setWorkflow(true);
}
final public static function getAllRelationships() {
return id(new PhutilClassMapQuery())
->setAncestorClass(__CLASS__)
->setUniqueMethod('getRelationshipConstant')
->execute();
}
private function getActionURI($object) {
$phid = $object->getPHID();
$type = $this->getRelationshipConstant();
return "/search/rel/{$type}/{$phid}/";
}
}

View file

@ -0,0 +1,99 @@
<?php
final class PhabricatorObjectRelationshipList extends Phobject {
private $viewer;
private $object;
private $relationships;
public function setViewer(PhabricatorUser $viewer) {
$this->viewer = $viewer;
return $this;
}
public function getViewer() {
if ($this->viewer === null) {
throw new PhutilInvalidStateException('setViewer');
}
return $this->viewer;
}
public function setObject($object) {
$this->object = $object;
return $this;
}
public function getObject() {
if ($this->object === null) {
throw new PhutilInvalidStateException('setObject');
}
return $this->object;
}
public function setRelationships(array $relationships) {
assert_instances_of($relationships, 'PhabricatorObjectRelationship');
$this->relationships = $relationships;
return $this;
}
public function getRelationships() {
if ($this->relationships === null) {
throw new PhutilInvalidStateException('setRelationships');
}
return $this->relationships;
}
public function newActionMenu() {
$relationships = $this->getRelationships();
$object = $this->getObject();
$actions = array();
foreach ($relationships as $key => $relationship) {
if (!$relationship->shouldAppearInActionMenu()) {
continue;
}
$actions[$key] = $relationship->newAction($object);
}
if (!$actions) {
return null;
}
$actions = msort($actions, 'getName');
return id(new PhabricatorActionView())
->setName(pht('Edit Related Objects...'))
->setIcon('fa-link')
->setSubmenu($actions);
}
public function getRelationship($key) {
return idx($this->relationships, $key);
}
public static function newForObject(PhabricatorUser $viewer, $object) {
$relationships = PhabricatorObjectRelationship::getAllRelationships();
$results = array();
foreach ($relationships as $key => $relationship) {
$relationship = clone $relationship;
$relationship->setViewer($viewer);
if (!$relationship->isEnabledForObject($object)) {
continue;
}
$results[$key] = $relationship;
}
return id(new self())
->setViewer($viewer)
->setObject($object)
->setRelationships($results);
}
}

View file

@ -16,14 +16,22 @@ final class PhabricatorSearchDatasource
} }
public function getComponentDatasources() { public function getComponentDatasources() {
return array( $sources = array(
id(new PhabricatorPeopleDatasource())->setEnrichResults(true), new PhabricatorPeopleDatasource(),
new PhabricatorProjectDatasource(), new PhabricatorProjectDatasource(),
new PhabricatorApplicationDatasource(), new PhabricatorApplicationDatasource(),
new PhabricatorTypeaheadMonogramDatasource(), new PhabricatorTypeaheadMonogramDatasource(),
new DiffusionRepositoryDatasource(), new DiffusionRepositoryDatasource(),
new DiffusionSymbolDatasource(), new DiffusionSymbolDatasource(),
); );
// These results are always rendered in the full browse display mode, so
// set the browse flag on all component sources.
foreach ($sources as $source) {
$source->setIsBrowse(true);
}
return $sources;
} }
} }

View file

@ -931,6 +931,7 @@ abstract class PhabricatorApplicationTransactionEditor
$object->openTransaction(); $object->openTransaction();
} }
try {
foreach ($xactions as $xaction) { foreach ($xactions as $xaction) {
$this->applyInternalEffects($object, $xaction); $this->applyInternalEffects($object, $xaction);
} }
@ -940,8 +941,6 @@ abstract class PhabricatorApplicationTransactionEditor
try { try {
$object->save(); $object->save();
} catch (AphrontDuplicateKeyQueryException $ex) { } catch (AphrontDuplicateKeyQueryException $ex) {
$object->killTransaction();
// This callback has an opportunity to throw a better exception, // This callback has an opportunity to throw a better exception,
// so execution may end here. // so execution may end here.
$this->didCatchDuplicateKeyException($object, $xactions, $ex); $this->didCatchDuplicateKeyException($object, $xactions, $ex);
@ -973,7 +972,11 @@ abstract class PhabricatorApplicationTransactionEditor
$read_locking = false; $read_locking = false;
} }
$object->saveTransaction(); $object->saveTransaction();
} catch (Exception $ex) {
$object->killTransaction();
throw $ex;
}
// Now that we've completely applied the core transaction set, try to apply // Now that we've completely applied the core transaction set, try to apply
// Herald rules. Herald rules are allowed to either take direct actions on // Herald rules. Herald rules are allowed to either take direct actions on

View file

@ -343,6 +343,7 @@ class PhabricatorApplicationTransactionCommentView extends AphrontView {
array( array(
'id' => $this->getPreviewPanelID(), 'id' => $this->getPreviewPanelID(),
'style' => 'display: none', 'style' => 'display: none',
'class' => 'phui-comment-preview-view',
), ),
$preview); $preview);
} }

View file

@ -65,7 +65,8 @@ final class PhabricatorTypeaheadModularDatasourceController
} }
$composite $composite
->setOffset($offset); ->setOffset($offset)
->setIsBrowse(true);
} }
$results = $composite->loadResults(); $results = $composite->loadResults();
@ -142,9 +143,6 @@ final class PhabricatorTypeaheadModularDatasourceController
$items = array(); $items = array();
foreach ($results as $result) { foreach ($results as $result) {
$token = PhabricatorTypeaheadTokenView::newFromTypeaheadResult(
$result);
// Disable already-selected tokens. // Disable already-selected tokens.
$disabled = isset($exclude[$result->getPHID()]); $disabled = isset($exclude[$result->getPHID()]);
@ -161,15 +159,14 @@ final class PhabricatorTypeaheadModularDatasourceController
), ),
pht('Select')); pht('Select'));
$information = $this->renderBrowseResult($result, $button);
$items[] = phutil_tag( $items[] = phutil_tag(
'div', 'div',
array( array(
'class' => 'typeahead-browse-item grouped', 'class' => 'typeahead-browse-item grouped',
), ),
array( $information);
$token,
$button,
));
} }
$markup = array( $markup = array(
@ -250,6 +247,8 @@ final class PhabricatorTypeaheadModularDatasourceController
->setRenderDialogAsDiv(true) ->setRenderDialogAsDiv(true)
->setTitle($source->getBrowseTitle()) ->setTitle($source->getBrowseTitle())
->appendChild($browser) ->appendChild($browser)
->setResizeX(true)
->setResizeY($frame_id)
->addFooter($function_help) ->addFooter($function_help)
->addCancelButton('/', pht('Close')); ->addCancelButton('/', pht('Close'));
} }
@ -350,4 +349,60 @@ final class PhabricatorTypeaheadModularDatasourceController
->appendChild($view); ->appendChild($view);
} }
private function renderBrowseResult(
PhabricatorTypeaheadResult $result,
$button) {
$class = array();
$style = array();
$separator = " \xC2\xB7 ";
$class[] = 'phabricator-main-search-typeahead-result';
$name = phutil_tag(
'div',
array(
'class' => 'result-name',
),
$result->getDisplayName());
$icon = $result->getIcon();
$icon = id(new PHUIIconView())->setIcon($icon);
$attributes = $result->getAttributes();
$attributes = phutil_implode_html($separator, $attributes);
$attributes = array($icon, ' ', $attributes);
$closed = $result->getClosed();
if ($closed) {
$class[] = 'result-closed';
$attributes = array($closed, $separator, $attributes);
}
$attributes = phutil_tag(
'div',
array(
'class' => 'result-type',
),
$attributes);
$image = $result->getImageURI();
if ($image) {
$style[] = 'background-image: url('.$image.');';
$class[] = 'has-image';
}
return phutil_tag(
'div',
array(
'class' => implode(' ', $class),
'style' => implode(' ', $style),
),
array(
$button,
$name,
$attributes,
));
}
} }

View file

@ -37,6 +37,7 @@ abstract class PhabricatorTypeaheadCompositeDatasource
} }
$stack = $this->getFunctionStack(); $stack = $this->getFunctionStack();
$is_browse = $this->getIsBrowse();
$results = array(); $results = array();
foreach ($this->getUsableDatasources() as $source) { foreach ($this->getUsableDatasources() as $source) {
@ -70,6 +71,10 @@ abstract class PhabricatorTypeaheadCompositeDatasource
$source->setLimit($offset + $limit); $source->setLimit($offset + $limit);
} }
if ($is_browse) {
$source->setIsBrowse(true);
}
$source_results = $source->loadResults(); $source_results = $source->loadResults();
$source_results = $source->didLoadResults($source_results); $source_results = $source->didLoadResults($source_results);
$results[] = $source_results; $results[] = $source_results;

View file

@ -12,6 +12,7 @@ abstract class PhabricatorTypeaheadDatasource extends Phobject {
private $limit; private $limit;
private $parameters = array(); private $parameters = array();
private $functionStack = array(); private $functionStack = array();
private $isBrowse;
public function setLimit($limit) { public function setLimit($limit) {
$this->limit = $limit; $this->limit = $limit;
@ -71,6 +72,15 @@ abstract class PhabricatorTypeaheadDatasource extends Phobject {
return idx($this->parameters, $name, $default); return idx($this->parameters, $name, $default);
} }
public function setIsBrowse($is_browse) {
$this->isBrowse = $is_browse;
return $this;
}
public function getIsBrowse() {
return $this->isBrowse;
}
public function getDatasourceURI() { public function getDatasourceURI() {
$uri = new PhutilURI('/typeahead/class/'.get_class($this).'/'); $uri = new PhutilURI('/typeahead/class/'.get_class($this).'/');
$uri->setQueryParams($this->parameters); $uri->setQueryParams($this->parameters);
@ -199,7 +209,8 @@ abstract class PhabricatorTypeaheadDatasource extends Phobject {
protected function newFunctionResult() { protected function newFunctionResult() {
return id(new PhabricatorTypeaheadResult()) return id(new PhabricatorTypeaheadResult())
->setTokenType(PhabricatorTypeaheadTokenView::TYPE_FUNCTION) ->setTokenType(PhabricatorTypeaheadTokenView::TYPE_FUNCTION)
->setIcon('fa-asterisk'); ->setIcon('fa-asterisk')
->addAttribute(pht('Function'));
} }
public function newInvalidToken($name) { public function newInvalidToken($name) {

View file

@ -17,6 +17,7 @@ final class PhabricatorTypeaheadResult extends Phobject {
private $tokenType; private $tokenType;
private $unique; private $unique;
private $autocomplete; private $autocomplete;
private $attributes = array();
public function setIcon($icon) { public function setIcon($icon) {
$this->icon = $icon; $this->icon = $icon;
@ -188,4 +189,26 @@ final class PhabricatorTypeaheadResult extends Phobject {
return null; return null;
} }
public function getImageURI() {
return $this->imageURI;
}
public function getClosed() {
return $this->closed;
}
public function resetAttributes() {
$this->attributes = array();
return $this;
}
public function getAttributes() {
return $this->attributes;
}
public function addAttribute($attribute) {
$this->attributes[] = $attribute;
return $this;
}
} }

View file

@ -134,7 +134,6 @@ final class PhabricatorEdgeEditor extends Phobject {
$caught = $ex; $caught = $ex;
} }
if ($caught) { if ($caught) {
$this->killTransactions(); $this->killTransactions();
} }

View file

@ -28,10 +28,20 @@ final class PhabricatorEdgesDestructionEngineExtension
foreach ($edges as $type => $type_edges) { foreach ($edges as $type => $type_edges) {
foreach ($type_edges as $src => $src_edges) { foreach ($type_edges as $src => $src_edges) {
foreach ($src_edges as $dst => $edge) { foreach ($src_edges as $dst => $edge) {
$editor->removeEdge($edge['src'], $edge['type'], $edge['dst']); try {
$editor->removeEdge($edge['src'], $edge['type'], $edge['dst']);
} catch (Exception $ex) {
// We can run into an exception while removing the edge if the
// edge type no longer exists. This prevents us from figuring out
// if there's an inverse type. Just ignore any errors here and
// continue, since the best we can do is clean up all the edges
// we still have information about. See T11201.
phlog($ex);
}
} }
} }
} }
$editor->save(); $editor->save();
} }

View file

@ -206,73 +206,73 @@ final class PhabricatorUSEnglishTranslation
), ),
), ),
'%s added %s blocking task(s): %s.' => array( '%s added %s subtask(s): %s.' => array(
array( array(
'%s added a blocking task: %3$s.', '%s added a subtask: %3$s.',
'%s added blocking tasks: %3$s.', '%s added subtasks: %3$s.',
), ),
), ),
'%s added %s blocked task(s): %s.' => array( '%s added %s parent task(s): %s.' => array(
array( array(
'%s added a blocked task: %3$s.', '%s added a parent task: %3$s.',
'%s added blocked tasks: %3$s.', '%s added parent tasks: %3$s.',
), ),
), ),
'%s removed %s blocking task(s): %s.' => array( '%s removed %s subtask(s): %s.' => array(
array( array(
'%s removed a blocking task: %3$s.', '%s removed a subtask: %3$s.',
'%s removed blocking tasks: %3$s.', '%s removed subtasks: %3$s.',
), ),
), ),
'%s removed %s blocked task(s): %s.' => array( '%s removed %s parent task(s): %s.' => array(
array( array(
'%s removed a blocked task: %3$s.', '%s removed a parent task: %3$s.',
'%s removed blocked tasks: %3$s.', '%s removed parent tasks: %3$s.',
), ),
), ),
'%s added %s blocking task(s) for %s: %s.' => array( '%s added %s subtask(s) for %s: %s.' => array(
array( array(
'%s added a blocking task for %3$s: %4$s.', '%s added a subtask for %3$s: %4$s.',
'%s added blocking tasks for %3$s: %4$s.', '%s added subtasks for %3$s: %4$s.',
), ),
), ),
'%s added %s blocked task(s) for %s: %s.' => array( '%s added %s parent task(s) for %s: %s.' => array(
array( array(
'%s added a blocked task for %3$s: %4$s.', '%s added a parent task for %3$s: %4$s.',
'%s added blocked tasks for %3$s: %4$s.', '%s added parent tasks for %3$s: %4$s.',
), ),
), ),
'%s removed %s blocking task(s) for %s: %s.' => array( '%s removed %s subtask(s) for %s: %s.' => array(
array( array(
'%s removed a blocking task for %3$s: %4$s.', '%s removed a subtask for %3$s: %4$s.',
'%s removed blocking tasks for %3$s: %4$s.', '%s removed subtasks for %3$s: %4$s.',
), ),
), ),
'%s removed %s blocked task(s) for %s: %s.' => array( '%s removed %s parent task(s) for %s: %s.' => array(
array( array(
'%s removed a blocked task for %3$s: %4$s.', '%s removed a parent task for %3$s: %4$s.',
'%s removed blocked tasks for %3$s: %4$s.', '%s removed parent tasks for %3$s: %4$s.',
), ),
), ),
'%s edited blocking task(s), added %s: %s; removed %s: %s.' => '%s edited subtask(s), added %s: %s; removed %s: %s.' =>
'%s edited blocking tasks, added: %3$s; removed: %5$s.', '%s edited subtasks, added: %3$s; removed: %5$s.',
'%s edited blocking task(s) for %s, added %s: %s; removed %s: %s.' => '%s edited subtask(s) for %s, added %s: %s; removed %s: %s.' =>
'%s edited blocking tasks for %s, added: %4$s; removed: %6$s.', '%s edited subtasks for %s, added: %4$s; removed: %6$s.',
'%s edited blocked task(s), added %s: %s; removed %s: %s.' => '%s edited parent task(s), added %s: %s; removed %s: %s.' =>
'%s edited blocked tasks, added: %3$s; removed: %5$s.', '%s edited parent tasks, added: %3$s; removed: %5$s.',
'%s edited blocked task(s) for %s, added %s: %s; removed %s: %s.' => '%s edited parent task(s) for %s, added %s: %s; removed %s: %s.' =>
'%s edited blocked tasks for %s, added: %4$s; removed: %6$s.', '%s edited parent tasks for %s, added: %4$s; removed: %6$s.',
'%s edited answer(s), added %s: %s; removed %d: %s.' => '%s edited answer(s), added %s: %s; removed %d: %s.' =>
'%s edited answers, added: %3$s; removed: %5$s.', '%s edited answers, added: %3$s; removed: %5$s.',

View file

@ -24,6 +24,8 @@ final class AphrontDialogView
private $flush; private $flush;
private $validationException; private $validationException;
private $objectList; private $objectList;
private $resizeX;
private $resizeY;
const WIDTH_DEFAULT = 'default'; const WIDTH_DEFAULT = 'default';
@ -72,6 +74,24 @@ final class AphrontDialogView
return $this->shortTitle; return $this->shortTitle;
} }
public function setResizeY($resize_y) {
$this->resizeY = $resize_y;
return $this;
}
public function getResizeY() {
return $this->resizeY;
}
public function setResizeX($resize_x) {
$this->resizeX = $resize_x;
return $this;
}
public function getResizeX() {
return $this->resizeX;
}
public function addSubmitButton($text = null) { public function addSubmitButton($text = null) {
if (!$text) { if (!$text) {
$text = pht('Okay'); $text = pht('Okay');
@ -347,6 +367,20 @@ final class AphrontDialogView
$this->footers); $this->footers);
} }
$resize = null;
if ($this->resizeX || $this->resizeY) {
$resize = javelin_tag(
'div',
array(
'class' => 'aphront-dialog-resize',
'sigil' => 'jx-dialog-resize',
'meta' => array(
'resizeX' => $this->resizeX,
'resizeY' => $this->resizeY,
),
));
}
$tail = null; $tail = null;
if ($buttons || $footer) { if ($buttons || $footer) {
$tail = phutil_tag( $tail = phutil_tag(
@ -357,6 +391,7 @@ final class AphrontDialogView
array( array(
$buttons, $buttons,
$footer, $footer,
$resize,
)); ));
} }

View file

@ -10,6 +10,7 @@ final class PhabricatorObjectSelectorDialog extends Phobject {
private $searchURI; private $searchURI;
private $selectedFilter; private $selectedFilter;
private $excluded; private $excluded;
private $initialPHIDs;
private $title; private $title;
private $header; private $header;
@ -77,6 +78,15 @@ final class PhabricatorObjectSelectorDialog extends Phobject {
return $this; return $this;
} }
public function setInitialPHIDs(array $initial_phids) {
$this->initialPHIDs = $initial_phids;
return $this;
}
public function getInitialPHIDs() {
return $this->initialPHIDs;
}
public function buildDialog() { public function buildDialog() {
$user = $this->user; $user = $this->user;
@ -171,8 +181,14 @@ final class PhabricatorObjectSelectorDialog extends Phobject {
$view = new PhabricatorHandleObjectSelectorDataView($handle); $view = new PhabricatorHandleObjectSelectorDataView($handle);
$handle_views[$phid] = $view->renderData(); $handle_views[$phid] = $view->renderData();
} }
$dialog->addHiddenInput('phids', implode(';', array_keys($this->handles))); $dialog->addHiddenInput('phids', implode(';', array_keys($this->handles)));
$initial_phids = $this->getInitialPHIDs();
if ($initial_phids) {
$initial_phids = implode(', ', $initial_phids);
$dialog->addHiddenInput('initialPHIDs', $initial_phids);
}
Javelin::initBehavior( Javelin::initBehavior(
'phabricator-object-selector', 'phabricator-object-selector',
@ -188,7 +204,10 @@ final class PhabricatorObjectSelectorDialog extends Phobject {
'handles' => $handle_views, 'handles' => $handle_views,
)); ));
return $dialog; $dialog->setResizeX(true);
$dialog->setResizeY($results_id);
return $dialog;
} }
} }

View file

@ -21,6 +21,10 @@ final class PhabricatorActionListView extends AphrontView {
return $this; return $this;
} }
public function getID() {
return $this->id;
}
public function render() { public function render() {
$viewer = $this->getViewer(); $viewer = $this->getViewer();
@ -44,13 +48,26 @@ final class PhabricatorActionListView extends AphrontView {
require_celerity_resource('phabricator-action-list-view-css'); require_celerity_resource('phabricator-action-list-view-css');
$items = array();
foreach ($actions as $action) {
foreach ($action->getItems() as $item) {
$items[] = $item;
}
}
return phutil_tag( return phutil_tag(
'ul', 'ul',
array( array(
'class' => 'phabricator-action-list-view', 'class' => 'phabricator-action-list-view',
'id' => $this->id, 'id' => $this->id,
), ),
$actions); $items);
}
public function getDropdownMenuMetadata() {
return array(
'items' => (string)hsprintf('%s', $this),
);
} }

View file

@ -14,6 +14,10 @@ final class PhabricatorActionView extends AphrontView {
private $metadata; private $metadata;
private $selected; private $selected;
private $openInNewWindow; private $openInNewWindow;
private $submenu = array();
private $hidden;
private $depth;
private $id;
public function setSelected($selected) { public function setSelected($selected) {
$this->selected = $selected; $this->selected = $selected;
@ -66,6 +70,10 @@ final class PhabricatorActionView extends AphrontView {
return $this; return $this;
} }
public function getName() {
return $this->name;
}
public function setLabel($label) { public function setLabel($label) {
$this->label = $label; $this->label = $label;
return $this; return $this;
@ -95,7 +103,60 @@ final class PhabricatorActionView extends AphrontView {
return $this->openInNewWindow; return $this->openInNewWindow;
} }
public function getID() {
if (!$this->id) {
$this->id = celerity_generate_unique_node_id();
}
return $this->id;
}
public function setSubmenu(array $submenu) {
$this->submenu = $submenu;
if (!$this->getHref()) {
$this->setHref('#');
}
return $this;
}
public function getItems($depth = 0) {
$items = array();
$items[] = $this;
foreach ($this->submenu as $action) {
foreach ($action->getItems($depth + 1) as $item) {
$item
->setHidden(true)
->setDepth($depth + 1);
$items[] = $item;
}
}
return $items;
}
public function setHidden($hidden) {
$this->hidden = $hidden;
return $this;
}
public function getHidden() {
return $this->hidden;
}
public function setDepth($depth) {
$this->depth = $depth;
return $this;
}
public function getDepth() {
return $this->depth;
}
public function render() { public function render() {
$caret_id = celerity_generate_unique_node_id();
$icon = null; $icon = null;
if ($this->icon) { if ($this->icon) {
@ -155,6 +216,18 @@ final class PhabricatorActionView extends AphrontView {
$target = null; $target = null;
} }
if ($this->submenu) {
$caret = javelin_tag(
'span',
array(
'class' => 'caret-right',
'id' => $caret_id,
),
'');
} else {
$caret = null;
}
$item = javelin_tag( $item = javelin_tag(
'a', 'a',
array( array(
@ -164,7 +237,7 @@ final class PhabricatorActionView extends AphrontView {
'sigil' => $sigils, 'sigil' => $sigils,
'meta' => $this->metadata, 'meta' => $this->metadata,
), ),
array($icon, $this->name)); array($icon, $this->name, $caret));
} }
} else { } else {
$item = phutil_tag( $item = phutil_tag(
@ -190,10 +263,47 @@ final class PhabricatorActionView extends AphrontView {
$classes[] = 'phabricator-action-view-selected'; $classes[] = 'phabricator-action-view-selected';
} }
return phutil_tag( if ($this->submenu) {
$classes[] = 'phabricator-action-view-submenu';
}
$style = array();
if ($this->hidden) {
$style[] = 'display: none;';
}
if ($this->depth) {
$indent = ($this->depth * 16);
$style[] = "margin-left: {$indent}px;";
}
$sigil = null;
$meta = null;
if ($this->submenu) {
Javelin::initBehavior('phui-submenu');
$sigil = 'phui-submenu';
$item_ids = array();
foreach ($this->submenu as $subitem) {
$item_ids[] = $subitem->getID();
}
$meta = array(
'itemIDs' => $item_ids,
'caretID' => $caret_id,
);
}
return javelin_tag(
'li', 'li',
array( array(
'id' => $this->getID(),
'class' => implode(' ', $classes), 'class' => implode(' ', $classes),
'style' => implode(' ', $style),
'sigil' => $sigil,
'meta' => $meta,
), ),
$item); $item);
} }

View file

@ -222,7 +222,7 @@ final class PhabricatorStandardPageView extends PhabricatorBarePageView
} }
if ($user) { if ($user) {
if ($user->isLoggedIn()) { if ($user->isUserActivated()) {
$offset = $user->getTimeZoneOffset(); $offset = $user->getTimeZoneOffset();
$ignore_key = PhabricatorTimezoneIgnoreOffsetSetting::SETTINGKEY; $ignore_key = PhabricatorTimezoneIgnoreOffsetSetting::SETTINGKEY;

View file

@ -104,10 +104,19 @@ final class PHUIButtonView extends AphrontTagView {
public function setDropdownMenu(PhabricatorActionListView $actions) { public function setDropdownMenu(PhabricatorActionListView $actions) {
Javelin::initBehavior('phui-dropdown-menu'); Javelin::initBehavior('phui-dropdown-menu');
$this->addSigil('phui-dropdown-menu');
$this->setMetadata($actions->getDropdownMenuMetadata());
return $this;
}
public function setDropdownMenuID($id) {
Javelin::initBehavior('phui-dropdown-menu');
$this->addSigil('phui-dropdown-menu'); $this->addSigil('phui-dropdown-menu');
$this->setMetadata( $this->setMetadata(
array( array(
'items' => $actions, 'menuID' => $id,
)); ));
return $this; return $this;

View file

@ -24,6 +24,7 @@ final class PHUIHeaderView extends AphrontTagView {
private $badges = array(); private $badges = array();
private $href; private $href;
private $actionList; private $actionList;
private $actionListID;
public function setHeader($header) { public function setHeader($header) {
$this->header = $header; $this->header = $header;
@ -90,6 +91,11 @@ final class PHUIHeaderView extends AphrontTagView {
return $this; return $this;
} }
public function setActionListID($action_list_id) {
$this->actionListID = $action_list_id;
return $this;
}
public function setPolicyObject(PhabricatorPolicyInterface $object) { public function setPolicyObject(PhabricatorPolicyInterface $object) {
$this->policyObject = $object; $this->policyObject = $object;
return $this; return $this;
@ -189,14 +195,20 @@ final class PHUIHeaderView extends AphrontTagView {
protected function getTagContent() { protected function getTagContent() {
if ($this->actionList) { if ($this->actionList || $this->actionListID) {
$action_button = id(new PHUIButtonView()) $action_button = id(new PHUIButtonView())
->setTag('a') ->setTag('a')
->setText(pht('Actions')) ->setText(pht('Actions'))
->setHref('#') ->setHref('#')
->setIcon('fa-bars') ->setIcon('fa-bars')
->addClass('phui-mobile-menu') ->addClass('phui-mobile-menu');
->setDropdownMenu($this->actionList);
if ($this->actionList) {
$action_button->setDropdownMenu($this->actionList);
} else if ($this->actionListID) {
$action_button->setDropdownMenuID($this->actionListID);
}
$this->addActionLink($action_button); $this->addActionLink($action_button);
} }

View file

@ -43,10 +43,7 @@ final class PHUIListItemView extends AphrontTagView {
Javelin::initBehavior('phui-dropdown-menu'); Javelin::initBehavior('phui-dropdown-menu');
$this->addSigil('phui-dropdown-menu'); $this->addSigil('phui-dropdown-menu');
$this->setMetadata( $this->setMetadata($actions->getDropdownMenuMetadata());
array(
'items' => $actions,
));
return $this; return $this;
} }

View file

@ -327,9 +327,7 @@ final class PHUITimelineEventView extends AphrontView {
'sigil' => $sigil, 'sigil' => $sigil,
'aria-haspopup' => 'true', 'aria-haspopup' => 'true',
'aria-expanded' => 'false', 'aria-expanded' => 'false',
'meta' => array( 'meta' => $action_list->getDropdownMenuMetadata(),
'items' => hsprintf('%s', $action_list),
),
), ),
array( array(
$aural, $aural,

View file

@ -114,7 +114,7 @@ final class PHUITwoColumnView extends AphrontTagView {
$curtain = $this->getCurtain(); $curtain = $this->getCurtain();
if ($curtain) { if ($curtain) {
$action_list = $curtain->getActionList(); $action_list = $curtain->getActionList();
$this->header->setActionList($action_list); $this->header->setActionListID($action_list->getID());
} }
$header = phutil_tag_div( $header = phutil_tag_div(

View file

@ -48,6 +48,7 @@
.aphront-dialog-tail { .aphront-dialog-tail {
border: none; border: none;
position: relative;
background: {$lightgreybackground}; background: {$lightgreybackground};
padding: 8px 16px; padding: 8px 16px;
border-top: 1px solid {$thinblueborder}; border-top: 1px solid {$thinblueborder};
@ -55,6 +56,23 @@
border-bottom-right-radius: 3px; border-bottom-right-radius: 3px;
} }
.device .aphront-dialog-resize {
/* No resizing on devices. */
display: none;
}
.aphront-dialog-resize {
position: absolute;
right: -4px;
bottom: -4px;
width: 18px;
height: 18px;
background-image: url(/rsrc/image/resize.png);
background-size: 100%;
cursor: nwse-resize;
pointer-events: all;
}
.aphront-dialog-foot { .aphront-dialog-foot {
padding: 6px 0; padding: 6px 0;
float: left; float: left;

View file

@ -57,10 +57,26 @@ input.typeahead-browse-input {
.typeahead-browse-item button { .typeahead-browse-item button {
float: right; float: right;
margin: 2px 6px; margin: 8px 6px 0;
} }
.typeahead-browse-item a.jx-tokenizer-token { .typeahead-browse-item a.jx-tokenizer-token {
margin-top: 1px; margin-top: 1px;
margin-left: 6px; margin-left: 6px;
} }
.typeahead-browse-item .phabricator-main-search-typeahead-result {
margin: 2px 0;
padding: 0 8px;
}
.typeahead-browse-item .phabricator-main-search-typeahead-result.has-image {
padding-left: 48px;
}
.typeahead-browse-item
.phabricator-main-search-typeahead-result.result-closed
.result-name {
text-decoration: line-through;
color: {$lightgreytext};
}

View file

@ -264,6 +264,32 @@ a.policy-control .caret {
border-top-color: #000; border-top-color: #000;
} }
.phabricator-action-view-submenu .caret-right {
float: right;
margin-top: 4px;
margin-right: 6px;
border-left-color: {$alphablue};
}
.phabricator-action-view-submenu .caret {
float: right;
margin-top: 5px;
margin-right: 4px;
border-top: 7px solid {$lightgreytext};
}
.phabricator-action-list-view .phabricator-action-view-submenu.phui-submenu-open
.phabricator-action-view-item {
background-color: rgba({$alphablue}, 0.07);
color: {$sky};
border-radius: 3px;
}
.phabricator-action-list-view .phabricator-action-view-submenu.phui-submenu-open
.phabricator-action-view-item .phui-icon-view {
color: {$sky};
}
/* Icons */ /* Icons */
.button.has-icon { .button.has-icon {
position: relative; position: relative;

View file

@ -169,8 +169,9 @@
/* When tags appear in property lists, give them a little more vertical /* When tags appear in property lists, give them a little more vertical
spacing. */ spacing. */
.phui-property-list-view .phui-tag-view { .phui-property-list-value .phui-tag-view {
margin: 2px 0; margin: 2px 0;
white-space: pre-wrap;
} }
.phui-property-list-has-actions .phui-property-list-properties-wrap { .phui-property-list-has-actions .phui-property-list-properties-wrap {

View file

@ -417,3 +417,7 @@ a.phui-timeline-menu .phui-icon-view {
.phui-timeline-badges .phui-badge-mini .phui-icon-view { .phui-timeline-badges .phui-badge-mini .phui-icon-view {
font-size: 10px; font-size: 10px;
} }
.phui-comment-preview-view {
margin-bottom: 20px;
}

View file

@ -157,6 +157,80 @@ JX.install('Workflow', {
_getActiveWorkflow : function() { _getActiveWorkflow : function() {
var stack = JX.Workflow._stack; var stack = JX.Workflow._stack;
return stack[stack.length - 1]; return stack[stack.length - 1];
},
_onresizestart: function(e) {
var self = JX.Workflow;
if (self._resizing) {
return;
}
var workflow = self._getActiveWorkflow();
if (!workflow) {
return;
}
e.kill();
var form = JX.DOM.find(workflow._root, 'div', 'jx-dialog');
var resize = e.getNodeData('jx-dialog-resize');
var node_y = JX.$(resize.resizeY);
var dim = JX.Vector.getDim(form);
dim.y = JX.Vector.getDim(node_y).y;
if (!form._minimumSize) {
form._minimumSize = dim;
}
self._resizing = {
min: form._minimumSize,
form: form,
startPos: JX.$V(e),
startDim: dim,
resizeY: node_y,
resizeX: resize.resizeX
};
},
_onmousemove: function(e) {
var self = JX.Workflow;
if (!self._resizing) {
return;
}
var spec = self._resizing;
var form = spec.form;
var min = spec.min;
var delta = JX.$V(e).add(-spec.startPos.x, -spec.startPos.y);
var src_dim = spec.startDim;
var dst_dim = JX.$V(src_dim.x + delta.x, src_dim.y + delta.y);
if (dst_dim.x < min.x) {
dst_dim.x = min.x;
}
if (dst_dim.y < min.y) {
dst_dim.y = min.y;
}
if (spec.resizeX) {
JX.$V(dst_dim.x, null).setDim(form);
}
if (spec.resizeY) {
JX.$V(null, dst_dim.y).setDim(spec.resizeY);
}
},
_onmouseup: function() {
var self = JX.Workflow;
if (!self._resizing) {
return;
}
self._resizing = false;
} }
}, },
@ -220,6 +294,12 @@ JX.install('Workflow', {
[], [],
JX.Workflow._onsyntheticsubmit); JX.Workflow._onsyntheticsubmit);
JX.DOM.listen(
this._root,
'mousedown',
'jx-dialog-resize',
JX.Workflow._onresizestart);
// Note that even in the presence of a content frame, we're doing // Note that even in the presence of a content frame, we're doing
// everything here at top level: dialogs are fully modal and cover // everything here at top level: dialogs are fully modal and cover
// the entire window. // the entire window.
@ -413,6 +493,9 @@ JX.install('Workflow', {
} }
JX.Stratcom.listen('keydown', null, close_dialog_when_user_presses_escape); JX.Stratcom.listen('keydown', null, close_dialog_when_user_presses_escape);
JX.Stratcom.listen('mousemove', null, JX.Workflow._onmousemove);
JX.Stratcom.listen('mouseup', null, JX.Workflow._onmouseup);
} }
}); });

Binary file not shown.

After

Width:  |  Height:  |  Size: 288 B

View file

@ -18,10 +18,21 @@ JX.behavior('phabricator-object-selector', function(config) {
var query_timer = null; var query_timer = null;
var query_delay = 50; var query_delay = 50;
var phid_input = JX.DOM.find( // TODO: This is fairly grotesque, but the dialog has two different forms
// inside it and there's no way to sigil the inputs in the "real" form right
// now. Clean this up when the dialog as a whole gets cleaned up.
var inputs = JX.DOM.scry(
JX.$(config.form), JX.$(config.form),
'input', 'input',
'aphront-dialog-application-input'); 'aphront-dialog-application-input');
var phid_input;
for (var ii = 0; ii < inputs.length; ii++) {
if (inputs[ii].name == 'phids') {
phid_input = inputs[ii];
break;
}
}
var last_value = JX.$(config.query).value; var last_value = JX.$(config.query).value;

View file

@ -16,17 +16,42 @@ JX.behavior('phui-dropdown-menu', function() {
e.kill(); e.kill();
var list = JX.$H(data.items).getFragment().firstChild; var list;
var placeholder;
if (data.items) {
list = JX.$H(data.items).getFragment().firstChild;
} else {
list = JX.$(data.menuID);
placeholder = JX.$N('span');
}
var icon = e.getNode('phui-dropdown-menu'); var icon = e.getNode('phui-dropdown-menu');
data.menu = new JX.PHUIXDropdownMenu(icon); data.menu = new JX.PHUIXDropdownMenu(icon);
data.menu.setContent(list);
data.menu.listen('open', function() {
if (placeholder) {
JX.DOM.replace(list, placeholder);
}
data.menu.setContent(list);
});
data.menu.listen('close', function() {
if (placeholder) {
JX.DOM.replace(placeholder, list);
}
});
data.menu.open(); data.menu.open();
JX.DOM.listen(list, 'click', 'tag:a', function(e) { JX.DOM.listen(list, 'click', 'tag:a', function(e) {
if (!e.isNormalClick()) { if (!e.isNormalClick()) {
return; return;
} }
if (JX.Stratcom.pass()) {
return;
}
data.menu.close(); data.menu.close();
}); });
}); });

View file

@ -0,0 +1,44 @@
/**
* @provides javelin-behavior-phui-submenu
* @requires javelin-behavior
* javelin-stratcom
* javelin-dom
*/
JX.behavior('phui-submenu', function() {
JX.Stratcom.listen('click', 'phui-submenu', function(e) {
if (!e.isNormalClick()) {
return;
}
var node = e.getNode('phui-submenu');
var data = e.getNodeData('phui-submenu');
e.kill();
data.open = !data.open;
for (var ii = 0; ii < data.itemIDs.length; ii++) {
var id = data.itemIDs[ii];
var item = JX.$(id);
if (data.open) {
JX.DOM.show(item);
} else {
JX.DOM.hide(item);
}
// Add a class so we can animate zany effects.
JX.DOM.alterClass(item, 'phui-submenu-animate', data.open);
}
JX.DOM.alterClass(node, 'phui-submenu-open', data.open);
// Toggle the caret from ">" to "V" when opening the menu, and back again
// when closing it.
var caret = JX.$(data.caretID);
JX.DOM.alterClass(caret, 'caret', data.open);
JX.DOM.alterClass(caret, 'caret-right', !data.open);
});
});

View file

@ -42,7 +42,7 @@ JX.install('PHUIXDropdownMenu', {
JX.Stratcom.listen('keydown', null, JX.bind(this, this._onkey)); JX.Stratcom.listen('keydown', null, JX.bind(this, this._onkey));
}, },
events: ['open'], events: ['open', 'close'],
properties: { properties: {
width: null, width: null,
@ -83,6 +83,8 @@ JX.install('PHUIXDropdownMenu', {
this._open = false; this._open = false;
this._hide(); this._hide();
this.invoke('close');
return this; return this;
}, },