1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-30 18:52:42 +01:00

(stable) Promote 2016 Week 33

This commit is contained in:
epriestley 2016-08-14 04:37:11 -07:00
commit f3d8e3832c
54 changed files with 606 additions and 826 deletions

View file

@ -7,7 +7,7 @@
*/
return array(
'names' => array(
'core.pkg.css' => '90c46327',
'core.pkg.css' => '340c5b75',
'core.pkg.js' => 'b562c3db',
'darkconsole.pkg.js' => 'e7393ebb',
'differential.pkg.css' => '3fb7f532',
@ -24,7 +24,7 @@ return array(
'rsrc/css/aphront/multi-column.css' => 'fd18389d',
'rsrc/css/aphront/notification.css' => '3f6c89c9',
'rsrc/css/aphront/panel-view.css' => '8427b78d',
'rsrc/css/aphront/phabricator-nav-view.css' => '09f3d0db',
'rsrc/css/aphront/phabricator-nav-view.css' => 'b29426e9',
'rsrc/css/aphront/table-view.css' => '832656fd',
'rsrc/css/aphront/tokenizer.css' => '056da01b',
'rsrc/css/aphront/tooltip.css' => '1a07aea8',
@ -32,7 +32,7 @@ return array(
'rsrc/css/aphront/typeahead.css' => 'd4f16145',
'rsrc/css/application/almanac/almanac.css' => 'dbb9b3af',
'rsrc/css/application/auth/auth.css' => '0877ed6e',
'rsrc/css/application/base/main-menu-view.css' => 'b623169f',
'rsrc/css/application/base/main-menu-view.css' => '3b0d39f7',
'rsrc/css/application/base/notification-menu.css' => 'f31c0bde',
'rsrc/css/application/base/phabricator-application-launch-view.css' => '95351601',
'rsrc/css/application/base/phui-theme.css' => '027ba77e',
@ -41,7 +41,7 @@ return array(
'rsrc/css/application/conduit/conduit-api.css' => '7bc725c4',
'rsrc/css/application/config/config-options.css' => '0ede4c9b',
'rsrc/css/application/config/config-template.css' => '8e6c6fcd',
'rsrc/css/application/config/config-welcome.css' => '6abd79be',
'rsrc/css/application/config/config-welcome.css' => '035aa483',
'rsrc/css/application/config/setup-issue.css' => 'db7e9c40',
'rsrc/css/application/config/unhandled-exception.css' => '4c96257a',
'rsrc/css/application/conpherence/durable-column.css' => '86396117',
@ -131,7 +131,7 @@ return array(
'rsrc/css/phui/phui-curtain-view.css' => '7148ae25',
'rsrc/css/phui/phui-document-pro.css' => 'dc3d46ed',
'rsrc/css/phui/phui-document-summary.css' => '9ca48bdf',
'rsrc/css/phui/phui-document.css' => '715aedfb',
'rsrc/css/phui/phui-document.css' => 'c32e8dec',
'rsrc/css/phui/phui-feed-story.css' => 'aa49845d',
'rsrc/css/phui/phui-fontkit.css' => '9cda225e',
'rsrc/css/phui/phui-form-view.css' => 'fab0a10f',
@ -163,7 +163,6 @@ return array(
'rsrc/css/phui/workboards/phui-workcard.css' => '0c62d7c5',
'rsrc/css/phui/workboards/phui-workpanel.css' => '92197373',
'rsrc/css/sprite-login.css' => '60e8560e',
'rsrc/css/sprite-menu.css' => '9dd65b92',
'rsrc/css/sprite-tokens.css' => '9cdfd599',
'rsrc/css/syntax/syntax-default.css' => '9923583c',
'rsrc/externals/d3/d3.min.js' => 'a11a5ff2',
@ -320,6 +319,7 @@ return array(
'rsrc/image/icon/tango/upload.png' => '7bbb7984',
'rsrc/image/icon/unsubscribe.png' => '25725013',
'rsrc/image/lightblue-header.png' => '5c168b6d',
'rsrc/image/logo/light-eye.png' => '1a576ddd',
'rsrc/image/main_texture.png' => '29a2c5ad',
'rsrc/image/menu_texture.png' => '5a17580d',
'rsrc/image/people/harding.png' => '45aa614e',
@ -343,8 +343,6 @@ return array(
'rsrc/image/resize.png' => 'fd476de4',
'rsrc/image/sprite-login-X2.png' => 'e3991e37',
'rsrc/image/sprite-login.png' => '03d5af29',
'rsrc/image/sprite-menu-X2.png' => 'cfd8fca5',
'rsrc/image/sprite-menu.png' => 'd7a99faa',
'rsrc/image/sprite-tokens-X2.png' => '804a5232',
'rsrc/image/sprite-tokens.png' => 'b41d03da',
'rsrc/image/texture/card-gradient.png' => '815f26e8',
@ -548,7 +546,7 @@ return array(
'changeset-view-manager' => 'a2828756',
'conduit-api-css' => '7bc725c4',
'config-options-css' => '0ede4c9b',
'config-welcome-css' => '6abd79be',
'config-welcome-css' => '035aa483',
'conpherence-durable-column-view' => '86396117',
'conpherence-menu-css' => 'f99fee4c',
'conpherence-message-pane-css' => '5897d3ac',
@ -785,8 +783,8 @@ return array(
'phabricator-flag-css' => '5337623f',
'phabricator-keyboard-shortcut' => '1ae869f2',
'phabricator-keyboard-shortcut-manager' => '4a021c10',
'phabricator-main-menu-view' => 'b623169f',
'phabricator-nav-view-css' => '09f3d0db',
'phabricator-main-menu-view' => '3b0d39f7',
'phabricator-nav-view-css' => 'b29426e9',
'phabricator-notification' => 'ccf1cbf8',
'phabricator-notification-css' => '3f6c89c9',
'phabricator-notification-menu-css' => 'f31c0bde',
@ -837,7 +835,7 @@ return array(
'phui-crumbs-view-css' => '9dac418c',
'phui-curtain-view-css' => '7148ae25',
'phui-document-summary-view-css' => '9ca48bdf',
'phui-document-view-css' => '715aedfb',
'phui-document-view-css' => 'c32e8dec',
'phui-document-view-pro-css' => 'dc3d46ed',
'phui-feed-story-css' => 'aa49845d',
'phui-font-icon-base-css' => '6449bce8',
@ -891,7 +889,6 @@ return array(
'releeph-request-typeahead-css' => '667a48ae',
'setup-issue-css' => 'db7e9c40',
'sprite-login-css' => '60e8560e',
'sprite-menu-css' => '9dd65b92',
'sprite-tokens-css' => '9cdfd599',
'syntax-default-css' => '9923583c',
'syntax-highlighting-css' => '769d3498',
@ -1165,6 +1162,9 @@ return array(
'javelin-dom',
'javelin-magical-init',
),
'3b0d39f7' => array(
'phui-theme-css',
),
'3cb0b2fc' => array(
'javelin-behavior',
'javelin-dom',
@ -1844,9 +1844,6 @@ return array(
'javelin-dom',
'javelin-util',
),
'b623169f' => array(
'phui-theme-css',
),
'b6993408' => array(
'javelin-behavior',
'javelin-stratcom',
@ -2246,7 +2243,6 @@ return array(
'aphront-tooltip-css',
'phabricator-flag-css',
'phui-info-view-css',
'sprite-menu-css',
'phabricator-main-menu-view',
'phabricator-notification-css',
'phabricator-notification-menu-css',

View file

@ -104,7 +104,6 @@ return array(
'aphront-tooltip-css',
'phabricator-flag-css',
'phui-info-view-css',
'sprite-menu-css',
'phabricator-main-menu-view',
'phabricator-notification-css',

View file

@ -1,31 +0,0 @@
{
"version": 1,
"sprites": {
"dark-eye": {
"name": "dark-eye",
"rule": ".dark-eye",
"hash": "c8112e52666fa1cb509ebb2cdf3a3df5"
},
"dark-logo": {
"name": "dark-logo",
"rule": ".dark-logo",
"hash": "e3425da87e8f6737d8db0063d064cd7d"
},
"light-eye": {
"name": "light-eye",
"rule": ".light-eye",
"hash": "5b6bf7c8c10d4f7414d976f6e79ae2ff"
},
"light-logo": {
"name": "light-logo",
"rule": ".light-logo",
"hash": "bee37c0a86825ec7ded38936b1ba7b65"
}
},
"scales": [
1,
2
],
"header": "\/**\n * @provides sprite-menu-css\n * @generated\n *\/\n\n.sprite-menu {\n background-image: url(\/rsrc\/image\/sprite-menu.png);\n background-repeat: no-repeat;\n}\n\n@media\nonly screen and (min-device-pixel-ratio: 1.5),\nonly screen and (-webkit-min-device-pixel-ratio: 1.5),\nonly screen and (min-resolution: 1.5dppx) {\n .sprite-menu {\n background-image: url(\/rsrc\/image\/sprite-menu-X2.png);\n background-size: {X}px {Y}px;\n }\n}\n",
"type": "standard"
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_repository.repository_commit
CHANGE summary summary VARCHAR(255) NOT NULL COLLATE {$COLLATE_TEXT};

View file

@ -27,7 +27,6 @@ $webroot = Filesystem::readablePath($webroot);
$generator = new CeleritySpriteGenerator();
$sheets = array(
'menu' => $generator->buildMenuSheet(),
'tokens' => $generator->buildTokenSheet(),
'login' => $generator->buildLoginSheet(),
);

View file

@ -1622,9 +1622,7 @@ phutil_register_library_map(array(
'PHUIDiffTableOfContentsItemView' => 'infrastructure/diff/view/PHUIDiffTableOfContentsItemView.php',
'PHUIDiffTableOfContentsListView' => 'infrastructure/diff/view/PHUIDiffTableOfContentsListView.php',
'PHUIDiffTwoUpInlineCommentRowScaffold' => 'infrastructure/diff/view/PHUIDiffTwoUpInlineCommentRowScaffold.php',
'PHUIDocumentExample' => 'applications/uiexample/examples/PHUIDocumentExample.php',
'PHUIDocumentSummaryView' => 'view/phui/PHUIDocumentSummaryView.php',
'PHUIDocumentView' => 'view/phui/PHUIDocumentView.php',
'PHUIDocumentViewPro' => 'view/phui/PHUIDocumentViewPro.php',
'PHUIFeedStoryExample' => 'applications/uiexample/examples/PHUIFeedStoryExample.php',
'PHUIFeedStoryView' => 'view/phui/PHUIFeedStoryView.php',
@ -2268,7 +2266,7 @@ phutil_register_library_map(array(
'PhabricatorCustomFieldStorage' => 'infrastructure/customfield/storage/PhabricatorCustomFieldStorage.php',
'PhabricatorCustomFieldStorageQuery' => 'infrastructure/customfield/query/PhabricatorCustomFieldStorageQuery.php',
'PhabricatorCustomFieldStringIndexStorage' => 'infrastructure/customfield/storage/PhabricatorCustomFieldStringIndexStorage.php',
'PhabricatorCustomHeaderConfigType' => 'applications/config/custom/PhabricatorCustomHeaderConfigType.php',
'PhabricatorCustomLogoConfigType' => 'applications/config/custom/PhabricatorCustomLogoConfigType.php',
'PhabricatorDaemon' => 'infrastructure/daemon/PhabricatorDaemon.php',
'PhabricatorDaemonBulkJobController' => 'applications/daemon/controller/PhabricatorDaemonBulkJobController.php',
'PhabricatorDaemonBulkJobListController' => 'applications/daemon/controller/PhabricatorDaemonBulkJobListController.php',
@ -3383,6 +3381,7 @@ phutil_register_library_map(array(
'PhabricatorRepositoryCommitPHIDType' => 'applications/repository/phid/PhabricatorRepositoryCommitPHIDType.php',
'PhabricatorRepositoryCommitParserWorker' => 'applications/repository/worker/PhabricatorRepositoryCommitParserWorker.php',
'PhabricatorRepositoryCommitRef' => 'applications/repository/engine/PhabricatorRepositoryCommitRef.php',
'PhabricatorRepositoryCommitTestCase' => 'applications/repository/storage/__tests__/PhabricatorRepositoryCommitTestCase.php',
'PhabricatorRepositoryConfigOptions' => 'applications/repository/config/PhabricatorRepositoryConfigOptions.php',
'PhabricatorRepositoryDAO' => 'applications/repository/storage/PhabricatorRepositoryDAO.php',
'PhabricatorRepositoryDiscoveryEngine' => 'applications/repository/engine/PhabricatorRepositoryDiscoveryEngine.php',
@ -6270,9 +6269,7 @@ phutil_register_library_map(array(
'PHUIDiffTableOfContentsItemView' => 'AphrontView',
'PHUIDiffTableOfContentsListView' => 'AphrontView',
'PHUIDiffTwoUpInlineCommentRowScaffold' => 'PHUIDiffInlineCommentRowScaffold',
'PHUIDocumentExample' => 'PhabricatorUIExample',
'PHUIDocumentSummaryView' => 'AphrontTagView',
'PHUIDocumentView' => 'AphrontTagView',
'PHUIDocumentViewPro' => 'AphrontTagView',
'PHUIFeedStoryExample' => 'PhabricatorUIExample',
'PHUIFeedStoryView' => 'AphrontView',
@ -7014,7 +7011,7 @@ phutil_register_library_map(array(
'PhabricatorCustomFieldStorage' => 'PhabricatorLiskDAO',
'PhabricatorCustomFieldStorageQuery' => 'Phobject',
'PhabricatorCustomFieldStringIndexStorage' => 'PhabricatorCustomFieldIndexStorage',
'PhabricatorCustomHeaderConfigType' => 'PhabricatorConfigOptionType',
'PhabricatorCustomLogoConfigType' => 'PhabricatorConfigOptionType',
'PhabricatorDaemon' => 'PhutilDaemon',
'PhabricatorDaemonBulkJobController' => 'PhabricatorDaemonController',
'PhabricatorDaemonBulkJobListController' => 'PhabricatorDaemonBulkJobController',
@ -8342,6 +8339,7 @@ phutil_register_library_map(array(
'PhabricatorRepositoryCommitPHIDType' => 'PhabricatorPHIDType',
'PhabricatorRepositoryCommitParserWorker' => 'PhabricatorWorker',
'PhabricatorRepositoryCommitRef' => 'Phobject',
'PhabricatorRepositoryCommitTestCase' => 'PhabricatorTestCase',
'PhabricatorRepositoryConfigOptions' => 'PhabricatorApplicationConfigOptions',
'PhabricatorRepositoryDAO' => 'PhabricatorLiskDAO',
'PhabricatorRepositoryDiscoveryEngine' => 'PhabricatorRepositoryEngine',

View file

@ -28,6 +28,10 @@ final class PhabricatorCalendarApplication extends PhabricatorApplication {
return "\xE2\x8C\xA8";
}
public function getApplicationGroup() {
return self::GROUP_UTILITIES;
}
public function isPrototype() {
return true;
}

View file

@ -2,58 +2,6 @@
final class CeleritySpriteGenerator extends Phobject {
public function buildMenuSheet() {
$sprites = array();
$colors = array(
'dark',
'light',
);
$sources = array();
foreach ($colors as $color) {
$sources[$color.'-logo'] = array(
'x' => 96,
'y' => 40,
'css' => '.'.$color.'-logo',
);
$sources[$color.'-eye'] = array(
'x' => 40,
'y' => 40,
'css' => '.'.$color.'-eye',
);
}
$scales = array(
'1x' => 1,
'2x' => 2,
);
$template = new PhutilSprite();
foreach ($sources as $name => $spec) {
$sprite = id(clone $template)
->setName($name)
->setSourceSize($spec['x'], $spec['y'])
->setTargetCSS($spec['css']);
foreach ($scales as $scale_name => $scale) {
$path = 'menu_'.$scale_name.'/'.$name.'.png';
$path = $this->getPath($path);
$sprite->setSourceFile($path, $scale);
}
$sprites[] = $sprite;
}
$sheet = $this->buildSheet('menu', true);
$sheet->setScales($scales);
foreach ($sprites as $sprite) {
$sheet->addSprite($sprite);
}
return $sheet;
}
public function buildTokenSheet() {
$icons = $this->getDirectoryList('tokens_1x');
$scales = array(

View file

@ -328,6 +328,10 @@ final class PhabricatorExtraConfigSetupCheck extends PhabricatorSetupCheck {
'metamta.re-prefix' => $global_settings_reason,
'metamta.vary-subjects' => $global_settings_reason,
'ui.custom-header' => pht(
'This option has been replaced with `ui.logo`, which provides more '.
'flexible configuration options.'),
);
return $ancient_config;

View file

@ -43,7 +43,6 @@ final class PhabricatorConfigClusterRepositoriesController
$all_repositories = id(new PhabricatorRepositoryQuery())
->setViewer($viewer)
->withHosted(PhabricatorRepositoryQuery::HOSTED_PHABRICATOR)
->withTypes(
array(
PhabricatorRepositoryType::REPOSITORY_TYPE_GIT,

View file

@ -98,7 +98,8 @@ final class PhabricatorConfigEditController
}
}
$form = new AphrontFormView();
$form = id(new AphrontFormView())
->setEncType('multipart/form-data');
$error_view = null;
if ($errors) {
@ -144,9 +145,9 @@ final class PhabricatorConfigEditController
}
if ($option->getHidden() || $option->getLocked()) {
$control = null;
$controls = array();
} else {
$control = $this->renderControl(
$controls = $this->renderControls(
$option,
$display_value,
$e_value);
@ -201,9 +202,9 @@ final class PhabricatorConfigEditController
}
}
$form
->appendChild($control);
foreach ($controls as $control) {
$form->appendControl($control);
}
if (!$option->getLocked()) {
$form->appendChild(
@ -279,23 +280,23 @@ final class PhabricatorConfigEditController
$e_value = null;
$errors = array();
$value = $request->getStr('value');
if (!strlen($value)) {
$value = null;
$xaction->setNewValue(
array(
'deleted' => true,
'value' => null,
));
return array($e_value, $errors, $value, $xaction);
}
if ($option->isCustomType()) {
$info = $option->getCustomObject()->readRequest($option, $request);
list($e_value, $errors, $set_value, $value) = $info;
} else {
$value = $request->getStr('value');
if (!strlen($value)) {
$value = null;
$xaction->setNewValue(
array(
'deleted' => true,
'value' => null,
));
return array($e_value, $errors, $value, $xaction);
}
$type = $option->getType();
$set_value = null;
@ -415,13 +416,13 @@ final class PhabricatorConfigEditController
}
}
private function renderControl(
private function renderControls(
PhabricatorConfigOption $option,
$display_value,
$e_value) {
if ($option->isCustomType()) {
$control = $option->getCustomObject()->renderControl(
$controls = $option->getCustomObject()->renderControls(
$option,
$display_value,
$e_value);
@ -487,9 +488,11 @@ final class PhabricatorConfigEditController
->setError($e_value)
->setValue($display_value)
->setName('value');
$controls = array($control);
}
return $control;
return $controls;
}
private function renderExamples(PhabricatorConfigOption $option) {

View file

@ -358,7 +358,7 @@ final class PhabricatorConfigWelcomeController
$quick_header = new PHUIRemarkupView(
$viewer, pht('=Quick Start Guide'));
return id(new PHUIDocumentView())
$document = id(new PHUIDocumentViewPro())
->setHeader($header)
->setFluid(true)
->appendChild($setup_header)
@ -367,6 +367,11 @@ final class PhabricatorConfigWelcomeController
->appendChild($explore)
->appendChild($quick_header)
->appendChild($quick);
return id(new PHUIBoxView())
->setBorder(true)
->appendChild($document)
->addClass('mlb');
}
private function newItem(AphrontRequest $request, $icon, $content) {

View file

@ -31,6 +31,16 @@ abstract class PhabricatorConfigOptionType extends Phobject {
}
public function renderControls(
PhabricatorConfigOption $option,
$display_value,
$e_value) {
$control = $this->renderControl($option, $display_value, $e_value);
return array($control);
}
public function renderControl(
PhabricatorConfigOption $option,
$display_value,

View file

@ -1,48 +0,0 @@
<?php
final class PhabricatorCustomHeaderConfigType
extends PhabricatorConfigOptionType {
public function validateOption(PhabricatorConfigOption $option, $value) {
if (phid_get_type($value) != PhabricatorFileFilePHIDType::TYPECONST) {
throw new Exception(
pht(
'%s is not a valid file PHID.',
$value));
}
$file = id(new PhabricatorFileQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withPHIDs(array($value))
->executeOne();
if (!$file) {
throw new Exception(
pht(
'%s is not a valid file PHID.',
$value));
}
$most_open_policy = PhabricatorPolicies::getMostOpenPolicy();
if ($file->getViewPolicy() != $most_open_policy) {
throw new Exception(
pht(
'Specified file %s has policy "%s" but should have policy "%s".',
$value,
$file->getViewPolicy(),
$most_open_policy));
}
if (!$file->isViewableImage()) {
throw new Exception(
pht(
'Specified file %s is not a viewable image.',
$value));
}
}
public static function getExampleConfig() {
$config = 'PHID-FILE-abcd1234abcd1234abcd';
return $config;
}
}

View file

@ -0,0 +1,118 @@
<?php
final class PhabricatorCustomLogoConfigType
extends PhabricatorConfigOptionType {
public static function getLogoImagePHID() {
$logo = PhabricatorEnv::getEnvConfig('ui.logo');
return idx($logo, 'logoImagePHID');
}
public static function getLogoWordmark() {
$logo = PhabricatorEnv::getEnvConfig('ui.logo');
return idx($logo, 'wordmarkText');
}
public function validateOption(PhabricatorConfigOption $option, $value) {
if (!is_array($value)) {
throw new Exception(
pht(
'Logo configuration is not valid: value must be a dictionary.'));
}
PhutilTypeSpec::checkMap(
$value,
array(
'logoImagePHID' => 'optional string|null',
'wordmarkText' => 'optional string|null',
));
}
public function readRequest(
PhabricatorConfigOption $option,
AphrontRequest $request) {
$viewer = $request->getViewer();
$view_policy = PhabricatorPolicies::POLICY_PUBLIC;
if ($request->getBool('removeLogo')) {
$logo_image_phid = null;
} else if ($request->getFileExists('logoImage')) {
$logo_image = PhabricatorFile::newFromPHPUpload(
idx($_FILES, 'logoImage'),
array(
'name' => 'logo',
'authorPHID' => $viewer->getPHID(),
'viewPolicy' => $view_policy,
'canCDN' => true,
'isExplicitUpload' => true,
));
$logo_image_phid = $logo_image->getPHID();
} else {
$logo_image_phid = self::getLogoImagePHID();
}
$wordmark_text = $request->getStr('wordmarkText');
$value = array(
'logoImagePHID' => $logo_image_phid,
'wordmarkText' => $wordmark_text,
);
$errors = array();
$e_value = null;
try {
$this->validateOption($option, $value);
} catch (Exception $ex) {
$e_value = pht('Invalid');
$errors[] = $ex->getMessage();
$value = array();
}
return array($e_value, $errors, $value, phutil_json_encode($value));
}
public function renderControls(
PhabricatorConfigOption $option,
$display_value,
$e_value) {
try {
$value = phutil_json_decode($display_value);
} catch (Exception $ex) {
$value = array();
}
$logo_image_phid = idx($value, 'logoImagePHID');
$wordmark_text = idx($value, 'wordmarkText');
$controls = array();
// TODO: This should be a PHUIFormFileControl, but that currently only
// works in "workflow" forms. It isn't trivial to convert this form into
// a workflow form, nor is it trivial to make the newer control work
// in non-workflow forms.
$controls[] = id(new AphrontFormFileControl())
->setName('logoImage')
->setLabel(pht('Logo Image'));
if ($logo_image_phid) {
$controls[] = id(new AphrontFormCheckboxControl())
->addCheckbox(
'removeLogo',
1,
pht('Remove Custom Logo'));
}
$controls[] = id(new AphrontFormTextControl())
->setName('wordmarkText')
->setLabel(pht('Wordmark'))
->setPlaceholder(pht('Phabricator'))
->setValue($wordmark_text);
return $controls;
}
}

View file

@ -20,9 +20,6 @@ final class PhabricatorUIConfigOptions
}
public function getOptions() {
$custom_header_example =
PhabricatorCustomHeaderConfigType::getExampleConfig();
$experimental_link = 'https://secure.phabricator.com/T4214';
$options = array(
'blindigo' => 'blindigo',
'red' => 'red',
@ -48,11 +45,24 @@ final class PhabricatorUIConfigOptions
]
EOJSON;
$logo_type = 'custom:PhabricatorCustomLogoConfigType';
return array(
$this->newOption('ui.header-color', 'enum', 'blindigo')
->setDescription(
pht('Sets the default color scheme of Phabricator.'))
->setEnumOptions($options),
$this->newOption('ui.logo', $logo_type, array())
->setSummary(
pht('Customize the logo and wordmark text in the header.'))
->setDescription(
pht(
"Customize the logo image and text which appears in the main ".
"site header:\n\n".
" - **Logo Image**: Upload a new 80 x 80px image to replace the ".
"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.footer-items', 'list<wild>', array())
->setSummary(
pht(
@ -69,31 +79,6 @@ EOJSON;
" omit this if you just want a piece of text, like a copyright ".
" notice."))
->addExample($example, pht('Basic Example')),
$this->newOption(
'ui.custom-header',
'custom:PhabricatorCustomHeaderConfigType',
null)
->setSummary(
pht('Customize the Phabricator logo.'))
->setDescription(
pht('You can customize the Phabricator logo by specifying the '.
'phid for a viewable image you have uploaded to Phabricator '.
'via the [[ /file/ | Files application]]. This image should '.
'be:'."\n".
' - 192px X 80px; while not enforced, images with these '.
'dimensions will look best across devices.'."\n".
' - have view policy public if [[ '.
'/config/edit/policy.allow-public | `policy.allow-public`]] '.
'is true and otherwise view policy user; mismatches in these '.
'policy settings will result in a broken logo for some users.'.
"\n\n".
'You should restart Phabricator after updating this value '.
'to see this change take effect.'.
"\n\n".
'As this feature is experimental, please read [[ %s | T4214 ]] '.
'for up to date information.',
$experimental_link))
->addExample($custom_header_example, pht('Valid Config')),
);
}

View file

@ -70,7 +70,12 @@ abstract class PhabricatorConfigSchemaSpec extends Phobject {
}
$details = $this->getDetailsForDataType($type);
list($column_type, $charset, $collation, $nullable, $auto) = $details;
$column_type = $details['type'];
$charset = $details['charset'];
$collation = $details['collation'];
$nullable = $details['nullable'];
$auto = $details['auto'];
$column = $this->newColumn($name)
->setDataType($type)
@ -182,11 +187,17 @@ abstract class PhabricatorConfigSchemaSpec extends Phobject {
->setName($name);
}
public function getMaximumByteLengthForDataType($data_type) {
$info = $this->getDetailsForDataType($data_type);
return idx($info, 'bytes');
}
private function getDetailsForDataType($data_type) {
$column_type = null;
$charset = null;
$collation = null;
$auto = false;
$bytes = null;
// If the type ends with "?", make the column nullable.
$nullable = false;
@ -211,7 +222,6 @@ abstract class PhabricatorConfigSchemaSpec extends Phobject {
'text255' => true,
'text160' => true,
'text128' => true,
'text80' => true,
'text64' => true,
'text40' => true,
'text32' => true,
@ -237,6 +247,10 @@ abstract class PhabricatorConfigSchemaSpec extends Phobject {
$type = $matches[1];
$size = idx($matches, 2);
if ($size) {
$bytes = $size;
}
switch ($type) {
case 'text':
if ($is_binary) {
@ -363,7 +377,14 @@ abstract class PhabricatorConfigSchemaSpec extends Phobject {
}
}
return array($column_type, $charset, $collation, $nullable, $auto);
return array(
'type' => $column_type,
'charset' => $charset,
'collation' => $collation,
'nullable' => $nullable,
'auto' => $auto,
'bytes' => $bytes,
);
}
}

View file

@ -598,7 +598,7 @@ final class DiffusionRepositoryClusterEngine extends Phobject {
->setArgv($argv)
->setSudoAsDaemon(true)
->setCredentialPHID($repository->getCredentialPHID())
->setURI($repository->getRemoteURI())
->setURI($repository->getRemoteURIObject())
->newFuture();
$future->setCWD($local_path);

View file

@ -18,6 +18,10 @@ final class PhabricatorFeedApplication extends PhabricatorApplication {
return 'fa-newspaper-o';
}
public function getApplicationGroup() {
return self::GROUP_UTILITIES;
}
public function canUninstall() {
return false;
}

View file

@ -2,6 +2,10 @@
final class HeraldRuleViewController extends HeraldController {
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('id');

View file

@ -249,9 +249,13 @@ final class HeraldTranscriptController extends HeraldController {
foreach ($rule_xscripts as $rule_xscript) {
$rule_id = $rule_xscript->getRuleID();
$rule_monogram = pht('H%d', $rule_id);
$rule_uri = '/'.$rule_monogram;
$rule_item = id(new PHUIObjectItemView())
->setObjectName(pht('H%d', $rule_id))
->setHeader($rule_xscript->getRuleName());
->setObjectName($rule_monogram)
->setHeader($rule_xscript->getRuleName())
->setHref($rule_uri);
if (!$rule_xscript->getResult()) {
$rule_item->setDisabled(true);

View file

@ -288,39 +288,40 @@ final class HeraldRule extends HeraldDAO
}
public function getPolicy($capability) {
if ($capability == PhabricatorPolicyCapability::CAN_VIEW) {
return PhabricatorPolicies::getMostOpenPolicy();
}
if ($this->isGlobalRule()) {
switch ($capability) {
case PhabricatorPolicyCapability::CAN_VIEW:
return PhabricatorPolicies::POLICY_USER;
case PhabricatorPolicyCapability::CAN_EDIT:
$app = 'PhabricatorHeraldApplication';
$herald = PhabricatorApplication::getByClass($app);
$global = HeraldManageGlobalRulesCapability::CAPABILITY;
return $herald->getPolicy($global);
}
$app = 'PhabricatorHeraldApplication';
$herald = PhabricatorApplication::getByClass($app);
$global = HeraldManageGlobalRulesCapability::CAPABILITY;
return $herald->getPolicy($global);
} else if ($this->isObjectRule()) {
return $this->getTriggerObject()->getPolicy($capability);
} else {
return PhabricatorPolicies::POLICY_NOONE;
return $this->getAuthorPHID();
}
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
if ($this->isPersonalRule()) {
return ($viewer->getPHID() == $this->getAuthorPHID());
} else {
return false;
}
return false;
}
public function describeAutomaticCapability($capability) {
if ($this->isPersonalRule()) {
return pht("A personal rule's owner can always view and edit it.");
} else if ($this->isObjectRule()) {
return pht('Object rules inherit the policies of their objects.');
if ($capability == PhabricatorPolicyCapability::CAN_VIEW) {
return null;
}
return null;
if ($this->isGlobalRule()) {
return pht(
'Global Herald rules can be edited by users with the "Can Manage '.
'Global Rules" Herald application permission.');
} else if ($this->isObjectRule()) {
return pht('Object rules inherit the edit policies of their objects.');
} else {
return pht('A personal rule can only be edited by its owner.');
}
}

View file

@ -22,6 +22,10 @@ final class PhabricatorPackagesApplication extends PhabricatorApplication {
return 'fa-gift';
}
public function getApplicationGroup() {
return self::GROUP_UTILITIES;
}
public function isPrototype() {
return true;
}

View file

@ -30,6 +30,10 @@ final class PhabricatorPeopleApplication extends PhabricatorApplication {
return pht('Sort of a social utility.');
}
public function getApplicationGroup() {
return self::GROUP_UTILITIES;
}
public function canUninstall() {
return false;
}

View file

@ -26,6 +26,10 @@ final class PhabricatorPhurlApplication extends PhabricatorApplication {
return true;
}
public function getApplicationGroup() {
return self::GROUP_UTILITIES;
}
public function getRemarkupRules() {
return array(
new PhabricatorPhurlRemarkupRule(),

View file

@ -34,6 +34,10 @@ final class PhabricatorPonderApplication extends PhabricatorApplication {
);
}
public function getApplicationGroup() {
return self::GROUP_UTILITIES;
}
public function supportsEmailIntegration() {
return true;
}

View file

@ -18,6 +18,10 @@ final class PhabricatorReleephApplication extends PhabricatorApplication {
return 'fa-flag-checkered';
}
public function getApplicationGroup() {
return self::GROUP_UTILITIES;
}
public function isPrototype() {
return true;
}

View file

@ -108,7 +108,7 @@ final class PhabricatorRepositoryCommit
'mailKey' => 'bytes20',
'authorPHID' => 'phid?',
'auditStatus' => 'uint32',
'summary' => 'text80',
'summary' => 'text255',
'importStatus' => 'uint32',
),
self::CONFIG_KEY_SCHEMA => array(

View file

@ -2,12 +2,6 @@
final class PhabricatorRepositoryCommitData extends PhabricatorRepositoryDAO {
/**
* NOTE: We denormalize this into the commit table; make sure the sizes
* match up.
*/
const SUMMARY_MAX_LENGTH = 80;
protected $commitID;
protected $authorName = '';
protected $commitMessage = '';
@ -38,10 +32,14 @@ final class PhabricatorRepositoryCommitData extends PhabricatorRepositoryDAO {
}
public static function summarizeCommitMessage($message) {
$max_bytes = id(new PhabricatorRepositoryCommit())
->getColumnMaximumByteLength('summary');
$summary = phutil_split_lines($message, $retain_endings = false);
$summary = head($summary);
$summary = id(new PhutilUTF8StringTruncator())
->setMaximumBytes(self::SUMMARY_MAX_LENGTH)
->setMaximumBytes($max_bytes)
->setMaximumGlyphs(80)
->truncateString($summary);
return $summary;

View file

@ -0,0 +1,38 @@
<?php
final class PhabricatorRepositoryCommitTestCase
extends PhabricatorTestCase {
public function testSummarizeCommits() {
// Cyrillic "zhe".
$zhe = "\xD0\xB6";
// Symbol "Snowman".
$snowman = "\xE2\x98\x83";
// Emoji "boar".
$boar = "\xF0\x9F\x90\x97";
// Proper unicode truncation is tested elsewhere, this is just making
// sure column length handling is sane.
$map = array(
'' => 0,
'a' => 1,
str_repeat('a', 81) => 82,
str_repeat('a', 255) => 82,
str_repeat('aa ', 30) => 80,
str_repeat($zhe, 300) => 161,
str_repeat($snowman, 300) => 240,
str_repeat($boar, 300) => 255,
);
foreach ($map as $input => $expect) {
$actual = PhabricatorRepositoryCommitData::summarizeCommitMessage(
$input);
$this->assertEqual($expect, strlen($actual));
}
}
}

View file

@ -1,199 +0,0 @@
<?php
final class PHUIDocumentExample extends PhabricatorUIExample {
public function getName() {
return pht('Document View');
}
public function getDescription() {
return pht('Useful for areas of large content navigation');
}
public function renderExample() {
$request = $this->getRequest();
$user = $request->getUser();
$action = id(new PHUIListItemView())
->setName(pht('Actions'))
->setType(PHUIListItemView::TYPE_LABEL);
$action1 = id(new PHUIListItemView())
->setName(pht('Edit Document'))
->setHref('#')
->setIcon('fa-edit')
->setType(PHUIListItemView::TYPE_LINK);
$action2 = id(new PHUIListItemView())
->setName(pht('Move Document'))
->setHref('#')
->setIcon('fa-arrows')
->setType(PHUIListItemView::TYPE_LINK);
$action3 = id(new PHUIListItemView())
->setName(pht('Delete Document'))
->setHref('#')
->setIcon('fa-times')
->setType(PHUIListItemView::TYPE_LINK);
$action4 = id(new PHUIListItemView())
->setName(pht('View History'))
->setHref('#')
->setIcon('fa-list')
->setType(PHUIListItemView::TYPE_LINK);
$action5 = id(new PHUIListItemView())
->setName(pht('Subscribe'))
->setHref('#')
->setIcon('fa-plus-circle')
->setType(PHUIListItemView::TYPE_LINK);
$divider = id(new PHUIListItemView())
->setType(PHUIListItemView::TYPE_DIVIDER);
$header = id(new PHUIHeaderView())
->setHeader(pht('Installation'));
$label1 = id(new PHUIListItemView())
->setName(pht('Getting Started'))
->setType(PHUIListItemView::TYPE_LABEL);
$label2 = id(new PHUIListItemView())
->setName(pht('Documentation'))
->setType(PHUIListItemView::TYPE_LABEL);
$item1 = id(new PHUIListItemView())
->setName(pht('Installation'))
->setHref('#')
->setType(PHUIListItemView::TYPE_LINK);
$item2 = id(new PHUIListItemView())
->setName(pht('Webserver Config'))
->setHref('#')
->setType(PHUIListItemView::TYPE_LINK);
$item3 = id(new PHUIListItemView())
->setName(pht('Adding Users'))
->setHref('#')
->setType(PHUIListItemView::TYPE_LINK);
$item4 = id(new PHUIListItemView())
->setName(pht('Debugging'))
->setHref('#')
->setType(PHUIListItemView::TYPE_LINK);
$sidenav = id(new PHUIListView())
->setType(PHUIListView::SIDENAV_LIST)
->addMenuItem($action)
->addMenuItem($action1)
->addMenuItem($action2)
->addMenuItem($action3)
->addMenuItem($action4)
->addMenuItem($action5)
->addMenuItem($divider)
->addMenuItem($label1)
->addMenuItem($item1)
->addMenuItem($item2)
->addMenuItem($item3)
->addMenuItem($item4)
->addMenuItem($label2)
->addMenuItem($item2)
->addMenuItem($item3)
->addMenuItem($item4)
->addMenuItem($item1);
$home = id(new PHUIListItemView())
->setIcon('fa-home')
->setHref('#')
->setType(PHUIListItemView::TYPE_ICON);
$item1 = id(new PHUIListItemView())
->setName(pht('Installation'))
->setHref('#')
->setSelected(true)
->setType(PHUIListItemView::TYPE_LINK);
$item2 = id(new PHUIListItemView())
->setName(pht('Webserver Config'))
->setHref('#')
->setType(PHUIListItemView::TYPE_LINK);
$item3 = id(new PHUIListItemView())
->setName(pht('Adding Users'))
->setHref('#')
->setType(PHUIListItemView::TYPE_LINK);
$item4 = id(new PHUIListItemView())
->setName(pht('Debugging'))
->setHref('#')
->setType(PHUIListItemView::TYPE_LINK);
$topnav = id(new PHUIListView())
->setType(PHUIListView::NAVBAR_LIST)
->addMenuItem($home)
->addMenuItem($item1)
->addMenuItem($item2)
->addMenuItem($item3)
->addMenuItem($item4);
$document = hsprintf(
'<p class="pl">Lorem ipsum dolor sit amet, consectetur adipisicing, '.
'sed do eiusmod tempor incididunt ut labore et dolore magna '.
'aliqua. Ut enim ad minim veniam, quis nostrud exercitation '.
'ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis '.
'aute irure dolor in reprehenderit in voluptate velit esse cillum '.
'dolore eu fugiat nulla pariatur. Excepteur sint occaecat '.
'cupidatat non proident, sunt in culpa qui officia deserunt '.
'mollit anim id est laborum.</p>'.
'<p class="plr pll plb">Lorem ipsum dolor sit amet, consectetur, '.
'sed do eiusmod tempor incididunt ut labore et dolore magna '.
'aliqua. Ut enim ad minim veniam, quis nostrud exercitation '.
'ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis '.
'aute irure dolor in reprehenderit in voluptate velit esse cillum '.
'dolore eu fugiat nulla pariatur. Excepteur sint occaecat '.
'cupidatat non proident, sunt in culpa qui officia deserunt '.
'mollit anim id est laborum.</p>'.
'<p class="plr pll plb">Lorem ipsum dolor sit amet, consectetur, '.
'sed do eiusmod tempor incididunt ut labore et dolore magna '.
'aliqua. Ut enim ad minim veniam, quis nostrud exercitation '.
'ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis '.
'aute irure dolor in reprehenderit in voluptate velit esse cillum '.
'dolore eu fugiat nulla pariatur. Excepteur sint occaecat '.
'cupidatat non proident, sunt in culpa qui officia deserunt '.
'mollit anim id est laborum.</p>'.
'<p class="plr pll plb">Lorem ipsum dolor sit amet, consectetur, '.
'sed do eiusmod tempor incididunt ut labore et dolore magna '.
'aliqua. Ut enim ad minim veniam, quis nostrud exercitation '.
'ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis '.
'aute irure dolor in reprehenderit in voluptate velit esse cillum '.
'dolore eu fugiat nulla pariatur. Excepteur sint occaecat '.
'cupidatat non proident, sunt in culpa qui officia deserunt '.
'mollit anim id est laborum.</p>'.
'<p class="plr pll plb">Lorem ipsum dolor sit amet, consectetur, '.
'sed do eiusmod tempor incididunt ut labore et dolore magna '.
'aliqua. Ut enim ad minim veniam, quis nostrud exercitation '.
'ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis '.
'aute irure dolor in reprehenderit in voluptate velit esse cillum '.
'dolore eu fugiat nulla pariatur. Excepteur sint occaecat '.
'cupidatat non proident, sunt in culpa qui officia deserunt '.
'mollit anim id est laborum.</p>'.
'<p class="plr pll plb">Lorem ipsum dolor sit amet, consectetur, '.
'sed do eiusmod tempor incididunt ut labore et dolore magna '.
'aliqua. Ut enim ad minim veniam, quis nostrud exercitation '.
'ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis '.
'aute irure dolor in reprehenderit in voluptate velit esse cillum '.
'dolore eu fugiat nulla pariatur. Excepteur sint occaecat '.
'cupidatat non proident, sunt in culpa qui officia deserunt '.
'mollit anim id est laborum.</p>');
$content = new PHUIDocumentView();
$content->setBook(pht('Book or Project Name'), pht('Article'));
$content->setHeader($header);
$content->setFluid(true);
$content->setTopNav($topnav);
$content->setSidenav($sidenav);
$content->appendChild($document);
return $content;
}
}

View file

@ -3,96 +3,138 @@
Use Herald to get notified of changes you care about.
= Overview =
Overview
========
Herald allows you to write processing rules that take effect when objects (such
as Differential revisions and commits) are created or updated. For instance, you
might want to get notified every time someone sends out a revision that affects
some file you're interested in, even if they didn't add you as a reviewer.
Herald allows you to write rules which run automatically when objects (like
tasks or commits) are created or updated. For instance, you might want to get
notified every time someone sends out a revision that affects some file you're
interested in, even if they didn't add you as a reviewer.
Herald is less useful for small organizations (where everyone will generally
know most of what's going on) but the usefulness of the application increases
as an organization scales. Once there is too much activity to keep track of it
all, Herald allows you to filter it down so you're only notified of things you
are interested in.
= Global and Personal Rules =
You can create two kinds of Herald rules, //global// and //personal//:
- **Personal Rules** are rules you own, but they can only affect you. Only
you can edit or delete personal rules, but their actions are limited to
adding you to CC, subscribing you, etc.
- **Global Rules** are rules everyone owns, and they can affect anything.
Anyone can edit or delete a global rule, and they can take any action,
including affecting projects and mailing lists.
The general idea is to prevent individuals from controlling rules that affect
shared resources, so if a rule needs to be updated it's not a big deal if the
person who created it is on vacation.
= Rules, Conditions and Actions =
The best way to think of Herald is as a system similar to the mail rules you can
set up in most email clients, to organize mail based on "To", "Subject", etc.
One way to think about Herald is that it is a lot like the mail rules you can
set up in most email clients to organize mail based on "To", "Subject", etc.
Herald works very similarly, but operates on Phabricator objects (like revisions
and commits) instead of emails.
Every time an object is created or updated, Herald rules are run on it and
the actions for any matching rules are taken.
For example, you can write a personal rule like this which triggers on tasks:
To create a new Herald rule, choose which type of event you want to act on
(e.g., changes to Differential Revisions, or Commits), and then set a list of
conditions. For example, you might add the condition `Author is alincoln
(Abraham Lincoln)` to keep track of everything alincoln does. Finally, set
a list of actions to take when the conditions match, like adding yourself to the
CC list.
> When [ all of ] these conditions are met:
> [ Title ][ contains ][ quasar ]
> Take these actions [ every time ] this rule matches:
> [ Add me as a subscriber ]
Now you'll automatically be added to CC any time alincoln creates a revision,
and can keep an eye on what he's up to.
This rule will automatically subscribe you to any newly created or updated
tasks that contain "quasar" in the title.
= Available Actions =
Herald rules are often used to: notify users, add reviewers, initiate audits,
classify objects, block commits, enforce CLAs, and run builds.
Herald rules can take a number of actions. Note that some actions are only
available from Global rules, and others only from Personal rules. Additionally,
not every action is available for every object type (for instance, you can not
trigger an audit based on a Differential revision).
- **Add CC**: Add a user or mailing list to the CC list for the object. For
personal rules, you can only add yourself.
- **Remove CC**: Remove a user or mailing list from the CC list for the
object. For personal rules, you can only remove yourself.
- **Send an Email to**: Send one email, but don't subscribe to other updates.
For personal rules, you can only email yourself.
- **Trigger an Audit**: For commits, trigger an audit request for a project
or user. For personal rules, you can only trigger an audit request to
yourself.
- **Mark with flag**: Flag the object for later review. This action is only
available on personal rules. If an object already has a flag, this action
will not add another flag.
- **Do Nothing**: Don't do anything. This can be used to disable a rule
temporarily, or to create a rule for an "Another Herald rule" condition.
Working with Rules
==================
= Testing Rules =
To create new Herald rules, navigate to the {nav Herald} application and select
{nav Create Herald Rule}.
When you've created a rule, use the "Test Console" to test it out. Enter a
revision or commit and Herald will do a dry run against that object, showing
you which rules //would// match had it actually been updated. Dry runs executed
via the test console don't take any actions.
Next, you'll choose an event that you want to write a rule for: for example,
a rule for when commits are discovered or a rule for when tasks are created or
updated.
= Advanced Herald =
After selecting an event, choose the type of rule to create. See "Rule Types"
below for a more detailed discussion.
A few features in Herald are particularly complicated:
Name the rule and provide conditions and actions. When events occur, the rule
will be evaluated automatically. If the conditions pass, the actions will be
taken.
- **matches regexp pair**: for Differential revisions, you can set a condition
like "Any changed file content matches regexp pair...". This allows you to
specify two regexes in JSON format. The first will be used to match the
filename of the changed file; the second will be used to match the content.
For example, if you want to match revisions which add or remove calls to
a "muffinize" function, //but only in JS files//, you can set the value
to `["/\\.js$/", "/muffinize/"]` or similar.
- **Another Herald rule**: you can create Herald rules which depend on other
rules. This can be useful if you need to express a more complicated predicate
than "all" vs "any" allows, or have a common set of conditions which you want
to share between several rules. If a rule is only being used as a group of
conditions, you can set the action to "Do Nothing".
To test rules, use {nav Herald > Test Console}. See "Testing Rules" below
for greater detail.
To review which rules did or did not trigger for a particular event (and why),
see {nav Herald > Transcripts}.
Rule Types
==========
You can create three kinds of Herald rules: personal rules, object rules, and
global rules.
- **Personal Rules** are rules owned by an individual. They're often used
to keep people informed about changes they're interested in.
- **Object Rules** are rules associated with an object (like a repository
or project). These are similar to global rules.
- **Global Rules** are apply to all objects. They're often used to block
commits or run builds.
Rule Policies
=============
All Herald rules are always visible to all users.
The edit policy for a rule depends on what type of rule it is:
- Personal rules are owned by a particular user, and can only be created or
edited by that user.
- Object rules are associated with a particular object (like a repository),
and can only be created or edited by users who can edit that object. That
is, if you can edit a repository, you can also create object rules for it
and edit existing object rules.
- Global rules are administrative and can only be created or edited by users
with the **Can Manage Global Rules** Herald application permission.
When rules are about to evaluate, they may first perform some policy tests.
- Personal rules check if the owning user can see the object which the rule
is about to run on. If the user can not see the object, the rule does not
run. This prevents individuals from writing rules which give them access
to information they don't have permission to see.
- Object and global rules **bypass policies** and always execute. This makes
them very powerful, and is why the **Can Manage Global Rules** policy is
restricted by default.
Testing Rules
=============
When you've created a rule, use the {nav Herald > Test Console} to test it out.
Enter an object name (like `D123`, `rXYZabcdef`, or `T456`) and Herald will
execute a dry run against that object, showing you which rules //would// match
had it actually been updated. Dry runs executed via the test console don't take
any actions.
Advanced Herald
===============
A few features in Herald are particularly complicated or unintuitive.
Condition **matches regexp pair**: Some conditions allow you to select the
operator "matches regexp pair". For example, you can write a rule against
revisions like this one:
> When [ all of ] these conditions are met:
> [ Changed file content ][ matches regexp pair ][ ... ]
This condition allows you to specify two regexes in JSON format. The first will
be used to match the filename of the changed file; the second will be used to
match the content. You can use these together to express conditions like
"content in Javascript files".
For example, if you want to match revisions which add or remove calls to a
"muffinize" function, //but only in JS files//, you can set the value to
`["/\\.js$/", "/muffinize/"]` or similar. This condition is satisfied only
when the filename matches the first expression and the conent matches the
second expression.
**Another Herald rule**: you can create Herald rules which depend on other
rules.
This can be useful if you need to express a more complicated condition
than "all" vs "any" allows, or have a common set of conditions which you want
to share between several rules.
If a rule is only being used as a group of conditions, you can set the action
to "Do Nothing".

View file

@ -11,7 +11,7 @@ abstract class PhabricatorDaemon extends PhutilDaemon {
}
protected function willSleep($duration) {
LiskDAO::closeAllConnections();
LiskDAO::closeInactiveConnections(60);
return;
}
@ -34,6 +34,24 @@ abstract class PhabricatorDaemon extends PhutilDaemon {
return $command;
}
// We may reach this method while already running as the daemon user: for
// example, active and passive synchronization of clustered repositories
// run the same commands through the same code, but as different users.
// By default, `sudo` won't let you sudo to yourself, so we can get into
// trouble if we're already running as the daemon user unless the host has
// been configured to let the daemon user run commands as itself.
// Since this is silly and more complicated than doing this check, don't
// use `sudo` if we're already running as the correct user.
if (function_exists('posix_getuid')) {
$uid = posix_getuid();
$info = posix_getpwuid($uid);
if ($info && $info['name'] == $user) {
return $command;
}
}
// Get the absolute path so we're safe against the caller wiping out
// PATH.
$sudo = Filesystem::resolveBinary('sudo');

View file

@ -1621,10 +1621,55 @@ abstract class LiskDAO extends Phobject {
return (bool)self::$transactionIsolationLevel;
}
public static function closeAllConnections() {
self::$connections = array();
/**
* Close any connections with no recent activity.
*
* Long-running processes can use this method to clean up connections which
* have not been used recently.
*
* @param int Close connections with no activity for this many seconds.
* @return void
*/
public static function closeInactiveConnections($idle_window) {
$connections = self::$connections;
$now = PhabricatorTime::getNow();
foreach ($connections as $key => $connection) {
$last_active = $connection->getLastActiveEpoch();
$idle_duration = ($now - $last_active);
if ($idle_duration <= $idle_window) {
continue;
}
self::closeConnection($key);
}
}
public static function closeAllConnections() {
$connections = self::$connections;
foreach ($connections as $key => $connection) {
self::closeConnection($key);
}
}
private static function closeConnection($key) {
if (empty(self::$connections[$key])) {
throw new Exception(
pht(
'No database connection with connection key "%s" exists!',
$key));
}
$connection = self::$connections[$key];
unset(self::$connections[$key]);
$connection->close();
}
/* -( Utilities )---------------------------------------------------------- */
@ -1831,7 +1876,6 @@ abstract class LiskDAO extends Phobject {
return $this->getConfigOption(self::CONFIG_BINARY);
}
public function getSchemaColumns() {
$custom_map = $this->getConfigOption(self::CONFIG_COLUMN_SCHEMA);
if (!$custom_map) {
@ -1953,4 +1997,21 @@ abstract class LiskDAO extends Phobject {
return $custom_map + $default_map;
}
public function getColumnMaximumByteLength($column) {
$map = $this->getSchemaColumns();
if (!isset($map[$column])) {
throw new Exception(
pht(
'Object (of class "%s") does not have a column "%s".',
get_class($this),
$column));
}
$data_type = $map[$column];
return id(new PhabricatorStorageSchemaSpec())
->getMaximumByteLengthForDataType($data_type);
}
}

View file

@ -122,15 +122,16 @@ abstract class PhabricatorLiskDAO extends LiskDAO {
}
$replica = PhabricatorDatabaseRef::getReplicaDatabaseRef();
if (!$replica) {
throw new Exception(
pht('No valid databases are configured!'));
if ($replica) {
$connection = $replica->newApplicationConnection($database);
$connection->setReadOnly(true);
if ($replica->isReachable($connection)) {
return $connection;
}
}
$connection = $replica->newApplicationConnection($database);
$connection->setReadOnly(true);
if ($replica->isReachable($connection)) {
return $connection;
if (!$master && !$replica) {
$this->raiseUnconfigured($database);
}
$this->raiseUnreachable($database);
@ -153,10 +154,18 @@ abstract class PhabricatorLiskDAO extends LiskDAO {
$database));
}
private function raiseUnconfigured($database) {
throw new Exception(
pht(
'Unable to establish a connection to any database host '.
'(while trying "%s"). No masters or replicas are configured.',
$database));
}
private function raiseUnreachable($database) {
throw new PhabricatorClusterStrandedException(
pht(
'Unable to establish a connection to ANY database host '.
'Unable to establish a connection to any database host '.
'(while trying "%s"). All masters and replicas are completely '.
'unreachable.',
$database));

View file

@ -31,7 +31,7 @@ final class PhabricatorStorageManagementShellWorkflow
}
return phutil_passthru(
'mysql --protocol=TCP --default-character-set=utf8 '.
'mysql --protocol=TCP --default-character-set=utf8mb4 '.
'-u %s %C -h %s %C',
$api->getUser(),
$flag_password,

View file

@ -297,11 +297,13 @@ final class PhabricatorMainMenuView extends AphrontView {
}
private function renderPhabricatorLogo() {
$style_logo = null;
$custom_header = PhabricatorEnv::getEnvConfig('ui.custom-header');
$custom_header = PhabricatorCustomLogoConfigType::getLogoImagePHID();
$logo_style = array();
if ($custom_header) {
$cache = PhabricatorCaches::getImmutableCache();
$cache_key_logo = 'ui.custom-header.logo-phid.v1.'.$custom_header;
$cache_key_logo = 'ui.custom-header.logo-phid.v3.'.$custom_header;
$logo_uri = $cache->getKey($cache_key_logo);
if (!$logo_uri) {
$file = id(new PhabricatorFileQuery())
@ -313,21 +315,32 @@ final class PhabricatorMainMenuView extends AphrontView {
$cache->setKey($cache_key_logo, $logo_uri);
}
}
if ($logo_uri) {
$style_logo =
'background-size: 96px 40px; '.
'background-position: 0px 0px; '.
'background-image: url('.$logo_uri.');';
}
$logo_style[] = 'background-size: 40px 40px;';
$logo_style[] = 'background-position: 0 0;';
$logo_style[] = 'background-image: url('.$logo_uri.')';
}
$color = PhabricatorEnv::getEnvConfig('ui.header-color');
if ($color == 'light') {
$color = 'dark';
} else {
$color = 'light';
$logo_node = phutil_tag(
'span',
array(
'class' => 'phabricator-main-menu-eye',
'style' => implode(' ', $logo_style),
));
$wordmark_text = PhabricatorCustomLogoConfigType::getLogoWordmark();
if (!strlen($wordmark_text)) {
$wordmark_text = pht('Phabricator');
}
$wordmark_node = phutil_tag(
'span',
array(
'class' => 'phabricator-wordmark',
),
$wordmark_text);
return phutil_tag(
'a',
array(
@ -341,19 +354,8 @@ final class PhabricatorMainMenuView extends AphrontView {
'aural' => true,
),
pht('Home')),
phutil_tag(
'span',
array(
'class' => 'sprite-menu phabricator-main-menu-eye '.$color.'-eye',
),
''),
phutil_tag(
'span',
array(
'class' => 'sprite-menu phabricator-main-menu-logo '.$color.'-logo',
'style' => $style_logo,
),
''),
$logo_node,
$wordmark_node,
));
}

View file

@ -1,162 +0,0 @@
<?php
final class PHUIDocumentView extends AphrontTagView {
/* For mobile displays, where do you want the sidebar */
const NAV_BOTTOM = 'nav_bottom';
const NAV_TOP = 'nav_top';
private $offset;
private $header;
private $sidenav;
private $topnav;
private $crumbs;
private $bookname;
private $bookdescription;
private $mobileview;
private $fluid;
public function setOffset($offset) {
$this->offset = $offset;
return $this;
}
public function setHeader(PHUIHeaderView $header) {
$header->setTall(true);
$this->header = $header;
return $this;
}
public function setSideNav(PHUIListView $list, $display = self::NAV_BOTTOM) {
$list->setType(PHUIListView::SIDENAV_LIST);
$this->sidenav = $list;
$this->mobileview = $display;
return $this;
}
public function setTopNav(PHUIListView $list) {
$list->setType(PHUIListView::NAVBAR_LIST);
$this->topnav = $list;
return $this;
}
public function setCrumbs(PHUIListView $list) {
$this->crumbs = $list;
return $this;
}
public function setBook($name, $description) {
$this->bookname = $name;
$this->bookdescription = $description;
return $this;
}
public function setFluid($fluid) {
$this->fluid = $fluid;
return $this;
}
protected function getTagAttributes() {
$classes = array();
if ($this->offset) {
$classes[] = 'phui-document-offset';
}
if ($this->fluid) {
$classes[] = 'phui-document-fluid';
}
return array(
'class' => $classes,
);
}
protected function getTagContent() {
require_celerity_resource('phui-document-view-css');
$classes = array();
$classes[] = 'phui-document-view';
if ($this->offset) {
$classes[] = 'phui-offset-view';
}
if ($this->sidenav) {
$classes[] = 'phui-sidenav-view';
}
$sidenav = null;
if ($this->sidenav) {
$sidenav = phutil_tag(
'div',
array(
'class' => 'phui-document-sidenav',
),
$this->sidenav);
}
$book = null;
if ($this->bookname) {
$book = pht('%s (%s)', $this->bookname, $this->bookdescription);
}
$topnav = null;
if ($this->topnav) {
$topnav = phutil_tag(
'div',
array(
'class' => 'phui-document-topnav',
),
$this->topnav);
}
$crumbs = null;
if ($this->crumbs) {
$crumbs = phutil_tag(
'div',
array(
'class' => 'phui-document-crumbs',
),
$this->bookName);
}
$main_content = $this->renderChildren();
if ($book) {
$this->header->setSubheader($book);
}
$content_inner = phutil_tag(
'div',
array(
'class' => 'phui-document-inner',
),
array(
$this->header,
$topnav,
$main_content,
$crumbs,
));
if ($this->mobileview == self::NAV_BOTTOM) {
$order = array($content_inner, $sidenav);
} else {
$order = array($sidenav, $content_inner);
}
$content = phutil_tag(
'div',
array(
'class' => 'phui-document-content',
),
$order);
$view = phutil_tag(
'div',
array(
'class' => implode(' ', $classes),
),
$content);
return $view;
}
}

View file

@ -68,6 +68,12 @@
margin-left: 212px;
}
.has-drag-nav ul.phui-list-view {
height: 100%;
overflow-y: auto;
overflow-x: hidden;
}
.device-desktop .phui-navigation-shell .has-drag-nav .phabricator-nav-local {
width: 205px;
padding: 0;

View file

@ -41,27 +41,20 @@
}
.phabricator-main-menu-brand {
display: inline-block;
width: 148px;
height: 44px;
float: left;
margin-right: 4px;
margin-right: 6px;
padding-left: 6px;
}
.phabricator-main-menu-logo {
position: absolute;
width: 96px;
height: 40px;
left: 52px;
top: 2px;
}
.phabricator-main-menu-eye {
position: absolute;
margin: 2px 0;
width: 40px;
height: 40px;
top: 2px;
float: left;
display: block;
background-image: url(/rsrc/image/logo/light-eye.png);
background-size: 40px 40px;
}
.device-desktop .phabricator-main-menu-brand:hover {
@ -69,6 +62,21 @@
cursor: hand;
}
.device-phone .phabricator-wordmark {
max-width: 112px; /* iPhone 5 limitation */
}
.phabricator-wordmark {
float: left;
color: #fff;
font-size: 18px;
margin: 9px 4px 9px 6px;
padding-right: 8px;
max-width: 175px;
overflow: hidden;
white-space: nowrap;
}
/* - Expand/Collapse Button ----------------------------------------------------
On phones, the menu switches to a vertical layout and uses a button to expand

View file

@ -19,5 +19,10 @@
width: 32px;
float: left;
text-align: center;
margin-left: 16px;
}
.phui-document-view-pro .phui-document-content .config-welcome-box
.phabricator-remarkup {
margin: 0;
padding: 0;
}

View file

@ -8,15 +8,6 @@
position: relative;
}
.phui-document-view .phui-header-shell {
padding: 16px;
background-color: {$bluebackground};
}
.phui-document-content {
border-radius: 3px;
}
.device-desktop .phui-document-view {
border: 1px solid {$lightblueborder};
max-width: 960px;
@ -33,44 +24,11 @@
margin: 16px;
}
.phui-crumbs-view + .phui-document-fluid .phui-document-view {
margin-top: 0;
}
.device-desktop .phui-document-view.phui-offset-view {
max-width: 800px;
}
/* Fix so that Phriction Document preview is the same width as the document */
.device-desktop .phui-remarkup-preview .phui-document-view {
width: 800px;
}
.phui-document-sidenav {
position:absolute;
width: 200px;
text-overflow: ellipsis;
top: 0;
right: 0;
}
.device-phone .phui-document-sidenav {
position: static;
width: auto;
border-top: 1px solid {$thinblueborder};
border-bottom: 1px solid {$thinblueborder};
}
.device-phone .phui-sidenav-view .phui-document-inner {
margin: 0;
}
.phui-sidenav-view .phui-document-inner {
margin-right: 200px;
border-right: 1px solid {$thinblueborder};
background: #fff;
}
.phui-document-content .phui-header-shell {
border-top: none;
border-bottom: 1px solid {$lightblueborder};
@ -88,11 +46,6 @@
padding: 8px 0 4px;
}
.phui-document-content .phui-property-list-container {
border-bottom: 1px solid {$thinblueborder};
background-color: {$lightgreybackground};
}
.legalpad .phui-document-content .phui-property-list-view {
border: none;
box-shadow: none;
@ -127,18 +80,10 @@
margin: 16px 0;
}
.device-desktop .phui-document-offset {
padding-right: 120px;
}
.phui-document-view .phui-info-severity-nodata {
background-color: {$lightgreybackground};
}
body .phui-document-view .phui-header-shell.phui-bleed-header {
padding: 0;
}
.phui-document-view .phui-property-list-section-header {
padding: 20px 24px 0px;
border-top: none;

View file

@ -1,36 +0,0 @@
/**
* @provides sprite-menu-css
* @generated
*/
.sprite-menu {
background-image: url(/rsrc/image/sprite-menu.png);
background-repeat: no-repeat;
}
@media
only screen and (min-device-pixel-ratio: 1.5),
only screen and (-webkit-min-device-pixel-ratio: 1.5),
only screen and (min-resolution: 1.5dppx) {
.sprite-menu {
background-image: url(/rsrc/image/sprite-menu-X2.png);
background-size: 97px 123px;
}
}
.dark-logo {
background-position: 0px 0px;
}
.dark-eye {
background-position: 0px -82px;
}
.light-logo {
background-position: 0px -41px;
}
.light-eye {
background-position: -41px -82px;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB