1
0
Fork 0
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:
epriestley 2017-04-29 04:56:25 -07:00
commit 53bc57ecef
31 changed files with 919 additions and 192 deletions

View file

@ -9,8 +9,8 @@ return array(
'names' => array( 'names' => array(
'conpherence.pkg.css' => 'ff161f2d', 'conpherence.pkg.css' => 'ff161f2d',
'conpherence.pkg.js' => 'b5b51108', 'conpherence.pkg.js' => 'b5b51108',
'core.pkg.css' => '005d943f', 'core.pkg.css' => '84ce260a',
'core.pkg.js' => '47a69358', 'core.pkg.js' => 'fffe0122',
'darkconsole.pkg.js' => '1f9a31bc', 'darkconsole.pkg.js' => '1f9a31bc',
'differential.pkg.css' => '90b30783', 'differential.pkg.css' => '90b30783',
'differential.pkg.js' => 'ddfeb49b', 'differential.pkg.js' => 'ddfeb49b',
@ -32,7 +32,7 @@ return array(
'rsrc/css/aphront/notification.css' => '3f6c89c9', 'rsrc/css/aphront/notification.css' => '3f6c89c9',
'rsrc/css/aphront/panel-view.css' => '8427b78d', 'rsrc/css/aphront/panel-view.css' => '8427b78d',
'rsrc/css/aphront/phabricator-nav-view.css' => 'faf6a6fc', '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/tokenizer.css' => '9a8cb501',
'rsrc/css/aphront/tooltip.css' => '173b9431', 'rsrc/css/aphront/tooltip.css' => '173b9431',
'rsrc/css/aphront/typeahead-browse.css' => '8904346a', '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/main-menu-view.css' => '5294060f',
'rsrc/css/application/base/notification-menu.css' => '6a697e43', 'rsrc/css/application/base/notification-menu.css' => '6a697e43',
'rsrc/css/application/base/phui-theme.css' => '9f261c6b', '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/chatlog/chatlog.css' => 'd295b020',
'rsrc/css/application/conduit/conduit-api.css' => '7bc725c4', 'rsrc/css/application/conduit/conduit-api.css' => '7bc725c4',
'rsrc/css/application/config/config-options.css' => '0ede4c9b', 'rsrc/css/application/config/config-options.css' => '0ede4c9b',
@ -116,7 +116,7 @@ return array(
'rsrc/css/core/core.css' => '9f4cb463', 'rsrc/css/core/core.css' => '9f4cb463',
'rsrc/css/core/remarkup.css' => '17c0fb37', 'rsrc/css/core/remarkup.css' => '17c0fb37',
'rsrc/css/core/syntax.css' => 'cae95e89', '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/diviner/diviner-shared.css' => '896f1d43',
'rsrc/css/font/font-awesome.css' => 'e838e088', 'rsrc/css/font/font-awesome.css' => 'e838e088',
'rsrc/css/font/font-lato.css' => 'c7ccd872', '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-color.css' => 'cd2b9b77',
'rsrc/css/phui/object-item/phui-oi-drag-ui.css' => 'f12cbc9f', '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-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/object-item/phui-oi-simple-ui.css' => 'a8beebea',
'rsrc/css/phui/phui-action-list.css' => 'c01858f4', 'rsrc/css/phui/phui-action-list.css' => 'c01858f4',
'rsrc/css/phui/phui-action-panel.css' => '91c7b835', '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-comment-panel.css' => 'f50152ad',
'rsrc/css/phui/phui-crumbs-view.css' => '6ece3bbb', 'rsrc/css/phui/phui-crumbs-view.css' => '6ece3bbb',
'rsrc/css/phui/phui-curtain-view.css' => '679743bb', '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-summary.css' => '9ca48bdf',
'rsrc/css/phui/phui-document.css' => 'c32e8dec', 'rsrc/css/phui/phui-document.css' => 'c32e8dec',
'rsrc/css/phui/phui-feed-story.css' => '44a9c8e9', '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-form.css' => 'a5570f70',
'rsrc/css/phui/phui-head-thing.css' => 'fd311e5f', 'rsrc/css/phui/phui-head-thing.css' => 'fd311e5f',
'rsrc/css/phui/phui-header-view.css' => 'e082678d', '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-set-selector.css' => '87db8fee',
'rsrc/css/phui/phui-icon.css' => '12b387a1', 'rsrc/css/phui/phui-icon.css' => '12b387a1',
'rsrc/css/phui/phui-image-mask.css' => 'a8498f9c', '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-object-selector.js' => 'e0ec7f2f',
'rsrc/js/core/behavior-oncopy.js' => '2926fff2', 'rsrc/js/core/behavior-oncopy.js' => '2926fff2',
'rsrc/js/core/behavior-phabricator-nav.js' => '08675c6d', '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-read-only-warning.js' => 'ba158207',
'rsrc/js/core/behavior-refresh-csrf.js' => 'ab2f381b', 'rsrc/js/core/behavior-refresh-csrf.js' => 'ab2f381b',
'rsrc/js/core/behavior-remarkup-preview.js' => '4b700e9e', '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/phui/behavior-phui-tab-group.js' => '0a0b10e9',
'rsrc/js/phuix/PHUIXActionListView.js' => 'b5c256b8', 'rsrc/js/phuix/PHUIXActionListView.js' => 'b5c256b8',
'rsrc/js/phuix/PHUIXActionView.js' => 'b3465b9b', '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/PHUIXDropdownMenu.js' => '8018ee50',
'rsrc/js/phuix/PHUIXFormControl.js' => '83e03671', 'rsrc/js/phuix/PHUIXFormControl.js' => '83e03671',
'rsrc/js/phuix/PHUIXIconView.js' => 'bff6884b', 'rsrc/js/phuix/PHUIXIconView.js' => 'bff6884b',
@ -549,7 +549,7 @@ return array(
'aphront-list-filter-view-css' => '5d6f0526', 'aphront-list-filter-view-css' => '5d6f0526',
'aphront-multi-column-view-css' => '84cc6640', 'aphront-multi-column-view-css' => '84cc6640',
'aphront-panel-view-css' => '8427b78d', 'aphront-panel-view-css' => '8427b78d',
'aphront-table-view-css' => '6ca8e057', 'aphront-table-view-css' => '34cf86b4',
'aphront-tokenizer-control-css' => '9a8cb501', 'aphront-tokenizer-control-css' => '9a8cb501',
'aphront-tooltip-css' => '173b9431', 'aphront-tooltip-css' => '173b9431',
'aphront-typeahead-control-css' => '8a84cc7d', 'aphront-typeahead-control-css' => '8a84cc7d',
@ -673,7 +673,7 @@ return array(
'javelin-behavior-phabricator-notification-example' => '8ce821c5', 'javelin-behavior-phabricator-notification-example' => '8ce821c5',
'javelin-behavior-phabricator-object-selector' => 'e0ec7f2f', 'javelin-behavior-phabricator-object-selector' => 'e0ec7f2f',
'javelin-behavior-phabricator-oncopy' => '2926fff2', '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-reveal-content' => '60821bc7',
'javelin-behavior-phabricator-search-typeahead' => 'eded9ee8', 'javelin-behavior-phabricator-search-typeahead' => 'eded9ee8',
'javelin-behavior-phabricator-show-older-transactions' => '94c65b72', 'javelin-behavior-phabricator-show-older-transactions' => '94c65b72',
@ -809,7 +809,7 @@ return array(
'phabricator-shaped-request' => '7cbe244b', 'phabricator-shaped-request' => '7cbe244b',
'phabricator-slowvote-css' => 'a94b7230', 'phabricator-slowvote-css' => 'a94b7230',
'phabricator-source-code-view-css' => '4383192f', 'phabricator-source-code-view-css' => '4383192f',
'phabricator-standard-page-view' => '89da5a9c', 'phabricator-standard-page-view' => 'eb5b80c5',
'phabricator-textareautils' => '320810c8', 'phabricator-textareautils' => '320810c8',
'phabricator-title' => '485aaa6c', 'phabricator-title' => '485aaa6c',
'phabricator-tooltip' => '8fadb715', 'phabricator-tooltip' => '8fadb715',
@ -824,7 +824,7 @@ return array(
'phabricator-uiexample-reactor-select' => 'a155550f', 'phabricator-uiexample-reactor-select' => 'a155550f',
'phabricator-uiexample-reactor-sendclass' => '1def2711', 'phabricator-uiexample-reactor-sendclass' => '1def2711',
'phabricator-uiexample-reactor-sendproperties' => 'b1f0ccee', 'phabricator-uiexample-reactor-sendproperties' => 'b1f0ccee',
'phabricator-zindex-css' => '5e72c4e0', 'phabricator-zindex-css' => '0233d039',
'phame-css' => 'b3a0b3a3', 'phame-css' => 'b3a0b3a3',
'pholio-css' => 'ca89d380', 'pholio-css' => 'ca89d380',
'pholio-edit-css' => '07676f51', 'pholio-edit-css' => '07676f51',
@ -853,7 +853,7 @@ return array(
'phui-curtain-view-css' => '679743bb', 'phui-curtain-view-css' => '679743bb',
'phui-document-summary-view-css' => '9ca48bdf', 'phui-document-summary-view-css' => '9ca48bdf',
'phui-document-view-css' => 'c32e8dec', 'phui-document-view-css' => 'c32e8dec',
'phui-document-view-pro-css' => 'f56738ed', 'phui-document-view-pro-css' => '62c4dcbf',
'phui-feed-story-css' => '44a9c8e9', 'phui-feed-story-css' => '44a9c8e9',
'phui-font-icon-base-css' => '870a7360', 'phui-font-icon-base-css' => '870a7360',
'phui-fontkit-css' => '1320ed01', 'phui-fontkit-css' => '1320ed01',
@ -862,7 +862,7 @@ return array(
'phui-head-thing-view-css' => 'fd311e5f', 'phui-head-thing-view-css' => 'fd311e5f',
'phui-header-view-css' => 'e082678d', 'phui-header-view-css' => 'e082678d',
'phui-hovercard' => '1bd28176', 'phui-hovercard' => '1bd28176',
'phui-hovercard-view-css' => 'ae091fc5', 'phui-hovercard-view-css' => 'f0592bcf',
'phui-icon-set-selector-css' => '87db8fee', 'phui-icon-set-selector-css' => '87db8fee',
'phui-icon-view-css' => '12b387a1', 'phui-icon-view-css' => '12b387a1',
'phui-image-mask-css' => 'a8498f9c', 'phui-image-mask-css' => 'a8498f9c',
@ -877,7 +877,7 @@ return array(
'phui-oi-color-css' => 'cd2b9b77', 'phui-oi-color-css' => 'cd2b9b77',
'phui-oi-drag-ui-css' => 'f12cbc9f', 'phui-oi-drag-ui-css' => 'f12cbc9f',
'phui-oi-flush-ui-css' => '9d9685d6', '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-oi-simple-ui-css' => 'a8beebea',
'phui-pager-css' => '77d8a794', 'phui-pager-css' => '77d8a794',
'phui-pinboard-view-css' => '2495140e', 'phui-pinboard-view-css' => '2495140e',
@ -896,7 +896,7 @@ return array(
'phui-workpanel-view-css' => 'a3a63478', 'phui-workpanel-view-css' => 'a3a63478',
'phuix-action-list-view' => 'b5c256b8', 'phuix-action-list-view' => 'b5c256b8',
'phuix-action-view' => 'b3465b9b', 'phuix-action-view' => 'b3465b9b',
'phuix-autocomplete' => 'd713a2c5', 'phuix-autocomplete' => 'f6699267',
'phuix-dropdown-menu' => '8018ee50', 'phuix-dropdown-menu' => '8018ee50',
'phuix-form-control-view' => '83e03671', 'phuix-form-control-view' => '83e03671',
'phuix-icon-view' => 'bff6884b', 'phuix-icon-view' => 'bff6884b',
@ -988,17 +988,6 @@ return array(
'javelin-dom', 'javelin-dom',
'javelin-router', 'javelin-router',
), ),
'0ca788bd' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
'phabricator-phtize',
'phabricator-textareautils',
'javelin-workflow',
'javelin-vector',
'phuix-autocomplete',
'javelin-mask',
),
'0f764c35' => array( '0f764c35' => array(
'javelin-install', 'javelin-install',
'javelin-util', 'javelin-util',
@ -1822,6 +1811,17 @@ return array(
'javelin-util', 'javelin-util',
'phabricator-busy', 'phabricator-busy',
), ),
'acd29eee' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
'phabricator-phtize',
'phabricator-textareautils',
'javelin-workflow',
'javelin-vector',
'phuix-autocomplete',
'javelin-mask',
),
'b003d4fb' => array( 'b003d4fb' => array(
'javelin-behavior', 'javelin-behavior',
'javelin-stratcom', 'javelin-stratcom',
@ -2087,12 +2087,6 @@ return array(
'javelin-json', 'javelin-json',
'phabricator-prefab', 'phabricator-prefab',
), ),
'd713a2c5' => array(
'javelin-install',
'javelin-dom',
'phuix-icon-view',
'phabricator-prefab',
),
'd7a74243' => array( 'd7a74243' => array(
'javelin-behavior', 'javelin-behavior',
'javelin-stratcom', 'javelin-stratcom',
@ -2220,6 +2214,12 @@ return array(
'javelin-util', 'javelin-util',
'javelin-reactor', 'javelin-reactor',
), ),
'f6699267' => array(
'javelin-install',
'javelin-dom',
'phuix-icon-view',
'phabricator-prefab',
),
'f7fc67ec' => array( 'f7fc67ec' => array(
'javelin-install', 'javelin-install',
'javelin-typeahead', 'javelin-typeahead',

View file

@ -1,34 +1,4 @@
<?php <?php
// Rebuild all Conpherence Room images to profile standards // This migration once resized room images for Conpherence, but the File table
// // later changed significantly. See T12628.
$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']);
}

View 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()));
}

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_owners.owners_package
DROP originalName;

File diff suppressed because one or more lines are too long

View file

@ -3204,6 +3204,7 @@ phutil_register_library_map(array(
'PhabricatorOwnersDefaultViewCapability' => 'applications/owners/capability/PhabricatorOwnersDefaultViewCapability.php', 'PhabricatorOwnersDefaultViewCapability' => 'applications/owners/capability/PhabricatorOwnersDefaultViewCapability.php',
'PhabricatorOwnersDetailController' => 'applications/owners/controller/PhabricatorOwnersDetailController.php', 'PhabricatorOwnersDetailController' => 'applications/owners/controller/PhabricatorOwnersDetailController.php',
'PhabricatorOwnersEditController' => 'applications/owners/controller/PhabricatorOwnersEditController.php', 'PhabricatorOwnersEditController' => 'applications/owners/controller/PhabricatorOwnersEditController.php',
'PhabricatorOwnersHovercardEngineExtension' => 'applications/owners/engineextension/PhabricatorOwnersHovercardEngineExtension.php',
'PhabricatorOwnersListController' => 'applications/owners/controller/PhabricatorOwnersListController.php', 'PhabricatorOwnersListController' => 'applications/owners/controller/PhabricatorOwnersListController.php',
'PhabricatorOwnersOwner' => 'applications/owners/storage/PhabricatorOwnersOwner.php', 'PhabricatorOwnersOwner' => 'applications/owners/storage/PhabricatorOwnersOwner.php',
'PhabricatorOwnersPackage' => 'applications/owners/storage/PhabricatorOwnersPackage.php', 'PhabricatorOwnersPackage' => 'applications/owners/storage/PhabricatorOwnersPackage.php',
@ -8406,6 +8407,7 @@ phutil_register_library_map(array(
'PhabricatorOwnersDefaultViewCapability' => 'PhabricatorPolicyCapability', 'PhabricatorOwnersDefaultViewCapability' => 'PhabricatorPolicyCapability',
'PhabricatorOwnersDetailController' => 'PhabricatorOwnersController', 'PhabricatorOwnersDetailController' => 'PhabricatorOwnersController',
'PhabricatorOwnersEditController' => 'PhabricatorOwnersController', 'PhabricatorOwnersEditController' => 'PhabricatorOwnersController',
'PhabricatorOwnersHovercardEngineExtension' => 'PhabricatorHovercardEngineExtension',
'PhabricatorOwnersListController' => 'PhabricatorOwnersController', 'PhabricatorOwnersListController' => 'PhabricatorOwnersController',
'PhabricatorOwnersOwner' => 'PhabricatorOwnersDAO', 'PhabricatorOwnersOwner' => 'PhabricatorOwnersDAO',
'PhabricatorOwnersPackage' => array( 'PhabricatorOwnersPackage' => array(

View file

@ -205,9 +205,8 @@ abstract class AphrontApplicationConfiguration extends Phobject {
DarkConsoleXHProfPluginAPI::saveProfilerSample($access_log); DarkConsoleXHProfPluginAPI::saveProfilerSample($access_log);
// Add points to the rate limits for this request. // Add points to the rate limits for this request.
if (isset($_SERVER['REMOTE_ADDR'])) { $rate_token = PhabricatorStartup::getRateLimitToken();
$user_ip = $_SERVER['REMOTE_ADDR']; if ($rate_token !== null) {
// The base score for a request allows users to make 30 requests per // The base score for a request allows users to make 30 requests per
// minute. // minute.
$score = (1000 / 30); $score = (1000 / 30);
@ -217,7 +216,7 @@ abstract class AphrontApplicationConfiguration extends Phobject {
$score = $score / 5; $score = $score / 5;
} }
PhabricatorStartup::addRateLimitScore($user_ip, $score); PhabricatorStartup::addRateLimitScore($rate_token, $score);
} }
if ($processing_exception) { if ($processing_exception) {

View file

@ -11,7 +11,7 @@ final class PhabricatorBadgesApplication extends PhabricatorApplication {
} }
public function getShortDescription() { public function getShortDescription() {
return pht('Achievements and Notority'); return pht('Achievements and Notoriety');
} }
public function getIcon() { public function getIcon() {

View file

@ -35,6 +35,7 @@ final class PhabricatorAccessLogConfigOptions
'P' => pht('The logged-in user PHID, if one is logged in.'), 'P' => pht('The logged-in user PHID, if one is logged in.'),
'i' => pht('Request input, in bytes.'), 'i' => pht('Request input, in bytes.'),
'o' => pht('Request output, in bytes.'), 'o' => pht('Request output, in bytes.'),
'I' => pht('Cluster instance name, if configured.'),
); );
$http_map = $common_map + array( $http_map = $common_map + array(

View file

@ -559,8 +559,7 @@ final class DiffusionRepositoryClusterEngine extends Phobject {
return false; return false;
} }
// TODO: For now, this is only supported for Git. if (!$repository->supportsSynchronization()) {
if (!$repository->isGit()) {
return false; return false;
} }

View file

@ -2,9 +2,33 @@
final class PhabricatorFileTransformTestCase extends PhabricatorTestCase { final class PhabricatorFileTransformTestCase extends PhabricatorTestCase {
protected function getPhabricatorTestCaseConfiguration() {
return array(
self::PHABRICATOR_TESTCONFIG_BUILD_STORAGE_FIXTURES => true,
);
}
public function testGetAllTransforms() { public function testGetAllTransforms() {
PhabricatorFileTransform::getAllTransforms(); PhabricatorFileTransform::getAllTransforms();
$this->assertTrue(true); $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);
}
}
} }

View file

@ -320,13 +320,16 @@ final class LegalpadDocumentSignController extends LegalpadController {
$crumbs->setBorder(true); $crumbs->setBorder(true);
$crumbs->addTextCrumb($document->getMonogram()); $crumbs->addTextCrumb($document->getMonogram());
$box = id(new PHUITwoColumnView())
->setFooter($signature_box);
return $this->newPage() return $this->newPage()
->setTitle($title) ->setTitle($title)
->setCrumbs($crumbs) ->setCrumbs($crumbs)
->setPageObjectPHIDs(array($document->getPHID())) ->setPageObjectPHIDs(array($document->getPHID()))
->appendChild(array( ->appendChild(array(
$content, $content,
$signature_box, $box,
)); ));
} }

View file

@ -24,24 +24,52 @@ final class ManiphestHovercardEngineExtension
$task, $task,
$data) { $data) {
$viewer = $this->getViewer(); $viewer = $this->getViewer();
require_celerity_resource('phui-workcard-view-css');
$hovercard $id = $task->getID();
->setTitle($task->getMonogram()) $task = id(new ManiphestTaskQuery())
->setDetail($task->getTitle()); ->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(); $owner_phid = $task->getOwnerPHID();
if ($owner_phid) { if ($owner_phid) {
$owner = $viewer->renderHandle($owner_phid); $owner_handle = $handles[$owner_phid];
} else { $card->setOwner($owner_handle);
$owner = phutil_tag('em', array(), pht('None'));
} }
$hovercard->addField(pht('Assigned To'), $owner);
$hovercard->addField( $project_phids = $task->getProjectPHIDs();
pht('Priority'), $project_handles = array_select_keys($handles, $project_phids);
ManiphestTaskPriority::getTaskPriorityName($task->getPriority())); 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));
} }
} }

View file

@ -323,6 +323,10 @@ final class PhabricatorOwnersDetailController
'wide', 'wide',
)); ));
if ($info) {
$table->setNotice($info);
}
$header = id(new PHUIHeaderView()) $header = id(new PHUIHeaderView())
->setHeader(pht('Paths')) ->setHeader(pht('Paths'))
->setHeaderIcon('fa-folder-open'); ->setHeaderIcon('fa-folder-open');
@ -332,10 +336,6 @@ final class PhabricatorOwnersDetailController
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setTable($table); ->setTable($table);
if ($info) {
$box->setInfoView($info);
}
return $box; return $box;
} }

View file

@ -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);
}
}
}

View file

@ -12,7 +12,6 @@ final class PhabricatorOwnersPackage
PhabricatorNgramsInterface { PhabricatorNgramsInterface {
protected $name; protected $name;
protected $originalName;
protected $auditingEnabled; protected $auditingEnabled;
protected $autoReview; protected $autoReview;
protected $description; protected $description;
@ -105,8 +104,7 @@ final class PhabricatorOwnersPackage
self::CONFIG_TIMESTAMPS => false, self::CONFIG_TIMESTAMPS => false,
self::CONFIG_AUX_PHID => true, self::CONFIG_AUX_PHID => true,
self::CONFIG_COLUMN_SCHEMA => array( self::CONFIG_COLUMN_SCHEMA => array(
'name' => 'sort128', 'name' => 'sort',
'originalName' => 'text255',
'description' => 'text', 'description' => 'text',
'primaryOwnerPHID' => 'phid?', 'primaryOwnerPHID' => 'phid?',
'auditingEnabled' => 'bool', 'auditingEnabled' => 'bool',
@ -137,9 +135,6 @@ final class PhabricatorOwnersPackage
public function setName($name) { public function setName($name) {
$this->name = $name; $this->name = $name;
if (!$this->getID()) {
$this->originalName = $name;
}
return $this; return $this;
} }

View file

@ -540,6 +540,14 @@ final class PhabricatorUserEditor extends PhabricatorEditor {
$email->setIsPrimary(1); $email->setIsPrimary(1);
$email->save(); $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( $log = PhabricatorUserLog::initializeNewLog(
$actor, $actor,
$user->getPHID(), $user->getPHID(),

View file

@ -1929,10 +1929,31 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
'Cluster hosts must correctly route their intracluster requests.')); '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); shuffle($results);
return head($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() { public function getAlmanacServiceCacheKey() {
$service_phid = $this->getAlmanacServicePHID(); $service_phid = $this->getAlmanacServicePHID();
if (!$service_phid) { if (!$service_phid) {

View file

@ -1,8 +1,7 @@
@title Configuring a Preamble Script @title Configuring a Preamble Script
@group config @group config
Adjust environmental settings (SSL, remote IP, rate limiting) using a preamble Adjust environmental settings (SSL, remote IPs) using a preamble script.
script.
Overview Overview
======== ========

View file

@ -30,6 +30,7 @@ final class PhabricatorAccessLog extends Phobject {
'h' => php_uname('n'), 'h' => php_uname('n'),
'p' => getmypid(), 'p' => getmypid(),
'e' => time(), 'e' => time(),
'I' => PhabricatorEnv::getEnvConfig('cluster.instance'),
)); ));
self::$log = $log; self::$log = $log;

View file

@ -20,6 +20,7 @@ final class PhabricatorSSHLog extends Phobject {
'h' => php_uname('n'), 'h' => php_uname('n'),
'p' => getmypid(), 'p' => getmypid(),
'e' => time(), 'e' => time(),
'I' => PhabricatorEnv::getEnvConfig('cluster.instance'),
); );
$sudo_user = PhabricatorEnv::getEnvConfig('phd.user'); $sudo_user = PhabricatorEnv::getEnvConfig('phd.user');

View file

@ -112,14 +112,17 @@ final class PHUIHovercardView extends AphrontTagView {
$body = array(); $body = array();
$body_title = null;
if ($this->detail) { if ($this->detail) {
$body_title = $this->detail; $body_title = $this->detail;
} else { } else if (!$this->fields) {
// Fallback for object handles // Fallback for object handles
$body_title = $handle->getFullName(); $body_title = $handle->getFullName();
} }
if ($body_title) {
$body[] = phutil_tag_div('phui-hovercard-body-header', $body_title); $body[] = phutil_tag_div('phui-hovercard-body-header', $body_title);
}
foreach ($this->fields as $field) { foreach ($this->fields as $field) {
$item = array( $item = array(

View file

@ -50,6 +50,7 @@ final class PhabricatorStartup {
// iterate on it a bit for Conduit, some of the specific score levels, and // iterate on it a bit for Conduit, some of the specific score levels, and
// to deal with NAT'd offices. // to deal with NAT'd offices.
private static $maximumRate = 0; private static $maximumRate = 0;
private static $rateLimitToken;
/* -( Accessing Request Information )-------------------------------------- */ /* -( Accessing Request Information )-------------------------------------- */
@ -137,8 +138,9 @@ final class PhabricatorStartup {
// we can switch over to relying on our own exception recovery mechanisms. // we can switch over to relying on our own exception recovery mechanisms.
ini_set('display_errors', 0); ini_set('display_errors', 0);
if (isset($_SERVER['REMOTE_ADDR'])) { $rate_token = self::getRateLimitToken();
self::rateLimitRequest($_SERVER['REMOTE_ADDR']); if ($rate_token !== null) {
self::rateLimitRequest($rate_token);
} }
self::normalizeInput(); 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 * 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. * 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); $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 // Give the user some bonus points for getting rate limited. This keeps
// bad actors who keep slamming the 429 page locked out completely, // bad actors who keep slamming the 429 page locked out completely,
// instead of letting them get a burst of requests through every minute // instead of letting them get a burst of requests through every minute
// after a bucket expires. // after a bucket expires.
self::addRateLimitScore($user_identity, 50); $penalty = 50;
self::didRateLimit($user_identity);
self::addRateLimitScore($user_identity, $penalty);
$score += $penalty;
self::didRateLimit($user_identity, $score, $limit);
} }
} }
@ -729,15 +766,21 @@ final class PhabricatorStartup {
return; return;
} }
$is_apcu = (bool)function_exists('apcu_fetch');
$current = self::getRateLimitBucket(); $current = self::getRateLimitBucket();
// There's a bit of a race here, if a second process reads the bucket before // There's a bit of a race here, if a second process reads the bucket
// this one writes it, but it's fine if we occasionally fail to record a // before this one writes it, but it's fine if we occasionally fail to
// user's score. If they're making requests fast enough to hit rate // record a user's score. If they're making requests fast enough to hit
// limiting, we'll get them soon. // rate limiting, we'll get them soon enough.
$bucket_key = self::getRateLimitBucketKey($current); $bucket_key = self::getRateLimitBucketKey($current);
if ($is_apcu) {
$bucket = apcu_fetch($bucket_key);
} else {
$bucket = apc_fetch($bucket_key); $bucket = apc_fetch($bucket_key);
}
if (!is_array($bucket)) { if (!is_array($bucket)) {
$bucket = array(); $bucket = array();
} }
@ -747,8 +790,13 @@ final class PhabricatorStartup {
} }
$bucket[$user_identity] += $score; $bucket[$user_identity] += $score;
if ($is_apcu) {
apcu_store($bucket_key, $bucket);
} else {
apc_store($bucket_key, $bucket); apc_store($bucket_key, $bucket);
} }
}
/** /**
@ -761,11 +809,12 @@ final class PhabricatorStartup {
* @task ratelimit * @task ratelimit
*/ */
private static function canRateLimit() { private static function canRateLimit() {
if (!self::$maximumRate) { if (!self::$maximumRate) {
return false; return false;
} }
if (!function_exists('apc_fetch')) { if (!function_exists('apc_fetch') && !function_exists('apcu_fetch')) {
return false; return false;
} }
@ -826,16 +875,26 @@ final class PhabricatorStartup {
* @task ratelimit * @task ratelimit
*/ */
private static function getRateLimitScore($user_identity) { private static function getRateLimitScore($user_identity) {
$is_apcu = (bool)function_exists('apcu_fetch');
$min_key = self::getRateLimitMinKey(); $min_key = self::getRateLimitMinKey();
// Identify the oldest bucket stored in APC. // Identify the oldest bucket stored in APC.
$cur = self::getRateLimitBucket(); $cur = self::getRateLimitBucket();
if ($is_apcu) {
$min = apcu_fetch($min_key);
} else {
$min = apc_fetch($min_key); $min = apc_fetch($min_key);
}
// If we don't have any buckets stored yet, store the current bucket as // If we don't have any buckets stored yet, store the current bucket as
// the oldest bucket. // the oldest bucket.
if (!$min) { if (!$min) {
if ($is_apcu) {
apcu_store($min_key, $cur);
} else {
apc_store($min_key, $cur); apc_store($min_key, $cur);
}
$min = $cur; $min = $cur;
} }
@ -844,14 +903,25 @@ final class PhabricatorStartup {
// up an old bucket once per minute. // up an old bucket once per minute.
$count = self::getRateLimitBucketCount(); $count = self::getRateLimitBucketCount();
for ($cursor = $min; $cursor < ($cur - $count); $cursor++) { 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); apc_store($min_key, $cursor + 1);
} }
}
// Now, sum up the user's scores in all of the active buckets. // Now, sum up the user's scores in all of the active buckets.
$score = 0; $score = 0;
for (; $cursor <= $cur; $cursor++) { 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])) { if (isset($bucket[$user_identity])) {
$score += $bucket[$user_identity]; $score += $bucket[$user_identity];
} }
@ -868,12 +938,11 @@ final class PhabricatorStartup {
* @return exit This method **does not return**. * @return exit This method **does not return**.
* @task ratelimit * @task ratelimit
*/ */
private static function didRateLimit() { private static function didRateLimit($user_identity, $score, $limit) {
$message = $message =
"TOO MANY REQUESTS\n". "TOO MANY REQUESTS\n".
"You are issuing too many requests too quickly.\n". "You (\"{$user_identity}\") are issuing too many requests ".
"To adjust limits, see \"Configuring a Preamble Script\" in the ". "too quickly.\n";
"documentation.";
header( header(
'Content-Type: text/plain; charset=utf-8', 'Content-Type: text/plain; charset=utf-8',

View file

@ -29,6 +29,10 @@
border-bottom: 1px solid {$thinblueborder}; border-bottom: 1px solid {$thinblueborder};
} }
.phui-two-column-view .aphront-table-notice .phui-info-view {
margin: 0;
}
.aphront-table-view tr.alt { .aphront-table-view tr.alt {
background: {$lightgreybackground}; background: {$lightgreybackground};
} }

View file

@ -61,9 +61,10 @@ body.white-background {
} }
.keyboard-focus-focus-reticle { .keyboard-focus-focus-reticle {
background: #ffffd3; background: rgba(255, 255, 211, 0.15);
position: absolute; position: absolute;
border: 1px solid #999900; border: 1px solid {$yellow};
pointer-events: none;
} }
a.handle-status-closed { a.handle-status-closed {

View file

@ -2,16 +2,16 @@
* @provides phabricator-zindex-css * @provides phabricator-zindex-css
*/ */
.keyboard-focus-focus-reticle {
z-index: 1;
}
.device .phabricator-action-list-view.phabricator-action-list-toggle, .device .phabricator-action-list-view.phabricator-action-list-toggle,
.device-desktop .phui-document-content .device-desktop .phui-document-content
.phabricator-action-list-view.phabricator-action-list-toggle { .phabricator-action-list-view.phabricator-action-list-toggle {
z-index: 1; z-index: 1;
} }
.keyboard-focus-focus-reticle {
z-index: 2;
}
.device-desktop .phui-timeline-minor-event .phui-timeline-image { .device-desktop .phui-timeline-minor-event .phui-timeline-image {
z-index: 2; z-index: 2;
} }

View file

@ -302,6 +302,7 @@ ul.phui-oi-list-view {
display: inline-block; display: inline-block;
color: {$greytext}; color: {$greytext};
vertical-align: top; vertical-align: top;
margin-right: 4px;
} }
.phui-oi-attribute-spacer { .phui-oi-attribute-spacer {

View file

@ -74,7 +74,7 @@ body.printable {
a.button.phui-document-toc { a.button.phui-document-toc {
display: inline-block; display: inline-block;
height: 16px; height: 16px;
width: 16px; width: 20px;
padding: 3px 8px 4px 8px; padding: 3px 8px 4px 8px;
} }
@ -88,7 +88,7 @@ a.button.phui-document-toc {
z-index: 30; z-index: 30;
background-color: #fff; background-color: #fff;
top: 52px; top: 52px;
left: -44px; left: -40px;
} }
.device .phui-document-view-pro .phui-document-toc { .device .phui-document-view-pro .phui-document-toc {

View file

@ -105,3 +105,11 @@
.phui-hovercard-tail a.button { .phui-hovercard-tail a.button {
margin: 3px; 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;
}

View file

@ -393,6 +393,12 @@ JX.behavior('phabricator-remarkup-assist', function(config) {
return; return;
} }
// Let other listeners (particularly the inline autocomplete) have a
// chance to handle this event.
if (JX.Stratcom.pass()) {
return;
}
var raw = e.getRawEvent(); var raw = e.getRawEvent();
if (raw.shiftKey) { if (raw.shiftKey) {
// If the shift key is pressed, let the browser write a newline into // 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. // This allows 'workflow' and similar actions to take effect.
// Such as pontificate in Conpherence // Such as pontificate in Conpherence
var form = e.getNode('tag:form'); var form = e.getNode('tag:form');
var r = JX.DOM.invoke(form, 'didSyntheticSubmit'); JX.DOM.invoke(form, 'didSyntheticSubmit');
}); });
} }

View file

@ -127,7 +127,7 @@ JX.install('PHUIXAutocomplete', {
} }
// Get all the text on the current line. If the line only contains // 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. // numbered list.
var line = area.value.substring(0, head - 1); var line = area.value.substring(0, head - 1);
line = line.split('\n'); line = line.split('\n');
@ -454,7 +454,7 @@ JX.install('PHUIXAutocomplete', {
// If the user hasn't typed any text yet after typing the character // If the user hasn't typed any text yet after typing the character
// which can summon the autocomplete, deactivate and let the keystroke // 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. // autocomplete character and the user is trying to type a newline.
if (range.start == this._cursorHead) { if (range.start == this._cursorHead) {
this._deactivate(); 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(); var ignore = this._getIgnoreList();
for (ii = 0; ii < ignore.length; ii++) { for (ii = 0; ii < ignore.length; ii++) {
if (trim.indexOf(ignore[ii]) === 0) { if (text.indexOf(ignore[ii]) === 0) {
this._deactivate(); this._deactivate();
return; return;
} }