1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-28 17:52:43 +01:00

(stable) Promote 2016 Week 23

This commit is contained in:
epriestley 2016-06-03 14:34:06 -07:00
commit 03403ca8b0
119 changed files with 3027 additions and 360 deletions

View file

@ -10,7 +10,7 @@ return array(
'core.pkg.css' => '8aeacc63',
'core.pkg.js' => '3f15fa62',
'darkconsole.pkg.js' => 'e7393ebb',
'differential.pkg.css' => '33da0633',
'differential.pkg.css' => 'a3a7e5df',
'differential.pkg.js' => '4b7d8f19',
'diffusion.pkg.css' => '91c5d3a6',
'diffusion.pkg.js' => '3a9a8bfa',
@ -57,7 +57,7 @@ return array(
'rsrc/css/application/dashboard/dashboard.css' => 'bc6f2127',
'rsrc/css/application/diff/inline-comment-summary.css' => '51efda3a',
'rsrc/css/application/differential/add-comment.css' => 'c47f8c40',
'rsrc/css/application/differential/changeset-view.css' => '7bcbe615',
'rsrc/css/application/differential/changeset-view.css' => 'febd2372',
'rsrc/css/application/differential/core.css' => '5b7b8ff4',
'rsrc/css/application/differential/phui-inline-comment.css' => '5953c28e',
'rsrc/css/application/differential/revision-comment.css' => '14b8565a',
@ -81,7 +81,7 @@ return array(
'rsrc/css/application/owners/owners-path-editor.css' => '2f00933b',
'rsrc/css/application/paste/paste.css' => '1898e534',
'rsrc/css/application/people/people-profile.css' => '2473d929',
'rsrc/css/application/phame/phame.css' => '737792d6',
'rsrc/css/application/phame/phame.css' => '7448a969',
'rsrc/css/application/pholio/pholio-edit.css' => 'b15fec4a',
'rsrc/css/application/pholio/pholio-inline-comments.css' => '8e545e49',
'rsrc/css/application/pholio/pholio.css' => 'ca89d380',
@ -116,7 +116,7 @@ return array(
'rsrc/css/layout/phabricator-side-menu-view.css' => 'dd849797',
'rsrc/css/layout/phabricator-source-code-view.css' => 'cbeef983',
'rsrc/css/phui/calendar/phui-calendar-day.css' => 'd1cf6f93',
'rsrc/css/phui/calendar/phui-calendar-list.css' => 'e0866209',
'rsrc/css/phui/calendar/phui-calendar-list.css' => '56e6381a',
'rsrc/css/phui/calendar/phui-calendar-month.css' => '476be7e0',
'rsrc/css/phui/calendar/phui-calendar.css' => 'ccabe893',
'rsrc/css/phui/phui-action-list.css' => 'c5eba19d',
@ -156,7 +156,7 @@ return array(
'rsrc/css/phui/phui-status.css' => 'd5263e49',
'rsrc/css/phui/phui-tag-view.css' => '6bbd83e2',
'rsrc/css/phui/phui-timeline-view.css' => '6e342216',
'rsrc/css/phui/phui-two-column-view.css' => 'b9538af1',
'rsrc/css/phui/phui-two-column-view.css' => '9fb86c85',
'rsrc/css/phui/workboards/phui-workboard-color.css' => 'ac6fe6a7',
'rsrc/css/phui/workboards/phui-workboard.css' => 'e6d89647',
'rsrc/css/phui/workboards/phui-workcard.css' => '0c62d7c5',
@ -552,7 +552,7 @@ return array(
'conpherence-update-css' => 'faf6be09',
'conpherence-widget-pane-css' => '775eaaba',
'd3' => 'a11a5ff2',
'differential-changeset-view-css' => '7bcbe615',
'differential-changeset-view-css' => 'febd2372',
'differential-core-view-css' => '5b7b8ff4',
'differential-inline-comment-editor' => '64a5550f',
'differential-revision-add-comment-css' => 'c47f8c40',
@ -806,7 +806,7 @@ return array(
'phabricator-uiexample-reactor-sendclass' => '1def2711',
'phabricator-uiexample-reactor-sendproperties' => 'b1f0ccee',
'phabricator-zindex-css' => '5b6fcf3f',
'phame-css' => '737792d6',
'phame-css' => '7448a969',
'pholio-css' => 'ca89d380',
'pholio-edit-css' => 'b15fec4a',
'pholio-inline-comments-css' => '8e545e49',
@ -822,7 +822,7 @@ return array(
'phui-button-css' => 'a64a8de6',
'phui-calendar-css' => 'ccabe893',
'phui-calendar-day-css' => 'd1cf6f93',
'phui-calendar-list-css' => 'e0866209',
'phui-calendar-list-css' => '56e6381a',
'phui-calendar-month-css' => '476be7e0',
'phui-chart-css' => '6bf6f78e',
'phui-crumbs-view-css' => '6b813619',
@ -859,7 +859,7 @@ return array(
'phui-tag-view-css' => '6bbd83e2',
'phui-theme-css' => '027ba77e',
'phui-timeline-view-css' => '6e342216',
'phui-two-column-view-css' => 'b9538af1',
'phui-two-column-view-css' => '9fb86c85',
'phui-workboard-color-css' => 'ac6fe6a7',
'phui-workboard-view-css' => 'e6d89647',
'phui-workcard-view-css' => '0c62d7c5',
@ -1513,9 +1513,6 @@ return array(
'javelin-stratcom',
'javelin-util',
),
'7bcbe615' => array(
'phui-inline-comment-view-css',
),
'7cbe244b' => array(
'javelin-install',
'javelin-util',
@ -2203,6 +2200,9 @@ return array(
'fea0eb47' => array(
'javelin-install',
),
'febd2372' => array(
'phui-inline-comment-view-css',
),
),
'packages' => array(
'core.pkg.css' => array(

View file

@ -74,35 +74,9 @@ foreach ($applications as $application) {
/* -( User preferences )--------------------------------------------------- */
echo pht('Migrating user preferences...')."\n";
$table = new PhabricatorUserPreferences();
$conn_w = $table->establishConnection('w');
$pref_pinned = PhabricatorUserPreferences::PREFERENCE_APP_PINNED;
foreach (new LiskMigrationIterator(new PhabricatorUser()) as $user) {
$user_preferences = $user->loadPreferences();
$old_pinned_apps = $user_preferences->getPreference($pref_pinned);
$new_pinned_apps = array();
if (!$old_pinned_apps) {
continue;
}
foreach ($old_pinned_apps as $pinned_app) {
$new_pinned_apps[] = idx($map, $pinned_app, $pinned_app);
}
$user_preferences
->setPreference($pref_pinned, $new_pinned_apps);
queryfx(
$conn_w,
'UPDATE %T SET preferences = %s WHERE id = %d',
$user_preferences->getTableName(),
json_encode($user_preferences->getPreferences()),
$user_preferences->getID());
}
// This originally migrated pinned applications in user preferences, but was
// removed to simplify preference changes after about 22 months.
/* -( Dashboard installs )------------------------------------------------- */

View file

@ -0,0 +1,19 @@
CREATE TABLE {$NAMESPACE}_user.user_preferencestransaction (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
phid VARBINARY(64) NOT NULL,
authorPHID VARBINARY(64) NOT NULL,
objectPHID VARBINARY(64) NOT NULL,
viewPolicy VARBINARY(64) NOT NULL,
editPolicy VARBINARY(64) NOT NULL,
commentPHID VARBINARY(64) DEFAULT NULL,
commentVersion INT UNSIGNED NOT NULL,
transactionType VARCHAR(32) COLLATE {$COLLATE_TEXT} NOT NULL,
oldValue LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
newValue LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
contentSource LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
metadata LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
dateCreated INT UNSIGNED NOT NULL,
dateModified INT UNSIGNED NOT NULL,
UNIQUE KEY `key_phid` (`phid`),
KEY `key_object` (`objectPHID`)
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_user.user_preferences
ADD dateCreated INT UNSIGNED NOT NULL;

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_user.user_preferences
ADD dateModified INT UNSIGNED NOT NULL;

View file

@ -0,0 +1,2 @@
UPDATE {$NAMESPACE}_user.user_preferences
SET dateCreated = UNIX_TIMESTAMP() WHERE dateCreated = 0;

View file

@ -0,0 +1,2 @@
UPDATE {$NAMESPACE}_user.user_preferences
SET dateModified = UNIX_TIMESTAMP() WHERE dateModified = 0;

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_user.user_preferences
ADD phid VARBINARY(64) NOT NULL;

View file

@ -0,0 +1,17 @@
<?php
$table = new PhabricatorUserPreferences();
$conn_w = $table->establishConnection('w');
foreach (new LiskMigrationIterator($table) as $row) {
if ($row->getPHID() !== '') {
continue;
}
queryfx(
$conn_w,
'UPDATE %T SET phid = %s WHERE id = %d',
$table->getTableName(),
$table->generatePHID(),
$row->getID());
}

View file

@ -0,0 +1,11 @@
CREATE TABLE {$NAMESPACE}_user.user_cache (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
userPHID VARBINARY(64) NOT NULL,
cacheIndex BINARY(12) NOT NULL,
cacheKey VARCHAR(255) NOT NULL COLLATE {$COLLATE_TEXT},
cacheData LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT},
cacheType VARCHAR(32) NOT NULL COLLATE {$COLLATE_TEXT},
UNIQUE KEY `key_usercache` (userPHID, cacheIndex),
KEY `key_cachekey` (cacheIndex),
KEY `key_cachetype` (cacheType)
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};

View file

@ -0,0 +1,59 @@
<?php
// Move timezone, translation and pronoun from the user object to preferences
// so they can be defaulted and edited like other settings.
$table = new PhabricatorUser();
$conn_w = $table->establishConnection('w');
$table_name = $table->getTableName();
$prefs_table = new PhabricatorUserPreferences();
foreach (new LiskRawMigrationIterator($conn_w, $table_name) as $row) {
$phid = $row['phid'];
$pref_row = queryfx_one(
$conn_w,
'SELECT preferences FROM %T WHERE userPHID = %s',
$prefs_table->getTableName(),
$phid);
if ($pref_row) {
try {
$prefs = phutil_json_decode($pref_row['preferences']);
} catch (Exception $ex) {
$prefs = array();
}
} else {
$prefs = array();
}
$zone = $row['timezoneIdentifier'];
if (strlen($zone)) {
$prefs[PhabricatorTimezoneSetting::SETTINGKEY] = $zone;
}
$pronoun = $row['sex'];
if (strlen($pronoun)) {
$prefs[PhabricatorPronounSetting::SETTINGKEY] = $pronoun;
}
$translation = $row['translation'];
if (strlen($translation)) {
$prefs[PhabricatorTranslationSetting::SETTINGKEY] = $translation;
}
if ($prefs) {
queryfx(
$conn_w,
'INSERT INTO %T (phid, userPHID, preferences, dateModified, dateCreated)
VALUES (%s, %s, %s, UNIX_TIMESTAMP(), UNIX_TIMESTAMP())
ON DUPLICATE KEY UPDATE preferences = VALUES(preferences)',
$prefs_table->getTableName(),
$prefs_table->generatePHID(),
$phid,
phutil_json_encode($prefs));
}
}
$prefs_key = PhabricatorUserPreferencesCacheType::KEY_PREFERENCES;
PhabricatorUserCache::clearCacheForAllUsers($prefs_key);

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_user.user
DROP COLUMN timezoneIdentifier;

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_user.user
DROP COLUMN translation;

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_user.user
DROP COLUMN sex;

View file

@ -1711,6 +1711,7 @@ phutil_register_library_map(array(
'PhabricatorAccessControlTestCase' => 'applications/base/controller/__tests__/PhabricatorAccessControlTestCase.php',
'PhabricatorAccessLog' => 'infrastructure/log/PhabricatorAccessLog.php',
'PhabricatorAccessLogConfigOptions' => 'applications/config/option/PhabricatorAccessLogConfigOptions.php',
'PhabricatorAccessibilitySetting' => 'applications/settings/setting/PhabricatorAccessibilitySetting.php',
'PhabricatorAccountSettingsPanel' => 'applications/settings/panel/PhabricatorAccountSettingsPanel.php',
'PhabricatorActionListView' => 'view/layout/PhabricatorActionListView.php',
'PhabricatorActionView' => 'view/layout/PhabricatorActionView.php',
@ -2238,6 +2239,7 @@ phutil_register_library_map(array(
'PhabricatorDaemonsApplication' => 'applications/daemon/application/PhabricatorDaemonsApplication.php',
'PhabricatorDaemonsSetupCheck' => 'applications/config/check/PhabricatorDaemonsSetupCheck.php',
'PhabricatorDailyRoutineTriggerClock' => 'infrastructure/daemon/workers/clock/PhabricatorDailyRoutineTriggerClock.php',
'PhabricatorDarkConsoleSetting' => 'applications/settings/setting/PhabricatorDarkConsoleSetting.php',
'PhabricatorDashboard' => 'applications/dashboard/storage/PhabricatorDashboard.php',
'PhabricatorDashboardAddPanelController' => 'applications/dashboard/controller/PhabricatorDashboardAddPanelController.php',
'PhabricatorDashboardApplication' => 'applications/dashboard/application/PhabricatorDashboardApplication.php',
@ -2295,6 +2297,7 @@ phutil_register_library_map(array(
'PhabricatorDatabaseSetupCheck' => 'applications/config/check/PhabricatorDatabaseSetupCheck.php',
'PhabricatorDatasourceEditField' => 'applications/transactions/editfield/PhabricatorDatasourceEditField.php',
'PhabricatorDatasourceEditType' => 'applications/transactions/edittype/PhabricatorDatasourceEditType.php',
'PhabricatorDateFormatSetting' => 'applications/settings/setting/PhabricatorDateFormatSetting.php',
'PhabricatorDateTimeSettingsPanel' => 'applications/settings/panel/PhabricatorDateTimeSettingsPanel.php',
'PhabricatorDebugController' => 'applications/system/controller/PhabricatorDebugController.php',
'PhabricatorDefaultRequestExceptionHandler' => 'aphront/handler/PhabricatorDefaultRequestExceptionHandler.php',
@ -2373,13 +2376,20 @@ phutil_register_library_map(array(
'PhabricatorEditPage' => 'applications/transactions/editengine/PhabricatorEditPage.php',
'PhabricatorEditType' => 'applications/transactions/edittype/PhabricatorEditType.php',
'PhabricatorEditor' => 'infrastructure/PhabricatorEditor.php',
'PhabricatorEditorMultipleSetting' => 'applications/settings/setting/PhabricatorEditorMultipleSetting.php',
'PhabricatorEditorSetting' => 'applications/settings/setting/PhabricatorEditorSetting.php',
'PhabricatorElasticFulltextStorageEngine' => 'applications/search/fulltextstorage/PhabricatorElasticFulltextStorageEngine.php',
'PhabricatorElasticSearchSetupCheck' => 'applications/config/check/PhabricatorElasticSearchSetupCheck.php',
'PhabricatorEmailAddressesSettingsPanel' => 'applications/settings/panel/PhabricatorEmailAddressesSettingsPanel.php',
'PhabricatorEmailContentSource' => 'applications/metamta/contentsource/PhabricatorEmailContentSource.php',
'PhabricatorEmailFormatSetting' => 'applications/settings/setting/PhabricatorEmailFormatSetting.php',
'PhabricatorEmailFormatSettingsPanel' => 'applications/settings/panel/PhabricatorEmailFormatSettingsPanel.php',
'PhabricatorEmailLoginController' => 'applications/auth/controller/PhabricatorEmailLoginController.php',
'PhabricatorEmailNotificationsSetting' => 'applications/settings/setting/PhabricatorEmailNotificationsSetting.php',
'PhabricatorEmailPreferencesSettingsPanel' => 'applications/settings/panel/PhabricatorEmailPreferencesSettingsPanel.php',
'PhabricatorEmailRePrefixSetting' => 'applications/settings/setting/PhabricatorEmailRePrefixSetting.php',
'PhabricatorEmailSelfActionsSetting' => 'applications/settings/setting/PhabricatorEmailSelfActionsSetting.php',
'PhabricatorEmailVarySubjectsSetting' => 'applications/settings/setting/PhabricatorEmailVarySubjectsSetting.php',
'PhabricatorEmailVerificationController' => 'applications/auth/controller/PhabricatorEmailVerificationController.php',
'PhabricatorEmbedFileRemarkupRule' => 'applications/files/markup/PhabricatorEmbedFileRemarkupRule.php',
'PhabricatorEmojiRemarkupRule' => 'applications/macro/markup/PhabricatorEmojiRemarkupRule.php',
@ -2732,6 +2742,8 @@ phutil_register_library_map(array(
'PhabricatorMetaMTASendGridReceiveController' => 'applications/metamta/controller/PhabricatorMetaMTASendGridReceiveController.php',
'PhabricatorMetaMTAWorker' => 'applications/metamta/PhabricatorMetaMTAWorker.php',
'PhabricatorMetronomicTriggerClock' => 'infrastructure/daemon/workers/clock/PhabricatorMetronomicTriggerClock.php',
'PhabricatorMonospacedFontSetting' => 'applications/settings/setting/PhabricatorMonospacedFontSetting.php',
'PhabricatorMonospacedTextareasSetting' => 'applications/settings/setting/PhabricatorMonospacedTextareasSetting.php',
'PhabricatorMotivatorProfilePanel' => 'applications/search/profilepanel/PhabricatorMotivatorProfilePanel.php',
'PhabricatorMultiColumnUIExample' => 'applications/uiexample/examples/PhabricatorMultiColumnUIExample.php',
'PhabricatorMultiFactorSettingsPanel' => 'applications/settings/panel/PhabricatorMultiFactorSettingsPanel.php',
@ -2822,8 +2834,10 @@ phutil_register_library_map(array(
'PhabricatorObjectSelectorDialog' => 'view/control/PhabricatorObjectSelectorDialog.php',
'PhabricatorOffsetPagedQuery' => 'infrastructure/query/PhabricatorOffsetPagedQuery.php',
'PhabricatorOldWorldContentSource' => 'infrastructure/contentsource/PhabricatorOldWorldContentSource.php',
'PhabricatorOlderInlinesSetting' => 'applications/settings/setting/PhabricatorOlderInlinesSetting.php',
'PhabricatorOneTimeTriggerClock' => 'infrastructure/daemon/workers/clock/PhabricatorOneTimeTriggerClock.php',
'PhabricatorOpcodeCacheSpec' => 'applications/cache/spec/PhabricatorOpcodeCacheSpec.php',
'PhabricatorOptionGroupSetting' => 'applications/settings/setting/PhabricatorOptionGroupSetting.php',
'PhabricatorOwnerPathQuery' => 'applications/owners/query/PhabricatorOwnerPathQuery.php',
'PhabricatorOwnersApplication' => 'applications/owners/application/PhabricatorOwnersApplication.php',
'PhabricatorOwnersArchiveController' => 'applications/owners/controller/PhabricatorOwnersArchiveController.php',
@ -3155,6 +3169,7 @@ phutil_register_library_map(array(
'PhabricatorProjectsSearchEngineAttachment' => 'applications/project/engineextension/PhabricatorProjectsSearchEngineAttachment.php',
'PhabricatorProjectsSearchEngineExtension' => 'applications/project/engineextension/PhabricatorProjectsSearchEngineExtension.php',
'PhabricatorProjectsWatchersSearchEngineAttachment' => 'applications/project/engineextension/PhabricatorProjectsWatchersSearchEngineAttachment.php',
'PhabricatorPronounSetting' => 'applications/settings/setting/PhabricatorPronounSetting.php',
'PhabricatorProtocolAdapter' => 'infrastructure/daemon/bot/adapter/PhabricatorProtocolAdapter.php',
'PhabricatorPygmentSetupCheck' => 'applications/config/check/PhabricatorPygmentSetupCheck.php',
'PhabricatorQuery' => 'infrastructure/query/PhabricatorQuery.php',
@ -3348,11 +3363,15 @@ phutil_register_library_map(array(
'PhabricatorSecurityConfigOptions' => 'applications/config/option/PhabricatorSecurityConfigOptions.php',
'PhabricatorSecuritySetupCheck' => 'applications/config/check/PhabricatorSecuritySetupCheck.php',
'PhabricatorSelectEditField' => 'applications/transactions/editfield/PhabricatorSelectEditField.php',
'PhabricatorSelectSetting' => 'applications/settings/setting/PhabricatorSelectSetting.php',
'PhabricatorSendGridConfigOptions' => 'applications/config/option/PhabricatorSendGridConfigOptions.php',
'PhabricatorSessionsSettingsPanel' => 'applications/settings/panel/PhabricatorSessionsSettingsPanel.php',
'PhabricatorSetting' => 'applications/settings/setting/PhabricatorSetting.php',
'PhabricatorSettingsAddEmailAction' => 'applications/settings/action/PhabricatorSettingsAddEmailAction.php',
'PhabricatorSettingsAdjustController' => 'applications/settings/controller/PhabricatorSettingsAdjustController.php',
'PhabricatorSettingsApplication' => 'applications/settings/application/PhabricatorSettingsApplication.php',
'PhabricatorSettingsEditController' => 'applications/settings/controller/PhabricatorSettingsEditController.php',
'PhabricatorSettingsEditEngine' => 'applications/settings/editor/PhabricatorSettingsEditEngine.php',
'PhabricatorSettingsMainController' => 'applications/settings/controller/PhabricatorSettingsMainController.php',
'PhabricatorSettingsMainMenuBarExtension' => 'applications/settings/extension/PhabricatorSettingsMainMenuBarExtension.php',
'PhabricatorSettingsPanel' => 'applications/settings/panel/PhabricatorSettingsPanel.php',
@ -3363,6 +3382,7 @@ phutil_register_library_map(array(
'PhabricatorSetupIssueUIExample' => 'applications/uiexample/examples/PhabricatorSetupIssueUIExample.php',
'PhabricatorSetupIssueView' => 'applications/config/view/PhabricatorSetupIssueView.php',
'PhabricatorShortSite' => 'aphront/site/PhabricatorShortSite.php',
'PhabricatorShowFiletreeSetting' => 'applications/settings/setting/PhabricatorShowFiletreeSetting.php',
'PhabricatorSimpleEditType' => 'applications/transactions/edittype/PhabricatorSimpleEditType.php',
'PhabricatorSite' => 'aphront/site/PhabricatorSite.php',
'PhabricatorSlowvoteApplication' => 'applications/slowvote/application/PhabricatorSlowvoteApplication.php',
@ -3457,6 +3477,7 @@ phutil_register_library_map(array(
'PhabricatorStorageSetupCheck' => 'applications/config/check/PhabricatorStorageSetupCheck.php',
'PhabricatorStreamingProtocolAdapter' => 'infrastructure/daemon/bot/adapter/PhabricatorStreamingProtocolAdapter.php',
'PhabricatorStringListEditField' => 'applications/transactions/editfield/PhabricatorStringListEditField.php',
'PhabricatorStringSetting' => 'applications/settings/setting/PhabricatorStringSetting.php',
'PhabricatorSubscribableInterface' => 'applications/subscriptions/interface/PhabricatorSubscribableInterface.php',
'PhabricatorSubscribedToObjectEdgeType' => 'applications/transactions/edges/PhabricatorSubscribedToObjectEdgeType.php',
'PhabricatorSubscribersEditField' => 'applications/transactions/editfield/PhabricatorSubscribersEditField.php',
@ -3513,9 +3534,12 @@ phutil_register_library_map(array(
'PhabricatorTextAreaEditField' => 'applications/transactions/editfield/PhabricatorTextAreaEditField.php',
'PhabricatorTextEditField' => 'applications/transactions/editfield/PhabricatorTextEditField.php',
'PhabricatorTime' => 'infrastructure/time/PhabricatorTime.php',
'PhabricatorTimeFormatSetting' => 'applications/settings/setting/PhabricatorTimeFormatSetting.php',
'PhabricatorTimeGuard' => 'infrastructure/time/PhabricatorTimeGuard.php',
'PhabricatorTimeTestCase' => 'infrastructure/time/__tests__/PhabricatorTimeTestCase.php',
'PhabricatorTimezoneSetting' => 'applications/settings/setting/PhabricatorTimezoneSetting.php',
'PhabricatorTimezoneSetupCheck' => 'applications/config/check/PhabricatorTimezoneSetupCheck.php',
'PhabricatorTitleGlyphsSetting' => 'applications/settings/setting/PhabricatorTitleGlyphsSetting.php',
'PhabricatorToken' => 'applications/tokens/storage/PhabricatorToken.php',
'PhabricatorTokenController' => 'applications/tokens/controller/PhabricatorTokenController.php',
'PhabricatorTokenCount' => 'applications/tokens/storage/PhabricatorTokenCount.php',
@ -3544,6 +3568,7 @@ phutil_register_library_map(array(
'PhabricatorTransactionsDestructionEngineExtension' => 'applications/transactions/engineextension/PhabricatorTransactionsDestructionEngineExtension.php',
'PhabricatorTransactionsFulltextEngineExtension' => 'applications/transactions/engineextension/PhabricatorTransactionsFulltextEngineExtension.php',
'PhabricatorTransformedFile' => 'applications/files/storage/PhabricatorTransformedFile.php',
'PhabricatorTranslationSetting' => 'applications/settings/setting/PhabricatorTranslationSetting.php',
'PhabricatorTranslationsConfigOptions' => 'applications/config/option/PhabricatorTranslationsConfigOptions.php',
'PhabricatorTriggerAction' => 'infrastructure/daemon/workers/action/PhabricatorTriggerAction.php',
'PhabricatorTriggerClock' => 'infrastructure/daemon/workers/clock/PhabricatorTriggerClock.php',
@ -3568,12 +3593,15 @@ phutil_register_library_map(array(
'PhabricatorUIExampleRenderController' => 'applications/uiexample/controller/PhabricatorUIExampleRenderController.php',
'PhabricatorUIExamplesApplication' => 'applications/uiexample/application/PhabricatorUIExamplesApplication.php',
'PhabricatorUSEnglishTranslation' => 'infrastructure/internationalization/translation/PhabricatorUSEnglishTranslation.php',
'PhabricatorUnifiedDiffsSetting' => 'applications/settings/setting/PhabricatorUnifiedDiffsSetting.php',
'PhabricatorUnitTestContentSource' => 'infrastructure/contentsource/PhabricatorUnitTestContentSource.php',
'PhabricatorUnitsTestCase' => 'view/__tests__/PhabricatorUnitsTestCase.php',
'PhabricatorUnknownContentSource' => 'infrastructure/contentsource/PhabricatorUnknownContentSource.php',
'PhabricatorUnsubscribedFromObjectEdgeType' => 'applications/transactions/edges/PhabricatorUnsubscribedFromObjectEdgeType.php',
'PhabricatorUser' => 'applications/people/storage/PhabricatorUser.php',
'PhabricatorUserBlurbField' => 'applications/people/customfield/PhabricatorUserBlurbField.php',
'PhabricatorUserCache' => 'applications/people/storage/PhabricatorUserCache.php',
'PhabricatorUserCacheType' => 'applications/people/cache/PhabricatorUserCacheType.php',
'PhabricatorUserCardView' => 'applications/people/view/PhabricatorUserCardView.php',
'PhabricatorUserConfigOptions' => 'applications/people/config/PhabricatorUserConfigOptions.php',
'PhabricatorUserConfiguredCustomField' => 'applications/people/customfield/PhabricatorUserConfiguredCustomField.php',
@ -3592,6 +3620,11 @@ phutil_register_library_map(array(
'PhabricatorUserLogView' => 'applications/people/view/PhabricatorUserLogView.php',
'PhabricatorUserPHIDResolver' => 'applications/phid/resolver/PhabricatorUserPHIDResolver.php',
'PhabricatorUserPreferences' => 'applications/settings/storage/PhabricatorUserPreferences.php',
'PhabricatorUserPreferencesCacheType' => 'applications/people/cache/PhabricatorUserPreferencesCacheType.php',
'PhabricatorUserPreferencesEditor' => 'applications/settings/editor/PhabricatorUserPreferencesEditor.php',
'PhabricatorUserPreferencesPHIDType' => 'applications/settings/phid/PhabricatorUserPreferencesPHIDType.php',
'PhabricatorUserPreferencesQuery' => 'applications/settings/query/PhabricatorUserPreferencesQuery.php',
'PhabricatorUserPreferencesTransaction' => 'applications/settings/storage/PhabricatorUserPreferencesTransaction.php',
'PhabricatorUserProfile' => 'applications/people/storage/PhabricatorUserProfile.php',
'PhabricatorUserProfileEditor' => 'applications/people/editor/PhabricatorUserProfileEditor.php',
'PhabricatorUserRealNameField' => 'applications/people/customfield/PhabricatorUserRealNameField.php',
@ -3611,6 +3644,7 @@ phutil_register_library_map(array(
'PhabricatorViewerDatasource' => 'applications/people/typeahead/PhabricatorViewerDatasource.php',
'PhabricatorWatcherHasObjectEdgeType' => 'applications/transactions/edges/PhabricatorWatcherHasObjectEdgeType.php',
'PhabricatorWebContentSource' => 'infrastructure/contentsource/PhabricatorWebContentSource.php',
'PhabricatorWeekStartDaySetting' => 'applications/settings/setting/PhabricatorWeekStartDaySetting.php',
'PhabricatorWordPressAuthProvider' => 'applications/auth/provider/PhabricatorWordPressAuthProvider.php',
'PhabricatorWorker' => 'infrastructure/daemon/workers/PhabricatorWorker.php',
'PhabricatorWorkerActiveTask' => 'infrastructure/daemon/workers/storage/PhabricatorWorkerActiveTask.php',
@ -3674,7 +3708,9 @@ phutil_register_library_map(array(
'PhabricatorXHProfSample' => 'applications/xhprof/storage/PhabricatorXHProfSample.php',
'PhabricatorXHProfSampleListController' => 'applications/xhprof/controller/PhabricatorXHProfSampleListController.php',
'PhabricatorYoutubeRemarkupRule' => 'infrastructure/markup/rule/PhabricatorYoutubeRemarkupRule.php',
'Phame404Response' => 'applications/phame/site/Phame404Response.php',
'PhameBlog' => 'applications/phame/storage/PhameBlog.php',
'PhameBlog404Controller' => 'applications/phame/controller/blog/PhameBlog404Controller.php',
'PhameBlogArchiveController' => 'applications/phame/controller/blog/PhameBlogArchiveController.php',
'PhameBlogController' => 'applications/phame/controller/blog/PhameBlogController.php',
'PhameBlogCreateCapability' => 'applications/phame/capability/PhameBlogCreateCapability.php',
@ -6126,6 +6162,7 @@ phutil_register_library_map(array(
'PhabricatorAccessControlTestCase' => 'PhabricatorTestCase',
'PhabricatorAccessLog' => 'Phobject',
'PhabricatorAccessLogConfigOptions' => 'PhabricatorApplicationConfigOptions',
'PhabricatorAccessibilitySetting' => 'PhabricatorSelectSetting',
'PhabricatorAccountSettingsPanel' => 'PhabricatorSettingsPanel',
'PhabricatorActionListView' => 'AphrontView',
'PhabricatorActionView' => 'AphrontView',
@ -6744,6 +6781,7 @@ phutil_register_library_map(array(
'PhabricatorDaemonsApplication' => 'PhabricatorApplication',
'PhabricatorDaemonsSetupCheck' => 'PhabricatorSetupCheck',
'PhabricatorDailyRoutineTriggerClock' => 'PhabricatorTriggerClock',
'PhabricatorDarkConsoleSetting' => 'PhabricatorSelectSetting',
'PhabricatorDashboard' => array(
'PhabricatorDashboardDAO',
'PhabricatorApplicationTransactionInterface',
@ -6819,6 +6857,7 @@ phutil_register_library_map(array(
'PhabricatorDatabaseSetupCheck' => 'PhabricatorSetupCheck',
'PhabricatorDatasourceEditField' => 'PhabricatorTokenizerEditField',
'PhabricatorDatasourceEditType' => 'PhabricatorPHIDListEditType',
'PhabricatorDateFormatSetting' => 'PhabricatorSelectSetting',
'PhabricatorDateTimeSettingsPanel' => 'PhabricatorSettingsPanel',
'PhabricatorDebugController' => 'PhabricatorController',
'PhabricatorDefaultRequestExceptionHandler' => 'PhabricatorRequestExceptionHandler',
@ -6903,13 +6942,20 @@ phutil_register_library_map(array(
'PhabricatorEditPage' => 'Phobject',
'PhabricatorEditType' => 'Phobject',
'PhabricatorEditor' => 'Phobject',
'PhabricatorEditorMultipleSetting' => 'PhabricatorSelectSetting',
'PhabricatorEditorSetting' => 'PhabricatorStringSetting',
'PhabricatorElasticFulltextStorageEngine' => 'PhabricatorFulltextStorageEngine',
'PhabricatorElasticSearchSetupCheck' => 'PhabricatorSetupCheck',
'PhabricatorEmailAddressesSettingsPanel' => 'PhabricatorSettingsPanel',
'PhabricatorEmailContentSource' => 'PhabricatorContentSource',
'PhabricatorEmailFormatSetting' => 'PhabricatorSelectSetting',
'PhabricatorEmailFormatSettingsPanel' => 'PhabricatorSettingsPanel',
'PhabricatorEmailLoginController' => 'PhabricatorAuthController',
'PhabricatorEmailNotificationsSetting' => 'PhabricatorSelectSetting',
'PhabricatorEmailPreferencesSettingsPanel' => 'PhabricatorSettingsPanel',
'PhabricatorEmailRePrefixSetting' => 'PhabricatorSelectSetting',
'PhabricatorEmailSelfActionsSetting' => 'PhabricatorSelectSetting',
'PhabricatorEmailVarySubjectsSetting' => 'PhabricatorSelectSetting',
'PhabricatorEmailVerificationController' => 'PhabricatorAuthController',
'PhabricatorEmbedFileRemarkupRule' => 'PhabricatorObjectRemarkupRule',
'PhabricatorEmojiRemarkupRule' => 'PhutilRemarkupRule',
@ -7307,6 +7353,8 @@ phutil_register_library_map(array(
'PhabricatorMetaMTASendGridReceiveController' => 'PhabricatorMetaMTAController',
'PhabricatorMetaMTAWorker' => 'PhabricatorWorker',
'PhabricatorMetronomicTriggerClock' => 'PhabricatorTriggerClock',
'PhabricatorMonospacedFontSetting' => 'PhabricatorStringSetting',
'PhabricatorMonospacedTextareasSetting' => 'PhabricatorSelectSetting',
'PhabricatorMotivatorProfilePanel' => 'PhabricatorProfilePanel',
'PhabricatorMultiColumnUIExample' => 'PhabricatorUIExample',
'PhabricatorMultiFactorSettingsPanel' => 'PhabricatorSettingsPanel',
@ -7410,8 +7458,10 @@ phutil_register_library_map(array(
'PhabricatorObjectSelectorDialog' => 'Phobject',
'PhabricatorOffsetPagedQuery' => 'PhabricatorQuery',
'PhabricatorOldWorldContentSource' => 'PhabricatorContentSource',
'PhabricatorOlderInlinesSetting' => 'PhabricatorSelectSetting',
'PhabricatorOneTimeTriggerClock' => 'PhabricatorTriggerClock',
'PhabricatorOpcodeCacheSpec' => 'PhabricatorCacheSpec',
'PhabricatorOptionGroupSetting' => 'PhabricatorSetting',
'PhabricatorOwnerPathQuery' => 'Phobject',
'PhabricatorOwnersApplication' => 'PhabricatorApplication',
'PhabricatorOwnersArchiveController' => 'PhabricatorOwnersController',
@ -7815,6 +7865,7 @@ phutil_register_library_map(array(
'PhabricatorProjectsSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment',
'PhabricatorProjectsSearchEngineExtension' => 'PhabricatorSearchEngineExtension',
'PhabricatorProjectsWatchersSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment',
'PhabricatorPronounSetting' => 'PhabricatorSelectSetting',
'PhabricatorProtocolAdapter' => 'Phobject',
'PhabricatorPygmentSetupCheck' => 'PhabricatorSetupCheck',
'PhabricatorQuery' => 'Phobject',
@ -8061,11 +8112,15 @@ phutil_register_library_map(array(
'PhabricatorSecurityConfigOptions' => 'PhabricatorApplicationConfigOptions',
'PhabricatorSecuritySetupCheck' => 'PhabricatorSetupCheck',
'PhabricatorSelectEditField' => 'PhabricatorEditField',
'PhabricatorSelectSetting' => 'PhabricatorSetting',
'PhabricatorSendGridConfigOptions' => 'PhabricatorApplicationConfigOptions',
'PhabricatorSessionsSettingsPanel' => 'PhabricatorSettingsPanel',
'PhabricatorSetting' => 'Phobject',
'PhabricatorSettingsAddEmailAction' => 'PhabricatorSystemAction',
'PhabricatorSettingsAdjustController' => 'PhabricatorController',
'PhabricatorSettingsApplication' => 'PhabricatorApplication',
'PhabricatorSettingsEditController' => 'PhabricatorController',
'PhabricatorSettingsEditEngine' => 'PhabricatorEditEngine',
'PhabricatorSettingsMainController' => 'PhabricatorController',
'PhabricatorSettingsMainMenuBarExtension' => 'PhabricatorMainMenuBarExtension',
'PhabricatorSettingsPanel' => 'Phobject',
@ -8076,6 +8131,7 @@ phutil_register_library_map(array(
'PhabricatorSetupIssueUIExample' => 'PhabricatorUIExample',
'PhabricatorSetupIssueView' => 'AphrontView',
'PhabricatorShortSite' => 'PhabricatorSite',
'PhabricatorShowFiletreeSetting' => 'PhabricatorSelectSetting',
'PhabricatorSimpleEditType' => 'PhabricatorEditType',
'PhabricatorSite' => 'AphrontSite',
'PhabricatorSlowvoteApplication' => 'PhabricatorApplication',
@ -8187,6 +8243,7 @@ phutil_register_library_map(array(
'PhabricatorStorageSetupCheck' => 'PhabricatorSetupCheck',
'PhabricatorStreamingProtocolAdapter' => 'PhabricatorProtocolAdapter',
'PhabricatorStringListEditField' => 'PhabricatorEditField',
'PhabricatorStringSetting' => 'PhabricatorSetting',
'PhabricatorSubscribedToObjectEdgeType' => 'PhabricatorEdgeType',
'PhabricatorSubscribersEditField' => 'PhabricatorTokenizerEditField',
'PhabricatorSubscribersQuery' => 'PhabricatorQuery',
@ -8242,9 +8299,12 @@ phutil_register_library_map(array(
'PhabricatorTextAreaEditField' => 'PhabricatorEditField',
'PhabricatorTextEditField' => 'PhabricatorEditField',
'PhabricatorTime' => 'Phobject',
'PhabricatorTimeFormatSetting' => 'PhabricatorSelectSetting',
'PhabricatorTimeGuard' => 'Phobject',
'PhabricatorTimeTestCase' => 'PhabricatorTestCase',
'PhabricatorTimezoneSetting' => 'PhabricatorOptionGroupSetting',
'PhabricatorTimezoneSetupCheck' => 'PhabricatorSetupCheck',
'PhabricatorTitleGlyphsSetting' => 'PhabricatorSelectSetting',
'PhabricatorToken' => array(
'PhabricatorTokenDAO',
'PhabricatorPolicyInterface',
@ -8278,6 +8338,7 @@ phutil_register_library_map(array(
'PhabricatorTransactionsDestructionEngineExtension' => 'PhabricatorDestructionEngineExtension',
'PhabricatorTransactionsFulltextEngineExtension' => 'PhabricatorFulltextEngineExtension',
'PhabricatorTransformedFile' => 'PhabricatorFileDAO',
'PhabricatorTranslationSetting' => 'PhabricatorOptionGroupSetting',
'PhabricatorTranslationsConfigOptions' => 'PhabricatorApplicationConfigOptions',
'PhabricatorTriggerAction' => 'Phobject',
'PhabricatorTriggerClock' => 'Phobject',
@ -8302,6 +8363,7 @@ phutil_register_library_map(array(
'PhabricatorUIExampleRenderController' => 'PhabricatorController',
'PhabricatorUIExamplesApplication' => 'PhabricatorApplication',
'PhabricatorUSEnglishTranslation' => 'PhutilTranslation',
'PhabricatorUnifiedDiffsSetting' => 'PhabricatorSelectSetting',
'PhabricatorUnitTestContentSource' => 'PhabricatorContentSource',
'PhabricatorUnitsTestCase' => 'PhabricatorTestCase',
'PhabricatorUnknownContentSource' => 'PhabricatorContentSource',
@ -8319,6 +8381,8 @@ phutil_register_library_map(array(
'PhabricatorConduitResultInterface',
),
'PhabricatorUserBlurbField' => 'PhabricatorUserCustomField',
'PhabricatorUserCache' => 'PhabricatorUserDAO',
'PhabricatorUserCacheType' => 'Phobject',
'PhabricatorUserCardView' => 'AphrontTagView',
'PhabricatorUserConfigOptions' => 'PhabricatorApplicationConfigOptions',
'PhabricatorUserConfiguredCustomField' => array(
@ -8342,7 +8406,17 @@ phutil_register_library_map(array(
),
'PhabricatorUserLogView' => 'AphrontView',
'PhabricatorUserPHIDResolver' => 'PhabricatorPHIDResolver',
'PhabricatorUserPreferences' => 'PhabricatorUserDAO',
'PhabricatorUserPreferences' => array(
'PhabricatorUserDAO',
'PhabricatorPolicyInterface',
'PhabricatorDestructibleInterface',
'PhabricatorApplicationTransactionInterface',
),
'PhabricatorUserPreferencesCacheType' => 'PhabricatorUserCacheType',
'PhabricatorUserPreferencesEditor' => 'PhabricatorApplicationTransactionEditor',
'PhabricatorUserPreferencesPHIDType' => 'PhabricatorPHIDType',
'PhabricatorUserPreferencesQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorUserPreferencesTransaction' => 'PhabricatorApplicationTransaction',
'PhabricatorUserProfile' => 'PhabricatorUserDAO',
'PhabricatorUserProfileEditor' => 'PhabricatorApplicationTransactionEditor',
'PhabricatorUserRealNameField' => 'PhabricatorUserCustomField',
@ -8362,6 +8436,7 @@ phutil_register_library_map(array(
'PhabricatorViewerDatasource' => 'PhabricatorTypeaheadDatasource',
'PhabricatorWatcherHasObjectEdgeType' => 'PhabricatorEdgeType',
'PhabricatorWebContentSource' => 'PhabricatorContentSource',
'PhabricatorWeekStartDaySetting' => 'PhabricatorSelectSetting',
'PhabricatorWordPressAuthProvider' => 'PhabricatorOAuth2AuthProvider',
'PhabricatorWorker' => 'Phobject',
'PhabricatorWorkerActiveTask' => 'PhabricatorWorkerTask',
@ -8435,6 +8510,7 @@ phutil_register_library_map(array(
'PhabricatorXHProfSample' => 'PhabricatorXHProfDAO',
'PhabricatorXHProfSampleListController' => 'PhabricatorXHProfController',
'PhabricatorYoutubeRemarkupRule' => 'PhutilRemarkupRule',
'Phame404Response' => 'AphrontHTMLResponse',
'PhameBlog' => array(
'PhameDAO',
'PhabricatorPolicyInterface',
@ -8446,6 +8522,7 @@ phutil_register_library_map(array(
'PhabricatorApplicationTransactionInterface',
'PhabricatorConduitResultInterface',
),
'PhameBlog404Controller' => 'PhameLiveController',
'PhameBlogArchiveController' => 'PhameBlogController',
'PhameBlogController' => 'PhameController',
'PhameBlogCreateCapability' => 'PhabricatorPolicyCapability',

View file

@ -60,7 +60,7 @@ final class PhabricatorAuditApplication extends PhabricatorApplication {
$count = count($commits);
if ($count >= $limit) {
$count_str = pht('%s+ Problem Commit(s)', new PhutilNumber($limit - 1));
$count_str = pht('%s+ Problem Commits', new PhutilNumber($limit - 1));
} else {
$count_str = pht('%s Problem Commit(s)', new PhutilNumber($count));
}
@ -80,9 +80,13 @@ final class PhabricatorAuditApplication extends PhabricatorApplication {
$count = count($commits);
if ($count >= $limit) {
$count_str = pht('%s+ Problem Commit(s)', new PhutilNumber($limit - 1));
$count_str = pht(
'%s+ Commits Awaiting Audit',
new PhutilNumber($limit - 1));
} else {
$count_str = pht('%s Problem Commit(s)', new PhutilNumber($count));
$count_str = pht(
'%s Commit(s) Awaiting Audit',
new PhutilNumber($count));
}
$type = PhabricatorApplicationStatusView::TYPE_WARNING;

View file

@ -7,6 +7,7 @@
* @task hisec High Security
* @task partial Partial Sessions
* @task onetime One Time Login URIs
* @task cache User Cache
*/
final class PhabricatorAuthSessionEngine extends Phobject {
@ -111,9 +112,8 @@ final class PhabricatorAuthSessionEngine extends Phobject {
$conn_r = $session_table->establishConnection('r');
$session_key = PhabricatorHash::digest($session_token);
// NOTE: We're being clever here because this happens on every page load,
// and by joining we can save a query. This might be getting too clever
// for its own good, though...
$cache_parts = $this->getUserCacheQueryParts($conn_r);
list($cache_selects, $cache_joins, $cache_map) = $cache_parts;
$info = queryfx_one(
$conn_r,
@ -125,12 +125,15 @@ final class PhabricatorAuthSessionEngine extends Phobject {
s.isPartial AS s_isPartial,
s.signedLegalpadDocuments as s_signedLegalpadDocuments,
u.*
%Q
FROM %T u JOIN %T s ON u.phid = s.userPHID
AND s.type = %s AND s.sessionKey = %s',
AND s.type = %s AND s.sessionKey = %s %Q',
$cache_selects,
$user_table->getTableName(),
$session_table->getTableName(),
$session_type,
$session_key);
$session_key,
$cache_joins);
if (!$info) {
return null;
@ -141,14 +144,26 @@ final class PhabricatorAuthSessionEngine extends Phobject {
'sessionKey' => $session_key,
'type' => $session_type,
);
$cache_raw = array_fill_keys($cache_map, null);
foreach ($info as $key => $value) {
if (strncmp($key, 's_', 2) === 0) {
unset($info[$key]);
$session_dict[substr($key, 2)] = $value;
continue;
}
if (isset($cache_map[$key])) {
unset($info[$key]);
$cache_raw[$cache_map[$key]] = $value;
continue;
}
}
$user = $user_table->loadFromArray($info);
$user->attachRawCacheData($cache_raw);
switch ($session_type) {
case PhabricatorAuthSession::TYPE_WEB:
// Explicitly prevent bots and mailing lists from establishing web
@ -732,4 +747,68 @@ final class PhabricatorAuthSessionEngine extends Phobject {
return PhabricatorHash::digest(implode(':', $parts));
}
/* -( User Cache )--------------------------------------------------------- */
/**
* @task cache
*/
private function getUserCacheQueryParts(AphrontDatabaseConnection $conn) {
$cache_selects = array();
$cache_joins = array();
$cache_map = array();
$keys = array();
$cache_types = PhabricatorUserCacheType::getAllCacheTypes();
foreach ($cache_types as $cache_type) {
foreach ($cache_type->getAutoloadKeys() as $autoload_key) {
$keys[] = $autoload_key;
}
}
$cache_table = id(new PhabricatorUserCache())->getTableName();
$cache_idx = 1;
foreach ($keys as $key) {
$join_as = 'ucache_'.$cache_idx;
$select_as = 'ucache_'.$cache_idx.'_v';
$cache_selects[] = qsprintf(
$conn,
'%T.cacheData %T',
$join_as,
$select_as);
$cache_joins[] = qsprintf(
$conn,
'LEFT JOIN %T AS %T ON u.phid = %T.userPHID
AND %T.cacheIndex = %s',
$cache_table,
$join_as,
$join_as,
$join_as,
PhabricatorHash::digestForIndex($key));
$cache_map[$select_as] = $key;
$cache_idx++;
}
if ($cache_selects) {
$cache_selects = ', '.implode(', ', $cache_selects);
} else {
$cache_selects = '';
}
if ($cache_joins) {
$cache_joins = implode(' ', $cache_joins);
} else {
$cache_joins = '';
}
return array($cache_selects, $cache_joins, $cache_map);
}
}

View file

@ -106,10 +106,9 @@ abstract class PhabricatorController extends AphrontController {
PhabricatorEnv::setLocaleCode($user->getTranslation());
$preferences = $user->loadPreferences();
if (PhabricatorEnv::getEnvConfig('darkconsole.enabled')) {
$dark_console = PhabricatorUserPreferences::PREFERENCE_DARK_CONSOLE;
if ($preferences->getPreference($dark_console) ||
$dark_console = PhabricatorDarkConsoleSetting::SETTINGKEY;
if ($user->getUserSetting($dark_console) ||
PhabricatorEnv::getEnvConfig('darkconsole.always-on')) {
$console = new DarkConsoleCore();
$request->getApplicationConfiguration()->setConsole($console);

View file

@ -4,7 +4,7 @@ final class CalendarTimeUtilTestCase extends PhabricatorTestCase {
public function testTimestampsAtMidnight() {
$u = new PhabricatorUser();
$u->setTimezoneIdentifier('America/Los_Angeles');
$u->overrideTimezoneIdentifier('America/Los_Angeles');
$days = $this->getAllDays();
foreach ($days as $day) {
$data = CalendarTimeUtil::getCalendarWidgetTimestamps(
@ -19,7 +19,7 @@ final class CalendarTimeUtilTestCase extends PhabricatorTestCase {
public function testTimestampsStartDay() {
$u = new PhabricatorUser();
$u->setTimezoneIdentifier('America/Los_Angeles');
$u->overrideTimezoneIdentifier('America/Los_Angeles');
$days = $this->getAllDays();
foreach ($days as $day) {
$data = CalendarTimeUtil::getTimestamps(

View file

@ -151,10 +151,9 @@ final class PhabricatorCalendarEventSearchEngine
$display_start = $start_day->format('U');
$display_end = $next->format('U');
$preferences = $viewer->loadPreferences();
$pref_week_day = PhabricatorUserPreferences::PREFERENCE_WEEK_START_DAY;
$start_of_week = $viewer->getUserSetting(
PhabricatorWeekStartDaySetting::SETTINGKEY);
$start_of_week = $preferences->getPreference($pref_week_day, 0);
$end_of_week = ($start_of_week + 6) % 7;
$first_of_month = $start_day->format('w');

View file

@ -10,7 +10,7 @@ final class CelerityDefaultPostprocessor
}
public function getPostprocessorName() {
return pht('Use Default Colors');
return pht('Use Standard Colors');
}
public function buildDefaultPostprocessor() {

View file

@ -141,8 +141,14 @@ final class PhabricatorConfigWelcomeController
$content);
$settings_href = PhabricatorEnv::getURI('/settings/');
$prefs = $viewer->loadPreferences()->getPreferences();
$have_settings = !empty($prefs);
$preferences = id(new PhabricatorUserPreferencesQuery())
->setViewer($viewer)
->withUsers(array($viewer))
->executeOne();
$have_settings = ($preferences && $preferences->getPreferences());
if ($have_settings) {
$content = pht(
"=== Adjust Account Settings ===\n\n".

View file

@ -533,13 +533,20 @@ final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor {
protected function getMailTo(PhabricatorLiskDAO $object) {
$to_phids = array();
$participants = $object->getParticipants();
if (empty($participants)) {
if (!$participants) {
return $to_phids;
}
$preferences = id(new PhabricatorUserPreferences())
->loadAllWhere('userPHID in (%Ls)', array_keys($participants));
$participant_phids = mpull($participants, 'getParticipantPHID');
$preferences = id(new PhabricatorUserPreferencesQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withUserPHIDs($participant_phids)
->execute();
$preferences = mpull($preferences, null, 'getUserPHID');
foreach ($participants as $phid => $participant) {
$default = ConpherenceSettings::EMAIL_ALWAYS;
$preference = idx($preferences, $phid);
@ -557,6 +564,7 @@ final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor {
$to_phids[] = $phid;
}
}
return $to_phids;
}

View file

@ -72,14 +72,6 @@ final class ConpherenceThreadListView extends AphrontView {
$epoch = $data['epoch'];
$image = $data['image'];
$dom_id = $thread->getPHID().'-nav-item';
$glyph_pref = PhabricatorUserPreferences::PREFERENCE_TITLES;
$preferences = $user->loadPreferences();
if ($preferences->getPreference($glyph_pref) == 'glyph') {
$glyph = id(new PhabricatorConpherenceApplication())
->getTitleGlyph().' ';
} else {
$glyph = null;
}
return id(new ConpherenceMenuItemView())
->setUser($user)
@ -93,7 +85,7 @@ final class ConpherenceThreadListView extends AphrontView {
->addSigil('conpherence-menu-click')
->setMetadata(
array(
'title' => $glyph.$data['title'],
'title' => $data['title'],
'id' => $dom_id,
'threadID' => $thread->getID(),
));

View file

@ -23,13 +23,34 @@ final class DifferentialInlineCommentEditController
}
protected function createComment() {
// Verify revision and changeset correspond to actual objects.
// Verify revision and changeset correspond to actual objects, and are
// connected to one another.
$changeset_id = $this->getChangesetID();
$viewer = $this->getViewer();
$revision = $this->loadRevision();
if (!id(new DifferentialChangeset())->load($changeset_id)) {
throw new Exception(pht('Invalid changeset ID!'));
$changeset = id(new DifferentialChangesetQuery())
->setViewer($viewer)
->withIDs(array($changeset_id))
->executeOne();
if (!$changeset) {
throw new Exception(
pht(
'Invalid changeset ID "%s"!',
$changeset_id));
}
$diff = $changeset->getDiff();
if ($diff->getRevisionID() != $revision->getID()) {
throw new Exception(
pht(
'Changeset ID "%s" is part of diff ID "%s", but that diff '.
'is attached to reivsion "%s", not revision "%s".',
$changeset_id,
$diff->getID(),
$diff->getRevisionID(),
$revision->getID()));
}
return id(new DifferentialInlineComment())

View file

@ -93,7 +93,7 @@ final class DifferentialRevisionCloseDetailsController
'href' => $obj_handle->getURI(),
),
$obj_handle->getName());
$body_why = pht(
$body_why[] = pht(
'This commit and the active diff of %s had the same %s hash '.
'(%s) so we linked this commit to %s.',
$diff_link,

View file

@ -375,18 +375,19 @@ final class DifferentialRevisionViewController extends DifferentialController {
$crumbs->addTextCrumb($object_id, '/'.$object_id);
$crumbs->setBorder(true);
$prefs = $viewer->loadPreferences();
$pref_filetree = PhabricatorUserPreferences::PREFERENCE_DIFF_FILETREE;
$filetree_on = $viewer->compareUserSetting(
PhabricatorShowFiletreeSetting::SETTINGKEY,
PhabricatorShowFiletreeSetting::VALUE_ENABLE_FILETREE);
$nav = null;
if ($prefs->getPreference($pref_filetree)) {
$collapsed = $prefs->getPreference(
PhabricatorUserPreferences::PREFERENCE_NAV_COLLAPSED,
false);
if ($filetree_on) {
$collapsed_key = PhabricatorUserPreferences::PREFERENCE_NAV_COLLAPSED;
$collapsed_value = $viewer->getUserSetting($collapsed_key);
$nav = id(new DifferentialChangesetFileTreeSideNavBuilder())
->setTitle('D'.$revision->getID())
->setBaseURI(new PhutilURI('/D'.$revision->getID()))
->setCollapsed((bool)$collapsed)
->setCollapsed((bool)$collapsed_value)
->build($changesets);
}

View file

@ -150,11 +150,14 @@ final class DifferentialChangesetParser extends Phobject {
}
public static function getDefaultRendererForViewer(PhabricatorUser $viewer) {
$prefs = $viewer->loadPreferences();
$pref_unified = PhabricatorUserPreferences::PREFERENCE_DIFF_UNIFIED;
if ($prefs->getPreference($pref_unified) == 'unified') {
$is_unified = $viewer->compareUserSetting(
PhabricatorUnifiedDiffsSetting::SETTINGKEY,
PhabricatorUnifiedDiffsSetting::VALUE_ALWAYS_UNIFIED);
if ($is_unified) {
return '1up';
}
return null;
}
@ -458,6 +461,10 @@ final class DifferentialChangesetParser extends Phobject {
}
public function saveCache() {
if (PhabricatorEnv::isReadOnly()) {
return false;
}
if ($this->highlightErrors) {
return false;
}

View file

@ -175,9 +175,10 @@ final class DifferentialInlineCommentQuery
$viewer = $this->getViewer();
$pref = $viewer->loadPreferences()->getPreference(
PhabricatorUserPreferences::PREFERENCE_DIFF_GHOSTS);
if ($pref == 'disabled') {
$no_ghosts = $viewer->compareUserSetting(
PhabricatorOlderInlinesSetting::SETTINGKEY,
PhabricatorOlderInlinesSetting::VALUE_GHOST_INLINES_DISABLED);
if ($no_ghosts) {
return $inlines;
}

View file

@ -56,7 +56,10 @@ final class DiffusionBranchQueryConduitAPIMethod
} else {
$refs = id(new DiffusionLowLevelGitRefQuery())
->setRepository($repository)
->withIsOriginBranch(true)
->withRefTypes(
array(
PhabricatorRepositoryRefCursor::TYPE_BRANCH,
))
->execute();
}

View file

@ -72,7 +72,10 @@ final class DiffusionTagsQueryConduitAPIMethod
$refs = id(new DiffusionLowLevelGitRefQuery())
->setRepository($repository)
->withIsTag(true)
->withRefTypes(
array(
PhabricatorRepositoryRefCursor::TYPE_TAG,
))
->execute();
$tags = array();

View file

@ -325,14 +325,15 @@ final class DiffusionCommitController extends DiffusionController {
$add_comment = $this->renderAddCommentPanel($commit, $audit_requests);
$prefs = $viewer->loadPreferences();
$pref_filetree = PhabricatorUserPreferences::PREFERENCE_DIFF_FILETREE;
$filetree_on = $viewer->compareUserSetting(
PhabricatorShowFiletreeSetting::SETTINGKEY,
PhabricatorShowFiletreeSetting::VALUE_ENABLE_FILETREE);
$pref_collapse = PhabricatorUserPreferences::PREFERENCE_NAV_COLLAPSED;
$show_filetree = $prefs->getPreference($pref_filetree);
$collapsed = $prefs->getPreference($pref_collapse);
$collapsed = $viewer->getUserSetting($pref_collapse);
$nav = null;
if ($show_changesets && $show_filetree) {
if ($show_changesets && $filetree_on) {
$nav = id(new DifferentialChangesetFileTreeSideNavBuilder())
->setTitle($commit->getDisplayName())
->setBaseURI(new PhutilURI($commit->getURI()))

View file

@ -463,6 +463,8 @@ final class DiffusionURIEditor
break;
}
$was_hosted = $repository->isHosted();
if ($observe_uri) {
$repository
->setHosted(false)
@ -477,6 +479,17 @@ final class DiffusionURIEditor
$repository->save();
$is_hosted = $repository->isHosted();
// If we've swapped the repository from hosted to observed or vice versa,
// reset all the cluster version clocks.
if ($was_hosted != $is_hosted) {
$cluster_engine = id(new DiffusionRepositoryClusterEngine())
->setViewer($this->getActor())
->setRepository($repository)
->synchronizeWorkingCopyAfterHostingChange();
}
return $xactions;
}

View file

@ -137,12 +137,28 @@ final class DiffusionRepositoryStorageManagementPanel
$version = idx($versions, $device->getPHID());
if ($version) {
$version_number = $version->getRepositoryVersion();
$version_number = phutil_tag(
'a',
array(
'href' => "/diffusion/pushlog/view/{$version_number}/",
),
$version_number);
$href = null;
if ($repository->isHosted()) {
$href = "/diffusion/pushlog/view/{$version_number}/";
} else {
$commit = id(new DiffusionCommitQuery())
->setViewer($viewer)
->withIDs(array($version_number))
->executeOne();
if ($commit) {
$href = $commit->getURI();
}
}
if ($href) {
$version_number = phutil_tag(
'a',
array(
'href' => $href,
),
$version_number);
}
} else {
$version_number = '-';
}

View file

@ -82,6 +82,53 @@ final class DiffusionRepositoryClusterEngine extends Phobject {
}
/**
* @task sync
*/
public function synchronizeWorkingCopyAfterHostingChange() {
if (!$this->shouldEnableSynchronization()) {
return;
}
$repository = $this->getRepository();
$repository_phid = $repository->getPHID();
$versions = PhabricatorRepositoryWorkingCopyVersion::loadVersions(
$repository_phid);
$versions = mpull($versions, null, 'getDevicePHID');
// After converting a hosted repository to observed, or vice versa, we
// need to reset version numbers because the clocks for observed and hosted
// repositories run on different units.
// We identify all the cluster leaders and reset their version to 0.
// We identify all the cluster followers and demote them.
// This allows the cluter to start over again at version 0 but keep the
// same leaders.
if ($versions) {
$max_version = (int)max(mpull($versions, 'getRepositoryVersion'));
foreach ($versions as $version) {
$device_phid = $version->getDevicePHID();
if ($version->getRepositoryVersion() == $max_version) {
PhabricatorRepositoryWorkingCopyVersion::updateVersion(
$repository_phid,
$device_phid,
0);
} else {
PhabricatorRepositoryWorkingCopyVersion::demoteDevice(
$repository_phid,
$device_phid);
}
}
}
return $this;
}
/**
* @task sync
*/
@ -149,14 +196,18 @@ final class DiffusionRepositoryClusterEngine extends Phobject {
$max_version = (int)max(mpull($versions, 'getRepositoryVersion'));
if ($max_version > $this_version) {
$fetchable = array();
foreach ($versions as $version) {
if ($version->getRepositoryVersion() == $max_version) {
$fetchable[] = $version->getDevicePHID();
if ($repository->isHosted()) {
$fetchable = array();
foreach ($versions as $version) {
if ($version->getRepositoryVersion() == $max_version) {
$fetchable[] = $version->getDevicePHID();
}
}
}
$this->synchronizeWorkingCopyFromDevices($fetchable);
$this->synchronizeWorkingCopyFromDevices($fetchable);
} else {
$this->synchornizeWorkingCopyFromRemote();
}
PhabricatorRepositoryWorkingCopyVersion::updateVersion(
$repository_phid,
@ -329,6 +380,47 @@ final class DiffusionRepositoryClusterEngine extends Phobject {
}
public function synchronizeWorkingCopyAfterDiscovery($new_version) {
if (!$this->shouldEnableSynchronization()) {
return;
}
$repository = $this->getRepository();
$repository_phid = $repository->getPHID();
if ($repository->isHosted()) {
return;
}
$viewer = $this->getViewer();
$device = AlmanacKeys::getLiveDevice();
$device_phid = $device->getPHID();
// NOTE: We are not holding a lock here because this method is only called
// from PhabricatorRepositoryDiscoveryEngine, which already holds a device
// lock. Even if we do race here and record an older version, the
// consequences are mild: we only do extra work to correct it later.
$versions = PhabricatorRepositoryWorkingCopyVersion::loadVersions(
$repository_phid);
$versions = mpull($versions, null, 'getDevicePHID');
$this_version = idx($versions, $device_phid);
if ($this_version) {
$this_version = (int)$this_version->getRepositoryVersion();
} else {
$this_version = -1;
}
if ($new_version > $this_version) {
PhabricatorRepositoryWorkingCopyVersion::updateVersion(
$repository_phid,
$device_phid,
$new_version);
}
}
/**
* @task sync
*/
@ -471,13 +563,6 @@ final class DiffusionRepositoryClusterEngine extends Phobject {
return false;
}
// TODO: It may eventually make sense to try to version and synchronize
// observed repositories (so that daemons don't do reads against out-of
// date hosts), but don't bother for now.
if (!$repository->isHosted()) {
return false;
}
$device = AlmanacKeys::getLiveDevice();
if (!$device) {
return false;
@ -487,6 +572,50 @@ final class DiffusionRepositoryClusterEngine extends Phobject {
}
/**
* @task internal
*/
private function synchornizeWorkingCopyFromRemote() {
$repository = $this->getRepository();
$device = AlmanacKeys::getLiveDevice();
$local_path = $repository->getLocalPath();
$fetch_uri = $repository->getRemoteURIEnvelope();
if ($repository->isGit()) {
$this->requireWorkingCopy();
$argv = array(
'fetch --prune -- %P %s',
$fetch_uri,
'+refs/*:refs/*',
);
} else {
throw new Exception(pht('Remote sync only supported for git!'));
}
$future = DiffusionCommandEngine::newCommandEngine($repository)
->setArgv($argv)
->setSudoAsDaemon(true)
->setCredentialPHID($repository->getCredentialPHID())
->setProtocol($repository->getRemoteProtocol())
->newFuture();
$future->setCWD($local_path);
try {
$future->resolvex();
} catch (Exception $ex) {
$this->logLine(
pht(
'Synchronization of "%s" from remote failed: %s',
$device->getName(),
$ex->getMessage()));
throw $ex;
}
}
/**
* @task internal
*/
@ -560,17 +689,7 @@ final class DiffusionRepositoryClusterEngine extends Phobject {
$local_path = $repository->getLocalPath();
if ($repository->isGit()) {
if (!Filesystem::pathExists($local_path)) {
throw new Exception(
pht(
'Repository "%s" does not have a working copy on this device '.
'yet, so it can not be synchronized. Wait for the daemons to '.
'construct one or run `bin/repository update %s` on this host '.
'("%s") to build it explicitly.',
$repository->getDisplayName(),
$repository->getMonogram(),
$device->getName()));
}
$this->requireWorkingCopy();
$argv = array(
'fetch --prune -- %s %s',
@ -622,4 +741,24 @@ final class DiffusionRepositoryClusterEngine extends Phobject {
}
return $this;
}
private function requireWorkingCopy() {
$repository = $this->getRepository();
$local_path = $repository->getLocalPath();
if (!Filesystem::pathExists($local_path)) {
$device = AlmanacKeys::getLiveDevice();
throw new Exception(
pht(
'Repository "%s" does not have a working copy on this device '.
'yet, so it can not be synchronized. Wait for the daemons to '.
'construct one or run `bin/repository update %s` on this host '.
'("%s") to build it explicitly.',
$repository->getDisplayName(),
$repository->getMonogram(),
$device->getName()));
}
}
}

View file

@ -67,19 +67,23 @@ final class DiffusionLowLevelCommitFieldsQuery
->withCommitHashes($hash_list)
->execute();
if (!empty($revisions)) {
if ($revisions) {
$revision = $this->pickBestRevision($revisions);
$fields['revisionID'] = $revision->getID();
$revision_hashes = $revision->getHashes();
$revision_hashes = DiffusionCommitHash::convertArrayToObjects(
$revision_hashes);
$revision_hashes = mpull($revision_hashes, 'getHashType');
$revision_hashes = mpull($revision_hashes, null, 'getHashType');
// sort the hashes in the order the mighty
// @{class:ArcanstDifferentialRevisionHash} does; probably unnecessary
// but should future proof things nicely.
$revision_hashes = array_select_keys(
$revision_hashes,
ArcanistDifferentialRevisionHash::getTypes());
foreach ($hashes as $hash) {
$revision_hash = idx($revision_hashes, $hash->getHashType());
if (!$revision_hash) {

View file

@ -6,30 +6,33 @@
*/
final class DiffusionLowLevelGitRefQuery extends DiffusionLowLevelQuery {
private $isTag;
private $isOriginBranch;
private $refTypes;
public function withIsTag($is_tag) {
$this->isTag = $is_tag;
return $this;
}
public function withIsOriginBranch($is_origin_branch) {
$this->isOriginBranch = $is_origin_branch;
public function withRefTypes(array $ref_types) {
$this->refTypes = $ref_types;
return $this;
}
protected function executeQuery() {
$ref_types = $this->refTypes;
if ($ref_types) {
$type_branch = PhabricatorRepositoryRefCursor::TYPE_BRANCH;
$type_tag = PhabricatorRepositoryRefCursor::TYPE_TAG;
$ref_types = array_fuse($ref_types);
$with_branches = isset($ref_types[$type_branch]);
$with_tags = isset($ref_types[$type_tag]);
} else {
$with_branches = true;
$with_tags = true;
}
$repository = $this->getRepository();
$prefixes = array();
$any = ($this->isTag || $this->isOriginBranch);
if (!$any) {
throw new Exception(pht('Specify types of refs to query.'));
}
if ($this->isOriginBranch) {
if ($with_branches) {
if ($repository->isWorkingCopyBare()) {
$prefix = 'refs/heads/';
} else {
@ -39,7 +42,7 @@ final class DiffusionLowLevelGitRefQuery extends DiffusionLowLevelQuery {
$prefixes[] = $prefix;
}
if ($this->isTag) {
if ($with_tags) {
$prefixes[] = 'refs/tags/';
}

View file

@ -66,8 +66,11 @@ final class DiffusionLowLevelResolveRefsQuery
// First, resolve branches and tags.
$ref_map = id(new DiffusionLowLevelGitRefQuery())
->setRepository($repository)
->withIsTag(true)
->withIsOriginBranch(true)
->withRefTypes(
array(
PhabricatorRepositoryRefCursor::TYPE_BRANCH,
PhabricatorRepositoryRefCursor::TYPE_TAG,
))
->execute();
$ref_map = mgroup($ref_map, 'getShortName');

View file

@ -207,9 +207,10 @@ final class PhabricatorFeedStoryPublisher extends Phobject {
$tags = $this->getMailTags();
if ($tags) {
$all_prefs = id(new PhabricatorUserPreferences())->loadAllWhere(
'userPHID in (%Ls)',
$phids);
$all_prefs = id(new PhabricatorUserPreferencesQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withUserPHIDs($phids)
->execute();
$all_prefs = mpull($all_prefs, null, 'getUserPHID');
}

View file

@ -164,33 +164,6 @@ final class PhabricatorMetaMTAMail
return $this->getParam('herald-force-recipients', array());
}
public function getTranslation(array $objects) {
$default_translation = PhabricatorEnv::getEnvConfig('translation.provider');
$return = null;
$recipients = array_merge(
idx($this->parameters, 'to', array()),
idx($this->parameters, 'cc', array()));
foreach (array_select_keys($objects, $recipients) as $object) {
$translation = null;
if ($object instanceof PhabricatorUser) {
$translation = $object->getTranslation();
}
if (!$translation) {
$translation = $default_translation;
}
if ($return && $translation != $return) {
return $default_translation;
}
$return = $translation;
}
if (!$return) {
$return = $default_translation;
}
return $return;
}
public function addPHIDHeaders($name, array $phids) {
$phids = array_unique($phids);
foreach ($phids as $phid) {
@ -940,9 +913,10 @@ final class PhabricatorMetaMTAMail
}
}
$all_prefs = id(new PhabricatorUserPreferences())->loadAllWhere(
'userPHID in (%Ls)',
$actor_phids);
$all_prefs = id(new PhabricatorUserPreferencesQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withUserPHIDs($actor_phids)
->execute();
$all_prefs = mpull($all_prefs, null, 'getUserPHID');
$value_email = PhabricatorUserPreferences::MAILTAG_PREFERENCE_EMAIL;

View file

@ -0,0 +1,70 @@
<?php
abstract class PhabricatorUserCacheType extends Phobject {
final public function getViewer() {
return PhabricatorUser::getOmnipotentUser();
}
public function getAutoloadKeys() {
return array();
}
public function canManageKey($key) {
return false;
}
public function getDefaultValue() {
return array();
}
public function getValueFromStorage($value) {
return phutil_json_decode($value);
}
public function getValueForStorage($value) {
return phutil_json_encode($value);
}
public function newValueForUsers($key, array $users) {
return array();
}
final public function getUserCacheType() {
return $this->getPhobjectClassConstant('CACHETYPE');
}
public static function getAllCacheTypes() {
return id(new PhutilClassMapQuery())
->setAncestorClass(__CLASS__)
->setUniqueMethod('getUserCacheType')
->execute();
}
public static function getCacheTypeForKey($key) {
$all = self::getAllCacheTypes();
foreach ($all as $type) {
if ($type->canManageKey($key)) {
return $type;
}
}
return null;
}
public static function requireCacheTypeForKey($key) {
$type = self::getCacheTypeForKey($key);
if (!$type) {
throw new Exception(
pht(
'Failed to load UserCacheType to manage key "%s". This cache type '.
'is required.',
$key));
}
return $type;
}
}

View file

@ -0,0 +1,31 @@
<?php
final class PhabricatorUserPreferencesCacheType
extends PhabricatorUserCacheType {
const CACHETYPE = 'preferences';
const KEY_PREFERENCES = 'user.preferences.v1';
public function getAutoloadKeys() {
return array(
self::KEY_PREFERENCES,
);
}
public function canManageKey($key) {
return ($key === self::KEY_PREFERENCES);
}
public function newValueForUsers($key, array $users) {
$viewer = $this->getViewer();
$preferences = id(new PhabricatorUserPreferencesQuery())
->setViewer($viewer)
->withUserPHIDs(mpull($users, 'getPHID'))
->execute();
return mpull($preferences, 'getPreferences', 'getUserPHID');
}
}

View file

@ -136,6 +136,10 @@ final class PhabricatorMentionRemarkupRule extends PhutilRemarkupRule {
),
'@'.$user->getUserName());
} else {
if ($engine->getConfig('uri.full')) {
$user_href = PhabricatorEnv::getURI($user_href);
}
$tag = id(new PHUITagView())
->setType(PHUITagView::TYPE_PERSON)
->setPHID($user->getPHID())

View file

@ -5,6 +5,8 @@
* @task image-cache Profile Image Cache
* @task factors Multi-Factor Authentication
* @task handles Managing Handles
* @task settings Settings
* @task cache User Cache
*/
final class PhabricatorUser
extends PhabricatorUserDAO
@ -25,15 +27,12 @@ final class PhabricatorUser
protected $userName;
protected $realName;
protected $sex;
protected $translation;
protected $passwordSalt;
protected $passwordHash;
protected $profileImagePHID;
protected $profileImageCache;
protected $availabilityCache;
protected $availabilityCacheTTL;
protected $timezoneIdentifier = '';
protected $consoleEnabled = 0;
protected $consoleVisible = 0;
@ -61,18 +60,16 @@ final class PhabricatorUser
private $alternateCSRFString = self::ATTACHABLE;
private $session = self::ATTACHABLE;
private $rawCacheData = array();
private $usableCacheData = array();
private $authorities = array();
private $handlePool;
private $csrfSalt;
private $timezoneOverride;
protected function readField($field) {
switch ($field) {
case 'timezoneIdentifier':
// If the user hasn't set one, guess the server's time.
return nonempty(
$this->timezoneIdentifier,
date_default_timezone_get());
// Make sure these return booleans.
case 'isAdmin':
return (bool)$this->isAdmin;
@ -188,8 +185,6 @@ final class PhabricatorUser
self::CONFIG_COLUMN_SCHEMA => array(
'userName' => 'sort64',
'realName' => 'text128',
'sex' => 'text4?',
'translation' => 'text64?',
'passwordSalt' => 'text32?',
'passwordHash' => 'text128?',
'profileImagePHID' => 'phid?',
@ -201,7 +196,6 @@ final class PhabricatorUser
'isMailingList' => 'bool',
'isDisabled' => 'bool',
'isAdmin' => 'bool',
'timezoneIdentifier' => 'text255',
'isEmailVerified' => 'uint32',
'isApproved' => 'uint32',
'accountSecret' => 'bytes64',
@ -258,11 +252,6 @@ final class PhabricatorUser
return $this;
}
// To satisfy PhutilPerson.
public function getSex() {
return $this->sex;
}
public function getMonogram() {
return '@'.$this->getUsername();
}
@ -487,6 +476,71 @@ final class PhabricatorUser
'(isPrimary = 1)');
}
/* -( Settings )----------------------------------------------------------- */
public function getUserSetting($key) {
$settings_key = PhabricatorUserPreferencesCacheType::KEY_PREFERENCES;
$settings = $this->requireCacheData($settings_key);
if (array_key_exists($key, $settings)) {
return $settings[$key];
}
$defaults = PhabricatorSetting::getAllEnabledSettings($this);
if (isset($defaults[$key])) {
return $defaults[$key]->getSettingDefaultValue();
}
return null;
}
/**
* Test if a given setting is set to a particular value.
*
* @param const Setting key.
* @param wild Value to compare.
* @return bool True if the setting has the specified value.
* @task settings
*/
public function compareUserSetting($key, $value) {
$actual = $this->getUserSetting($key);
return ($actual == $value);
}
public function getTranslation() {
return $this->getUserSetting(PhabricatorTranslationSetting::SETTINGKEY);
}
public function getTimezoneIdentifier() {
if ($this->timezoneOverride) {
return $this->timezoneOverride;
}
return $this->getUserSetting(PhabricatorTimezoneSetting::SETTINGKEY);
}
/**
* Override the user's timezone identifier.
*
* This is primarily useful for unit tests.
*
* @param string New timezone identifier.
* @return this
* @task settings
*/
public function overrideTimezoneIdentifier($identifier) {
$this->timezoneOverride = $identifier;
return $this;
}
public function getSex() {
return $this->getUserSetting(PhabricatorPronounSetting::SETTINGKEY);
}
public function loadPreferences() {
if ($this->preferences) {
return $this->preferences;
@ -494,14 +548,16 @@ final class PhabricatorUser
$preferences = null;
if ($this->getPHID()) {
$preferences = id(new PhabricatorUserPreferences())->loadOneWhere(
'userPHID = %s',
$this->getPHID());
$preferences = id(new PhabricatorUserPreferencesQuery())
->setViewer($this)
->withUsers(array($this))
->executeOne();
}
if (!$preferences) {
$preferences = new PhabricatorUserPreferences();
$preferences->setUserPHID($this->getPHID());
$preferences->attachUser($this);
$default_dict = array(
PhabricatorUserPreferences::PREFERENCE_TITLES => 'glyph',
@ -968,6 +1024,10 @@ final class PhabricatorUser
* @task availability
*/
public function writeAvailabilityCache(array $availability, $ttl) {
if (PhabricatorEnv::isReadOnly()) {
return $this;
}
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
queryfx(
$this->establishConnection('w'),
@ -1289,11 +1349,12 @@ final class PhabricatorUser
$external->delete();
}
$prefs = id(new PhabricatorUserPreferences())->loadAllWhere(
'userPHID = %s',
$this->getPHID());
$prefs = id(new PhabricatorUserPreferencesQuery())
->setViewer($engine->getViewer())
->withUsers(array($this))
->execute();
foreach ($prefs as $pref) {
$pref->delete();
$engine->destroyObject($pref);
}
$profiles = id(new PhabricatorUserProfile())->loadAllWhere(
@ -1454,4 +1515,68 @@ final class PhabricatorUser
}
/* -( User Cache )--------------------------------------------------------- */
/**
* @task cache
*/
public function attachRawCacheData(array $data) {
$this->rawCacheData = $data + $this->rawCacheData;
return $this;
}
/**
* @task cache
*/
protected function requireCacheData($key) {
if (isset($this->usableCacheData[$key])) {
return $this->usableCacheData[$key];
}
$type = PhabricatorUserCacheType::requireCacheTypeForKey($key);
if (isset($this->rawCacheData[$key])) {
$raw_value = $this->rawCacheData[$key];
$usable_value = $type->getValueFromStorage($raw_value);
$this->usableCacheData[$key] = $usable_value;
return $usable_value;
}
$usable_value = $type->getDefaultValue();
$user_phid = $this->getPHID();
if ($user_phid) {
$map = $type->newValueForUsers($key, array($this));
if (array_key_exists($user_phid, $map)) {
$usable_value = $map[$user_phid];
$raw_value = $type->getValueForStorage($usable_value);
$this->rawCacheData[$key] = $raw_value;
PhabricatorUserCache::writeCache(
$type,
$key,
$user_phid,
$raw_value);
}
}
$this->usableCacheData[$key] = $usable_value;
return $usable_value;
}
/**
* @task cache
*/
public function clearCacheData($key) {
unset($this->rawCacheData[$key]);
unset($this->usableCacheData[$key]);
return $this;
}
}

View file

@ -0,0 +1,110 @@
<?php
final class PhabricatorUserCache extends PhabricatorUserDAO {
protected $userPHID;
protected $cacheIndex;
protected $cacheKey;
protected $cacheData;
protected $cacheType;
protected function getConfiguration() {
return array(
self::CONFIG_TIMESTAMPS => false,
self::CONFIG_COLUMN_SCHEMA => array(
'cacheIndex' => 'bytes12',
'cacheKey' => 'text255',
'cacheData' => 'text',
'cacheType' => 'text32',
),
self::CONFIG_KEY_SCHEMA => array(
'key_usercache' => array(
'columns' => array('userPHID', 'cacheIndex'),
'unique' => true,
),
'key_cachekey' => array(
'columns' => array('cacheIndex'),
),
'key_cachetype' => array(
'columns' => array('cacheType'),
),
),
) + parent::getConfiguration();
}
public function save() {
$this->cacheIndex = Filesystem::digestForIndex($this->getCacheKey());
return parent::save();
}
public static function writeCache(
PhabricatorUserCacheType $type,
$key,
$user_phid,
$raw_value) {
if (PhabricatorEnv::isReadOnly()) {
return;
}
$table = new self();
$conn_w = $table->establishConnection('w');
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
queryfx(
$conn_w,
'INSERT INTO %T (userPHID, cacheIndex, cacheKey, cacheData, cacheType)
VALUES (%s, %s, %s, %s, %s)
ON DUPLICATE KEY UPDATE cacheData = VALUES(cacheData)',
$table->getTableName(),
$user_phid,
PhabricatorHash::digestForIndex($key),
$key,
$raw_value,
$type->getUserCacheType());
unset($unguarded);
}
public static function clearCache($key, $user_phid) {
if (PhabricatorEnv::isReadOnly()) {
return;
}
$table = new self();
$conn_w = $table->establishConnection('w');
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
queryfx(
$conn_w,
'DELETE FROM %T WHERE cacheIndex = %s AND userPHID = %s',
$table->getTableName(),
PhabricatorHash::digestForIndex($key),
$user_phid);
unset($unguarded);
}
public static function clearCacheForAllUsers($key) {
if (PhabricatorEnv::isReadOnly()) {
return;
}
$table = new self();
$conn_w = $table->establishConnection('w');
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
queryfx(
$conn_w,
'DELETE FROM %T WHERE cacheIndex = %s',
$table->getTableName(),
PhabricatorHash::digestForIndex($key));
unset($unguarded);
}
}

View file

@ -93,7 +93,9 @@ final class PhabricatorPhameApplication extends PhabricatorApplication {
'/' => array(
'' => 'PhameBlogViewController',
'post/(?P<id>\d+)/(?:(?P<slug>[^/]+)/)?' => 'PhamePostViewController',
'.*' => 'PhameBlog404Controller',
),
);
}

View file

@ -70,6 +70,8 @@ abstract class PhameLiveController extends PhameController {
$blog = $blog_query->executeOne();
if (!$blog) {
$this->isExternal = $is_external;
$this->isLive = $is_live;
return new Aphront404Response();
}
@ -81,6 +83,9 @@ abstract class PhameLiveController extends PhameController {
$blog = null;
}
$this->isExternal = $is_external;
$this->isLive = $is_live;
if ($post_id) {
$post_query = id(new PhamePostQuery())
->setViewer($viewer)
@ -109,8 +114,6 @@ abstract class PhameLiveController extends PhameController {
$post = null;
}
$this->isExternal = $is_external;
$this->isLive = $is_live;
$this->blog = $blog;
$this->post = $post;
@ -188,4 +191,30 @@ abstract class PhameLiveController extends PhameController {
return $crumbs;
}
public function willSendResponse(AphrontResponse $response) {
if ($this->getIsExternal()) {
if ($response instanceof Aphront404Response) {
$page = $this->newPage()
->setCrumbs($this->buildApplicationCrumbs());
$response = id(new Phame404Response())
->setPage($page);
}
}
return parent::willSendResponse($response);
}
public function newPage() {
$page = parent::newPage();
if ($this->getIsLive()) {
$page
->setShowChrome(false)
->setShowFooter(false);
}
return $page;
}
}

View file

@ -0,0 +1,14 @@
<?php
final class PhameBlog404Controller extends PhameLiveController {
public function handleRequest(AphrontRequest $request) {
$response = $this->setupLiveEnvironment();
if ($response) {
return $response;
}
return new Aphront404Response();
}
}

View file

@ -108,12 +108,6 @@ final class PhameBlogViewController extends PhameLiveController {
$about,
));
if ($is_live) {
$page
->setShowChrome(false)
->setShowFooter(false);
}
return $page;
}

View file

@ -78,10 +78,14 @@ final class PhamePostViewController
->executeOne();
$blogger_profile = $blogger->loadUserProfile();
$author_uri = '/p/'.$blogger->getUsername().'/';
$author_uri = PhabricatorEnv::getURI($author_uri);
$author = phutil_tag(
'a',
array(
'href' => '/p/'.$blogger->getUsername().'/',
'href' => $author_uri,
),
$blogger->getUsername());
@ -105,7 +109,7 @@ final class PhamePostViewController
$blogger_profile->getTitle(),
))
->setImage($blogger->getProfileImageURI())
->setImageHref('/p/'.$blogger->getUsername());
->setImageHref($author_uri);
$timeline = $this->buildTransactionTimeline(
$post,
@ -128,10 +132,10 @@ final class PhamePostViewController
$next_view = new PhameNextPostView();
if ($next) {
$next_view->setNext($next->getTitle(), $next->getViewURI());
$next_view->setNext($next->getTitle(), $next->getLiveURI());
}
if ($prev) {
$next_view->setPrevious($prev->getTitle(), $prev->getViewURI());
$next_view->setPrevious($prev->getTitle(), $prev->getLiveURI());
}
$document->setFoot($next_view);

View file

@ -0,0 +1,43 @@
<?php
final class Phame404Response extends AphrontHTMLResponse {
private $page;
public function setPage(AphrontPageView $page) {
$this->page = $page;
return $this;
}
public function getPage() {
return $this->page;
}
public function getHTTPResponseCode() {
return 404;
}
public function buildResponseString() {
require_celerity_resource('phame-css');
$not_found = phutil_tag(
'div',
array(
'class' => 'phame-404',
),
array(
phutil_tag('strong', array(), pht('404 Not Found')),
phutil_tag('br'),
pht('Wherever you go, there you are.'),
phutil_tag('br'),
pht('But the page you seek is elsewhere.'),
));
$page = $this->getPage()
->setTitle(pht('404 Not Found'))
->appendChild($not_found);
return $page->render();
}
}

View file

@ -62,8 +62,19 @@ final class PhamePostListView extends AphrontTagView {
$list = array();
foreach ($posts as $post) {
$blogger = $handles[$post->getBloggerPHID()]->renderLink();
$blogger_name = $handles[$post->getBloggerPHID()]->getName();
$blogger_uri = $handles[$post->getBloggerPHID()]->getURI();
$blogger_uri = PhabricatorEnv::getURI($blogger_uri);
// Render a link manually to make sure we point at the correct domain.
$blogger = phutil_tag(
'a',
array(
'href' => $blogger_uri,
),
$blogger_name);
$blogger = phutil_tag('strong', array(), $blogger);
$blogger_image = $handles[$post->getBloggerPHID()]->getImageURI();
$phame_post = null;
@ -74,7 +85,6 @@ final class PhamePostListView extends AphrontTagView {
$phame_post = phutil_tag('em', array(), pht('(Empty Post)'));
}
$blogger = phutil_tag('strong', array(), $blogger);
$date = phabricator_datetime($post->getDatePublished(), $viewer);
$blog = $post->getBlog();

View file

@ -201,8 +201,7 @@ final class PhabricatorPolicyQuery
$default_limit = 5;
// If possible, show the user's 10 most recently used projects.
$preferences = $viewer->loadPreferences();
$favorites = $preferences->getPreference($pref_key);
$favorites = $viewer->getUserSetting($pref_key);
if (!is_array($favorites)) {
$favorites = array();
}

View file

@ -21,12 +21,17 @@ final class PhabricatorProjectMembersAddController
}
$this->setProject($project);
$done_uri = "/project/members/{$id}/";
if (!$project->supportsEditMembers()) {
return new Aphront404Response();
}
$copy = pht('Parent projects and milestones do not support adding '.
'members. You can add members directly to any non-parent subproject.');
$done_uri = "/project/members/{$id}/";
return $this->newDialog()
->setTitle(pht('Unsupported Project'))
->appendParagraph($copy)
->addCancelButton($done_uri);
}
if ($request->isFormPost()) {
$member_phids = $request->getArr('memberPHIDs');

View file

@ -32,12 +32,18 @@ final class PhabricatorProjectUpdateController
return new Aphront404Response();
}
if (!$project->supportsEditMembers()) {
return new Aphront404Response();
}
$done_uri = "/project/members/{$id}/";
if (!$project->supportsEditMembers()) {
$copy = pht('Parent projects and milestones do not support adding '.
'members. You can add members directly to any non-parent subproject.');
return $this->newDialog()
->setTitle(pht('Unsupported Project'))
->appendParagraph($copy)
->addCancelButton($done_uri);
}
if ($request->isFormPost()) {
$edge_action = null;
switch ($action) {

View file

@ -63,6 +63,7 @@ final class PhabricatorRepositoryDiscoveryEngine
private function discoverCommitsWithLock() {
$repository = $this->getRepository();
$viewer = $this->getViewer();
$vcs = $repository->getVersionControlSystem();
switch ($vcs) {
@ -104,6 +105,14 @@ final class PhabricatorRepositoryDiscoveryEngine
$this->commitCache[$ref->getIdentifier()] = true;
}
$version = $this->getObservedVersion($repository);
if ($version !== null) {
id(new DiffusionRepositoryClusterEngine())
->setViewer($viewer)
->setRepository($repository)
->synchronizeWorkingCopyAfterDiscovery($version);
}
return $refs;
}
@ -121,9 +130,15 @@ final class PhabricatorRepositoryDiscoveryEngine
$this->verifyGitOrigin($repository);
}
// TODO: This should also import tags, but some of the logic is still
// branch-specific today.
$branches = id(new DiffusionLowLevelGitRefQuery())
->setRepository($repository)
->withIsOriginBranch(true)
->withRefTypes(
array(
PhabricatorRepositoryRefCursor::TYPE_BRANCH,
))
->execute();
if (!$branches) {
@ -642,4 +657,49 @@ final class PhabricatorRepositoryDiscoveryEngine
return true;
}
private function getObservedVersion(PhabricatorRepository $repository) {
if ($repository->isHosted()) {
return null;
}
if ($repository->isGit()) {
return $this->getGitObservedVersion($repository);
}
return null;
}
private function getGitObservedVersion(PhabricatorRepository $repository) {
$refs = id(new DiffusionLowLevelGitRefQuery())
->setRepository($repository)
->execute();
if (!$refs) {
return null;
}
// In Git, the observed version is the most recently discovered commit
// at any repository HEAD. It's possible for this to regress temporarily
// if a branch is pushed and then deleted. This is acceptable because it
// doesn't do anything meaningfully bad and will fix itself on the next
// push.
$ref_identifiers = mpull($refs, 'getCommitIdentifier');
$ref_identifiers = array_fuse($ref_identifiers);
$version = queryfx_one(
$repository->establishConnection('w'),
'SELECT MAX(id) version FROM %T WHERE repositoryID = %d
AND commitIdentifier IN (%Ls)',
id(new PhabricatorRepositoryCommit())->getTableName(),
$repository->getID(),
$ref_identifiers);
if (!$version) {
return null;
}
return (int)$version['version'];
}
}

View file

@ -108,27 +108,27 @@ final class PhabricatorRepositoryPullEngine
} else {
$this->executeSubversionCreate();
}
} else {
if (!$repository->isHosted()) {
$this->logPull(
pht(
'Updating the working copy for repository "%s".',
$repository->getDisplayName()));
if ($is_git) {
$this->verifyGitOrigin($repository);
$this->executeGitUpdate();
} else if ($is_hg) {
$this->executeMercurialUpdate();
}
}
id(new DiffusionRepositoryClusterEngine())
->setViewer($viewer)
->setRepository($repository)
->synchronizeWorkingCopyBeforeRead();
if (!$repository->isHosted()) {
$this->logPull(
pht(
'Updating the working copy for repository "%s".',
$repository->getDisplayName()));
if ($is_git) {
$this->verifyGitOrigin($repository);
$this->executeGitUpdate();
} else if ($is_hg) {
$this->executeMercurialUpdate();
}
}
if ($repository->isHosted()) {
id(new DiffusionRepositoryClusterEngine())
->setViewer($viewer)
->setRepository($repository)
->synchronizeWorkingCopyBeforeRead();
if ($is_git) {
$this->installGitHook();
} else if ($is_svn) {

View file

@ -452,7 +452,10 @@ final class PhabricatorRepositoryRefEngine
private function loadGitBranchPositions(PhabricatorRepository $repository) {
return id(new DiffusionLowLevelGitRefQuery())
->setRepository($repository)
->withIsOriginBranch(true)
->withRefTypes(
array(
PhabricatorRepositoryRefCursor::TYPE_BRANCH,
))
->execute();
}
@ -463,7 +466,10 @@ final class PhabricatorRepositoryRefEngine
private function loadGitTagPositions(PhabricatorRepository $repository) {
return id(new DiffusionLowLevelGitRefQuery())
->setRepository($repository)
->withIsTag(true)
->withRefTypes(
array(
PhabricatorRepositoryRefCursor::TYPE_TAG,
))
->execute();
}

View file

@ -2061,7 +2061,10 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
$clone[] = $uri;
}
return msort($clone, 'getURIScore');
$clone = msort($clone, 'getURIScore');
$clone = array_reverse($clone);
return $clone;
}

View file

@ -598,18 +598,25 @@ final class PhabricatorRepositoryURI
$score = 0;
$io_points = array(
self::IO_READWRITE => 20,
self::IO_READ => 10,
self::IO_READWRITE => 200,
self::IO_READ => 100,
);
$score += idx($io_points, $this->getEffectiveIoType(), 0);
$protocol_points = array(
self::BUILTIN_PROTOCOL_SSH => 3,
self::BUILTIN_PROTOCOL_HTTPS => 2,
self::BUILTIN_PROTOCOL_HTTP => 1,
self::BUILTIN_PROTOCOL_SSH => 30,
self::BUILTIN_PROTOCOL_HTTPS => 20,
self::BUILTIN_PROTOCOL_HTTP => 10,
);
$score += idx($protocol_points, $this->getBuiltinProtocol(), 0);
$identifier_points = array(
self::BUILTIN_IDENTIFIER_SHORTNAME => 3,
self::BUILTIN_IDENTIFIER_CALLSIGN => 2,
self::BUILTIN_IDENTIFIER_ID => 1,
);
$score += idx($identifier_points, $this->getBuiltinIdentifier(), 0);
return $score;
}

View file

@ -385,8 +385,7 @@ abstract class PhabricatorProfilePanelEngine extends Phobject {
$collapse_key =
PhabricatorUserPreferences::PREFERENCE_PROFILE_MENU_COLLAPSED;
$preferences = $viewer->loadPreferences();
$is_collapsed = $preferences->getPreference($collapse_key, false);
$is_collapsed = $viewer->getUserSetting($collapse_key);
if ($is_collapsed) {
$nav->addClass('phui-profile-menu-collapsed');

View file

@ -68,7 +68,7 @@ abstract class PhabricatorSearchEngineAPIMethod
'This is a standard **ApplicationSearch** method which will let you '.
'list, query, or search for objects. For documentation on these '.
'endpoints, see **[[ %s | Conduit API: Using Search Endpoints ]]**.',
PhabricatorEnv::getDoclink('Conduit API: Using Edit Endpoints'));
PhabricatorEnv::getDoclink('Conduit API: Using Search Endpoints'));
}
final public function getMethodDocumentation() {

View file

@ -34,6 +34,8 @@ final class PhabricatorSettingsApplication extends PhabricatorApplication {
'adjust/' => 'PhabricatorSettingsAdjustController',
'timezone/(?P<offset>[^/]+)/'
=> 'PhabricatorSettingsTimezoneController',
'(?P<type>user)/(?P<username>[^/]+)/(?:panel/(?P<key>[^/]+)/)?'
=> 'PhabricatorSettingsEditController',
),
);
}

View file

@ -0,0 +1,35 @@
<?php
final class PhabricatorSettingsEditController
extends PhabricatorController {
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$engine = id(new PhabricatorSettingsEditEngine())
->setController($this);
switch ($request->getURIData('type')) {
case 'user':
$user = id(new PhabricatorPeopleQuery())
->setViewer($viewer)
->withUsernames(array($request->getURIData('username')))
->executeOne();
$preferences = $user->loadPreferences();
PhabricatorPolicyFilter::requireCapability(
$viewer,
$preferences,
PhabricatorPolicyCapability::CAN_EDIT);
$engine->setTargetObject($preferences);
break;
default:
return new Aphront404Response();
}
return $engine->buildResponse();
}
}

View file

@ -31,6 +31,7 @@ final class PhabricatorSettingsTimezoneController
$timezone = $request->getStr('timezone');
$pref_ignore = PhabricatorUserPreferences::PREFERENCE_IGNORE_OFFSET;
$pref_timezone = PhabricatorTimezoneSetting::SETTINGKEY;
$preferences = $viewer->loadPreferences();
@ -52,11 +53,11 @@ final class PhabricatorSettingsTimezoneController
if (isset($options[$timezone])) {
$preferences
->setPreference($pref_ignore, null)
->setPreference($pref_timezone, $timezone)
->save();
$viewer
->setTimezoneIdentifier($timezone)
->save();
$viewer->clearCacheData(
PhabricatorUserPreferencesCacheType::KEY_PREFERENCES);
}
}
@ -115,9 +116,9 @@ final class PhabricatorSettingsTimezoneController
$offset = $offset / 60;
if ($offset >= 0) {
return pht('GMT-%d', $offset);
return pht('UTC-%d', $offset);
} else {
return pht('GMT+%d', -$offset);
return pht('UTC+%d', -$offset);
}
}

View file

@ -0,0 +1,97 @@
<?php
final class PhabricatorSettingsEditEngine
extends PhabricatorEditEngine {
const ENGINECONST = 'settings.settings';
public function isEngineConfigurable() {
return false;
}
public function getEngineName() {
return pht('Settings');
}
public function getSummaryHeader() {
return pht('Edit Settings Configurations');
}
public function getSummaryText() {
return pht('This engine is used to edit settings.');
}
public function getEngineApplicationClass() {
return 'PhabricatorSettingsApplication';
}
protected function newEditableObject() {
return new PhabricatorUserPreferences();
}
protected function newObjectQuery() {
return new PhabricatorUserPreferencesQuery();
}
protected function getObjectCreateTitleText($object) {
return pht('Create Settings');
}
protected function getObjectCreateButtonText($object) {
return pht('Create Settings');
}
protected function getObjectEditTitleText($object) {
return pht('Edit Settings');
}
protected function getObjectEditShortText($object) {
return pht('Edit Settings');
}
protected function getObjectCreateShortText() {
return pht('Create Settings');
}
protected function getObjectName() {
return pht('Settings');
}
protected function getEditorURI() {
return '/settings/edit/';
}
protected function getObjectCreateCancelURI($object) {
return '/settings/';
}
protected function getObjectViewURI($object) {
// TODO: This isn't correct...
return '/settings/user/'.$this->getViewer()->getUsername().'/';
}
protected function getCreateNewObjectPolicy() {
return PhabricatorPolicies::POLICY_ADMIN;
}
protected function buildCustomEditFields($object) {
$viewer = $this->getViewer();
$settings = PhabricatorSetting::getAllEnabledSettings($viewer);
foreach ($settings as $key => $setting) {
$setting = clone $setting;
$setting->setViewer($viewer);
$settings[$key] = $setting;
}
$fields = array();
foreach ($settings as $setting) {
foreach ($setting->newCustomEditFields($object) as $field) {
$fields[] = $field;
}
}
return $fields;
}
}

View file

@ -0,0 +1,154 @@
<?php
final class PhabricatorUserPreferencesEditor
extends PhabricatorApplicationTransactionEditor {
public function getEditorApplicationClass() {
return 'PhabricatorSettingsApplication';
}
public function getEditorObjectsDescription() {
return pht('Settings');
}
public function getTransactionTypes() {
$types = parent::getTransactionTypes();
$types[] = PhabricatorUserPreferencesTransaction::TYPE_SETTING;
return $types;
}
protected function getCustomTransactionOldValue(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
$setting_key = $xaction->getMetadataValue(
PhabricatorUserPreferencesTransaction::PROPERTY_SETTING);
switch ($xaction->getTransactionType()) {
case PhabricatorUserPreferencesTransaction::TYPE_SETTING:
return $object->getPreference($setting_key);
}
return parent::getCustomTransactionOldValue($object, $xaction);
}
protected function getCustomTransactionNewValue(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
$actor = $this->getActor();
$setting_key = $xaction->getMetadataValue(
PhabricatorUserPreferencesTransaction::PROPERTY_SETTING);
$settings = PhabricatorSetting::getAllEnabledSettings($actor);
$setting = $settings[$setting_key];
switch ($xaction->getTransactionType()) {
case PhabricatorUserPreferencesTransaction::TYPE_SETTING:
$value = $xaction->getNewValue();
$value = $setting->getTransactionNewValue($value);
return $value;
}
return parent::getCustomTransactionNewValue($object, $xaction);
}
protected function applyCustomInternalTransaction(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
$setting_key = $xaction->getMetadataValue(
PhabricatorUserPreferencesTransaction::PROPERTY_SETTING);
switch ($xaction->getTransactionType()) {
case PhabricatorUserPreferencesTransaction::TYPE_SETTING:
$new_value = $xaction->getNewValue();
if ($new_value === null) {
$object->unsetPreference($setting_key);
} else {
$object->setPreference($setting_key, $new_value);
}
return;
}
return parent::applyCustomInternalTransaction($object, $xaction);
}
protected function applyCustomExternalTransaction(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case PhabricatorUserPreferencesTransaction::TYPE_SETTING:
return;
}
return parent::applyCustomExternalTransaction($object, $xaction);
}
protected function validateTransaction(
PhabricatorLiskDAO $object,
$type,
array $xactions) {
$errors = parent::validateTransaction($object, $type, $xactions);
$actor = $this->getActor();
$settings = PhabricatorSetting::getAllEnabledSettings($actor);
switch ($type) {
case PhabricatorUserPreferencesTransaction::TYPE_SETTING:
foreach ($xactions as $xaction) {
$setting_key = $xaction->getMetadataValue(
PhabricatorUserPreferencesTransaction::PROPERTY_SETTING);
$setting = idx($settings, $setting_key);
if (!$setting) {
$errors[] = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Invalid'),
pht(
'There is no known application setting with key "%s".',
$setting_key),
$xaction);
continue;
}
try {
$setting->validateTransactionValue($xaction->getNewValue());
} catch (Exception $ex) {
$errors[] = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Invalid'),
$ex->getMessage(),
$xaction);
}
}
break;
}
return $errors;
}
protected function applyFinalEffects(
PhabricatorLiskDAO $object,
array $xactions) {
$user_phid = $object->getUserPHID();
if ($user_phid) {
PhabricatorUserCache::clearCache(
PhabricatorUserPreferencesCacheType::KEY_PREFERENCES,
$user_phid);
} else {
PhabricatorUserCache::clearCacheForAllUsers(
PhabricatorUserPreferencesCacheType::KEY_PREFERENCES);
}
return $xactions;
}
}

View file

@ -23,21 +23,29 @@ final class PhabricatorAccountSettingsPanel extends PhabricatorSettingsPanel {
$user = $this->getUser();
$username = $user->getUsername();
$preferences = $user->loadPreferences();
$errors = array();
if ($request->isFormPost()) {
$sex = $request->getStr('sex');
$sexes = array(PhutilPerson::SEX_MALE, PhutilPerson::SEX_FEMALE);
if (in_array($sex, $sexes)) {
$user->setSex($sex);
$new_value = $sex;
} else {
$user->setSex(null);
$new_value = null;
}
// Checked in runtime.
$user->setTranslation($request->getStr('translation'));
$preferences->setPreference(
PhabricatorPronounSetting::SETTINGKEY,
$new_value);
$preferences->setPreference(
PhabricatorTranslationSetting::SETTINGKEY,
$request->getStr('translation'));
if (!$errors) {
$user->save();
$preferences->save();
return id(new AphrontRedirectResponse())
->setURI($this->getPanelURI('?saved=true'));
}

View file

@ -18,6 +18,7 @@ final class PhabricatorDateTimeSettingsPanel extends PhabricatorSettingsPanel {
$user = $request->getUser();
$username = $user->getUsername();
$pref_timezone = PhabricatorTimezoneSetting::SETTINGKEY;
$pref_time = PhabricatorUserPreferences::PREFERENCE_TIME_FORMAT;
$pref_date = PhabricatorUserPreferences::PREFERENCE_DATE_FORMAT;
$pref_week_start = PhabricatorUserPreferences::PREFERENCE_WEEK_START_DAY;
@ -27,13 +28,12 @@ final class PhabricatorDateTimeSettingsPanel extends PhabricatorSettingsPanel {
$errors = array();
if ($request->isFormPost()) {
$new_timezone = $request->getStr('timezone');
if (in_array($new_timezone, DateTimeZone::listIdentifiers(), true)) {
$user->setTimezoneIdentifier($new_timezone);
} else {
if (!in_array($new_timezone, DateTimeZone::listIdentifiers(), true)) {
$errors[] = pht('The selected timezone is not a valid timezone.');
}
$preferences
->setPreference($pref_timezone, $new_timezone)
->setPreference(
$pref_time,
$request->getStr($pref_time))
@ -47,7 +47,7 @@ final class PhabricatorDateTimeSettingsPanel extends PhabricatorSettingsPanel {
if (!$errors) {
$preferences->save();
$user->save();
return id(new AphrontRedirectResponse())
->setURI($this->getPanelURI('?saved=true'));
}

View file

@ -0,0 +1,39 @@
<?php
final class PhabricatorUserPreferencesPHIDType extends PhabricatorPHIDType {
const TYPECONST = 'PSET';
public function getTypeName() {
return pht('Settings');
}
public function newObject() {
return new PhabricatorUserPreferences();
}
public function getPHIDTypeApplicationClass() {
return 'PhabricatorSettingsApplication';
}
protected function buildQueryForObjects(
PhabricatorObjectQuery $query,
array $phids) {
return id(new PhabricatorUserPreferencesQuery())
->withPHIDs($phids);
}
public function loadHandles(
PhabricatorHandleQuery $query,
array $handles,
array $objects) {
$viewer = $query->getViewer();
foreach ($handles as $phid => $handle) {
$preferences = $objects[$phid];
$handle->setName(pht('Settings %d', $preferences->getID()));
}
}
}

View file

@ -0,0 +1,118 @@
<?php
final class PhabricatorUserPreferencesQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $phids;
private $userPHIDs;
private $users = array();
public function withIDs(array $ids) {
$this->ids = $ids;
return $this;
}
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
public function withUserPHIDs(array $phids) {
$this->userPHIDs = $phids;
return $this;
}
public function withUsers(array $users) {
assert_instances_of($users, 'PhabricatorUser');
$this->users = mpull($users, null, 'getPHID');
$this->withUserPHIDs(array_keys($this->users));
return $this;
}
public function newResultObject() {
return new PhabricatorUserPreferences();
}
protected function loadPage() {
return $this->loadStandardPage($this->newResultObject());
}
protected function willFilterPage(array $prefs) {
$user_phids = mpull($prefs, 'getUserPHID');
$user_phids = array_filter($user_phids);
// If some of the preferences are attached to users, try to use any objects
// we were handed first. If we're missing some, load them.
if ($user_phids) {
$users = $this->users;
$user_phids = array_fuse($user_phids);
$load_phids = array_diff_key($user_phids, $users);
$load_phids = array_keys($load_phids);
if ($load_phids) {
$load_users = id(new PhabricatorPeopleQuery())
->setViewer($this->getViewer())
->withPHIDs($load_phids)
->execute();
$load_users = mpull($load_users, null, 'getPHID');
$users += $load_users;
}
} else {
$users = array();
}
foreach ($prefs as $key => $pref) {
$user_phid = $pref->getUserPHID();
if (!$user_phid) {
$pref->attachUser(null);
continue;
}
$user = idx($users, $user_phid);
if (!$user) {
$this->didRejectResult($pref);
unset($prefs[$key]);
continue;
}
$pref->attachUser($user);
}
return $prefs;
}
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
$where = parent::buildWhereClauseParts($conn);
if ($this->ids !== null) {
$where[] = qsprintf(
$conn,
'id IN (%Ld)',
$this->ids);
}
if ($this->phids !== null) {
$where[] = qsprintf(
$conn,
'phid IN (%Ls)',
$this->phids);
}
if ($this->userPHIDs !== null) {
$where[] = qsprintf(
$conn,
'userPHID IN (%Ls)',
$this->userPHIDs);
}
return $where;
}
public function getQueryApplicationClass() {
return 'PhabricatorSettingsApplication';
}
}

View file

@ -0,0 +1,39 @@
<?php
final class PhabricatorAccessibilitySetting
extends PhabricatorSelectSetting {
const SETTINGKEY = 'resource-postprocessor';
public function getSettingName() {
return pht('Accessibility');
}
protected function getControlInstructions() {
return pht(
'If you have difficulty reading the Phabricator UI, these settings '.
'may make Phabricator more accessible.');
}
public function getSettingDefaultValue() {
return CelerityDefaultPostprocessor::POSTPROCESSOR_KEY;
}
protected function getSelectOptions() {
$postprocessor_map = CelerityPostprocessor::getAllPostprocessors();
$postprocessor_map = mpull($postprocessor_map, 'getPostprocessorName');
asort($postprocessor_map);
$postprocessor_order = array(
CelerityDefaultPostprocessor::POSTPROCESSOR_KEY,
);
$postprocessor_map = array_select_keys(
$postprocessor_map,
$postprocessor_order) + $postprocessor_map;
return $postprocessor_map;
}
}

View file

@ -0,0 +1,34 @@
<?php
final class PhabricatorDarkConsoleSetting
extends PhabricatorSelectSetting {
const SETTINGKEY = 'dark_console';
const VALUE_DARKCONSOLE_DISABLED = '0';
const VALUE_DARKCONSOLE_ENABLED = '1';
public function getSettingName() {
return pht('DarkConsole');
}
protected function getControlInstructions() {
return pht(
'DarkConsole is a debugging console for developing and troubleshooting '.
'Phabricator applications. After enabling DarkConsole, press the '.
'{nav `} key on your keyboard to toggle it on or off.');
}
public function getSettingDefaultValue() {
return self::VALUE_DARKCONSOLE_DISABLED;
}
protected function getSelectOptions() {
return array(
self::VALUE_DARKCONSOLE_DISABLED => pht('Disable DarkConsole'),
self::VALUE_DARKCONSOLE_ENABLED => pht('Enable DarkConsole'),
);
}
}

View file

@ -0,0 +1,34 @@
<?php
final class PhabricatorDateFormatSetting
extends PhabricatorSelectSetting {
const SETTINGKEY = 'date-format';
const VALUE_FORMAT_ISO = 'Y-m-d';
const VALUE_FORMAT_US = 'n/j/Y';
const VALUE_FORMAT_EUROPE = 'd-m-Y';
public function getSettingName() {
return pht('Date Format');
}
protected function getControlInstructions() {
return pht(
'Select the format you prefer for editing dates.');
}
public function getSettingDefaultValue() {
return self::VALUE_FORMAT_ISO;
}
protected function getSelectOptions() {
return array(
self::VALUE_FORMAT_ISO => pht('ISO 8601: 2000-02-28'),
self::VALUE_FORMAT_US => pht('US: 2/28/2000'),
self::VALUE_FORMAT_EUROPE => pht('Europe: 28-02-2000'),
);
}
}

View file

@ -0,0 +1,32 @@
<?php
final class PhabricatorEditorMultipleSetting
extends PhabricatorSelectSetting {
const SETTINGKEY = 'multiedit';
const VALUE_SPACES = 'spaces';
const VALUE_SINGLE = 'disable';
public function getSettingName() {
return pht('Edit Mulitple Files');
}
protected function getControlInstructions() {
return pht(
'Some editors support opening multiple files with a single URI. You '.
'can specify the behavior of your editor here.');
}
public function getSettingDefaultValue() {
return self::VALUE_SPACES;
}
protected function getSelectOptions() {
return array(
self::VALUE_SPACES => pht('Supported, Separated by Spaces'),
self::VALUE_SINGLE => pht('Not Supported'),
);
}
}

View file

@ -0,0 +1,33 @@
<?php
final class PhabricatorEditorSetting
extends PhabricatorStringSetting {
const SETTINGKEY = 'editor';
public function getSettingName() {
return pht('Editor Link');
}
protected function getControlInstructions() {
return pht(
"Many text editors can be configured as URI handlers for special ".
"protocols like `editor://`. If you have such an editor, Phabricator ".
"can generate links that you can click to open files locally.".
"\n\n".
"These special variables are supported:".
"\n\n".
"| Value | Replaced With |\n".
"|-------|---------------|\n".
"| `%%f` | Filename |\n".
"| `%%l` | Line Number |\n".
"| `%%r` | Repository Callsign |\n".
"| `%%%%` | Literal `%%` |\n".
"\n\n".
"For complete instructions on editor configuration, ".
"see **[[ %s | %s ]]**.",
PhabricatorEnv::getDoclink('User Guide: Configuring an External Editor'),
pht('User Guide: Configuring an External Editor'));
}
}

View file

@ -0,0 +1,32 @@
<?php
final class PhabricatorEmailFormatSetting
extends PhabricatorSelectSetting {
const SETTINGKEY = 'html-emails';
const VALUE_HTML_EMAIL = 'true';
const VALUE_TEXT_EMAIL = 'false';
public function getSettingName() {
return pht('HTML Email');
}
protected function getControlInstructions() {
return pht(
'You can opt to receive plain text email from Phabricator instead '.
'of HTML email. Plain text email works better with some clients.');
}
public function getSettingDefaultValue() {
return self::VALUE_HTML_EMAIL;
}
protected function getSelectOptions() {
return array(
self::VALUE_HTML_EMAIL => pht('Send HTML Email'),
self::VALUE_TEXT_EMAIL => pht('Send Plain Text Email'),
);
}
}

View file

@ -0,0 +1,36 @@
<?php
final class PhabricatorEmailNotificationsSetting
extends PhabricatorSelectSetting {
const SETTINGKEY = 'no-mail';
const VALUE_SEND_MAIL = '0';
const VALUE_NO_MAIL = '1';
public function getSettingName() {
return pht('Email Notifications');
}
protected function getControlInstructions() {
return pht(
'If you disable **Email Notifications**, Phabricator will never '.
'send email to notify you about events. This preference overrides '.
'all your other settings.'.
"\n\n".
"//You will still receive some administrative email, like password ".
"reset email.//");
}
public function getSettingDefaultValue() {
return self::VALUE_SEND_MAIL;
}
protected function getSelectOptions() {
return array(
self::VALUE_SEND_MAIL => pht('Enable Email Notifications'),
self::VALUE_NO_MAIL => pht('Disable Email Notifications'),
);
}
}

View file

@ -0,0 +1,40 @@
<?php
final class PhabricatorEmailRePrefixSetting
extends PhabricatorSelectSetting {
const SETTINGKEY = 're-prefix';
const VALUE_RE_PREFIX = 'true';
const VALUE_NO_PREFIX = 'false';
public function getSettingName() {
return pht('Add "Re:" Prefix');
}
protected function getControlInstructions() {
return pht(
'The **Add "Re:" Prefix** setting adds "Re:" in front of all messages, '.
'even if they are not replies. If you use **Mail.app** on Mac OS X, '.
'this may improve mail threading.'.
"\n\n".
"| Setting | Example Mail Subject\n".
"|------------------------|----------------\n".
"| Enable \"Re:\" Prefix | ".
"`Re: [Differential] [Accepted] D123: Example Revision`\n".
"| Disable \"Re:\" Prefix | ".
"`[Differential] [Accepted] D123: Example Revision`");
}
public function getSettingDefaultValue() {
return self::VALUE_RE_PREFIX;
}
protected function getSelectOptions() {
return array(
self::VALUE_RE_PREFIX => pht('Enable "Re:" Prefix'),
self::VALUE_NO_PREFIX => pht('Disable "Re:" Prefix'),
);
}
}

View file

@ -0,0 +1,32 @@
<?php
final class PhabricatorEmailSelfActionsSetting
extends PhabricatorSelectSetting {
const SETTINGKEY = 'self-mail';
const VALUE_SEND_SELF = '0';
const VALUE_NO_SELF = '1';
public function getSettingName() {
return pht('Self Actions');
}
protected function getControlInstructions() {
return pht(
'If you disable **Self Actions**, Phabricator will not notify '.
'you about actions you take.');
}
public function getSettingDefaultValue() {
return self::VALUE_SEND_SELF;
}
protected function getSelectOptions() {
return array(
self::VALUE_SEND_SELF => pht('Enable Self Action Mail'),
self::VALUE_NO_SELF => pht('Disable Self Action Mail'),
);
}
}

View file

@ -0,0 +1,44 @@
<?php
final class PhabricatorEmailVarySubjectsSetting
extends PhabricatorSelectSetting {
const SETTINGKEY = 'vary-subjects';
const VALUE_VARY_SUBJECTS = 'true';
const VALUE_STATIC_SUBJECTS = 'false';
public function getSettingName() {
return pht('Vary Subjects');
}
protected function getControlInstructions() {
return pht(
'With **Vary Subjects** enabled, most mail subject lines will include '.
'a brief description of their content, like `[Closed]` for a '.
'notification about someone closing a task.'.
"\n\n".
"| Setting | Example Mail Subject\n".
"|----------------------|----------------\n".
"| Vary Subjects | ".
"`[Maniphest] [Closed] T123: Example Task`\n".
"| Do Not Vary Subjects | ".
"`[Maniphest] T123: Example Task`\n".
"\n".
'This can make mail more useful, but some clients have difficulty '.
'threading these messages. Disabling this option may improve '.
'threading at the cost of making subject lines less useful.');
}
public function getSettingDefaultValue() {
return self::VALUE_VARY_SUBJECTS;
}
protected function getSelectOptions() {
return array(
self::VALUE_VARY_SUBJECTS => pht('Enable "Re:" Prefix'),
self::VALUE_STATIC_SUBJECTS => pht('Disable "Re:" Prefix'),
);
}
}

View file

@ -0,0 +1,40 @@
<?php
final class PhabricatorMonospacedFontSetting
extends PhabricatorStringSetting {
const SETTINGKEY = 'monospaced';
public function getSettingName() {
return pht('Monospaced Font');
}
protected function getControlInstructions() {
return pht(
'You can customize the font used when showing monospaced text, '.
'including source code. You should enter a valid CSS font declaration '.
'like: `13px Consolas`');
}
public function validateTransactionValue($value) {
if (!strlen($value)) {
return;
}
$filtered = self::filterMonospacedCSSRule($value);
if ($filtered !== $value) {
throw new Exception(
pht(
'Monospaced font value "%s" is unsafe. You may only enter '.
'letters, numbers, spaces, commas, periods, forward slashes '.
'and double quotes.',
$value));
}
}
public static function filterMonospacedCSSRule($monospaced) {
// Prevent the user from doing dangerous things.
return preg_replace('([^a-z0-9 ,"./]+)i', '', $monospaced);
}
}

View file

@ -0,0 +1,34 @@
<?php
final class PhabricatorMonospacedTextareasSetting
extends PhabricatorSelectSetting {
const SETTINGKEY = 'monospaced-textareas';
const VALUE_TEXT_VARIABLE_WIDTH = 'disabled';
const VALUE_TEXT_MONOSPACED = 'enabled';
public function getSettingName() {
return pht('Monospaced Textareas');
}
protected function getControlInstructions() {
return pht(
'You can choose to use either a monospaced or variable-width font '.
'in textareas in the UI. Textareas are used for editing descriptions '.
'and writing comments, among other things.');
}
public function getSettingDefaultValue() {
return self::VALUE_TEXT_VARIABLE_WIDTH;
}
protected function getSelectOptions() {
return array(
self::VALUE_TEXT_VARIABLE_WIDTH => pht('Use Variable-Width Font'),
self::VALUE_TEXT_MONOSPACED => pht('Use Monospaced Font'),
);
}
}

View file

@ -0,0 +1,35 @@
<?php
final class PhabricatorOlderInlinesSetting
extends PhabricatorSelectSetting {
const SETTINGKEY = 'diff-ghosts';
const VALUE_GHOST_INLINES_ENABLED = 'default';
const VALUE_GHOST_INLINES_DISABLED = 'disabled';
public function getSettingName() {
return pht('Show Older Inlines');
}
protected function getControlInstructions() {
return pht(
'When a revision is updated, Phabricator attempts to bring inline '.
'comments on the older version forward to the new changes. You can '.
'disable this behavior if you prefer comments stay anchored in one '.
'place.');
}
public function getSettingDefaultValue() {
return self::VALUE_GHOST_INLINES_ENABLED;
}
protected function getSelectOptions() {
return array(
self::VALUE_GHOST_INLINES_ENABLED => pht('Enabled'),
self::VALUE_GHOST_INLINES_DISABLED => pht('Disabled'),
);
}
}

View file

@ -0,0 +1,73 @@
<?php
abstract class PhabricatorOptionGroupSetting
extends PhabricatorSetting {
abstract protected function getSelectOptionGroups();
final protected function getSelectOptionMap() {
$groups = $this->getSelectOptionGroups();
$map = array();
foreach ($groups as $group) {
$map += $group['options'];
}
return $map;
}
final protected function newCustomEditField($object) {
$setting_key = $this->getSettingKey();
$default_value = $object->getDefaultValue($setting_key);
$options = $this->getSelectOptionGroups();
$map = $this->getSelectOptionMap();
if (isset($map[$default_value])) {
$default_label = pht('Default (%s)', $map[$default_value]);
} else {
$default_label = pht('Default (Unknown, "%s")', $default_value);
}
$head_key = head_key($options);
$options[$head_key]['options'] = array(
'' => $default_label,
) + $options[$head_key]['options'];
$flat_options = array();
foreach ($options as $group) {
$flat_options[$group['label']] = $group['options'];
}
return $this->newEditField($object, new PhabricatorSelectEditField())
->setOptions($flat_options);
}
final public function validateTransactionValue($value) {
if (!strlen($value)) {
return;
}
$map = $this->getSelectOptionMap();
if (!isset($map[$value])) {
throw new Exception(
pht(
'Value "%s" is not valid for setting "%s": valid values are %s.',
$value,
$this->getSettingName(),
implode(', ', array_keys($map))));
}
return;
}
public function getTransactionNewValue($value) {
if (!strlen($value)) {
return null;
}
return (string)$value;
}
}

View file

@ -0,0 +1,35 @@
<?php
final class PhabricatorPronounSetting
extends PhabricatorSelectSetting {
const SETTINGKEY = 'pronoun';
public function getSettingName() {
return pht('Pronoun');
}
protected function getControlInstructions() {
return pht('Choose the pronoun you prefer.');
}
public function getSettingDefaultValue() {
return PhutilPerson::SEX_UNKNOWN;
}
protected function getSelectOptions() {
$viewer = $this->getViewer();
$username = $viewer->getUsername();
$label_unknown = pht('%s updated their profile', $username);
$label_her = pht('%s updated her profile', $username);
$label_his = pht('%s updated his profile', $username);
return array(
PhutilPerson::SEX_UNKNOWN => $label_unknown,
PhutilPerson::SEX_MALE => $label_his,
PhutilPerson::SEX_FEMALE => $label_her,
);
}
}

View file

@ -0,0 +1,57 @@
<?php
abstract class PhabricatorSelectSetting
extends PhabricatorSetting {
abstract protected function getSelectOptions();
final protected function newCustomEditField($object) {
$setting_key = $this->getSettingKey();
$default_value = $object->getDefaultValue($setting_key);
$options = $this->getSelectOptions();
if (isset($options[$default_value])) {
$default_label = pht('Default (%s)', $options[$default_value]);
} else {
$default_label = pht('Default (Unknown, "%s")', $default_value);
}
if (empty($options[''])) {
$options = array(
'' => $default_label,
) + $options;
}
return $this->newEditField($object, new PhabricatorSelectEditField())
->setOptions($options);
}
final public function validateTransactionValue($value) {
if (!strlen($value)) {
return;
}
$options = $this->getSelectOptions();
if (!isset($options[$value])) {
throw new Exception(
pht(
'Value "%s" is not valid for setting "%s": valid values are %s.',
$value,
$this->getSettingName(),
implode(', ', array_keys($options))));
}
return;
}
public function getTransactionNewValue($value) {
if (!strlen($value)) {
return null;
}
return (string)$value;
}
}

View file

@ -0,0 +1,96 @@
<?php
abstract class PhabricatorSetting extends Phobject {
private $viewer;
public function setViewer(PhabricatorUser $viewer) {
$this->viewer = $viewer;
return $this;
}
public function getViewer() {
return $this->viewer;
}
abstract public function getSettingName();
protected function getControlInstructions() {
return null;
}
protected function isEnabledForViewer(PhabricatorUser $viewer) {
return true;
}
public function getSettingDefaultValue() {
return null;
}
final public function getSettingKey() {
return $this->getPhobjectClassConstant('SETTINGKEY');
}
public static function getAllSettings() {
return id(new PhutilClassMapQuery())
->setAncestorClass(__CLASS__)
->setUniqueMethod('getSettingKey')
->execute();
}
public static function getAllEnabledSettings(PhabricatorUser $viewer) {
$settings = self::getAllSettings();
foreach ($settings as $key => $setting) {
if (!$setting->isEnabledForViewer($viewer)) {
unset($settings[$key]);
}
}
return $settings;
}
final public function newCustomEditFields($object) {
$fields = array();
$field = $this->newCustomEditField($object);
if ($field) {
$fields[] = $field;
}
return $fields;
}
protected function newCustomEditField($object) {
return null;
}
protected function newEditField($object, PhabricatorEditField $template) {
$setting_property = PhabricatorUserPreferencesTransaction::PROPERTY_SETTING;
$setting_key = $this->getSettingKey();
$value = $object->getPreference($setting_key);
$xaction_type = PhabricatorUserPreferencesTransaction::TYPE_SETTING;
$label = $this->getSettingName();
$template
->setKey($setting_key)
->setLabel($label)
->setValue($value)
->setTransactionType($xaction_type)
->setMetadataValue($setting_property, $setting_key);
$instructions = $this->getControlInstructions();
if (strlen($instructions)) {
$template->setControlInstructions($instructions);
}
return $template;
}
public function validateTransactionValue($value) {
return;
}
public function getTransactionNewValue($value) {
return $value;
}
}

View file

@ -0,0 +1,34 @@
<?php
final class PhabricatorShowFiletreeSetting
extends PhabricatorSelectSetting {
const SETTINGKEY = 'diff-filetree';
const VALUE_DISABLE_FILETREE = 0;
const VALUE_ENABLE_FILETREE = 1;
public function getSettingName() {
return pht('Show Filetree');
}
protected function getControlInstructions() {
return pht(
'When viewing a revision or commit, you can enable a sidebar showing '.
'affected files. When this option is enabled, press {nav %s} to show '.
'or hide the sidebar.',
'f');
}
public function getSettingDefaultValue() {
return self::VALUE_DISABLE_FILETREE;
}
protected function getSelectOptions() {
return array(
self::VALUE_DISABLE_FILETREE => pht('Disable Filetree'),
self::VALUE_ENABLE_FILETREE => pht('Enable Filetree'),
);
}
}

View file

@ -0,0 +1,18 @@
<?php
abstract class PhabricatorStringSetting
extends PhabricatorSetting {
final protected function newCustomEditField($object) {
return $this->newEditField($object, new PhabricatorTextEditField());
}
public function getTransactionNewValue($value) {
if (!strlen($value)) {
return null;
}
return (string)$value;
}
}

View file

@ -0,0 +1,32 @@
<?php
final class PhabricatorTimeFormatSetting
extends PhabricatorSelectSetting {
const SETTINGKEY = 'time-format';
const VALUE_FORMAT_12HOUR = 'g:i A';
const VALUE_FORMAT_24HOUR = 'H:i';
public function getSettingName() {
return pht('Time Format');
}
protected function getControlInstructions() {
return pht(
'Select the format you prefer for editing and displaying time.');
}
public function getSettingDefaultValue() {
return self::VALUE_FORMAT_12HOUR;
}
protected function getSelectOptions() {
return array(
self::VALUE_FORMAT_12HOUR => pht('12 Hour, 2:34 PM'),
self::VALUE_FORMAT_24HOUR => pht('24 Hour, 14:34'),
);
}
}

View file

@ -0,0 +1,53 @@
<?php
final class PhabricatorTimezoneSetting
extends PhabricatorOptionGroupSetting {
const SETTINGKEY = 'timezone';
public function getSettingName() {
return pht('Timezone');
}
public function getSettingDefaultValue() {
return date_default_timezone_get();
}
protected function getSelectOptionGroups() {
$timezones = DateTimeZone::listIdentifiers();
$now = new DateTime('@'.PhabricatorTime::getNow());
$groups = array();
foreach ($timezones as $timezone) {
$zone = new DateTimeZone($timezone);
$offset = -($zone->getOffset($now) / (60 * 60));
$groups[$offset][] = $timezone;
}
krsort($groups);
$option_groups = array(
array(
'label' => pht('Default'),
'options' => array(),
),
);
foreach ($groups as $offset => $group) {
if ($offset >= 0) {
$label = pht('UTC-%d', $offset);
} else {
$label = pht('UTC+%d', -$offset);
}
sort($group);
$option_groups[] = array(
'label' => $label,
'options' => array_fuse($group),
);
}
return $option_groups;
}
}

View file

@ -0,0 +1,33 @@
<?php
final class PhabricatorTitleGlyphsSetting
extends PhabricatorSelectSetting {
const SETTINGKEY = 'titles';
const VALUE_TITLE_GLYPHS = 'glyph';
const VALUE_TITLE_TEXT = 'text';
public function getSettingName() {
return pht('Page Titles');
}
protected function getControlInstructions() {
return pht(
'Phabricator uses unicode glyphs in page titles to provide a compact '.
'representation of the current application. You can substitute plain '.
'text instead if these glyphs do not display on your system.');
}
public function getSettingDefaultValue() {
return self::VALUE_TITLE_GLYPHS;
}
protected function getSelectOptions() {
return array(
self::VALUE_TITLE_GLYPHS => pht("Use Unicode Glyphs: \xE2\x9A\x99"),
self::VALUE_TITLE_TEXT => pht('Use Plain Text: [Differential]'),
);
}
}

View file

@ -0,0 +1,92 @@
<?php
final class PhabricatorTranslationSetting
extends PhabricatorOptionGroupSetting {
const SETTINGKEY = 'translation';
public function getSettingName() {
return pht('Translation');
}
public function getSettingDefaultValue() {
return 'en_US';
}
protected function getSelectOptionGroups() {
$is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
$locales = PhutilLocale::loadAllLocales();
$group_labels = array(
'normal' => pht('Translations'),
'limited' => pht('Limited Translations'),
'silly' => pht('Silly Translations'),
'test' => pht('Developer/Test Translations'),
);
$groups = array_fill_keys(array_keys($group_labels), array());
$translations = array();
foreach ($locales as $locale) {
$code = $locale->getLocaleCode();
// Get the locale's localized name if it's available. For example,
// "Deutsch" instead of "German". This helps users who do not speak the
// current language to find the correct setting.
$raw_scope = PhabricatorEnv::beginScopedLocale($code);
$name = $locale->getLocaleName();
unset($raw_scope);
if ($locale->isSillyLocale()) {
if ($is_serious) {
// Omit silly locales on serious business installs.
continue;
}
$groups['silly'][$code] = $name;
continue;
}
if ($locale->isTestLocale()) {
$groups['test'][$code] = $name;
continue;
}
$strings = PhutilTranslation::getTranslationMapForLocale($code);
$size = count($strings);
// If a translation is English, assume it can fall back to the default
// strings and don't caveat its completeness.
$is_english = (substr($code, 0, 3) == 'en_');
// Arbitrarily pick some number of available strings to promote a
// translation out of the "limited" group. The major goal is just to
// keep locales with very few strings out of the main group, so users
// aren't surprised if a locale has no upstream translations available.
if ($size > 512 || $is_english) {
$type = 'normal';
} else {
$type = 'limited';
}
$groups[$type][$code] = $name;
}
$results = array();
foreach ($groups as $key => $group) {
$label = $group_labels[$key];
if (!$group) {
continue;
}
asort($group);
$results[] = array(
'label' => $label,
'options' => $group,
);
}
return $results;
}
}

View file

@ -0,0 +1,35 @@
<?php
final class PhabricatorUnifiedDiffsSetting
extends PhabricatorSelectSetting {
const SETTINGKEY = 'diff-unified';
const VALUE_ON_SMALL_SCREENS = 'default';
const VALUE_ALWAYS_UNIFIED = 'unified';
public function getSettingName() {
return pht('Show Unified Diffs');
}
protected function getControlInstructions() {
return pht(
'Phabricator normally shows diffs in a side-by-side layout on large '.
'screens and automatically switches to a unified view on small '.
'screens (like mobile phones). If you prefer unified diffs even on '.
'large screens, you can select them for use on all displays.');
}
public function getSettingDefaultValue() {
return self::VALUE_ON_SMALL_SCREENS;
}
protected function getSelectOptions() {
return array(
self::VALUE_ON_SMALL_SCREENS => pht('On Small Screens'),
self::VALUE_ALWAYS_UNIFIED => pht('Always'),
);
}
}

View file

@ -0,0 +1,33 @@
<?php
final class PhabricatorWeekStartDaySetting
extends PhabricatorSelectSetting {
const SETTINGKEY = 'week-start-day';
public function getSettingName() {
return pht('Week Starts On');
}
protected function getControlInstructions() {
return pht(
'Choose which day a calendar week should begin on.');
}
public function getSettingDefaultValue() {
return 0;
}
protected function getSelectOptions() {
return array(
0 => pht('Sunday'),
1 => pht('Monday'),
2 => pht('Tuesday'),
3 => pht('Wednesday'),
4 => pht('Thursday'),
5 => pht('Friday'),
6 => pht('Saturday'),
);
}
}

View file

@ -1,6 +1,11 @@
<?php
final class PhabricatorUserPreferences extends PhabricatorUserDAO {
final class PhabricatorUserPreferences
extends PhabricatorUserDAO
implements
PhabricatorPolicyInterface,
PhabricatorDestructibleInterface,
PhabricatorApplicationTransactionInterface {
const PREFERENCE_MONOSPACED = 'monospaced';
const PREFERENCE_DARK_CONSOLE = 'dark_console';
@ -51,12 +56,14 @@ final class PhabricatorUserPreferences extends PhabricatorUserDAO {
protected $userPHID;
protected $preferences = array();
private $user = self::ATTACHABLE;
protected function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
self::CONFIG_SERIALIZATION => array(
'preferences' => self::SERIALIZATION_JSON,
),
self::CONFIG_TIMESTAMPS => false,
self::CONFIG_KEY_SCHEMA => array(
'userPHID' => array(
'columns' => array('userPHID'),
@ -66,6 +73,11 @@ final class PhabricatorUserPreferences extends PhabricatorUserDAO {
) + parent::getConfiguration();
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(
PhabricatorUserPreferencesPHIDType::TYPECONST);
}
public function getPreference($key, $default = null) {
return idx($this->preferences, $key, $default);
}
@ -80,6 +92,21 @@ final class PhabricatorUserPreferences extends PhabricatorUserDAO {
return $this;
}
public function getDefaultValue($key) {
$setting = self::getSettingObject($key);
if (!$setting) {
return null;
}
return $setting->getSettingDefaultValue();
}
private static function getSettingObject($key) {
$settings = PhabricatorSetting::getAllSettings();
return idx($settings, $key);
}
public function getPinnedApplications(array $apps, PhabricatorUser $viewer) {
$pref_pinned = self::PREFERENCE_APP_PINNED;
$pinned = $this->getPreference($pref_pinned);
@ -115,4 +142,117 @@ final class PhabricatorUserPreferences extends PhabricatorUserDAO {
return preg_replace('([^a-z0-9 ,"./]+)i', '', $monospaced);
}
public function attachUser(PhabricatorUser $user = null) {
$this->user = $user;
return $this;
}
public function getUser() {
return $this->assertAttached($this->user);
}
public function hasManagedUser() {
$user_phid = $this->getUserPHID();
if (!$user_phid) {
return false;
}
$user = $this->getUser();
if ($user->getIsSystemAgent() || $user->getIsMailingList()) {
return true;
}
return false;
}
// TODO: Remove this once all edits go through the Editor. For now, some
// old edits just do direct saves so make sure we nuke the cache.
public function save() {
PhabricatorUserCache::clearCache(
PhabricatorUserPreferencesCacheType::KEY_PREFERENCES,
$this->getUserPHID());
return parent::save();
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
);
}
public function getPolicy($capability) {
switch ($capability) {
case PhabricatorPolicyCapability::CAN_VIEW:
$user_phid = $this->getUserPHID();
if ($user_phid) {
return $user_phid;
}
return PhabricatorPolicies::getMostOpenPolicy();
case PhabricatorPolicyCapability::CAN_EDIT:
if ($this->hasManagedUser()) {
return PhabricatorPolicies::POLICY_ADMIN;
}
$user_phid = $this->getUserPHID();
if ($user_phid) {
return $user_phid;
}
return PhabricatorPolicies::POLICY_ADMIN;
}
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
if ($this->hasManagedUser()) {
if ($viewer->getIsAdmin()) {
return true;
}
}
return false;
}
public function describeAutomaticCapability($capability) {
return null;
}
/* -( PhabricatorDestructibleInterface )----------------------------------- */
public function destroyObjectPermanently(
PhabricatorDestructionEngine $engine) {
$this->delete();
}
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
public function getApplicationTransactionEditor() {
return new PhabricatorUserPreferencesEditor();
}
public function getApplicationTransactionObject() {
return $this;
}
public function getApplicationTransactionTemplate() {
return new PhabricatorUserPreferencesTransaction();
}
public function willRenderTimeline(
PhabricatorApplicationTransactionView $timeline,
AphrontRequest $request) {
return $timeline;
}
}

View file

@ -0,0 +1,22 @@
<?php
final class PhabricatorUserPreferencesTransaction
extends PhabricatorApplicationTransaction {
const TYPE_SETTING = 'setting';
const PROPERTY_SETTING = 'setting.key';
public function getApplicationName() {
return 'user';
}
public function getApplicationTransactionCommentObject() {
return null;
}
public function getApplicationTransactionType() {
return PhabricatorUserPreferencesPHIDType::TYPECONST;
}
}

View file

@ -216,39 +216,3 @@ Finally, edit `/etc/mail/virtusertable` and add an entry like this:
That will forward all mail to @yourdomain.com to the Phabricator processing
script. Run `sudo /etc/mail/make` or similar and then restart sendmail with
`sudo /etc/init.d/sendmail restart`.
= Local MTA: Configuring Lamson =
Before you can configure Lamson, you need to install Mailparse. See the section
"Installing Mailparse" above.
In contrast to Sendmail, Lamson is relatively easy to configure. It is fairly
minimal, and is suitable for a development or testing environment. Lamson
listens for incoming SMTP mails and passes the content directly to Phabricator.
To get started, follow the provided instructions
(<http://lamsonproject.org/docs/getting_started.html>) to set up an instance.
One likely deployment issue is that binding to port 25 requires root
privileges. Lamson is capable of starting as root then dropping privileges, but
you must supply `-uid` and `-gid` arguments to do so, as demonstrated by
Step 8 in Lamson's deployment tutorial (located here:
<http://lamsonproject.org/docs/deploying_oneshotblog.html>).
The Lamson handler code itself is very concise; it merely needs to pass the
content of the email to Phabricator:
import logging, subprocess
from lamson.routing import route, stateless
from lamson import view
PHABRICATOR_ROOT = "/path/to/phabricator"
PHABRICATOR_ENV = "custom/myconf"
LOGGING_ENABLED = True
@route("(address)@(host)", address=".+")
@stateless
def START(message, address=None, host=None):
if LOGGING_ENABLED:
logging.debug("%s", message.original)
process = subprocess.Popen([PHABRICATOR_ROOT + "scripts/mail/mail_handler.php",PHABRICATOR_ENV],stdin=subprocess.PIPE)
process.communicate(message.original)

View file

@ -351,10 +351,13 @@ final class PhabricatorMarkupEngine extends Phobject {
* @task engine
*/
public static function newPhameMarkupEngine() {
return self::newMarkupEngine(array(
'macros' => false,
'uri.full' => true,
));
return self::newMarkupEngine(
array(
'macros' => false,
'uri.full' => true,
'uri.same-window' => true,
'uri.base' => PhabricatorEnv::getURI('/'),
));
}
@ -487,6 +490,14 @@ final class PhabricatorMarkupEngine extends Phobject {
$engine->setConfig('uri.full', $options['uri.full']);
if (isset($options['uri.base'])) {
$engine->setConfig('uri.base', $options['uri.base']);
}
if (isset($options['uri.same-window'])) {
$engine->setConfig('uri.same-window', $options['uri.same-window']);
}
$rules = array();
$rules[] = new PhutilRemarkupEscapeRemarkupRule();
$rules[] = new PhutilRemarkupMonospaceRule();

Some files were not shown because too many files have changed in this diff Show more