1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2025-02-13 15:28:35 +01:00

(stable) Promote 2018 Week 11

This commit is contained in:
epriestley 2018-03-16 16:37:23 -07:00
commit f03d6d6a2a
76 changed files with 1408 additions and 441 deletions

View file

Before

Width:  |  Height:  |  Size: 9.7 KiB

After

Width:  |  Height:  |  Size: 9.7 KiB

View file

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

View file

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View file

Before

Width:  |  Height:  |  Size: 5 KiB

After

Width:  |  Height:  |  Size: 5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -10,13 +10,11 @@ return array(
'conpherence.pkg.css' => 'e68cf1fa',
'conpherence.pkg.js' => '15191c65',
'core.pkg.css' => 'c218ed53',
'core.pkg.js' => '8b7400e7',
'darkconsole.pkg.js' => '1f9a31bc',
'core.pkg.js' => '8581cd02',
'differential.pkg.css' => '113e692c',
'differential.pkg.js' => 'f6d809c0',
'diffusion.pkg.css' => 'a2d17c7d',
'diffusion.pkg.js' => '6134c5a1',
'favicon.ico' => '30672e08',
'maniphest.pkg.css' => '4845691a',
'maniphest.pkg.js' => '4d7e79c8',
'rsrc/audio/basic/alert.mp3' => '98461568',
@ -25,7 +23,7 @@ return array(
'rsrc/audio/basic/tap.mp3' => 'fc2fd796',
'rsrc/audio/basic/ting.mp3' => '17660001',
'rsrc/css/aphront/aphront-bars.css' => '231ac33c',
'rsrc/css/aphront/dark-console.css' => 'f7b071f1',
'rsrc/css/aphront/dark-console.css' => '0e14e8f6',
'rsrc/css/aphront/dialog-view.css' => '6bfc244b',
'rsrc/css/aphront/list-filter-view.css' => '5d6f0526',
'rsrc/css/aphront/multi-column.css' => '84cc6640',
@ -47,7 +45,7 @@ return array(
'rsrc/css/application/conduit/conduit-api.css' => '7bc725c4',
'rsrc/css/application/config/config-options.css' => '4615667b',
'rsrc/css/application/config/config-template.css' => '8f18fa41',
'rsrc/css/application/config/setup-issue.css' => '7dae7f18',
'rsrc/css/application/config/setup-issue.css' => '30ee0173',
'rsrc/css/application/config/unhandled-exception.css' => '4c96257a',
'rsrc/css/application/conpherence/color.css' => 'abb4c358',
'rsrc/css/application/conpherence/durable-column.css' => '89ea6bef',
@ -270,28 +268,8 @@ return array(
'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadPreloadedSource.js' => '54f314a0',
'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadSource.js' => 'ab9e0a82',
'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadStaticSource.js' => '6c0e62fa',
'rsrc/favicons/apple-touch-icon-114x114.png' => '12a24178',
'rsrc/favicons/apple-touch-icon-120x120.png' => '0d1543c7',
'rsrc/favicons/apple-touch-icon-144x144.png' => '8043b5a5',
'rsrc/favicons/apple-touch-icon-152x152.png' => '65905ecd',
'rsrc/favicons/apple-touch-icon-57x57.png' => '2bfc7b0a',
'rsrc/favicons/apple-touch-icon-60x60.png' => '8ff52925',
'rsrc/favicons/apple-touch-icon-72x72.png' => 'a2bb65d6',
'rsrc/favicons/apple-touch-icon-76x76.png' => '2d061a11',
'rsrc/favicons/favicon-128.png' => '72f7e812',
'rsrc/favicons/favicon-16x16.png' => 'fc6275ba',
'rsrc/favicons/favicon-196x196.png' => '95db275e',
'rsrc/favicons/favicon-32x32.png' => '5bd18b6c',
'rsrc/favicons/favicon-96x96.png' => '7242c8e9',
'rsrc/favicons/favicon-mention.ico' => '1fdd0fa4',
'rsrc/favicons/favicon-message.ico' => '115bc010',
'rsrc/favicons/favicon.ico' => 'cdb11121',
'rsrc/favicons/mask-icon.svg' => 'e132a80f',
'rsrc/favicons/mstile-144x144.png' => '310c2ee5',
'rsrc/favicons/mstile-150x150.png' => '74bf5133',
'rsrc/favicons/mstile-310x150.png' => '4a49d3ee',
'rsrc/favicons/mstile-310x310.png' => 'a52ab264',
'rsrc/favicons/mstile-70x70.png' => '5edce7b8',
'rsrc/image/BFCFDA.png' => 'd5ec91f4',
'rsrc/image/actions/edit.png' => '2fc41442',
'rsrc/image/avatar.png' => '17d346a4',
@ -374,7 +352,7 @@ return array(
'rsrc/image/texture/table_header_tall.png' => 'd56b434f',
'rsrc/js/application/aphlict/Aphlict.js' => 'e1d4b11a',
'rsrc/js/application/aphlict/behavior-aphlict-dropdown.js' => 'caade6f2',
'rsrc/js/application/aphlict/behavior-aphlict-listen.js' => '4cc4f460',
'rsrc/js/application/aphlict/behavior-aphlict-listen.js' => '599a8f5f',
'rsrc/js/application/aphlict/behavior-aphlict-status.js' => '5e2634b9',
'rsrc/js/application/aphlict/behavior-desktop-notifications-control.js' => '27ca6289',
'rsrc/js/application/calendar/behavior-day-view.js' => '4b3c4443',
@ -412,7 +390,7 @@ return array(
'rsrc/js/application/diffusion/behavior-load-blame.js' => '42126667',
'rsrc/js/application/diffusion/behavior-locate-file.js' => '6d3e1947',
'rsrc/js/application/diffusion/behavior-pull-lastmodified.js' => 'f01586dc',
'rsrc/js/application/doorkeeper/behavior-doorkeeper-tag.js' => 'e5822781',
'rsrc/js/application/doorkeeper/behavior-doorkeeper-tag.js' => '1db13e70',
'rsrc/js/application/drydock/drydock-live-operation-status.js' => '901935ef',
'rsrc/js/application/files/behavior-icon-composer.js' => '8499b6ab',
'rsrc/js/application/files/behavior-launch-icon-composer.js' => '48086888',
@ -467,7 +445,7 @@ return array(
'rsrc/js/core/KeyboardShortcut.js' => '1ae869f2',
'rsrc/js/core/KeyboardShortcutManager.js' => 'c19dd9b9',
'rsrc/js/core/MultirowRowManager.js' => 'b5d57730',
'rsrc/js/core/Notification.js' => '008faf9c',
'rsrc/js/core/Notification.js' => '4f774dac',
'rsrc/js/core/Prefab.js' => '77b0ae28',
'rsrc/js/core/ShapedRequest.js' => '7cbe244b',
'rsrc/js/core/TextAreaUtils.js' => '320810c8',
@ -483,7 +461,6 @@ return array(
'rsrc/js/core/behavior-detect-timezone.js' => '4c193c96',
'rsrc/js/core/behavior-device.js' => 'a3714c76',
'rsrc/js/core/behavior-drag-and-drop-textarea.js' => '484a6e22',
'rsrc/js/core/behavior-error-log.js' => '6882e80a',
'rsrc/js/core/behavior-fancy-datepicker.js' => 'ecf4e799',
'rsrc/js/core/behavior-file-tree.js' => '88236f00',
'rsrc/js/core/behavior-form.js' => '5c54cbf3',
@ -522,7 +499,7 @@ return array(
'rsrc/js/core/behavior-workflow.js' => '0a3f3021',
'rsrc/js/core/darkconsole/DarkLog.js' => 'c8e1ffe3',
'rsrc/js/core/darkconsole/DarkMessage.js' => 'c48cccdd',
'rsrc/js/core/darkconsole/behavior-dark-console.js' => '17bb8539',
'rsrc/js/core/darkconsole/behavior-dark-console.js' => '66888767',
'rsrc/js/core/phtize.js' => 'd254d646',
'rsrc/js/phui/behavior-phui-dropdown-menu.js' => 'b95d6f7d',
'rsrc/js/phui/behavior-phui-file-upload.js' => 'b003d4fb',
@ -541,7 +518,7 @@ return array(
'symbols' => array(
'almanac-css' => 'dbb9b3af',
'aphront-bars' => '231ac33c',
'aphront-dark-console-css' => 'f7b071f1',
'aphront-dark-console-css' => '0e14e8f6',
'aphront-dialog-view-css' => '6bfc244b',
'aphront-list-filter-view-css' => '5d6f0526',
'aphront-multi-column-view-css' => '84cc6640',
@ -589,7 +566,7 @@ return array(
'javelin-aphlict' => 'e1d4b11a',
'javelin-behavior' => '61cbc29a',
'javelin-behavior-aphlict-dropdown' => 'caade6f2',
'javelin-behavior-aphlict-listen' => '4cc4f460',
'javelin-behavior-aphlict-listen' => '599a8f5f',
'javelin-behavior-aphlict-status' => '5e2634b9',
'javelin-behavior-aphront-basic-tokenizer' => 'b3a4b884',
'javelin-behavior-aphront-drag-and-drop-textarea' => '484a6e22',
@ -609,7 +586,7 @@ return array(
'javelin-behavior-conpherence-pontificate' => '55616e04',
'javelin-behavior-conpherence-search' => '9bbf3762',
'javelin-behavior-countdown-timer' => 'e4cc26b3',
'javelin-behavior-dark-console' => '17bb8539',
'javelin-behavior-dark-console' => '66888767',
'javelin-behavior-dashboard-async-panel' => '469c0d9e',
'javelin-behavior-dashboard-move-panels' => '408bf173',
'javelin-behavior-dashboard-query-panel-select' => '453c5375',
@ -629,12 +606,11 @@ return array(
'javelin-behavior-diffusion-jump-to' => '73d09eef',
'javelin-behavior-diffusion-locate-file' => '6d3e1947',
'javelin-behavior-diffusion-pull-lastmodified' => 'f01586dc',
'javelin-behavior-doorkeeper-tag' => 'e5822781',
'javelin-behavior-doorkeeper-tag' => '1db13e70',
'javelin-behavior-drydock-live-operation-status' => '901935ef',
'javelin-behavior-durable-column' => '2ae077e1',
'javelin-behavior-editengine-reorder-configs' => 'd7a74243',
'javelin-behavior-editengine-reorder-fields' => 'b59e1e96',
'javelin-behavior-error-log' => '6882e80a',
'javelin-behavior-event-all-day' => 'b41537c9',
'javelin-behavior-fancy-datepicker' => 'ecf4e799',
'javelin-behavior-global-drag-and-drop' => '960f6a39',
@ -796,7 +772,7 @@ return array(
'phabricator-keyboard-shortcut-manager' => 'c19dd9b9',
'phabricator-main-menu-view' => '1802a242',
'phabricator-nav-view-css' => 'a9e3e6d5',
'phabricator-notification' => '008faf9c',
'phabricator-notification' => '4f774dac',
'phabricator-notification-css' => '457861ec',
'phabricator-notification-menu-css' => '10685bd4',
'phabricator-object-selector-css' => '85ee8ce6',
@ -902,7 +878,7 @@ return array(
'releeph-preview-branch' => 'b7a6f4a5',
'releeph-request-differential-create-dialog' => '8d8b92cd',
'releeph-request-typeahead-css' => '667a48ae',
'setup-issue-css' => '7dae7f18',
'setup-issue-css' => '30ee0173',
'sprite-login-css' => '396f3c3a',
'sprite-tokens-css' => '9cdfd599',
'syntax-default-css' => '9923583c',
@ -918,13 +894,6 @@ return array(
'javelin-typeahead-preloaded-source',
'javelin-util',
),
'008faf9c' => array(
'javelin-install',
'javelin-dom',
'javelin-stratcom',
'javelin-util',
'phabricator-notification-css',
),
'013ffff9' => array(
'javelin-install',
'javelin-util',
@ -1008,16 +977,6 @@ return array(
'aphront-typeahead-control-css',
'phui-tag-view-css',
),
'17bb8539' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-util',
'javelin-dom',
'javelin-request',
'phabricator-keyboard-shortcut',
'phabricator-darklog',
'phabricator-darkmessage',
),
'1802a242' => array(
'phui-theme-css',
),
@ -1045,6 +1004,13 @@ return array(
'javelin-request',
'javelin-uri',
),
'1db13e70' => array(
'javelin-behavior',
'javelin-dom',
'javelin-json',
'javelin-workflow',
'javelin-magical-init',
),
'1f6794f6' => array(
'javelin-behavior',
'javelin-stratcom',
@ -1286,20 +1252,6 @@ return array(
'javelin-uri',
'phabricator-notification',
),
'4cc4f460' => array(
'javelin-behavior',
'javelin-aphlict',
'javelin-stratcom',
'javelin-request',
'javelin-uri',
'javelin-dom',
'javelin-json',
'javelin-router',
'javelin-util',
'javelin-leader',
'javelin-sound',
'phabricator-notification',
),
'4d863052' => array(
'javelin-dom',
'javelin-util',
@ -1316,6 +1268,13 @@ return array(
'javelin-stratcom',
'javelin-dom',
),
'4f774dac' => array(
'javelin-install',
'javelin-dom',
'javelin-stratcom',
'javelin-util',
'phabricator-notification-css',
),
'503e17fd' => array(
'javelin-install',
'javelin-typeahead-source',
@ -1370,6 +1329,20 @@ return array(
'javelin-uri',
'phabricator-file-upload',
),
'599a8f5f' => array(
'javelin-behavior',
'javelin-aphlict',
'javelin-stratcom',
'javelin-request',
'javelin-uri',
'javelin-dom',
'javelin-json',
'javelin-router',
'javelin-util',
'javelin-leader',
'javelin-sound',
'phabricator-notification',
),
'59a7976a' => array(
'javelin-install',
'javelin-dom',
@ -1430,6 +1403,16 @@ return array(
'javelin-typeahead-ondemand-source',
'javelin-util',
),
66888767 => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-util',
'javelin-dom',
'javelin-request',
'phabricator-keyboard-shortcut',
'phabricator-darklog',
'phabricator-darkmessage',
),
'66a6def1' => array(
'javelin-behavior',
'javelin-dom',
@ -1443,9 +1426,6 @@ return array(
'javelin-dom',
'phabricator-notification',
),
'6882e80a' => array(
'javelin-dom',
),
'68af71ca' => array(
'javelin-install',
'javelin-dom',
@ -2108,13 +2088,6 @@ return array(
'javelin-behavior',
'javelin-dom',
),
'e5822781' => array(
'javelin-behavior',
'javelin-dom',
'javelin-json',
'javelin-workflow',
'javelin-magical-init',
),
'e74b7517' => array(
'javelin-install',
'phuix-button-view',
@ -2364,10 +2337,6 @@ return array(
'javelin-behavior-user-menu',
'phabricator-favicon',
),
'darkconsole.pkg.js' => array(
'javelin-behavior-dark-console',
'javelin-behavior-error-log',
),
'differential.pkg.css' => array(
'differential-core-view-css',
'differential-changeset-view-css',

View file

@ -224,8 +224,4 @@ return array(
'javelin-behavior-maniphest-subpriority-editor',
'javelin-behavior-maniphest-list-editor',
),
'darkconsole.pkg.js' => array(
'javelin-behavior-dark-console',
'javelin-behavior-error-log',
),
);

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_differential.differential_reviewer
ADD options LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT};

View file

@ -0,0 +1,2 @@
UPDATE {$NAMESPACE}_differential.differential_reviewer
SET options = '{}' WHERE options = '';

View file

@ -45,7 +45,7 @@ set +x
sudo apt-get -qq update
sudo apt-get install \
$GIT mysql-server apache2 dpkg-dev \
php5 php5-mysql php5-gd php5-dev php5-curl php-apc php5-cli php5-json
php5 php5-mysqlnd php5-gd php5-dev php5-curl php-apc php5-cli php5-json
# Enable mod_rewrite
sudo a2enmod rewrite

View file

@ -2055,6 +2055,8 @@ phutil_register_library_map(array(
'PhabricatorApplicationTransactionValidationResponse' => 'applications/transactions/response/PhabricatorApplicationTransactionValidationResponse.php',
'PhabricatorApplicationTransactionValueController' => 'applications/transactions/controller/PhabricatorApplicationTransactionValueController.php',
'PhabricatorApplicationTransactionView' => 'applications/transactions/view/PhabricatorApplicationTransactionView.php',
'PhabricatorApplicationTransactionWarningException' => 'applications/transactions/exception/PhabricatorApplicationTransactionWarningException.php',
'PhabricatorApplicationTransactionWarningResponse' => 'applications/transactions/response/PhabricatorApplicationTransactionWarningResponse.php',
'PhabricatorApplicationUninstallController' => 'applications/meta/controller/PhabricatorApplicationUninstallController.php',
'PhabricatorApplicationsApplication' => 'applications/meta/application/PhabricatorApplicationsApplication.php',
'PhabricatorApplicationsController' => 'applications/meta/controller/PhabricatorApplicationsController.php',
@ -2953,6 +2955,8 @@ phutil_register_library_map(array(
'PhabricatorFactObjectDimension' => 'applications/fact/storage/PhabricatorFactObjectDimension.php',
'PhabricatorFactRaw' => 'applications/fact/storage/PhabricatorFactRaw.php',
'PhabricatorFactUpdateIterator' => 'applications/fact/extract/PhabricatorFactUpdateIterator.php',
'PhabricatorFaviconRef' => 'applications/files/favicon/PhabricatorFaviconRef.php',
'PhabricatorFaviconRefQuery' => 'applications/files/favicon/PhabricatorFaviconRefQuery.php',
'PhabricatorFavoritesApplication' => 'applications/favorites/application/PhabricatorFavoritesApplication.php',
'PhabricatorFavoritesController' => 'applications/favorites/controller/PhabricatorFavoritesController.php',
'PhabricatorFavoritesMainMenuBarExtension' => 'applications/favorites/engineextension/PhabricatorFavoritesMainMenuBarExtension.php',
@ -4331,7 +4335,6 @@ phutil_register_library_map(array(
'PhabricatorSystemDAO' => 'applications/system/storage/PhabricatorSystemDAO.php',
'PhabricatorSystemDestructionGarbageCollector' => 'applications/system/garbagecollector/PhabricatorSystemDestructionGarbageCollector.php',
'PhabricatorSystemDestructionLog' => 'applications/system/storage/PhabricatorSystemDestructionLog.php',
'PhabricatorSystemFaviconController' => 'applications/system/controller/PhabricatorSystemFaviconController.php',
'PhabricatorSystemReadOnlyController' => 'applications/system/controller/PhabricatorSystemReadOnlyController.php',
'PhabricatorSystemRemoveDestroyWorkflow' => 'applications/system/management/PhabricatorSystemRemoveDestroyWorkflow.php',
'PhabricatorSystemRemoveLogWorkflow' => 'applications/system/management/PhabricatorSystemRemoveLogWorkflow.php',
@ -7483,6 +7486,8 @@ phutil_register_library_map(array(
'PhabricatorApplicationTransactionValidationResponse' => 'AphrontProxyResponse',
'PhabricatorApplicationTransactionValueController' => 'PhabricatorApplicationTransactionController',
'PhabricatorApplicationTransactionView' => 'AphrontView',
'PhabricatorApplicationTransactionWarningException' => 'Exception',
'PhabricatorApplicationTransactionWarningResponse' => 'AphrontProxyResponse',
'PhabricatorApplicationUninstallController' => 'PhabricatorApplicationsController',
'PhabricatorApplicationsApplication' => 'PhabricatorApplication',
'PhabricatorApplicationsController' => 'PhabricatorController',
@ -8512,6 +8517,8 @@ phutil_register_library_map(array(
'PhabricatorFactObjectDimension' => 'PhabricatorFactDimension',
'PhabricatorFactRaw' => 'PhabricatorFactDAO',
'PhabricatorFactUpdateIterator' => 'PhutilBufferedIterator',
'PhabricatorFaviconRef' => 'Phobject',
'PhabricatorFaviconRefQuery' => 'Phobject',
'PhabricatorFavoritesApplication' => 'PhabricatorApplication',
'PhabricatorFavoritesController' => 'PhabricatorController',
'PhabricatorFavoritesMainMenuBarExtension' => 'PhabricatorMainMenuBarExtension',
@ -10142,7 +10149,6 @@ phutil_register_library_map(array(
'PhabricatorSystemDAO' => 'PhabricatorLiskDAO',
'PhabricatorSystemDestructionGarbageCollector' => 'PhabricatorGarbageCollector',
'PhabricatorSystemDestructionLog' => 'PhabricatorSystemDAO',
'PhabricatorSystemFaviconController' => 'PhabricatorController',
'PhabricatorSystemReadOnlyController' => 'PhabricatorController',
'PhabricatorSystemRemoveDestroyWorkflow' => 'PhabricatorSystemRemoveWorkflow',
'PhabricatorSystemRemoveLogWorkflow' => 'PhabricatorSystemRemoveWorkflow',

View file

@ -67,6 +67,51 @@ final class PhabricatorPHPConfigSetupCheck extends PhabricatorSetupCheck {
->addPHPConfig('always_populate_raw_post_data');
}
if (!extension_loaded('mysqli')) {
$summary = pht(
'Install the MySQLi extension to improve database behavior.');
$message = pht(
'PHP is currently using the very old "mysql" extension to interact '.
'with the database. You should install the newer "mysqli" extension '.
'to improve behaviors (like error handling and query timeouts).'.
"\n\n".
'Phabricator will work with the older extension, but upgrading to the '.
'newer extension is recommended.'.
"\n\n".
'You may be able to install the extension with a command like: %s',
// NOTE: We're intentionally telling you to install "mysqlnd" here; on
// Ubuntu, there's no separate "mysqli" package.
phutil_tag('tt', array(), 'sudo apt-get install php5-mysqlnd'));
$this->newIssue('php.mysqli')
->setName(pht('MySQLi Extension Not Available'))
->setSummary($summary)
->setMessage($message);
} else if (!defined('MYSQLI_ASYNC')) {
$summary = pht(
'Configure the MySQL Native Driver to improve database behavior.');
$message = pht(
'PHP is currently using the older MySQL external driver instead of '.
'the newer MySQL native driver. The older driver lacks options and '.
'features (like support for query timeouts) which allow Phabricator '.
'to interact better with the database.'.
"\n\n".
'Phabricator will work with the older driver, but upgrading to the '.
'native driver is recommended.'.
"\n\n".
'You may be able to install the native driver with a command like: %s',
phutil_tag('tt', array(), 'sudo apt-get install php5-mysqlnd'));
$this->newIssue('php.myqlnd')
->setName(pht('MySQL Native Driver Not Available'))
->setSummary($summary)
->setMessage($message);
}
}
}

View file

@ -64,6 +64,10 @@ EOJSON;
"Phabricator logo in the site header.\n\n".
" - **Wordmark**: Choose new text to display next to the logo. ".
"By default, the header displays //Phabricator//.\n\n")),
$this->newOption('ui.favicons', 'wild', array())
->setSummary(pht('Customize favicons.'))
->setDescription(pht('Customize favicons.'))
->setLocked(true),
$this->newOption('ui.footer-items', $footer_type, array())
->setSummary(
pht(

View file

@ -39,10 +39,13 @@ final class DarkConsoleErrorLogPlugin extends DarkConsolePlugin {
$file = $row['file'];
$line = $row['line'];
$tag = phutil_tag(
$tag = javelin_tag(
'a',
array(
'onclick' => jsprintf('show_details(%d)', $index),
'sigil' => 'darkconsole-expand',
'meta' => array(
'expandID' => 'row-details-'.$index,
),
),
$row['str'].' at ['.basename($file).':'.$line.']');
$rows[] = array($tag);

View file

@ -29,6 +29,18 @@ final class DarkConsoleServicesPlugin extends DarkConsolePlugin {
return false;
}
public function didStartup() {
$should_analyze = self::isQueryAnalyzerRequested();
if ($should_analyze) {
PhutilServiceProfiler::getInstance()
->setCollectStackTraces(true);
}
return null;
}
/**
* @phutil-external-symbol class PhabricatorStartup
*/
@ -266,6 +278,16 @@ final class DarkConsoleServicesPlugin extends DarkConsolePlugin {
$info,
$analysis,
);
if ($row['trace']) {
$rows[] = array(
null,
null,
null,
$row['trace'],
null,
);
}
}
$table = new AphrontTableView($rows);
@ -274,7 +296,7 @@ final class DarkConsoleServicesPlugin extends DarkConsolePlugin {
null,
'n',
'n',
'wide',
'wide prewrap',
'',
));
$table->setHeaders(

View file

@ -154,59 +154,41 @@ final class DifferentialTransactionEditor
$edge_ref_task = DifferentialRevisionHasTaskEdgeType::EDGECONST;
$is_sticky_accept = PhabricatorEnv::getEnvConfig(
'differential.sticky-accept');
$downgrade_rejects = false;
$downgrade_accepts = false;
$want_downgrade = array();
$must_downgrade = array();
if ($this->getIsCloseByCommit()) {
// Never downgrade reviewers when we're closing a revision after a
// commit.
} else {
switch ($xaction->getTransactionType()) {
case DifferentialRevisionUpdateTransaction::TRANSACTIONTYPE:
$downgrade_rejects = true;
if (!$is_sticky_accept) {
// If "sticky accept" is disabled, also downgrade the accepts.
$downgrade_accepts = true;
}
$want_downgrade[] = DifferentialReviewerStatus::STATUS_ACCEPTED;
$want_downgrade[] = DifferentialReviewerStatus::STATUS_REJECTED;
break;
case DifferentialRevisionRequestReviewTransaction::TRANSACTIONTYPE:
$downgrade_rejects = true;
if ((!$is_sticky_accept) ||
(!$object->isChangePlanned())) {
// If the old state isn't "changes planned", downgrade the accepts.
// This exception allows an accepted revision to go through
// "Plan Changes" -> "Request Review" and return to "accepted" if
// the author didn't update the revision, essentially undoing the
// "Plan Changes".
$downgrade_accepts = true;
if (!$object->isChangePlanned()) {
// If the old state isn't "Changes Planned", downgrade the accepts
// even if they're sticky.
// We don't downgrade for "Changes Planned" to allow an author to
// undo a "Plan Changes" by immediately following it up with a
// "Request Review".
$want_downgrade[] = DifferentialReviewerStatus::STATUS_ACCEPTED;
$must_downgrade[] = DifferentialReviewerStatus::STATUS_ACCEPTED;
}
$want_downgrade[] = DifferentialReviewerStatus::STATUS_REJECTED;
break;
}
}
$new_accept = DifferentialReviewerStatus::STATUS_ACCEPTED;
$new_reject = DifferentialReviewerStatus::STATUS_REJECTED;
$old_accept = DifferentialReviewerStatus::STATUS_ACCEPTED_OLDER;
$old_reject = DifferentialReviewerStatus::STATUS_REJECTED_OLDER;
$downgrade = array();
if ($downgrade_accepts) {
$downgrade[] = DifferentialReviewerStatus::STATUS_ACCEPTED;
}
if ($downgrade_rejects) {
$downgrade[] = DifferentialReviewerStatus::STATUS_REJECTED;
}
if ($downgrade) {
if ($want_downgrade) {
$void_type = DifferentialRevisionVoidTransaction::TRANSACTIONTYPE;
$results[] = id(new DifferentialTransaction())
->setTransactionType($void_type)
->setIgnoreOnNoEffect(true)
->setNewValue($downgrade);
->setMetadataValue('void.force', $must_downgrade)
->setNewValue($want_downgrade);
}
$is_commandeer = false;

View file

@ -10,12 +10,16 @@ final class DifferentialReviewer
protected $lastCommentDiffPHID;
protected $lastActorPHID;
protected $voidedPHID;
protected $options = array();
private $authority = array();
private $changesets = self::ATTACHABLE;
protected function getConfiguration() {
return array(
self::CONFIG_SERIALIZATION => array(
'options' => self::SERIALIZATION_JSON,
),
self::CONFIG_COLUMN_SCHEMA => array(
'reviewerStatus' => 'text64',
'lastActionDiffPHID' => 'phid?',
@ -64,6 +68,15 @@ final class DifferentialReviewer
return $this->assertAttached($this->changesets);
}
public function setOption($key, $value) {
$this->options[$key] = $value;
return $this;
}
public function getOption($key, $default = null) {
return idx($this->options, $key, $default);
}
public function isResigned() {
$status_resigned = DifferentialReviewerStatus::STATUS_RESIGNED;
return ($this->getReviewerStatus() == $status_resigned);

View file

@ -66,11 +66,6 @@ final class DifferentialRevisionCommandeerTransaction
'been closed. You can only commandeer open revisions.'));
}
if ($object->isDraft()) {
throw new Exception(
pht('You can not commandeer a draft revision.'));
}
if ($this->isViewerRevisionAuthor($object, $viewer)) {
throw new Exception(
pht(

View file

@ -164,7 +164,14 @@ abstract class DifferentialRevisionReviewTransaction
DifferentialRevision $revision,
PhabricatorUser $viewer,
$value,
$status) {
$status,
array $reviewer_options = array()) {
PhutilTypeSpec::checkMap(
$reviewer_options,
array(
'sticky' => 'optional bool',
));
$map = array();
@ -253,6 +260,8 @@ abstract class DifferentialRevisionReviewTransaction
// accepted.
$reviewer->setVoidedPHID(null);
$reviewer->setOption('sticky', idx($reviewer_options, 'sticky'));
try {
$reviewer->save();
} catch (AphrontDuplicateKeyQueryException $ex) {

View file

@ -16,21 +16,43 @@ final class DifferentialRevisionVoidTransaction
}
public function generateNewValue($object, $value) {
$table = new DifferentialReviewer();
$table_name = $table->getTableName();
$conn = $table->establishConnection('w');
$rows = queryfx_all(
$conn,
'SELECT reviewerPHID FROM %T
WHERE revisionPHID = %s
AND voidedPHID IS NULL
AND reviewerStatus IN (%Ls)',
$table_name,
$reviewers = id(new DifferentialReviewer())->loadAllWhere(
'revisionPHID = %s
AND voidedPHID IS NULL
AND reviewerStatus IN (%Ls)',
$object->getPHID(),
$value);
return ipull($rows, 'reviewerPHID');
$must_downgrade = $this->getMetadataValue('void.force', array());
$must_downgrade = array_fuse($must_downgrade);
$default = PhabricatorEnv::getEnvConfig('differential.sticky-accept');
foreach ($reviewers as $key => $reviewer) {
$status = $reviewer->getReviewerStatus();
// If this void is forced, always downgrade. For example, this happens
// when an author chooses "Request Review": existing reviews are always
// voided, even if they're sticky.
if (isset($must_downgrade[$status])) {
continue;
}
// Otherwise, if this is a sticky accept, don't void it. Accepts may be
// explicitly sticky or unsticky, or they'll use the default value if
// no value is specified.
$is_sticky = $reviewer->getOption('sticky');
$is_sticky = coalesce($is_sticky, $default);
if ($status === DifferentialReviewerStatus::STATUS_ACCEPTED) {
if ($is_sticky) {
unset($reviewers[$key]);
continue;
}
}
}
return mpull($reviewers, 'getReviewerPHID');
}
public function getTransactionHasEffect($object, $old, $new) {

View file

@ -97,6 +97,7 @@ final class DoorkeeperBridgeJIRA extends DoorkeeperBridge {
$ref->setAttribute('title', idx($fields, 'summary'));
$ref->setAttribute('description', idx($result, 'description'));
$ref->setAttribute('shortname', $result['key']);
$obj = $ref->getExternalObject();
if ($obj->getID()) {

View file

@ -13,17 +13,13 @@ final class DoorkeeperTagsController extends PhabricatorController {
}
$refs = array();
$id_map = array();
foreach ($tags as $tag_spec) {
foreach ($tags as $key => $tag_spec) {
$tag = $tag_spec['ref'];
$ref = id(new DoorkeeperObjectRef())
->setApplicationType($tag[0])
->setApplicationDomain($tag[1])
->setObjectType($tag[2])
->setObjectID($tag[3]);
$key = $ref->getObjectKey();
$id_map[$key] = $tag_spec['id'];
$refs[$key] = $ref;
}
@ -43,19 +39,30 @@ final class DoorkeeperTagsController extends PhabricatorController {
continue;
}
$id = $id_map[$key];
$tag_spec = $tags[$key];
$id = $tag_spec['id'];
$view = idx($tag_spec, 'view');
$is_short = ($view == 'short');
if ($is_short) {
$name = $ref->getShortName();
} else {
$name = $ref->getFullName();
}
$tag = id(new PHUITagView())
->setID($id)
->setName($ref->getFullName())
->setName($name)
->setHref($uri)
->setType(PHUITagView::TYPE_OBJECT)
->setExternal(true)
->render();
$results[] = array(
'id' => $id,
'markup' => $tag,
'id' => $id,
'markup' => $tag,
);
}

View file

@ -107,6 +107,13 @@ final class DoorkeeperObjectRef extends Phobject {
pht('External Object'));
}
public function getShortName() {
return coalesce(
$this->getAttribute('shortname'),
$this->getAttribute('name'),
pht('External Object'));
}
public function getObjectKey() {
if (!$this->objectKey) {
$this->objectKey = PhabricatorHash::digestForIndex(

View file

@ -4,11 +4,41 @@ abstract class DoorkeeperRemarkupRule extends PhutilRemarkupRule {
const KEY_TAGS = 'doorkeeper.tags';
const VIEW_FULL = 'full';
const VIEW_SHORT = 'short';
public function getPriority() {
return 350.0;
}
protected function addDoorkeeperTag(array $spec) {
PhutilTypeSpec::checkMap(
$spec,
array(
'href' => 'string',
'tag' => 'map<string, wild>',
'name' => 'optional string',
'view' => 'optional string',
));
$spec = $spec + array(
'view' => self::VIEW_FULL,
);
$views = array(
self::VIEW_FULL,
self::VIEW_SHORT,
);
$views = array_fuse($views);
if (!isset($views[$spec['view']])) {
throw new Exception(
pht(
'Unsupported Doorkeeper tag view mode "%s". Supported modes are: %s.',
$spec['view'],
implode(', ', $views)));
}
$key = self::KEY_TAGS;
$engine = $this->getEngine();
$token = $engine->storeText(get_class($this));
@ -36,19 +66,26 @@ abstract class DoorkeeperRemarkupRule extends PhutilRemarkupRule {
$refs = array();
foreach ($tags as $spec) {
$tag_id = celerity_generate_unique_node_id();
$href = $spec['href'];
$name = idx($spec, 'name', $href);
$refs[] = array(
'id' => $tag_id,
) + $spec['tag'];
$this->assertFlatText($href);
$this->assertFlatText($name);
if ($this->getEngine()->isTextMode()) {
$view = $spec['href'];
$view = "{$name} <{$href}>";
} else {
$tag_id = celerity_generate_unique_node_id();
$refs[] = array(
'id' => $tag_id,
'view' => $spec['view'],
) + $spec['tag'];
$view = id(new PHUITagView())
->setID($tag_id)
->setName($this->assertFlatText($spec['href']))
->setHref($this->assertFlatText($spec['href']))
->setName($name)
->setHref($href)
->setType(PHUITagView::TYPE_OBJECT)
->setExternal(true);
}
@ -56,7 +93,9 @@ abstract class DoorkeeperRemarkupRule extends PhutilRemarkupRule {
$engine->overwriteStoredText($spec['token'], $view);
}
Javelin::initBehavior('doorkeeper-tag', array('tags' => $refs));
if ($refs) {
Javelin::initBehavior('doorkeeper-tag', array('tags' => $refs));
}
$engine->setTextMetadata($key, array());
}

View file

@ -0,0 +1,447 @@
<?php
final class PhabricatorFaviconRef extends Phobject {
private $viewer;
private $width;
private $height;
private $emblems;
private $uri;
private $cacheKey;
public function __construct() {
$this->emblems = array(null, null, null, null);
}
public function setViewer(PhabricatorUser $viewer) {
$this->viewer = $viewer;
return $this;
}
public function getViewer() {
return $this->viewer;
}
public function setWidth($width) {
$this->width = $width;
return $this;
}
public function getWidth() {
return $this->width;
}
public function setHeight($height) {
$this->height = $height;
return $this;
}
public function getHeight() {
return $this->height;
}
public function setEmblems(array $emblems) {
if (count($emblems) !== 4) {
throw new Exception(
pht(
'Expected four elements in icon emblem list. To omit an emblem, '.
'pass "null".'));
}
$this->emblems = $emblems;
return $this;
}
public function getEmblems() {
return $this->emblems;
}
public function setURI($uri) {
$this->uri = $uri;
return $this;
}
public function getURI() {
return $this->uri;
}
public function setCacheKey($cache_key) {
$this->cacheKey = $cache_key;
return $this;
}
public function getCacheKey() {
return $this->cacheKey;
}
public function newDigest() {
return PhabricatorHash::digestForIndex(serialize($this->toDictionary()));
}
public function toDictionary() {
return array(
'width' => $this->width,
'height' => $this->height,
'emblems' => $this->emblems,
);
}
public static function newConfigurationDigest() {
$all_resources = self::getAllResources();
// Because we need to access this cache on every page, it's very sticky.
// Try to dirty it automatically if any relevant configuration changes.
$inputs = array(
'resources' => $all_resources,
'prod' => PhabricatorEnv::getProductionURI('/'),
'cdn' => PhabricatorEnv::getEnvConfig('security.alternate-file-domain'),
'havepng' => function_exists('imagepng'),
);
return PhabricatorHash::digestForIndex(serialize($inputs));
}
private static function getAllResources() {
$custom_resources = PhabricatorEnv::getEnvConfig('ui.favicons');
foreach ($custom_resources as $key => $custom_resource) {
$custom_resources[$key] = array(
'source-type' => 'file',
'default' => false,
) + $custom_resource;
}
$builtin_resources = self::getBuiltinResources();
return array_merge($builtin_resources, $custom_resources);
}
private static function getBuiltinResources() {
return array(
array(
'source-type' => 'builtin',
'source' => 'favicon/default-76x76.png',
'version' => 1,
'width' => 76,
'height' => 76,
'default' => true,
),
array(
'source-type' => 'builtin',
'source' => 'favicon/default-120x120.png',
'version' => 1,
'width' => 120,
'height' => 120,
'default' => true,
),
array(
'source-type' => 'builtin',
'source' => 'favicon/default-128x128.png',
'version' => 1,
'width' => 128,
'height' => 128,
'default' => true,
),
array(
'source-type' => 'builtin',
'source' => 'favicon/default-152x152.png',
'version' => 1,
'width' => 152,
'height' => 152,
'default' => true,
),
array(
'source-type' => 'builtin',
'source' => 'favicon/dot-pink-64x64.png',
'version' => 1,
'width' => 64,
'height' => 64,
'emblem' => 'dot-pink',
'default' => true,
),
array(
'source-type' => 'builtin',
'source' => 'favicon/dot-red-64x64.png',
'version' => 1,
'width' => 64,
'height' => 64,
'emblem' => 'dot-red',
'default' => true,
),
);
}
public function newURI() {
$dst_w = $this->getWidth();
$dst_h = $this->getHeight();
$template = $this->newTemplateFile(null, $dst_w, $dst_h);
$template_file = $template['file'];
$cache = $this->loadCachedFile($template_file);
if ($cache) {
return $cache->getViewURI();
}
$data = $this->newCompositedFavicon($template);
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
$caught = null;
try {
$favicon_file = $this->newFaviconFile($data);
$xform = id(new PhabricatorTransformedFile())
->setOriginalPHID($template_file->getPHID())
->setTransformedPHID($favicon_file->getPHID())
->setTransform($this->getCacheKey());
try {
$xform->save();
} catch (AphrontDuplicateKeyQueryException $ex) {
unset($unguarded);
$cache = $this->loadCachedFile($template_file);
if (!$cache) {
throw $ex;
}
id(new PhabricatorDestructionEngine())
->destroyObject($favicon_file);
return $cache->getViewURI();
}
} catch (Exception $ex) {
$caught = $ex;
}
unset($unguarded);
if ($caught) {
throw $caught;
}
return $favicon_file->getViewURI();
}
private function loadCachedFile(PhabricatorFile $template_file) {
$viewer = $this->getViewer();
$xform = id(new PhabricatorTransformedFile())->loadOneWhere(
'originalPHID = %s AND transform = %s',
$template_file->getPHID(),
$this->getCacheKey());
if (!$xform) {
return null;
}
return id(new PhabricatorFileQuery())
->setViewer($viewer)
->withPHIDs(array($xform->getTransformedPHID()))
->executeOne();
}
private function newCompositedFavicon($template) {
$dst_w = $this->getWidth();
$dst_h = $this->getHeight();
$src_w = $template['width'];
$src_h = $template['height'];
$template_data = $template['file']->loadFileData();
if (!function_exists('imagecreatefromstring')) {
return $template_data;
}
$src = @imagecreatefromstring($template_data);
if (!$src) {
return $template_data;
}
$dst = imagecreatetruecolor($dst_w, $dst_h);
imagesavealpha($dst, true);
$transparent = imagecolorallocatealpha($dst, 0, 255, 0, 127);
imagefill($dst, 0, 0, $transparent);
imagecopyresampled(
$dst,
$src,
0,
0,
0,
0,
$dst_w,
$dst_h,
$src_w,
$src_h);
// Now, copy any icon emblems on top of the image. These are dots or other
// marks used to indicate status information.
$emblem_w = (int)floor(min($dst_w, $dst_h) / 2);
$emblem_h = $emblem_w;
foreach ($this->emblems as $key => $emblem) {
if ($emblem === null) {
continue;
}
$emblem_template = $this->newTemplateFile(
$emblem,
$emblem_w,
$emblem_h);
switch ($key) {
case 0:
$emblem_x = $dst_w - $emblem_w;
$emblem_y = 0;
break;
case 1:
$emblem_x = $dst_w - $emblem_w;
$emblem_y = $dst_h - $emblem_h;
break;
case 2:
$emblem_x = 0;
$emblem_y = $dst_h - $emblem_h;
break;
case 3:
$emblem_x = 0;
$emblem_y = 0;
break;
}
$emblem_data = $emblem_template['file']->loadFileData();
$src = @imagecreatefromstring($emblem_data);
if (!$src) {
continue;
}
imagecopyresampled(
$dst,
$src,
$emblem_x,
$emblem_y,
0,
0,
$emblem_w,
$emblem_h,
$emblem_template['width'],
$emblem_template['height']);
}
return PhabricatorImageTransformer::saveImageDataInAnyFormat(
$dst,
'image/png');
}
private function newTemplateFile($emblem, $width, $height) {
$all_resources = self::getAllResources();
$scores = array();
$ratio = $width / $height;
foreach ($all_resources as $key => $resource) {
// We can't use an emblem resource for a different emblem, nor for an
// icon base. We also can't use an icon base as an emblem. That is, if
// we're looking for a picture of a red dot, we have to actually find
// a red dot, not just any image which happens to have a similar size.
if (idx($resource, 'emblem') !== $emblem) {
continue;
}
$resource_width = $resource['width'];
$resource_height = $resource['height'];
// Never use a resource with a different aspect ratio.
if (($resource_width / $resource_height) !== $ratio) {
continue;
}
// Try to use custom resources instead of default resources.
if ($resource['default']) {
$default_score = 1;
} else {
$default_score = 0;
}
$width_diff = ($resource_width - $width);
// If we have to resize an image, we'd rather scale a larger image down
// than scale a smaller image up.
if ($width_diff < 0) {
$scale_score = 1;
} else {
$scale_score = 0;
}
// Otherwise, we'd rather scale an image a little bit (ideally, zero)
// than scale an image a lot.
$width_score = abs($width_diff);
$scores[$key] = id(new PhutilSortVector())
->addInt($default_score)
->addInt($scale_score)
->addInt($width_score);
}
if (!$scores) {
if ($emblem === null) {
throw new Exception(
pht(
'Found no background template resource for dimensions %dx%d.',
$width,
$height));
} else {
throw new Exception(
pht(
'Found no template resource (for emblem "%s") with dimensions '.
'%dx%d.',
$emblem,
$width,
$height));
}
}
$scores = msortv($scores, 'getSelf');
$best_score = head_key($scores);
$viewer = $this->getViewer();
$resource = $all_resources[$best_score];
if ($resource['source-type'] === 'builtin') {
$file = PhabricatorFile::loadBuiltin($viewer, $resource['source']);
if (!$file) {
throw new Exception(
pht(
'Failed to load favicon template builtin "%s".',
$resource['source']));
}
} else {
$file = id(new PhabricatorFileQuery())
->setViewer($viewer)
->withPHIDs(array($resource['source']))
->executeOne();
if (!$file) {
throw new Exception(
pht(
'Failed to load favicon template with PHID "%s".',
$resource['source']));
}
}
return array(
'width' => $resource['width'],
'height' => $resource['height'],
'file' => $file,
);
}
private function newFaviconFile($data) {
return PhabricatorFile::newFromFileData(
$data,
array(
'name' => 'favicon',
'canCDN' => true,
));
}
}

View file

@ -0,0 +1,55 @@
<?php
final class PhabricatorFaviconRefQuery extends Phobject {
private $refs;
public function withRefs(array $refs) {
assert_instances_of($refs, 'PhabricatorFaviconRef');
$this->refs = $refs;
return $this;
}
public function execute() {
$viewer = PhabricatorUser::getOmnipotentUser();
$refs = $this->refs;
$config_digest = PhabricatorFaviconRef::newConfigurationDigest();
$ref_map = array();
foreach ($refs as $ref) {
$ref_digest = $ref->newDigest();
$ref_key = "favicon({$config_digest},{$ref_digest},8)";
$ref
->setViewer($viewer)
->setCacheKey($ref_key);
$ref_map[$ref_key] = $ref;
}
$cache = PhabricatorCaches::getImmutableCache();
$ref_hits = $cache->getKeys(array_keys($ref_map));
foreach ($ref_hits as $ref_key => $ref_uri) {
$ref_map[$ref_key]->setURI($ref_uri);
unset($ref_map[$ref_key]);
}
if ($ref_map) {
$new_map = array();
foreach ($ref_map as $ref_key => $ref) {
$ref_uri = $ref->newURI();
$ref->setURI($ref_uri);
$new_map[$ref_key] = $ref_uri;
}
$cache->setKeys($new_map);
}
return $refs;
}
}

View file

@ -152,61 +152,96 @@ final class PhabricatorFileQuery
}
protected function loadPage() {
$files = $this->loadStandardPage(new PhabricatorFile());
$files = $this->loadStandardPage($this->newResultObject());
if (!$files) {
return $files;
}
// Figure out which files we need to load attached objects for. In most
// cases, we need to load attached objects to perform policy checks for
// files.
// However, in some special cases where we know files will always be
// visible, we skip this. See T8478 and T13106.
$need_objects = array();
$need_xforms = array();
foreach ($files as $file) {
$always_visible = false;
if ($file->getIsProfileImage()) {
$always_visible = true;
}
if ($file->isBuiltin()) {
$always_visible = true;
}
if ($always_visible) {
// We just treat these files as though they aren't attached to
// anything. This saves a query in common cases when we're loading
// profile images or builtins. We could be slightly more nuanced
// about this and distinguish between "not attached to anything" and
// "might be attached but policy checks don't need to care".
$file->attachObjectPHIDs(array());
continue;
}
$need_objects[] = $file;
$need_xforms[] = $file;
}
$viewer = $this->getViewer();
$is_omnipotent = $viewer->isOmnipotent();
// We need to load attached objects to perform policy checks for files.
// First, load the edges.
$edge_type = PhabricatorFileHasObjectEdgeType::EDGECONST;
$file_phids = mpull($files, 'getPHID');
$edges = id(new PhabricatorEdgeQuery())
->withSourcePHIDs($file_phids)
->withEdgeTypes(array($edge_type))
->execute();
// If we have any files left which do need objects, load the edges now.
$object_phids = array();
foreach ($files as $file) {
$phids = array_keys($edges[$file->getPHID()][$edge_type]);
$file->attachObjectPHIDs($phids);
if ($need_objects) {
$edge_type = PhabricatorFileHasObjectEdgeType::EDGECONST;
$file_phids = mpull($need_objects, 'getPHID');
if ($file->getIsProfileImage()) {
// If this is a profile image, don't bother loading related files.
// It will always be visible, and we can get into trouble if we try
// to load objects and end up stuck in a cycle. See T8478.
continue;
}
$edges = id(new PhabricatorEdgeQuery())
->withSourcePHIDs($file_phids)
->withEdgeTypes(array($edge_type))
->execute();
if ($is_omnipotent) {
// If the viewer is omnipotent, we don't need to load the associated
// objects either since they can certainly see the object. Skipping
// this can improve performance and prevent cycles.
continue;
}
foreach ($need_objects as $file) {
$phids = array_keys($edges[$file->getPHID()][$edge_type]);
$file->attachObjectPHIDs($phids);
foreach ($phids as $phid) {
$object_phids[$phid] = true;
if ($is_omnipotent) {
// If the viewer is omnipotent, we don't need to load the associated
// objects either since the viewer can certainly see the object.
// Skipping this can improve performance and prevent cycles. This
// could possibly become part of the profile/builtin code above which
// short circuits attacment policy checks in cases where we know them
// to be unnecessary.
continue;
}
foreach ($phids as $phid) {
$object_phids[$phid] = true;
}
}
}
// If this file is a transform of another file, load that file too. If you
// can see the original file, you can see the thumbnail.
// TODO: It might be nice to put this directly on PhabricatorFile and remove
// the PhabricatorTransformedFile table, which would be a little simpler.
// TODO: It might be nice to put this directly on PhabricatorFile and
// remove the PhabricatorTransformedFile table, which would be a little
// simpler.
$xforms = id(new PhabricatorTransformedFile())->loadAllWhere(
'transformedPHID IN (%Ls)',
$file_phids);
$xform_phids = mpull($xforms, 'getOriginalPHID', 'getTransformedPHID');
foreach ($xform_phids as $derived_phid => $original_phid) {
$object_phids[$original_phid] = true;
if ($need_xforms) {
$xforms = id(new PhabricatorTransformedFile())->loadAllWhere(
'transformedPHID IN (%Ls)',
mpull($need_xforms, 'getPHID'));
$xform_phids = mpull($xforms, 'getOriginalPHID', 'getTransformedPHID');
foreach ($xform_phids as $derived_phid => $original_phid) {
$object_phids[$original_phid] = true;
}
} else {
$xform_phids = array();
}
$object_phids = array_keys($object_phids);

View file

@ -1152,7 +1152,6 @@ final class PhabricatorFile extends PhabricatorFileDAO
$params = array(
'name' => $builtin->getBuiltinDisplayName(),
'ttl.relative' => phutil_units('7 days in seconds'),
'canCDN' => true,
'builtin' => $key,
);
@ -1648,7 +1647,7 @@ final class PhabricatorFile extends PhabricatorFileDAO
public function getFieldValuesForConduit() {
return array(
'name' => $this->getName(),
'dataURI' => $this->getCDNURI(),
'dataURI' => $this->getCDNURI('data'),
'size' => (int)$this->getByteSize(),
);
}

View file

@ -71,7 +71,8 @@ final class PhabricatorHarbormasterApplication extends PhabricatorApplication {
),
'build/' => array(
$this->getQueryRoutePattern() => 'HarbormasterBuildListController',
'(?P<id>\d+)/' => 'HarbormasterBuildViewController',
'(?P<id>\d+)/(?:(?P<generation>\d+)/)?'
=> 'HarbormasterBuildViewController',
'(?P<action>pause|resume|restart|abort)/'.
'(?P<id>\d+)/(?:(?P<via>[^/]+)/)?'
=> 'HarbormasterBuildActionController',

View file

@ -8,7 +8,6 @@ final class HarbormasterBuildViewController
$viewer = $request->getUser();
$id = $request->getURIData('id');
$generation = $request->getInt('g');
$build = id(new HarbormasterBuildQuery())
->setViewer($viewer)
@ -21,6 +20,7 @@ final class HarbormasterBuildViewController
require_celerity_resource('harbormaster-css');
$title = pht('Build %d', $id);
$warnings = array();
$page_header = id(new PHUIHeaderView())
->setHeader($title)
@ -28,7 +28,9 @@ final class HarbormasterBuildViewController
->setPolicyObject($build)
->setHeaderIcon('fa-cubes');
if ($build->isRestarting()) {
$is_restarting = $build->isRestarting();
if ($is_restarting) {
$page_header->setStatus(
'fa-exclamation-triangle', 'red', pht('Restarting'));
} else if ($build->isPausing()) {
@ -42,19 +44,53 @@ final class HarbormasterBuildViewController
'fa-exclamation-triangle', 'red', pht('Aborting'));
}
$max_generation = (int)$build->getBuildGeneration();
if ($max_generation === 0) {
$min_generation = 0;
} else {
$min_generation = 1;
}
if ($is_restarting) {
$max_generation = $max_generation + 1;
}
$generation = $request->getURIData('generation');
if ($generation === null) {
$generation = $max_generation;
} else {
$generation = (int)$generation;
}
if ($generation < $min_generation || $generation > $max_generation) {
return new Aphront404Response();
}
if ($generation < $max_generation) {
$warnings[] = pht(
'You are viewing an older run of this build. %s',
phutil_tag(
'a',
array(
'href' => $build->getURI(),
),
pht('View Current Build')));
}
$curtain = $this->buildCurtainView($build);
$properties = $this->buildPropertyList($build);
$history = $this->buildHistoryTable(
$build,
$generation,
$min_generation,
$max_generation);
$crumbs = $this->buildApplicationCrumbs();
$this->addBuildableCrumb($crumbs, $build->getBuildable());
$crumbs->addTextCrumb($title);
$crumbs->setBorder(true);
if ($generation === null || $generation > $build->getBuildGeneration() ||
$generation < 0) {
$generation = $build->getBuildGeneration();
}
$build_targets = id(new HarbormasterBuildTargetQuery())
->setViewer($viewer)
->needBuildSteps(true)
@ -264,14 +300,25 @@ final class HarbormasterBuildViewController
new HarbormasterBuildTransactionQuery());
$timeline->setShouldTerminate(true);
if ($warnings) {
$warnings = id(new PHUIInfoView())
->setErrors($warnings)
->setSeverity(PHUIInfoView::SEVERITY_WARNING);
} else {
$warnings = null;
}
$view = id(new PHUITwoColumnView())
->setHeader($page_header)
->setCurtain($curtain)
->setMainColumn(array(
$properties,
$targets,
$timeline,
));
->setMainColumn(
array(
$warnings,
$properties,
$history,
$targets,
$timeline,
));
return $this->newPage()
->setTitle($title)
@ -561,10 +608,6 @@ final class HarbormasterBuildViewController
pht('Build Plan'),
$handles[$build->getBuildPlanPHID()]->renderLink());
$properties->addProperty(
pht('Restarts'),
$build->getBuildGeneration());
$properties->addProperty(
pht('Status'),
$this->getStatus($build));
@ -576,6 +619,53 @@ final class HarbormasterBuildViewController
}
private function buildHistoryTable(
HarbormasterBuild $build,
$generation,
$min_generation,
$max_generation) {
if ($max_generation === $min_generation) {
return null;
}
$viewer = $this->getViewer();
$uri = $build->getURI();
$rows = array();
$rowc = array();
for ($ii = $max_generation; $ii >= $min_generation; $ii--) {
if ($generation == $ii) {
$rowc[] = 'highlighted';
} else {
$rowc[] = null;
}
$rows[] = array(
phutil_tag(
'a',
array(
'href' => $uri.$ii.'/',
),
pht('Run %d', $ii)),
);
}
$table = id(new AphrontTableView($rows))
->setColumnClasses(
array(
'pri wide',
))
->setRowClasses($rowc);
return id(new PHUIObjectBoxView())
->setHeaderText(pht('History'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setTable($table);
}
private function getStatus(HarbormasterBuild $build) {
$status_view = new PHUIStatusListView();

View file

@ -147,7 +147,7 @@ final class HarbormasterBuildEngine extends Phobject {
// If it is different, they will automatically stop what they're doing
// and abort.
// Previously we used to delete targets, logs and artifacts here. Instead
// Previously we used to delete targets, logs and artifacts here. Instead,
// leave them around so users can view previous generations of this build.
}

View file

@ -51,13 +51,22 @@ final class HarbormasterUIEventListener
return;
}
$buildable = id(new HarbormasterBuildableQuery())
->setViewer($viewer)
->withManualBuildables(false)
->withBuildablePHIDs(array($buildable_phid))
->needBuilds(true)
->needTargets(true)
->executeOne();
try {
$buildable = id(new HarbormasterBuildableQuery())
->setViewer($viewer)
->withManualBuildables(false)
->withBuildablePHIDs(array($buildable_phid))
->needBuilds(true)
->needTargets(true)
->executeOne();
} catch (PhabricatorPolicyException $ex) {
// TODO: See PHI430. When this query raises a policy exception, it
// fatals the whole page because it happens very late in execution,
// during final page rendering. If the viewer can't see the buildable or
// some of the builds, just hide this element for now.
return;
}
if (!$buildable) {
return;
}

View file

@ -236,7 +236,10 @@ final class PhabricatorMemeEngine extends Phobject {
Filesystem::writeFile($output_name, $memed_frame_data);
}
$future = new ExecFuture('convert -loop 0 %Ls %s', $output_files, $output);
$future = new ExecFuture(
'convert -dispose background -loop 0 %Ls %s',
$output_files,
$output);
$future->setTimeout(10)->resolvex();
return Filesystem::readFile($output);
@ -297,6 +300,9 @@ final class PhabricatorMemeEngine extends Phobject {
$font_max = 72;
$font_min = 5;
$margin_x = 16;
$margin_y = 16;
$last = null;
$cursor = floor(($font_max + $font_min) / 2);
$min = $font_min;
@ -321,12 +327,12 @@ final class PhabricatorMemeEngine extends Phobject {
// text extends, for example if it has a "y".
$descend = $box[3];
if ($height > $dim_y) {
if (($height + $margin_y) > $dim_y) {
$all_fit = false;
break;
}
if ($width > $dim_x) {
if (($width + $margin_x) > $dim_x) {
$all_fit = false;
break;
}

View file

@ -284,9 +284,6 @@ final class ManiphestTaskDetailController extends ManiphestController {
$edit_config = $edit_engine->loadDefaultEditConfiguration($task);
$can_create = (bool)$edit_config;
$can_reassign = $edit_engine->hasEditAccessToTransaction(
ManiphestTaskOwnerTransaction::TRANSACTIONTYPE);
if ($can_create) {
$form_key = $edit_config->getIdentifier();
$edit_uri = id(new PhutilURI("/task/edit/form/{$form_key}/"))

View file

@ -357,29 +357,69 @@ final class PhabricatorProjectQuery
}
protected function didFilterPage(array $projects) {
$viewer = $this->getViewer();
if ($this->needImages) {
$file_phids = mpull($projects, 'getProfileImagePHID');
$file_phids = array_filter($file_phids);
$need_images = $projects;
// First, try to load custom profile images for any projects with custom
// images.
$file_phids = array();
foreach ($need_images as $key => $project) {
$image_phid = $project->getProfileImagePHID();
if ($image_phid) {
$file_phids[$key] = $image_phid;
}
}
if ($file_phids) {
$files = id(new PhabricatorFileQuery())
->setParentQuery($this)
->setViewer($this->getViewer())
->setViewer($viewer)
->withPHIDs($file_phids)
->execute();
$files = mpull($files, null, 'getPHID');
} else {
$files = array();
foreach ($file_phids as $key => $image_phid) {
$file = idx($files, $image_phid);
if (!$file) {
continue;
}
$need_images[$key]->attachProfileImageFile($file);
unset($need_images[$key]);
}
}
foreach ($projects as $project) {
$file = idx($files, $project->getProfileImagePHID());
if (!$file) {
$builtin = PhabricatorProjectIconSet::getIconImage(
$project->getIcon());
$file = PhabricatorFile::loadBuiltin($this->getViewer(),
'projects/'.$builtin);
// For projects with default images, or projects where the custom image
// failed to load, load a builtin image.
if ($need_images) {
$builtin_map = array();
$builtins = array();
foreach ($need_images as $key => $project) {
$icon = $project->getIcon();
$builtin_name = PhabricatorProjectIconSet::getIconImage($icon);
$builtin_name = 'projects/'.$builtin_name;
$builtin = id(new PhabricatorFilesOnDiskBuiltinFile())
->setName($builtin_name);
$builtin_key = $builtin->getBuiltinFileKey();
$builtins[] = $builtin;
$builtin_map[$key] = $builtin_key;
}
$builtin_files = PhabricatorFile::loadBuiltins(
$viewer,
$builtins);
foreach ($need_images as $key => $project) {
$builtin_key = $builtin_map[$key];
$builtin_file = $builtin_files[$builtin_key];
$project->attachProfileImageFile($builtin_file);
}
$project->attachProfileImageFile($file);
}
}

View file

@ -23,6 +23,14 @@ final class PhabricatorProjectSearchEngine
id(new PhabricatorSearchTextField())
->setLabel(pht('Name'))
->setKey('name'),
id(new PhabricatorSearchStringListField())
->setLabel(pht('Slugs'))
->setIsHidden(true)
->setKey('slugs')
->setDescription(
pht(
'Search for projects with particular slugs. (Slugs are the same '.
'as project hashtags.)')),
id(new PhabricatorUsersSearchField())
->setLabel(pht('Members'))
->setKey('memberPHIDs')
@ -81,6 +89,10 @@ final class PhabricatorProjectSearchEngine
$query->withNameTokens($tokens);
}
if ($map['slugs']) {
$query->withSlugs($map['slugs']);
}
if ($map['memberPHIDs']) {
$query->withMemberPHIDs($map['memberPHIDs']);
}

View file

@ -45,12 +45,19 @@ final class PhabricatorSubscriptionsSubscribersPolicyRule
$this->subscribed[$viewer_phid] = array();
}
// Load the project PHIDs the user is a member of.
// Load the project PHIDs the user is a member of. We use the omnipotent
// user here because projects may themselves have "Subscribers" visibility
// policies and we don't want to get stuck in an infinite stack of
// recursive policy checks. See T13106.
if (!isset($this->sourcePHIDs[$viewer_phid])) {
$source_phids = PhabricatorEdgeQuery::loadDestinationPHIDs(
$viewer_phid,
PhabricatorProjectMemberOfProjectEdgeType::EDGECONST);
$projects = id(new PhabricatorProjectQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withMemberPHIDs(array($viewer_phid))
->execute();
$source_phids = mpull($projects, 'getPHID');
$source_phids[] = $viewer_phid;
$this->sourcePHIDs[$viewer_phid] = $source_phids;
}

View file

@ -26,7 +26,6 @@ final class PhabricatorSystemApplication extends PhabricatorApplication {
'/readonly/' => array(
'(?P<reason>[^/]+)/' => 'PhabricatorSystemReadOnlyController',
),
'/favicon.ico' => 'PhabricatorSystemFaviconController',
);
}

View file

@ -1,19 +0,0 @@
<?php
final class PhabricatorSystemFaviconController extends PhabricatorController {
public function shouldRequireLogin() {
return false;
}
public function processRequest() {
$webroot = dirname(phutil_get_library_root('phabricator')).'/webroot/';
$content = Filesystem::readFile($webroot.'/rsrc/favicons/favicon.ico');
return id(new AphrontFileResponse())
->setContent($content)
->setMimeType('image/x-icon')
->setCacheDurationInSeconds(phutil_units('24 hours in seconds'))
->setCanCDN(true);
}
}

View file

@ -1398,59 +1398,6 @@ abstract class PhabricatorEditEngine
}
/**
* Test if the viewer could apply a certain type of change by using the
* normal "Edit" form.
*
* This method returns `true` if the user has access to an edit form and
* that edit form has a field which applied the specified transaction type,
* and that field is visible and editable for the user.
*
* For example, you can use it to test if a user is able to reassign tasks
* or not, prior to rendering dedicated UI for task reassignment.
*
* Note that this method does NOT test if the user can actually edit the
* current object, just if they have access to the related field.
*
* @param const Transaction type to test for.
* @return bool True if the user could "Edit" to apply the transaction type.
*/
final public function hasEditAccessToTransaction($xaction_type) {
$viewer = $this->getViewer();
$object = $this->getTargetObject();
if (!$object) {
$object = $this->newEditableObject();
}
$config = $this->loadDefaultEditConfiguration($object);
if (!$config) {
return false;
}
$fields = $this->buildEditFields($object);
$field = null;
foreach ($fields as $form_field) {
$field_xaction_type = $form_field->getTransactionType();
if ($field_xaction_type === $xaction_type) {
$field = $form_field;
break;
}
}
if (!$field) {
return false;
}
if (!$field->shouldReadValueFromSubmit()) {
return false;
}
return true;
}
public function newNUXButton($text) {
$specs = $this->newCreateActionSpecifications(array());
$head = head($specs);
@ -1968,6 +1915,7 @@ abstract class PhabricatorEditEngine
->setContinueOnNoEffect($request->isContinueRequest())
->setContinueOnMissingFields(true)
->setContentSourceFromRequest($request)
->setRaiseWarnings(!$request->getBool('editEngine.warnings'))
->setIsPreview($is_preview);
try {
@ -1980,6 +1928,10 @@ abstract class PhabricatorEditEngine
return id(new PhabricatorApplicationTransactionNoEffectResponse())
->setCancelURI($view_uri)
->setException($ex);
} catch (PhabricatorApplicationTransactionWarningException $ex) {
return id(new PhabricatorApplicationTransactionWarningResponse())
->setCancelURI($view_uri)
->setException($ex);
}
if (!$is_preview) {

View file

@ -48,6 +48,7 @@ abstract class PhabricatorApplicationTransactionEditor
private $mentionedPHIDs;
private $continueOnNoEffect;
private $continueOnMissingFields;
private $raiseWarnings;
private $parentMessageID;
private $heraldAdapter;
private $heraldTranscript;
@ -273,6 +274,15 @@ abstract class PhabricatorApplicationTransactionEditor
return $this->applicationEmail;
}
public function setRaiseWarnings($raise_warnings) {
$this->raiseWarnings = $raise_warnings;
return $this;
}
public function getRaiseWarnings() {
return $this->raiseWarnings;
}
public function getTransactionTypesForObject($object) {
$old = $this->object;
try {
@ -919,6 +929,19 @@ abstract class PhabricatorApplicationTransactionEditor
throw new PhabricatorApplicationTransactionValidationException($errors);
}
if ($this->raiseWarnings) {
$warnings = array();
foreach ($xactions as $xaction) {
if ($this->hasWarnings($object, $xaction)) {
$warnings[] = $xaction;
}
}
if ($warnings) {
throw new PhabricatorApplicationTransactionWarningException(
$warnings);
}
}
$this->willApplyTransactions($object, $xactions);
if ($object->getID()) {
@ -4277,4 +4300,28 @@ abstract class PhabricatorApplicationTransactionEditor
}
}
private function hasWarnings($object, $xaction) {
// TODO: For the moment, this is a very un-modular hack to support
// exactly one type of warning (mentioning users on a draft revision)
// that we want to show. See PHI433.
if (!($object instanceof DifferentialRevision)) {
return false;
}
if (!$object->isDraft()) {
return false;
}
$type = $xaction->getTransactionType();
if ($type != PhabricatorTransactions::TYPE_SUBSCRIBERS) {
return false;
}
// NOTE: This will currently warn even if you're only removing
// subscribers.
return true;
}
}

View file

@ -0,0 +1,13 @@
<?php
final class PhabricatorApplicationTransactionWarningException
extends Exception {
private $xactions;
public function __construct(array $xactions) {
$this->xactions = $xactions;
parent::__construct();
}
}

View file

@ -0,0 +1,58 @@
<?php
final class PhabricatorApplicationTransactionWarningResponse
extends AphrontProxyResponse {
private $viewer;
private $exception;
private $cancelURI;
public function setCancelURI($cancel_uri) {
$this->cancelURI = $cancel_uri;
return $this;
}
public function setException(
PhabricatorApplicationTransactionWarningException $exception) {
$this->exception = $exception;
return $this;
}
protected function buildProxy() {
return new AphrontDialogResponse();
}
public function reduceProxyResponse() {
$request = $this->getRequest();
$title = pht('Warning: Unexpected Effects');
$head = pht(
'This is a draft revision that will not publish any notifications '.
'until the author requests review.');
$tail = pht(
'Mentioned or subscribed users will not be notified.');
$continue = pht('Tell No One');
$dialog = id(new AphrontDialogView())
->setViewer($request->getViewer())
->setTitle($title);
$dialog->appendParagraph($head);
$dialog->appendParagraph($tail);
$passthrough = $request->getPassthroughRequestParameters();
foreach ($passthrough as $key => $value) {
$dialog->addHiddenInput($key, $value);
}
$dialog
->addHiddenInput('editEngine.warnings', 1)
->addSubmitButton($continue)
->addCancelButton($this->cancelURI);
return $this->getProxy()->setDialog($dialog);
}
}

View file

@ -123,7 +123,7 @@ Otherwise, here's a general description of what you need to install:
- MySQL Server (usually "mysqld" or "mysql-server")
- PHP (usually "php")
- Required PHP extensions: mbstring, iconv, mysql (or mysqli), curl, pcntl
(these might be something like "php-mysql" or "php5-mysql")
(these might be something like "php-mysql" or "php5-mysqlnd")
- Optional PHP extensions: gd, apc (special instructions for APC are available
below if you have difficulty installing it), xhprof (instructions below,
you only need this if you are developing Phabricator)

View file

@ -9,14 +9,23 @@ Overview
This document describes available support resources.
The upstream provides active, free support for a narrow range of problems
(primarily, security issues and reproducible bugs).
The upstream provides free support for a narrow range of problems (primarily,
security issues and reproducible bugs) and paid support for virtually anything.
The upstream does not provide free support for general problems with installing
or configuring Phabricator. You may be able to get some help with these
kinds of issues from the community.
Paid Support
============
If you'd like upstream support, see ((pacts)).
This is the only way to request features and the only way to get guaranteed
answers from experts quickly.
Reporting Security Vulnerabilities
==================================
@ -31,8 +40,7 @@ Reporting Bugs
The upstream will accept **reproducible** bug reports in modern, first-party
production code running in reasonable environments. Before submitting a bug
report you **must update** to the latest version of Phabricator. This reduces
support costs on the upstream, please be mindful.
report you **must update** to the latest version of Phabricator.
To report bugs, see @{article:Contributing Bug Reports}.
@ -52,14 +60,10 @@ If you'd like to contribute to Phabricator, start with
Installation and Setup Help
===========================
Installation and setup help is available from the upstream at consulting rates.
See [[ https://secure.phabricator.com/w/consulting/ | Consulting ]] for details.
Helping individual installs navigate unique setup problems takes our time
away from developing Phabricator, so we can not offer this service for free.
You may be able to get free help with these issues from the
[[ https://phurl.io/u/discourse | community ]]. See below for details.
[[ https://phurl.io/u/discourse | community ]].
You can also pay us for support. See ((pacts)).
Hosting
@ -67,8 +71,8 @@ Hosting
The upstream offers Phabricator as a hosted service at
[[ https://phacility.com | Phacility ]]. This simplifies setting up and
operating a Phabricator instance, and gives you access to a broader range
of upstream support services.
operating a Phabricator instance, and automatically gives you access to a
broader range of upstream support services.
Running this service gives us a strong financial incentive to make installing
and operating Phabricator as difficult as possible. Blinded by greed, we toil
@ -79,9 +83,10 @@ ourselves can hope to navigate.
Phabricator Community
=====================
We provide hosting for a
[[ https://phurl.io/u/discourse | Discussion Forum ]]
where admins and users help and answer questions from other community members.
Upstream developers may occasionally participate, but this is mostly
a user to user community. If you run into general problems, but are not
interested in paid support, this is the main place to find help.
We provide hosting for a [[ https://phurl.io/u/discourse | Discussion Forum ]]
where admininstrators and users help and answer questions from other community
members.
Upstream developers occasionally participate, but this is mostly a user to user
community. If you run into general problems, but are not interested in paid
support, this is the main place to find help.

View file

@ -43,6 +43,27 @@ The most useful header for routing is generally `X-Phabricator-Stamps`. This
is a list of attributes which describe the object the mail is about and the
actions which the mail informs you about.
Stamps and Gmail
================
If you use a client which can not perform header matching (like Gmail), you can
change the {nav Settings > Email Format > Send Stamps} setting to include the
stamps in the mail body and then match them with body rules.
When writing filter rules against mail stamps in Gmail, you should quote any
filters you want to apply. For example, specify rules like this, with quotes:
> "author(@alice)"
Note that Gmail will ignore some symbols when matching mail against filtering
rules, so you can get false positives if the body of the message includes text
like `author alice` (the same words in the same order, without the special
symbols).
You'll also get false positives if the message body includes the text of a
mail stamp explicitly in a normal text field like a summary, description, or
comment.
There's no way to avoid these false positives other than using a different
client with support for more powerful filtering rules, but these false
positives should normally be uncommon.

View file

@ -279,6 +279,15 @@ abstract class PhabricatorObjectRemarkupRule extends PhutilRemarkupRule {
return $matches[0];
}
// If we're rendering a table of contents, just render the raw input.
// This could perhaps be handled more gracefully but it seems unusual to
// put something like "{P123}" in a header and it's not obvious what users
// expect? See T8845.
$engine = $this->getEngine();
if ($engine->getState('toc')) {
return $matches[0];
}
return $this->markupObject(array(
'type' => 'embed',
'id' => $matches[1],
@ -292,6 +301,12 @@ abstract class PhabricatorObjectRemarkupRule extends PhutilRemarkupRule {
return $matches[0];
}
// If we're rendering a table of contents, just render the monogram.
$engine = $this->getEngine();
if ($engine->getState('toc')) {
return $matches[0];
}
return $this->markupObject(array(
'type' => 'ref',
'id' => $matches[1],

View file

@ -71,6 +71,14 @@ class PhabricatorBarePageView extends AphrontPageView {
));
}
$referrer_tag = phutil_tag(
'meta',
array(
'name' => 'referrer',
'content' => 'no-referrer',
));
$mask_icon = phutil_tag(
'link',
array(
@ -80,47 +88,7 @@ class PhabricatorBarePageView extends AphrontPageView {
'/rsrc/favicons/mask-icon.svg'),
));
$icon_tag_76 = phutil_tag(
'link',
array(
'rel' => 'apple-touch-icon',
'href' => celerity_get_resource_uri(
'/rsrc/favicons/apple-touch-icon-76x76.png'),
));
$icon_tag_120 = phutil_tag(
'link',
array(
'rel' => 'apple-touch-icon',
'sizes' => '120x120',
'href' => celerity_get_resource_uri(
'/rsrc/favicons/apple-touch-icon-120x120.png'),
));
$icon_tag_152 = phutil_tag(
'link',
array(
'rel' => 'apple-touch-icon',
'sizes' => '152x152',
'href' => celerity_get_resource_uri(
'/rsrc/favicons/apple-touch-icon-152x152.png'),
));
$favicon_tag = phutil_tag(
'link',
array(
'id' => 'favicon',
'rel' => 'shortcut icon',
'href' => celerity_get_resource_uri(
'/rsrc/favicons/favicon.ico'),
));
$referrer_tag = phutil_tag(
'meta',
array(
'name' => 'referrer',
'content' => 'no-referrer',
));
$favicon_links = $this->newFavicons();
$response = CelerityAPI::getStaticResourceResponse();
@ -136,13 +104,10 @@ class PhabricatorBarePageView extends AphrontPageView {
}
return hsprintf(
'%s%s%s%s%s%s%s%s',
'%s%s%s%s%s',
$viewport_tag,
$mask_icon,
$icon_tag_76,
$icon_tag_120,
$icon_tag_152,
$favicon_tag,
$favicon_links,
$referrer_tag,
$response->renderResourcesOfType('css'));
}
@ -156,4 +121,61 @@ class PhabricatorBarePageView extends AphrontPageView {
return $response->renderResourcesOfType('js');
}
private function newFavicons() {
$favicon_refs = array(
array(
'rel' => 'apple-touch-icon',
'sizes' => '76x76',
'width' => 76,
'height' => 76,
),
array(
'rel' => 'apple-touch-icon',
'sizes' => '120x120',
'width' => 120,
'height' => 120,
),
array(
'rel' => 'apple-touch-icon',
'sizes' => '152x152',
'width' => 152,
'height' => 152,
),
array(
'rel' => 'icon',
'id' => 'favicon',
'width' => 64,
'height' => 64,
),
);
$fetch_refs = array();
foreach ($favicon_refs as $key => $spec) {
$ref = id(new PhabricatorFaviconRef())
->setWidth($spec['width'])
->setHeight($spec['height']);
$favicon_refs[$key]['ref'] = $ref;
$fetch_refs[] = $ref;
}
id(new PhabricatorFaviconRefQuery())
->withRefs($fetch_refs)
->execute();
$favicon_links = array();
foreach ($favicon_refs as $spec) {
$favicon_links[] = phutil_tag(
'link',
array(
'rel' => $spec['rel'],
'sizes' => idx($spec, 'sizes'),
'id' => idx($spec, 'id'),
'href' => $spec['ref']->getURI(),
));
}
return $favicon_links;
}
}

View file

@ -330,9 +330,6 @@ final class PhabricatorStandardPageView extends PhabricatorBarePageView
Javelin::initBehavior(
'dark-console',
$this->getConsoleConfig());
// Change this to initBehavior when there is some behavior to initialize
require_celerity_resource('javelin-behavior-error-log');
}
if ($user) {

View file

@ -23,15 +23,29 @@ final class PhabricatorMainMenuView extends AphrontView {
return $this->controller;
}
private function getFaviconURI($type = null) {
switch ($type) {
case 'message':
return celerity_get_resource_uri('/rsrc/favicons/favicon-message.ico');
case 'mention':
return celerity_get_resource_uri('/rsrc/favicons/favicon-mention.ico');
default:
return celerity_get_resource_uri('/rsrc/favicons/favicon.ico');
}
private static function getFavicons() {
$refs = array();
$refs['favicon'] = id(new PhabricatorFaviconRef())
->setWidth(64)
->setHeight(64);
$refs['message_favicon'] = id(new PhabricatorFaviconRef())
->setWidth(64)
->setHeight(64)
->setEmblems(
array(
'dot-pink',
null,
null,
null,
));
id(new PhabricatorFaviconRefQuery())
->withRefs($refs)
->execute();
return mpull($refs, 'getURI');
}
public function render() {
@ -428,10 +442,7 @@ final class PhabricatorMainMenuView extends AphrontView {
'countType' => $conpherence_data['countType'],
'countNumber' => $message_count_number,
'unreadClass' => 'message-unread',
'favicon' => $this->getFaviconURI('default'),
'message_favicon' => $this->getFaviconURI('message'),
'mention_favicon' => $this->getFaviconURI('mention'),
));
) + self::getFavicons());
$message_notification_dropdown = javelin_tag(
'div',
@ -509,10 +520,7 @@ final class PhabricatorMainMenuView extends AphrontView {
'countType' => $notification_data['countType'],
'countNumber' => $count_number,
'unreadClass' => 'alert-unread',
'favicon' => $this->getFaviconURI('default'),
'message_favicon' => $this->getFaviconURI('message'),
'mention_favicon' => $this->getFaviconURI('mention'),
));
) + self::getFavicons());
$notification_dropdown = javelin_tag(
'div',
@ -594,10 +602,7 @@ final class PhabricatorMainMenuView extends AphrontView {
'countType' => null,
'countNumber' => null,
'unreadClass' => 'setup-unread',
'favicon' => $this->getFaviconURI('default'),
'message_favicon' => $this->getFaviconURI('message'),
'mention_favicon' => $this->getFaviconURI('mention'),
));
) + self::getFavicons());
$setup_notification_dropdown = javelin_tag(
'div',

View file

@ -117,8 +117,8 @@ final class PHUITagView extends AphrontTagView {
return $this;
}
public function setSlimShady($mm) {
$this->slimShady = $mm;
public function setSlimShady($is_eminem) {
$this->slimShady = $is_eminem;
return $this;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

View file

@ -104,6 +104,7 @@
font-size: 11px;
background: #333333;
color: #ffffff;
border-bottom: 0px;
}
.dark-console .aphront-table-view td {
@ -210,10 +211,10 @@
}
.dark-console-panel-request-log-separator {
background-color: #e8e8e8;
border-bottom: 1px solid #b7b7b7;
border-top: 1px solid #b7b7b7;
height: 2px;
background-color: #e8e8e8;
border-bottom: 1px solid #b7b7b7;
border-top: 1px solid #b7b7b7;
height: 2px;
}
.dark-console-panel-ErrorLog {

View file

@ -84,7 +84,7 @@
}
.setup-issue-body {
padding: 16px 16px 0 16px;
padding: 16px;
}
.setup-issue-status {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 175 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

View file

@ -78,7 +78,7 @@ JX.behavior('aphlict-listen', function(config) {
JX.Stratcom.invoke('notification-panel-update', null, {});
var response = e.getData();
if (!response.showAnyNotification) {
if (!response.showAnyNotification && !response.showDesktopNotification) {
return;
}
@ -86,6 +86,7 @@ JX.behavior('aphlict-listen', function(config) {
new JX.Notification()
.setContent(JX.$H(response.content))
.setKey(response.primaryObjectPHID)
.setShowAsWebNotification(response.showAnyNotification)
.setShowAsDesktopNotification(response.showDesktopNotification)
.setTitle(response.title)
.setBody(response.body)

View file

@ -40,9 +40,19 @@ JX.behavior('doorkeeper-tag', function(config, statics) {
};
for (var ii = 0; ii < tags.length; ii++) {
var tag_key = tags[ii].ref.join('@');
var key_parts = [];
key_parts = key_parts.concat(tags[ii].ref);
key_parts.push(tags[ii].view);
var tag_key = key_parts.join(' ');
if (tag_key in statics.cache) {
have.push({id: tags[ii].id, markup: statics.cache[tag_key]});
have.push(
{
id: tags[ii].id,
markup: statics.cache[tag_key]
});
} else {
need.push(tags[ii]);
keys[tags[ii].id] = tag_key;
@ -54,7 +64,11 @@ JX.behavior('doorkeeper-tag', function(config, statics) {
}
if (need.length) {
new JX.Workflow('/doorkeeper/tags/', {tags: JX.JSON.stringify(need)})
var data = {
tags: JX.JSON.stringify(need)
};
new JX.Workflow('/doorkeeper/tags/', data)
.setHandler(function(r) { draw(r.tags); })
.start();
}

View file

@ -27,6 +27,7 @@ JX.install('Notification', {
_hideTimer : null,
_duration : 12000,
_asDesktop : false,
_asWeb : true,
_key : null,
_title : null,
_body : null,
@ -88,6 +89,11 @@ JX.install('Notification', {
return this;
},
setShowAsWebNotification: function(mode) {
this._asWeb = mode;
return this;
},
setShowAsDesktopNotification : function(mode) {
this._asDesktop = mode;
return this;
@ -242,6 +248,13 @@ JX.install('Notification', {
var notifications = [];
for (var ii = 0; ii < self._active.length; ii++) {
// Don't render this notification if it's not configured to show as
// a web notification.
if (!self._active[ii]._asWeb) {
continue;
}
notifications.push(self._active[ii]._getContainer());
if (!(--limit)) {
break;

View file

@ -1,19 +0,0 @@
/**
* @provides javelin-behavior-error-log
* @requires javelin-dom
*/
/* exported show_details */
var current_details = null;
function show_details(row) {
var node = JX.$('row-details-' + row);
if (current_details !== null) {
JX.$('row-details-' + current_details).style.display = 'none';
}
node.style.display = 'block';
current_details = row;
}

View file

@ -404,4 +404,24 @@ JX.behavior('dark-console', function(config, statics) {
}
if (!statics.expand) {
statics.expand = true;
var current_details = null;
JX.Stratcom.listen('click', 'darkconsole-expand', function(e) {
e.kill();
if (current_details) {
current_details.style.display = 'none';
current_details = null;
}
var id = e.getNodeData('darkconsole-expand').expandID;
var node = JX.$(id);
node.style.display = 'block';
current_details = node;
});
}
});