mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-15 18:10:53 +01:00
(stable) Promote 2017 Week 17
This commit is contained in:
commit
53bc57ecef
31 changed files with 919 additions and 192 deletions
|
@ -9,8 +9,8 @@ return array(
|
|||
'names' => array(
|
||||
'conpherence.pkg.css' => 'ff161f2d',
|
||||
'conpherence.pkg.js' => 'b5b51108',
|
||||
'core.pkg.css' => '005d943f',
|
||||
'core.pkg.js' => '47a69358',
|
||||
'core.pkg.css' => '84ce260a',
|
||||
'core.pkg.js' => 'fffe0122',
|
||||
'darkconsole.pkg.js' => '1f9a31bc',
|
||||
'differential.pkg.css' => '90b30783',
|
||||
'differential.pkg.js' => 'ddfeb49b',
|
||||
|
@ -32,7 +32,7 @@ return array(
|
|||
'rsrc/css/aphront/notification.css' => '3f6c89c9',
|
||||
'rsrc/css/aphront/panel-view.css' => '8427b78d',
|
||||
'rsrc/css/aphront/phabricator-nav-view.css' => 'faf6a6fc',
|
||||
'rsrc/css/aphront/table-view.css' => '6ca8e057',
|
||||
'rsrc/css/aphront/table-view.css' => '34cf86b4',
|
||||
'rsrc/css/aphront/tokenizer.css' => '9a8cb501',
|
||||
'rsrc/css/aphront/tooltip.css' => '173b9431',
|
||||
'rsrc/css/aphront/typeahead-browse.css' => '8904346a',
|
||||
|
@ -42,7 +42,7 @@ return array(
|
|||
'rsrc/css/application/base/main-menu-view.css' => '5294060f',
|
||||
'rsrc/css/application/base/notification-menu.css' => '6a697e43',
|
||||
'rsrc/css/application/base/phui-theme.css' => '9f261c6b',
|
||||
'rsrc/css/application/base/standard-page-view.css' => '89da5a9c',
|
||||
'rsrc/css/application/base/standard-page-view.css' => 'eb5b80c5',
|
||||
'rsrc/css/application/chatlog/chatlog.css' => 'd295b020',
|
||||
'rsrc/css/application/conduit/conduit-api.css' => '7bc725c4',
|
||||
'rsrc/css/application/config/config-options.css' => '0ede4c9b',
|
||||
|
@ -116,7 +116,7 @@ return array(
|
|||
'rsrc/css/core/core.css' => '9f4cb463',
|
||||
'rsrc/css/core/remarkup.css' => '17c0fb37',
|
||||
'rsrc/css/core/syntax.css' => 'cae95e89',
|
||||
'rsrc/css/core/z-index.css' => '5e72c4e0',
|
||||
'rsrc/css/core/z-index.css' => '0233d039',
|
||||
'rsrc/css/diviner/diviner-shared.css' => '896f1d43',
|
||||
'rsrc/css/font/font-awesome.css' => 'e838e088',
|
||||
'rsrc/css/font/font-lato.css' => 'c7ccd872',
|
||||
|
@ -131,7 +131,7 @@ return array(
|
|||
'rsrc/css/phui/object-item/phui-oi-color.css' => 'cd2b9b77',
|
||||
'rsrc/css/phui/object-item/phui-oi-drag-ui.css' => 'f12cbc9f',
|
||||
'rsrc/css/phui/object-item/phui-oi-flush-ui.css' => '9d9685d6',
|
||||
'rsrc/css/phui/object-item/phui-oi-list-view.css' => '5c383524',
|
||||
'rsrc/css/phui/object-item/phui-oi-list-view.css' => '7c8ec27a',
|
||||
'rsrc/css/phui/object-item/phui-oi-simple-ui.css' => 'a8beebea',
|
||||
'rsrc/css/phui/phui-action-list.css' => 'c01858f4',
|
||||
'rsrc/css/phui/phui-action-panel.css' => '91c7b835',
|
||||
|
@ -146,7 +146,7 @@ return array(
|
|||
'rsrc/css/phui/phui-comment-panel.css' => 'f50152ad',
|
||||
'rsrc/css/phui/phui-crumbs-view.css' => '6ece3bbb',
|
||||
'rsrc/css/phui/phui-curtain-view.css' => '679743bb',
|
||||
'rsrc/css/phui/phui-document-pro.css' => 'f56738ed',
|
||||
'rsrc/css/phui/phui-document-pro.css' => '62c4dcbf',
|
||||
'rsrc/css/phui/phui-document-summary.css' => '9ca48bdf',
|
||||
'rsrc/css/phui/phui-document.css' => 'c32e8dec',
|
||||
'rsrc/css/phui/phui-feed-story.css' => '44a9c8e9',
|
||||
|
@ -155,7 +155,7 @@ return array(
|
|||
'rsrc/css/phui/phui-form.css' => 'a5570f70',
|
||||
'rsrc/css/phui/phui-head-thing.css' => 'fd311e5f',
|
||||
'rsrc/css/phui/phui-header-view.css' => 'e082678d',
|
||||
'rsrc/css/phui/phui-hovercard.css' => 'ae091fc5',
|
||||
'rsrc/css/phui/phui-hovercard.css' => 'f0592bcf',
|
||||
'rsrc/css/phui/phui-icon-set-selector.css' => '87db8fee',
|
||||
'rsrc/css/phui/phui-icon.css' => '12b387a1',
|
||||
'rsrc/css/phui/phui-image-mask.css' => 'a8498f9c',
|
||||
|
@ -508,7 +508,7 @@ return array(
|
|||
'rsrc/js/core/behavior-object-selector.js' => 'e0ec7f2f',
|
||||
'rsrc/js/core/behavior-oncopy.js' => '2926fff2',
|
||||
'rsrc/js/core/behavior-phabricator-nav.js' => '08675c6d',
|
||||
'rsrc/js/core/behavior-phabricator-remarkup-assist.js' => '0ca788bd',
|
||||
'rsrc/js/core/behavior-phabricator-remarkup-assist.js' => 'acd29eee',
|
||||
'rsrc/js/core/behavior-read-only-warning.js' => 'ba158207',
|
||||
'rsrc/js/core/behavior-refresh-csrf.js' => 'ab2f381b',
|
||||
'rsrc/js/core/behavior-remarkup-preview.js' => '4b700e9e',
|
||||
|
@ -536,7 +536,7 @@ return array(
|
|||
'rsrc/js/phui/behavior-phui-tab-group.js' => '0a0b10e9',
|
||||
'rsrc/js/phuix/PHUIXActionListView.js' => 'b5c256b8',
|
||||
'rsrc/js/phuix/PHUIXActionView.js' => 'b3465b9b',
|
||||
'rsrc/js/phuix/PHUIXAutocomplete.js' => 'd713a2c5',
|
||||
'rsrc/js/phuix/PHUIXAutocomplete.js' => 'f6699267',
|
||||
'rsrc/js/phuix/PHUIXDropdownMenu.js' => '8018ee50',
|
||||
'rsrc/js/phuix/PHUIXFormControl.js' => '83e03671',
|
||||
'rsrc/js/phuix/PHUIXIconView.js' => 'bff6884b',
|
||||
|
@ -549,7 +549,7 @@ return array(
|
|||
'aphront-list-filter-view-css' => '5d6f0526',
|
||||
'aphront-multi-column-view-css' => '84cc6640',
|
||||
'aphront-panel-view-css' => '8427b78d',
|
||||
'aphront-table-view-css' => '6ca8e057',
|
||||
'aphront-table-view-css' => '34cf86b4',
|
||||
'aphront-tokenizer-control-css' => '9a8cb501',
|
||||
'aphront-tooltip-css' => '173b9431',
|
||||
'aphront-typeahead-control-css' => '8a84cc7d',
|
||||
|
@ -673,7 +673,7 @@ return array(
|
|||
'javelin-behavior-phabricator-notification-example' => '8ce821c5',
|
||||
'javelin-behavior-phabricator-object-selector' => 'e0ec7f2f',
|
||||
'javelin-behavior-phabricator-oncopy' => '2926fff2',
|
||||
'javelin-behavior-phabricator-remarkup-assist' => '0ca788bd',
|
||||
'javelin-behavior-phabricator-remarkup-assist' => 'acd29eee',
|
||||
'javelin-behavior-phabricator-reveal-content' => '60821bc7',
|
||||
'javelin-behavior-phabricator-search-typeahead' => 'eded9ee8',
|
||||
'javelin-behavior-phabricator-show-older-transactions' => '94c65b72',
|
||||
|
@ -809,7 +809,7 @@ return array(
|
|||
'phabricator-shaped-request' => '7cbe244b',
|
||||
'phabricator-slowvote-css' => 'a94b7230',
|
||||
'phabricator-source-code-view-css' => '4383192f',
|
||||
'phabricator-standard-page-view' => '89da5a9c',
|
||||
'phabricator-standard-page-view' => 'eb5b80c5',
|
||||
'phabricator-textareautils' => '320810c8',
|
||||
'phabricator-title' => '485aaa6c',
|
||||
'phabricator-tooltip' => '8fadb715',
|
||||
|
@ -824,7 +824,7 @@ return array(
|
|||
'phabricator-uiexample-reactor-select' => 'a155550f',
|
||||
'phabricator-uiexample-reactor-sendclass' => '1def2711',
|
||||
'phabricator-uiexample-reactor-sendproperties' => 'b1f0ccee',
|
||||
'phabricator-zindex-css' => '5e72c4e0',
|
||||
'phabricator-zindex-css' => '0233d039',
|
||||
'phame-css' => 'b3a0b3a3',
|
||||
'pholio-css' => 'ca89d380',
|
||||
'pholio-edit-css' => '07676f51',
|
||||
|
@ -853,7 +853,7 @@ return array(
|
|||
'phui-curtain-view-css' => '679743bb',
|
||||
'phui-document-summary-view-css' => '9ca48bdf',
|
||||
'phui-document-view-css' => 'c32e8dec',
|
||||
'phui-document-view-pro-css' => 'f56738ed',
|
||||
'phui-document-view-pro-css' => '62c4dcbf',
|
||||
'phui-feed-story-css' => '44a9c8e9',
|
||||
'phui-font-icon-base-css' => '870a7360',
|
||||
'phui-fontkit-css' => '1320ed01',
|
||||
|
@ -862,7 +862,7 @@ return array(
|
|||
'phui-head-thing-view-css' => 'fd311e5f',
|
||||
'phui-header-view-css' => 'e082678d',
|
||||
'phui-hovercard' => '1bd28176',
|
||||
'phui-hovercard-view-css' => 'ae091fc5',
|
||||
'phui-hovercard-view-css' => 'f0592bcf',
|
||||
'phui-icon-set-selector-css' => '87db8fee',
|
||||
'phui-icon-view-css' => '12b387a1',
|
||||
'phui-image-mask-css' => 'a8498f9c',
|
||||
|
@ -877,7 +877,7 @@ return array(
|
|||
'phui-oi-color-css' => 'cd2b9b77',
|
||||
'phui-oi-drag-ui-css' => 'f12cbc9f',
|
||||
'phui-oi-flush-ui-css' => '9d9685d6',
|
||||
'phui-oi-list-view-css' => '5c383524',
|
||||
'phui-oi-list-view-css' => '7c8ec27a',
|
||||
'phui-oi-simple-ui-css' => 'a8beebea',
|
||||
'phui-pager-css' => '77d8a794',
|
||||
'phui-pinboard-view-css' => '2495140e',
|
||||
|
@ -896,7 +896,7 @@ return array(
|
|||
'phui-workpanel-view-css' => 'a3a63478',
|
||||
'phuix-action-list-view' => 'b5c256b8',
|
||||
'phuix-action-view' => 'b3465b9b',
|
||||
'phuix-autocomplete' => 'd713a2c5',
|
||||
'phuix-autocomplete' => 'f6699267',
|
||||
'phuix-dropdown-menu' => '8018ee50',
|
||||
'phuix-form-control-view' => '83e03671',
|
||||
'phuix-icon-view' => 'bff6884b',
|
||||
|
@ -988,17 +988,6 @@ return array(
|
|||
'javelin-dom',
|
||||
'javelin-router',
|
||||
),
|
||||
'0ca788bd' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-stratcom',
|
||||
'javelin-dom',
|
||||
'phabricator-phtize',
|
||||
'phabricator-textareautils',
|
||||
'javelin-workflow',
|
||||
'javelin-vector',
|
||||
'phuix-autocomplete',
|
||||
'javelin-mask',
|
||||
),
|
||||
'0f764c35' => array(
|
||||
'javelin-install',
|
||||
'javelin-util',
|
||||
|
@ -1822,6 +1811,17 @@ return array(
|
|||
'javelin-util',
|
||||
'phabricator-busy',
|
||||
),
|
||||
'acd29eee' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-stratcom',
|
||||
'javelin-dom',
|
||||
'phabricator-phtize',
|
||||
'phabricator-textareautils',
|
||||
'javelin-workflow',
|
||||
'javelin-vector',
|
||||
'phuix-autocomplete',
|
||||
'javelin-mask',
|
||||
),
|
||||
'b003d4fb' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-stratcom',
|
||||
|
@ -2087,12 +2087,6 @@ return array(
|
|||
'javelin-json',
|
||||
'phabricator-prefab',
|
||||
),
|
||||
'd713a2c5' => array(
|
||||
'javelin-install',
|
||||
'javelin-dom',
|
||||
'phuix-icon-view',
|
||||
'phabricator-prefab',
|
||||
),
|
||||
'd7a74243' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-stratcom',
|
||||
|
@ -2220,6 +2214,12 @@ return array(
|
|||
'javelin-util',
|
||||
'javelin-reactor',
|
||||
),
|
||||
'f6699267' => array(
|
||||
'javelin-install',
|
||||
'javelin-dom',
|
||||
'phuix-icon-view',
|
||||
'phabricator-prefab',
|
||||
),
|
||||
'f7fc67ec' => array(
|
||||
'javelin-install',
|
||||
'javelin-typeahead',
|
||||
|
|
|
@ -1,34 +1,4 @@
|
|||
<?php
|
||||
|
||||
// Rebuild all Conpherence Room images to profile standards
|
||||
//
|
||||
$table = new ConpherenceThread();
|
||||
$conn = $table->establishConnection('w');
|
||||
$table_name = 'conpherence_thread';
|
||||
|
||||
foreach (new LiskRawMigrationIterator($conn, $table_name) as $row) {
|
||||
|
||||
$images = phutil_json_decode($row['imagePHIDs']);
|
||||
if (!$images) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$file_phid = idx($images, 'original');
|
||||
|
||||
$file = id(new PhabricatorFileQuery())
|
||||
->setViewer(PhabricatorUser::getOmnipotentUser())
|
||||
->withPHIDs(array($file_phid))
|
||||
->executeOne();
|
||||
|
||||
$xform = PhabricatorFileTransform::getTransformByKey(
|
||||
PhabricatorFileThumbnailTransform::TRANSFORM_PROFILE);
|
||||
$xformed = $xform->executeTransform($file);
|
||||
$new_phid = $xformed->getPHID();
|
||||
|
||||
queryfx(
|
||||
$conn,
|
||||
'UPDATE %T SET profileImagePHID = %s WHERE id = %d',
|
||||
$table->getTableName(),
|
||||
$new_phid,
|
||||
$row['id']);
|
||||
}
|
||||
// This migration once resized room images for Conpherence, but the File table
|
||||
// later changed significantly. See T12628.
|
||||
|
|
34
resources/sql/autopatches/20170424.user.01.verify.php
Normal file
34
resources/sql/autopatches/20170424.user.01.verify.php
Normal file
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
$table = new PhabricatorUser();
|
||||
$conn = $table->establishConnection('w');
|
||||
|
||||
foreach (new LiskMigrationIterator($table) as $user) {
|
||||
// Ignore users who are verified.
|
||||
if ($user->getIsEmailVerified()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ignore unverified users with missing (rare) or unverified (common)
|
||||
// primary emails: it's correct that their accounts are not verified.
|
||||
$primary = $user->loadPrimaryEmail();
|
||||
if (!$primary) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$primary->getIsVerified()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
queryfx(
|
||||
$conn,
|
||||
'UPDATE %T SET isEmailVerified = 1 WHERE id = %d',
|
||||
$table->getTableName(),
|
||||
$user->getID());
|
||||
|
||||
echo tsprintf(
|
||||
"%s\n",
|
||||
pht(
|
||||
'Corrected account verification state for user "%s".',
|
||||
$user->getUsername()));
|
||||
}
|
2
resources/sql/autopatches/20170427.owners.01.long.sql
Normal file
2
resources/sql/autopatches/20170427.owners.01.long.sql
Normal file
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_owners.owners_package
|
||||
DROP originalName;
|
File diff suppressed because one or more lines are too long
|
@ -3204,6 +3204,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorOwnersDefaultViewCapability' => 'applications/owners/capability/PhabricatorOwnersDefaultViewCapability.php',
|
||||
'PhabricatorOwnersDetailController' => 'applications/owners/controller/PhabricatorOwnersDetailController.php',
|
||||
'PhabricatorOwnersEditController' => 'applications/owners/controller/PhabricatorOwnersEditController.php',
|
||||
'PhabricatorOwnersHovercardEngineExtension' => 'applications/owners/engineextension/PhabricatorOwnersHovercardEngineExtension.php',
|
||||
'PhabricatorOwnersListController' => 'applications/owners/controller/PhabricatorOwnersListController.php',
|
||||
'PhabricatorOwnersOwner' => 'applications/owners/storage/PhabricatorOwnersOwner.php',
|
||||
'PhabricatorOwnersPackage' => 'applications/owners/storage/PhabricatorOwnersPackage.php',
|
||||
|
@ -8406,6 +8407,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorOwnersDefaultViewCapability' => 'PhabricatorPolicyCapability',
|
||||
'PhabricatorOwnersDetailController' => 'PhabricatorOwnersController',
|
||||
'PhabricatorOwnersEditController' => 'PhabricatorOwnersController',
|
||||
'PhabricatorOwnersHovercardEngineExtension' => 'PhabricatorHovercardEngineExtension',
|
||||
'PhabricatorOwnersListController' => 'PhabricatorOwnersController',
|
||||
'PhabricatorOwnersOwner' => 'PhabricatorOwnersDAO',
|
||||
'PhabricatorOwnersPackage' => array(
|
||||
|
|
|
@ -205,9 +205,8 @@ abstract class AphrontApplicationConfiguration extends Phobject {
|
|||
DarkConsoleXHProfPluginAPI::saveProfilerSample($access_log);
|
||||
|
||||
// Add points to the rate limits for this request.
|
||||
if (isset($_SERVER['REMOTE_ADDR'])) {
|
||||
$user_ip = $_SERVER['REMOTE_ADDR'];
|
||||
|
||||
$rate_token = PhabricatorStartup::getRateLimitToken();
|
||||
if ($rate_token !== null) {
|
||||
// The base score for a request allows users to make 30 requests per
|
||||
// minute.
|
||||
$score = (1000 / 30);
|
||||
|
@ -217,7 +216,7 @@ abstract class AphrontApplicationConfiguration extends Phobject {
|
|||
$score = $score / 5;
|
||||
}
|
||||
|
||||
PhabricatorStartup::addRateLimitScore($user_ip, $score);
|
||||
PhabricatorStartup::addRateLimitScore($rate_token, $score);
|
||||
}
|
||||
|
||||
if ($processing_exception) {
|
||||
|
|
|
@ -11,7 +11,7 @@ final class PhabricatorBadgesApplication extends PhabricatorApplication {
|
|||
}
|
||||
|
||||
public function getShortDescription() {
|
||||
return pht('Achievements and Notority');
|
||||
return pht('Achievements and Notoriety');
|
||||
}
|
||||
|
||||
public function getIcon() {
|
||||
|
|
|
@ -35,6 +35,7 @@ final class PhabricatorAccessLogConfigOptions
|
|||
'P' => pht('The logged-in user PHID, if one is logged in.'),
|
||||
'i' => pht('Request input, in bytes.'),
|
||||
'o' => pht('Request output, in bytes.'),
|
||||
'I' => pht('Cluster instance name, if configured.'),
|
||||
);
|
||||
|
||||
$http_map = $common_map + array(
|
||||
|
|
|
@ -559,8 +559,7 @@ final class DiffusionRepositoryClusterEngine extends Phobject {
|
|||
return false;
|
||||
}
|
||||
|
||||
// TODO: For now, this is only supported for Git.
|
||||
if (!$repository->isGit()) {
|
||||
if (!$repository->supportsSynchronization()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,9 +2,33 @@
|
|||
|
||||
final class PhabricatorFileTransformTestCase extends PhabricatorTestCase {
|
||||
|
||||
protected function getPhabricatorTestCaseConfiguration() {
|
||||
return array(
|
||||
self::PHABRICATOR_TESTCONFIG_BUILD_STORAGE_FIXTURES => true,
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetAllTransforms() {
|
||||
PhabricatorFileTransform::getAllTransforms();
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
public function testThumbTransformDefaults() {
|
||||
$xforms = PhabricatorFileTransform::getAllTransforms();
|
||||
$file = new PhabricatorFile();
|
||||
|
||||
foreach ($xforms as $xform) {
|
||||
if (!($xform instanceof PhabricatorFileThumbnailTransform)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// For thumbnails, generate the default thumbnail. This should be able
|
||||
// to generate something rather than throwing an exception because we
|
||||
// forgot to add a default file to the builtin resources. See T12614.
|
||||
$xform->getDefaultTransform($file);
|
||||
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -320,13 +320,16 @@ final class LegalpadDocumentSignController extends LegalpadController {
|
|||
$crumbs->setBorder(true);
|
||||
$crumbs->addTextCrumb($document->getMonogram());
|
||||
|
||||
$box = id(new PHUITwoColumnView())
|
||||
->setFooter($signature_box);
|
||||
|
||||
return $this->newPage()
|
||||
->setTitle($title)
|
||||
->setCrumbs($crumbs)
|
||||
->setPageObjectPHIDs(array($document->getPHID()))
|
||||
->appendChild(array(
|
||||
$content,
|
||||
$signature_box,
|
||||
$box,
|
||||
));
|
||||
}
|
||||
|
||||
|
|
|
@ -24,24 +24,52 @@ final class ManiphestHovercardEngineExtension
|
|||
$task,
|
||||
$data) {
|
||||
$viewer = $this->getViewer();
|
||||
require_celerity_resource('phui-workcard-view-css');
|
||||
|
||||
$hovercard
|
||||
->setTitle($task->getMonogram())
|
||||
->setDetail($task->getTitle());
|
||||
$id = $task->getID();
|
||||
$task = id(new ManiphestTaskQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($id))
|
||||
->needProjectPHIDs(true)
|
||||
->executeOne();
|
||||
|
||||
$phids = array();
|
||||
$owner_phid = $task->getOwnerPHID();
|
||||
if ($owner_phid) {
|
||||
$phids[$owner_phid] = $owner_phid;
|
||||
}
|
||||
foreach ($task->getProjectPHIDs() as $phid) {
|
||||
$phids[$phid] = $phid;
|
||||
}
|
||||
|
||||
$handles = $viewer->loadHandles($phids);
|
||||
$handles = iterator_to_array($handles);
|
||||
|
||||
$card = id(new ProjectBoardTaskCard())
|
||||
->setViewer($viewer)
|
||||
->setTask($task)
|
||||
->setCanEdit(false);
|
||||
|
||||
$owner_phid = $task->getOwnerPHID();
|
||||
if ($owner_phid) {
|
||||
$owner = $viewer->renderHandle($owner_phid);
|
||||
} else {
|
||||
$owner = phutil_tag('em', array(), pht('None'));
|
||||
$owner_handle = $handles[$owner_phid];
|
||||
$card->setOwner($owner_handle);
|
||||
}
|
||||
$hovercard->addField(pht('Assigned To'), $owner);
|
||||
|
||||
$hovercard->addField(
|
||||
pht('Priority'),
|
||||
ManiphestTaskPriority::getTaskPriorityName($task->getPriority()));
|
||||
$project_phids = $task->getProjectPHIDs();
|
||||
$project_handles = array_select_keys($handles, $project_phids);
|
||||
if ($project_handles) {
|
||||
$card->setProjectHandles($project_handles);
|
||||
}
|
||||
|
||||
$item = $card->getItem();
|
||||
$card = id(new PHUIObjectItemListView())
|
||||
->setFlush(true)
|
||||
->setItemClass('phui-workcard')
|
||||
->addClass('hovercard-task-view')
|
||||
->addItem($item);
|
||||
$hovercard->appendChild($card);
|
||||
|
||||
$hovercard->addTag(ManiphestView::renderTagForTask($task));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -323,6 +323,10 @@ final class PhabricatorOwnersDetailController
|
|||
'wide',
|
||||
));
|
||||
|
||||
if ($info) {
|
||||
$table->setNotice($info);
|
||||
}
|
||||
|
||||
$header = id(new PHUIHeaderView())
|
||||
->setHeader(pht('Paths'))
|
||||
->setHeaderIcon('fa-folder-open');
|
||||
|
@ -332,10 +336,6 @@ final class PhabricatorOwnersDetailController
|
|||
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
||||
->setTable($table);
|
||||
|
||||
if ($info) {
|
||||
$box->setInfoView($info);
|
||||
}
|
||||
|
||||
return $box;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorOwnersHovercardEngineExtension
|
||||
extends PhabricatorHovercardEngineExtension {
|
||||
|
||||
const EXTENSIONKEY = 'owners';
|
||||
|
||||
public function isExtensionEnabled() {
|
||||
return PhabricatorApplication::isClassInstalled(
|
||||
'PhabricatorOwnersApplication');
|
||||
}
|
||||
|
||||
public function getExtensionName() {
|
||||
return pht('Owner Packages');
|
||||
}
|
||||
|
||||
public function canRenderObjectHovercard($object) {
|
||||
return ($object instanceof PhabricatorOwnersPackage);
|
||||
}
|
||||
|
||||
public function willRenderHovercards(array $objects) {
|
||||
$viewer = $this->getViewer();
|
||||
$phids = mpull($objects, 'getPHID');
|
||||
|
||||
$packages = id(new PhabricatorOwnersPackageQuery())
|
||||
->setViewer($viewer)
|
||||
->withPHIDs($phids)
|
||||
->execute();
|
||||
$packages = mpull($packages, null, 'getPHID');
|
||||
|
||||
return array(
|
||||
'packages' => $packages,
|
||||
);
|
||||
}
|
||||
|
||||
public function renderHovercard(
|
||||
PHUIHovercardView $hovercard,
|
||||
PhabricatorObjectHandle $handle,
|
||||
$object,
|
||||
$data) {
|
||||
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$package = idx($data['packages'], $object->getPHID());
|
||||
if (!$package) {
|
||||
return;
|
||||
}
|
||||
|
||||
$title = pht('%s: %s', 'O'.$package->getID(), $package->getName());
|
||||
$hovercard->setTitle($title);
|
||||
|
||||
$dominion = $package->getDominion();
|
||||
$dominion_map = PhabricatorOwnersPackage::getDominionOptionsMap();
|
||||
$spec = idx($dominion_map, $dominion, array());
|
||||
$name = idx($spec, 'short', $dominion);
|
||||
$hovercard->addField(pht('Dominion'), $name);
|
||||
|
||||
$auto = $package->getAutoReview();
|
||||
$autoreview_map = PhabricatorOwnersPackage::getAutoreviewOptionsMap();
|
||||
$spec = idx($autoreview_map, $auto, array());
|
||||
$name = idx($spec, 'name', $auto);
|
||||
$hovercard->addField(pht('Auto Review'), $name);
|
||||
|
||||
if ($package->isArchived()) {
|
||||
$tag = id(new PHUITagView())
|
||||
->setName(pht('Archived'))
|
||||
->setShade(PHUITagView::COLOR_INDIGO)
|
||||
->setType(PHUITagView::TYPE_OBJECT);
|
||||
$hovercard->addTag($tag);
|
||||
}
|
||||
|
||||
$owner_phids = $package->getOwnerPHIDs();
|
||||
|
||||
$hovercard->addField(
|
||||
pht('Owners'),
|
||||
$viewer->renderHandleList($owner_phids)->setAsInline(true));
|
||||
|
||||
$description = $package->getDescription();
|
||||
if (strlen($description)) {
|
||||
$description = id(new PhutilUTF8StringTruncator())
|
||||
->setMaximumGlyphs(120)
|
||||
->truncateString($description);
|
||||
|
||||
$hovercard->addField(pht('Description'), $description);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -12,7 +12,6 @@ final class PhabricatorOwnersPackage
|
|||
PhabricatorNgramsInterface {
|
||||
|
||||
protected $name;
|
||||
protected $originalName;
|
||||
protected $auditingEnabled;
|
||||
protected $autoReview;
|
||||
protected $description;
|
||||
|
@ -105,8 +104,7 @@ final class PhabricatorOwnersPackage
|
|||
self::CONFIG_TIMESTAMPS => false,
|
||||
self::CONFIG_AUX_PHID => true,
|
||||
self::CONFIG_COLUMN_SCHEMA => array(
|
||||
'name' => 'sort128',
|
||||
'originalName' => 'text255',
|
||||
'name' => 'sort',
|
||||
'description' => 'text',
|
||||
'primaryOwnerPHID' => 'phid?',
|
||||
'auditingEnabled' => 'bool',
|
||||
|
@ -137,9 +135,6 @@ final class PhabricatorOwnersPackage
|
|||
|
||||
public function setName($name) {
|
||||
$this->name = $name;
|
||||
if (!$this->getID()) {
|
||||
$this->originalName = $name;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
|
|
@ -540,6 +540,14 @@ final class PhabricatorUserEditor extends PhabricatorEditor {
|
|||
$email->setIsPrimary(1);
|
||||
$email->save();
|
||||
|
||||
// If the user doesn't have the verified flag set on their account
|
||||
// yet, set it. We've made sure the email is verified above. See
|
||||
// T12635 for discussion.
|
||||
if (!$user->getIsEmailVerified()) {
|
||||
$user->setIsEmailVerified(1);
|
||||
$user->save();
|
||||
}
|
||||
|
||||
$log = PhabricatorUserLog::initializeNewLog(
|
||||
$actor,
|
||||
$user->getPHID(),
|
||||
|
|
|
@ -1929,10 +1929,31 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
|
|||
'Cluster hosts must correctly route their intracluster requests.'));
|
||||
}
|
||||
|
||||
if (count($results) > 1) {
|
||||
if (!$this->supportsSynchronization()) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Repository "%s" is bound to multiple active repository hosts, '.
|
||||
'but this repository does not support cluster synchronization. '.
|
||||
'Declusterize this repository or move it to a service with only '.
|
||||
'one host.',
|
||||
$this->getDisplayName()));
|
||||
}
|
||||
}
|
||||
|
||||
shuffle($results);
|
||||
return head($results);
|
||||
}
|
||||
|
||||
public function supportsSynchronization() {
|
||||
// TODO: For now, this is only supported for Git.
|
||||
if (!$this->isGit()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getAlmanacServiceCacheKey() {
|
||||
$service_phid = $this->getAlmanacServicePHID();
|
||||
if (!$service_phid) {
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
@title Configuring a Preamble Script
|
||||
@group config
|
||||
|
||||
Adjust environmental settings (SSL, remote IP, rate limiting) using a preamble
|
||||
script.
|
||||
Adjust environmental settings (SSL, remote IPs) using a preamble script.
|
||||
|
||||
Overview
|
||||
========
|
||||
|
|
|
@ -30,6 +30,7 @@ final class PhabricatorAccessLog extends Phobject {
|
|||
'h' => php_uname('n'),
|
||||
'p' => getmypid(),
|
||||
'e' => time(),
|
||||
'I' => PhabricatorEnv::getEnvConfig('cluster.instance'),
|
||||
));
|
||||
|
||||
self::$log = $log;
|
||||
|
|
|
@ -20,6 +20,7 @@ final class PhabricatorSSHLog extends Phobject {
|
|||
'h' => php_uname('n'),
|
||||
'p' => getmypid(),
|
||||
'e' => time(),
|
||||
'I' => PhabricatorEnv::getEnvConfig('cluster.instance'),
|
||||
);
|
||||
|
||||
$sudo_user = PhabricatorEnv::getEnvConfig('phd.user');
|
||||
|
|
|
@ -112,14 +112,17 @@ final class PHUIHovercardView extends AphrontTagView {
|
|||
|
||||
$body = array();
|
||||
|
||||
$body_title = null;
|
||||
if ($this->detail) {
|
||||
$body_title = $this->detail;
|
||||
} else {
|
||||
} else if (!$this->fields) {
|
||||
// Fallback for object handles
|
||||
$body_title = $handle->getFullName();
|
||||
}
|
||||
|
||||
if ($body_title) {
|
||||
$body[] = phutil_tag_div('phui-hovercard-body-header', $body_title);
|
||||
}
|
||||
|
||||
foreach ($this->fields as $field) {
|
||||
$item = array(
|
||||
|
|
|
@ -50,6 +50,7 @@ final class PhabricatorStartup {
|
|||
// iterate on it a bit for Conduit, some of the specific score levels, and
|
||||
// to deal with NAT'd offices.
|
||||
private static $maximumRate = 0;
|
||||
private static $rateLimitToken;
|
||||
|
||||
|
||||
/* -( Accessing Request Information )-------------------------------------- */
|
||||
|
@ -137,8 +138,9 @@ final class PhabricatorStartup {
|
|||
// we can switch over to relying on our own exception recovery mechanisms.
|
||||
ini_set('display_errors', 0);
|
||||
|
||||
if (isset($_SERVER['REMOTE_ADDR'])) {
|
||||
self::rateLimitRequest($_SERVER['REMOTE_ADDR']);
|
||||
$rate_token = self::getRateLimitToken();
|
||||
if ($rate_token !== null) {
|
||||
self::rateLimitRequest($rate_token);
|
||||
}
|
||||
|
||||
self::normalizeInput();
|
||||
|
@ -680,6 +682,36 @@ final class PhabricatorStartup {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set a token to identify the client for purposes of rate limiting.
|
||||
*
|
||||
* By default, the `REMOTE_ADDR` is used. If your install is behind a load
|
||||
* balancer, you may want to parse `X-Forwarded-For` and use that address
|
||||
* instead.
|
||||
*
|
||||
* @param string Client identity for rate limiting.
|
||||
*/
|
||||
public static function setRateLimitToken($token) {
|
||||
self::$rateLimitToken = $token;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the current client identity for rate limiting.
|
||||
*/
|
||||
public static function getRateLimitToken() {
|
||||
if (self::$rateLimitToken !== null) {
|
||||
return self::$rateLimitToken;
|
||||
}
|
||||
|
||||
if (isset($_SERVER['REMOTE_ADDR'])) {
|
||||
return $_SERVER['REMOTE_ADDR'];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if the user (identified by `$user_identity`) has issued too many
|
||||
* requests recently. If they have, end the request with a 429 error code.
|
||||
|
@ -699,13 +731,18 @@ final class PhabricatorStartup {
|
|||
}
|
||||
|
||||
$score = self::getRateLimitScore($user_identity);
|
||||
if ($score > (self::$maximumRate * self::getRateLimitBucketCount())) {
|
||||
$limit = self::$maximumRate * self::getRateLimitBucketCount();
|
||||
if ($score > $limit) {
|
||||
// Give the user some bonus points for getting rate limited. This keeps
|
||||
// bad actors who keep slamming the 429 page locked out completely,
|
||||
// instead of letting them get a burst of requests through every minute
|
||||
// after a bucket expires.
|
||||
self::addRateLimitScore($user_identity, 50);
|
||||
self::didRateLimit($user_identity);
|
||||
$penalty = 50;
|
||||
|
||||
self::addRateLimitScore($user_identity, $penalty);
|
||||
$score += $penalty;
|
||||
|
||||
self::didRateLimit($user_identity, $score, $limit);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -729,15 +766,21 @@ final class PhabricatorStartup {
|
|||
return;
|
||||
}
|
||||
|
||||
$is_apcu = (bool)function_exists('apcu_fetch');
|
||||
$current = self::getRateLimitBucket();
|
||||
|
||||
// There's a bit of a race here, if a second process reads the bucket before
|
||||
// this one writes it, but it's fine if we occasionally fail to record a
|
||||
// user's score. If they're making requests fast enough to hit rate
|
||||
// limiting, we'll get them soon.
|
||||
// There's a bit of a race here, if a second process reads the bucket
|
||||
// before this one writes it, but it's fine if we occasionally fail to
|
||||
// record a user's score. If they're making requests fast enough to hit
|
||||
// rate limiting, we'll get them soon enough.
|
||||
|
||||
$bucket_key = self::getRateLimitBucketKey($current);
|
||||
if ($is_apcu) {
|
||||
$bucket = apcu_fetch($bucket_key);
|
||||
} else {
|
||||
$bucket = apc_fetch($bucket_key);
|
||||
}
|
||||
|
||||
if (!is_array($bucket)) {
|
||||
$bucket = array();
|
||||
}
|
||||
|
@ -747,8 +790,13 @@ final class PhabricatorStartup {
|
|||
}
|
||||
|
||||
$bucket[$user_identity] += $score;
|
||||
|
||||
if ($is_apcu) {
|
||||
apcu_store($bucket_key, $bucket);
|
||||
} else {
|
||||
apc_store($bucket_key, $bucket);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
|
@ -761,11 +809,12 @@ final class PhabricatorStartup {
|
|||
* @task ratelimit
|
||||
*/
|
||||
private static function canRateLimit() {
|
||||
|
||||
if (!self::$maximumRate) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!function_exists('apc_fetch')) {
|
||||
if (!function_exists('apc_fetch') && !function_exists('apcu_fetch')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -826,16 +875,26 @@ final class PhabricatorStartup {
|
|||
* @task ratelimit
|
||||
*/
|
||||
private static function getRateLimitScore($user_identity) {
|
||||
$is_apcu = (bool)function_exists('apcu_fetch');
|
||||
|
||||
$min_key = self::getRateLimitMinKey();
|
||||
|
||||
// Identify the oldest bucket stored in APC.
|
||||
$cur = self::getRateLimitBucket();
|
||||
if ($is_apcu) {
|
||||
$min = apcu_fetch($min_key);
|
||||
} else {
|
||||
$min = apc_fetch($min_key);
|
||||
}
|
||||
|
||||
// If we don't have any buckets stored yet, store the current bucket as
|
||||
// the oldest bucket.
|
||||
if (!$min) {
|
||||
if ($is_apcu) {
|
||||
apcu_store($min_key, $cur);
|
||||
} else {
|
||||
apc_store($min_key, $cur);
|
||||
}
|
||||
$min = $cur;
|
||||
}
|
||||
|
||||
|
@ -844,14 +903,25 @@ final class PhabricatorStartup {
|
|||
// up an old bucket once per minute.
|
||||
$count = self::getRateLimitBucketCount();
|
||||
for ($cursor = $min; $cursor < ($cur - $count); $cursor++) {
|
||||
apc_delete(self::getRateLimitBucketKey($cursor));
|
||||
$bucket_key = self::getRateLimitBucketKey($cursor);
|
||||
if ($is_apcu) {
|
||||
apcu_delete($bucket_key);
|
||||
apcu_store($min_key, $cursor + 1);
|
||||
} else {
|
||||
apc_delete($bucket_key);
|
||||
apc_store($min_key, $cursor + 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Now, sum up the user's scores in all of the active buckets.
|
||||
$score = 0;
|
||||
for (; $cursor <= $cur; $cursor++) {
|
||||
$bucket = apc_fetch(self::getRateLimitBucketKey($cursor));
|
||||
$bucket_key = self::getRateLimitBucketKey($cursor);
|
||||
if ($is_apcu) {
|
||||
$bucket = apcu_fetch($bucket_key);
|
||||
} else {
|
||||
$bucket = apc_fetch($bucket_key);
|
||||
}
|
||||
if (isset($bucket[$user_identity])) {
|
||||
$score += $bucket[$user_identity];
|
||||
}
|
||||
|
@ -868,12 +938,11 @@ final class PhabricatorStartup {
|
|||
* @return exit This method **does not return**.
|
||||
* @task ratelimit
|
||||
*/
|
||||
private static function didRateLimit() {
|
||||
private static function didRateLimit($user_identity, $score, $limit) {
|
||||
$message =
|
||||
"TOO MANY REQUESTS\n".
|
||||
"You are issuing too many requests too quickly.\n".
|
||||
"To adjust limits, see \"Configuring a Preamble Script\" in the ".
|
||||
"documentation.";
|
||||
"You (\"{$user_identity}\") are issuing too many requests ".
|
||||
"too quickly.\n";
|
||||
|
||||
header(
|
||||
'Content-Type: text/plain; charset=utf-8',
|
||||
|
|
|
@ -29,6 +29,10 @@
|
|||
border-bottom: 1px solid {$thinblueborder};
|
||||
}
|
||||
|
||||
.phui-two-column-view .aphront-table-notice .phui-info-view {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.aphront-table-view tr.alt {
|
||||
background: {$lightgreybackground};
|
||||
}
|
||||
|
|
|
@ -61,9 +61,10 @@ body.white-background {
|
|||
}
|
||||
|
||||
.keyboard-focus-focus-reticle {
|
||||
background: #ffffd3;
|
||||
background: rgba(255, 255, 211, 0.15);
|
||||
position: absolute;
|
||||
border: 1px solid #999900;
|
||||
border: 1px solid {$yellow};
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
a.handle-status-closed {
|
||||
|
|
|
@ -2,16 +2,16 @@
|
|||
* @provides phabricator-zindex-css
|
||||
*/
|
||||
|
||||
.keyboard-focus-focus-reticle {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.device .phabricator-action-list-view.phabricator-action-list-toggle,
|
||||
.device-desktop .phui-document-content
|
||||
.phabricator-action-list-view.phabricator-action-list-toggle {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.keyboard-focus-focus-reticle {
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.device-desktop .phui-timeline-minor-event .phui-timeline-image {
|
||||
z-index: 2;
|
||||
}
|
||||
|
|
|
@ -302,6 +302,7 @@ ul.phui-oi-list-view {
|
|||
display: inline-block;
|
||||
color: {$greytext};
|
||||
vertical-align: top;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.phui-oi-attribute-spacer {
|
||||
|
|
|
@ -74,7 +74,7 @@ body.printable {
|
|||
a.button.phui-document-toc {
|
||||
display: inline-block;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
width: 20px;
|
||||
padding: 3px 8px 4px 8px;
|
||||
}
|
||||
|
||||
|
@ -88,7 +88,7 @@ a.button.phui-document-toc {
|
|||
z-index: 30;
|
||||
background-color: #fff;
|
||||
top: 52px;
|
||||
left: -44px;
|
||||
left: -40px;
|
||||
}
|
||||
|
||||
.device .phui-document-view-pro .phui-document-toc {
|
||||
|
|
|
@ -105,3 +105,11 @@
|
|||
.phui-hovercard-tail a.button {
|
||||
margin: 3px;
|
||||
}
|
||||
|
||||
.phui-hovercard-wrapper .hovercard-task-view {
|
||||
box-shadow: 0px 4px 16px rgba(0,0,0,.2);
|
||||
}
|
||||
|
||||
.hovercard-task-view .phui-oi-disabled.phui-workcard {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
|
|
@ -393,6 +393,12 @@ JX.behavior('phabricator-remarkup-assist', function(config) {
|
|||
return;
|
||||
}
|
||||
|
||||
// Let other listeners (particularly the inline autocomplete) have a
|
||||
// chance to handle this event.
|
||||
if (JX.Stratcom.pass()) {
|
||||
return;
|
||||
}
|
||||
|
||||
var raw = e.getRawEvent();
|
||||
if (raw.shiftKey) {
|
||||
// If the shift key is pressed, let the browser write a newline into
|
||||
|
@ -412,8 +418,7 @@ JX.behavior('phabricator-remarkup-assist', function(config) {
|
|||
// This allows 'workflow' and similar actions to take effect.
|
||||
// Such as pontificate in Conpherence
|
||||
var form = e.getNode('tag:form');
|
||||
var r = JX.DOM.invoke(form, 'didSyntheticSubmit');
|
||||
|
||||
JX.DOM.invoke(form, 'didSyntheticSubmit');
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -127,7 +127,7 @@ JX.install('PHUIXAutocomplete', {
|
|||
}
|
||||
|
||||
// Get all the text on the current line. If the line only contains
|
||||
// whitespace, don't actiavte: the user is probably typing code or a
|
||||
// whitespace, don't activate: the user is probably typing code or a
|
||||
// numbered list.
|
||||
var line = area.value.substring(0, head - 1);
|
||||
line = line.split('\n');
|
||||
|
@ -454,7 +454,7 @@ JX.install('PHUIXAutocomplete', {
|
|||
|
||||
// If the user hasn't typed any text yet after typing the character
|
||||
// which can summon the autocomplete, deactivate and let the keystroke
|
||||
// through. For example, We hit this when a line ends with an
|
||||
// through. For example, we hit this when a line ends with an
|
||||
// autocomplete character and the user is trying to type a newline.
|
||||
if (range.start == this._cursorHead) {
|
||||
this._deactivate();
|
||||
|
@ -529,9 +529,13 @@ JX.install('PHUIXAutocomplete', {
|
|||
}
|
||||
}
|
||||
|
||||
// Deactivate immediately if the user types an ignored token like ":)",
|
||||
// the smiley face emoticon. Note that we test against "text", not
|
||||
// "trim", because the ignore list and suffix list can otherwise
|
||||
// interact destructively.
|
||||
var ignore = this._getIgnoreList();
|
||||
for (ii = 0; ii < ignore.length; ii++) {
|
||||
if (trim.indexOf(ignore[ii]) === 0) {
|
||||
if (text.indexOf(ignore[ii]) === 0) {
|
||||
this._deactivate();
|
||||
return;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue