From 3849a6999581b5d520016c5a833688475f561fab Mon Sep 17 00:00:00 2001 From: Asher Baker Date: Sat, 4 Jun 2016 12:17:14 +0000 Subject: [PATCH 01/67] Modernize metamta.differential.patch-format Summary: Change metamta.differential.patch-format over to an enum option now that they're implemented. Test Plan: Looked at settings page. Reviewers: #blessed_reviewers, epriestley Reviewed By: #blessed_reviewers, epriestley Subscribers: epriestley Differential Revision: https://secure.phabricator.com/D16032 --- .../config/PhabricatorDifferentialConfigOptions.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/applications/differential/config/PhabricatorDifferentialConfigOptions.php b/src/applications/differential/config/PhabricatorDifferentialConfigOptions.php index 110960aac5..fa0bd0c891 100644 --- a/src/applications/differential/config/PhabricatorDifferentialConfigOptions.php +++ b/src/applications/differential/config/PhabricatorDifferentialConfigOptions.php @@ -261,13 +261,14 @@ final class PhabricatorDifferentialConfigOptions "that many lines. For instance, a value of 100 means 'inline ". "patches if they are no longer than 100 lines'. By default, ". "patches are not inlined.")), - // TODO: Implement 'enum'? Options are 'unified' or 'git'. $this->newOption( 'metamta.differential.patch-format', - 'string', + 'enum', 'unified') ->setDescription( - pht("Format for inlined or attached patches: 'git' or 'unified'.")), + pht('Format for inlined or attached patches.')) + ->setEnumOptions( + array('unified' => 'unified', 'git' => 'git')), ); } From 57c2f61b75973fda9463bd550611a85badf69ba5 Mon Sep 17 00:00:00 2001 From: epriestley Date: Thu, 2 Jun 2016 16:57:45 -0700 Subject: [PATCH 02/67] Modularize Conpherence notification preferences Summary: Ref T4103. This is a weird standalone setting that I didn't clean up earlier. Also fix an issue with the PronounSetting and the Editor not interacting properly. Test Plan: Edited using new EditEngine UI. Reviewers: chad Reviewed By: chad Maniphest Tasks: T4103 Differential Revision: https://secure.phabricator.com/D16014 --- src/__phutil_library_map__.php | 2 ++ .../PhabricatorUserPreferencesEditor.php | 6 ++++ ...ricatorConpherenceNotificationsSetting.php | 31 +++++++++++++++++++ 3 files changed, 39 insertions(+) create mode 100644 src/applications/settings/setting/PhabricatorConpherenceNotificationsSetting.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 2533344e43..fd7426ce7d 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -2145,6 +2145,7 @@ phutil_register_library_map(array( 'PhabricatorConfigVersionsModule' => 'applications/config/module/PhabricatorConfigVersionsModule.php', 'PhabricatorConfigWelcomeController' => 'applications/config/controller/PhabricatorConfigWelcomeController.php', 'PhabricatorConpherenceApplication' => 'applications/conpherence/application/PhabricatorConpherenceApplication.php', + 'PhabricatorConpherenceNotificationsSetting' => 'applications/settings/setting/PhabricatorConpherenceNotificationsSetting.php', 'PhabricatorConpherencePreferencesSettingsPanel' => 'applications/settings/panel/PhabricatorConpherencePreferencesSettingsPanel.php', 'PhabricatorConpherenceThreadPHIDType' => 'applications/conpherence/phid/PhabricatorConpherenceThreadPHIDType.php', 'PhabricatorConsoleApplication' => 'applications/console/application/PhabricatorConsoleApplication.php', @@ -6676,6 +6677,7 @@ phutil_register_library_map(array( 'PhabricatorConfigVersionsModule' => 'PhabricatorConfigModule', 'PhabricatorConfigWelcomeController' => 'PhabricatorConfigController', 'PhabricatorConpherenceApplication' => 'PhabricatorApplication', + 'PhabricatorConpherenceNotificationsSetting' => 'PhabricatorSelectSetting', 'PhabricatorConpherencePreferencesSettingsPanel' => 'PhabricatorSettingsPanel', 'PhabricatorConpherenceThreadPHIDType' => 'PhabricatorPHIDType', 'PhabricatorConsoleApplication' => 'PhabricatorApplication', diff --git a/src/applications/settings/editor/PhabricatorUserPreferencesEditor.php b/src/applications/settings/editor/PhabricatorUserPreferencesEditor.php index bbb469eb01..d320391091 100644 --- a/src/applications/settings/editor/PhabricatorUserPreferencesEditor.php +++ b/src/applications/settings/editor/PhabricatorUserPreferencesEditor.php @@ -99,6 +99,12 @@ final class PhabricatorUserPreferencesEditor $actor = $this->getActor(); $settings = PhabricatorSetting::getAllEnabledSettings($actor); + foreach ($settings as $key => $setting) { + $setting = clone $setting; + $setting->setViewer($actor); + $settings[$key] = $setting; + } + switch ($type) { case PhabricatorUserPreferencesTransaction::TYPE_SETTING: foreach ($xactions as $xaction) { diff --git a/src/applications/settings/setting/PhabricatorConpherenceNotificationsSetting.php b/src/applications/settings/setting/PhabricatorConpherenceNotificationsSetting.php new file mode 100644 index 0000000000..7009fbcdc1 --- /dev/null +++ b/src/applications/settings/setting/PhabricatorConpherenceNotificationsSetting.php @@ -0,0 +1,31 @@ + pht('Send Email'), + self::VALUE_CONPHERENCE_NOTIFY => pht('Send Notifications'), + ); + } + +} From 67482fd19d2c7f3a2624f60c839bb78475d14227 Mon Sep 17 00:00:00 2001 From: epriestley Date: Thu, 2 Jun 2016 17:12:15 -0700 Subject: [PATCH 03/67] Continue modernizing application access to user preferences Summary: Ref T4103. This is just incremental cleanup: - Add "internal" settings, which aren't editable via the UI. They can still do validation and run through the normal pathway. Move a couple settings to use this. - Remove `getPreference()` on `PhabricatorUser`, which was a sort of prototype version of `getUserSetting()`. - Make `getUserSetting()` validate setting values before returning them, to improve robustness if we change allowable values later. - Add a user setting cache, since reading user settings was getting fairly expensive on Calendar. - Improve performance of setting validation for timezone setting (don't require building/computing all timezone offsets). - Since we have the cache anyway, make the timezone override a little more general in its approach. - Move editor stuff to use `getUserSetting()`. Test Plan: - Changed search scopes. - Reconciled local and server timezone settings by ignoring and changing timezones. - Changed date/time settings, browsed Calendar, queried date ranges. - Verified editor links generate properly in Diffusion. - Browsed around with time/date settings looking at timestamps. - Grepped for `getPreference()`, nuked all the ones coming off `$user` or `$viewer` that I could find. - Changed accessiblity to high-contrast colors. - Ran all unit tests. - Grepped for removed constants. Reviewers: chad Reviewed By: chad Maniphest Tasks: T4103 Differential Revision: https://secure.phabricator.com/D16015 --- src/__phutil_library_map__.php | 6 + src/aphront/response/AphrontAjaxResponse.php | 4 +- .../people/storage/PhabricatorUser.php | 122 ++++++++++-------- .../PhabricatorSettingsTimezoneController.php | 5 +- .../PhabricatorDateTimeSettingsPanel.php | 2 +- .../setting/PhabricatorInternalSetting.php | 4 + .../setting/PhabricatorSearchScopeSetting.php | 16 +++ .../settings/setting/PhabricatorSetting.php | 4 + ...PhabricatorTimezoneIgnoreOffsetSetting.php | 12 ++ .../setting/PhabricatorTimezoneSetting.php | 25 ++++ .../storage/PhabricatorUserPreferences.php | 3 - .../form/control/AphrontFormDateControl.php | 19 +-- .../control/AphrontFormDateControlValue.php | 10 +- src/view/page/PhabricatorBarePageView.php | 4 +- src/view/page/PhabricatorStandardPageView.php | 9 +- .../menu/PhabricatorMainMenuSearchView.php | 8 +- .../phui/calendar/PHUICalendarListView.php | 10 +- src/view/viewutils.php | 8 +- 18 files changed, 171 insertions(+), 100 deletions(-) create mode 100644 src/applications/settings/setting/PhabricatorInternalSetting.php create mode 100644 src/applications/settings/setting/PhabricatorSearchScopeSetting.php create mode 100644 src/applications/settings/setting/PhabricatorTimezoneIgnoreOffsetSetting.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index fd7426ce7d..28b0344977 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -2594,6 +2594,7 @@ phutil_register_library_map(array( 'PhabricatorInlineCommentPreviewController' => 'infrastructure/diff/PhabricatorInlineCommentPreviewController.php', 'PhabricatorInlineSummaryView' => 'infrastructure/diff/view/PhabricatorInlineSummaryView.php', 'PhabricatorInstructionsEditField' => 'applications/transactions/editfield/PhabricatorInstructionsEditField.php', + 'PhabricatorInternalSetting' => 'applications/settings/setting/PhabricatorInternalSetting.php', 'PhabricatorInternationalizationManagementExtractWorkflow' => 'infrastructure/internationalization/management/PhabricatorInternationalizationManagementExtractWorkflow.php', 'PhabricatorInternationalizationManagementWorkflow' => 'infrastructure/internationalization/management/PhabricatorInternationalizationManagementWorkflow.php', 'PhabricatorInvalidConfigSetupCheck' => 'applications/config/check/PhabricatorInvalidConfigSetupCheck.php', @@ -3353,6 +3354,7 @@ phutil_register_library_map(array( 'PhabricatorSearchResultBucketGroup' => 'applications/search/buckets/PhabricatorSearchResultBucketGroup.php', 'PhabricatorSearchResultView' => 'applications/search/view/PhabricatorSearchResultView.php', 'PhabricatorSearchSchemaSpec' => 'applications/search/storage/PhabricatorSearchSchemaSpec.php', + 'PhabricatorSearchScopeSetting' => 'applications/settings/setting/PhabricatorSearchScopeSetting.php', 'PhabricatorSearchSelectController' => 'applications/search/controller/PhabricatorSearchSelectController.php', 'PhabricatorSearchSelectField' => 'applications/search/field/PhabricatorSearchSelectField.php', 'PhabricatorSearchStringListField' => 'applications/search/field/PhabricatorSearchStringListField.php', @@ -3538,6 +3540,7 @@ phutil_register_library_map(array( 'PhabricatorTimeFormatSetting' => 'applications/settings/setting/PhabricatorTimeFormatSetting.php', 'PhabricatorTimeGuard' => 'infrastructure/time/PhabricatorTimeGuard.php', 'PhabricatorTimeTestCase' => 'infrastructure/time/__tests__/PhabricatorTimeTestCase.php', + 'PhabricatorTimezoneIgnoreOffsetSetting' => 'applications/settings/setting/PhabricatorTimezoneIgnoreOffsetSetting.php', 'PhabricatorTimezoneSetting' => 'applications/settings/setting/PhabricatorTimezoneSetting.php', 'PhabricatorTimezoneSetupCheck' => 'applications/config/check/PhabricatorTimezoneSetupCheck.php', 'PhabricatorTitleGlyphsSetting' => 'applications/settings/setting/PhabricatorTitleGlyphsSetting.php', @@ -7196,6 +7199,7 @@ phutil_register_library_map(array( 'PhabricatorInlineCommentPreviewController' => 'PhabricatorController', 'PhabricatorInlineSummaryView' => 'AphrontView', 'PhabricatorInstructionsEditField' => 'PhabricatorEditField', + 'PhabricatorInternalSetting' => 'PhabricatorSetting', 'PhabricatorInternationalizationManagementExtractWorkflow' => 'PhabricatorInternationalizationManagementWorkflow', 'PhabricatorInternationalizationManagementWorkflow' => 'PhabricatorManagementWorkflow', 'PhabricatorInvalidConfigSetupCheck' => 'PhabricatorSetupCheck', @@ -8103,6 +8107,7 @@ phutil_register_library_map(array( 'PhabricatorSearchResultBucketGroup' => 'Phobject', 'PhabricatorSearchResultView' => 'AphrontView', 'PhabricatorSearchSchemaSpec' => 'PhabricatorConfigSchemaSpec', + 'PhabricatorSearchScopeSetting' => 'PhabricatorInternalSetting', 'PhabricatorSearchSelectController' => 'PhabricatorSearchBaseController', 'PhabricatorSearchSelectField' => 'PhabricatorSearchField', 'PhabricatorSearchStringListField' => 'PhabricatorSearchField', @@ -8304,6 +8309,7 @@ phutil_register_library_map(array( 'PhabricatorTimeFormatSetting' => 'PhabricatorSelectSetting', 'PhabricatorTimeGuard' => 'Phobject', 'PhabricatorTimeTestCase' => 'PhabricatorTestCase', + 'PhabricatorTimezoneIgnoreOffsetSetting' => 'PhabricatorInternalSetting', 'PhabricatorTimezoneSetting' => 'PhabricatorOptionGroupSetting', 'PhabricatorTimezoneSetupCheck' => 'PhabricatorSetupCheck', 'PhabricatorTitleGlyphsSetting' => 'PhabricatorSelectSetting', diff --git a/src/aphront/response/AphrontAjaxResponse.php b/src/aphront/response/AphrontAjaxResponse.php index b13a62f268..1ccb3fe97e 100644 --- a/src/aphront/response/AphrontAjaxResponse.php +++ b/src/aphront/response/AphrontAjaxResponse.php @@ -64,8 +64,8 @@ final class AphrontAjaxResponse extends AphrontResponse { if ($request) { $viewer = $request->getViewer(); if ($viewer) { - $postprocessor_key = $viewer->getPreference( - PhabricatorUserPreferences::PREFERENCE_RESOURCE_POSTPROCESSOR); + $postprocessor_key = $viewer->getUserSetting( + PhabricatorAccessibilitySetting::SETTINGKEY); if (strlen($postprocessor_key)) { $response->setPostprocessorKey($postprocessor_key); } diff --git a/src/applications/people/storage/PhabricatorUser.php b/src/applications/people/storage/PhabricatorUser.php index c0559b7e7d..2ba5d2b2cd 100644 --- a/src/applications/people/storage/PhabricatorUser.php +++ b/src/applications/people/storage/PhabricatorUser.php @@ -66,7 +66,9 @@ final class PhabricatorUser private $authorities = array(); private $handlePool; private $csrfSalt; - private $timezoneOverride; + + private $settingCacheKeys = array(); + private $settingCache = array(); protected function readField($field) { switch ($field) { @@ -481,19 +483,50 @@ final class PhabricatorUser public function getUserSetting($key) { + // NOTE: We store available keys and cached values separately to make it + // faster to check for `null` in the cache, which is common. + if (isset($this->settingCacheKeys[$key])) { + return $this->settingCache[$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(); + + if (array_key_exists($key, $settings)) { + $value = $settings[$key]; + + // Make sure the value is valid before we return it. This makes things + // more robust when options are changed or removed. + if (isset($defaults[$key])) { + try { + id(clone $defaults[$key]) + ->setViewer($this) + ->assertValidValue($value); + + $this->settingCacheKeys[$key] = true; + $this->settingCache[$key] = $value; + + return $value; + } catch (Exception $ex) { + // Fall through below and return the default value. + } + } } - return null; + if (isset($defaults[$key])) { + $value = id(clone $defaults[$key]) + ->setViewer($this) + ->getSettingDefaultValue(); + } else { + $value = null; + } + + $this->settingCacheKeys[$key] = true; + $this->settingCache[$key] = $value; + + return $value; } @@ -510,15 +543,25 @@ final class PhabricatorUser return ($actual == $value); } + + /** + * @task settings + */ + public function clearUserSettingCache() { + $this->settingCacheKeys = array(); + $this->settingCache = array(); + + $settings_key = PhabricatorUserPreferencesCacheType::KEY_PREFERENCES; + $this->clearCacheData($settings_key); + + return $this; + } + public function getTranslation() { return $this->getUserSetting(PhabricatorTranslationSetting::SETTINGKEY); } public function getTimezoneIdentifier() { - if ($this->timezoneOverride) { - return $this->timezoneOverride; - } - return $this->getUserSetting(PhabricatorTimezoneSetting::SETTINGKEY); } @@ -533,7 +576,9 @@ final class PhabricatorUser * @task settings */ public function overrideTimezoneIdentifier($identifier) { - $this->timezoneOverride = $identifier; + $timezone_key = PhabricatorTimezoneSetting::SETTINGKEY; + $this->settingCacheKeys[$timezone_key] = true; + $this->settingCache[$timezone_key] = $identifier; return $this; } @@ -578,17 +623,17 @@ final class PhabricatorUser $line, PhabricatorRepository $repository = null) { - $editor = $this->loadPreferences()->getPreference( - PhabricatorUserPreferences::PREFERENCE_EDITOR); + $editor = $this->getUserSetting(PhabricatorEditorSetting::SETTINGKEY); if (is_array($path)) { - $multiedit = $this->loadPreferences()->getPreference( - PhabricatorUserPreferences::PREFERENCE_MULTIEDIT); + $multi_key = PhabricatorEditorMultipleSetting::SETTINGKEY; + $multiedit = $this->getUserSetting($multi_key); switch ($multiedit) { - case '': + case PhabricatorEditorMultipleSetting::VALUE_SPACES: $path = implode(' ', $path); break; - case 'disable': + case PhabricatorEditorMultipleSetting::VALUE_SINGLE: + default: return null; } } @@ -848,48 +893,13 @@ final class PhabricatorUser $format = 'M j'; } else { // Same year, month and day so show a time of day. - $pref_time = PhabricatorUserPreferences::PREFERENCE_TIME_FORMAT; - $format = $this->getPreference($pref_time); + $pref_time = PhabricatorTimeFormatSetting::SETTINGKEY; + $format = $this->getUserSetting($pref_time); } return $when->format($format); } - public function getPreference($key) { - $preferences = $this->loadPreferences(); - - // TODO: After T4103 and T7707 this should eventually be pushed down the - // stack into modular preference definitions and role profiles. This is - // just fixing T8601 and mildly anticipating those changes. - $value = $preferences->getPreference($key); - - $allowed_values = null; - switch ($key) { - case PhabricatorUserPreferences::PREFERENCE_TIME_FORMAT: - $allowed_values = array( - 'g:i A', - 'H:i', - ); - break; - case PhabricatorUserPreferences::PREFERENCE_DATE_FORMAT: - $allowed_values = array( - 'Y-m-d', - 'n/j/Y', - 'd-m-Y', - ); - break; - } - - if ($allowed_values !== null) { - $allowed_values = array_fuse($allowed_values); - if (empty($allowed_values[$value])) { - $value = head($allowed_values); - } - } - - return $value; - } - public function __toString() { return $this->getUsername(); } diff --git a/src/applications/settings/controller/PhabricatorSettingsTimezoneController.php b/src/applications/settings/controller/PhabricatorSettingsTimezoneController.php index 6b5fb39f22..4f34c39fa0 100644 --- a/src/applications/settings/controller/PhabricatorSettingsTimezoneController.php +++ b/src/applications/settings/controller/PhabricatorSettingsTimezoneController.php @@ -30,7 +30,7 @@ final class PhabricatorSettingsTimezoneController if ($request->isFormPost()) { $timezone = $request->getStr('timezone'); - $pref_ignore = PhabricatorUserPreferences::PREFERENCE_IGNORE_OFFSET; + $pref_ignore = PhabricatorTimezoneIgnoreOffsetSetting::SETTINGKEY; $pref_timezone = PhabricatorTimezoneSetting::SETTINGKEY; $preferences = $viewer->loadPreferences(); @@ -56,8 +56,7 @@ final class PhabricatorSettingsTimezoneController ->setPreference($pref_timezone, $timezone) ->save(); - $viewer->clearCacheData( - PhabricatorUserPreferencesCacheType::KEY_PREFERENCES); + $viewer->clearUserSettingCache(); } } diff --git a/src/applications/settings/panel/PhabricatorDateTimeSettingsPanel.php b/src/applications/settings/panel/PhabricatorDateTimeSettingsPanel.php index 6c21a88894..c9c2645030 100644 --- a/src/applications/settings/panel/PhabricatorDateTimeSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorDateTimeSettingsPanel.php @@ -22,7 +22,7 @@ final class PhabricatorDateTimeSettingsPanel extends PhabricatorSettingsPanel { $pref_time = PhabricatorUserPreferences::PREFERENCE_TIME_FORMAT; $pref_date = PhabricatorUserPreferences::PREFERENCE_DATE_FORMAT; $pref_week_start = PhabricatorUserPreferences::PREFERENCE_WEEK_START_DAY; - $pref_ignore = PhabricatorUserPreferences::PREFERENCE_IGNORE_OFFSET; + $pref_ignore = PhabricatorTimezoneIgnoreOffsetSetting::SETTINGKEY; $preferences = $user->loadPreferences(); $errors = array(); diff --git a/src/applications/settings/setting/PhabricatorInternalSetting.php b/src/applications/settings/setting/PhabricatorInternalSetting.php new file mode 100644 index 0000000000..af77608f41 --- /dev/null +++ b/src/applications/settings/setting/PhabricatorInternalSetting.php @@ -0,0 +1,4 @@ +validateTransactionValue($value); + } + public function getTransactionNewValue($value) { return $value; } diff --git a/src/applications/settings/setting/PhabricatorTimezoneIgnoreOffsetSetting.php b/src/applications/settings/setting/PhabricatorTimezoneIgnoreOffsetSetting.php new file mode 100644 index 0000000000..3a0223b572 --- /dev/null +++ b/src/applications/settings/setting/PhabricatorTimezoneIgnoreOffsetSetting.php @@ -0,0 +1,12 @@ +formatTime($epoch, 'Y!m!d!g:i A'); + $readable = $this->formatTime($epoch, 'Y!m!d!'.$this->getTimeFormat()); $readable = explode('!', $readable, 4); $year = $readable[0]; @@ -140,13 +140,15 @@ final class AphrontFormDateControl extends AphrontFormControl { } private function getTimeFormat() { - return $this->getViewer() - ->getPreference(PhabricatorUserPreferences::PREFERENCE_TIME_FORMAT); + $viewer = $this->getViewer(); + $time_key = PhabricatorTimeFormatSetting::SETTINGKEY; + return $viewer->getUserSetting($time_key); } private function getDateFormat() { - return $this->getViewer() - ->getPreference(PhabricatorUserPreferences::PREFERENCE_DATE_FORMAT); + $viewer = $this->getViewer(); + $date_key = PhabricatorDateFormatSetting::SETTINGKEY; + return $viewer->getUserSetting($date_key); } private function getTimeInputValue() { @@ -241,7 +243,6 @@ final class AphrontFormDateControl extends AphrontFormControl { 'format' => $this->getTimeFormat(), )); - $time_sel = javelin_tag( 'input', array( @@ -262,9 +263,9 @@ final class AphrontFormDateControl extends AphrontFormControl { ), $time_sel); - $preferences = $this->getViewer()->loadPreferences(); - $pref_week_start = PhabricatorUserPreferences::PREFERENCE_WEEK_START_DAY; - $week_start = $preferences->getPreference($pref_week_start, 0); + $viewer = $this->getViewer(); + $week_key = PhabricatorWeekStartDaySetting::SETTINGKEY; + $week_start = $viewer->getUserSetting($week_key); Javelin::initBehavior('fancy-datepicker', array( 'format' => $this->getDateFormat(), diff --git a/src/view/form/control/AphrontFormDateControlValue.php b/src/view/form/control/AphrontFormDateControlValue.php index f5bfda1cc9..aeb35cdb15 100644 --- a/src/view/form/control/AphrontFormDateControlValue.php +++ b/src/view/form/control/AphrontFormDateControlValue.php @@ -207,13 +207,15 @@ final class AphrontFormDateControlValue extends Phobject { } private function getTimeFormat() { - return $this->getViewer() - ->getPreference(PhabricatorUserPreferences::PREFERENCE_TIME_FORMAT); + $viewer = $this->getViewer(); + $time_key = PhabricatorTimeFormatSetting::SETTINGKEY; + return $viewer->getUserSetting($time_key); } private function getDateFormat() { - return $this->getViewer() - ->getPreference(PhabricatorUserPreferences::PREFERENCE_DATE_FORMAT); + $viewer = $this->getViewer(); + $date_key = PhabricatorDateFormatSetting::SETTINGKEY; + return $viewer->getUserSetting($date_key); } private function getFormattedDateFromDate($date, $time) { diff --git a/src/view/page/PhabricatorBarePageView.php b/src/view/page/PhabricatorBarePageView.php index 6a4444c981..82b4d43faf 100644 --- a/src/view/page/PhabricatorBarePageView.php +++ b/src/view/page/PhabricatorBarePageView.php @@ -130,8 +130,8 @@ class PhabricatorBarePageView extends AphrontPageView { if ($this->getRequest()) { $viewer = $this->getRequest()->getViewer(); if ($viewer) { - $postprocessor_key = $viewer->getPreference( - PhabricatorUserPreferences::PREFERENCE_RESOURCE_POSTPROCESSOR); + $postprocessor_key = $viewer->getUserSetting( + PhabricatorAccessibilitySetting::SETTINGKEY); if (strlen($postprocessor_key)) { $response->setPostProcessorKey($postprocessor_key); } diff --git a/src/view/page/PhabricatorStandardPageView.php b/src/view/page/PhabricatorStandardPageView.php index e2d373ab5a..cdadf7c309 100644 --- a/src/view/page/PhabricatorStandardPageView.php +++ b/src/view/page/PhabricatorStandardPageView.php @@ -225,13 +225,8 @@ final class PhabricatorStandardPageView extends PhabricatorBarePageView if ($user->isLoggedIn()) { $offset = $user->getTimeZoneOffset(); - $preferences = $user->loadPreferences(); - $ignore_key = PhabricatorUserPreferences::PREFERENCE_IGNORE_OFFSET; - - $ignore = $preferences->getPreference($ignore_key); - if (!strlen($ignore)) { - $ignore = null; - } + $ignore_key = PhabricatorTimezoneIgnoreOffsetSetting::SETTINGKEY; + $ignore = $user->getUserSetting($ignore_key); Javelin::initBehavior( 'detect-timezone', diff --git a/src/view/page/menu/PhabricatorMainMenuSearchView.php b/src/view/page/menu/PhabricatorMainMenuSearchView.php index d3c7319f7d..aa1855ce17 100644 --- a/src/view/page/menu/PhabricatorMainMenuSearchView.php +++ b/src/view/page/menu/PhabricatorMainMenuSearchView.php @@ -50,7 +50,7 @@ final class PhabricatorMainMenuSearchView extends AphrontView { ''); $search_datasource = new PhabricatorSearchDatasource(); - $scope_key = PhabricatorUserPreferences::PREFERENCE_SEARCH_SCOPE; + $scope_key = PhabricatorSearchScopeSetting::SETTINGKEY; Javelin::initBehavior( 'phabricator-search-typeahead', @@ -177,10 +177,8 @@ final class PhabricatorMainMenuSearchView extends AphrontView { 'href' => PhabricatorEnv::getDoclink('Search User Guide'), ); - $scope_key = PhabricatorUserPreferences::PREFERENCE_SEARCH_SCOPE; - $current_value = $viewer->loadPreferences()->getPreference( - $scope_key, - 'all'); + $scope_key = PhabricatorSearchScopeSetting::SETTINGKEY; + $current_value = $viewer->getUserSetting($scope_key); $current_icon = 'fa-globe'; foreach ($items as $item) { diff --git a/src/view/phui/calendar/PHUICalendarListView.php b/src/view/phui/calendar/PHUICalendarListView.php index fc2929adae..7dd921d769 100644 --- a/src/view/phui/calendar/PHUICalendarListView.php +++ b/src/view/phui/calendar/PHUICalendarListView.php @@ -147,16 +147,18 @@ final class PHUICalendarListView extends AphrontTagView { } private function getEventTooltip(AphrontCalendarEventView $event) { - $time_pref = $this->getUser() - ->getPreference(PhabricatorUserPreferences::PREFERENCE_TIME_FORMAT); + $viewer = $this->getViewer(); + $time_key = PhabricatorTimeFormatSetting::SETTINGKEY; + $time_pref = $viewer->getUserSetting($time_key); Javelin::initBehavior('phabricator-tooltips'); $start = id(AphrontFormDateControlValue::newFromEpoch( - $this->getUser(), + $viewer, $event->getEpochStart())); + $end = id(AphrontFormDateControlValue::newFromEpoch( - $this->getUser(), + $viewer, $event->getEpochEnd())); $start_date = $start->getDateTime()->format('m d Y'); diff --git a/src/view/viewutils.php b/src/view/viewutils.php index 8d57cd02d0..ea89399e22 100644 --- a/src/view/viewutils.php +++ b/src/view/viewutils.php @@ -31,21 +31,21 @@ function phabricator_relative_date($epoch, $user, $on = false) { } function phabricator_time($epoch, $user) { - $time_key = PhabricatorUserPreferences::PREFERENCE_TIME_FORMAT; + $time_key = PhabricatorTimeFormatSetting::SETTINGKEY; return phabricator_format_local_time( $epoch, $user, - $user->getPreference($time_key)); + $user->getUserSetting($time_key)); } function phabricator_datetime($epoch, $user) { - $time_key = PhabricatorUserPreferences::PREFERENCE_TIME_FORMAT; + $time_key = PhabricatorTimeFormatSetting::SETTINGKEY; return phabricator_format_local_time( $epoch, $user, pht('%s, %s', phutil_date_format($epoch), - $user->getPreference($time_key))); + $user->getUserSetting($time_key))); } /** From 2f936094d8df8562d0616c37fcd73fce74536281 Mon Sep 17 00:00:00 2001 From: epriestley Date: Fri, 3 Jun 2016 05:05:51 -0700 Subject: [PATCH 04/67] Convert "Account" and "Date and Time" settings to EditEngine Summary: Ref T4103. This pretty much replaces these panels in-place with similar looking ones that go through EditEngine. This has a few rough edges but they're pretty minor and/or hard to hit (for example, when editing another user's settings, the crumbs have a redundant link in them). Test Plan: - Edited my own settings. - Edited a bot user's settings. - Tried to edit another user's settings (failed). {F1674465} Reviewers: chad Reviewed By: chad Maniphest Tasks: T4103 Differential Revision: https://secure.phabricator.com/D16017 --- src/__phutil_library_map__.php | 6 +- .../PhabricatorSettingsApplication.php | 5 +- .../PhabricatorSettingsMainController.php | 44 +++-- .../editor/PhabricatorSettingsEditEngine.php | 85 +++++++++ .../panel/PhabricatorAccountSettingsPanel.php | 165 +----------------- .../PhabricatorDateTimeSettingsPanel.php | 115 +----------- .../PhabricatorEditEngineSettingsPanel.php | 74 ++++++++ .../panel/PhabricatorSettingsPanel.php | 50 ++++-- .../setting/PhabricatorDateFormatSetting.php | 8 + .../setting/PhabricatorPronounSetting.php | 11 ++ .../settings/setting/PhabricatorSetting.php | 14 ++ .../setting/PhabricatorTimeFormatSetting.php | 8 + .../setting/PhabricatorTimezoneSetting.php | 12 ++ .../setting/PhabricatorTranslationSetting.php | 13 ++ .../PhabricatorWeekStartDaySetting.php | 8 + .../editengine/PhabricatorEditEngine.php | 79 +++++++-- 16 files changed, 370 insertions(+), 327 deletions(-) create mode 100644 src/applications/settings/panel/PhabricatorEditEngineSettingsPanel.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 28b0344977..2180dbb387 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -2372,6 +2372,7 @@ phutil_register_library_map(array( 'PhabricatorEditEngineQuery' => 'applications/transactions/query/PhabricatorEditEngineQuery.php', 'PhabricatorEditEngineSearchEngine' => 'applications/transactions/query/PhabricatorEditEngineSearchEngine.php', 'PhabricatorEditEngineSelectCommentAction' => 'applications/transactions/commentaction/PhabricatorEditEngineSelectCommentAction.php', + 'PhabricatorEditEngineSettingsPanel' => 'applications/settings/panel/PhabricatorEditEngineSettingsPanel.php', 'PhabricatorEditEngineTokenizerCommentAction' => 'applications/transactions/commentaction/PhabricatorEditEngineTokenizerCommentAction.php', 'PhabricatorEditField' => 'applications/transactions/editfield/PhabricatorEditField.php', 'PhabricatorEditPage' => 'applications/transactions/editengine/PhabricatorEditPage.php', @@ -6167,7 +6168,7 @@ phutil_register_library_map(array( 'PhabricatorAccessLog' => 'Phobject', 'PhabricatorAccessLogConfigOptions' => 'PhabricatorApplicationConfigOptions', 'PhabricatorAccessibilitySetting' => 'PhabricatorSelectSetting', - 'PhabricatorAccountSettingsPanel' => 'PhabricatorSettingsPanel', + 'PhabricatorAccountSettingsPanel' => 'PhabricatorEditEngineSettingsPanel', 'PhabricatorActionListView' => 'AphrontView', 'PhabricatorActionView' => 'AphrontView', 'PhabricatorActivitySettingsPanel' => 'PhabricatorSettingsPanel', @@ -6863,7 +6864,7 @@ phutil_register_library_map(array( 'PhabricatorDatasourceEditField' => 'PhabricatorTokenizerEditField', 'PhabricatorDatasourceEditType' => 'PhabricatorPHIDListEditType', 'PhabricatorDateFormatSetting' => 'PhabricatorSelectSetting', - 'PhabricatorDateTimeSettingsPanel' => 'PhabricatorSettingsPanel', + 'PhabricatorDateTimeSettingsPanel' => 'PhabricatorEditEngineSettingsPanel', 'PhabricatorDebugController' => 'PhabricatorController', 'PhabricatorDefaultRequestExceptionHandler' => 'PhabricatorRequestExceptionHandler', 'PhabricatorDefaultSyntaxStyle' => 'PhabricatorSyntaxStyle', @@ -6942,6 +6943,7 @@ phutil_register_library_map(array( 'PhabricatorEditEngineQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhabricatorEditEngineSearchEngine' => 'PhabricatorApplicationSearchEngine', 'PhabricatorEditEngineSelectCommentAction' => 'PhabricatorEditEngineCommentAction', + 'PhabricatorEditEngineSettingsPanel' => 'PhabricatorSettingsPanel', 'PhabricatorEditEngineTokenizerCommentAction' => 'PhabricatorEditEngineCommentAction', 'PhabricatorEditField' => 'Phobject', 'PhabricatorEditPage' => 'Phobject', diff --git a/src/applications/settings/application/PhabricatorSettingsApplication.php b/src/applications/settings/application/PhabricatorSettingsApplication.php index 4dba9a2e25..fa518cadeb 100644 --- a/src/applications/settings/application/PhabricatorSettingsApplication.php +++ b/src/applications/settings/application/PhabricatorSettingsApplication.php @@ -29,7 +29,10 @@ final class PhabricatorSettingsApplication extends PhabricatorApplication { public function getRoutes() { return array( '/settings/' => array( - '(?:(?P\d+)/)?(?:panel/(?P[^/]+)/)?' + '(?:(?P\d+)/)?'. + '(?:panel/(?P(?P[^/]+))/'. + '(?:(?Psaved)/)?'. + ')?' => 'PhabricatorSettingsMainController', 'adjust/' => 'PhabricatorSettingsAdjustController', 'timezone/(?P[^/]+)/' diff --git a/src/applications/settings/controller/PhabricatorSettingsMainController.php b/src/applications/settings/controller/PhabricatorSettingsMainController.php index bf9b171031..b23fd5f73f 100644 --- a/src/applications/settings/controller/PhabricatorSettingsMainController.php +++ b/src/applications/settings/controller/PhabricatorSettingsMainController.php @@ -10,8 +10,14 @@ final class PhabricatorSettingsMainController } private function isSelf() { + $user = $this->getUser(); + if (!$user) { + return false; + } + + $user_phid = $user->getPHID(); + $viewer_phid = $this->getViewer()->getPHID(); - $user_phid = $this->getUser()->getPHID(); return ($viewer_phid == $user_phid); } @@ -45,21 +51,19 @@ final class PhabricatorSettingsMainController $key = $nav->selectFilter($key, head($panels)->getPanelKey()); - $panel = $panels[$key]; - $panel->setUser($this->getUser()); - $panel->setViewer($viewer); + $panel = $panels[$key] + ->setUser($this->getUser()) + ->setViewer($viewer) + ->setController($this) + ->setNavigation($nav); $response = $panel->processRequest($request); - if ($response instanceof AphrontResponse) { + if (($response instanceof AphrontResponse) || + ($response instanceof AphrontResponseProducerInterface)) { return $response; } $crumbs = $this->buildApplicationCrumbs(); - if (!$this->isSelf()) { - $crumbs->addTextCrumb( - $this->getUser()->getUsername(), - '/p/'.$this->getUser()->getUsername().'/'); - } $crumbs->addTextCrumb($panel->getPanelName()); $title = $panel->getPanelName(); @@ -76,11 +80,7 @@ final class PhabricatorSettingsMainController } private function buildPanels() { - $panels = id(new PhutilClassMapQuery()) - ->setAncestorClass('PhabricatorSettingsPanel') - ->setExpandMethod('buildPanels') - ->setUniqueMethod('getPanelKey') - ->execute(); + $panels = PhabricatorSettingsPanel::getAllPanels(); $result = array(); foreach ($panels as $key => $panel) { @@ -107,8 +107,6 @@ final class PhabricatorSettingsMainController $result[$key] = $panel; } - $result = msort($result, 'getPanelSortKey'); - if (!$result) { throw new Exception(pht('No settings panels are available.')); } @@ -145,4 +143,16 @@ final class PhabricatorSettingsMainController return $this->renderSideNav($panels)->getMenu(); } + protected function buildApplicationCrumbs() { + $crumbs = parent::buildApplicationCrumbs(); + + $user = $this->getUser(); + if (!$this->isSelf() && $user) { + $username = $user->getUsername(); + $crumbs->addTextCrumb($username, "/p/{$username}/"); + } + + return $crumbs; + } + } diff --git a/src/applications/settings/editor/PhabricatorSettingsEditEngine.php b/src/applications/settings/editor/PhabricatorSettingsEditEngine.php index 5b077b9485..69274eb3be 100644 --- a/src/applications/settings/editor/PhabricatorSettingsEditEngine.php +++ b/src/applications/settings/editor/PhabricatorSettingsEditEngine.php @@ -5,6 +5,27 @@ final class PhabricatorSettingsEditEngine const ENGINECONST = 'settings.settings'; + private $isSelfEdit; + private $profileURI; + + public function setIsSelfEdit($is_self_edit) { + $this->isSelfEdit = $is_self_edit; + return $this; + } + + public function getIsSelfEdit() { + return $this->isSelfEdit; + } + + public function setProfileURI($profile_uri) { + $this->profileURI = $profile_uri; + return $this; + } + + public function getProfileURI() { + return $this->profileURI; + } + public function isEngineConfigurable() { return false; } @@ -54,6 +75,12 @@ final class PhabricatorSettingsEditEngine } protected function getObjectName() { + $page = $this->getSelectedPage(); + + if ($page) { + return $page->getLabel(); + } + return pht('Settings'); } @@ -74,6 +101,62 @@ final class PhabricatorSettingsEditEngine return PhabricatorPolicies::POLICY_ADMIN; } + public function getEffectiveObjectEditCancelURI($object) { + if ($this->getIsSelfEdit()) { + return null; + } + + if ($this->getProfileURI()) { + return $this->getProfileURI(); + } + + return parent::getEffectiveObjectEditCancelURI($object); + } + + protected function newPages($object) { + $viewer = $this->getViewer(); + $user = $object->getUser(); + + $panels = PhabricatorSettingsPanel::getAllPanels(); + + foreach ($panels as $key => $panel) { + if (!($panel instanceof PhabricatorEditEngineSettingsPanel)) { + unset($panels[$key]); + continue; + } + + $panel->setViewer($viewer); + if ($user) { + $panel->setUser($user); + } + } + + $pages = array(); + $uris = array(); + foreach ($panels as $key => $panel) { + $uris[$key] = $panel->getPanelURI(); + + $page = $panel->newEditEnginePage(); + if (!$page) { + continue; + } + $pages[] = $page; + } + + $more_pages = array( + id(new PhabricatorEditPage()) + ->setKey('extra') + ->setLabel(pht('Extra Settings')) + ->setIsDefault(true), + ); + + foreach ($more_pages as $page) { + $pages[] = $page; + } + + return $pages; + } + protected function buildCustomEditFields($object) { $viewer = $this->getViewer(); $settings = PhabricatorSetting::getAllEnabledSettings($viewer); @@ -84,6 +167,8 @@ final class PhabricatorSettingsEditEngine $settings[$key] = $setting; } + $settings = msortv($settings, 'getSettingOrderVector'); + $fields = array(); foreach ($settings as $setting) { foreach ($setting->newCustomEditFields($object) as $field) { diff --git a/src/applications/settings/panel/PhabricatorAccountSettingsPanel.php b/src/applications/settings/panel/PhabricatorAccountSettingsPanel.php index 195971d8a7..f06f41ca4f 100644 --- a/src/applications/settings/panel/PhabricatorAccountSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorAccountSettingsPanel.php @@ -1,10 +1,9 @@ getViewer(); - $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)) { - $new_value = $sex; - } else { - $new_value = null; - } - - $preferences->setPreference( - PhabricatorPronounSetting::SETTINGKEY, - $new_value); - - $preferences->setPreference( - PhabricatorTranslationSetting::SETTINGKEY, - $request->getStr('translation')); - - if (!$errors) { - $preferences->save(); - - return id(new AphrontRedirectResponse()) - ->setURI($this->getPanelURI('?saved=true')); - } - } - - $label_unknown = pht('%s updated their profile', $username); - $label_her = pht('%s updated her profile', $username); - $label_his = pht('%s updated his profile', $username); - - $sexes = array( - PhutilPerson::SEX_UNKNOWN => $label_unknown, - PhutilPerson::SEX_MALE => $label_his, - PhutilPerson::SEX_FEMALE => $label_her, - ); - - $translations = $this->getTranslationOptions(); - - $form = new AphrontFormView(); - $form - ->setUser($viewer) - ->appendChild( - id(new AphrontFormSelectControl()) - ->setOptions($translations) - ->setLabel(pht('Translation')) - ->setName('translation') - ->setValue($user->getTranslation())) - ->appendRemarkupInstructions(pht('**Choose the pronoun you prefer:**')) - ->appendChild( - id(new AphrontFormSelectControl()) - ->setOptions($sexes) - ->setLabel(pht('Pronoun')) - ->setName('sex') - ->setValue($user->getSex())) - ->appendChild( - id(new AphrontFormSubmitControl()) - ->setValue(pht('Save Account Settings'))); - - $form_box = id(new PHUIObjectBoxView()) - ->setHeaderText(pht('Account Settings')) - ->setFormSaved($request->getStr('saved')) - ->setFormErrors($errors) - ->setForm($form); - - return array( - $form_box, - ); - } - - private function getTranslationOptions() { - $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; - } - - // TODO: Select a default properly. - $default = 'en_US'; - - $results = array(); - foreach ($groups as $key => $group) { - $label = $group_labels[$key]; - if (!$group) { - continue; - } - - asort($group); - - if ($key == 'normal') { - $group = array( - '' => pht('Server Default: %s', $locales[$default]->getLocaleName()), - ) + $group; - } - - $results[$label] = $group; - } - - return $results; - } - } diff --git a/src/applications/settings/panel/PhabricatorDateTimeSettingsPanel.php b/src/applications/settings/panel/PhabricatorDateTimeSettingsPanel.php index c9c2645030..9d9b015fbf 100644 --- a/src/applications/settings/panel/PhabricatorDateTimeSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorDateTimeSettingsPanel.php @@ -1,10 +1,9 @@ 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; - $pref_ignore = PhabricatorTimezoneIgnoreOffsetSetting::SETTINGKEY; - $preferences = $user->loadPreferences(); - - $errors = array(); - if ($request->isFormPost()) { - $new_timezone = $request->getStr('timezone'); - 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)) - ->setPreference( - $pref_date, - $request->getStr($pref_date)) - ->setPreference( - $pref_week_start, - $request->getStr($pref_week_start)) - ->setPreference($pref_ignore, null); - - if (!$errors) { - $preferences->save(); - - return id(new AphrontRedirectResponse()) - ->setURI($this->getPanelURI('?saved=true')); - } - } - - $timezone_ids = DateTimeZone::listIdentifiers(); - $timezone_id_map = array_fuse($timezone_ids); - - $form = new AphrontFormView(); - $form - ->setUser($user) - ->appendChild( - id(new AphrontFormSelectControl()) - ->setLabel(pht('Timezone')) - ->setName('timezone') - ->setOptions($timezone_id_map) - ->setValue($user->getTimezoneIdentifier())) - ->appendChild( - id(new AphrontFormSelectControl()) - ->setLabel(pht('Time-of-Day Format')) - ->setName($pref_time) - ->setOptions(array( - 'g:i A' => pht('12-hour (2:34 PM)'), - 'H:i' => pht('24-hour (14:34)'), - )) - ->setCaption( - pht('Format used when rendering a time of day.')) - ->setValue($preferences->getPreference($pref_time))) - ->appendChild( - id(new AphrontFormSelectControl()) - ->setLabel(pht('Date Format')) - ->setName($pref_date) - ->setOptions(array( - 'Y-m-d' => pht('ISO 8601 (2000-02-28)'), - 'n/j/Y' => pht('US (2/28/2000)'), - 'd-m-Y' => pht('European (28-02-2000)'), - )) - ->setCaption( - pht('Format used when rendering a date.')) - ->setValue($preferences->getPreference($pref_date))) - ->appendChild( - id(new AphrontFormSelectControl()) - ->setLabel(pht('Week Starts On')) - ->setOptions($this->getWeekDays()) - ->setName($pref_week_start) - ->setCaption( - pht('Calendar weeks will start with this day.')) - ->setValue($preferences->getPreference($pref_week_start, 0))) - ->appendChild( - id(new AphrontFormSubmitControl()) - ->setValue(pht('Save Account Settings'))); - - $form_box = id(new PHUIObjectBoxView()) - ->setHeaderText(pht('Date and Time Settings')) - ->setFormSaved($request->getStr('saved')) - ->setFormErrors($errors) - ->setForm($form); - - return array( - $form_box, - ); + public function isEditableByAdministrators() { + return true; } - private function getWeekDays() { - return array( - pht('Sunday'), - pht('Monday'), - pht('Tuesday'), - pht('Wednesday'), - pht('Thursday'), - pht('Friday'), - pht('Saturday'), - ); - } } diff --git a/src/applications/settings/panel/PhabricatorEditEngineSettingsPanel.php b/src/applications/settings/panel/PhabricatorEditEngineSettingsPanel.php new file mode 100644 index 0000000000..7e80b3ae40 --- /dev/null +++ b/src/applications/settings/panel/PhabricatorEditEngineSettingsPanel.php @@ -0,0 +1,74 @@ +getViewer(); + $user = $this->getUser(); + + if ($user->getPHID() === $viewer->getPHID()) { + $is_self = true; + } else { + $is_self = false; + } + + if ($user->getPHID()) { + $profile_uri = '/people/manage/'.$user->getID().'/'; + } else { + $profile_uri = null; + } + + $engine = id(new PhabricatorSettingsEditEngine()) + ->setController($this->getController()) + ->setNavigation($this->getNavigation()) + ->setHideHeader(true) + ->setIsSelfEdit($is_self) + ->setProfileURI($profile_uri); + + $preferences = $user->loadPreferences(); + + PhabricatorPolicyFilter::requireCapability( + $viewer, + $preferences, + PhabricatorPolicyCapability::CAN_EDIT); + + $engine->setTargetObject($preferences); + + return $engine->buildResponse(); + } + + final public function newEditEnginePage() { + $field_keys = $this->getPanelSettingsKeys(); + if (!$field_keys) { + return null; + } + + $key = $this->getPanelKey(); + $label = $this->getPanelName(); + $panel_uri = $this->getPanelURI().'saved/'; + + return id(new PhabricatorEditPage()) + ->setKey($key) + ->setLabel($label) + ->setViewURI($panel_uri) + ->setFieldKeys($field_keys); + } + + final public function getPanelSettingsKeys() { + $viewer = $this->getViewer(); + $settings = PhabricatorSetting::getAllEnabledSettings($viewer); + + $this_key = $this->getPanelKey(); + + $panel_settings = array(); + foreach ($settings as $setting) { + if ($setting->getSettingPanelKey() == $this_key) { + $panel_settings[] = $setting; + } + } + + return mpull($panel_settings, 'getSettingKey'); + } + +} diff --git a/src/applications/settings/panel/PhabricatorSettingsPanel.php b/src/applications/settings/panel/PhabricatorSettingsPanel.php index 341b7a4f69..1f05147daa 100644 --- a/src/applications/settings/panel/PhabricatorSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorSettingsPanel.php @@ -17,9 +17,10 @@ abstract class PhabricatorSettingsPanel extends Phobject { private $user; private $viewer; + private $controller; + private $navigation; private $overrideURI; - public function setUser(PhabricatorUser $user) { $this->user = $user; return $this; @@ -43,6 +44,32 @@ abstract class PhabricatorSettingsPanel extends Phobject { return $this; } + final public function setController(PhabricatorController $controller) { + $this->controller = $controller; + return $this; + } + + final public function getController() { + return $this->controller; + } + + final public function setNavigation(AphrontSideNavFilterView $navigation) { + $this->navigation = $navigation; + return $this; + } + + final public function getNavigation() { + return $this->navigation; + } + + final public static function getAllPanels() { + return id(new PhutilClassMapQuery()) + ->setAncestorClass(__CLASS__) + ->setUniqueMethod('getPanelKey') + ->setSortMethod('getPanelSortKey') + ->execute(); + } + /* -( Panel Configuration )------------------------------------------------ */ @@ -54,7 +81,9 @@ abstract class PhabricatorSettingsPanel extends Phobject { * @return string Unique panel identifier (used in URIs). * @task config */ - abstract public function getPanelKey(); + public function getPanelKey() { + return $this->getPhobjectClassConstant('PANELKEY'); + } /** @@ -95,23 +124,6 @@ abstract class PhabricatorSettingsPanel extends Phobject { } - /** - * You can use this callback to generate multiple similar panels which all - * share the same implementation. For example, OAuth providers each have a - * separate panel, but the implementation for each panel is the same. - * - * To generate multiple panels, build them here and return a list. By default, - * the current panel (`$this`) is returned alone. For most panels, this - * is the right implementation. - * - * @return list Zero or more panels. - * @task config - */ - public function buildPanels() { - return array($this); - } - - /** * Return true if this panel is available to administrators while editing * system agent accounts. diff --git a/src/applications/settings/setting/PhabricatorDateFormatSetting.php b/src/applications/settings/setting/PhabricatorDateFormatSetting.php index b2289c4872..a6f5659bb4 100644 --- a/src/applications/settings/setting/PhabricatorDateFormatSetting.php +++ b/src/applications/settings/setting/PhabricatorDateFormatSetting.php @@ -13,6 +13,14 @@ final class PhabricatorDateFormatSetting return pht('Date Format'); } + public function getSettingPanelKey() { + return PhabricatorDateTimeSettingsPanel::PANELKEY; + } + + protected function getSettingOrder() { + return 200; + } + protected function getControlInstructions() { return pht( 'Select the format you prefer for editing dates.'); diff --git a/src/applications/settings/setting/PhabricatorPronounSetting.php b/src/applications/settings/setting/PhabricatorPronounSetting.php index b1fad77ed0..a1e01d2fd6 100644 --- a/src/applications/settings/setting/PhabricatorPronounSetting.php +++ b/src/applications/settings/setting/PhabricatorPronounSetting.php @@ -9,6 +9,14 @@ final class PhabricatorPronounSetting return pht('Pronoun'); } + public function getSettingPanelKey() { + return PhabricatorAccountSettingsPanel::PANELKEY; + } + + protected function getSettingOrder() { + return 200; + } + protected function getControlInstructions() { return pht('Choose the pronoun you prefer.'); } @@ -18,6 +26,9 @@ final class PhabricatorPronounSetting } protected function getSelectOptions() { + // TODO: When editing another user's settings as an administrator, this + // is not the best username: the user's username would be better. + $viewer = $this->getViewer(); $username = $viewer->getUsername(); diff --git a/src/applications/settings/setting/PhabricatorSetting.php b/src/applications/settings/setting/PhabricatorSetting.php index caa463c752..517cc67317 100644 --- a/src/applications/settings/setting/PhabricatorSetting.php +++ b/src/applications/settings/setting/PhabricatorSetting.php @@ -15,6 +15,20 @@ abstract class PhabricatorSetting extends Phobject { abstract public function getSettingName(); + public function getSettingPanelKey() { + return null; + } + + protected function getSettingOrder() { + return 1000; + } + + public function getSettingOrderVector() { + return id(new PhutilSortVector()) + ->addInt($this->getSettingOrder()) + ->addString($this->getSettingName()); + } + protected function getControlInstructions() { return null; } diff --git a/src/applications/settings/setting/PhabricatorTimeFormatSetting.php b/src/applications/settings/setting/PhabricatorTimeFormatSetting.php index cd9c4d02d9..6fafa4e9a1 100644 --- a/src/applications/settings/setting/PhabricatorTimeFormatSetting.php +++ b/src/applications/settings/setting/PhabricatorTimeFormatSetting.php @@ -12,6 +12,14 @@ final class PhabricatorTimeFormatSetting return pht('Time Format'); } + public function getSettingPanelKey() { + return PhabricatorDateTimeSettingsPanel::PANELKEY; + } + + protected function getSettingOrder() { + return 300; + } + protected function getControlInstructions() { return pht( 'Select the format you prefer for editing and displaying time.'); diff --git a/src/applications/settings/setting/PhabricatorTimezoneSetting.php b/src/applications/settings/setting/PhabricatorTimezoneSetting.php index 0aaa3d5b50..e68849e2ae 100644 --- a/src/applications/settings/setting/PhabricatorTimezoneSetting.php +++ b/src/applications/settings/setting/PhabricatorTimezoneSetting.php @@ -9,6 +9,18 @@ final class PhabricatorTimezoneSetting return pht('Timezone'); } + public function getSettingPanelKey() { + return PhabricatorDateTimeSettingsPanel::PANELKEY; + } + + protected function getSettingOrder() { + return 100; + } + + protected function getControlInstructions() { + return pht('Select your local timezone.'); + } + public function getSettingDefaultValue() { return date_default_timezone_get(); } diff --git a/src/applications/settings/setting/PhabricatorTranslationSetting.php b/src/applications/settings/setting/PhabricatorTranslationSetting.php index dd0ba95a66..3f4c5a43db 100644 --- a/src/applications/settings/setting/PhabricatorTranslationSetting.php +++ b/src/applications/settings/setting/PhabricatorTranslationSetting.php @@ -9,10 +9,23 @@ final class PhabricatorTranslationSetting return pht('Translation'); } + public function getSettingPanelKey() { + return PhabricatorAccountSettingsPanel::PANELKEY; + } + + protected function getSettingOrder() { + return 100; + } + public function getSettingDefaultValue() { return 'en_US'; } + protected function getControlInstructions() { + return pht( + 'Choose which language you would like the Phabricator UI to use.'); + } + protected function getSelectOptionGroups() { $is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business'); $locales = PhutilLocale::loadAllLocales(); diff --git a/src/applications/settings/setting/PhabricatorWeekStartDaySetting.php b/src/applications/settings/setting/PhabricatorWeekStartDaySetting.php index 8d3bb93c07..148fa2b08b 100644 --- a/src/applications/settings/setting/PhabricatorWeekStartDaySetting.php +++ b/src/applications/settings/setting/PhabricatorWeekStartDaySetting.php @@ -9,6 +9,14 @@ final class PhabricatorWeekStartDaySetting return pht('Week Starts On'); } + public function getSettingPanelKey() { + return PhabricatorDateTimeSettingsPanel::PANELKEY; + } + + protected function getSettingOrder() { + return 400; + } + protected function getControlInstructions() { return pht( 'Choose which day a calendar week should begin on.'); diff --git a/src/applications/transactions/editengine/PhabricatorEditEngine.php b/src/applications/transactions/editengine/PhabricatorEditEngine.php index 1717b2cc7a..7374bf786a 100644 --- a/src/applications/transactions/editengine/PhabricatorEditEngine.php +++ b/src/applications/transactions/editengine/PhabricatorEditEngine.php @@ -26,6 +26,8 @@ abstract class PhabricatorEditEngine private $targetObject; private $page; private $pages; + private $navigation; + private $hideHeader; final public function setViewer(PhabricatorUser $viewer) { $this->viewer = $viewer; @@ -80,6 +82,24 @@ abstract class PhabricatorEditEngine return $this->targetObject; } + public function setNavigation(AphrontSideNavFilterView $navigation) { + $this->navigation = $navigation; + return $this; + } + + public function getNavigation() { + return $this->navigation; + } + + public function setHideHeader($hide_header) { + $this->hideHeader = $hide_header; + return $this; + } + + public function getHideHeader() { + return $this->hideHeader; + } + /* -( Managing Fields )---------------------------------------------------- */ @@ -1090,17 +1110,22 @@ abstract class PhabricatorEditEngine ->addSubmitButton($submit_button); } - $header = id(new PHUIHeaderView()) - ->setHeader($header_text) - ->setHeaderIcon($header_icon); + $crumbs = $this->buildCrumbs($object, $final = true); + + if ($this->getHideHeader()) { + $header = null; + $crumbs->setBorder(false); + } else { + $header = id(new PHUIHeaderView()) + ->setHeader($header_text) + ->setHeaderIcon($header_icon); + $crumbs->setBorder(true); + } if ($action_button) { $header->addActionLink($action_button); } - $crumbs = $this->buildCrumbs($object, $final = true); - $crumbs->setBorder(true); - $box = id(new PHUIObjectBoxView()) ->setUser($viewer) ->setHeaderText($this->getObjectName()) @@ -1108,12 +1133,30 @@ abstract class PhabricatorEditEngine ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->appendChild($form); - $view = id(new PHUITwoColumnView()) - ->setHeader($header) - ->setFooter(array( - $box, - $previews, - )); + // This is fairly questionable, but in use by Settings. + if ($request->getURIData('formSaved')) { + $box->setFormSaved(true); + } + + $content = array( + $box, + $previews, + ); + + $view = new PHUITwoColumnView(); + + if ($header) { + $view->setHeader($header); + } + + $navigation = $this->getNavigation(); + if ($navigation) { + $view + ->setNavigation($navigation) + ->setMainColumn($content); + } else { + $view->setFooter($content); + } return $controller->newPage() ->setTitle($header_text) @@ -1155,10 +1198,14 @@ abstract class PhabricatorEditEngine } if (!$request->isAjax()) { - $form->appendControl( - id(new AphrontFormSubmitControl()) - ->addCancelButton($cancel_uri) - ->setValue($submit_button)); + $buttons = id(new AphrontFormSubmitControl()) + ->setValue($submit_button); + + if ($cancel_uri) { + $buttons->addCancelButton($cancel_uri); + } + + $form->appendControl($buttons); } return $form; From 2725fdf80008c611e05a33762a3fa068af4e6ed7 Mon Sep 17 00:00:00 2001 From: epriestley Date: Fri, 3 Jun 2016 05:22:40 -0700 Subject: [PATCH 05/67] When a user changes their timezone, clear their ignored timezone offset Summary: Ref T4103. We have a couple of settings like this where changing one setting changes another (e.g., enabling DarkConsole makes the console visible). Provide a mechanism to let changing timezone really mean "change timezone, and also clear the timezone offset". Test Plan: Swapped timezones, reconciled them by ignoring the offset, changed timezone again to another zone with the same offset, got asked to reconcile again. Reviewers: chad Reviewed By: chad Maniphest Tasks: T4103 Differential Revision: https://secure.phabricator.com/D16018 --- .../PhabricatorUserPreferencesEditor.php | 40 ++++++++++++++----- .../settings/setting/PhabricatorSetting.php | 14 +++++++ .../setting/PhabricatorTimezoneSetting.php | 12 ++++++ 3 files changed, 57 insertions(+), 9 deletions(-) diff --git a/src/applications/settings/editor/PhabricatorUserPreferencesEditor.php b/src/applications/settings/editor/PhabricatorUserPreferencesEditor.php index d320391091..918142f95c 100644 --- a/src/applications/settings/editor/PhabricatorUserPreferencesEditor.php +++ b/src/applications/settings/editor/PhabricatorUserPreferencesEditor.php @@ -19,6 +19,23 @@ final class PhabricatorUserPreferencesEditor return $types; } + protected function expandTransaction( + PhabricatorLiskDAO $object, + PhabricatorApplicationTransaction $xaction) { + + $setting_key = $xaction->getMetadataValue( + PhabricatorUserPreferencesTransaction::PROPERTY_SETTING); + + $settings = $this->getSettings(); + $setting = idx($settings, $setting_key); + if ($setting) { + return $setting->expandSettingTransaction($object, $xaction); + } + + return parent::expandTransaction($object, $xaction); + } + + protected function getCustomTransactionOldValue( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { @@ -95,15 +112,7 @@ final class PhabricatorUserPreferencesEditor array $xactions) { $errors = parent::validateTransaction($object, $type, $xactions); - - $actor = $this->getActor(); - $settings = PhabricatorSetting::getAllEnabledSettings($actor); - - foreach ($settings as $key => $setting) { - $setting = clone $setting; - $setting->setViewer($actor); - $settings[$key] = $setting; - } + $settings = $this->getSettings(); switch ($type) { case PhabricatorUserPreferencesTransaction::TYPE_SETTING: @@ -157,4 +166,17 @@ final class PhabricatorUserPreferencesEditor return $xactions; } + private function getSettings() { + $actor = $this->getActor(); + $settings = PhabricatorSetting::getAllEnabledSettings($actor); + + foreach ($settings as $key => $setting) { + $setting = clone $setting; + $setting->setViewer($actor); + $settings[$key] = $setting; + } + + return $settings; + } + } diff --git a/src/applications/settings/setting/PhabricatorSetting.php b/src/applications/settings/setting/PhabricatorSetting.php index 517cc67317..8307d17082 100644 --- a/src/applications/settings/setting/PhabricatorSetting.php +++ b/src/applications/settings/setting/PhabricatorSetting.php @@ -111,4 +111,18 @@ abstract class PhabricatorSetting extends Phobject { return $value; } + public function expandSettingTransaction($object, $xaction) { + return array($xaction); + } + + protected function newSettingTransaction($object, $key, $value) { + $setting_property = PhabricatorUserPreferencesTransaction::PROPERTY_SETTING; + $xaction_type = PhabricatorUserPreferencesTransaction::TYPE_SETTING; + + return id(clone $object->getApplicationTransactionTemplate()) + ->setTransactionType($xaction_type) + ->setMetadataValue($setting_property, $key) + ->setNewValue($value); + } + } diff --git a/src/applications/settings/setting/PhabricatorTimezoneSetting.php b/src/applications/settings/setting/PhabricatorTimezoneSetting.php index e68849e2ae..b7a6b94e30 100644 --- a/src/applications/settings/setting/PhabricatorTimezoneSetting.php +++ b/src/applications/settings/setting/PhabricatorTimezoneSetting.php @@ -87,4 +87,16 @@ final class PhabricatorTimezoneSetting return $option_groups; } + public function expandSettingTransaction($object, $xaction) { + // When the user changes their timezone, we also clear any ignored + // timezone offset. + return array( + $xaction, + $this->newSettingTransaction( + $object, + PhabricatorTimezoneIgnoreOffsetSetting::SETTINGKEY, + null), + ); + } + } From eb673fd783e49d74914cba7726b548193c469ee2 Mon Sep 17 00:00:00 2001 From: epriestley Date: Fri, 3 Jun 2016 05:31:33 -0700 Subject: [PATCH 06/67] Formalize and fully modularize settings panel groups Summary: Ref T4103. Settings panels are grouped into categories of similar panels (like "Email" or "Sessions and Logs"). Currently, this is done informally, by just grouping and ordering by strings. This won't work well with translations, since it means the ordering is entirely dependent on the language order, so the first settings panel you see might be something irrelvant or confusing. We'd also potentially break third-party stuff by changing strings, but do so in a silent hard-to-detect way. Provide formal objects and modularize the panel groups completely. Test Plan: Verified all panels still appear properly and in the same groups and order. Reviewers: chad Reviewed By: chad Maniphest Tasks: T4103 Differential Revision: https://secure.phabricator.com/D16020 --- src/__phutil_library_map__.php | 14 +++++ .../PhabricatorConduitTokensSettingsPanel.php | 4 +- .../DiffusionSetPasswordSettingsPanel.php | 4 +- ...OAuthServerAuthorizationsSettingsPanel.php | 4 +- .../PhabricatorSettingsMainController.php | 9 ++-- .../panel/PhabricatorAccountSettingsPanel.php | 4 +- .../PhabricatorActivitySettingsPanel.php | 8 +-- ...torConpherencePreferencesSettingsPanel.php | 4 +- .../PhabricatorDateTimeSettingsPanel.php | 4 +- ...catorDesktopNotificationsSettingsPanel.php | 4 +- ...catorDeveloperPreferencesSettingsPanel.php | 4 +- ...habricatorDiffPreferencesSettingsPanel.php | 4 +- ...ricatorDisplayPreferencesSettingsPanel.php | 4 +- ...PhabricatorEmailAddressesSettingsPanel.php | 4 +- .../PhabricatorEmailFormatSettingsPanel.php | 4 +- ...abricatorEmailPreferencesSettingsPanel.php | 4 +- ...abricatorExternalAccountsSettingsPanel.php | 8 +-- ...habricatorHomePreferencesSettingsPanel.php | 4 +- .../PhabricatorMultiFactorSettingsPanel.php | 4 +- .../PhabricatorPasswordSettingsPanel.php | 4 +- .../panel/PhabricatorSSHKeysSettingsPanel.php | 4 +- .../PhabricatorSessionsSettingsPanel.php | 4 +- .../panel/PhabricatorSettingsPanel.php | 52 +++++++++++++------ .../panel/PhabricatorTokensSettingsPanel.php | 8 +-- .../PhabricatorSettingsAccountPanelGroup.php | 16 ++++++ ...bricatorSettingsApplicationsPanelGroup.php | 16 ++++++ ...icatorSettingsAuthenticationPanelGroup.php | 16 ++++++ ...PhabricatorSettingsDeveloperPanelGroup.php | 16 ++++++ .../PhabricatorSettingsEmailPanelGroup.php | 16 ++++++ .../PhabricatorSettingsLogsPanelGroup.php | 16 ++++++ .../PhabricatorSettingsPanelGroup.php | 48 +++++++++++++++++ 31 files changed, 240 insertions(+), 75 deletions(-) create mode 100644 src/applications/settings/panelgroup/PhabricatorSettingsAccountPanelGroup.php create mode 100644 src/applications/settings/panelgroup/PhabricatorSettingsApplicationsPanelGroup.php create mode 100644 src/applications/settings/panelgroup/PhabricatorSettingsAuthenticationPanelGroup.php create mode 100644 src/applications/settings/panelgroup/PhabricatorSettingsDeveloperPanelGroup.php create mode 100644 src/applications/settings/panelgroup/PhabricatorSettingsEmailPanelGroup.php create mode 100644 src/applications/settings/panelgroup/PhabricatorSettingsLogsPanelGroup.php create mode 100644 src/applications/settings/panelgroup/PhabricatorSettingsPanelGroup.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 2180dbb387..593c9b29cf 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -3371,14 +3371,21 @@ phutil_register_library_map(array( 'PhabricatorSendGridConfigOptions' => 'applications/config/option/PhabricatorSendGridConfigOptions.php', 'PhabricatorSessionsSettingsPanel' => 'applications/settings/panel/PhabricatorSessionsSettingsPanel.php', 'PhabricatorSetting' => 'applications/settings/setting/PhabricatorSetting.php', + 'PhabricatorSettingsAccountPanelGroup' => 'applications/settings/panelgroup/PhabricatorSettingsAccountPanelGroup.php', 'PhabricatorSettingsAddEmailAction' => 'applications/settings/action/PhabricatorSettingsAddEmailAction.php', 'PhabricatorSettingsAdjustController' => 'applications/settings/controller/PhabricatorSettingsAdjustController.php', 'PhabricatorSettingsApplication' => 'applications/settings/application/PhabricatorSettingsApplication.php', + 'PhabricatorSettingsApplicationsPanelGroup' => 'applications/settings/panelgroup/PhabricatorSettingsApplicationsPanelGroup.php', + 'PhabricatorSettingsAuthenticationPanelGroup' => 'applications/settings/panelgroup/PhabricatorSettingsAuthenticationPanelGroup.php', + 'PhabricatorSettingsDeveloperPanelGroup' => 'applications/settings/panelgroup/PhabricatorSettingsDeveloperPanelGroup.php', 'PhabricatorSettingsEditController' => 'applications/settings/controller/PhabricatorSettingsEditController.php', 'PhabricatorSettingsEditEngine' => 'applications/settings/editor/PhabricatorSettingsEditEngine.php', + 'PhabricatorSettingsEmailPanelGroup' => 'applications/settings/panelgroup/PhabricatorSettingsEmailPanelGroup.php', + 'PhabricatorSettingsLogsPanelGroup' => 'applications/settings/panelgroup/PhabricatorSettingsLogsPanelGroup.php', 'PhabricatorSettingsMainController' => 'applications/settings/controller/PhabricatorSettingsMainController.php', 'PhabricatorSettingsMainMenuBarExtension' => 'applications/settings/extension/PhabricatorSettingsMainMenuBarExtension.php', 'PhabricatorSettingsPanel' => 'applications/settings/panel/PhabricatorSettingsPanel.php', + 'PhabricatorSettingsPanelGroup' => 'applications/settings/panelgroup/PhabricatorSettingsPanelGroup.php', 'PhabricatorSettingsTimezoneController' => 'applications/settings/controller/PhabricatorSettingsTimezoneController.php', 'PhabricatorSetupCheck' => 'applications/config/check/PhabricatorSetupCheck.php', 'PhabricatorSetupCheckTestCase' => 'applications/config/check/__tests__/PhabricatorSetupCheckTestCase.php', @@ -8125,14 +8132,21 @@ phutil_register_library_map(array( 'PhabricatorSendGridConfigOptions' => 'PhabricatorApplicationConfigOptions', 'PhabricatorSessionsSettingsPanel' => 'PhabricatorSettingsPanel', 'PhabricatorSetting' => 'Phobject', + 'PhabricatorSettingsAccountPanelGroup' => 'PhabricatorSettingsPanelGroup', 'PhabricatorSettingsAddEmailAction' => 'PhabricatorSystemAction', 'PhabricatorSettingsAdjustController' => 'PhabricatorController', 'PhabricatorSettingsApplication' => 'PhabricatorApplication', + 'PhabricatorSettingsApplicationsPanelGroup' => 'PhabricatorSettingsPanelGroup', + 'PhabricatorSettingsAuthenticationPanelGroup' => 'PhabricatorSettingsPanelGroup', + 'PhabricatorSettingsDeveloperPanelGroup' => 'PhabricatorSettingsPanelGroup', 'PhabricatorSettingsEditController' => 'PhabricatorController', 'PhabricatorSettingsEditEngine' => 'PhabricatorEditEngine', + 'PhabricatorSettingsEmailPanelGroup' => 'PhabricatorSettingsPanelGroup', + 'PhabricatorSettingsLogsPanelGroup' => 'PhabricatorSettingsPanelGroup', 'PhabricatorSettingsMainController' => 'PhabricatorController', 'PhabricatorSettingsMainMenuBarExtension' => 'PhabricatorMainMenuBarExtension', 'PhabricatorSettingsPanel' => 'Phobject', + 'PhabricatorSettingsPanelGroup' => 'Phobject', 'PhabricatorSettingsTimezoneController' => 'PhabricatorController', 'PhabricatorSetupCheck' => 'Phobject', 'PhabricatorSetupCheckTestCase' => 'PhabricatorTestCase', diff --git a/src/applications/conduit/settings/PhabricatorConduitTokensSettingsPanel.php b/src/applications/conduit/settings/PhabricatorConduitTokensSettingsPanel.php index 6f315783cc..80e92d45c9 100644 --- a/src/applications/conduit/settings/PhabricatorConduitTokensSettingsPanel.php +++ b/src/applications/conduit/settings/PhabricatorConduitTokensSettingsPanel.php @@ -15,8 +15,8 @@ final class PhabricatorConduitTokensSettingsPanel return pht('Conduit API Tokens'); } - public function getPanelGroup() { - return pht('Sessions and Logs'); + public function getPanelGroupKey() { + return PhabricatorSettingsLogsPanelGroup::PANELGROUPKEY; } public function isEnabled() { diff --git a/src/applications/diffusion/panel/DiffusionSetPasswordSettingsPanel.php b/src/applications/diffusion/panel/DiffusionSetPasswordSettingsPanel.php index 454ed01570..aa1ffb2e43 100644 --- a/src/applications/diffusion/panel/DiffusionSetPasswordSettingsPanel.php +++ b/src/applications/diffusion/panel/DiffusionSetPasswordSettingsPanel.php @@ -14,8 +14,8 @@ final class DiffusionSetPasswordSettingsPanel extends PhabricatorSettingsPanel { return pht('VCS Password'); } - public function getPanelGroup() { - return pht('Authentication'); + public function getPanelGroupKey() { + return PhabricatorSettingsAuthenticationPanelGroup::PANELGROUPKEY; } public function isEnabled() { diff --git a/src/applications/oauthserver/panel/PhabricatorOAuthServerAuthorizationsSettingsPanel.php b/src/applications/oauthserver/panel/PhabricatorOAuthServerAuthorizationsSettingsPanel.php index e282a3d5e4..06f26b2ee3 100644 --- a/src/applications/oauthserver/panel/PhabricatorOAuthServerAuthorizationsSettingsPanel.php +++ b/src/applications/oauthserver/panel/PhabricatorOAuthServerAuthorizationsSettingsPanel.php @@ -11,8 +11,8 @@ final class PhabricatorOAuthServerAuthorizationsSettingsPanel return pht('OAuth Authorizations'); } - public function getPanelGroup() { - return pht('Sessions and Logs'); + public function getPanelGroupKey() { + return PhabricatorSettingsLogsPanelGroup::PANELGROUPKEY; } public function isEnabled() { diff --git a/src/applications/settings/controller/PhabricatorSettingsMainController.php b/src/applications/settings/controller/PhabricatorSettingsMainController.php index b23fd5f73f..a174fa83e8 100644 --- a/src/applications/settings/controller/PhabricatorSettingsMainController.php +++ b/src/applications/settings/controller/PhabricatorSettingsMainController.php @@ -80,7 +80,7 @@ final class PhabricatorSettingsMainController } private function buildPanels() { - $panels = PhabricatorSettingsPanel::getAllPanels(); + $panels = PhabricatorSettingsPanel::getAllDisplayPanels(); $result = array(); foreach ($panels as $key => $panel) { @@ -125,11 +125,12 @@ final class PhabricatorSettingsMainController $nav->setBaseURI(new PhutilURI($this->getApplicationURI($base_uri))); - $group = null; + $group_key = null; foreach ($panels as $panel) { - if ($panel->getPanelGroup() != $group) { + if ($panel->getPanelGroupKey() != $group_key) { + $group_key = $panel->getPanelGroupKey(); $group = $panel->getPanelGroup(); - $nav->addLabel($group); + $nav->addLabel($group->getPanelGroupName()); } $nav->addFilter($panel->getPanelKey(), $panel->getPanelName()); diff --git a/src/applications/settings/panel/PhabricatorAccountSettingsPanel.php b/src/applications/settings/panel/PhabricatorAccountSettingsPanel.php index f06f41ca4f..f1734f1432 100644 --- a/src/applications/settings/panel/PhabricatorAccountSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorAccountSettingsPanel.php @@ -9,8 +9,8 @@ final class PhabricatorAccountSettingsPanel return pht('Account'); } - public function getPanelGroup() { - return pht('Account Information'); + public function getPanelGroupKey() { + return PhabricatorSettingsAccountPanelGroup::PANELGROUPKEY; } public function isEditableByAdministrators() { diff --git a/src/applications/settings/panel/PhabricatorActivitySettingsPanel.php b/src/applications/settings/panel/PhabricatorActivitySettingsPanel.php index 757ac9c332..23c212bf30 100644 --- a/src/applications/settings/panel/PhabricatorActivitySettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorActivitySettingsPanel.php @@ -14,12 +14,8 @@ final class PhabricatorActivitySettingsPanel extends PhabricatorSettingsPanel { return pht('Activity Logs'); } - public function getPanelGroup() { - return pht('Sessions and Logs'); - } - - public function isEnabled() { - return true; + public function getPanelGroupKey() { + return PhabricatorSettingsLogsPanelGroup::PANELGROUPKEY; } public function processRequest(AphrontRequest $request) { diff --git a/src/applications/settings/panel/PhabricatorConpherencePreferencesSettingsPanel.php b/src/applications/settings/panel/PhabricatorConpherencePreferencesSettingsPanel.php index 69f84f16a1..87a837c6fb 100644 --- a/src/applications/settings/panel/PhabricatorConpherencePreferencesSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorConpherencePreferencesSettingsPanel.php @@ -16,8 +16,8 @@ final class PhabricatorConpherencePreferencesSettingsPanel return pht('Conpherence Preferences'); } - public function getPanelGroup() { - return pht('Application Settings'); + public function getPanelGroupKey() { + return PhabricatorSettingsApplicationsPanelGroup::PANELGROUPKEY; } public function processRequest(AphrontRequest $request) { diff --git a/src/applications/settings/panel/PhabricatorDateTimeSettingsPanel.php b/src/applications/settings/panel/PhabricatorDateTimeSettingsPanel.php index 9d9b015fbf..7d554b579c 100644 --- a/src/applications/settings/panel/PhabricatorDateTimeSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorDateTimeSettingsPanel.php @@ -9,8 +9,8 @@ final class PhabricatorDateTimeSettingsPanel return pht('Date and Time'); } - public function getPanelGroup() { - return pht('Account Information'); + public function getPanelGroupKey() { + return PhabricatorSettingsAccountPanelGroup::PANELGROUPKEY; } public function isEditableByAdministrators() { diff --git a/src/applications/settings/panel/PhabricatorDesktopNotificationsSettingsPanel.php b/src/applications/settings/panel/PhabricatorDesktopNotificationsSettingsPanel.php index 8da45a518c..65beabec6a 100644 --- a/src/applications/settings/panel/PhabricatorDesktopNotificationsSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorDesktopNotificationsSettingsPanel.php @@ -21,8 +21,8 @@ final class PhabricatorDesktopNotificationsSettingsPanel return pht('Desktop Notifications'); } - public function getPanelGroup() { - return pht('Application Settings'); + public function getPanelGroupKey() { + return PhabricatorSettingsApplicationsPanelGroup::PANELGROUPKEY; } public function processRequest(AphrontRequest $request) { diff --git a/src/applications/settings/panel/PhabricatorDeveloperPreferencesSettingsPanel.php b/src/applications/settings/panel/PhabricatorDeveloperPreferencesSettingsPanel.php index 1d81cc67b2..7892c59a55 100644 --- a/src/applications/settings/panel/PhabricatorDeveloperPreferencesSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorDeveloperPreferencesSettingsPanel.php @@ -11,8 +11,8 @@ final class PhabricatorDeveloperPreferencesSettingsPanel return pht('Developer Settings'); } - public function getPanelGroup() { - return pht('Developer'); + public function getPanelGroupKey() { + return PhabricatorSettingsDeveloperPanelGroup::PANELGROUPKEY; } public function processRequest(AphrontRequest $request) { diff --git a/src/applications/settings/panel/PhabricatorDiffPreferencesSettingsPanel.php b/src/applications/settings/panel/PhabricatorDiffPreferencesSettingsPanel.php index 72fca5e879..72a1dcec25 100644 --- a/src/applications/settings/panel/PhabricatorDiffPreferencesSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorDiffPreferencesSettingsPanel.php @@ -11,8 +11,8 @@ final class PhabricatorDiffPreferencesSettingsPanel return pht('Diff Preferences'); } - public function getPanelGroup() { - return pht('Application Settings'); + public function getPanelGroupKey() { + return PhabricatorSettingsApplicationsPanelGroup::PANELGROUPKEY; } public function processRequest(AphrontRequest $request) { diff --git a/src/applications/settings/panel/PhabricatorDisplayPreferencesSettingsPanel.php b/src/applications/settings/panel/PhabricatorDisplayPreferencesSettingsPanel.php index 6895aaedc7..ce97caf982 100644 --- a/src/applications/settings/panel/PhabricatorDisplayPreferencesSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorDisplayPreferencesSettingsPanel.php @@ -11,8 +11,8 @@ final class PhabricatorDisplayPreferencesSettingsPanel return pht('Display Preferences'); } - public function getPanelGroup() { - return pht('Application Settings'); + public function getPanelGroupKey() { + return PhabricatorSettingsApplicationsPanelGroup::PANELGROUPKEY; } public function processRequest(AphrontRequest $request) { diff --git a/src/applications/settings/panel/PhabricatorEmailAddressesSettingsPanel.php b/src/applications/settings/panel/PhabricatorEmailAddressesSettingsPanel.php index 464214dc73..ac02fae00e 100644 --- a/src/applications/settings/panel/PhabricatorEmailAddressesSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorEmailAddressesSettingsPanel.php @@ -11,8 +11,8 @@ final class PhabricatorEmailAddressesSettingsPanel return pht('Email Addresses'); } - public function getPanelGroup() { - return pht('Email'); + public function getPanelGroupKey() { + return PhabricatorSettingsEmailPanelGroup::PANELGROUPKEY; } public function isEditableByAdministrators() { diff --git a/src/applications/settings/panel/PhabricatorEmailFormatSettingsPanel.php b/src/applications/settings/panel/PhabricatorEmailFormatSettingsPanel.php index 3396774065..677ccfd647 100644 --- a/src/applications/settings/panel/PhabricatorEmailFormatSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorEmailFormatSettingsPanel.php @@ -11,8 +11,8 @@ final class PhabricatorEmailFormatSettingsPanel return pht('Email Format'); } - public function getPanelGroup() { - return pht('Email'); + public function getPanelGroupKey() { + return PhabricatorSettingsEmailPanelGroup::PANELGROUPKEY; } public function isEditableByAdministrators() { diff --git a/src/applications/settings/panel/PhabricatorEmailPreferencesSettingsPanel.php b/src/applications/settings/panel/PhabricatorEmailPreferencesSettingsPanel.php index c4142412c6..162ed9ceac 100644 --- a/src/applications/settings/panel/PhabricatorEmailPreferencesSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorEmailPreferencesSettingsPanel.php @@ -11,8 +11,8 @@ final class PhabricatorEmailPreferencesSettingsPanel return pht('Email Preferences'); } - public function getPanelGroup() { - return pht('Email'); + public function getPanelGroupKey() { + return PhabricatorSettingsEmailPanelGroup::PANELGROUPKEY; } public function isEditableByAdministrators() { diff --git a/src/applications/settings/panel/PhabricatorExternalAccountsSettingsPanel.php b/src/applications/settings/panel/PhabricatorExternalAccountsSettingsPanel.php index c9b6269a87..662d4e6cf6 100644 --- a/src/applications/settings/panel/PhabricatorExternalAccountsSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorExternalAccountsSettingsPanel.php @@ -11,12 +11,8 @@ final class PhabricatorExternalAccountsSettingsPanel return pht('External Accounts'); } - public function getPanelGroup() { - return pht('Authentication'); - } - - public function isEnabled() { - return true; + public function getPanelGroupKey() { + return PhabricatorSettingsAuthenticationPanelGroup::PANELGROUPKEY; } public function processRequest(AphrontRequest $request) { diff --git a/src/applications/settings/panel/PhabricatorHomePreferencesSettingsPanel.php b/src/applications/settings/panel/PhabricatorHomePreferencesSettingsPanel.php index 06b17fecca..a4c75f88ea 100644 --- a/src/applications/settings/panel/PhabricatorHomePreferencesSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorHomePreferencesSettingsPanel.php @@ -11,8 +11,8 @@ final class PhabricatorHomePreferencesSettingsPanel return pht('Home Page'); } - public function getPanelGroup() { - return pht('Application Settings'); + public function getPanelGroupKey() { + return PhabricatorSettingsApplicationsPanelGroup::PANELGROUPKEY; } public function processRequest(AphrontRequest $request) { diff --git a/src/applications/settings/panel/PhabricatorMultiFactorSettingsPanel.php b/src/applications/settings/panel/PhabricatorMultiFactorSettingsPanel.php index 9bcfbba3be..77eb1c55d5 100644 --- a/src/applications/settings/panel/PhabricatorMultiFactorSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorMultiFactorSettingsPanel.php @@ -11,8 +11,8 @@ final class PhabricatorMultiFactorSettingsPanel return pht('Multi-Factor Auth'); } - public function getPanelGroup() { - return pht('Authentication'); + public function getPanelGroupKey() { + return PhabricatorSettingsAuthenticationPanelGroup::PANELGROUPKEY; } public function processRequest(AphrontRequest $request) { diff --git a/src/applications/settings/panel/PhabricatorPasswordSettingsPanel.php b/src/applications/settings/panel/PhabricatorPasswordSettingsPanel.php index f2db373945..fa91dac210 100644 --- a/src/applications/settings/panel/PhabricatorPasswordSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorPasswordSettingsPanel.php @@ -10,8 +10,8 @@ final class PhabricatorPasswordSettingsPanel extends PhabricatorSettingsPanel { return pht('Password'); } - public function getPanelGroup() { - return pht('Authentication'); + public function getPanelGroupKey() { + return PhabricatorSettingsAuthenticationPanelGroup::PANELGROUPKEY; } public function isEnabled() { diff --git a/src/applications/settings/panel/PhabricatorSSHKeysSettingsPanel.php b/src/applications/settings/panel/PhabricatorSSHKeysSettingsPanel.php index 4c716e1dff..f91b17557a 100644 --- a/src/applications/settings/panel/PhabricatorSSHKeysSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorSSHKeysSettingsPanel.php @@ -14,8 +14,8 @@ final class PhabricatorSSHKeysSettingsPanel extends PhabricatorSettingsPanel { return pht('SSH Public Keys'); } - public function getPanelGroup() { - return pht('Authentication'); + public function getPanelGroupKey() { + return PhabricatorSettingsAuthenticationPanelGroup::PANELGROUPKEY; } public function isEnabled() { diff --git a/src/applications/settings/panel/PhabricatorSessionsSettingsPanel.php b/src/applications/settings/panel/PhabricatorSessionsSettingsPanel.php index 2d38e76225..e3a37c9dfc 100644 --- a/src/applications/settings/panel/PhabricatorSessionsSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorSessionsSettingsPanel.php @@ -10,8 +10,8 @@ final class PhabricatorSessionsSettingsPanel extends PhabricatorSettingsPanel { return pht('Sessions'); } - public function getPanelGroup() { - return pht('Sessions and Logs'); + public function getPanelGroupKey() { + return PhabricatorSettingsLogsPanelGroup::PANELGROUPKEY; } public function isEnabled() { diff --git a/src/applications/settings/panel/PhabricatorSettingsPanel.php b/src/applications/settings/panel/PhabricatorSettingsPanel.php index 1f05147daa..27679e733c 100644 --- a/src/applications/settings/panel/PhabricatorSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorSettingsPanel.php @@ -7,8 +7,6 @@ * submits the form. By extending this class, you can add new settings * panels. * - * NOTE: This stuff is new and might not be completely stable. - * * @task config Panel Configuration * @task panel Panel Implementation * @task internal Internals @@ -63,11 +61,38 @@ abstract class PhabricatorSettingsPanel extends Phobject { } final public static function getAllPanels() { - return id(new PhutilClassMapQuery()) + $panels = id(new PhutilClassMapQuery()) ->setAncestorClass(__CLASS__) ->setUniqueMethod('getPanelKey') - ->setSortMethod('getPanelSortKey') ->execute(); + return msortv($panels, 'getPanelOrderVector'); + } + + final public static function getAllDisplayPanels() { + $panels = array(); + $groups = PhabricatorSettingsPanelGroup::getAllPanelGroupsWithPanels(); + foreach ($groups as $group) { + foreach ($group->getPanels() as $key => $panel) { + $panels[$key] = $panel; + } + } + + return $panels; + } + + final public function getPanelGroup() { + $group_key = $this->getPanelGroupKey(); + + $groups = PhabricatorSettingsPanelGroup::getAllPanelGroupsWithPanels(); + $group = idx($groups, $group_key); + if (!$group) { + throw new Exception( + pht( + 'No settings panel group with key "%s" exists!', + $group_key)); + } + + return $group; } @@ -97,17 +122,12 @@ abstract class PhabricatorSettingsPanel extends Phobject { /** - * Return a human-readable group name for this panel. For instance, if you - * had several related panels like "Volume Settings" and - * "Microphone Settings", you might put them in a group called "Audio". + * Return a panel group key constant for this panel. * - * When displayed, panels are grouped with other panels that have the same - * group name. - * - * @return string Human-readable panel group name. + * @return const Panel group key. * @task config */ - abstract public function getPanelGroup(); + abstract public function getPanelGroupKey(); /** @@ -192,11 +212,9 @@ abstract class PhabricatorSettingsPanel extends Phobject { * @return string Sortable key. * @task internal */ - final public function getPanelSortKey() { - return sprintf( - '%s'.chr(255).'%s', - $this->getPanelGroup(), - $this->getPanelName()); + final public function getPanelOrderVector() { + return id(new PhutilSortVector()) + ->addString($this->getPanelName()); } } diff --git a/src/applications/settings/panel/PhabricatorTokensSettingsPanel.php b/src/applications/settings/panel/PhabricatorTokensSettingsPanel.php index 030ad37116..d2e0be4a53 100644 --- a/src/applications/settings/panel/PhabricatorTokensSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorTokensSettingsPanel.php @@ -10,12 +10,8 @@ final class PhabricatorTokensSettingsPanel extends PhabricatorSettingsPanel { return pht('Temporary Tokens'); } - public function getPanelGroup() { - return pht('Sessions and Logs'); - } - - public function isEnabled() { - return true; + public function getPanelGroupKey() { + return PhabricatorSettingsLogsPanelGroup::PANELGROUPKEY; } public function processRequest(AphrontRequest $request) { diff --git a/src/applications/settings/panelgroup/PhabricatorSettingsAccountPanelGroup.php b/src/applications/settings/panelgroup/PhabricatorSettingsAccountPanelGroup.php new file mode 100644 index 0000000000..f81d5d487a --- /dev/null +++ b/src/applications/settings/panelgroup/PhabricatorSettingsAccountPanelGroup.php @@ -0,0 +1,16 @@ +addInt($this->getPanelGroupOrder()) + ->addString($this->getPanelGroupName()); + } + + final public function getPanelGroupKey() { + return $this->getPhobjectClassConstant('PANELGROUPKEY'); + } + + final public static function getAllPanelGroups() { + $groups = id(new PhutilClassMapQuery()) + ->setAncestorClass(__CLASS__) + ->setUniqueMethod('getPanelGroupKey') + ->execute(); + + return msortv($groups, 'getPanelGroupOrderVector'); + } + + final public static function getAllPanelGroupsWithPanels() { + $groups = self::getAllPanelGroups(); + + $panels = PhabricatorSettingsPanel::getAllPanels(); + $panels = mgroup($panels, 'getPanelGroupKey'); + foreach ($groups as $key => $group) { + $group->panels = idx($panels, $key, array()); + } + + return $groups; + } + + public function getPanels() { + return $this->panels; + } + +} From d5f924b3fabbc311f765fde9fa5382d461d9c15c Mon Sep 17 00:00:00 2001 From: epriestley Date: Fri, 3 Jun 2016 06:04:40 -0700 Subject: [PATCH 07/67] Fix reading ad-hoc settings with no formal Settings object Summary: Ref T4103. Some settings (like the collapsed/expanded state of the diff filetree) are currently ad-hoc. They weren't being read correctly. Also, simplify the caching code a little bit. Test Plan: Toggled filetree, reloaded page, got sticky behavior. Reviewers: chad Reviewed By: chad Maniphest Tasks: T4103 Differential Revision: https://secure.phabricator.com/D16021 --- .../people/storage/PhabricatorUser.php | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/applications/people/storage/PhabricatorUser.php b/src/applications/people/storage/PhabricatorUser.php index 2ba5d2b2cd..0d450c5a88 100644 --- a/src/applications/people/storage/PhabricatorUser.php +++ b/src/applications/people/storage/PhabricatorUser.php @@ -505,13 +505,13 @@ final class PhabricatorUser ->setViewer($this) ->assertValidValue($value); - $this->settingCacheKeys[$key] = true; - $this->settingCache[$key] = $value; - - return $value; + return $this->writeUserSettingCache($key, $value); } catch (Exception $ex) { // Fall through below and return the default value. } + } else { + // This is an ad-hoc setting with no controlling object. + return $this->writeUserSettingCache($key, $value); } } @@ -523,10 +523,7 @@ final class PhabricatorUser $value = null; } - $this->settingCacheKeys[$key] = true; - $this->settingCache[$key] = $value; - - return $value; + return $this->writeUserSettingCache($key, $value); } @@ -557,6 +554,12 @@ final class PhabricatorUser return $this; } + private function writeUserSettingCache($key, $value) { + $this->settingCacheKeys[$key] = true; + $this->settingCache[$key] = $value; + return $value; + } + public function getTranslation() { return $this->getUserSetting(PhabricatorTranslationSetting::SETTINGKEY); } From ef28adae9ac26fadfba93953dfbf0ab851ab31a1 Mon Sep 17 00:00:00 2001 From: epriestley Date: Fri, 3 Jun 2016 06:12:10 -0700 Subject: [PATCH 08/67] Use EditEngine for Conpherence preferences Summary: Ref T4103. Only trick here is hiding the panel if Conpherence is not installed. Test Plan: - Edited Conpherence preferences. - Uninstalled Conpherence, saw panel vanish. Reviewers: chad Reviewed By: chad Maniphest Tasks: T4103 Differential Revision: https://secure.phabricator.com/D16022 --- src/__phutil_library_map__.php | 2 +- .../PhabricatorSettingsMainController.php | 5 +- ...torConpherencePreferencesSettingsPanel.php | 58 +------------------ .../PhabricatorEditEngineSettingsPanel.php | 6 ++ ...ricatorConpherenceNotificationsSetting.php | 10 ++++ 5 files changed, 23 insertions(+), 58 deletions(-) diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 593c9b29cf..398c85fea6 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -6689,7 +6689,7 @@ phutil_register_library_map(array( 'PhabricatorConfigWelcomeController' => 'PhabricatorConfigController', 'PhabricatorConpherenceApplication' => 'PhabricatorApplication', 'PhabricatorConpherenceNotificationsSetting' => 'PhabricatorSelectSetting', - 'PhabricatorConpherencePreferencesSettingsPanel' => 'PhabricatorSettingsPanel', + 'PhabricatorConpherencePreferencesSettingsPanel' => 'PhabricatorEditEngineSettingsPanel', 'PhabricatorConpherenceThreadPHIDType' => 'PhabricatorPHIDType', 'PhabricatorConsoleApplication' => 'PhabricatorApplication', 'PhabricatorConsoleContentSource' => 'PhabricatorContentSource', diff --git a/src/applications/settings/controller/PhabricatorSettingsMainController.php b/src/applications/settings/controller/PhabricatorSettingsMainController.php index a174fa83e8..e16a9b2e1d 100644 --- a/src/applications/settings/controller/PhabricatorSettingsMainController.php +++ b/src/applications/settings/controller/PhabricatorSettingsMainController.php @@ -80,11 +80,14 @@ final class PhabricatorSettingsMainController } private function buildPanels() { + $viewer = $this->getViewer(); $panels = PhabricatorSettingsPanel::getAllDisplayPanels(); $result = array(); foreach ($panels as $key => $panel) { - $panel->setUser($this->user); + $panel + ->setViewer($viewer) + ->setUser($this->user); if (!$panel->isEnabled()) { continue; diff --git a/src/applications/settings/panel/PhabricatorConpherencePreferencesSettingsPanel.php b/src/applications/settings/panel/PhabricatorConpherencePreferencesSettingsPanel.php index 87a837c6fb..68c1e21dfe 100644 --- a/src/applications/settings/panel/PhabricatorConpherencePreferencesSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorConpherencePreferencesSettingsPanel.php @@ -1,16 +1,9 @@ getUser(); - $preferences = $user->loadPreferences(); - - $pref = PhabricatorUserPreferences::PREFERENCE_CONPH_NOTIFICATIONS; - - if ($request->isFormPost()) { - $notifications = $request->getInt($pref); - $preferences->setPreference($pref, $notifications); - $preferences->save(); - return id(new AphrontRedirectResponse()) - ->setURI($this->getPanelURI('?saved=true')); - } - - $form = id(new AphrontFormView()) - ->setUser($user) - ->appendChild( - id(new AphrontFormSelectControl()) - ->setLabel(pht('Conpherence Notifications')) - ->setName($pref) - ->setValue($preferences->getPreference($pref)) - ->setOptions( - array( - ConpherenceSettings::EMAIL_ALWAYS - => pht('Email Always'), - ConpherenceSettings::NOTIFICATIONS_ONLY - => pht('Notifications Only'), - )) - ->setCaption( - pht( - 'Should Conpherence send emails for updates or '. - 'notifications only? This global setting can be overridden '. - 'on a per-thread basis within Conpherence.'))) - ->appendChild( - id(new AphrontFormSubmitControl()) - ->setValue(pht('Save Preferences'))); - - $form_box = id(new PHUIObjectBoxView()) - ->setHeaderText(pht('Conpherence Preferences')) - ->setForm($form) - ->setFormSaved($request->getBool('saved')); - - return array( - $form_box, - ); - } - } diff --git a/src/applications/settings/panel/PhabricatorEditEngineSettingsPanel.php b/src/applications/settings/panel/PhabricatorEditEngineSettingsPanel.php index 7e80b3ae40..658e9829fb 100644 --- a/src/applications/settings/panel/PhabricatorEditEngineSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorEditEngineSettingsPanel.php @@ -38,6 +38,12 @@ abstract class PhabricatorEditEngineSettingsPanel return $engine->buildResponse(); } + final public function isEnabled() { + // Only enable the panel if it has any fields. + $field_keys = $this->getPanelSettingsKeys(); + return (bool)$field_keys; + } + final public function newEditEnginePage() { $field_keys = $this->getPanelSettingsKeys(); if (!$field_keys) { diff --git a/src/applications/settings/setting/PhabricatorConpherenceNotificationsSetting.php b/src/applications/settings/setting/PhabricatorConpherenceNotificationsSetting.php index 7009fbcdc1..0472112313 100644 --- a/src/applications/settings/setting/PhabricatorConpherenceNotificationsSetting.php +++ b/src/applications/settings/setting/PhabricatorConpherenceNotificationsSetting.php @@ -12,11 +12,21 @@ final class PhabricatorConpherenceNotificationsSetting return pht('Conpherence Notifications'); } + public function getSettingPanelKey() { + return PhabricatorConpherencePreferencesSettingsPanel::PANELKEY; + } + protected function getControlInstructions() { return pht( 'Choose the default notification behavior for Conpherence rooms.'); } + protected function isEnabledForViewer(PhabricatorUser $viewer) { + return PhabricatorApplication::isClassInstalledForViewer( + 'PhabricatorConpherenceApplication', + $viewer); + } + public function getSettingDefaultValue() { return self::VALUE_CONPHERENCE_EMAIL; } From 45f347fe4777b95403a00e88040d5fae2731449a Mon Sep 17 00:00:00 2001 From: epriestley Date: Fri, 3 Jun 2016 06:35:08 -0700 Subject: [PATCH 09/67] Use EditEngine for diff, display, developer and most email settings Summary: Ref T4103. This converts other straightforward panels to modern stuff. Test Plan: - Edited various settings. - Tried to set a bogus editor value. Reviewers: chad Reviewed By: chad Maniphest Tasks: T4103 Differential Revision: https://secure.phabricator.com/D16023 --- src/__phutil_library_map__.php | 8 +- ...catorDeveloperPreferencesSettingsPanel.php | 87 +------ ...habricatorDiffPreferencesSettingsPanel.php | 94 +------- ...ricatorDisplayPreferencesSettingsPanel.php | 188 +-------------- .../PhabricatorEmailFormatSettingsPanel.php | 216 +----------------- .../PhabricatorAccessibilitySetting.php | 10 +- .../setting/PhabricatorDarkConsoleSetting.php | 12 + .../PhabricatorEditorMultipleSetting.php | 8 + .../setting/PhabricatorEditorSetting.php | 31 +++ .../setting/PhabricatorEmailFormatSetting.php | 12 + .../PhabricatorEmailRePrefixSetting.php | 12 + .../PhabricatorEmailVarySubjectsSetting.php | 12 + .../PhabricatorMonospacedFontSetting.php | 8 + .../PhabricatorMonospacedTextareasSetting.php | 8 + .../PhabricatorOlderInlinesSetting.php | 8 + .../PhabricatorShowFiletreeSetting.php | 8 + .../setting/PhabricatorTitleGlyphsSetting.php | 8 + .../PhabricatorUnifiedDiffsSetting.php | 8 + 18 files changed, 156 insertions(+), 582 deletions(-) diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 398c85fea6..524d5a7647 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -6880,9 +6880,9 @@ phutil_register_library_map(array( 'PhabricatorDestructionEngineExtension' => 'Phobject', 'PhabricatorDestructionEngineExtensionModule' => 'PhabricatorConfigModule', 'PhabricatorDeveloperConfigOptions' => 'PhabricatorApplicationConfigOptions', - 'PhabricatorDeveloperPreferencesSettingsPanel' => 'PhabricatorSettingsPanel', + 'PhabricatorDeveloperPreferencesSettingsPanel' => 'PhabricatorEditEngineSettingsPanel', 'PhabricatorDiffInlineCommentQuery' => 'PhabricatorApplicationTransactionCommentQuery', - 'PhabricatorDiffPreferencesSettingsPanel' => 'PhabricatorSettingsPanel', + 'PhabricatorDiffPreferencesSettingsPanel' => 'PhabricatorEditEngineSettingsPanel', 'PhabricatorDifferenceEngine' => 'Phobject', 'PhabricatorDifferentialApplication' => 'PhabricatorApplication', 'PhabricatorDifferentialAttachCommitWorkflow' => 'PhabricatorDifferentialManagementWorkflow', @@ -6893,7 +6893,7 @@ phutil_register_library_map(array( 'PhabricatorDiffusionApplication' => 'PhabricatorApplication', 'PhabricatorDiffusionConfigOptions' => 'PhabricatorApplicationConfigOptions', 'PhabricatorDisabledUserController' => 'PhabricatorAuthController', - 'PhabricatorDisplayPreferencesSettingsPanel' => 'PhabricatorSettingsPanel', + 'PhabricatorDisplayPreferencesSettingsPanel' => 'PhabricatorEditEngineSettingsPanel', 'PhabricatorDisqusAuthProvider' => 'PhabricatorOAuth2AuthProvider', 'PhabricatorDividerProfilePanel' => 'PhabricatorProfilePanel', 'PhabricatorDivinerApplication' => 'PhabricatorApplication', @@ -6963,7 +6963,7 @@ phutil_register_library_map(array( 'PhabricatorEmailAddressesSettingsPanel' => 'PhabricatorSettingsPanel', 'PhabricatorEmailContentSource' => 'PhabricatorContentSource', 'PhabricatorEmailFormatSetting' => 'PhabricatorSelectSetting', - 'PhabricatorEmailFormatSettingsPanel' => 'PhabricatorSettingsPanel', + 'PhabricatorEmailFormatSettingsPanel' => 'PhabricatorEditEngineSettingsPanel', 'PhabricatorEmailLoginController' => 'PhabricatorAuthController', 'PhabricatorEmailNotificationsSetting' => 'PhabricatorSelectSetting', 'PhabricatorEmailPreferencesSettingsPanel' => 'PhabricatorSettingsPanel', diff --git a/src/applications/settings/panel/PhabricatorDeveloperPreferencesSettingsPanel.php b/src/applications/settings/panel/PhabricatorDeveloperPreferencesSettingsPanel.php index 7892c59a55..9f49f4927b 100644 --- a/src/applications/settings/panel/PhabricatorDeveloperPreferencesSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorDeveloperPreferencesSettingsPanel.php @@ -1,11 +1,9 @@ getUser(); - $preferences = $user->loadPreferences(); - - $pref_dark_console = PhabricatorUserPreferences::PREFERENCE_DARK_CONSOLE; - - $dark_console_value = $preferences->getPreference($pref_dark_console); - - if ($request->isFormPost()) { - $new_dark_console = $request->getBool($pref_dark_console); - $preferences->setPreference($pref_dark_console, $new_dark_console); - - // If the user turned Dark Console on, enable it (as though they had hit - // "`"). - if ($new_dark_console && !$dark_console_value) { - $user->setConsoleVisible(true); - $user->save(); - } - - $preferences->save(); - - return id(new AphrontRedirectResponse()) - ->setURI($this->getPanelURI('?saved=true')); - } - - $is_console_enabled = PhabricatorEnv::getEnvConfig('darkconsole.enabled'); - - $preamble = pht( - "**DarkConsole** is a developer console which can help build and ". - "debug Phabricator applications. It includes tools for understanding ". - "errors, performance, service calls, and other low-level aspects of ". - "Phabricator's inner workings."); - - if ($is_console_enabled) { - $instructions = pht( - "%s\n\n". - 'You can enable it for your account below. Enabling DarkConsole will '. - 'slightly decrease performance, but give you access to debugging '. - 'tools. You may want to disable it again later if you only need it '. - 'temporarily.'. - "\n\n". - 'NOTE: After enabling DarkConsole, **press the ##%s## key on your '. - 'keyboard** to show or hide it.', - $preamble, - '`'); - } else { - $instructions = pht( - "%s\n\n". - 'Before you can turn on DarkConsole, it needs to be enabled in '. - 'the configuration for this install (`%s`).', - $preamble, - 'darkconsole.enabled'); - } - - $form = id(new AphrontFormView()) - ->setUser($user) - ->appendRemarkupInstructions($instructions) - ->appendChild( - id(new AphrontFormSelectControl()) - ->setLabel(pht('Dark Console')) - ->setName($pref_dark_console) - ->setValue($dark_console_value) - ->setOptions( - array( - 0 => pht('Disable DarkConsole'), - 1 => pht('Enable DarkConsole'), - )) - ->setDisabled(!$is_console_enabled)) - ->appendChild( - id(new AphrontFormSubmitControl()) - ->setValue(pht('Save Preferences'))); - - $form_box = id(new PHUIObjectBoxView()) - ->setHeaderText(pht('Developer Settings')) - ->setFormSaved($request->getBool('saved')) - ->setForm($form); - - return array( - $form_box, - ); - } } diff --git a/src/applications/settings/panel/PhabricatorDiffPreferencesSettingsPanel.php b/src/applications/settings/panel/PhabricatorDiffPreferencesSettingsPanel.php index 72a1dcec25..40e38461bc 100644 --- a/src/applications/settings/panel/PhabricatorDiffPreferencesSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorDiffPreferencesSettingsPanel.php @@ -1,11 +1,9 @@ getUser(); - $preferences = $user->loadPreferences(); - - $pref_unified = PhabricatorUserPreferences::PREFERENCE_DIFF_UNIFIED; - $pref_ghosts = PhabricatorUserPreferences::PREFERENCE_DIFF_GHOSTS; - $pref_filetree = PhabricatorUserPreferences::PREFERENCE_DIFF_FILETREE; - - if ($request->isFormPost()) { - $filetree = $request->getInt($pref_filetree); - - if ($filetree && !$preferences->getPreference($pref_filetree)) { - $preferences->setPreference( - PhabricatorUserPreferences::PREFERENCE_NAV_COLLAPSED, - false); - } - - $preferences->setPreference($pref_filetree, $filetree); - - $unified = $request->getStr($pref_unified); - $preferences->setPreference($pref_unified, $unified); - - $ghosts = $request->getStr($pref_ghosts); - $preferences->setPreference($pref_ghosts, $ghosts); - - $preferences->save(); - return id(new AphrontRedirectResponse()) - ->setURI($this->getPanelURI('?saved=true')); - } - - $form = id(new AphrontFormView()) - ->setUser($user) - ->appendRemarkupInstructions( - 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 as '. - 'the default layout.')) - ->appendChild( - id(new AphrontFormSelectControl()) - ->setLabel(pht('Show Unified Diffs')) - ->setName($pref_unified) - ->setValue($preferences->getPreference($pref_unified)) - ->setOptions( - array( - 'default' => pht('On Small Screens'), - 'unified' => pht('Always'), - ))) - ->appendChild( - id(new AphrontFormSelectControl()) - ->setLabel(pht('Show Older Inlines')) - ->setName($pref_ghosts) - ->setValue($preferences->getPreference($pref_ghosts)) - ->setOptions( - array( - 'default' => pht('Enabled'), - 'disabled' => pht('Disabled'), - ))) - ->appendChild( - id(new AphrontFormSelectControl()) - ->setLabel(pht('Show Filetree')) - ->setName($pref_filetree) - ->setValue($preferences->getPreference($pref_filetree)) - ->setOptions( - array( - 0 => pht('Disable Filetree'), - 1 => pht('Enable Filetree'), - )) - ->setCaption( - pht( - 'When looking at a revision or commit, enable a sidebar '. - 'showing affected files. You can press %s to show or hide '. - 'the sidebar.', - phutil_tag('tt', array(), 'f')))) - ->appendChild( - id(new AphrontFormSubmitControl()) - ->setValue(pht('Save Preferences'))); - - $form_box = id(new PHUIObjectBoxView()) - ->setHeaderText(pht('Diff Preferences')) - ->setFormSaved($request->getBool('saved')) - ->setForm($form); - - return array( - $form_box, - ); - } } diff --git a/src/applications/settings/panel/PhabricatorDisplayPreferencesSettingsPanel.php b/src/applications/settings/panel/PhabricatorDisplayPreferencesSettingsPanel.php index ce97caf982..64f9a0c32f 100644 --- a/src/applications/settings/panel/PhabricatorDisplayPreferencesSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorDisplayPreferencesSettingsPanel.php @@ -1,11 +1,9 @@ getUser(); - $preferences = $user->loadPreferences(); - - $pref_monospaced = PhabricatorUserPreferences::PREFERENCE_MONOSPACED; - $pref_editor = PhabricatorUserPreferences::PREFERENCE_EDITOR; - $pref_multiedit = PhabricatorUserPreferences::PREFERENCE_MULTIEDIT; - $pref_titles = PhabricatorUserPreferences::PREFERENCE_TITLES; - $pref_monospaced_textareas = - PhabricatorUserPreferences::PREFERENCE_MONOSPACED_TEXTAREAS; - $pref_postprocessor = - PhabricatorUserPreferences::PREFERENCE_RESOURCE_POSTPROCESSOR; - - $errors = array(); - $e_editor = null; - if ($request->isFormPost()) { - $monospaced = $request->getStr($pref_monospaced); - $monospaced = PhabricatorUserPreferences::filterMonospacedCSSRule( - $monospaced); - - $preferences->setPreference($pref_titles, $request->getStr($pref_titles)); - $preferences->setPreference($pref_editor, $request->getStr($pref_editor)); - $preferences->setPreference( - $pref_multiedit, - $request->getStr($pref_multiedit)); - $preferences->setPreference($pref_monospaced, $monospaced); - $preferences->setPreference( - $pref_monospaced_textareas, - $request->getStr($pref_monospaced_textareas)); - $preferences->setPreference( - $pref_postprocessor, - $request->getStr($pref_postprocessor)); - - $editor_pattern = $preferences->getPreference($pref_editor); - if (strlen($editor_pattern)) { - $ok = PhabricatorHelpEditorProtocolController::hasAllowedProtocol( - $editor_pattern); - if (!$ok) { - $allowed_key = 'uri.allowed-editor-protocols'; - $allowed_protocols = PhabricatorEnv::getEnvConfig($allowed_key); - - $proto_names = array(); - foreach (array_keys($allowed_protocols) as $protocol) { - $proto_names[] = $protocol.'://'; - } - - $errors[] = pht( - 'Editor link has an invalid or missing protocol. You must '. - 'use a whitelisted editor protocol from this list: %s. To '. - 'add protocols, update %s.', - implode(', ', $proto_names), - phutil_tag('tt', array(), $allowed_key)); - - $e_editor = pht('Invalid'); - } - } - - if (!$errors) { - $preferences->save(); - return id(new AphrontRedirectResponse()) - ->setURI($this->getPanelURI('?saved=true')); - } - } - - $example_string = << PhabricatorEnv::getDoclink( - 'User Guide: Configuring an External Editor'), - ), - pht('User Guide: Configuring an External Editor')); - - $pref_monospaced_textareas_value = $preferences - ->getPreference($pref_monospaced_textareas); - if (!$pref_monospaced_textareas_value) { - $pref_monospaced_textareas_value = 'disabled'; - } - - $editor_instructions = pht( - 'Link to edit files in external editor. '. - '%%f is replaced by filename, %%l by line number, %%r by repository '. - 'callsign, %%%% by literal %%. For documentation, see: %s', - $editor_doc_link); - - $font_instructions = pht( - 'Overrides default fonts in tools like Differential. '. - 'Input should be valid CSS "font" declaration, such as '. - '"13px Consolas"'); - - $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; - - $form = id(new AphrontFormView()) - ->setUser($user) - ->appendControl( - id(new AphrontFormSelectControl()) - ->setLabel(pht('Accessibility')) - ->setName($pref_postprocessor) - ->setValue($preferences->getPreference($pref_postprocessor)) - ->setOptions($postprocessor_map)) - ->appendChild( - id(new AphrontFormSelectControl()) - ->setLabel(pht('Page Titles')) - ->setName($pref_titles) - ->setValue($preferences->getPreference($pref_titles)) - ->setOptions( - array( - 'glyph' => - pht( - 'In page titles, show Tool names as unicode glyphs: %s', - "\xE2\x9A\x99"), - 'text' => - pht( - 'In page titles, show Tool names as plain text: '. - '[Differential]'), - ))) - ->appendChild( - id(new AphrontFormTextControl()) - ->setLabel(pht('Editor Link')) - ->setName($pref_editor) - ->setCaption($editor_instructions) - ->setError($e_editor) - ->setValue($preferences->getPreference($pref_editor))) - ->appendChild( - id(new AphrontFormSelectControl()) - ->setLabel(pht('Edit Multiple Files')) - ->setName($pref_multiedit) - ->setOptions(array( - '' => pht('Supported (paths separated by spaces)'), - 'disable' => pht('Not Supported'), - )) - ->setValue($preferences->getPreference($pref_multiedit))) - ->appendChild( - id(new AphrontFormTextControl()) - ->setLabel(pht('Monospaced Font')) - ->setName($pref_monospaced) - ->setCaption($font_instructions) - ->setValue($preferences->getPreference($pref_monospaced))) - ->appendChild( - id(new AphrontFormMarkupControl()) - ->setValue(phutil_tag( - 'pre', - array('class' => 'PhabricatorMonospaced'), - $example_string))) - ->appendChild( - id(new AphrontFormRadioButtonControl()) - ->setLabel(pht('Monospaced Textareas')) - ->setName($pref_monospaced_textareas) - ->setValue($pref_monospaced_textareas_value) - ->addButton('enabled', pht('Enabled'), - pht('Show all textareas using the monospaced font defined above.')) - ->addButton('disabled', pht('Disabled'), null)); - - $form->appendChild( - id(new AphrontFormSubmitControl()) - ->setValue(pht('Save Preferences'))); - - $form_box = id(new PHUIObjectBoxView()) - ->setHeaderText(pht('Display Preferences')) - ->setFormErrors($errors) - ->setFormSaved($request->getStr('saved') === 'true') - ->setForm($form); - - return array( - $form_box, - ); - } } diff --git a/src/applications/settings/panel/PhabricatorEmailFormatSettingsPanel.php b/src/applications/settings/panel/PhabricatorEmailFormatSettingsPanel.php index 677ccfd647..2a8bf32b71 100644 --- a/src/applications/settings/panel/PhabricatorEmailFormatSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorEmailFormatSettingsPanel.php @@ -1,11 +1,9 @@ getViewer(); - $user = $this->getUser(); - - $preferences = $user->loadPreferences(); - - $pref_re_prefix = PhabricatorUserPreferences::PREFERENCE_RE_PREFIX; - $pref_vary = PhabricatorUserPreferences::PREFERENCE_VARY_SUBJECT; - $prefs_html_email = PhabricatorUserPreferences::PREFERENCE_HTML_EMAILS; - - $errors = array(); - if ($request->isFormPost()) { - - if (PhabricatorMetaMTAMail::shouldMultiplexAllMail()) { - if ($request->getStr($pref_re_prefix) == 'default') { - $preferences->unsetPreference($pref_re_prefix); - } else { - $preferences->setPreference( - $pref_re_prefix, - $request->getBool($pref_re_prefix)); - } - - if ($request->getStr($pref_vary) == 'default') { - $preferences->unsetPreference($pref_vary); - } else { - $preferences->setPreference( - $pref_vary, - $request->getBool($pref_vary)); - } - - if ($request->getStr($prefs_html_email) == 'default') { - $preferences->unsetPreference($prefs_html_email); - } else { - $preferences->setPreference( - $prefs_html_email, - $request->getBool($prefs_html_email)); - } - } - - $preferences->save(); - - return id(new AphrontRedirectResponse()) - ->setURI($this->getPanelURI('?saved=true')); - } - - $re_prefix_default = PhabricatorEnv::getEnvConfig('metamta.re-prefix') - ? pht('Enabled') - : pht('Disabled'); - - $vary_default = PhabricatorEnv::getEnvConfig('metamta.vary-subjects') - ? pht('Vary') - : pht('Do Not Vary'); - - $html_emails_default = pht('HTML'); - - $re_prefix_value = $preferences->getPreference($pref_re_prefix); - if ($re_prefix_value === null) { - $re_prefix_value = 'default'; - } else { - $re_prefix_value = $re_prefix_value - ? 'true' - : 'false'; - } - - $vary_value = $preferences->getPreference($pref_vary); - if ($vary_value === null) { - $vary_value = 'default'; - } else { - $vary_value = $vary_value - ? 'true' - : 'false'; - } - - $html_emails_value = $preferences->getPreference($prefs_html_email); - if ($html_emails_value === null) { - $html_emails_value = 'default'; - } else { - $html_emails_value = $html_emails_value - ? 'true' - : 'false'; - } - - $form = new AphrontFormView(); - $form - ->setUser($viewer); - - if (PhabricatorMetaMTAMail::shouldMultiplexAllMail()) { - $html_email_control = id(new AphrontFormSelectControl()) - ->setName($prefs_html_email) - ->setOptions( - array( - 'default' => pht('Default (%s)', $html_emails_default), - 'true' => pht('Send HTML Email'), - 'false' => pht('Send Plain Text Email'), - )) - ->setValue($html_emails_value); - - $re_control = id(new AphrontFormSelectControl()) - ->setName($pref_re_prefix) - ->setOptions( - array( - 'default' => pht('Use Server Default (%s)', $re_prefix_default), - 'true' => pht('Enable "Re:" prefix'), - 'false' => pht('Disable "Re:" prefix'), - )) - ->setValue($re_prefix_value); - - $vary_control = id(new AphrontFormSelectControl()) - ->setName($pref_vary) - ->setOptions( - array( - 'default' => pht('Use Server Default (%s)', $vary_default), - 'true' => pht('Vary Subjects'), - 'false' => pht('Do Not Vary Subjects'), - )) - ->setValue($vary_value); - } else { - $html_email_control = id(new AphrontFormStaticControl()) - ->setValue(pht('Server Default (%s)', $html_emails_default)); - - $re_control = id(new AphrontFormStaticControl()) - ->setValue(pht('Server Default (%s)', $re_prefix_default)); - - $vary_control = id(new AphrontFormStaticControl()) - ->setValue(pht('Server Default (%s)', $vary_default)); - } - - $form - ->appendRemarkupInstructions( - pht( - 'These settings fine-tune some technical aspects of how email is '. - 'formatted. You may be able to adjust them to make mail more '. - 'useful or improve threading.')); - - if (!PhabricatorMetaMTAMail::shouldMultiplexAllMail()) { - $form->appendRemarkupInstructions( - pht( - 'NOTE: This install of Phabricator is configured to send a '. - 'single mail message to all recipients, so all settings are '. - 'locked at the server default value.')); - } - - $form - ->appendRemarkupInstructions( - pht( - "You can use the **HTML Email** setting to control whether ". - "Phabricator send you HTML email (which has more color and ". - "formatting) or plain text email (which is more compatible).\n". - "\n". - "WARNING: This feature is new and experimental! If you enable ". - "it, mail may not render properly and replying to mail may not ". - "work as well.")) - ->appendChild( - $html_email_control - ->setLabel(pht('HTML Email'))) - ->appendRemarkupInstructions('') - ->appendRemarkupInstructions( - 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`")) - ->appendChild( - $re_control - ->setLabel(pht('Add "Re:" Prefix'))) - ->appendRemarkupInstructions('') - ->appendRemarkupInstructions( - 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 less useful subject lines.')) - ->appendChild( - $vary_control - ->setLabel(pht('Vary Subjects'))); - - $form - ->appendChild( - id(new AphrontFormSubmitControl()) - ->setValue(pht('Save Preferences'))); - - $form_box = id(new PHUIObjectBoxView()) - ->setHeaderText(pht('Email Format')) - ->setFormSaved($request->getStr('saved')) - ->setFormErrors($errors) - ->setForm($form); - - return id(new AphrontNullView()) - ->appendChild( - array( - $form_box, - )); - } - } diff --git a/src/applications/settings/setting/PhabricatorAccessibilitySetting.php b/src/applications/settings/setting/PhabricatorAccessibilitySetting.php index a1c273dfd7..774bfcd895 100644 --- a/src/applications/settings/setting/PhabricatorAccessibilitySetting.php +++ b/src/applications/settings/setting/PhabricatorAccessibilitySetting.php @@ -9,9 +9,17 @@ final class PhabricatorAccessibilitySetting return pht('Accessibility'); } + public function getSettingPanelKey() { + return PhabricatorDisplayPreferencesSettingsPanel::PANELKEY; + } + + protected function getSettingOrder() { + return 100; + } + protected function getControlInstructions() { return pht( - 'If you have difficulty reading the Phabricator UI, these settings '. + 'If you have difficulty reading the Phabricator UI, this setting '. 'may make Phabricator more accessible.'); } diff --git a/src/applications/settings/setting/PhabricatorDarkConsoleSetting.php b/src/applications/settings/setting/PhabricatorDarkConsoleSetting.php index a95b221e63..be78487a89 100644 --- a/src/applications/settings/setting/PhabricatorDarkConsoleSetting.php +++ b/src/applications/settings/setting/PhabricatorDarkConsoleSetting.php @@ -12,6 +12,18 @@ final class PhabricatorDarkConsoleSetting return pht('DarkConsole'); } + public function getSettingPanelKey() { + return PhabricatorDeveloperPreferencesSettingsPanel::PANELKEY; + } + + protected function getSettingOrder() { + return 100; + } + + protected function isEnabledForViewer(PhabricatorUser $viewer) { + return PhabricatorEnv::getEnvConfig('darkconsole.enabled'); + } + protected function getControlInstructions() { return pht( 'DarkConsole is a debugging console for developing and troubleshooting '. diff --git a/src/applications/settings/setting/PhabricatorEditorMultipleSetting.php b/src/applications/settings/setting/PhabricatorEditorMultipleSetting.php index 4320b5a82a..a5d64b9fcb 100644 --- a/src/applications/settings/setting/PhabricatorEditorMultipleSetting.php +++ b/src/applications/settings/setting/PhabricatorEditorMultipleSetting.php @@ -12,6 +12,14 @@ final class PhabricatorEditorMultipleSetting return pht('Edit Mulitple Files'); } + public function getSettingPanelKey() { + return PhabricatorDisplayPreferencesSettingsPanel::PANELKEY; + } + + protected function getSettingOrder() { + return 400; + } + protected function getControlInstructions() { return pht( 'Some editors support opening multiple files with a single URI. You '. diff --git a/src/applications/settings/setting/PhabricatorEditorSetting.php b/src/applications/settings/setting/PhabricatorEditorSetting.php index d0059a69c6..30465e9540 100644 --- a/src/applications/settings/setting/PhabricatorEditorSetting.php +++ b/src/applications/settings/setting/PhabricatorEditorSetting.php @@ -9,6 +9,14 @@ final class PhabricatorEditorSetting return pht('Editor Link'); } + public function getSettingPanelKey() { + return PhabricatorDisplayPreferencesSettingsPanel::PANELKEY; + } + + protected function getSettingOrder() { + return 300; + } + protected function getControlInstructions() { return pht( "Many text editors can be configured as URI handlers for special ". @@ -30,4 +38,27 @@ final class PhabricatorEditorSetting pht('User Guide: Configuring an External Editor')); } + public function validateTransactionValue($value) { + $ok = PhabricatorHelpEditorProtocolController::hasAllowedProtocol($value); + if ($ok) { + return; + } + + $allowed_key = 'uri.allowed-editor-protocols'; + $allowed_protocols = PhabricatorEnv::getEnvConfig($allowed_key); + + $proto_names = array(); + foreach (array_keys($allowed_protocols) as $protocol) { + $proto_names[] = $protocol.'://'; + } + + throw new Exception( + pht( + 'Editor link has an invalid or missing protocol. You must '. + 'use a whitelisted editor protocol from this list: %s. To '. + 'add protocols, update "%s" in Config.', + implode(', ', $proto_names), + $allowed_key)); + } + } diff --git a/src/applications/settings/setting/PhabricatorEmailFormatSetting.php b/src/applications/settings/setting/PhabricatorEmailFormatSetting.php index f3adf78694..9fb8a43125 100644 --- a/src/applications/settings/setting/PhabricatorEmailFormatSetting.php +++ b/src/applications/settings/setting/PhabricatorEmailFormatSetting.php @@ -12,6 +12,18 @@ final class PhabricatorEmailFormatSetting return pht('HTML Email'); } + public function getSettingPanelKey() { + return PhabricatorEmailFormatSettingsPanel::PANELKEY; + } + + protected function getSettingOrder() { + return 100; + } + + protected function isEnabledForViewer(PhabricatorUser $viewer) { + return PhabricatorMetaMTAMail::shouldMultiplexAllMail(); + } + protected function getControlInstructions() { return pht( 'You can opt to receive plain text email from Phabricator instead '. diff --git a/src/applications/settings/setting/PhabricatorEmailRePrefixSetting.php b/src/applications/settings/setting/PhabricatorEmailRePrefixSetting.php index 596697e420..994f02ec0f 100644 --- a/src/applications/settings/setting/PhabricatorEmailRePrefixSetting.php +++ b/src/applications/settings/setting/PhabricatorEmailRePrefixSetting.php @@ -12,6 +12,18 @@ final class PhabricatorEmailRePrefixSetting return pht('Add "Re:" Prefix'); } + public function getSettingPanelKey() { + return PhabricatorEmailFormatSettingsPanel::PANELKEY; + } + + protected function getSettingOrder() { + return 200; + } + + protected function isEnabledForViewer(PhabricatorUser $viewer) { + return PhabricatorMetaMTAMail::shouldMultiplexAllMail(); + } + protected function getControlInstructions() { return pht( 'The **Add "Re:" Prefix** setting adds "Re:" in front of all messages, '. diff --git a/src/applications/settings/setting/PhabricatorEmailVarySubjectsSetting.php b/src/applications/settings/setting/PhabricatorEmailVarySubjectsSetting.php index 10bfafff08..bdaa5dc6ef 100644 --- a/src/applications/settings/setting/PhabricatorEmailVarySubjectsSetting.php +++ b/src/applications/settings/setting/PhabricatorEmailVarySubjectsSetting.php @@ -12,6 +12,18 @@ final class PhabricatorEmailVarySubjectsSetting return pht('Vary Subjects'); } + public function getSettingPanelKey() { + return PhabricatorEmailFormatSettingsPanel::PANELKEY; + } + + protected function getSettingOrder() { + return 300; + } + + protected function isEnabledForViewer(PhabricatorUser $viewer) { + return PhabricatorMetaMTAMail::shouldMultiplexAllMail(); + } + protected function getControlInstructions() { return pht( 'With **Vary Subjects** enabled, most mail subject lines will include '. diff --git a/src/applications/settings/setting/PhabricatorMonospacedFontSetting.php b/src/applications/settings/setting/PhabricatorMonospacedFontSetting.php index ba7be082f5..3ce55b26ad 100644 --- a/src/applications/settings/setting/PhabricatorMonospacedFontSetting.php +++ b/src/applications/settings/setting/PhabricatorMonospacedFontSetting.php @@ -9,6 +9,14 @@ final class PhabricatorMonospacedFontSetting return pht('Monospaced Font'); } + public function getSettingPanelKey() { + return PhabricatorDisplayPreferencesSettingsPanel::PANELKEY; + } + + protected function getSettingOrder() { + return 500; + } + protected function getControlInstructions() { return pht( 'You can customize the font used when showing monospaced text, '. diff --git a/src/applications/settings/setting/PhabricatorMonospacedTextareasSetting.php b/src/applications/settings/setting/PhabricatorMonospacedTextareasSetting.php index 6094da23dc..ef2244f6e8 100644 --- a/src/applications/settings/setting/PhabricatorMonospacedTextareasSetting.php +++ b/src/applications/settings/setting/PhabricatorMonospacedTextareasSetting.php @@ -12,6 +12,14 @@ final class PhabricatorMonospacedTextareasSetting return pht('Monospaced Textareas'); } + public function getSettingPanelKey() { + return PhabricatorDisplayPreferencesSettingsPanel::PANELKEY; + } + + protected function getSettingOrder() { + return 600; + } + protected function getControlInstructions() { return pht( 'You can choose to use either a monospaced or variable-width font '. diff --git a/src/applications/settings/setting/PhabricatorOlderInlinesSetting.php b/src/applications/settings/setting/PhabricatorOlderInlinesSetting.php index e9dcfbbc2c..ac1f57f457 100644 --- a/src/applications/settings/setting/PhabricatorOlderInlinesSetting.php +++ b/src/applications/settings/setting/PhabricatorOlderInlinesSetting.php @@ -12,6 +12,14 @@ final class PhabricatorOlderInlinesSetting return pht('Show Older Inlines'); } + protected function getSettingOrder() { + return 200; + } + + public function getSettingPanelKey() { + return PhabricatorDiffPreferencesSettingsPanel::PANELKEY; + } + protected function getControlInstructions() { return pht( 'When a revision is updated, Phabricator attempts to bring inline '. diff --git a/src/applications/settings/setting/PhabricatorShowFiletreeSetting.php b/src/applications/settings/setting/PhabricatorShowFiletreeSetting.php index 0721a1c637..1f8161ec08 100644 --- a/src/applications/settings/setting/PhabricatorShowFiletreeSetting.php +++ b/src/applications/settings/setting/PhabricatorShowFiletreeSetting.php @@ -12,6 +12,14 @@ final class PhabricatorShowFiletreeSetting return pht('Show Filetree'); } + protected function getSettingOrder() { + return 300; + } + + public function getSettingPanelKey() { + return PhabricatorDiffPreferencesSettingsPanel::PANELKEY; + } + protected function getControlInstructions() { return pht( 'When viewing a revision or commit, you can enable a sidebar showing '. diff --git a/src/applications/settings/setting/PhabricatorTitleGlyphsSetting.php b/src/applications/settings/setting/PhabricatorTitleGlyphsSetting.php index f2f0b6ec8d..04efbcb0f0 100644 --- a/src/applications/settings/setting/PhabricatorTitleGlyphsSetting.php +++ b/src/applications/settings/setting/PhabricatorTitleGlyphsSetting.php @@ -12,6 +12,14 @@ final class PhabricatorTitleGlyphsSetting return pht('Page Titles'); } + public function getSettingPanelKey() { + return PhabricatorDisplayPreferencesSettingsPanel::PANELKEY; + } + + protected function getSettingOrder() { + return 200; + } + protected function getControlInstructions() { return pht( 'Phabricator uses unicode glyphs in page titles to provide a compact '. diff --git a/src/applications/settings/setting/PhabricatorUnifiedDiffsSetting.php b/src/applications/settings/setting/PhabricatorUnifiedDiffsSetting.php index 96c00bb170..5a94397418 100644 --- a/src/applications/settings/setting/PhabricatorUnifiedDiffsSetting.php +++ b/src/applications/settings/setting/PhabricatorUnifiedDiffsSetting.php @@ -12,6 +12,14 @@ final class PhabricatorUnifiedDiffsSetting return pht('Show Unified Diffs'); } + protected function getSettingOrder() { + return 100; + } + + public function getSettingPanelKey() { + return PhabricatorDiffPreferencesSettingsPanel::PANELKEY; + } + protected function getControlInstructions() { return pht( 'Phabricator normally shows diffs in a side-by-side layout on large '. From 9a076b71a3a6913cbf0f28d19ead70ea571f1558 Mon Sep 17 00:00:00 2001 From: epriestley Date: Fri, 3 Jun 2016 06:44:43 -0700 Subject: [PATCH 10/67] Remove standalone huge single page setting edit controller Summary: Ref T4103. This isn't necessary or particularly useful anymore since panels have been converted. Test Plan: Visited URI, got a 404. Reviewers: chad Reviewed By: chad Maniphest Tasks: T4103 Differential Revision: https://secure.phabricator.com/D16024 --- src/__phutil_library_map__.php | 2 -- .../PhabricatorSettingsApplication.php | 2 -- .../PhabricatorSettingsEditController.php | 35 ------------------- 3 files changed, 39 deletions(-) delete mode 100644 src/applications/settings/controller/PhabricatorSettingsEditController.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 524d5a7647..da5bc9da22 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -3378,7 +3378,6 @@ phutil_register_library_map(array( 'PhabricatorSettingsApplicationsPanelGroup' => 'applications/settings/panelgroup/PhabricatorSettingsApplicationsPanelGroup.php', 'PhabricatorSettingsAuthenticationPanelGroup' => 'applications/settings/panelgroup/PhabricatorSettingsAuthenticationPanelGroup.php', 'PhabricatorSettingsDeveloperPanelGroup' => 'applications/settings/panelgroup/PhabricatorSettingsDeveloperPanelGroup.php', - 'PhabricatorSettingsEditController' => 'applications/settings/controller/PhabricatorSettingsEditController.php', 'PhabricatorSettingsEditEngine' => 'applications/settings/editor/PhabricatorSettingsEditEngine.php', 'PhabricatorSettingsEmailPanelGroup' => 'applications/settings/panelgroup/PhabricatorSettingsEmailPanelGroup.php', 'PhabricatorSettingsLogsPanelGroup' => 'applications/settings/panelgroup/PhabricatorSettingsLogsPanelGroup.php', @@ -8139,7 +8138,6 @@ phutil_register_library_map(array( 'PhabricatorSettingsApplicationsPanelGroup' => 'PhabricatorSettingsPanelGroup', 'PhabricatorSettingsAuthenticationPanelGroup' => 'PhabricatorSettingsPanelGroup', 'PhabricatorSettingsDeveloperPanelGroup' => 'PhabricatorSettingsPanelGroup', - 'PhabricatorSettingsEditController' => 'PhabricatorController', 'PhabricatorSettingsEditEngine' => 'PhabricatorEditEngine', 'PhabricatorSettingsEmailPanelGroup' => 'PhabricatorSettingsPanelGroup', 'PhabricatorSettingsLogsPanelGroup' => 'PhabricatorSettingsPanelGroup', diff --git a/src/applications/settings/application/PhabricatorSettingsApplication.php b/src/applications/settings/application/PhabricatorSettingsApplication.php index fa518cadeb..5d0ef5c44e 100644 --- a/src/applications/settings/application/PhabricatorSettingsApplication.php +++ b/src/applications/settings/application/PhabricatorSettingsApplication.php @@ -37,8 +37,6 @@ final class PhabricatorSettingsApplication extends PhabricatorApplication { 'adjust/' => 'PhabricatorSettingsAdjustController', 'timezone/(?P[^/]+)/' => 'PhabricatorSettingsTimezoneController', - '(?Puser)/(?P[^/]+)/(?:panel/(?P[^/]+)/)?' - => 'PhabricatorSettingsEditController', ), ); } diff --git a/src/applications/settings/controller/PhabricatorSettingsEditController.php b/src/applications/settings/controller/PhabricatorSettingsEditController.php deleted file mode 100644 index aad58437db..0000000000 --- a/src/applications/settings/controller/PhabricatorSettingsEditController.php +++ /dev/null @@ -1,35 +0,0 @@ -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(); - } - -} From 1e17fd31a4f984e896d2705874fdf16835e5cb86 Mon Sep 17 00:00:00 2001 From: epriestley Date: Fri, 3 Jun 2016 07:24:55 -0700 Subject: [PATCH 11/67] Modernize Conpherence access to user preferences Summary: Ref T4103. Conpherence is doing some weird stuff and has its own redudnant settings object. - Get rid of `ConpherenceSettings`. - Use `getUserSetting()` instead of `loadPreferences()`. - When applying transactions, add a new mechanism to efficiently prefill caches (this will still work anyway, but it's slower if we don't bulk-fetch). Test Plan: - Changed global Conpherence setting. - Created a new Conpherence, saw setting set to global default. - Changed local room setting. - Submitted messages. - Saw cache prefill for all particpiants in database. Reviewers: chad Reviewed By: chad Maniphest Tasks: T4103 Differential Revision: https://secure.phabricator.com/D16025 --- src/__phutil_library_map__.php | 4 +- .../constants/ConpherenceSettings.php | 22 ---- .../ConpherenceUpdateController.php | 7 +- .../ConpherenceWidgetController.php | 37 ++----- .../conpherence/editor/ConpherenceEditor.php | 31 +++--- .../PhabricatorUserPreferencesCacheType.php | 8 +- .../people/query/PhabricatorPeopleQuery.php | 102 ++++++++++++++++++ .../people/storage/PhabricatorUserCache.php | 49 +++++++-- ...ricatorUserPreferencesTransactionQuery.php | 10 ++ ...ricatorConpherenceNotificationsSetting.php | 9 ++ 10 files changed, 200 insertions(+), 79 deletions(-) delete mode 100644 src/applications/conpherence/constants/ConpherenceSettings.php create mode 100644 src/applications/settings/query/PhabricatorUserPreferencesTransactionQuery.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index da5bc9da22..44b9a4c217 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -312,7 +312,6 @@ phutil_register_library_map(array( 'ConpherenceRoomListController' => 'applications/conpherence/controller/ConpherenceRoomListController.php', 'ConpherenceRoomTestCase' => 'applications/conpherence/__tests__/ConpherenceRoomTestCase.php', 'ConpherenceSchemaSpec' => 'applications/conpherence/storage/ConpherenceSchemaSpec.php', - 'ConpherenceSettings' => 'applications/conpherence/constants/ConpherenceSettings.php', 'ConpherenceTestCase' => 'applications/conpherence/__tests__/ConpherenceTestCase.php', 'ConpherenceThread' => 'applications/conpherence/storage/ConpherenceThread.php', 'ConpherenceThreadIndexEngineExtension' => 'applications/conpherence/engineextension/ConpherenceThreadIndexEngineExtension.php', @@ -3636,6 +3635,7 @@ phutil_register_library_map(array( 'PhabricatorUserPreferencesPHIDType' => 'applications/settings/phid/PhabricatorUserPreferencesPHIDType.php', 'PhabricatorUserPreferencesQuery' => 'applications/settings/query/PhabricatorUserPreferencesQuery.php', 'PhabricatorUserPreferencesTransaction' => 'applications/settings/storage/PhabricatorUserPreferencesTransaction.php', + 'PhabricatorUserPreferencesTransactionQuery' => 'applications/settings/query/PhabricatorUserPreferencesTransactionQuery.php', 'PhabricatorUserProfile' => 'applications/people/storage/PhabricatorUserProfile.php', 'PhabricatorUserProfileEditor' => 'applications/people/editor/PhabricatorUserProfileEditor.php', 'PhabricatorUserRealNameField' => 'applications/people/customfield/PhabricatorUserRealNameField.php', @@ -4565,7 +4565,6 @@ phutil_register_library_map(array( 'ConpherenceRoomListController' => 'ConpherenceController', 'ConpherenceRoomTestCase' => 'ConpherenceTestCase', 'ConpherenceSchemaSpec' => 'PhabricatorConfigSchemaSpec', - 'ConpherenceSettings' => 'ConpherenceConstants', 'ConpherenceTestCase' => 'PhabricatorTestCase', 'ConpherenceThread' => array( 'ConpherenceDAO', @@ -8439,6 +8438,7 @@ phutil_register_library_map(array( 'PhabricatorUserPreferencesPHIDType' => 'PhabricatorPHIDType', 'PhabricatorUserPreferencesQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhabricatorUserPreferencesTransaction' => 'PhabricatorApplicationTransaction', + 'PhabricatorUserPreferencesTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 'PhabricatorUserProfile' => 'PhabricatorUserDAO', 'PhabricatorUserProfileEditor' => 'PhabricatorApplicationTransactionEditor', 'PhabricatorUserRealNameField' => 'PhabricatorUserCustomField', diff --git a/src/applications/conpherence/constants/ConpherenceSettings.php b/src/applications/conpherence/constants/ConpherenceSettings.php deleted file mode 100644 index 0413ba536a..0000000000 --- a/src/applications/conpherence/constants/ConpherenceSettings.php +++ /dev/null @@ -1,22 +0,0 @@ -setSettings(array('notifications' => $notifications)); $participant->save(); + + $label = PhabricatorConpherenceNotificationsSetting::getSettingLabel( + $notifications); + $result = pht( 'Updated notification settings to "%s".', - ConpherenceSettings::getHumanString($notifications)); + $label); + return id(new AphrontAjaxResponse()) ->setContent($result); break; diff --git a/src/applications/conpherence/controller/ConpherenceWidgetController.php b/src/applications/conpherence/controller/ConpherenceWidgetController.php index af9d799519..3e749c6cf2 100644 --- a/src/applications/conpherence/controller/ConpherenceWidgetController.php +++ b/src/applications/conpherence/controller/ConpherenceWidgetController.php @@ -2,17 +2,6 @@ final class ConpherenceWidgetController extends ConpherenceController { - private $userPreferences; - - public function setUserPreferences(PhabricatorUserPreferences $pref) { - $this->userPreferences = $pref; - return $this; - } - - public function getUserPreferences() { - return $this->userPreferences; - } - public function shouldAllowPublic() { return true; } @@ -35,8 +24,6 @@ final class ConpherenceWidgetController extends ConpherenceController { } $this->setConpherence($conpherence); - $this->setUserPreferences($user->loadPreferences()); - switch ($request->getStr('widget')) { case 'widgets-people': $content = $this->renderPeopleWidgetPaneContent(); @@ -143,28 +130,24 @@ final class ConpherenceWidgetController extends ConpherenceController { ), $text); } - $default = ConpherenceSettings::EMAIL_ALWAYS; - $preference = $this->getUserPreferences(); - if ($preference) { - $default = $preference->getPreference( - PhabricatorUserPreferences::PREFERENCE_CONPH_NOTIFICATIONS, - ConpherenceSettings::EMAIL_ALWAYS); - } + $notification_key = PhabricatorConpherenceNotificationsSetting::SETTINGKEY; + $notification_default = $viewer->getUserSetting($notification_key); + $settings = $participant->getSettings(); $notifications = idx( $settings, 'notifications', - $default); + $notification_default); $options = id(new AphrontFormRadioButtonControl()) ->addButton( - ConpherenceSettings::EMAIL_ALWAYS, - ConpherenceSettings::getHumanString( - ConpherenceSettings::EMAIL_ALWAYS), + PhabricatorConpherenceNotificationsSetting::VALUE_CONPHERENCE_EMAIL, + PhabricatorConpherenceNotificationsSetting::getSettingLabel( + PhabricatorConpherenceNotificationsSetting::VALUE_CONPHERENCE_EMAIL), '') ->addButton( - ConpherenceSettings::NOTIFICATIONS_ONLY, - ConpherenceSettings::getHumanString( - ConpherenceSettings::NOTIFICATIONS_ONLY), + PhabricatorConpherenceNotificationsSetting::VALUE_CONPHERENCE_NOTIFY, + PhabricatorConpherenceNotificationsSetting::getSettingLabel( + PhabricatorConpherenceNotificationsSetting::VALUE_CONPHERENCE_NOTIFY), '') ->setName('notifications') ->setValue($notifications); diff --git a/src/applications/conpherence/editor/ConpherenceEditor.php b/src/applications/conpherence/editor/ConpherenceEditor.php index c68bb429a3..bca70b4299 100644 --- a/src/applications/conpherence/editor/ConpherenceEditor.php +++ b/src/applications/conpherence/editor/ConpherenceEditor.php @@ -541,26 +541,29 @@ final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor { $participant_phids = mpull($participants, 'getParticipantPHID'); - $preferences = id(new PhabricatorUserPreferencesQuery()) + $users = id(new PhabricatorPeopleQuery()) ->setViewer(PhabricatorUser::getOmnipotentUser()) - ->withUserPHIDs($participant_phids) + ->withPHIDs($participant_phids) + ->needUserSettings(true) ->execute(); - $preferences = mpull($preferences, null, 'getUserPHID'); + $users = mpull($users, null, 'getPHID'); + + $notification_key = PhabricatorConpherenceNotificationsSetting::SETTINGKEY; + $notification_email = + PhabricatorConpherenceNotificationsSetting::VALUE_CONPHERENCE_EMAIL; foreach ($participants as $phid => $participant) { - $default = ConpherenceSettings::EMAIL_ALWAYS; - $preference = idx($preferences, $phid); - if ($preference) { - $default = $preference->getPreference( - PhabricatorUserPreferences::PREFERENCE_CONPH_NOTIFICATIONS, - ConpherenceSettings::EMAIL_ALWAYS); + $user = idx($users, $phid); + if ($user) { + $default = $user->getUserSetting($notification_key); + } else { + $default = $notification_email; } + $settings = $participant->getSettings(); - $notifications = idx( - $settings, - 'notifications', - $default); - if ($notifications == ConpherenceSettings::EMAIL_ALWAYS) { + $notifications = idx($settings, 'notifications', $default); + + if ($notifications == $notification_email) { $to_phids[] = $phid; } } diff --git a/src/applications/people/cache/PhabricatorUserPreferencesCacheType.php b/src/applications/people/cache/PhabricatorUserPreferencesCacheType.php index 88eeb3bbd5..77aeafbf16 100644 --- a/src/applications/people/cache/PhabricatorUserPreferencesCacheType.php +++ b/src/applications/people/cache/PhabricatorUserPreferencesCacheType.php @@ -20,12 +20,16 @@ final class PhabricatorUserPreferencesCacheType public function newValueForUsers($key, array $users) { $viewer = $this->getViewer(); + $user_phids = mpull($users, 'getPHID'); + $preferences = id(new PhabricatorUserPreferencesQuery()) ->setViewer($viewer) - ->withUserPHIDs(mpull($users, 'getPHID')) + ->withUserPHIDs($user_phids) ->execute(); - return mpull($preferences, 'getPreferences', 'getUserPHID'); + $empty = array_fill_keys($user_phids, array()); + + return mpull($preferences, 'getPreferences', 'getUserPHID') + $empty; } } diff --git a/src/applications/people/query/PhabricatorPeopleQuery.php b/src/applications/people/query/PhabricatorPeopleQuery.php index 77feeb313a..cd7121299f 100644 --- a/src/applications/people/query/PhabricatorPeopleQuery.php +++ b/src/applications/people/query/PhabricatorPeopleQuery.php @@ -23,6 +23,7 @@ final class PhabricatorPeopleQuery private $needProfileImage; private $needAvailability; private $needBadges; + private $cacheKeys = array(); public function withIDs(array $ids) { $this->ids = $ids; @@ -119,6 +120,18 @@ final class PhabricatorPeopleQuery return $this; } + public function needUserSettings($need) { + $cache_key = PhabricatorUserPreferencesCacheType::KEY_PREFERENCES; + + if ($need) { + $this->cacheKeys[$cache_key] = true; + } else { + unset($this->cacheKeys[$cache_key]); + } + + return $this; + } + public function newResultObject() { return new PhabricatorUser(); } @@ -238,6 +251,8 @@ final class PhabricatorPeopleQuery } } + $this->fillUserCaches($users); + return $users; } @@ -481,4 +496,91 @@ final class PhabricatorPeopleQuery } } + private function fillUserCaches(array $users) { + if (!$this->cacheKeys) { + return; + } + + $user_map = mpull($users, null, 'getPHID'); + $keys = array_keys($this->cacheKeys); + + $hashes = array(); + foreach ($keys as $key) { + $hashes[] = PhabricatorHash::digestForIndex($key); + } + + // First, pull any available caches. If we wanted to be particularly clever + // we could do this with JOINs in the main query. + + $cache_table = new PhabricatorUserCache(); + $cache_conn = $cache_table->establishConnection('r'); + + $cache_data = queryfx_all( + $cache_conn, + 'SELECT cacheKey, userPHID, cacheData FROM %T + WHERE cacheIndex IN (%Ls) AND userPHID IN (%Ls)', + $cache_table->getTableName(), + $hashes, + array_keys($user_map)); + + $need = array(); + + $cache_data = igroup($cache_data, 'userPHID'); + foreach ($user_map as $user_phid => $user) { + $raw_rows = idx($cache_data, $user_phid, array()); + if (!$raw_rows) { + continue; + } + $raw_data = ipull($raw_rows, 'cacheData', 'cacheKey'); + + foreach ($keys as $key) { + if (isset($raw_data[$key]) || array_key_exists($key, $raw_data)) { + continue; + } + $need[$key][$user_phid] = $user; + } + + $user->attachRawCacheData($raw_data); + } + + // If we missed any cache values, bulk-construct them now. This is + // usually much cheaper than generating them on-demand for each user + // record. + + if (!$need) { + return; + } + + $writes = array(); + foreach ($need as $cache_key => $need_users) { + $type = PhabricatorUserCacheType::getCacheTypeForKey($cache_key); + if (!$type) { + continue; + } + + $data = $type->newValueForUsers($cache_key, $need_users); + + foreach ($data as $user_phid => $value) { + $raw_value = $type->getValueForStorage($value); + $data[$user_phid] = $raw_value; + $writes[] = array( + 'userPHID' => $user_phid, + 'key' => $cache_key, + 'type' => $type, + 'value' => $raw_value, + ); + } + + foreach ($need_users as $user_phid => $user) { + if (isset($data[$user_phid]) || array_key_exists($user_phid, $data)) { + $user->attachRawCacheData( + array( + $cache_key => $data[$user_phid], + )); + } + } + } + + PhabricatorUserCache::writeCaches($writes); + } } diff --git a/src/applications/people/storage/PhabricatorUserCache.php b/src/applications/people/storage/PhabricatorUserCache.php index 7228a9dfa5..f8f50be64c 100644 --- a/src/applications/people/storage/PhabricatorUserCache.php +++ b/src/applications/people/storage/PhabricatorUserCache.php @@ -42,27 +42,54 @@ final class PhabricatorUserCache extends PhabricatorUserDAO { $key, $user_phid, $raw_value) { + self::writeCaches( + array( + array( + 'type' => $type, + 'key' => $key, + 'userPHID' => $user_phid, + 'value' => $raw_value, + ), + )); + } + public static function writeCaches(array $values) { if (PhabricatorEnv::isReadOnly()) { return; } + if (!$values) { + return; + } + $table = new self(); $conn_w = $table->establishConnection('w'); + $sql = array(); + foreach ($values as $value) { + $key = $value['key']; + + $sql[] = qsprintf( + $conn_w, + '(%s, %s, %s, %s, %s)', + $value['userPHID'], + PhabricatorHash::digestForIndex($key), + $key, + $value['value'], + $value['type']->getUserCacheType()); + } + $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()); + foreach (PhabricatorLiskDAO::chunkSQL($sql) as $chunk) { + queryfx( + $conn_w, + 'INSERT INTO %T (userPHID, cacheIndex, cacheKey, cacheData, cacheType) + VALUES %Q + ON DUPLICATE KEY UPDATE cacheData = VALUES(cacheData)', + $table->getTableName(), + $chunk); + } unset($unguarded); } diff --git a/src/applications/settings/query/PhabricatorUserPreferencesTransactionQuery.php b/src/applications/settings/query/PhabricatorUserPreferencesTransactionQuery.php new file mode 100644 index 0000000000..7955830340 --- /dev/null +++ b/src/applications/settings/query/PhabricatorUserPreferencesTransactionQuery.php @@ -0,0 +1,10 @@ + pht('Send Email'), self::VALUE_CONPHERENCE_NOTIFY => pht('Send Notifications'), From 5c8ff3d37c2a64840b054ce0b858b76f571bd2b2 Mon Sep 17 00:00:00 2001 From: epriestley Date: Fri, 3 Jun 2016 07:48:26 -0700 Subject: [PATCH 12/67] Convert Diffusion blame and color into standard internal settings Summary: Ref T4103. Modernize the blame/color toggles in Diffusion. These have no separate settings UI. Test Plan: Toggled blame and colors, reloaded pages, settings stuck. Reviewers: chad Reviewed By: chad Maniphest Tasks: T4103 Differential Revision: https://secure.phabricator.com/D16026 --- src/__phutil_library_map__.php | 4 +++ .../controller/DiffusionBrowseController.php | 31 ++++++++++--------- .../PhabricatorDiffusionBlameSetting.php | 16 ++++++++++ .../PhabricatorDiffusionColorSetting.php | 16 ++++++++++ .../storage/PhabricatorUserPreferences.php | 31 +++++++++++++++++-- 5 files changed, 81 insertions(+), 17 deletions(-) create mode 100644 src/applications/settings/setting/PhabricatorDiffusionBlameSetting.php create mode 100644 src/applications/settings/setting/PhabricatorDiffusionColorSetting.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 44b9a4c217..346f6797ab 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -2319,6 +2319,8 @@ phutil_register_library_map(array( 'PhabricatorDifferentialManagementWorkflow' => 'applications/differential/management/PhabricatorDifferentialManagementWorkflow.php', 'PhabricatorDifferentialRevisionTestDataGenerator' => 'applications/differential/lipsum/PhabricatorDifferentialRevisionTestDataGenerator.php', 'PhabricatorDiffusionApplication' => 'applications/diffusion/application/PhabricatorDiffusionApplication.php', + 'PhabricatorDiffusionBlameSetting' => 'applications/settings/setting/PhabricatorDiffusionBlameSetting.php', + 'PhabricatorDiffusionColorSetting' => 'applications/settings/setting/PhabricatorDiffusionColorSetting.php', 'PhabricatorDiffusionConfigOptions' => 'applications/diffusion/config/PhabricatorDiffusionConfigOptions.php', 'PhabricatorDisabledUserController' => 'applications/auth/controller/PhabricatorDisabledUserController.php', 'PhabricatorDisplayPreferencesSettingsPanel' => 'applications/settings/panel/PhabricatorDisplayPreferencesSettingsPanel.php', @@ -6889,6 +6891,8 @@ phutil_register_library_map(array( 'PhabricatorDifferentialManagementWorkflow' => 'PhabricatorManagementWorkflow', 'PhabricatorDifferentialRevisionTestDataGenerator' => 'PhabricatorTestDataGenerator', 'PhabricatorDiffusionApplication' => 'PhabricatorApplication', + 'PhabricatorDiffusionBlameSetting' => 'PhabricatorInternalSetting', + 'PhabricatorDiffusionColorSetting' => 'PhabricatorInternalSetting', 'PhabricatorDiffusionConfigOptions' => 'PhabricatorApplicationConfigOptions', 'PhabricatorDisabledUserController' => 'PhabricatorAuthController', 'PhabricatorDisplayPreferencesSettingsPanel' => 'PhabricatorEditEngineSettingsPanel', diff --git a/src/applications/diffusion/controller/DiffusionBrowseController.php b/src/applications/diffusion/controller/DiffusionBrowseController.php index 1357bed620..30c2b8265d 100644 --- a/src/applications/diffusion/controller/DiffusionBrowseController.php +++ b/src/applications/diffusion/controller/DiffusionBrowseController.php @@ -105,28 +105,31 @@ final class DiffusionBrowseController extends DiffusionController { $path = $drequest->getPath(); - $preferences = $viewer->loadPreferences(); + $blame_key = PhabricatorDiffusionBlameSetting::SETTINGKEY; + $color_key = PhabricatorDiffusionColorSetting::SETTINGKEY; $show_blame = $request->getBool( 'blame', - $preferences->getPreference( - PhabricatorUserPreferences::PREFERENCE_DIFFUSION_BLAME, - false)); + $viewer->getUserSetting($blame_key)); + $show_color = $request->getBool( 'color', - $preferences->getPreference( - PhabricatorUserPreferences::PREFERENCE_DIFFUSION_COLOR, - true)); + $viewer->getUserSetting($color_key)); $view = $request->getStr('view'); if ($request->isFormPost() && $view != 'raw' && $viewer->isLoggedIn()) { - $preferences->setPreference( - PhabricatorUserPreferences::PREFERENCE_DIFFUSION_BLAME, - $show_blame); - $preferences->setPreference( - PhabricatorUserPreferences::PREFERENCE_DIFFUSION_COLOR, - $show_color); - $preferences->save(); + $preferences = PhabricatorUserPreferences::loadUserPreferences($viewer); + + $editor = id(new PhabricatorUserPreferencesEditor()) + ->setActor($viewer) + ->setContentSourceFromRequest($request) + ->setContinueOnNoEffect(true) + ->setContinueOnMissingFields(true); + + $xactions = array(); + $xactions[] = $preferences->newTransaction($blame_key, $show_blame); + $xactions[] = $preferences->newTransaction($color_key, $show_color); + $editor->applyTransactions($preferences, $xactions); $uri = $request->getRequestURI() ->alter('blame', null) diff --git a/src/applications/settings/setting/PhabricatorDiffusionBlameSetting.php b/src/applications/settings/setting/PhabricatorDiffusionBlameSetting.php new file mode 100644 index 0000000000..bc98045506 --- /dev/null +++ b/src/applications/settings/setting/PhabricatorDiffusionBlameSetting.php @@ -0,0 +1,16 @@ +setViewer($user) + ->withUsers(array($user)) + ->executeOne(); + if ($preferences) { + return $preferences; + } + + return id(new self()) + ->setUserPHID($user->getPHID()) + ->attachUser($user); + } + + public function newTransaction($key, $value) { + $setting_property = PhabricatorUserPreferencesTransaction::PROPERTY_SETTING; + $xaction_type = PhabricatorUserPreferencesTransaction::TYPE_SETTING; + + return id(clone $this->getApplicationTransactionTemplate()) + ->setTransactionType($xaction_type) + ->setMetadataValue($setting_property, $key) + ->setNewValue($value); + } /* -( PhabricatorPolicyInterface )----------------------------------------- */ From 44e88f186c86ee3d5334ae61b5a98292c9a031e4 Mon Sep 17 00:00:00 2001 From: epriestley Date: Fri, 3 Jun 2016 09:19:13 -0700 Subject: [PATCH 13/67] Modernize "favorite project policies" setting Summary: Ref T4103. Convert this into a proper internal setting and use transactions to mutate it. Also remove some no-longer-used old non-modular settings constants. Test Plan: - Used policy dropdown, saw recently-used projects. - Selected some new projects, saw them appear. - Grepped for all removed constants. Reviewers: chad Reviewed By: chad Maniphest Tasks: T4103 Differential Revision: https://secure.phabricator.com/D16027 --- src/__phutil_library_map__.php | 2 ++ .../people/storage/PhabricatorUser.php | 9 --------- .../PhabricatorPolicyEditController.php | 19 ++++++++++++------ .../policy/query/PhabricatorPolicyQuery.php | 2 +- .../PhabricatorPolicyFavoritesSetting.php | 16 +++++++++++++++ .../storage/PhabricatorUserPreferences.php | 20 ------------------- .../phui/calendar/PHUICalendarMonthView.php | 2 +- 7 files changed, 33 insertions(+), 37 deletions(-) create mode 100644 src/applications/settings/setting/PhabricatorPolicyFavoritesSetting.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 346f6797ab..db61cc72b6 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -3031,6 +3031,7 @@ phutil_register_library_map(array( 'PhabricatorPolicyEditField' => 'applications/transactions/editfield/PhabricatorPolicyEditField.php', 'PhabricatorPolicyException' => 'applications/policy/exception/PhabricatorPolicyException.php', 'PhabricatorPolicyExplainController' => 'applications/policy/controller/PhabricatorPolicyExplainController.php', + 'PhabricatorPolicyFavoritesSetting' => 'applications/settings/setting/PhabricatorPolicyFavoritesSetting.php', 'PhabricatorPolicyFilter' => 'applications/policy/filter/PhabricatorPolicyFilter.php', 'PhabricatorPolicyInterface' => 'applications/policy/interface/PhabricatorPolicyInterface.php', 'PhabricatorPolicyManagementShowWorkflow' => 'applications/policy/management/PhabricatorPolicyManagementShowWorkflow.php', @@ -7706,6 +7707,7 @@ phutil_register_library_map(array( 'PhabricatorPolicyEditField' => 'PhabricatorEditField', 'PhabricatorPolicyException' => 'Exception', 'PhabricatorPolicyExplainController' => 'PhabricatorPolicyController', + 'PhabricatorPolicyFavoritesSetting' => 'PhabricatorInternalSetting', 'PhabricatorPolicyFilter' => 'Phobject', 'PhabricatorPolicyInterface' => 'PhabricatorPHIDInterface', 'PhabricatorPolicyManagementShowWorkflow' => 'PhabricatorPolicyManagementWorkflow', diff --git a/src/applications/people/storage/PhabricatorUser.php b/src/applications/people/storage/PhabricatorUser.php index 0d450c5a88..b18aa0e4cf 100644 --- a/src/applications/people/storage/PhabricatorUser.php +++ b/src/applications/people/storage/PhabricatorUser.php @@ -606,15 +606,6 @@ final class PhabricatorUser $preferences = new PhabricatorUserPreferences(); $preferences->setUserPHID($this->getPHID()); $preferences->attachUser($this); - - $default_dict = array( - PhabricatorUserPreferences::PREFERENCE_TITLES => 'glyph', - PhabricatorUserPreferences::PREFERENCE_EDITOR => '', - PhabricatorUserPreferences::PREFERENCE_MONOSPACED => '', - PhabricatorUserPreferences::PREFERENCE_DARK_CONSOLE => 0, - ); - - $preferences->setPreferences($default_dict); } $this->preferences = $preferences; diff --git a/src/applications/policy/controller/PhabricatorPolicyEditController.php b/src/applications/policy/controller/PhabricatorPolicyEditController.php index 3dd8924bb6..75701cc748 100644 --- a/src/applications/policy/controller/PhabricatorPolicyEditController.php +++ b/src/applications/policy/controller/PhabricatorPolicyEditController.php @@ -281,10 +281,8 @@ final class PhabricatorPolicyEditController // Save this project as one of the user's most recently used projects, // so we'll show it by default in future menus. - $pref_key = PhabricatorUserPreferences::PREFERENCE_FAVORITE_POLICIES; - - $preferences = $viewer->loadPreferences(); - $favorites = $preferences->getPreference($pref_key); + $favorites_key = PhabricatorPolicyFavoritesSetting::SETTINGKEY; + $favorites = $viewer->getUserSetting($favorites_key); if (!is_array($favorites)) { $favorites = array(); } @@ -293,8 +291,17 @@ final class PhabricatorPolicyEditController unset($favorites[$project_phid]); $favorites[$project_phid] = true; - $preferences->setPreference($pref_key, $favorites); - $preferences->save(); + $preferences = PhabricatorUserPreferences::loadUserPreferences($viewer); + + $editor = id(new PhabricatorUserPreferencesEditor()) + ->setActor($viewer) + ->setContentSourceFromRequest($request) + ->setContinueOnNoEffect(true) + ->setContinueOnMissingFields(true); + + $xactions = array(); + $xactions[] = $preferences->newTransaction($favorites_key, $favorites); + $editor->applyTransactions($preferences, $xactions); $data = array( 'phid' => $project->getPHID(), diff --git a/src/applications/policy/query/PhabricatorPolicyQuery.php b/src/applications/policy/query/PhabricatorPolicyQuery.php index 81c9bb8a51..e51b2ca401 100644 --- a/src/applications/policy/query/PhabricatorPolicyQuery.php +++ b/src/applications/policy/query/PhabricatorPolicyQuery.php @@ -195,7 +195,7 @@ final class PhabricatorPolicyQuery $viewer = $this->getViewer(); if ($viewer->getPHID()) { - $pref_key = PhabricatorUserPreferences::PREFERENCE_FAVORITE_POLICIES; + $pref_key = PhabricatorPolicyFavoritesSetting::SETTINGKEY; $favorite_limit = 10; $default_limit = 5; diff --git a/src/applications/settings/setting/PhabricatorPolicyFavoritesSetting.php b/src/applications/settings/setting/PhabricatorPolicyFavoritesSetting.php new file mode 100644 index 0000000000..61fb870625 --- /dev/null +++ b/src/applications/settings/setting/PhabricatorPolicyFavoritesSetting.php @@ -0,0 +1,16 @@ +getViewer(); - $week_key = PhabricatorUserPreferences::PREFERENCE_WEEK_START_DAY; + $week_key = PhabricatorWeekStartDaySetting::SETTINGKEY; $week_start = $viewer->getUserSetting($week_key); $week_end = ($week_start + 6) % 7; From 64d6593e9cba54b4a15204ff040ee435d250df73 Mon Sep 17 00:00:00 2001 From: epriestley Date: Fri, 3 Jun 2016 09:53:01 -0700 Subject: [PATCH 14/67] Modernize pinned homepage applications settings Summary: Ref T4103. A few bits here: - We have an ancient "tiles" preference which was just a fallback from 2-3 years ago. Throw that away. - Modenize the other pinned stuff. We should likely revisit this after the next homepage update but I just left the actual defaults alone for now. - Lightly prepare for global default editing. - Add a "reset to defaults" option. Test Plan: - Pinned, unpinned, reordered and reset application homepage order. Reviewers: chad Reviewed By: chad Maniphest Tasks: T4103 Differential Revision: https://secure.phabricator.com/D16028 --- src/__phutil_library_map__.php | 2 + .../controller/PhabricatorHomeController.php | 5 +- .../PhabricatorEditEngineSettingsPanel.php | 7 +- ...habricatorHomePreferencesSettingsPanel.php | 90 +++++++++++++------ .../panel/PhabricatorSettingsPanel.php | 18 ++++ .../PhabricatorPinnedApplicationsSetting.php | 33 +++++++ .../storage/PhabricatorUserPreferences.php | 49 +++------- 7 files changed, 131 insertions(+), 73 deletions(-) create mode 100644 src/applications/settings/setting/PhabricatorPinnedApplicationsSetting.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index db61cc72b6..a8c8231787 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -3008,6 +3008,7 @@ phutil_register_library_map(array( 'PhabricatorPhurlURLTransactionComment' => 'applications/phurl/storage/PhabricatorPhurlURLTransactionComment.php', 'PhabricatorPhurlURLTransactionQuery' => 'applications/phurl/query/PhabricatorPhurlURLTransactionQuery.php', 'PhabricatorPhurlURLViewController' => 'applications/phurl/controller/PhabricatorPhurlURLViewController.php', + 'PhabricatorPinnedApplicationsSetting' => 'applications/settings/setting/PhabricatorPinnedApplicationsSetting.php', 'PhabricatorPirateEnglishTranslation' => 'infrastructure/internationalization/translation/PhabricatorPirateEnglishTranslation.php', 'PhabricatorPlatformSite' => 'aphront/site/PhabricatorPlatformSite.php', 'PhabricatorPointsEditField' => 'applications/transactions/editfield/PhabricatorPointsEditField.php', @@ -7680,6 +7681,7 @@ phutil_register_library_map(array( 'PhabricatorPhurlURLTransactionComment' => 'PhabricatorApplicationTransactionComment', 'PhabricatorPhurlURLTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 'PhabricatorPhurlURLViewController' => 'PhabricatorPhurlController', + 'PhabricatorPinnedApplicationsSetting' => 'PhabricatorInternalSetting', 'PhabricatorPirateEnglishTranslation' => 'PhutilTranslation', 'PhabricatorPlatformSite' => 'PhabricatorSite', 'PhabricatorPointsEditField' => 'PhabricatorEditField', diff --git a/src/applications/home/controller/PhabricatorHomeController.php b/src/applications/home/controller/PhabricatorHomeController.php index 334a34e99b..3df8140420 100644 --- a/src/applications/home/controller/PhabricatorHomeController.php +++ b/src/applications/home/controller/PhabricatorHomeController.php @@ -15,9 +15,8 @@ abstract class PhabricatorHomeController extends PhabricatorController { ->withLaunchable(true) ->execute(); - $pinned = $user->loadPreferences()->getPinnedApplications( - $applications, - $user); + $pinned = $user->getUserSetting( + PhabricatorPinnedApplicationsSetting::SETTINGKEY); // Force "Applications" to appear at the bottom. $meta_app = 'PhabricatorApplicationsApplication'; diff --git a/src/applications/settings/panel/PhabricatorEditEngineSettingsPanel.php b/src/applications/settings/panel/PhabricatorEditEngineSettingsPanel.php index 658e9829fb..d542357438 100644 --- a/src/applications/settings/panel/PhabricatorEditEngineSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorEditEngineSettingsPanel.php @@ -26,12 +26,7 @@ abstract class PhabricatorEditEngineSettingsPanel ->setIsSelfEdit($is_self) ->setProfileURI($profile_uri); - $preferences = $user->loadPreferences(); - - PhabricatorPolicyFilter::requireCapability( - $viewer, - $preferences, - PhabricatorPolicyCapability::CAN_EDIT); + $preferences = $this->loadTargetPreferences(); $engine->setTargetObject($preferences); diff --git a/src/applications/settings/panel/PhabricatorHomePreferencesSettingsPanel.php b/src/applications/settings/panel/PhabricatorHomePreferencesSettingsPanel.php index a4c75f88ea..5369a2b90c 100644 --- a/src/applications/settings/panel/PhabricatorHomePreferencesSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorHomePreferencesSettingsPanel.php @@ -16,18 +16,19 @@ final class PhabricatorHomePreferencesSettingsPanel } public function processRequest(AphrontRequest $request) { - $user = $request->getUser(); - $preferences = $user->loadPreferences(); + $viewer = $this->getViewer(); + $preferences = $this->loadTargetPreferences(); + + $pinned_key = PhabricatorPinnedApplicationsSetting::SETTINGKEY; + $pinned = $preferences->getSettingValue($pinned_key); $apps = id(new PhabricatorApplicationQuery()) - ->setViewer($user) + ->setViewer($viewer) ->withInstalled(true) ->withUnlisted(false) ->withLaunchable(true) ->execute(); - $pinned = $preferences->getPinnedApplications($apps, $user); - $app_list = array(); foreach ($pinned as $app) { if (isset($apps[$app])) { @@ -35,6 +36,23 @@ final class PhabricatorHomePreferencesSettingsPanel } } + if ($request->getBool('reset')) { + if ($request->isFormPost()) { + $this->writePinnedApplications($preferences, null); + return id(new AphrontRedirectResponse()) + ->setURI($this->getPanelURI()); + } + + return $this->newDialog() + ->setTitle(pht('Reset Applications')) + ->addHiddenInput('reset', 'true') + ->appendParagraph( + pht('Reset pinned applications to their defaults?')) + ->addSubmitButton(pht('Reset Applications')) + ->addCancelButton($this->getPanelURI()); + } + + if ($request->getBool('add')) { $options = array(); foreach ($apps as $app) { @@ -48,10 +66,8 @@ final class PhabricatorHomePreferencesSettingsPanel $pin = $request->getStr('pin'); if (isset($options[$pin]) && !in_array($pin, $pinned)) { $pinned[] = $pin; - $preferences->setPreference( - PhabricatorUserPreferences::PREFERENCE_APP_PINNED, - $pinned); - $preferences->save(); + + $this->writePinnedApplications($preferences, $pinned); return id(new AphrontRedirectResponse()) ->setURI($this->getPanelURI()); @@ -65,21 +81,18 @@ final class PhabricatorHomePreferencesSettingsPanel ->setDisabledOptions(array_keys($app_list)); $form = id(new AphrontFormView()) - ->setUser($user) + ->setViewer($viewer) ->addHiddenInput('add', 'true') ->appendRemarkupInstructions( pht('Choose an application to pin to your home page.')) ->appendChild($options_control); - $dialog = id(new AphrontDialogView()) - ->setUser($user) + return $this->newDialog() ->setWidth(AphrontDialogView::WIDTH_FORM) ->setTitle(pht('Pin Application')) ->appendChild($form->buildLayoutView()) ->addSubmitButton(pht('Pin Application')) ->addCancelButton($this->getPanelURI()); - - return id(new AphrontDialogResponse())->setDialog($dialog); } $unpin = $request->getStr('unpin'); @@ -88,35 +101,28 @@ final class PhabricatorHomePreferencesSettingsPanel if ($app) { if ($request->isFormPost()) { $pinned = array_diff($pinned, array($unpin)); - $preferences->setPreference( - PhabricatorUserPreferences::PREFERENCE_APP_PINNED, - $pinned); - $preferences->save(); + + $this->writePinnedApplications($preferences, $pinned); return id(new AphrontRedirectResponse()) ->setURI($this->getPanelURI()); } - $dialog = id(new AphrontDialogView()) - ->setUser($user) + return $this->newDialog() ->setTitle(pht('Unpin Application')) + ->addHiddenInput('unpin', $unpin) ->appendParagraph( pht( 'Unpin the %s application from your home page?', phutil_tag('strong', array(), $app->getName()))) ->addSubmitButton(pht('Unpin Application')) - ->addCanceLButton($this->getPanelURI()); - - return id(new AphrontDialogResponse())->setDialog($dialog); + ->addCancelButton($this->getPanelURI()); } } $order = $request->getStrList('order'); if ($order && $request->validateCSRF()) { - $preferences->setPreference( - PhabricatorUserPreferences::PREFERENCE_APP_PINNED, - $order); - $preferences->save(); + $this->writePinnedApplications($preferences, $order); return id(new AphrontRedirectResponse()) ->setURI($this->getPanelURI()); @@ -125,7 +131,7 @@ final class PhabricatorHomePreferencesSettingsPanel $list_id = celerity_generate_unique_node_id(); $list = id(new PHUIObjectItemListView()) - ->setUser($user) + ->setViewer($viewer) ->setID($list_id); Javelin::initBehavior( @@ -182,7 +188,14 @@ final class PhabricatorHomePreferencesSettingsPanel ->setText(pht('Pin Application')) ->setHref($this->getPanelURI().'?add=true') ->setWorkflow(true) - ->setIcon('fa-thumb-tack')); + ->setIcon('fa-thumb-tack')) + ->addActionLink( + id(new PHUIButtonView()) + ->setTag('a') + ->setText(pht('Reset to Defaults')) + ->setHref($this->getPanelURI().'?reset=true') + ->setWorkflow(true) + ->setIcon('fa-recycle')); $box = id(new PHUIObjectBoxView()) ->setHeader($header) @@ -191,4 +204,23 @@ final class PhabricatorHomePreferencesSettingsPanel return $box; } + private function writePinnedApplications( + PhabricatorUserPreferences $preferences, + $pinned) { + + $viewer = $this->getViewer(); + $request = $this->getController()->getRequest(); + $pinned_key = PhabricatorPinnedApplicationsSetting::SETTINGKEY; + + $editor = id(new PhabricatorUserPreferencesEditor()) + ->setActor($viewer) + ->setContentSourceFromRequest($request) + ->setContinueOnNoEffect(true) + ->setContinueOnMissingFields(true); + + $xactions = array(); + $xactions[] = $preferences->newTransaction($pinned_key, $pinned); + $editor->applyTransactions($preferences, $xactions); + } + } diff --git a/src/applications/settings/panel/PhabricatorSettingsPanel.php b/src/applications/settings/panel/PhabricatorSettingsPanel.php index 27679e733c..86925bf4a7 100644 --- a/src/applications/settings/panel/PhabricatorSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorSettingsPanel.php @@ -217,4 +217,22 @@ abstract class PhabricatorSettingsPanel extends Phobject { ->addString($this->getPanelName()); } + protected function loadTargetPreferences() { + $viewer = $this->getViewer(); + $user = $this->getUser(); + + $preferences = PhabricatorUserPreferences::loadUserPreferences($user); + + PhabricatorPolicyFilter::requireCapability( + $viewer, + $preferences, + PhabricatorPolicyCapability::CAN_EDIT); + + return $preferences; + } + + protected function newDialog() { + return $this->getController()->newDialog(); + } + } diff --git a/src/applications/settings/setting/PhabricatorPinnedApplicationsSetting.php b/src/applications/settings/setting/PhabricatorPinnedApplicationsSetting.php new file mode 100644 index 0000000000..410e64adc3 --- /dev/null +++ b/src/applications/settings/setting/PhabricatorPinnedApplicationsSetting.php @@ -0,0 +1,33 @@ +getViewer(); + + $applications = id(new PhabricatorApplicationQuery()) + ->setViewer($viewer) + ->withInstalled(true) + ->withUnlisted(false) + ->withLaunchable(true) + ->execute(); + + $pinned = array(); + foreach ($applications as $application) { + if ($application->isPinnedByDefault($viewer)) { + $pinned[] = get_class($application); + } + } + + return $pinned; + } + + +} diff --git a/src/applications/settings/storage/PhabricatorUserPreferences.php b/src/applications/settings/storage/PhabricatorUserPreferences.php index 878e18b28c..16de4ce225 100644 --- a/src/applications/settings/storage/PhabricatorUserPreferences.php +++ b/src/applications/settings/storage/PhabricatorUserPreferences.php @@ -73,49 +73,28 @@ final class PhabricatorUserPreferences return null; } + $setting = id(clone $setting) + ->setViewer($this->getUser()); + return $setting->getSettingDefaultValue(); } + public function getSettingValue($key) { + if (array_key_exists($key, $this->preferences)) { + return $this->preferences[$key]; + } + + // TODO: If this setting set inherits from another preference set, + // we would look it up here. + + return $this->getDefaultValue($key); + } + 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); - - if ($pinned) { - return $pinned; - } - - $pref_tiles = self::PREFERENCE_APP_TILES; - $tiles = $this->getPreference($pref_tiles, array()); - $full_tile = 'full'; - - $large = array(); - foreach ($apps as $app) { - $show = $app->isPinnedByDefault($viewer); - - // TODO: This is legacy stuff, clean it up eventually. This approximately - // retains the old "tiles" preference. - if (isset($tiles[get_class($app)])) { - $show = ($tiles[get_class($app)] == $full_tile); - } - - if ($show) { - $large[] = get_class($app); - } - } - - return $large; - } - - public static function filterMonospacedCSSRule($monospaced) { - // Prevent the user from doing dangerous things. - return preg_replace('([^a-z0-9 ,"./]+)i', '', $monospaced); - } - public function attachUser(PhabricatorUser $user = null) { $this->user = $user; return $this; From 109917a94b39e061934c9f987e779be47a6ab95e Mon Sep 17 00:00:00 2001 From: epriestley Date: Fri, 3 Jun 2016 10:44:26 -0700 Subject: [PATCH 15/67] Turn DarkConsole settings into real settings Summary: Ref T4103. These settings long-predate proper settings and are based on hard-coded user properties. Turn them into real settings. (I didn't try to migrate the value since they're trivial to restore and only useful to developers.) Test Plan: - Toggled console on/off. - Swapped tabs. - Reloaded page, everything stayed sticky. Reviewers: chad Reviewed By: chad Subscribers: eadler Maniphest Tasks: T4103 Differential Revision: https://secure.phabricator.com/D16029 --- .../20160603.user.01.removedcenabled.sql | 2 ++ .../20160603.user.02.removedctab.sql | 2 ++ .../20160603.user.03.removedcvisible.sql | 2 ++ src/__phutil_library_map__.php | 4 +++ .../controller/DarkConsoleController.php | 34 ++++++++++++++----- .../console/core/DarkConsoleCore.php | 3 +- .../people/storage/PhabricatorUser.php | 7 ---- .../PhabricatorDarkConsoleTabSetting.php | 12 +++++++ .../PhabricatorDarkConsoleVisibleSetting.php | 12 +++++++ src/view/page/PhabricatorStandardPageView.php | 14 ++++++-- 10 files changed, 74 insertions(+), 18 deletions(-) create mode 100644 resources/sql/autopatches/20160603.user.01.removedcenabled.sql create mode 100644 resources/sql/autopatches/20160603.user.02.removedctab.sql create mode 100644 resources/sql/autopatches/20160603.user.03.removedcvisible.sql create mode 100644 src/applications/settings/setting/PhabricatorDarkConsoleTabSetting.php create mode 100644 src/applications/settings/setting/PhabricatorDarkConsoleVisibleSetting.php diff --git a/resources/sql/autopatches/20160603.user.01.removedcenabled.sql b/resources/sql/autopatches/20160603.user.01.removedcenabled.sql new file mode 100644 index 0000000000..92d6c354e3 --- /dev/null +++ b/resources/sql/autopatches/20160603.user.01.removedcenabled.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_user.user + DROP COLUMN consoleEnabled; diff --git a/resources/sql/autopatches/20160603.user.02.removedctab.sql b/resources/sql/autopatches/20160603.user.02.removedctab.sql new file mode 100644 index 0000000000..fea865908b --- /dev/null +++ b/resources/sql/autopatches/20160603.user.02.removedctab.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_user.user + DROP COLUMN consoleTab; diff --git a/resources/sql/autopatches/20160603.user.03.removedcvisible.sql b/resources/sql/autopatches/20160603.user.03.removedcvisible.sql new file mode 100644 index 0000000000..6cf5149f42 --- /dev/null +++ b/resources/sql/autopatches/20160603.user.03.removedcvisible.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_user.user + DROP COLUMN consoleVisible; diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index a8c8231787..d1e5874bd3 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -2240,6 +2240,8 @@ phutil_register_library_map(array( 'PhabricatorDaemonsSetupCheck' => 'applications/config/check/PhabricatorDaemonsSetupCheck.php', 'PhabricatorDailyRoutineTriggerClock' => 'infrastructure/daemon/workers/clock/PhabricatorDailyRoutineTriggerClock.php', 'PhabricatorDarkConsoleSetting' => 'applications/settings/setting/PhabricatorDarkConsoleSetting.php', + 'PhabricatorDarkConsoleTabSetting' => 'applications/settings/setting/PhabricatorDarkConsoleTabSetting.php', + 'PhabricatorDarkConsoleVisibleSetting' => 'applications/settings/setting/PhabricatorDarkConsoleVisibleSetting.php', 'PhabricatorDashboard' => 'applications/dashboard/storage/PhabricatorDashboard.php', 'PhabricatorDashboardAddPanelController' => 'applications/dashboard/controller/PhabricatorDashboardAddPanelController.php', 'PhabricatorDashboardApplication' => 'applications/dashboard/application/PhabricatorDashboardApplication.php', @@ -6797,6 +6799,8 @@ phutil_register_library_map(array( 'PhabricatorDaemonsSetupCheck' => 'PhabricatorSetupCheck', 'PhabricatorDailyRoutineTriggerClock' => 'PhabricatorTriggerClock', 'PhabricatorDarkConsoleSetting' => 'PhabricatorSelectSetting', + 'PhabricatorDarkConsoleTabSetting' => 'PhabricatorInternalSetting', + 'PhabricatorDarkConsoleVisibleSetting' => 'PhabricatorInternalSetting', 'PhabricatorDashboard' => array( 'PhabricatorDashboardDAO', 'PhabricatorApplicationTransactionInterface', diff --git a/src/applications/console/controller/DarkConsoleController.php b/src/applications/console/controller/DarkConsoleController.php index 3849ee0cb5..026083cb59 100644 --- a/src/applications/console/controller/DarkConsoleController.php +++ b/src/applications/console/controller/DarkConsoleController.php @@ -17,30 +17,48 @@ final class DarkConsoleController extends PhabricatorController { return true; } - public function processRequest() { - $request = $this->getRequest(); - $user = $request->getUser(); + public function handleRequest(AphrontRequest $request) { + $viewer = $this->getViewer(); $response = id(new AphrontAjaxResponse())->setDisableConsole(true); - if (!$user->isLoggedIn()) { + if (!$viewer->isLoggedIn()) { return $response; } $visible = $request->getStr('visible'); if (strlen($visible)) { - $user->setConsoleVisible((int)$visible); - $user->save(); + $this->writeDarkConsoleSetting( + PhabricatorDarkConsoleVisibleSetting::SETTINGKEY, + (int)$visible); return $response; } $tab = $request->getStr('tab'); if (strlen($tab)) { - $user->setConsoleTab($tab); - $user->save(); + $this->writeDarkConsoleSetting( + PhabricatorDarkConsoleTabSetting::SETTINGKEY, + $tab); return $response; } return new Aphront404Response(); } + private function writeDarkConsoleSetting($key, $value) { + $viewer = $this->getViewer(); + $request = $this->getRequest(); + + $preferences = PhabricatorUserPreferences::loadUserPreferences($viewer); + + $editor = id(new PhabricatorUserPreferencesEditor()) + ->setActor($viewer) + ->setContentSourceFromRequest($request) + ->setContinueOnNoEffect(true) + ->setContinueOnMissingFields(true); + + $xactions = array(); + $xactions[] = $preferences->newTransaction($key, $value); + $editor->applyTransactions($preferences, $xactions); + } + } diff --git a/src/applications/console/core/DarkConsoleCore.php b/src/applications/console/core/DarkConsoleCore.php index 2898a56e41..ddb22b9cfe 100644 --- a/src/applications/console/core/DarkConsoleCore.php +++ b/src/applications/console/core/DarkConsoleCore.php @@ -93,7 +93,8 @@ final class DarkConsoleCore extends Phobject { public function render(AphrontRequest $request) { $user = $request->getUser(); - $visible = $user ? $user->getConsoleVisible() : true; + $visible = $user->getUserSetting( + PhabricatorDarkConsoleVisibleSetting::SETTINGKEY); return javelin_tag( 'div', diff --git a/src/applications/people/storage/PhabricatorUser.php b/src/applications/people/storage/PhabricatorUser.php index b18aa0e4cf..0640f3c1b2 100644 --- a/src/applications/people/storage/PhabricatorUser.php +++ b/src/applications/people/storage/PhabricatorUser.php @@ -34,10 +34,6 @@ final class PhabricatorUser protected $availabilityCache; protected $availabilityCacheTTL; - protected $consoleEnabled = 0; - protected $consoleVisible = 0; - protected $consoleTab = ''; - protected $conduitCertificate; protected $isSystemAgent = 0; @@ -190,9 +186,6 @@ final class PhabricatorUser 'passwordSalt' => 'text32?', 'passwordHash' => 'text128?', 'profileImagePHID' => 'phid?', - 'consoleEnabled' => 'bool', - 'consoleVisible' => 'bool', - 'consoleTab' => 'text64', 'conduitCertificate' => 'text255', 'isSystemAgent' => 'bool', 'isMailingList' => 'bool', diff --git a/src/applications/settings/setting/PhabricatorDarkConsoleTabSetting.php b/src/applications/settings/setting/PhabricatorDarkConsoleTabSetting.php new file mode 100644 index 0000000000..87d66d9546 --- /dev/null +++ b/src/applications/settings/setting/PhabricatorDarkConsoleTabSetting.php @@ -0,0 +1,12 @@ +getUserSetting($setting_tab); + $visible = $user->getUserSetting($setting_visible); + } else { + $tab = null; + $visible = true; + } + return array( // NOTE: We use a generic label here to prevent input reflection // and mitigate compression attacks like BREACH. See discussion in // T3684. 'uri' => pht('Main Request'), - 'selected' => $user ? $user->getConsoleTab() : null, - 'visible' => $user ? (int)$user->getConsoleVisible() : true, + 'selected' => $tab, + 'visible' => $visible, 'headers' => $headers, ); } From 804e7a6e065a6aab79ebc4c1c983ff5528c41d1b Mon Sep 17 00:00:00 2001 From: epriestley Date: Fri, 3 Jun 2016 12:13:07 -0700 Subject: [PATCH 16/67] When a user enables/disables DarkConsole, forget their console UI visibility setting Summary: Ref T4103. This primarily makes sure the console gets turned on when you enable it so you aren't like "where's the console???" Test Plan: Enabled console, saw console. Reviewers: chad Reviewed By: chad Maniphest Tasks: T4103 Differential Revision: https://secure.phabricator.com/D16030 --- .../setting/PhabricatorDarkConsoleSetting.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/applications/settings/setting/PhabricatorDarkConsoleSetting.php b/src/applications/settings/setting/PhabricatorDarkConsoleSetting.php index be78487a89..28ee90788e 100644 --- a/src/applications/settings/setting/PhabricatorDarkConsoleSetting.php +++ b/src/applications/settings/setting/PhabricatorDarkConsoleSetting.php @@ -42,5 +42,17 @@ final class PhabricatorDarkConsoleSetting ); } + public function expandSettingTransaction($object, $xaction) { + // If the user has hidden the DarkConsole UI, forget their setting when + // they enable or disable it. + return array( + $xaction, + $this->newSettingTransaction( + $object, + PhabricatorDarkConsoleVisibleSetting::SETTINGKEY, + 1), + ); + } + } From fc45de29a6fac4cc4d0cd50ef9f7ac854c532729 Mon Sep 17 00:00:00 2001 From: epriestley Date: Fri, 3 Jun 2016 12:23:15 -0700 Subject: [PATCH 17/67] Modernize various menu collapse settings Summary: Ref T4103. Fully modernize the filetree show/hide, durable column show/hide, and profile menu collapse/wide settings. Test Plan: - Toggled filetree on/off, reloaded page, setting stuck. - Same with conpherence column and profile menus. Reviewers: chad Reviewed By: chad Maniphest Tasks: T4103 Differential Revision: https://secure.phabricator.com/D16034 --- src/__phutil_library_map__.php | 6 ++++++ .../view/ConpherenceDurableColumnView.php | 2 +- .../DifferentialRevisionViewController.php | 2 +- .../controller/DiffusionCommitController.php | 2 +- .../search/engine/PhabricatorProfilePanelEngine.php | 3 +-- .../PhabricatorConpherenceColumnVisibleSetting.php | 12 ++++++++++++ .../setting/PhabricatorFiletreeVisibleSetting.php | 12 ++++++++++++ .../PhabricatorProfileMenuCollapsedSetting.php | 12 ++++++++++++ .../settings/storage/PhabricatorUserPreferences.php | 6 ------ src/view/page/PhabricatorStandardPageView.php | 2 +- 10 files changed, 47 insertions(+), 12 deletions(-) create mode 100644 src/applications/settings/setting/PhabricatorConpherenceColumnVisibleSetting.php create mode 100644 src/applications/settings/setting/PhabricatorFiletreeVisibleSetting.php create mode 100644 src/applications/settings/setting/PhabricatorProfileMenuCollapsedSetting.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index d1e5874bd3..11ad46e5bf 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -2144,6 +2144,7 @@ phutil_register_library_map(array( 'PhabricatorConfigVersionsModule' => 'applications/config/module/PhabricatorConfigVersionsModule.php', 'PhabricatorConfigWelcomeController' => 'applications/config/controller/PhabricatorConfigWelcomeController.php', 'PhabricatorConpherenceApplication' => 'applications/conpherence/application/PhabricatorConpherenceApplication.php', + 'PhabricatorConpherenceColumnVisibleSetting' => 'applications/settings/setting/PhabricatorConpherenceColumnVisibleSetting.php', 'PhabricatorConpherenceNotificationsSetting' => 'applications/settings/setting/PhabricatorConpherenceNotificationsSetting.php', 'PhabricatorConpherencePreferencesSettingsPanel' => 'applications/settings/panel/PhabricatorConpherencePreferencesSettingsPanel.php', 'PhabricatorConpherenceThreadPHIDType' => 'applications/conpherence/phid/PhabricatorConpherenceThreadPHIDType.php', @@ -2515,6 +2516,7 @@ phutil_register_library_map(array( 'PhabricatorFilesManagementWorkflow' => 'applications/files/management/PhabricatorFilesManagementWorkflow.php', 'PhabricatorFilesOnDiskBuiltinFile' => 'applications/files/builtin/PhabricatorFilesOnDiskBuiltinFile.php', 'PhabricatorFilesOutboundRequestAction' => 'applications/files/action/PhabricatorFilesOutboundRequestAction.php', + 'PhabricatorFiletreeVisibleSetting' => 'applications/settings/setting/PhabricatorFiletreeVisibleSetting.php', 'PhabricatorFlag' => 'applications/flag/storage/PhabricatorFlag.php', 'PhabricatorFlagAddFlagHeraldAction' => 'applications/flag/herald/PhabricatorFlagAddFlagHeraldAction.php', 'PhabricatorFlagColor' => 'applications/flag/constants/PhabricatorFlagColor.php', @@ -3049,6 +3051,7 @@ phutil_register_library_map(array( 'PhabricatorPolicyTestObject' => 'applications/policy/__tests__/PhabricatorPolicyTestObject.php', 'PhabricatorPolicyType' => 'applications/policy/constants/PhabricatorPolicyType.php', 'PhabricatorPonderApplication' => 'applications/ponder/application/PhabricatorPonderApplication.php', + 'PhabricatorProfileMenuCollapsedSetting' => 'applications/settings/setting/PhabricatorProfileMenuCollapsedSetting.php', 'PhabricatorProfilePanel' => 'applications/search/profilepanel/PhabricatorProfilePanel.php', 'PhabricatorProfilePanelConfiguration' => 'applications/search/storage/PhabricatorProfilePanelConfiguration.php', 'PhabricatorProfilePanelConfigurationQuery' => 'applications/search/query/PhabricatorProfilePanelConfigurationQuery.php', @@ -6692,6 +6695,7 @@ phutil_register_library_map(array( 'PhabricatorConfigVersionsModule' => 'PhabricatorConfigModule', 'PhabricatorConfigWelcomeController' => 'PhabricatorConfigController', 'PhabricatorConpherenceApplication' => 'PhabricatorApplication', + 'PhabricatorConpherenceColumnVisibleSetting' => 'PhabricatorInternalSetting', 'PhabricatorConpherenceNotificationsSetting' => 'PhabricatorSelectSetting', 'PhabricatorConpherencePreferencesSettingsPanel' => 'PhabricatorEditEngineSettingsPanel', 'PhabricatorConpherenceThreadPHIDType' => 'PhabricatorPHIDType', @@ -7126,6 +7130,7 @@ phutil_register_library_map(array( 'PhabricatorFilesManagementWorkflow' => 'PhabricatorManagementWorkflow', 'PhabricatorFilesOnDiskBuiltinFile' => 'PhabricatorFilesBuiltinFile', 'PhabricatorFilesOutboundRequestAction' => 'PhabricatorSystemAction', + 'PhabricatorFiletreeVisibleSetting' => 'PhabricatorInternalSetting', 'PhabricatorFlag' => array( 'PhabricatorFlagDAO', 'PhabricatorPolicyInterface', @@ -7732,6 +7737,7 @@ phutil_register_library_map(array( ), 'PhabricatorPolicyType' => 'PhabricatorPolicyConstants', 'PhabricatorPonderApplication' => 'PhabricatorApplication', + 'PhabricatorProfileMenuCollapsedSetting' => 'PhabricatorInternalSetting', 'PhabricatorProfilePanel' => 'Phobject', 'PhabricatorProfilePanelConfiguration' => array( 'PhabricatorSearchDAO', diff --git a/src/applications/conpherence/view/ConpherenceDurableColumnView.php b/src/applications/conpherence/view/ConpherenceDurableColumnView.php index d892eaf62b..d265b37ff7 100644 --- a/src/applications/conpherence/view/ConpherenceDurableColumnView.php +++ b/src/applications/conpherence/view/ConpherenceDurableColumnView.php @@ -108,7 +108,7 @@ final class ConpherenceDurableColumnView extends AphrontTagView { } protected function getTagContent() { - $column_key = PhabricatorUserPreferences::PREFERENCE_CONPHERENCE_COLUMN; + $column_key = PhabricatorConpherenceColumnVisibleSetting::SETTINGKEY; Javelin::initBehavior( 'durable-column', diff --git a/src/applications/differential/controller/DifferentialRevisionViewController.php b/src/applications/differential/controller/DifferentialRevisionViewController.php index 39fc6ca201..32f9978e4d 100644 --- a/src/applications/differential/controller/DifferentialRevisionViewController.php +++ b/src/applications/differential/controller/DifferentialRevisionViewController.php @@ -381,7 +381,7 @@ final class DifferentialRevisionViewController extends DifferentialController { $nav = null; if ($filetree_on) { - $collapsed_key = PhabricatorUserPreferences::PREFERENCE_NAV_COLLAPSED; + $collapsed_key = PhabricatorFiletreeVisibleSetting::SETTINGKEY; $collapsed_value = $viewer->getUserSetting($collapsed_key); $nav = id(new DifferentialChangesetFileTreeSideNavBuilder()) diff --git a/src/applications/diffusion/controller/DiffusionCommitController.php b/src/applications/diffusion/controller/DiffusionCommitController.php index 6cdca19953..dedad2c7c5 100644 --- a/src/applications/diffusion/controller/DiffusionCommitController.php +++ b/src/applications/diffusion/controller/DiffusionCommitController.php @@ -329,7 +329,7 @@ final class DiffusionCommitController extends DiffusionController { PhabricatorShowFiletreeSetting::SETTINGKEY, PhabricatorShowFiletreeSetting::VALUE_ENABLE_FILETREE); - $pref_collapse = PhabricatorUserPreferences::PREFERENCE_NAV_COLLAPSED; + $pref_collapse = PhabricatorFiletreeVisibleSetting::SETTINGKEY; $collapsed = $viewer->getUserSetting($pref_collapse); $nav = null; diff --git a/src/applications/search/engine/PhabricatorProfilePanelEngine.php b/src/applications/search/engine/PhabricatorProfilePanelEngine.php index 6e2e74f3b6..d3c209ba68 100644 --- a/src/applications/search/engine/PhabricatorProfilePanelEngine.php +++ b/src/applications/search/engine/PhabricatorProfilePanelEngine.php @@ -382,8 +382,7 @@ abstract class PhabricatorProfilePanelEngine extends Phobject { $collapse_id = celerity_generate_unique_node_id(); $viewer = $this->getViewer(); - $collapse_key = - PhabricatorUserPreferences::PREFERENCE_PROFILE_MENU_COLLAPSED; + $collapse_key = PhabricatorProfileMenuCollapsedSetting::SETTINGKEY; $is_collapsed = $viewer->getUserSetting($collapse_key); diff --git a/src/applications/settings/setting/PhabricatorConpherenceColumnVisibleSetting.php b/src/applications/settings/setting/PhabricatorConpherenceColumnVisibleSetting.php new file mode 100644 index 0000000000..132f0920a5 --- /dev/null +++ b/src/applications/settings/setting/PhabricatorConpherenceColumnVisibleSetting.php @@ -0,0 +1,12 @@ +getUserPreference($column_key, false); } From e3f4f051fea261c2f009b5a5d5b2f5b037d5d80b Mon Sep 17 00:00:00 2001 From: epriestley Date: Sat, 4 Jun 2016 08:03:40 -0700 Subject: [PATCH 18/67] Add --purge-user to bin/cache purge Summary: Ref T4103. Provide a CLI mechanism for purging the user cache. Test Plan: - Purged with `--purge-user` and `--purge-all`. - Verified cache table got wiped. Reviewers: chad Reviewed By: chad Maniphest Tasks: T4103 Differential Revision: https://secure.phabricator.com/D16033 --- ...habricatorCacheManagementPurgeWorkflow.php | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/applications/cache/management/PhabricatorCacheManagementPurgeWorkflow.php b/src/applications/cache/management/PhabricatorCacheManagementPurgeWorkflow.php index 160fe4f5db..91ab0cf83d 100644 --- a/src/applications/cache/management/PhabricatorCacheManagementPurgeWorkflow.php +++ b/src/applications/cache/management/PhabricatorCacheManagementPurgeWorkflow.php @@ -26,6 +26,10 @@ final class PhabricatorCacheManagementPurgeWorkflow 'name' => 'purge-general', 'help' => pht('Purge the general cache.'), ), + array( + 'name' => 'purge-user', + 'help' => pht('Purge the user cache.'), + ), )); } @@ -38,6 +42,7 @@ final class PhabricatorCacheManagementPurgeWorkflow 'remarkup' => $purge_all || $args->getArg('purge-remarkup'), 'changeset' => $purge_all || $args->getArg('purge-changeset'), 'general' => $purge_all || $args->getArg('purge-general'), + 'user' => $purge_all || $args->getArg('purge-user'), ); if (!array_filter($purge)) { @@ -72,6 +77,12 @@ final class PhabricatorCacheManagementPurgeWorkflow $this->purgeGeneralCache(); $console->writeOut("%s\n", pht('Done.')); } + + if ($purge['user']) { + $console->writeOut(pht('Purging user cache...')); + $this->purgeUserCache(); + $console->writeOut("%s\n", pht('Done.')); + } } private function purgeRemarkupCache() { @@ -100,4 +111,14 @@ final class PhabricatorCacheManagementPurgeWorkflow 'cache_general'); } + private function purgeUserCache() { + $table = new PhabricatorUserCache(); + $conn_w = $table->establishConnection('w'); + + queryfx( + $conn_w, + 'TRUNCATE TABLE %T', + $table->getTableName()); + } + } From 6199e95577c9a87578c00240c7f7df36bd2d31fd Mon Sep 17 00:00:00 2001 From: epriestley Date: Fri, 3 Jun 2016 06:55:17 -0700 Subject: [PATCH 19/67] Use transactions to apply Ajax settings mutations Summary: Ref T4103. Some settings (mostly nav collapsed/expanded states) use this endpoint to make adjustments when users press keys (like `\` to toggle the durable column). All of these settings are now formal, so swap things over to transactions. Test Plan: Collapsed/expanded various navs, reloaded pages, settings stuck. Reviewers: chad Reviewed By: chad Maniphest Tasks: T4103 Differential Revision: https://secure.phabricator.com/D16035 --- .../PhabricatorSettingsAdjustController.php | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/applications/settings/controller/PhabricatorSettingsAdjustController.php b/src/applications/settings/controller/PhabricatorSettingsAdjustController.php index 7211431f16..277835a9ef 100644 --- a/src/applications/settings/controller/PhabricatorSettingsAdjustController.php +++ b/src/applications/settings/controller/PhabricatorSettingsAdjustController.php @@ -4,13 +4,23 @@ final class PhabricatorSettingsAdjustController extends PhabricatorController { public function handleRequest(AphrontRequest $request) { - $user = $request->getUser(); + $viewer = $this->getViewer(); - $prefs = $user->loadPreferences(); - $prefs->setPreference( - $request->getStr('key'), - $request->getStr('value')); - $prefs->save(); + $preferences = PhabricatorUserPreferences::loadUserPreferences($viewer); + + $editor = id(new PhabricatorUserPreferencesEditor()) + ->setActor($viewer) + ->setContentSourceFromRequest($request) + ->setContinueOnNoEffect(true) + ->setContinueOnMissingFields(true); + + $key = $request->getStr('key'); + $value = $request->getStr('value'); + + $xactions = array(); + $xactions[] = $preferences->newTransaction($key, $value); + + $editor->applyTransactions($preferences, $xactions); return id(new AphrontAjaxResponse())->setContent(array()); } From 9d7c28625240f8a0846bf8333218965945469353 Mon Sep 17 00:00:00 2001 From: epriestley Date: Sat, 4 Jun 2016 15:01:28 -0700 Subject: [PATCH 20/67] Allow users to turn off desktop notifications Summary: Fixes T8846. Ref T4103. I just took the shortest reasonable path here, this panel could use some attention on the next Conpherence iteration. Test Plan: Turned on/off desktop notifications. Observed corresponding behavior in test notifications. Reviewers: chad Reviewed By: chad Maniphest Tasks: T4103, T8846 Differential Revision: https://secure.phabricator.com/D16036 --- src/__phutil_library_map__.php | 2 ++ .../PhabricatorNotificationBuilder.php | 8 +++++-- ...icatorNotificationIndividualController.php | 4 +++- ...PhabricatorNotificationPanelController.php | 4 +++- ...catorDesktopNotificationsSettingsPanel.php | 22 +++++++++++-------- ...habricatorHomePreferencesSettingsPanel.php | 13 +---------- .../panel/PhabricatorSettingsPanel.php | 18 +++++++++++++++ ...PhabricatorDesktopNotificationsSetting.php | 12 ++++++++++ .../storage/PhabricatorUserPreferences.php | 2 -- 9 files changed, 58 insertions(+), 27 deletions(-) create mode 100644 src/applications/settings/setting/PhabricatorDesktopNotificationsSetting.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 11ad46e5bf..b42577f211 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -2305,6 +2305,7 @@ phutil_register_library_map(array( 'PhabricatorDebugController' => 'applications/system/controller/PhabricatorDebugController.php', 'PhabricatorDefaultRequestExceptionHandler' => 'aphront/handler/PhabricatorDefaultRequestExceptionHandler.php', 'PhabricatorDefaultSyntaxStyle' => 'infrastructure/syntax/PhabricatorDefaultSyntaxStyle.php', + 'PhabricatorDesktopNotificationsSetting' => 'applications/settings/setting/PhabricatorDesktopNotificationsSetting.php', 'PhabricatorDesktopNotificationsSettingsPanel' => 'applications/settings/panel/PhabricatorDesktopNotificationsSettingsPanel.php', 'PhabricatorDestructibleInterface' => 'applications/system/interface/PhabricatorDestructibleInterface.php', 'PhabricatorDestructionEngine' => 'applications/system/engine/PhabricatorDestructionEngine.php', @@ -6885,6 +6886,7 @@ phutil_register_library_map(array( 'PhabricatorDebugController' => 'PhabricatorController', 'PhabricatorDefaultRequestExceptionHandler' => 'PhabricatorRequestExceptionHandler', 'PhabricatorDefaultSyntaxStyle' => 'PhabricatorSyntaxStyle', + 'PhabricatorDesktopNotificationsSetting' => 'PhabricatorInternalSetting', 'PhabricatorDesktopNotificationsSettingsPanel' => 'PhabricatorSettingsPanel', 'PhabricatorDestructionEngine' => 'Phobject', 'PhabricatorDestructionEngineExtension' => 'Phobject', diff --git a/src/applications/notification/builder/PhabricatorNotificationBuilder.php b/src/applications/notification/builder/PhabricatorNotificationBuilder.php index dd4e19dcfb..a8af4ff00e 100644 --- a/src/applications/notification/builder/PhabricatorNotificationBuilder.php +++ b/src/applications/notification/builder/PhabricatorNotificationBuilder.php @@ -131,10 +131,14 @@ final class PhabricatorNotificationBuilder extends Phobject { $stories = $this->parseStories(); $dict = array(); + $viewer = $this->user; + $desktop_key = PhabricatorDesktopNotificationsSetting::SETTINGKEY; + $desktop_enabled = $viewer->getUserSetting($desktop_key); + foreach ($stories as $story) { if ($story instanceof PhabricatorApplicationTransactionFeedStory) { $dict[] = array( - 'desktopReady' => true, + 'desktopReady' => $desktop_enabled, 'title' => $story->renderText(), 'body' => $story->renderTextBody(), 'href' => $story->getURI(), @@ -142,7 +146,7 @@ final class PhabricatorNotificationBuilder extends Phobject { ); } else if ($story instanceof PhabricatorNotificationTestFeedStory) { $dict[] = array( - 'desktopReady' => true, + 'desktopReady' => $desktop_enabled, 'title' => pht('Test Notification'), 'body' => $story->renderText(), 'href' => null, diff --git a/src/applications/notification/controller/PhabricatorNotificationIndividualController.php b/src/applications/notification/controller/PhabricatorNotificationIndividualController.php index 68214d1965..bfaebaf606 100644 --- a/src/applications/notification/controller/PhabricatorNotificationIndividualController.php +++ b/src/applications/notification/controller/PhabricatorNotificationIndividualController.php @@ -30,7 +30,9 @@ final class PhabricatorNotificationIndividualController return $this->buildEmptyResponse(); } - $builder = new PhabricatorNotificationBuilder(array($story)); + $builder = id(new PhabricatorNotificationBuilder(array($story))) + ->setUser($viewer); + $content = $builder->buildView()->render(); $dict = $builder->buildDict(); $data = $dict[0]; diff --git a/src/applications/notification/controller/PhabricatorNotificationPanelController.php b/src/applications/notification/controller/PhabricatorNotificationPanelController.php index be68cc2de7..c9c3d9b94a 100644 --- a/src/applications/notification/controller/PhabricatorNotificationPanelController.php +++ b/src/applications/notification/controller/PhabricatorNotificationPanelController.php @@ -16,7 +16,9 @@ final class PhabricatorNotificationPanelController $clear_ui_class = 'phabricator-notification-clear-all'; $clear_uri = id(new PhutilURI('/notification/clear/')); if ($stories) { - $builder = new PhabricatorNotificationBuilder($stories); + $builder = id(new PhabricatorNotificationBuilder($stories)) + ->setUser($viewer); + $notifications_view = $builder->buildView(); $content = $notifications_view->render(); $clear_uri->setQueryParam( diff --git a/src/applications/settings/panel/PhabricatorDesktopNotificationsSettingsPanel.php b/src/applications/settings/panel/PhabricatorDesktopNotificationsSettingsPanel.php index 65beabec6a..207d2300b2 100644 --- a/src/applications/settings/panel/PhabricatorDesktopNotificationsSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorDesktopNotificationsSettingsPanel.php @@ -26,15 +26,19 @@ final class PhabricatorDesktopNotificationsSettingsPanel } public function processRequest(AphrontRequest $request) { - $user = $request->getUser(); - $preferences = $user->loadPreferences(); + $viewer = $this->getViewer(); + $preferences = $this->loadTargetPreferences(); - $pref = PhabricatorUserPreferences::PREFERENCE_DESKTOP_NOTIFICATIONS; + $notifications_key = PhabricatorDesktopNotificationsSetting::SETTINGKEY; + $notifications_value = $preferences->getSettingValue($notifications_key); if ($request->isFormPost()) { - $notifications = $request->getInt($pref); - $preferences->setPreference($pref, $notifications); - $preferences->save(); + + $this->writeSetting( + $preferences, + $notifications_key, + $request->getInt($notifications_key)); + return id(new AphrontRedirectResponse()) ->setURI($this->getPanelURI('?saved=true')); } @@ -106,13 +110,13 @@ final class PhabricatorDesktopNotificationsSettingsPanel ); $form = id(new AphrontFormView()) - ->setUser($user) + ->setUser($viewer) ->appendChild( id(new AphrontFormSelectControl()) ->setLabel($title) ->setControlID($control_id) - ->setName($pref) - ->setValue($preferences->getPreference($pref)) + ->setName($notifications_key) + ->setValue($notifications_value) ->setOptions( array( 1 => pht('Send Desktop Notifications Too'), diff --git a/src/applications/settings/panel/PhabricatorHomePreferencesSettingsPanel.php b/src/applications/settings/panel/PhabricatorHomePreferencesSettingsPanel.php index 5369a2b90c..4aa45abdb5 100644 --- a/src/applications/settings/panel/PhabricatorHomePreferencesSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorHomePreferencesSettingsPanel.php @@ -208,19 +208,8 @@ final class PhabricatorHomePreferencesSettingsPanel PhabricatorUserPreferences $preferences, $pinned) { - $viewer = $this->getViewer(); - $request = $this->getController()->getRequest(); $pinned_key = PhabricatorPinnedApplicationsSetting::SETTINGKEY; - - $editor = id(new PhabricatorUserPreferencesEditor()) - ->setActor($viewer) - ->setContentSourceFromRequest($request) - ->setContinueOnNoEffect(true) - ->setContinueOnMissingFields(true); - - $xactions = array(); - $xactions[] = $preferences->newTransaction($pinned_key, $pinned); - $editor->applyTransactions($preferences, $xactions); + $this->writeSetting($preferences, $pinned_key, $pinned); } } diff --git a/src/applications/settings/panel/PhabricatorSettingsPanel.php b/src/applications/settings/panel/PhabricatorSettingsPanel.php index 86925bf4a7..e2cdc91f00 100644 --- a/src/applications/settings/panel/PhabricatorSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorSettingsPanel.php @@ -235,4 +235,22 @@ abstract class PhabricatorSettingsPanel extends Phobject { return $this->getController()->newDialog(); } + protected function writeSetting( + PhabricatorUserPreferences $preferences, + $key, + $value) { + $viewer = $this->getViewer(); + $request = $this->getController()->getRequest(); + + $editor = id(new PhabricatorUserPreferencesEditor()) + ->setActor($viewer) + ->setContentSourceFromRequest($request) + ->setContinueOnNoEffect(true) + ->setContinueOnMissingFields(true); + + $xactions = array(); + $xactions[] = $preferences->newTransaction($key, $value); + $editor->applyTransactions($preferences, $xactions); + } + } diff --git a/src/applications/settings/setting/PhabricatorDesktopNotificationsSetting.php b/src/applications/settings/setting/PhabricatorDesktopNotificationsSetting.php new file mode 100644 index 0000000000..f590d37325 --- /dev/null +++ b/src/applications/settings/setting/PhabricatorDesktopNotificationsSetting.php @@ -0,0 +1,12 @@ + Date: Sat, 4 Jun 2016 15:08:30 -0700 Subject: [PATCH 21/67] Resolve timezone conflicts in a modern transactional way Summary: Ref T4103. Also get rid of the weird cache clear that nothing else uses and which we don't actually need. Test Plan: - Resolved timezone conflict by ignoring it. - Resolved timezone conflict by picking a valid timezone. Reviewers: chad Reviewed By: chad Maniphest Tasks: T4103 Differential Revision: https://secure.phabricator.com/D16037 --- .../people/storage/PhabricatorUser.php | 14 ------ .../PhabricatorSettingsTimezoneController.php | 43 ++++++++++++++----- 2 files changed, 32 insertions(+), 25 deletions(-) diff --git a/src/applications/people/storage/PhabricatorUser.php b/src/applications/people/storage/PhabricatorUser.php index 0640f3c1b2..3cfa7bd2bc 100644 --- a/src/applications/people/storage/PhabricatorUser.php +++ b/src/applications/people/storage/PhabricatorUser.php @@ -533,20 +533,6 @@ final class PhabricatorUser return ($actual == $value); } - - /** - * @task settings - */ - public function clearUserSettingCache() { - $this->settingCacheKeys = array(); - $this->settingCache = array(); - - $settings_key = PhabricatorUserPreferencesCacheType::KEY_PREFERENCES; - $this->clearCacheData($settings_key); - - return $this; - } - private function writeUserSettingCache($key, $value) { $this->settingCacheKeys[$key] = true; $this->settingCache[$key] = $value; diff --git a/src/applications/settings/controller/PhabricatorSettingsTimezoneController.php b/src/applications/settings/controller/PhabricatorSettingsTimezoneController.php index 4f34c39fa0..2e2e174e0f 100644 --- a/src/applications/settings/controller/PhabricatorSettingsTimezoneController.php +++ b/src/applications/settings/controller/PhabricatorSettingsTimezoneController.php @@ -27,18 +27,18 @@ final class PhabricatorSettingsTimezoneController $settings_help = pht( 'You can change your date and time preferences in Settings.'); + $did_calibrate = false; if ($request->isFormPost()) { $timezone = $request->getStr('timezone'); $pref_ignore = PhabricatorTimezoneIgnoreOffsetSetting::SETTINGKEY; $pref_timezone = PhabricatorTimezoneSetting::SETTINGKEY; - $preferences = $viewer->loadPreferences(); - if ($timezone == 'ignore') { - $preferences - ->setPreference($pref_ignore, $client_offset) - ->save(); + $this->writeSettings( + array( + $pref_ignore => $client_offset, + )); return $this->newDialog() ->setTitle(pht('Conflict Ignored')) @@ -51,18 +51,19 @@ final class PhabricatorSettingsTimezoneController } if (isset($options[$timezone])) { - $preferences - ->setPreference($pref_ignore, null) - ->setPreference($pref_timezone, $timezone) - ->save(); + $this->writeSettings( + array( + $pref_ignore => null, + $pref_timezone => $timezone, + )); - $viewer->clearUserSettingCache(); + $did_calibrate = true; } } $server_offset = $viewer->getTimeZoneOffset(); - if ($client_offset == $server_offset) { + if ($client_offset == $server_offset || $did_calibrate) { return $this->newDialog() ->setTitle(pht('Timezone Calibrated')) ->appendParagraph( @@ -121,4 +122,24 @@ final class PhabricatorSettingsTimezoneController } } + private function writeSettings(array $map) { + $request = $this->getRequest(); + $viewer = $this->getViewer(); + + $preferences = PhabricatorUserPreferences::loadUserPreferences($viewer); + + $editor = id(new PhabricatorUserPreferencesEditor()) + ->setActor($viewer) + ->setContentSourceFromRequest($request) + ->setContinueOnNoEffect(true) + ->setContinueOnMissingFields(true); + + $xactions = array(); + foreach ($map as $key => $value) { + $xactions[] = $preferences->newTransaction($key, $value); + } + + $editor->applyTransactions($preferences, $xactions); + } + } From 7ef6c0a523fcb41f8bbc2a639eccd8e19289f868 Mon Sep 17 00:00:00 2001 From: epriestley Date: Sat, 4 Jun 2016 15:16:12 -0700 Subject: [PATCH 22/67] Modularize all the mail preferences Summary: Ref T4103. This isn't completely perfect but should let us move forward without also expanding scope into "too much mail". I split the existing "Mail Preferences" into two panels: a "Mail Delivery" panel for the EditEngine settings, and a "2000000 dropdowns" panel for the two million dropdowns. This one retains the old code more or less unmodified. Test Plan: - Ran unit tests, which cover most of this stuff. - Grepped for all removed constants. - Ran migrations, inspected database results. - Changed settings in both modified panels. - This covers a lot of ground, but anything I missed will hopefully be fairly obvious. Reviewers: chad Reviewed By: chad Maniphest Tasks: T4103 Differential Revision: https://secure.phabricator.com/D16038 --- .../20160604.user.01.stringmailprefs.php | 47 ++++++ src/__phutil_library_map__.php | 4 + .../feed/PhabricatorFeedStoryPublisher.php | 9 +- .../storage/PhabricatorMetaMTAMail.php | 142 ++++++++++-------- .../PhabricatorMetaMTAMailTestCase.php | 60 +++++--- .../PhabricatorEmailDeliverySettingsPanel.php | 24 +++ ...abricatorEmailPreferencesSettingsPanel.php | 81 ++-------- .../setting/PhabricatorEmailFormatSetting.php | 4 +- .../PhabricatorEmailNotificationsSetting.php | 8 + .../PhabricatorEmailRePrefixSetting.php | 4 +- .../PhabricatorEmailSelfActionsSetting.php | 8 + .../setting/PhabricatorEmailTagsSetting.php | 21 +++ .../PhabricatorEmailVarySubjectsSetting.php | 6 +- .../storage/PhabricatorUserPreferences.php | 12 -- 14 files changed, 258 insertions(+), 172 deletions(-) create mode 100644 resources/sql/autopatches/20160604.user.01.stringmailprefs.php create mode 100644 src/applications/settings/panel/PhabricatorEmailDeliverySettingsPanel.php create mode 100644 src/applications/settings/setting/PhabricatorEmailTagsSetting.php diff --git a/resources/sql/autopatches/20160604.user.01.stringmailprefs.php b/resources/sql/autopatches/20160604.user.01.stringmailprefs.php new file mode 100644 index 0000000000..791177d8a3 --- /dev/null +++ b/resources/sql/autopatches/20160604.user.01.stringmailprefs.php @@ -0,0 +1,47 @@ +establishConnection('w'); + +// Convert "Mail Format", "Re Prefix" and "Vary Subjects" mail settings to +// string constants to avoid weird stuff where we store "true" and "false" as +// strings in the database. + +// Each of these keys will be converted to the first value if present and +// truthy, or the second value if present and falsey. +$remap = array( + 'html-emails' => array('html', 'text'), + 're-prefix' => array('re', 'none'), + 'vary-subject' => array('vary', 'static'), +); + +foreach (new LiskMigrationIterator($table) as $row) { + $dict = $row->getPreferences(); + + $should_update = false; + foreach ($remap as $key => $value) { + if (isset($dict[$key])) { + if ($dict[$key]) { + $dict[$key] = $value[0]; + } else { + $dict[$key] = $value[1]; + } + $should_update = true; + } + } + + if (!$should_update) { + continue; + } + + queryfx( + $conn_w, + 'UPDATE %T SET preferences = %s WHERE id = %d', + $table->getTableName(), + phutil_json_encode($dict), + $row->getID()); +} + +$prefs_key = PhabricatorUserPreferencesCacheType::KEY_PREFERENCES; +PhabricatorUserCache::clearCacheForAllUsers($prefs_key); diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index b42577f211..e936049317 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -2389,6 +2389,7 @@ phutil_register_library_map(array( 'PhabricatorElasticSearchSetupCheck' => 'applications/config/check/PhabricatorElasticSearchSetupCheck.php', 'PhabricatorEmailAddressesSettingsPanel' => 'applications/settings/panel/PhabricatorEmailAddressesSettingsPanel.php', 'PhabricatorEmailContentSource' => 'applications/metamta/contentsource/PhabricatorEmailContentSource.php', + 'PhabricatorEmailDeliverySettingsPanel' => 'applications/settings/panel/PhabricatorEmailDeliverySettingsPanel.php', 'PhabricatorEmailFormatSetting' => 'applications/settings/setting/PhabricatorEmailFormatSetting.php', 'PhabricatorEmailFormatSettingsPanel' => 'applications/settings/panel/PhabricatorEmailFormatSettingsPanel.php', 'PhabricatorEmailLoginController' => 'applications/auth/controller/PhabricatorEmailLoginController.php', @@ -2396,6 +2397,7 @@ phutil_register_library_map(array( 'PhabricatorEmailPreferencesSettingsPanel' => 'applications/settings/panel/PhabricatorEmailPreferencesSettingsPanel.php', 'PhabricatorEmailRePrefixSetting' => 'applications/settings/setting/PhabricatorEmailRePrefixSetting.php', 'PhabricatorEmailSelfActionsSetting' => 'applications/settings/setting/PhabricatorEmailSelfActionsSetting.php', + 'PhabricatorEmailTagsSetting' => 'applications/settings/setting/PhabricatorEmailTagsSetting.php', 'PhabricatorEmailVarySubjectsSetting' => 'applications/settings/setting/PhabricatorEmailVarySubjectsSetting.php', 'PhabricatorEmailVerificationController' => 'applications/auth/controller/PhabricatorEmailVerificationController.php', 'PhabricatorEmbedFileRemarkupRule' => 'applications/files/markup/PhabricatorEmbedFileRemarkupRule.php', @@ -6976,6 +6978,7 @@ phutil_register_library_map(array( 'PhabricatorElasticSearchSetupCheck' => 'PhabricatorSetupCheck', 'PhabricatorEmailAddressesSettingsPanel' => 'PhabricatorSettingsPanel', 'PhabricatorEmailContentSource' => 'PhabricatorContentSource', + 'PhabricatorEmailDeliverySettingsPanel' => 'PhabricatorEditEngineSettingsPanel', 'PhabricatorEmailFormatSetting' => 'PhabricatorSelectSetting', 'PhabricatorEmailFormatSettingsPanel' => 'PhabricatorEditEngineSettingsPanel', 'PhabricatorEmailLoginController' => 'PhabricatorAuthController', @@ -6983,6 +6986,7 @@ phutil_register_library_map(array( 'PhabricatorEmailPreferencesSettingsPanel' => 'PhabricatorSettingsPanel', 'PhabricatorEmailRePrefixSetting' => 'PhabricatorSelectSetting', 'PhabricatorEmailSelfActionsSetting' => 'PhabricatorSelectSetting', + 'PhabricatorEmailTagsSetting' => 'PhabricatorInternalSetting', 'PhabricatorEmailVarySubjectsSetting' => 'PhabricatorSelectSetting', 'PhabricatorEmailVerificationController' => 'PhabricatorAuthController', 'PhabricatorEmbedFileRemarkupRule' => 'PhabricatorObjectRemarkupRule', diff --git a/src/applications/feed/PhabricatorFeedStoryPublisher.php b/src/applications/feed/PhabricatorFeedStoryPublisher.php index be476012c3..e4b62fdf29 100644 --- a/src/applications/feed/PhabricatorFeedStoryPublisher.php +++ b/src/applications/feed/PhabricatorFeedStoryPublisher.php @@ -214,8 +214,8 @@ final class PhabricatorFeedStoryPublisher extends Phobject { $all_prefs = mpull($all_prefs, null, 'getUserPHID'); } - $pref_default = PhabricatorUserPreferences::MAILTAG_PREFERENCE_EMAIL; - $pref_ignore = PhabricatorUserPreferences::MAILTAG_PREFERENCE_IGNORE; + $pref_default = PhabricatorEmailTagsSetting::VALUE_EMAIL; + $pref_ignore = PhabricatorEmailTagsSetting::VALUE_IGNORE; $keep = array(); foreach ($phids as $phid) { @@ -224,9 +224,8 @@ final class PhabricatorFeedStoryPublisher extends Phobject { } if ($tags && isset($all_prefs[$phid])) { - $mailtags = $all_prefs[$phid]->getPreference( - PhabricatorUserPreferences::PREFERENCE_MAILTAGS, - array()); + $mailtags = $all_prefs[$phid]->getSettingValue( + PhabricatorEmailTagsSetting::SETTINGKEY); $notify = false; foreach ($tags as $tag) { diff --git a/src/applications/metamta/storage/PhabricatorMetaMTAMail.php b/src/applications/metamta/storage/PhabricatorMetaMTAMail.php index 9fb84aaa4f..dcc14f0cc1 100644 --- a/src/applications/metamta/storage/PhabricatorMetaMTAMail.php +++ b/src/applications/metamta/storage/PhabricatorMetaMTAMail.php @@ -435,32 +435,17 @@ final class PhabricatorMetaMTAMail $add_cc = array(); $add_to = array(); - // Only try to use preferences if everything is multiplexed, so we - // get consistent behavior. - $use_prefs = self::shouldMultiplexAllMail(); - - $prefs = null; - if ($use_prefs) { - - // If multiplexing is enabled, some recipients will be in "Cc" - // rather than "To". We'll move them to "To" later (or supply a - // dummy "To") but need to look for the recipient in either the - // "To" or "Cc" fields here. - $target_phid = head(idx($params, 'to', array())); - if (!$target_phid) { - $target_phid = head(idx($params, 'cc', array())); - } - - if ($target_phid) { - $user = id(new PhabricatorUser())->loadOneWhere( - 'phid = %s', - $target_phid); - if ($user) { - $prefs = $user->loadPreferences(); - } - } + // If multiplexing is enabled, some recipients will be in "Cc" + // rather than "To". We'll move them to "To" later (or supply a + // dummy "To") but need to look for the recipient in either the + // "To" or "Cc" fields here. + $target_phid = head(idx($params, 'to', array())); + if (!$target_phid) { + $target_phid = head(idx($params, 'cc', array())); } + $preferences = $this->loadPreferences($target_phid); + foreach ($params as $key => $value) { switch ($key) { case 'raw-from': @@ -526,15 +511,7 @@ final class PhabricatorMetaMTAMail $subject = array(); if ($is_threaded) { - $add_re = PhabricatorEnv::getEnvConfig('metamta.re-prefix'); - - if ($prefs) { - $add_re = $prefs->getPreference( - PhabricatorUserPreferences::PREFERENCE_RE_PREFIX, - $add_re); - } - - if ($add_re) { + if ($this->shouldAddRePrefix($preferences)) { $subject[] = 'Re:'; } } @@ -543,16 +520,7 @@ final class PhabricatorMetaMTAMail $vary_prefix = idx($params, 'vary-subject-prefix'); if ($vary_prefix != '') { - $use_subject = PhabricatorEnv::getEnvConfig( - 'metamta.vary-subjects'); - - if ($prefs) { - $use_subject = $prefs->getPreference( - PhabricatorUserPreferences::PREFERENCE_VARY_SUBJECT, - $use_subject); - } - - if ($use_subject) { + if ($this->shouldVarySubject($preferences)) { $subject[] = $vary_prefix; } } @@ -607,13 +575,7 @@ final class PhabricatorMetaMTAMail } $mailer->setBody($body); - $html_emails = true; - if ($use_prefs && $prefs) { - $html_emails = $prefs->getPreference( - PhabricatorUserPreferences::PREFERENCE_HTML_EMAILS, - $html_emails); - } - + $html_emails = $this->shouldSendHTML($preferences); if ($html_emails && isset($params['html-body'])) { $mailer->setHTMLBody($params['html-body']); } @@ -900,13 +862,12 @@ final class PhabricatorMetaMTAMail $from_user = id(new PhabricatorPeopleQuery()) ->setViewer($viewer) ->withPHIDs(array($from_phid)) + ->needUserSettings(true) ->execute(); $from_user = head($from_user); if ($from_user) { - $pref_key = PhabricatorUserPreferences::PREFERENCE_NO_SELF_MAIL; - $exclude_self = $from_user - ->loadPreferences() - ->getPreference($pref_key); + $pref_key = PhabricatorEmailSelfActionsSetting::SETTINGKEY; + $exclude_self = $from_user->getUserSetting($pref_key); if ($exclude_self) { $from_actor->setUndeliverable(PhabricatorMetaMTAActor::REASON_SELF); } @@ -919,7 +880,7 @@ final class PhabricatorMetaMTAMail ->execute(); $all_prefs = mpull($all_prefs, null, 'getUserPHID'); - $value_email = PhabricatorUserPreferences::MAILTAG_PREFERENCE_EMAIL; + $value_email = PhabricatorEmailTagsSetting::VALUE_EMAIL; // Exclude all recipients who have set preferences to not receive this type // of email (for example, a user who says they don't want emails about task @@ -927,9 +888,8 @@ final class PhabricatorMetaMTAMail $tags = $this->getParam('mailtags'); if ($tags) { foreach ($all_prefs as $phid => $prefs) { - $user_mailtags = $prefs->getPreference( - PhabricatorUserPreferences::PREFERENCE_MAILTAGS, - array()); + $user_mailtags = $prefs->getSettingValue( + PhabricatorEmailTagsSetting::SETTINGKEY); // The user must have elected to receive mail for at least one // of the mailtags. @@ -982,9 +942,8 @@ final class PhabricatorMetaMTAMail // Exclude recipients who don't want any mail. This rule is very strong // and runs last. foreach ($all_prefs as $phid => $prefs) { - $exclude = $prefs->getPreference( - PhabricatorUserPreferences::PREFERENCE_NO_MAIL, - false); + $exclude = $prefs->getSettingValue( + PhabricatorEmailNotificationsSetting::SETTINGKEY); if ($exclude) { $actors[$phid]->setUndeliverable( PhabricatorMetaMTAActor::REASON_MAIL_DISABLED); @@ -1142,6 +1101,67 @@ final class PhabricatorMetaMTAMail return $this->routingMap; } +/* -( Preferences )-------------------------------------------------------- */ + + + private function loadPreferences($target_phid) { + if (!self::shouldMultiplexAllMail()) { + $target_phid = null; + } + + if ($target_phid) { + $preferences = id(new PhabricatorUserPreferencesQuery()) + ->setViewer(PhabricatorUser::getOmnipotentUser()) + ->withUserPHIDs(array($target_phid)) + ->executeOne(); + } else { + $preferences = null; + } + + // TODO: Here, we would load global preferences once they exist. + + if (!$preferences) { + // If we haven't found suitable preferences yet, return an empty object + // which implicitly has all the default values. + $preferences = id(new PhabricatorUserPreferences()) + ->attachUser(new PhabricatorUser()); + } + + return $preferences; + } + + private function shouldAddRePrefix(PhabricatorUserPreferences $preferences) { + $default_value = PhabricatorEnv::getEnvConfig('metamta.re-prefix'); + + $value = $preferences->getPreference( + PhabricatorEmailRePrefixSetting::SETTINGKEY); + if ($value === null) { + return $default_value; + } + + return ($value == PhabricatorEmailRePrefixSetting::VALUE_RE_PREFIX); + } + + private function shouldVarySubject(PhabricatorUserPreferences $preferences) { + $default_value = PhabricatorEnv::getEnvConfig('metamta.vary-subjects'); + + $value = $preferences->getPreference( + PhabricatorEmailVarySubjectsSetting::SETTINGKEY); + + if ($value === null) { + return $default_value; + } + + return ($value == PhabricatorEmailVarySubjectsSetting::VALUE_VARY_SUBJECTS); + } + + private function shouldSendHTML(PhabricatorUserPreferences $preferences) { + $value = $preferences->getSettingValue( + PhabricatorEmailFormatSetting::SETTINGKEY); + + return ($value == PhabricatorEmailFormatSetting::VALUE_HTML_EMAIL); + } + /* -( PhabricatorPolicyInterface )----------------------------------------- */ diff --git a/src/applications/metamta/storage/__tests__/PhabricatorMetaMTAMailTestCase.php b/src/applications/metamta/storage/__tests__/PhabricatorMetaMTAMailTestCase.php index 952d7808da..2f892eb084 100644 --- a/src/applications/metamta/storage/__tests__/PhabricatorMetaMTAMailTestCase.php +++ b/src/applications/metamta/storage/__tests__/PhabricatorMetaMTAMailTestCase.php @@ -60,8 +60,6 @@ final class PhabricatorMetaMTAMailTestCase extends PhabricatorTestCase { $user = $this->generateNewTestUser(); $phid = $user->getPHID(); - $prefs = $user->loadPreferences(); - $mailer = new PhabricatorMailImplementationTestAdapter(); $mail = new PhabricatorMetaMTAMail(); @@ -79,27 +77,28 @@ final class PhabricatorMetaMTAMailTestCase extends PhabricatorTestCase { in_array($phid, $mail->buildRecipientList()), pht('"From" does not exclude recipients by default.')); - $prefs->setPreference( - PhabricatorUserPreferences::PREFERENCE_NO_SELF_MAIL, + $user = $this->writeSetting( + $user, + PhabricatorEmailSelfActionsSetting::SETTINGKEY, true); - $prefs->save(); $this->assertFalse( in_array($phid, $mail->buildRecipientList()), pht('"From" excludes recipients with no-self-mail set.')); - $prefs->unsetPreference( - PhabricatorUserPreferences::PREFERENCE_NO_SELF_MAIL); - $prefs->save(); + $user = $this->writeSetting( + $user, + PhabricatorEmailSelfActionsSetting::SETTINGKEY, + null); $this->assertTrue( in_array($phid, $mail->buildRecipientList()), pht('"From" does not exclude recipients by default.')); - $prefs->setPreference( - PhabricatorUserPreferences::PREFERENCE_NO_MAIL, + $user = $this->writeSetting( + $user, + PhabricatorEmailNotificationsSetting::SETTINGKEY, true); - $prefs->save(); $this->assertFalse( in_array($phid, $mail->buildRecipientList()), @@ -113,15 +112,15 @@ final class PhabricatorMetaMTAMailTestCase extends PhabricatorTestCase { $mail->setForceDelivery(false); - $prefs->unsetPreference( - PhabricatorUserPreferences::PREFERENCE_NO_MAIL); - $prefs->save(); + $user = $this->writeSetting( + $user, + PhabricatorEmailNotificationsSetting::SETTINGKEY, + null); $this->assertTrue( in_array($phid, $mail->buildRecipientList()), pht('"From" does not exclude recipients by default.')); - // Test that explicit exclusion works correctly. $mail->setExcludeMailRecipientPHIDs(array($phid)); @@ -133,12 +132,12 @@ final class PhabricatorMetaMTAMailTestCase extends PhabricatorTestCase { // Test that mail tag preferences exclude recipients. - $prefs->setPreference( - PhabricatorUserPreferences::PREFERENCE_MAILTAGS, + $user = $this->writeSetting( + $user, + PhabricatorEmailTagsSetting::SETTINGKEY, array( 'test-tag' => false, )); - $prefs->save(); $mail->setMailTags(array('test-tag')); @@ -146,8 +145,10 @@ final class PhabricatorMetaMTAMailTestCase extends PhabricatorTestCase { in_array($phid, $mail->buildRecipientList()), pht('Tag preference excludes recipients.')); - $prefs->unsetPreference(PhabricatorUserPreferences::PREFERENCE_MAILTAGS); - $prefs->save(); + $user = $this->writeSetting( + $user, + PhabricatorEmailTagsSetting::SETTINGKEY, + null); $this->assertTrue( in_array($phid, $mail->buildRecipientList()), @@ -215,4 +216,23 @@ final class PhabricatorMetaMTAMailTestCase extends PhabricatorTestCase { $case)); } + private function writeSetting(PhabricatorUser $user, $key, $value) { + $preferences = PhabricatorUserPreferences::loadUserPreferences($user); + + $editor = id(new PhabricatorUserPreferencesEditor()) + ->setActor($user) + ->setContentSource($this->newContentSource()) + ->setContinueOnNoEffect(true) + ->setContinueOnMissingFields(true); + + $xactions = array(); + $xactions[] = $preferences->newTransaction($key, $value); + $editor->applyTransactions($preferences, $xactions); + + return id(new PhabricatorPeopleQuery()) + ->setViewer($user) + ->withIDs(array($user->getID())) + ->executeOne(); + } + } diff --git a/src/applications/settings/panel/PhabricatorEmailDeliverySettingsPanel.php b/src/applications/settings/panel/PhabricatorEmailDeliverySettingsPanel.php new file mode 100644 index 0000000000..f7380a9d41 --- /dev/null +++ b/src/applications/settings/panel/PhabricatorEmailDeliverySettingsPanel.php @@ -0,0 +1,24 @@ +getUser()->getIsMailingList()) { + return true; + } + + return false; + } + +} diff --git a/src/applications/settings/panel/PhabricatorEmailPreferencesSettingsPanel.php b/src/applications/settings/panel/PhabricatorEmailPreferencesSettingsPanel.php index 162ed9ceac..02dbe7f21c 100644 --- a/src/applications/settings/panel/PhabricatorEmailPreferencesSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorEmailPreferencesSettingsPanel.php @@ -27,23 +27,12 @@ final class PhabricatorEmailPreferencesSettingsPanel $viewer = $this->getViewer(); $user = $this->getUser(); - $preferences = $user->loadPreferences(); + $preferences = $this->loadTargetPreferences(); - $pref_no_mail = PhabricatorUserPreferences::PREFERENCE_NO_MAIL; - $pref_no_self_mail = PhabricatorUserPreferences::PREFERENCE_NO_SELF_MAIL; - - $value_email = PhabricatorUserPreferences::MAILTAG_PREFERENCE_EMAIL; + $value_email = PhabricatorEmailTagsSetting::VALUE_EMAIL; $errors = array(); if ($request->isFormPost()) { - $preferences->setPreference( - $pref_no_mail, - $request->getStr($pref_no_mail)); - - $preferences->setPreference( - $pref_no_self_mail, - $request->getStr($pref_no_self_mail)); - $new_tags = $request->getArr('mailtags'); $mailtags = $preferences->getPreference('mailtags', array()); $all_tags = $this->getAllTags($user); @@ -51,59 +40,21 @@ final class PhabricatorEmailPreferencesSettingsPanel foreach ($all_tags as $key => $label) { $mailtags[$key] = (int)idx($new_tags, $key, $value_email); } - $preferences->setPreference('mailtags', $mailtags); - $preferences->save(); + $this->writeSetting( + $preferences, + PhabricatorEmailTagsSetting::SETTINGKEY, + $mailtags); return id(new AphrontRedirectResponse()) ->setURI($this->getPanelURI('?saved=true')); } - $form = new AphrontFormView(); - $form - ->setUser($viewer) - ->appendRemarkupInstructions( - pht( - 'These settings let you control how Phabricator notifies you about '. - 'events. You can configure Phabricator to send you an email, '. - 'just send a web notification, or not notify you at all.')) - ->appendRemarkupInstructions( - 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 may still receive some administrative email, like password ". - "reset email.//")) - ->appendChild( - id(new AphrontFormSelectControl()) - ->setLabel(pht('Email Notifications')) - ->setName($pref_no_mail) - ->setOptions( - array( - '0' => pht('Send me email notifications'), - '1' => pht('Never send email notifications'), - )) - ->setValue($preferences->getPreference($pref_no_mail, 0))) - ->appendRemarkupInstructions( - pht( - 'If you disable **Self Actions**, Phabricator will not notify '. - 'you about actions you take.')) - ->appendChild( - id(new AphrontFormSelectControl()) - ->setLabel(pht('Self Actions')) - ->setName($pref_no_self_mail) - ->setOptions( - array( - '0' => pht('Send me an email when I take an action'), - '1' => pht('Do not send me an email when I take an action'), - )) - ->setValue($preferences->getPreference($pref_no_self_mail, 0))); + $mailtags = $preferences->getSettingValue( + PhabricatorEmailTagsSetting::SETTINGKEY); - $mailtags = $preferences->getPreference('mailtags', array()); - - $form->appendChild( - id(new PHUIFormDividerControl())); + $form = id(new AphrontFormView()) + ->setUser($viewer); $form->appendRemarkupInstructions( pht( @@ -183,11 +134,7 @@ final class PhabricatorEmailPreferencesSettingsPanel ->setFormErrors($errors) ->setForm($form); - return id(new AphrontNullView()) - ->appendChild( - array( - $form_box, - )); + return $form_box; } private function getAllEditorsWithTags(PhabricatorUser $user) { @@ -222,9 +169,9 @@ final class PhabricatorEmailPreferencesSettingsPanel array $tags, array $prefs) { - $value_email = PhabricatorUserPreferences::MAILTAG_PREFERENCE_EMAIL; - $value_notify = PhabricatorUserPreferences::MAILTAG_PREFERENCE_NOTIFY; - $value_ignore = PhabricatorUserPreferences::MAILTAG_PREFERENCE_IGNORE; + $value_email = PhabricatorEmailTagsSetting::VALUE_EMAIL; + $value_notify = PhabricatorEmailTagsSetting::VALUE_NOTIFY; + $value_ignore = PhabricatorEmailTagsSetting::VALUE_IGNORE; $content = array(); foreach ($tags as $key => $label) { diff --git a/src/applications/settings/setting/PhabricatorEmailFormatSetting.php b/src/applications/settings/setting/PhabricatorEmailFormatSetting.php index 9fb8a43125..0cf0db5d74 100644 --- a/src/applications/settings/setting/PhabricatorEmailFormatSetting.php +++ b/src/applications/settings/setting/PhabricatorEmailFormatSetting.php @@ -5,8 +5,8 @@ final class PhabricatorEmailFormatSetting const SETTINGKEY = 'html-emails'; - const VALUE_HTML_EMAIL = 'true'; - const VALUE_TEXT_EMAIL = 'false'; + const VALUE_HTML_EMAIL = 'html'; + const VALUE_TEXT_EMAIL = 'text'; public function getSettingName() { return pht('HTML Email'); diff --git a/src/applications/settings/setting/PhabricatorEmailNotificationsSetting.php b/src/applications/settings/setting/PhabricatorEmailNotificationsSetting.php index e1aab295d8..dfbedb3a12 100644 --- a/src/applications/settings/setting/PhabricatorEmailNotificationsSetting.php +++ b/src/applications/settings/setting/PhabricatorEmailNotificationsSetting.php @@ -12,6 +12,14 @@ final class PhabricatorEmailNotificationsSetting return pht('Email Notifications'); } + public function getSettingPanelKey() { + return PhabricatorEmailDeliverySettingsPanel::PANELKEY; + } + + protected function getSettingOrder() { + return 100; + } + protected function getControlInstructions() { return pht( 'If you disable **Email Notifications**, Phabricator will never '. diff --git a/src/applications/settings/setting/PhabricatorEmailRePrefixSetting.php b/src/applications/settings/setting/PhabricatorEmailRePrefixSetting.php index 994f02ec0f..2a2f63b461 100644 --- a/src/applications/settings/setting/PhabricatorEmailRePrefixSetting.php +++ b/src/applications/settings/setting/PhabricatorEmailRePrefixSetting.php @@ -5,8 +5,8 @@ final class PhabricatorEmailRePrefixSetting const SETTINGKEY = 're-prefix'; - const VALUE_RE_PREFIX = 'true'; - const VALUE_NO_PREFIX = 'false'; + const VALUE_RE_PREFIX = 're'; + const VALUE_NO_PREFIX = 'none'; public function getSettingName() { return pht('Add "Re:" Prefix'); diff --git a/src/applications/settings/setting/PhabricatorEmailSelfActionsSetting.php b/src/applications/settings/setting/PhabricatorEmailSelfActionsSetting.php index c42954082c..f910c2b039 100644 --- a/src/applications/settings/setting/PhabricatorEmailSelfActionsSetting.php +++ b/src/applications/settings/setting/PhabricatorEmailSelfActionsSetting.php @@ -12,6 +12,14 @@ final class PhabricatorEmailSelfActionsSetting return pht('Self Actions'); } + public function getSettingPanelKey() { + return PhabricatorEmailDeliverySettingsPanel::PANELKEY; + } + + protected function getSettingOrder() { + return 200; + } + protected function getControlInstructions() { return pht( 'If you disable **Self Actions**, Phabricator will not notify '. diff --git a/src/applications/settings/setting/PhabricatorEmailTagsSetting.php b/src/applications/settings/setting/PhabricatorEmailTagsSetting.php new file mode 100644 index 0000000000..bb1a667d63 --- /dev/null +++ b/src/applications/settings/setting/PhabricatorEmailTagsSetting.php @@ -0,0 +1,21 @@ + Date: Sat, 4 Jun 2016 16:25:11 -0700 Subject: [PATCH 23/67] Remove PhabricatorUser->loadPreferences() Summary: Ref T4103. This method has no more callers. Test Plan: `grep` Reviewers: chad Reviewed By: chad Maniphest Tasks: T4103 Differential Revision: https://secure.phabricator.com/D16039 --- .../people/storage/PhabricatorUser.php | 23 ------------------- .../storage/PhabricatorUserPreferences.php | 10 -------- 2 files changed, 33 deletions(-) diff --git a/src/applications/people/storage/PhabricatorUser.php b/src/applications/people/storage/PhabricatorUser.php index 3cfa7bd2bc..917e0d9a02 100644 --- a/src/applications/people/storage/PhabricatorUser.php +++ b/src/applications/people/storage/PhabricatorUser.php @@ -568,29 +568,6 @@ final class PhabricatorUser return $this->getUserSetting(PhabricatorPronounSetting::SETTINGKEY); } - public function loadPreferences() { - if ($this->preferences) { - return $this->preferences; - } - - $preferences = null; - if ($this->getPHID()) { - $preferences = id(new PhabricatorUserPreferencesQuery()) - ->setViewer($this) - ->withUsers(array($this)) - ->executeOne(); - } - - if (!$preferences) { - $preferences = new PhabricatorUserPreferences(); - $preferences->setUserPHID($this->getPHID()); - $preferences->attachUser($this); - } - - $this->preferences = $preferences; - return $preferences; - } - public function loadEditorLink( $path, $line, diff --git a/src/applications/settings/storage/PhabricatorUserPreferences.php b/src/applications/settings/storage/PhabricatorUserPreferences.php index 6769930004..81b93b89d7 100644 --- a/src/applications/settings/storage/PhabricatorUserPreferences.php +++ b/src/applications/settings/storage/PhabricatorUserPreferences.php @@ -98,16 +98,6 @@ final class PhabricatorUserPreferences 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(); - } - /** * Load or create a preferences object for the given user. * From 2b344b2bb5005c8922136782d00664e64ad816c1 Mon Sep 17 00:00:00 2001 From: epriestley Date: Sun, 5 Jun 2016 06:20:32 -0700 Subject: [PATCH 24/67] Make caches misses throw by default intead of inline-generating Summary: Ref T4103. Ref T10078. Currently, when a user misses a cache we just build it for them. This is the behavior we want for the the viewer (so we don't have to build every cache up front if we don't actually need them), but not the right behavior for other users (since it allows performance problems to go undetected). Make inline cache generation strict by default, then make sure all the things that rely on cache data request the correct data (well, all of the things identified by unit tests, at least: there might be some more stuff I haven't hit yet). This fixes test failures in D16040, and backports a piece of that change. Test Plan: Identified and then fixed failures with `arc unit --everything`. Reviewers: chad Reviewed By: chad Maniphest Tasks: T4103, T10078 Differential Revision: https://secure.phabricator.com/D16042 --- .../engine/PhabricatorAuthSessionEngine.php | 1 + .../query/PhabricatorMetaMTAActorQuery.php | 1 + .../PhabricatorMailReplyHandler.php | 3 +++ .../people/query/PhabricatorPeopleQuery.php | 3 --- .../people/storage/PhabricatorUser.php | 17 ++++++++++++++++- .../testing/PhabricatorTestCase.php | 8 ++++++++ 6 files changed, 29 insertions(+), 4 deletions(-) diff --git a/src/applications/auth/engine/PhabricatorAuthSessionEngine.php b/src/applications/auth/engine/PhabricatorAuthSessionEngine.php index a3ee910629..06cb63c23a 100644 --- a/src/applications/auth/engine/PhabricatorAuthSessionEngine.php +++ b/src/applications/auth/engine/PhabricatorAuthSessionEngine.php @@ -163,6 +163,7 @@ final class PhabricatorAuthSessionEngine extends Phobject { $user = $user_table->loadFromArray($info); $user->attachRawCacheData($cache_raw); + $user->setAllowInlineCacheGeneration(true); switch ($session_type) { case PhabricatorAuthSession::TYPE_WEB: diff --git a/src/applications/metamta/query/PhabricatorMetaMTAActorQuery.php b/src/applications/metamta/query/PhabricatorMetaMTAActorQuery.php index a1f0aec057..38b04d6883 100644 --- a/src/applications/metamta/query/PhabricatorMetaMTAActorQuery.php +++ b/src/applications/metamta/query/PhabricatorMetaMTAActorQuery.php @@ -59,6 +59,7 @@ final class PhabricatorMetaMTAActorQuery extends PhabricatorQuery { $users = id(new PhabricatorPeopleQuery()) ->setViewer($this->getViewer()) ->withPHIDs($phids) + ->needUserSettings(true) ->execute(); $users = mpull($users, null, 'getPHID'); diff --git a/src/applications/metamta/replyhandler/PhabricatorMailReplyHandler.php b/src/applications/metamta/replyhandler/PhabricatorMailReplyHandler.php index e4da2a04d6..b0ae2de494 100644 --- a/src/applications/metamta/replyhandler/PhabricatorMailReplyHandler.php +++ b/src/applications/metamta/replyhandler/PhabricatorMailReplyHandler.php @@ -337,9 +337,12 @@ abstract class PhabricatorMailReplyHandler extends Phobject { $all_phids = array_merge($to, $cc); if ($all_phids) { + // We need user settings here because we'll check translations later + // when generating mail. $users = id(new PhabricatorPeopleQuery()) ->setViewer(PhabricatorUser::getOmnipotentUser()) ->withPHIDs($all_phids) + ->needUserSettings(true) ->execute(); $users = mpull($users, null, 'getPHID'); diff --git a/src/applications/people/query/PhabricatorPeopleQuery.php b/src/applications/people/query/PhabricatorPeopleQuery.php index cd7121299f..a5aac3f9ed 100644 --- a/src/applications/people/query/PhabricatorPeopleQuery.php +++ b/src/applications/people/query/PhabricatorPeopleQuery.php @@ -528,9 +528,6 @@ final class PhabricatorPeopleQuery $cache_data = igroup($cache_data, 'userPHID'); foreach ($user_map as $user_phid => $user) { $raw_rows = idx($cache_data, $user_phid, array()); - if (!$raw_rows) { - continue; - } $raw_data = ipull($raw_rows, 'cacheData', 'cacheKey'); foreach ($keys as $key) { diff --git a/src/applications/people/storage/PhabricatorUser.php b/src/applications/people/storage/PhabricatorUser.php index 917e0d9a02..137c4c4bd0 100644 --- a/src/applications/people/storage/PhabricatorUser.php +++ b/src/applications/people/storage/PhabricatorUser.php @@ -65,6 +65,7 @@ final class PhabricatorUser private $settingCacheKeys = array(); private $settingCache = array(); + private $allowInlineCacheGeneration; protected function readField($field) { switch ($field) { @@ -483,7 +484,11 @@ final class PhabricatorUser } $settings_key = PhabricatorUserPreferencesCacheType::KEY_PREFERENCES; - $settings = $this->requireCacheData($settings_key); + if ($this->getPHID()) { + $settings = $this->requireCacheData($settings_key); + } else { + $settings = array(); + } $defaults = PhabricatorSetting::getAllEnabledSettings($this); @@ -1486,6 +1491,10 @@ final class PhabricatorUser return $this; } + public function setAllowInlineCacheGeneration($allow_cache_generation) { + $this->allowInlineCacheGeneration = $allow_cache_generation; + return $this; + } /** * @task cache @@ -1506,6 +1515,12 @@ final class PhabricatorUser return $usable_value; } + // By default, we throw if a cache isn't available. This is consistent + // with the standard `needX()` + `attachX()` + `getX()` interaction. + if (!$this->allowInlineCacheGeneration) { + throw new PhabricatorDataNotAttachedException($this); + } + $usable_value = $type->getDefaultValue(); $user_phid = $this->getPHID(); diff --git a/src/infrastructure/testing/PhabricatorTestCase.php b/src/infrastructure/testing/PhabricatorTestCase.php index 4af75d157e..c9790cd1e4 100644 --- a/src/infrastructure/testing/PhabricatorTestCase.php +++ b/src/infrastructure/testing/PhabricatorTestCase.php @@ -202,6 +202,14 @@ abstract class PhabricatorTestCase extends PhutilTestCase { $editor->setActor($user); $editor->createNewUser($user, $email); + // When creating a new test user, we prefill their setting cache as empty. + // This is a little more efficient than doing a query to load the empty + // settings. + $user->attachRawCacheData( + array( + PhabricatorUserPreferencesCacheType::KEY_PREFERENCES => '[]', + )); + return $user; } From 6f1053c206f18e7053c6c24bf2fccdb211e8fdcc Mon Sep 17 00:00:00 2001 From: epriestley Date: Sat, 4 Jun 2016 17:39:33 -0700 Subject: [PATCH 25/67] Convert user profile images into a standard cache Summary: Ref T4103. Ref T10078. This moves profile image caches to new usercache infrastructure. These dirty automatically based on configuration and User properties, so add some stuff to make that happen. This reduces the number of queries issued on every page by 1. Test Plan: Browsed around, changed profile image, viewed as self, viewed as another user, verified no more query to pull this information on every page Reviewers: chad Reviewed By: chad Maniphest Tasks: T4103, T10078 Differential Revision: https://secure.phabricator.com/D16040 --- .../20160604.user.02.removeimagecache.sql | 2 + src/__phutil_library_map__.php | 2 + .../engine/PhabricatorAuthSessionEngine.php | 24 ++++- .../people/cache/PhabricatorUserCacheType.php | 8 ++ .../PhabricatorUserProfileImageCacheType.php | 82 ++++++++++++++++ .../PhabricatorPeopleMainMenuBarExtension.php | 10 +- .../people/query/PhabricatorPeopleQuery.php | 97 ++++++++----------- .../people/storage/PhabricatorUser.php | 79 +-------------- .../people/storage/PhabricatorUserCache.php | 4 +- 9 files changed, 165 insertions(+), 143 deletions(-) create mode 100644 resources/sql/autopatches/20160604.user.02.removeimagecache.sql create mode 100644 src/applications/people/cache/PhabricatorUserProfileImageCacheType.php diff --git a/resources/sql/autopatches/20160604.user.02.removeimagecache.sql b/resources/sql/autopatches/20160604.user.02.removeimagecache.sql new file mode 100644 index 0000000000..92de984709 --- /dev/null +++ b/resources/sql/autopatches/20160604.user.02.removeimagecache.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_user.user + DROP COLUMN profileImageCache; diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index e936049317..270b50d225 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -3650,6 +3650,7 @@ phutil_register_library_map(array( 'PhabricatorUserPreferencesTransactionQuery' => 'applications/settings/query/PhabricatorUserPreferencesTransactionQuery.php', 'PhabricatorUserProfile' => 'applications/people/storage/PhabricatorUserProfile.php', 'PhabricatorUserProfileEditor' => 'applications/people/editor/PhabricatorUserProfileEditor.php', + 'PhabricatorUserProfileImageCacheType' => 'applications/people/cache/PhabricatorUserProfileImageCacheType.php', 'PhabricatorUserRealNameField' => 'applications/people/customfield/PhabricatorUserRealNameField.php', 'PhabricatorUserRolesField' => 'applications/people/customfield/PhabricatorUserRolesField.php', 'PhabricatorUserSchemaSpec' => 'applications/people/storage/PhabricatorUserSchemaSpec.php', @@ -8465,6 +8466,7 @@ phutil_register_library_map(array( 'PhabricatorUserPreferencesTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 'PhabricatorUserProfile' => 'PhabricatorUserDAO', 'PhabricatorUserProfileEditor' => 'PhabricatorApplicationTransactionEditor', + 'PhabricatorUserProfileImageCacheType' => 'PhabricatorUserCacheType', 'PhabricatorUserRealNameField' => 'PhabricatorUserCustomField', 'PhabricatorUserRolesField' => 'PhabricatorUserCustomField', 'PhabricatorUserSchemaSpec' => 'PhabricatorConfigSchemaSpec', diff --git a/src/applications/auth/engine/PhabricatorAuthSessionEngine.php b/src/applications/auth/engine/PhabricatorAuthSessionEngine.php index 06cb63c23a..a7f5513056 100644 --- a/src/applications/auth/engine/PhabricatorAuthSessionEngine.php +++ b/src/applications/auth/engine/PhabricatorAuthSessionEngine.php @@ -113,7 +113,7 @@ final class PhabricatorAuthSessionEngine extends Phobject { $session_key = PhabricatorHash::digest($session_token); $cache_parts = $this->getUserCacheQueryParts($conn_r); - list($cache_selects, $cache_joins, $cache_map) = $cache_parts; + list($cache_selects, $cache_joins, $cache_map, $types_map) = $cache_parts; $info = queryfx_one( $conn_r, @@ -162,6 +162,7 @@ final class PhabricatorAuthSessionEngine extends Phobject { $user = $user_table->loadFromArray($info); + $cache_raw = $this->filterRawCacheData($user, $types_map, $cache_raw); $user->attachRawCacheData($cache_raw); $user->setAllowInlineCacheGeneration(true); @@ -761,11 +762,13 @@ final class PhabricatorAuthSessionEngine extends Phobject { $cache_map = array(); $keys = array(); + $types_map = array(); $cache_types = PhabricatorUserCacheType::getAllCacheTypes(); foreach ($cache_types as $cache_type) { foreach ($cache_type->getAutoloadKeys() as $autoload_key) { $keys[] = $autoload_key; + $types_map[$autoload_key] = $cache_type; } } @@ -809,7 +812,24 @@ final class PhabricatorAuthSessionEngine extends Phobject { $cache_joins = ''; } - return array($cache_selects, $cache_joins, $cache_map); + return array($cache_selects, $cache_joins, $cache_map, $types_map); + } + + private function filterRawCacheData( + PhabricatorUser $user, + array $types_map, + array $cache_raw) { + + foreach ($cache_raw as $cache_key => $cache_data) { + $type = $types_map[$cache_key]; + if ($type->shouldValidateRawCacheData()) { + if (!$type->isRawCacheDataValid($user, $cache_key, $cache_data)) { + unset($cache_raw[$cache_key]); + } + } + } + + return $cache_raw; } } diff --git a/src/applications/people/cache/PhabricatorUserCacheType.php b/src/applications/people/cache/PhabricatorUserCacheType.php index f2304b830d..29dec6e1f9 100644 --- a/src/applications/people/cache/PhabricatorUserCacheType.php +++ b/src/applications/people/cache/PhabricatorUserCacheType.php @@ -18,6 +18,14 @@ abstract class PhabricatorUserCacheType extends Phobject { return array(); } + public function shouldValidateRawCacheData() { + return false; + } + + public function isRawCacheDataValid(PhabricatorUser $user, $key, $data) { + throw new PhutilMethodNotImplementedException(); + } + public function getValueFromStorage($value) { return phutil_json_decode($value); } diff --git a/src/applications/people/cache/PhabricatorUserProfileImageCacheType.php b/src/applications/people/cache/PhabricatorUserProfileImageCacheType.php new file mode 100644 index 0000000000..8938441cc5 --- /dev/null +++ b/src/applications/people/cache/PhabricatorUserProfileImageCacheType.php @@ -0,0 +1,82 @@ +getViewer(); + + $file_phids = mpull($users, 'getProfileImagePHID'); + $file_phids = array_filter($file_phids); + + if ($file_phids) { + $files = id(new PhabricatorFileQuery()) + ->setViewer($viewer) + ->withPHIDs($file_phids) + ->execute(); + $files = mpull($files, null, 'getPHID'); + } else { + $files = array(); + } + + $results = array(); + foreach ($users as $user) { + $image_phid = $user->getProfileImagePHID(); + if (isset($files[$image_phid])) { + $image_uri = $files[$image_phid]->getBestURI(); + } else { + $image_uri = PhabricatorUser::getDefaultProfileImageURI(); + } + + $user_phid = $user->getPHID(); + $version = $this->getCacheVersion($user); + $results[$user_phid] = "{$version},{$image_uri}"; + } + + return $results; + } + + public function getValueFromStorage($value) { + $parts = explode(',', $value, 2); + return end($parts); + } + + public function getValueForStorage($value) { + return $value; + } + + public function shouldValidateRawCacheData() { + return true; + } + + public function isRawCacheDataValid(PhabricatorUser $user, $key, $data) { + $parts = explode(',', $data, 2); + $version = reset($parts); + return ($version === $this->getCacheVersion($user)); + } + + private function getCacheVersion(PhabricatorUser $user) { + $parts = array( + PhabricatorEnv::getCDNURI('/'), + PhabricatorEnv::getEnvConfig('cluster.instance'), + $user->getProfileImagePHID(), + ); + $parts = serialize($parts); + return PhabricatorHash::digestForIndex($parts); + } + +} diff --git a/src/applications/people/extension/PhabricatorPeopleMainMenuBarExtension.php b/src/applications/people/extension/PhabricatorPeopleMainMenuBarExtension.php index a92c5ba235..5028280ac4 100644 --- a/src/applications/people/extension/PhabricatorPeopleMainMenuBarExtension.php +++ b/src/applications/people/extension/PhabricatorPeopleMainMenuBarExtension.php @@ -7,15 +7,7 @@ final class PhabricatorPeopleMainMenuBarExtension public function buildMainMenus() { $viewer = $this->getViewer(); - - // TODO: This should get cached. - - $profile = id(new PhabricatorPeopleQuery()) - ->setViewer($viewer) - ->needProfileImage(true) - ->withPHIDs(array($viewer->getPHID())) - ->executeOne(); - $image = $profile->getProfileImageURI(); + $image = $viewer->getProfileImageURI(); $bar_item = id(new PHUIListItemView()) ->setName($viewer->getUsername()) diff --git a/src/applications/people/query/PhabricatorPeopleQuery.php b/src/applications/people/query/PhabricatorPeopleQuery.php index a5aac3f9ed..46722038e0 100644 --- a/src/applications/people/query/PhabricatorPeopleQuery.php +++ b/src/applications/people/query/PhabricatorPeopleQuery.php @@ -106,7 +106,14 @@ final class PhabricatorPeopleQuery } public function needProfileImage($need) { - $this->needProfileImage = $need; + $cache_key = PhabricatorUserProfileImageCacheType::KEY_URI; + + if ($need) { + $this->cacheKeys[$cache_key] = true; + } else { + unset($this->cacheKeys[$cache_key]); + } + return $this; } @@ -182,59 +189,6 @@ final class PhabricatorPeopleQuery } } - if ($this->needProfileImage) { - $rebuild = array(); - foreach ($users as $user) { - $image_uri = $user->getProfileImageCache(); - if ($image_uri) { - // This user has a valid cache, so we don't need to fetch any - // data or rebuild anything. - - $user->attachProfileImageURI($image_uri); - continue; - } - - // This user's cache is invalid or missing, so we're going to rebuild - // it. - $rebuild[] = $user; - } - - if ($rebuild) { - $file_phids = mpull($rebuild, 'getProfileImagePHID'); - $file_phids = array_filter($file_phids); - - if ($file_phids) { - // NOTE: We're using the omnipotent user here because older profile - // images do not have the 'profile' flag, so they may not be visible - // to the executing viewer. At some point, we could migrate to add - // this flag and then use the real viewer, or just use the real - // viewer after enough time has passed to limit the impact of old - // data. The consequence of missing here is that we cache a default - // image when a real image exists. - $files = id(new PhabricatorFileQuery()) - ->setParentQuery($this) - ->setViewer(PhabricatorUser::getOmnipotentUser()) - ->withPHIDs($file_phids) - ->execute(); - $files = mpull($files, null, 'getPHID'); - } else { - $files = array(); - } - - foreach ($rebuild as $user) { - $image_phid = $user->getProfileImagePHID(); - if (isset($files[$image_phid])) { - $image_uri = $files[$image_phid]->getBestURI(); - } else { - $image_uri = PhabricatorUser::getDefaultProfileImageURI(); - } - - $user->writeProfileImageCache($image_uri); - $user->attachProfileImageURI($image_uri); - } - } - } - if ($this->needAvailability) { $rebuild = array(); foreach ($users as $user) { @@ -509,6 +463,8 @@ final class PhabricatorPeopleQuery $hashes[] = PhabricatorHash::digestForIndex($key); } + $types = PhabricatorUserCacheType::getAllCacheTypes(); + // First, pull any available caches. If we wanted to be particularly clever // we could do this with JOINs in the main query. @@ -517,12 +473,43 @@ final class PhabricatorPeopleQuery $cache_data = queryfx_all( $cache_conn, - 'SELECT cacheKey, userPHID, cacheData FROM %T + 'SELECT cacheKey, userPHID, cacheData, cacheType FROM %T WHERE cacheIndex IN (%Ls) AND userPHID IN (%Ls)', $cache_table->getTableName(), $hashes, array_keys($user_map)); + $skip_validation = array(); + + // After we read caches from the database, discard any which have data that + // invalid or out of date. This allows cache types to implement TTLs or + // versions instead of or in addition to explicit cache clears. + foreach ($cache_data as $row_key => $row) { + $cache_type = $row['cacheType']; + + if (isset($skip_validation[$cache_type])) { + continue; + } + + if (empty($types[$cache_type])) { + unset($cache_data[$row_key]); + continue; + } + + $type = $types[$cache_type]; + if (!$type->shouldValidateRawCacheData()) { + $skip_validation[$cache_type] = true; + continue; + } + + $user = $user_map[$row['userPHID']]; + $raw_data = $row['cacheData']; + if (!$type->isRawCacheDataValid($user, $row['cacheKey'], $raw_data)) { + unset($cache_data[$row_key]); + continue; + } + } + $need = array(); $cache_data = igroup($cache_data, 'userPHID'); diff --git a/src/applications/people/storage/PhabricatorUser.php b/src/applications/people/storage/PhabricatorUser.php index 137c4c4bd0..ac13282923 100644 --- a/src/applications/people/storage/PhabricatorUser.php +++ b/src/applications/people/storage/PhabricatorUser.php @@ -30,7 +30,6 @@ final class PhabricatorUser protected $passwordSalt; protected $passwordHash; protected $profileImagePHID; - protected $profileImageCache; protected $availabilityCache; protected $availabilityCacheTTL; @@ -46,7 +45,6 @@ final class PhabricatorUser protected $accountSecret; - private $profileImage = self::ATTACHABLE; private $profile = null; private $availability = self::ATTACHABLE; private $preferences = null; @@ -196,7 +194,6 @@ final class PhabricatorUser 'isApproved' => 'uint32', 'accountSecret' => 'bytes64', 'isEnrolledInMultiFactor' => 'bool', - 'profileImageCache' => 'text255?', 'availabilityCache' => 'text255?', 'availabilityCacheTTL' => 'uint32?', ), @@ -218,7 +215,6 @@ final class PhabricatorUser ), ), self::CONFIG_NO_MUTATE => array( - 'profileImageCache' => true, 'availabilityCache' => true, 'availabilityCacheTTL' => true, ), @@ -791,13 +787,9 @@ final class PhabricatorUser return celerity_get_resource_uri('/rsrc/image/avatar.png'); } - public function attachProfileImageURI($uri) { - $this->profileImage = $uri; - return $this; - } - public function getProfileImageURI() { - return $this->assertAttached($this->profileImage); + $uri_key = PhabricatorUserProfileImageCacheType::KEY_URI; + return $this->requireCacheData($uri_key); } public function getFullName() { @@ -1008,72 +1000,6 @@ final class PhabricatorUser } -/* -( Profile Image Cache )------------------------------------------------ */ - - - /** - * Get this user's cached profile image URI. - * - * @return string|null Cached URI, if a URI is cached. - * @task image-cache - */ - public function getProfileImageCache() { - $version = $this->getProfileImageVersion(); - - $parts = explode(',', $this->profileImageCache, 2); - if (count($parts) !== 2) { - return null; - } - - if ($parts[0] !== $version) { - return null; - } - - return $parts[1]; - } - - - /** - * Generate a new cache value for this user's profile image. - * - * @return string New cache value. - * @task image-cache - */ - public function writeProfileImageCache($uri) { - $version = $this->getProfileImageVersion(); - $cache = "{$version},{$uri}"; - - $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); - queryfx( - $this->establishConnection('w'), - 'UPDATE %T SET profileImageCache = %s WHERE id = %d', - $this->getTableName(), - $cache, - $this->getID()); - unset($unguarded); - } - - - /** - * Get a version identifier for a user's profile image. - * - * This version will change if the image changes, or if any of the - * environment configuration which goes into generating a URI changes. - * - * @return string Cache version. - * @task image-cache - */ - private function getProfileImageVersion() { - $parts = array( - PhabricatorEnv::getCDNURI('/'), - PhabricatorEnv::getEnvConfig('cluster.instance'), - $this->getProfileImagePHID(), - ); - $parts = serialize($parts); - return PhabricatorHash::digestForIndex($parts); - } - - /* -( Multi-Factor Authentication )---------------------------------------- */ @@ -1529,6 +1455,7 @@ final class PhabricatorUser if (array_key_exists($user_phid, $map)) { $usable_value = $map[$user_phid]; $raw_value = $type->getValueForStorage($usable_value); + $usable_value = $type->getValueFromStorage($raw_value); $this->rawCacheData[$key] = $raw_value; PhabricatorUserCache::writeCache( diff --git a/src/applications/people/storage/PhabricatorUserCache.php b/src/applications/people/storage/PhabricatorUserCache.php index f8f50be64c..9bd1a9d2cb 100644 --- a/src/applications/people/storage/PhabricatorUserCache.php +++ b/src/applications/people/storage/PhabricatorUserCache.php @@ -86,7 +86,9 @@ final class PhabricatorUserCache extends PhabricatorUserDAO { $conn_w, 'INSERT INTO %T (userPHID, cacheIndex, cacheKey, cacheData, cacheType) VALUES %Q - ON DUPLICATE KEY UPDATE cacheData = VALUES(cacheData)', + ON DUPLICATE KEY UPDATE + cacheData = VALUES(cacheData), + cacheType = VALUES(cacheType)', $table->getTableName(), $chunk); } From c1331bcb7ba6c83fea0474a9df55c54e37d173fb Mon Sep 17 00:00:00 2001 From: epriestley Date: Sat, 4 Jun 2016 18:19:16 -0700 Subject: [PATCH 26/67] Cache user notification and message counts Summary: Ref T4103. Ref T10078. This puts a user cache in front of notification and message counts. This reduces the number of queries issued on every page by 4 (2x building the menu, 2x building Quicksand data). Also fixes some minor issues: - Daemons could choke on sending mail in the user's translation. - No-op object updates could fail in the daemons. - Questionable data access pattern in the file query coming out of the profile file cache. Test Plan: - Sent myself notifications. Saw count go up. - Cleared them by visiting objects and clearing all notifications. Saw count go down. - Sent myself messages. Saw count go up. - Cleared them by visiting threads. Saw count go down. Reviewers: chad Reviewed By: chad Maniphest Tasks: T4103, T10078 Differential Revision: https://secure.phabricator.com/D16041 --- src/__phutil_library_map__.php | 4 ++ .../query/AphlictDropdownDataQuery.php | 24 ++++----- .../controller/ConpherenceViewController.php | 9 ++-- .../conpherence/editor/ConpherenceEditor.php | 4 ++ .../storage/ConpherenceParticipant.php | 7 ++- .../feed/PhabricatorFeedStoryPublisher.php | 7 ++- .../files/query/PhabricatorFileQuery.php | 10 ++++ ...PhabricatorNotificationClearController.php | 4 ++ ...PhabricatorNotificationPanelController.php | 3 +- .../PhabricatorFeedStoryNotification.php | 16 ++---- .../PhabricatorUserMessageCountCacheType.php | 45 +++++++++++++++++ ...bricatorUserNotificationCountCacheType.php | 50 +++++++++++++++++++ .../people/storage/PhabricatorUser.php | 10 ++++ .../people/storage/PhabricatorUserCache.php | 13 +++-- .../PhabricatorUserPreferencesEditor.php | 1 - ...torApplicationTransactionPublishWorker.php | 6 ++- 16 files changed, 174 insertions(+), 39 deletions(-) create mode 100644 src/applications/people/cache/PhabricatorUserMessageCountCacheType.php create mode 100644 src/applications/people/cache/PhabricatorUserNotificationCountCacheType.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 270b50d225..a72ccdbeb6 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -3640,6 +3640,8 @@ phutil_register_library_map(array( 'PhabricatorUserIconField' => 'applications/people/customfield/PhabricatorUserIconField.php', 'PhabricatorUserLog' => 'applications/people/storage/PhabricatorUserLog.php', 'PhabricatorUserLogView' => 'applications/people/view/PhabricatorUserLogView.php', + 'PhabricatorUserMessageCountCacheType' => 'applications/people/cache/PhabricatorUserMessageCountCacheType.php', + 'PhabricatorUserNotificationCountCacheType' => 'applications/people/cache/PhabricatorUserNotificationCountCacheType.php', 'PhabricatorUserPHIDResolver' => 'applications/phid/resolver/PhabricatorUserPHIDResolver.php', 'PhabricatorUserPreferences' => 'applications/settings/storage/PhabricatorUserPreferences.php', 'PhabricatorUserPreferencesCacheType' => 'applications/people/cache/PhabricatorUserPreferencesCacheType.php', @@ -8451,6 +8453,8 @@ phutil_register_library_map(array( 'PhabricatorPolicyInterface', ), 'PhabricatorUserLogView' => 'AphrontView', + 'PhabricatorUserMessageCountCacheType' => 'PhabricatorUserCacheType', + 'PhabricatorUserNotificationCountCacheType' => 'PhabricatorUserCacheType', 'PhabricatorUserPHIDResolver' => 'PhabricatorPHIDResolver', 'PhabricatorUserPreferences' => array( 'PhabricatorUserDAO', diff --git a/src/applications/aphlict/query/AphlictDropdownDataQuery.php b/src/applications/aphlict/query/AphlictDropdownDataQuery.php index e8a8edb59f..7a15ec7f64 100644 --- a/src/applications/aphlict/query/AphlictDropdownDataQuery.php +++ b/src/applications/aphlict/query/AphlictDropdownDataQuery.php @@ -46,17 +46,15 @@ final class AphlictDropdownDataQuery extends Phobject { $is_c_installed = PhabricatorApplication::isClassInstalledForViewer( $conpherence_app, $viewer); - $raw_message_count_number = null; - $message_count_number = null; if ($is_c_installed) { - $unread_status = ConpherenceParticipationStatus::BEHIND; - $unread = id(new ConpherenceParticipantCountQuery()) - ->withParticipantPHIDs(array($viewer->getPHID())) - ->withParticipationStatus($unread_status) - ->execute(); - $raw_message_count_number = idx($unread, $viewer->getPHID(), 0); + $raw_message_count_number = $viewer->getUnreadMessageCount(); $message_count_number = $this->formatNumber($raw_message_count_number); + } else { + $raw_message_count_number = null; + $message_count_number = null; } + + $conpherence_data = array( 'isInstalled' => $is_c_installed, 'countType' => 'messages', @@ -69,15 +67,15 @@ final class AphlictDropdownDataQuery extends Phobject { $is_n_installed = PhabricatorApplication::isClassInstalledForViewer( $notification_app, $viewer); - $notification_count_number = null; - $raw_notification_count_number = null; if ($is_n_installed) { - $raw_notification_count_number = - id(new PhabricatorFeedStoryNotification()) - ->countUnread($viewer); + $raw_notification_count_number = $viewer->getUnreadNotificationCount(); $notification_count_number = $this->formatNumber( $raw_notification_count_number); + } else { + $notification_count_number = null; + $raw_notification_count_number = null; } + $notification_data = array( 'isInstalled' => $is_n_installed, 'countType' => 'notifications', diff --git a/src/applications/conpherence/controller/ConpherenceViewController.php b/src/applications/conpherence/controller/ConpherenceViewController.php index 2f01979df6..ea0fa7a295 100644 --- a/src/applications/conpherence/controller/ConpherenceViewController.php +++ b/src/applications/conpherence/controller/ConpherenceViewController.php @@ -68,9 +68,12 @@ final class ConpherenceViewController extends $latest_transaction = head($transactions); $participant = $conpherence->getParticipantIfExists($user->getPHID()); if ($participant) { - $write_guard = AphrontWriteGuard::beginScopedUnguardedWrites(); - $participant->markUpToDate($conpherence, $latest_transaction); - unset($write_guard); + if (!$participant->isUpToDate($conpherence)) { + $write_guard = AphrontWriteGuard::beginScopedUnguardedWrites(); + $participant->markUpToDate($conpherence, $latest_transaction); + $user->clearCacheData(PhabricatorUserMessageCountCacheType::KEY_COUNT); + unset($write_guard); + } } $data = ConpherenceTransactionRenderer::renderTransactions( diff --git a/src/applications/conpherence/editor/ConpherenceEditor.php b/src/applications/conpherence/editor/ConpherenceEditor.php index bca70b4299..77dcf84eec 100644 --- a/src/applications/conpherence/editor/ConpherenceEditor.php +++ b/src/applications/conpherence/editor/ConpherenceEditor.php @@ -422,6 +422,10 @@ final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor { $participant->save(); } + PhabricatorUserCache::clearCaches( + PhabricatorUserMessageCountCacheType::KEY_COUNT, + array_keys($participants)); + if ($xactions) { $data = array( 'type' => 'message', diff --git a/src/applications/conpherence/storage/ConpherenceParticipant.php b/src/applications/conpherence/storage/ConpherenceParticipant.php index a656dce776..32c3fb38e1 100644 --- a/src/applications/conpherence/storage/ConpherenceParticipant.php +++ b/src/applications/conpherence/storage/ConpherenceParticipant.php @@ -47,11 +47,16 @@ final class ConpherenceParticipant extends ConpherenceDAO { $this->setBehindTransactionPHID($xaction->getPHID()); $this->setSeenMessageCount($conpherence->getMessageCount()); $this->save(); + + PhabricatorUserCache::clearCache( + PhabricatorUserMessageCountCacheType::KEY_COUNT, + $this->getParticipantPHID()); } + return $this; } - private function isUpToDate(ConpherenceThread $conpherence) { + public function isUpToDate(ConpherenceThread $conpherence) { return ($this->getSeenMessageCount() == $conpherence->getMessageCount()) && diff --git a/src/applications/feed/PhabricatorFeedStoryPublisher.php b/src/applications/feed/PhabricatorFeedStoryPublisher.php index e4b62fdf29..3b59edc37b 100644 --- a/src/applications/feed/PhabricatorFeedStoryPublisher.php +++ b/src/applications/feed/PhabricatorFeedStoryPublisher.php @@ -159,7 +159,8 @@ final class PhabricatorFeedStoryPublisher extends Phobject { $will_receive_mail = array_fill_keys($this->mailRecipientPHIDs, true); - foreach (array_unique($subscribed_phids) as $user_phid) { + $user_phids = array_unique($subscribed_phids); + foreach ($user_phids as $user_phid) { if (isset($will_receive_mail[$user_phid])) { $mark_read = 1; } else { @@ -184,6 +185,10 @@ final class PhabricatorFeedStoryPublisher extends Phobject { $notif->getTableName(), implode(', ', $sql)); } + + PhabricatorUserCache::clearCaches( + PhabricatorUserNotificationCountCacheType::KEY_COUNT, + $user_phids); } private function sendNotification($chrono_key, array $subscribed_phids) { diff --git a/src/applications/files/query/PhabricatorFileQuery.php b/src/applications/files/query/PhabricatorFileQuery.php index 03446d9154..5e1876acd6 100644 --- a/src/applications/files/query/PhabricatorFileQuery.php +++ b/src/applications/files/query/PhabricatorFileQuery.php @@ -134,6 +134,9 @@ final class PhabricatorFileQuery return $files; } + $viewer = $this->getViewer(); + $is_omnipotent = $viewer->isOmnipotent(); + // We need to load attached objects to perform policy checks for files. // First, load the edges. @@ -156,6 +159,13 @@ final class PhabricatorFileQuery continue; } + if ($is_omnipotent) { + // If the viewer is omnipotent, we don't need to load the associated + // objects either since they can certainly see the object. Skipping + // this can improve performance and prevent cycles. + continue; + } + foreach ($phids as $phid) { $object_phids[$phid] = true; } diff --git a/src/applications/notification/controller/PhabricatorNotificationClearController.php b/src/applications/notification/controller/PhabricatorNotificationClearController.php index 0b6027d379..a826ae0a6b 100644 --- a/src/applications/notification/controller/PhabricatorNotificationClearController.php +++ b/src/applications/notification/controller/PhabricatorNotificationClearController.php @@ -18,6 +18,10 @@ final class PhabricatorNotificationClearController $viewer->getPHID(), $chrono_key); + PhabricatorUserCache::clearCache( + PhabricatorUserNotificationCountCacheType::KEY_COUNT, + $viewer->getPHID()); + return id(new AphrontReloadResponse()) ->setURI('/notification/'); } diff --git a/src/applications/notification/controller/PhabricatorNotificationPanelController.php b/src/applications/notification/controller/PhabricatorNotificationPanelController.php index c9c3d9b94a..be7eeb914e 100644 --- a/src/applications/notification/controller/PhabricatorNotificationPanelController.php +++ b/src/applications/notification/controller/PhabricatorNotificationPanelController.php @@ -71,8 +71,7 @@ final class PhabricatorNotificationPanelController $content, $connection_ui); - $unread_count = id(new PhabricatorFeedStoryNotification()) - ->countUnread($viewer); + $unread_count = $viewer->getUnreadNotificationCount(); $json = array( 'content' => $content, diff --git a/src/applications/notification/storage/PhabricatorFeedStoryNotification.php b/src/applications/notification/storage/PhabricatorFeedStoryNotification.php index d0b4acab53..89d8db5336 100644 --- a/src/applications/notification/storage/PhabricatorFeedStoryNotification.php +++ b/src/applications/notification/storage/PhabricatorFeedStoryNotification.php @@ -60,20 +60,10 @@ final class PhabricatorFeedStoryNotification extends PhabricatorFeedDAO { $object_phid); unset($unguarded); - } - public function countUnread(PhabricatorUser $user) { - $conn = $this->establishConnection('r'); - - $data = queryfx_one( - $conn, - 'SELECT COUNT(*) as count - FROM %T - WHERE userPHID = %s AND hasViewed = 0', - $this->getTableName(), - $user->getPHID()); - - return $data['count']; + $count_key = PhabricatorUserNotificationCountCacheType::KEY_COUNT; + PhabricatorUserCache::clearCache($count_key, $user->getPHID()); + $user->clearCacheData($count_key); } } diff --git a/src/applications/people/cache/PhabricatorUserMessageCountCacheType.php b/src/applications/people/cache/PhabricatorUserMessageCountCacheType.php new file mode 100644 index 0000000000..52f53fe345 --- /dev/null +++ b/src/applications/people/cache/PhabricatorUserMessageCountCacheType.php @@ -0,0 +1,45 @@ +withParticipantPHIDs($user_phids) + ->withParticipationStatus($unread_status) + ->execute(); + + $empty = array_fill_keys($user_phids, 0); + return $unread + $empty; + } + +} diff --git a/src/applications/people/cache/PhabricatorUserNotificationCountCacheType.php b/src/applications/people/cache/PhabricatorUserNotificationCountCacheType.php new file mode 100644 index 0000000000..8eb1f464ce --- /dev/null +++ b/src/applications/people/cache/PhabricatorUserNotificationCountCacheType.php @@ -0,0 +1,50 @@ +establishConnection('r'); + + $rows = queryfx_all( + $conn_r, + 'SELECT userPHID, COUNT(*) N FROM %T + WHERE userPHID IN (%Ls) AND hasViewed = 0 + GROUP BY userPHID', + $table->getTableName(), + $user_phids); + + $empty = array_fill_keys($user_phids, 0); + return ipull($rows, 'N', 'userPHID') + $empty; + } + +} diff --git a/src/applications/people/storage/PhabricatorUser.php b/src/applications/people/storage/PhabricatorUser.php index ac13282923..5dcd3ea073 100644 --- a/src/applications/people/storage/PhabricatorUser.php +++ b/src/applications/people/storage/PhabricatorUser.php @@ -792,6 +792,16 @@ final class PhabricatorUser return $this->requireCacheData($uri_key); } + public function getUnreadNotificationCount() { + $notification_key = PhabricatorUserNotificationCountCacheType::KEY_COUNT; + return $this->requireCacheData($notification_key); + } + + public function getUnreadMessageCount() { + $message_key = PhabricatorUserMessageCountCacheType::KEY_COUNT; + return $this->requireCacheData($message_key); + } + public function getFullName() { if (strlen($this->getRealName())) { return $this->getUsername().' ('.$this->getRealName().')'; diff --git a/src/applications/people/storage/PhabricatorUserCache.php b/src/applications/people/storage/PhabricatorUserCache.php index 9bd1a9d2cb..c74b583923 100644 --- a/src/applications/people/storage/PhabricatorUserCache.php +++ b/src/applications/people/storage/PhabricatorUserCache.php @@ -97,10 +97,18 @@ final class PhabricatorUserCache extends PhabricatorUserDAO { } public static function clearCache($key, $user_phid) { + return self::clearCaches($key, array($user_phid)); + } + + public static function clearCaches($key, array $user_phids) { if (PhabricatorEnv::isReadOnly()) { return; } + if (!$user_phids) { + return; + } + $table = new self(); $conn_w = $table->establishConnection('w'); @@ -108,15 +116,14 @@ final class PhabricatorUserCache extends PhabricatorUserDAO { queryfx( $conn_w, - 'DELETE FROM %T WHERE cacheIndex = %s AND userPHID = %s', + 'DELETE FROM %T WHERE cacheIndex = %s AND userPHID IN (%Ls)', $table->getTableName(), PhabricatorHash::digestForIndex($key), - $user_phid); + $user_phids); unset($unguarded); } - public static function clearCacheForAllUsers($key) { if (PhabricatorEnv::isReadOnly()) { return; diff --git a/src/applications/settings/editor/PhabricatorUserPreferencesEditor.php b/src/applications/settings/editor/PhabricatorUserPreferencesEditor.php index 918142f95c..cfb7fe105f 100644 --- a/src/applications/settings/editor/PhabricatorUserPreferencesEditor.php +++ b/src/applications/settings/editor/PhabricatorUserPreferencesEditor.php @@ -162,7 +162,6 @@ final class PhabricatorUserPreferencesEditor PhabricatorUserPreferencesCacheType::KEY_PREFERENCES); } - return $xactions; } diff --git a/src/applications/transactions/worker/PhabricatorApplicationTransactionPublishWorker.php b/src/applications/transactions/worker/PhabricatorApplicationTransactionPublishWorker.php index 0369fbff54..8fbdc19ad6 100644 --- a/src/applications/transactions/worker/PhabricatorApplicationTransactionPublishWorker.php +++ b/src/applications/transactions/worker/PhabricatorApplicationTransactionPublishWorker.php @@ -84,8 +84,10 @@ final class PhabricatorApplicationTransactionPublishWorker $xaction_phids = idx($data, 'xactionPHIDs'); if (!$xaction_phids) { - throw new PhabricatorWorkerPermanentFailureException( - pht('Task has no transaction PHIDs!')); + // It's okay if we don't have any transactions. This can happen when + // creating objects or performing no-op updates. We will still apply + // meaningful side effects like updating search engine indexes. + return array(); } $viewer = PhabricatorUser::getOmnipotentUser(); From 5ba7938d54298f535ca8072d640c919cf75d6137 Mon Sep 17 00:00:00 2001 From: epriestley Date: Sun, 5 Jun 2016 06:50:03 -0700 Subject: [PATCH 27/67] Simplify user cache management of data forms Summary: Ref T4103. Ref T10078. We currently have separate "usable" and "raw" values, but can simplify this by making `newValueForUsers()` return the raw value. Test Plan: Ran unit tests; browsed around; dropped caches and browsed around. Reviewers: chad Reviewed By: chad Maniphest Tasks: T4103, T10078 Differential Revision: https://secure.phabricator.com/D16043 --- .../people/cache/PhabricatorUserCacheType.php | 6 +----- .../cache/PhabricatorUserMessageCountCacheType.php | 4 ---- .../PhabricatorUserNotificationCountCacheType.php | 4 ---- .../cache/PhabricatorUserPreferencesCacheType.php | 14 ++++++++++++-- .../cache/PhabricatorUserProfileImageCacheType.php | 8 ++++---- .../people/query/PhabricatorPeopleQuery.php | 3 +-- .../people/storage/PhabricatorUser.php | 3 +-- 7 files changed, 19 insertions(+), 23 deletions(-) diff --git a/src/applications/people/cache/PhabricatorUserCacheType.php b/src/applications/people/cache/PhabricatorUserCacheType.php index 29dec6e1f9..27e965fb43 100644 --- a/src/applications/people/cache/PhabricatorUserCacheType.php +++ b/src/applications/people/cache/PhabricatorUserCacheType.php @@ -27,11 +27,7 @@ abstract class PhabricatorUserCacheType extends Phobject { } public function getValueFromStorage($value) { - return phutil_json_decode($value); - } - - public function getValueForStorage($value) { - return phutil_json_encode($value); + return $value; } public function newValueForUsers($key, array $users) { diff --git a/src/applications/people/cache/PhabricatorUserMessageCountCacheType.php b/src/applications/people/cache/PhabricatorUserMessageCountCacheType.php index 52f53fe345..b6ff7b7263 100644 --- a/src/applications/people/cache/PhabricatorUserMessageCountCacheType.php +++ b/src/applications/people/cache/PhabricatorUserMessageCountCacheType.php @@ -21,10 +21,6 @@ final class PhabricatorUserMessageCountCacheType return (int)$value; } - public function getValueForStorage($value) { - return $value; - } - public function newValueForUsers($key, array $users) { if (!$users) { return array(); diff --git a/src/applications/people/cache/PhabricatorUserNotificationCountCacheType.php b/src/applications/people/cache/PhabricatorUserNotificationCountCacheType.php index 8eb1f464ce..c24db0cc09 100644 --- a/src/applications/people/cache/PhabricatorUserNotificationCountCacheType.php +++ b/src/applications/people/cache/PhabricatorUserNotificationCountCacheType.php @@ -21,10 +21,6 @@ final class PhabricatorUserNotificationCountCacheType return (int)$value; } - public function getValueForStorage($value) { - return $value; - } - public function newValueForUsers($key, array $users) { if (!$users) { return array(); diff --git a/src/applications/people/cache/PhabricatorUserPreferencesCacheType.php b/src/applications/people/cache/PhabricatorUserPreferencesCacheType.php index 77aeafbf16..a2187a4599 100644 --- a/src/applications/people/cache/PhabricatorUserPreferencesCacheType.php +++ b/src/applications/people/cache/PhabricatorUserPreferencesCacheType.php @@ -17,6 +17,10 @@ final class PhabricatorUserPreferencesCacheType return ($key === self::KEY_PREFERENCES); } + public function getValueFromStorage($value) { + return phutil_json_decode($value); + } + public function newValueForUsers($key, array $users) { $viewer = $this->getViewer(); @@ -27,9 +31,15 @@ final class PhabricatorUserPreferencesCacheType ->withUserPHIDs($user_phids) ->execute(); - $empty = array_fill_keys($user_phids, array()); + $settings = mpull($preferences, 'getPreferences', 'getUserPHID'); - return mpull($preferences, 'getPreferences', 'getUserPHID') + $empty; + $results = array(); + foreach ($user_phids as $user_phid) { + $value = idx($settings, $user_phid, array()); + $results[$user_phid] = phutil_json_encode($value); + } + + return $results; } } diff --git a/src/applications/people/cache/PhabricatorUserProfileImageCacheType.php b/src/applications/people/cache/PhabricatorUserProfileImageCacheType.php index 8938441cc5..b3f944a3ed 100644 --- a/src/applications/people/cache/PhabricatorUserProfileImageCacheType.php +++ b/src/applications/people/cache/PhabricatorUserProfileImageCacheType.php @@ -17,6 +17,10 @@ final class PhabricatorUserProfileImageCacheType return ($key === self::KEY_URI); } + public function getDefaultValue() { + return PhabricatorUser::getDefaultProfileImageURI(); + } + public function newValueForUsers($key, array $users) { $viewer = $this->getViewer(); @@ -55,10 +59,6 @@ final class PhabricatorUserProfileImageCacheType return end($parts); } - public function getValueForStorage($value) { - return $value; - } - public function shouldValidateRawCacheData() { return true; } diff --git a/src/applications/people/query/PhabricatorPeopleQuery.php b/src/applications/people/query/PhabricatorPeopleQuery.php index 46722038e0..6c1fa7b68d 100644 --- a/src/applications/people/query/PhabricatorPeopleQuery.php +++ b/src/applications/people/query/PhabricatorPeopleQuery.php @@ -544,8 +544,7 @@ final class PhabricatorPeopleQuery $data = $type->newValueForUsers($cache_key, $need_users); - foreach ($data as $user_phid => $value) { - $raw_value = $type->getValueForStorage($value); + foreach ($data as $user_phid => $raw_value) { $data[$user_phid] = $raw_value; $writes[] = array( 'userPHID' => $user_phid, diff --git a/src/applications/people/storage/PhabricatorUser.php b/src/applications/people/storage/PhabricatorUser.php index 5dcd3ea073..e78cd25d70 100644 --- a/src/applications/people/storage/PhabricatorUser.php +++ b/src/applications/people/storage/PhabricatorUser.php @@ -1463,8 +1463,7 @@ final class PhabricatorUser 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); + $raw_value = $map[$user_phid]; $usable_value = $type->getValueFromStorage($raw_value); $this->rawCacheData[$key] = $raw_value; From aa4ba0fa92c1567e4a27e580af2eb712fc8c9413 Mon Sep 17 00:00:00 2001 From: epriestley Date: Sun, 5 Jun 2016 06:56:02 -0700 Subject: [PATCH 28/67] After toggling DarkConsole, force a user settings cache fill Summary: Ref T10078. Currently, you toggle DarkConsole and then load a page, but on the load we have to refill your settings cache since toggling DarkConsole dirtied it. This is fine, except that it makes it harder to understand what's going on with queries on a page. Just force it to reload right away instead. Test Plan: Toggled DarkConsole, reloaded page, no longer saw settings toggle-related cache fill. Reviewers: chad Reviewed By: chad Maniphest Tasks: T10078 Differential Revision: https://secure.phabricator.com/D16044 --- .../console/controller/DarkConsoleController.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/applications/console/controller/DarkConsoleController.php b/src/applications/console/controller/DarkConsoleController.php index 026083cb59..3a677920ee 100644 --- a/src/applications/console/controller/DarkConsoleController.php +++ b/src/applications/console/controller/DarkConsoleController.php @@ -59,6 +59,17 @@ final class DarkConsoleController extends PhabricatorController { $xactions = array(); $xactions[] = $preferences->newTransaction($key, $value); $editor->applyTransactions($preferences, $xactions); + + // Reload the user to regenerate their preferences cache. If we don't + // do this, the "Services" tab gets misleadingly spammed up with cache + // fills that are only filling because you toggled the console or switched + // tabs. This makes it harder to see what's really going on, so just force + // a cache regeneration here. + id(new PhabricatorPeopleQuery()) + ->setViewer($viewer) + ->withPHIDs(array($viewer->getPHID())) + ->needUserSettings(true) + ->execute(); } } From 7969f66dfe07809aaacc079ee4a84fa22688c74b Mon Sep 17 00:00:00 2001 From: epriestley Date: Sun, 5 Jun 2016 07:26:47 -0700 Subject: [PATCH 29/67] Fully modularize the "Quick Actions" menu Summary: Ref T10077. Currently, we issue 6+ queries on every page to build this menu, since the menu is built application-by-application. Build the menu with dedicated modules instead so a single "EditEngine" module can provide all of them with one query. I'd like to reduce this to 0 queries but I'm not totally sure what we want to do with this menu. This change removes these items, because EditEngine can not currently provide them: - Calendar: Eventually via EditEngine eventually. - Conpherence: Probably via EditEngine, doesn't seem too important. - People: Maybe via EditEngine, doesn't seem too important? "Welcome" is likely better? - Pholio: Eventually via EditEngine. It adds a bunch of other items as a side effect: {F1677151} This reduces the queries issued on every page by ~5. This also makes quick create actions visible while logged out (see T7073). Test Plan: - Viewed menu while logged in. - Viewed menu while logged out. - Viewed standalone version of menu. Reviewers: chad Reviewed By: chad Maniphest Tasks: T10077 Differential Revision: https://secure.phabricator.com/D16045 --- src/__phutil_library_map__.php | 4 ++ .../base/PhabricatorApplication.php | 11 ---- .../PhabricatorCalendarApplication.php | 12 ---- .../PhabricatorConpherenceApplication.php | 13 ---- .../PhabricatorHomeApplication.php | 64 ++++++------------- .../PhabricatorHomeQuickCreateController.php | 2 +- .../PhabricatorManiphestApplication.php | 6 -- .../PhabricatorPasteApplication.php | 6 -- .../PhabricatorPeopleApplication.php | 25 -------- .../PhabricatorPholioApplication.php | 12 ---- .../PhabricatorProjectApplication.php | 6 -- ...habricatorEditEngineCreateQuickActions.php | 46 +++++++++++++ .../quickmenu/PhabricatorQuickActions.php | 54 ++++++++++++++++ .../editengine/PhabricatorEditEngine.php | 36 +++++++---- 14 files changed, 151 insertions(+), 146 deletions(-) create mode 100644 src/applications/settings/quickmenu/PhabricatorEditEngineCreateQuickActions.php create mode 100644 src/applications/settings/quickmenu/PhabricatorQuickActions.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index a72ccdbeb6..ff39f84233 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -2370,6 +2370,7 @@ phutil_register_library_map(array( 'PhabricatorEditEngineConfigurationTransactionQuery' => 'applications/transactions/query/PhabricatorEditEngineConfigurationTransactionQuery.php', 'PhabricatorEditEngineConfigurationViewController' => 'applications/transactions/controller/PhabricatorEditEngineConfigurationViewController.php', 'PhabricatorEditEngineController' => 'applications/transactions/controller/PhabricatorEditEngineController.php', + 'PhabricatorEditEngineCreateQuickActions' => 'applications/settings/quickmenu/PhabricatorEditEngineCreateQuickActions.php', 'PhabricatorEditEngineExtension' => 'applications/transactions/engineextension/PhabricatorEditEngineExtension.php', 'PhabricatorEditEngineExtensionModule' => 'applications/transactions/engineextension/PhabricatorEditEngineExtensionModule.php', 'PhabricatorEditEngineListController' => 'applications/transactions/controller/PhabricatorEditEngineListController.php', @@ -3191,6 +3192,7 @@ phutil_register_library_map(array( 'PhabricatorQueryOrderItem' => 'infrastructure/query/order/PhabricatorQueryOrderItem.php', 'PhabricatorQueryOrderTestCase' => 'infrastructure/query/order/__tests__/PhabricatorQueryOrderTestCase.php', 'PhabricatorQueryOrderVector' => 'infrastructure/query/order/PhabricatorQueryOrderVector.php', + 'PhabricatorQuickActions' => 'applications/settings/quickmenu/PhabricatorQuickActions.php', 'PhabricatorRateLimitRequestExceptionHandler' => 'aphront/handler/PhabricatorRateLimitRequestExceptionHandler.php', 'PhabricatorRecaptchaConfigOptions' => 'applications/config/option/PhabricatorRecaptchaConfigOptions.php', 'PhabricatorRecipientHasBadgeEdgeType' => 'applications/badges/edge/PhabricatorRecipientHasBadgeEdgeType.php', @@ -6962,6 +6964,7 @@ phutil_register_library_map(array( 'PhabricatorEditEngineConfigurationTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 'PhabricatorEditEngineConfigurationViewController' => 'PhabricatorEditEngineController', 'PhabricatorEditEngineController' => 'PhabricatorApplicationTransactionController', + 'PhabricatorEditEngineCreateQuickActions' => 'PhabricatorQuickActions', 'PhabricatorEditEngineExtension' => 'Phobject', 'PhabricatorEditEngineExtensionModule' => 'PhabricatorConfigModule', 'PhabricatorEditEngineListController' => 'PhabricatorEditEngineController', @@ -7916,6 +7919,7 @@ phutil_register_library_map(array( 'Phobject', 'Iterator', ), + 'PhabricatorQuickActions' => 'Phobject', 'PhabricatorRateLimitRequestExceptionHandler' => 'PhabricatorRequestExceptionHandler', 'PhabricatorRecaptchaConfigOptions' => 'PhabricatorApplicationConfigOptions', 'PhabricatorRecipientHasBadgeEdgeType' => 'PhabricatorEdgeType', diff --git a/src/applications/base/PhabricatorApplication.php b/src/applications/base/PhabricatorApplication.php index a667717dd9..e010d4491f 100644 --- a/src/applications/base/PhabricatorApplication.php +++ b/src/applications/base/PhabricatorApplication.php @@ -335,17 +335,6 @@ abstract class PhabricatorApplication } - /** - * Build items for the "quick create" menu. - * - * @param PhabricatorUser The viewing user. - * @return list List of menu items. - */ - public function getQuickCreateItems(PhabricatorUser $viewer) { - return array(); - } - - /* -( Application Management )--------------------------------------------- */ diff --git a/src/applications/calendar/application/PhabricatorCalendarApplication.php b/src/applications/calendar/application/PhabricatorCalendarApplication.php index cf05e96666..71358366df 100644 --- a/src/applications/calendar/application/PhabricatorCalendarApplication.php +++ b/src/applications/calendar/application/PhabricatorCalendarApplication.php @@ -73,18 +73,6 @@ final class PhabricatorCalendarApplication extends PhabricatorApplication { ); } - public function getQuickCreateItems(PhabricatorUser $viewer) { - $items = array(); - - $item = id(new PHUIListItemView()) - ->setName(pht('Calendar Event')) - ->setIcon('fa-calendar') - ->setHref($this->getBaseURI().'event/create/'); - $items[] = $item; - - return $items; - } - public function getMailCommandObjects() { return array( 'event' => array( diff --git a/src/applications/conpherence/application/PhabricatorConpherenceApplication.php b/src/applications/conpherence/application/PhabricatorConpherenceApplication.php index ea06aaa456..8defc4d2be 100644 --- a/src/applications/conpherence/application/PhabricatorConpherenceApplication.php +++ b/src/applications/conpherence/application/PhabricatorConpherenceApplication.php @@ -48,19 +48,6 @@ final class PhabricatorConpherenceApplication extends PhabricatorApplication { ); } - public function getQuickCreateItems(PhabricatorUser $viewer) { - $items = array(); - - $item = id(new PHUIListItemView()) - ->setName(pht('Conpherence Room')) - ->setIcon('fa-comments') - ->setWorkflow(true) - ->setHref($this->getBaseURI().'new/'); - $items[] = $item; - - return $items; - } - public function getQuicksandURIPatternBlacklist() { return array( '/conpherence/.*', diff --git a/src/applications/home/application/PhabricatorHomeApplication.php b/src/applications/home/application/PhabricatorHomeApplication.php index 481ade105f..0d2a8fb346 100644 --- a/src/applications/home/application/PhabricatorHomeApplication.php +++ b/src/applications/home/application/PhabricatorHomeApplication.php @@ -42,51 +42,29 @@ final class PhabricatorHomeApplication extends PhabricatorApplication { PhabricatorUser $user, PhabricatorController $controller = null) { - $quick_create_items = $this->loadAllQuickCreateItems($user); $items = array(); + $create_id = celerity_generate_unique_node_id(); - if ($user->isLoggedIn() && - $user->isUserActivated() && - $quick_create_items) { - $create_id = celerity_generate_unique_node_id(); - Javelin::initBehavior( - 'aphlict-dropdown', - array( - 'bubbleID' => $create_id, - 'dropdownID' => 'phabricator-quick-create-menu', - 'local' => true, - 'desktop' => true, - 'right' => true, - )); + Javelin::initBehavior( + 'aphlict-dropdown', + array( + 'bubbleID' => $create_id, + 'dropdownID' => 'phabricator-quick-create-menu', + 'local' => true, + 'desktop' => true, + 'right' => true, + )); - $item = id(new PHUIListItemView()) - ->setName(pht('Create New...')) - ->setIcon('fa-plus') - ->addClass('core-menu-item') - ->setHref('/home/create/') - ->addSigil('quick-create-menu') - ->setID($create_id) - ->setAural(pht('Quick Create')) - ->setOrder(300); - $items[] = $item; - } - - return $items; - } - - public function loadAllQuickCreateItems(PhabricatorUser $viewer) { - $applications = id(new PhabricatorApplicationQuery()) - ->setViewer($viewer) - ->withInstalled(true) - ->execute(); - - $items = array(); - foreach ($applications as $application) { - $app_items = $application->getQuickCreateItems($viewer); - foreach ($app_items as $app_item) { - $items[] = $app_item; - } - } + $item = id(new PHUIListItemView()) + ->setName(pht('Quick Actions')) + ->setIcon('fa-plus') + ->addClass('core-menu-item') + ->setHref('/home/create/') + ->addSigil('quick-create-menu') + ->setID($create_id) + ->setAural(pht('Quick Actions')) + ->setOrder(300); + $items[] = $item; return $items; } @@ -95,7 +73,7 @@ final class PhabricatorHomeApplication extends PhabricatorApplication { PhabricatorUser $viewer, PhabricatorController $controller = null) { - $items = $this->loadAllQuickCreateItems($viewer); + $items = PhabricatorQuickActions::loadMenuItemsForUser($viewer); $view = null; if ($items) { diff --git a/src/applications/home/controller/PhabricatorHomeQuickCreateController.php b/src/applications/home/controller/PhabricatorHomeQuickCreateController.php index 6429443ed2..0100c40366 100644 --- a/src/applications/home/controller/PhabricatorHomeQuickCreateController.php +++ b/src/applications/home/controller/PhabricatorHomeQuickCreateController.php @@ -6,7 +6,7 @@ final class PhabricatorHomeQuickCreateController public function handleRequest(AphrontRequest $request) { $viewer = $this->getViewer(); - $items = $this->getCurrentApplication()->loadAllQuickCreateItems($viewer); + $items = PhabricatorQuickActions::loadMenuItemsForUser($viewer); $list = id(new PHUIObjectItemListView()) ->setUser($viewer); diff --git a/src/applications/maniphest/application/PhabricatorManiphestApplication.php b/src/applications/maniphest/application/PhabricatorManiphestApplication.php index 61186c7b8d..366353661f 100644 --- a/src/applications/maniphest/application/PhabricatorManiphestApplication.php +++ b/src/applications/maniphest/application/PhabricatorManiphestApplication.php @@ -90,12 +90,6 @@ final class PhabricatorManiphestApplication extends PhabricatorApplication { return $status; } - public function getQuickCreateItems(PhabricatorUser $viewer) { - return id(new ManiphestEditEngine()) - ->setViewer($viewer) - ->loadQuickCreateItems(); - } - public function supportsEmailIntegration() { return true; } diff --git a/src/applications/paste/application/PhabricatorPasteApplication.php b/src/applications/paste/application/PhabricatorPasteApplication.php index 1947c5377b..d644224e6e 100644 --- a/src/applications/paste/application/PhabricatorPasteApplication.php +++ b/src/applications/paste/application/PhabricatorPasteApplication.php @@ -76,12 +76,6 @@ final class PhabricatorPasteApplication extends PhabricatorApplication { ); } - public function getQuickCreateItems(PhabricatorUser $viewer) { - return id(new PhabricatorPasteEditEngine()) - ->setViewer($viewer) - ->loadQuickCreateItems(); - } - public function getMailCommandObjects() { return array( 'paste' => array( diff --git a/src/applications/people/application/PhabricatorPeopleApplication.php b/src/applications/people/application/PhabricatorPeopleApplication.php index c4bc87ea5a..5ea4e6aa1c 100644 --- a/src/applications/people/application/PhabricatorPeopleApplication.php +++ b/src/applications/people/application/PhabricatorPeopleApplication.php @@ -127,31 +127,6 @@ final class PhabricatorPeopleApplication extends PhabricatorApplication { return $status; } - public function getQuickCreateItems(PhabricatorUser $viewer) { - $items = array(); - - $can_create = PhabricatorPolicyFilter::hasCapability( - $viewer, - $this, - PeopleCreateUsersCapability::CAPABILITY); - - if ($can_create) { - $item = id(new PHUIListItemView()) - ->setName(pht('User Account')) - ->setIcon('fa-users') - ->setHref($this->getBaseURI().'create/'); - $items[] = $item; - } else if ($viewer->getIsAdmin()) { - $item = id(new PHUIListItemView()) - ->setName(pht('Bot Account')) - ->setIcon('fa-android') - ->setHref($this->getBaseURI().'new/bot/'); - $items[] = $item; - } - - return $items; - } - public function getApplicationSearchDocumentTypes() { return array( PhabricatorPeopleUserPHIDType::TYPECONST, diff --git a/src/applications/pholio/application/PhabricatorPholioApplication.php b/src/applications/pholio/application/PhabricatorPholioApplication.php index ceb1e36638..e7c841b68a 100644 --- a/src/applications/pholio/application/PhabricatorPholioApplication.php +++ b/src/applications/pholio/application/PhabricatorPholioApplication.php @@ -59,18 +59,6 @@ final class PhabricatorPholioApplication extends PhabricatorApplication { ); } - public function getQuickCreateItems(PhabricatorUser $viewer) { - $items = array(); - - $item = id(new PHUIListItemView()) - ->setName(pht('Pholio Mock')) - ->setIcon('fa-picture-o') - ->setHref($this->getBaseURI().'create/'); - $items[] = $item; - - return $items; - } - protected function getCustomCapabilities() { return array( PholioDefaultViewCapability::CAPABILITY => array( diff --git a/src/applications/project/application/PhabricatorProjectApplication.php b/src/applications/project/application/PhabricatorProjectApplication.php index 41620ad4e4..b8a6149e4f 100644 --- a/src/applications/project/application/PhabricatorProjectApplication.php +++ b/src/applications/project/application/PhabricatorProjectApplication.php @@ -108,12 +108,6 @@ final class PhabricatorProjectApplication extends PhabricatorApplication { ); } - public function getQuickCreateItems(PhabricatorUser $viewer) { - return id(new PhabricatorProjectEditEngine()) - ->setViewer($viewer) - ->loadQuickCreateItems(); - } - protected function getCustomCapabilities() { return array( ProjectCreateProjectsCapability::CAPABILITY => array(), diff --git a/src/applications/settings/quickmenu/PhabricatorEditEngineCreateQuickActions.php b/src/applications/settings/quickmenu/PhabricatorEditEngineCreateQuickActions.php new file mode 100644 index 0000000000..e4a8c520e5 --- /dev/null +++ b/src/applications/settings/quickmenu/PhabricatorEditEngineCreateQuickActions.php @@ -0,0 +1,46 @@ +getViewer(); + + $engines = PhabricatorEditEngine::getAllEditEngines(); + + foreach ($engines as $key => $engine) { + if (!$engine->hasQuickCreateActions()) { + unset($engines[$key]); + } + } + + if (!$engines) { + return array(); + } + + $engine_keys = array_keys($engines); + + $configs = id(new PhabricatorEditEngineConfigurationQuery()) + ->setViewer($viewer) + ->withEngineKeys($engine_keys) + ->withIsDefault(true) + ->withIsDisabled(false) + ->execute(); + $configs = msort($configs, 'getCreateSortKey'); + $configs = mgroup($configs, 'getEngineKey'); + + $items = array(); + foreach ($engines as $key => $engine) { + $engine_configs = idx($configs, $key, array()); + $engine_items = $engine->newQuickCreateActions($engine_configs); + foreach ($engine_items as $engine_item) { + $items[] = $engine_item; + } + } + + return $items; + } + +} diff --git a/src/applications/settings/quickmenu/PhabricatorQuickActions.php b/src/applications/settings/quickmenu/PhabricatorQuickActions.php new file mode 100644 index 0000000000..8a7711b809 --- /dev/null +++ b/src/applications/settings/quickmenu/PhabricatorQuickActions.php @@ -0,0 +1,54 @@ +viewer = $viewer; + return $this; + } + + public function getViewer() { + return $this->viewer; + } + + public function isEnabled() { + return true; + } + + abstract public function getQuickMenuItems(); + + final public function getQuickActionsKey() { + return $this->getPhobjectClassConstant('QUICKACTIONSKEY'); + } + + public static function getAllQuickActions() { + return id(new PhutilClassMapQuery()) + ->setAncestorClass(__CLASS__) + ->setUniqueMethod('getQuickActionsKey') + ->execute(); + } + + public static function loadMenuItemsForUser(PhabricatorUser $viewer) { + $actions = self::getAllQuickActions(); + + foreach ($actions as $key => $action) { + $action->setViewer($viewer); + if (!$action->isEnabled()) { + unset($actions[$key]); + continue; + } + } + + $items = array(); + foreach ($actions as $key => $action) { + foreach ($action->getQuickMenuItems() as $item) { + $items[] = $item; + } + } + + return $items; + } + +} diff --git a/src/applications/transactions/editengine/PhabricatorEditEngine.php b/src/applications/transactions/editengine/PhabricatorEditEngine.php index 7374bf786a..62cad81531 100644 --- a/src/applications/transactions/editengine/PhabricatorEditEngine.php +++ b/src/applications/transactions/editengine/PhabricatorEditEngine.php @@ -1972,20 +1972,34 @@ abstract class PhabricatorEditEngine return $application->getIcon(); } - public function loadQuickCreateItems() { - $items = array(); - - if (!$this->hasCreateCapability()) { - return $items; + public function hasQuickCreateActions() { + if (!$this->isEngineConfigurable()) { + return false; } - $configs = $this->loadUsableConfigurationsForCreate(); + return true; + } + + public function newQuickCreateActions(array $configs) { + $items = array(); if (!$configs) { - // No items to add. - } else if (count($configs) == 1) { + return array(); + } + + // If the viewer is logged in and can't create objects, don't show the + // menu item. If they're logged out, we assume they could create objects + // if they logged in, so we show the item as a hint about how to + // accomplish the action. + if ($this->getViewer()->isLoggedIn()) { + if (!$this->hasCreateCapability()) { + return array(); + } + } + + if (count($configs) == 1) { $config = head($configs); - $items[] = $this->newQuickCreateItem($config); + $items[] = $this->newQuickCreateAction($config); } else { $group_name = $this->getQuickCreateMenuHeaderText(); @@ -1994,7 +2008,7 @@ abstract class PhabricatorEditEngine ->setName($group_name); foreach ($configs as $config) { - $items[] = $this->newQuickCreateItem($config) + $items[] = $this->newQuickCreateAction($config) ->setIndented(true); } } @@ -2017,7 +2031,7 @@ abstract class PhabricatorEditEngine return $configs; } - private function newQuickCreateItem( + private function newQuickCreateAction( PhabricatorEditEngineConfiguration $config) { $item_name = $config->getName(); From c4de87a07ab07e3ea27153e84e94905038155542 Mon Sep 17 00:00:00 2001 From: epriestley Date: Sun, 5 Jun 2016 07:47:05 -0700 Subject: [PATCH 30/67] Improve some settings-related performance Summary: Ref T4103. Two small improvements: - Don't work as hard to validate translations. We just need to know if a translation exists, we don't need to count how many strings it has and build the entire menu. - Allow `getUserSetting()` to work on any setting without doing all the application/visibility checks. It's OK for code to look at, say, your "Conpherence Notifications" setting even if that application is not installed for you. Test Plan: Used XHProf and saw 404 page drop from ~60ms to ~40ms locally. Reviewers: chad Reviewed By: chad Maniphest Tasks: T4103 Differential Revision: https://secure.phabricator.com/D16046 --- .../application/PhabricatorHomeApplication.php | 16 +++++++++++++++- .../people/storage/PhabricatorUser.php | 5 ++++- .../setting/PhabricatorTranslationSetting.php | 5 +++++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/applications/home/application/PhabricatorHomeApplication.php b/src/applications/home/application/PhabricatorHomeApplication.php index 0d2a8fb346..9b31ee537f 100644 --- a/src/applications/home/application/PhabricatorHomeApplication.php +++ b/src/applications/home/application/PhabricatorHomeApplication.php @@ -2,6 +2,7 @@ final class PhabricatorHomeApplication extends PhabricatorApplication { + private $quickItems; const DASHBOARD_DEFAULT = 'dashboard:default'; public function getBaseURI() { @@ -42,6 +43,11 @@ final class PhabricatorHomeApplication extends PhabricatorApplication { PhabricatorUser $user, PhabricatorController $controller = null) { + $quick_items = $this->getQuickActionItems($user); + if (!$quick_items) { + return array(); + } + $items = array(); $create_id = celerity_generate_unique_node_id(); @@ -73,7 +79,7 @@ final class PhabricatorHomeApplication extends PhabricatorApplication { PhabricatorUser $viewer, PhabricatorController $controller = null) { - $items = PhabricatorQuickActions::loadMenuItemsForUser($viewer); + $items = $this->getQuickActionItems($viewer); $view = null; if ($items) { @@ -94,4 +100,12 @@ final class PhabricatorHomeApplication extends PhabricatorApplication { return $view; } + private function getQuickActionItems(PhabricatorUser $viewer) { + if ($this->quickItems === null) { + $items = PhabricatorQuickActions::loadMenuItemsForUser($viewer); + $this->quickItems = $items; + } + return $this->quickItems; + } + } diff --git a/src/applications/people/storage/PhabricatorUser.php b/src/applications/people/storage/PhabricatorUser.php index e78cd25d70..5e8cb77a72 100644 --- a/src/applications/people/storage/PhabricatorUser.php +++ b/src/applications/people/storage/PhabricatorUser.php @@ -486,7 +486,10 @@ final class PhabricatorUser $settings = array(); } - $defaults = PhabricatorSetting::getAllEnabledSettings($this); + // NOTE: To slightly improve performance, we're using all settings here, + // not just settings that are enabled for the current viewer. It's fine to + // get the value of a setting that we wouldn't let the user edit in the UI. + $defaults = PhabricatorSetting::getAllSettings(); if (array_key_exists($key, $settings)) { $value = $settings[$key]; diff --git a/src/applications/settings/setting/PhabricatorTranslationSetting.php b/src/applications/settings/setting/PhabricatorTranslationSetting.php index 3f4c5a43db..a30ded554d 100644 --- a/src/applications/settings/setting/PhabricatorTranslationSetting.php +++ b/src/applications/settings/setting/PhabricatorTranslationSetting.php @@ -26,6 +26,11 @@ final class PhabricatorTranslationSetting 'Choose which language you would like the Phabricator UI to use.'); } + public function assertValidValue($value) { + $locales = PhutilLocale::loadAllLocales(); + return isset($locales[$value]); + } + protected function getSelectOptionGroups() { $is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business'); $locales = PhutilLocale::loadAllLocales(); From c9ef7aeaa34a32042a3974e88b8bd1b650f47221 Mon Sep 17 00:00:00 2001 From: epriestley Date: Sun, 5 Jun 2016 10:23:28 -0700 Subject: [PATCH 31/67] Validate select/option settings more strictly when reading them Summary: Ref T4103. If the database has `""` (empty string) for select/option settings, we can let that value be effective in the UI right now. One consequence is that timestamps can vanish from the UI. Instead, be stricter and discard it as an invalid value. Test Plan: - Forced `time-format` setting to `''`. - Saw timestamps vanish before change. - Saw timestamps return to the default value after change. Reviewers: chad Reviewed By: chad Maniphest Tasks: T4103 Differential Revision: https://secure.phabricator.com/D16047 --- .../setting/PhabricatorSelectSetting.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/applications/settings/setting/PhabricatorSelectSetting.php b/src/applications/settings/setting/PhabricatorSelectSetting.php index 44369baa46..bccf450454 100644 --- a/src/applications/settings/setting/PhabricatorSelectSetting.php +++ b/src/applications/settings/setting/PhabricatorSelectSetting.php @@ -27,6 +27,25 @@ abstract class PhabricatorSelectSetting ->setOptions($options); } + public function assertValidValue($value) { + // This is a slightly stricter check than the transaction check. It's + // OK for empty string to go through transactions because it gets converted + // to null later, but we shouldn't be reading the empty string from + // storage. + if ($value === null) { + return; + } + + if (!strlen($value)) { + throw new Exception( + pht( + 'Empty string is not a valid setting for "%s".', + $this->getSettingName())); + } + + $this->validateTransactionValue($value); + } + final public function validateTransactionValue($value) { if (!strlen($value)) { return; From 421bf2e548f06411a2ce86583333ec5e3e92c643 Mon Sep 17 00:00:00 2001 From: epriestley Date: Sun, 5 Jun 2016 12:38:04 -0700 Subject: [PATCH 32/67] Allow administrators to configure global default settings Summary: Ref T4103. This just adds a single global default setting group, not full profiles. Primarily, I'm not sure how administrators are supposed to set profiles for users, since most ways user accounts get created don't really support setting roles.. When we figure that out, it should be reasonably easy to extend this. There also isn't much of a need for this now, since pretty much everyone just wants to turn off mail. Test Plan: - Edited personal settings. - Edited global settings. - Edited a bot's settings. - Tried to edit some other user's settings. - Saw defaults change appropriately as I edited global and personal settings. {F1677266} Reviewers: chad Reviewed By: chad Maniphest Tasks: T4103 Differential Revision: https://secure.phabricator.com/D16048 --- .../20160605.user.01.prefnulluser.sql | 2 + .../20160605.user.02.prefbuiltin.sql | 2 + .../20160605.user.03.builtinunique.sql | 2 + src/__phutil_library_map__.php | 4 + .../PhabricatorConduitTokensSettingsPanel.php | 10 +-- .../DiffusionSetPasswordSettingsPanel.php | 10 +-- .../PhabricatorUserPreferencesCacheType.php | 24 +++++- ...abricatorPeopleProfileManageController.php | 2 +- .../PhabricatorSettingsApplication.php | 10 ++- .../PhabricatorSettingsListController.php | 48 +++++++++++ .../PhabricatorSettingsMainController.php | 84 ++++++++++++++---- .../editor/PhabricatorSettingsEditEngine.php | 23 ++++- .../panel/PhabricatorAccountSettingsPanel.php | 6 +- .../PhabricatorActivitySettingsPanel.php | 8 +- ...torConpherencePreferencesSettingsPanel.php | 4 + .../PhabricatorDateTimeSettingsPanel.php | 6 +- ...catorDesktopNotificationsSettingsPanel.php | 2 +- ...catorDeveloperPreferencesSettingsPanel.php | 4 + ...habricatorDiffPreferencesSettingsPanel.php | 4 + ...ricatorDisplayPreferencesSettingsPanel.php | 4 + .../PhabricatorEditEngineSettingsPanel.php | 8 +- .../PhabricatorEmailDeliverySettingsPanel.php | 6 +- .../PhabricatorEmailFormatSettingsPanel.php | 6 +- ...abricatorEmailPreferencesSettingsPanel.php | 12 ++- ...habricatorHomePreferencesSettingsPanel.php | 6 +- .../panel/PhabricatorSSHKeysSettingsPanel.php | 14 ++- .../panel/PhabricatorSettingsPanel.php | 53 +++++++----- .../query/PhabricatorUserPreferencesQuery.php | 51 +++++++++++ ...PhabricatorUserPreferencesSearchEngine.php | 86 +++++++++++++++++++ .../PhabricatorPinnedApplicationsSetting.php | 5 ++ .../settings/setting/PhabricatorSetting.php | 7 +- .../storage/PhabricatorUserPreferences.php | 41 ++++++++- .../editengine/PhabricatorEditEngine.php | 6 +- 33 files changed, 464 insertions(+), 96 deletions(-) create mode 100644 resources/sql/autopatches/20160605.user.01.prefnulluser.sql create mode 100644 resources/sql/autopatches/20160605.user.02.prefbuiltin.sql create mode 100644 resources/sql/autopatches/20160605.user.03.builtinunique.sql create mode 100644 src/applications/settings/controller/PhabricatorSettingsListController.php create mode 100644 src/applications/settings/query/PhabricatorUserPreferencesSearchEngine.php diff --git a/resources/sql/autopatches/20160605.user.01.prefnulluser.sql b/resources/sql/autopatches/20160605.user.01.prefnulluser.sql new file mode 100644 index 0000000000..e592c4c82f --- /dev/null +++ b/resources/sql/autopatches/20160605.user.01.prefnulluser.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_user.user_preferences + CHANGE userPHID userPHID VARBINARY(64); diff --git a/resources/sql/autopatches/20160605.user.02.prefbuiltin.sql b/resources/sql/autopatches/20160605.user.02.prefbuiltin.sql new file mode 100644 index 0000000000..46b8db6580 --- /dev/null +++ b/resources/sql/autopatches/20160605.user.02.prefbuiltin.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_user.user_preferences + ADD builtinKey VARCHAR(32) COLLATE {$COLLATE_TEXT}; diff --git a/resources/sql/autopatches/20160605.user.03.builtinunique.sql b/resources/sql/autopatches/20160605.user.03.builtinunique.sql new file mode 100644 index 0000000000..ea13010fb8 --- /dev/null +++ b/resources/sql/autopatches/20160605.user.03.builtinunique.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_user.user_preferences + ADD UNIQUE KEY `key_builtin` (builtinKey); diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index ff39f84233..ea090a3932 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -3393,6 +3393,7 @@ phutil_register_library_map(array( 'PhabricatorSettingsDeveloperPanelGroup' => 'applications/settings/panelgroup/PhabricatorSettingsDeveloperPanelGroup.php', 'PhabricatorSettingsEditEngine' => 'applications/settings/editor/PhabricatorSettingsEditEngine.php', 'PhabricatorSettingsEmailPanelGroup' => 'applications/settings/panelgroup/PhabricatorSettingsEmailPanelGroup.php', + 'PhabricatorSettingsListController' => 'applications/settings/controller/PhabricatorSettingsListController.php', 'PhabricatorSettingsLogsPanelGroup' => 'applications/settings/panelgroup/PhabricatorSettingsLogsPanelGroup.php', 'PhabricatorSettingsMainController' => 'applications/settings/controller/PhabricatorSettingsMainController.php', 'PhabricatorSettingsMainMenuBarExtension' => 'applications/settings/extension/PhabricatorSettingsMainMenuBarExtension.php', @@ -3650,6 +3651,7 @@ phutil_register_library_map(array( 'PhabricatorUserPreferencesEditor' => 'applications/settings/editor/PhabricatorUserPreferencesEditor.php', 'PhabricatorUserPreferencesPHIDType' => 'applications/settings/phid/PhabricatorUserPreferencesPHIDType.php', 'PhabricatorUserPreferencesQuery' => 'applications/settings/query/PhabricatorUserPreferencesQuery.php', + 'PhabricatorUserPreferencesSearchEngine' => 'applications/settings/query/PhabricatorUserPreferencesSearchEngine.php', 'PhabricatorUserPreferencesTransaction' => 'applications/settings/storage/PhabricatorUserPreferencesTransaction.php', 'PhabricatorUserPreferencesTransactionQuery' => 'applications/settings/query/PhabricatorUserPreferencesTransactionQuery.php', 'PhabricatorUserProfile' => 'applications/people/storage/PhabricatorUserProfile.php', @@ -8170,6 +8172,7 @@ phutil_register_library_map(array( 'PhabricatorSettingsDeveloperPanelGroup' => 'PhabricatorSettingsPanelGroup', 'PhabricatorSettingsEditEngine' => 'PhabricatorEditEngine', 'PhabricatorSettingsEmailPanelGroup' => 'PhabricatorSettingsPanelGroup', + 'PhabricatorSettingsListController' => 'PhabricatorController', 'PhabricatorSettingsLogsPanelGroup' => 'PhabricatorSettingsPanelGroup', 'PhabricatorSettingsMainController' => 'PhabricatorController', 'PhabricatorSettingsMainMenuBarExtension' => 'PhabricatorMainMenuBarExtension', @@ -8470,6 +8473,7 @@ phutil_register_library_map(array( 'PhabricatorUserPreferencesEditor' => 'PhabricatorApplicationTransactionEditor', 'PhabricatorUserPreferencesPHIDType' => 'PhabricatorPHIDType', 'PhabricatorUserPreferencesQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', + 'PhabricatorUserPreferencesSearchEngine' => 'PhabricatorApplicationSearchEngine', 'PhabricatorUserPreferencesTransaction' => 'PhabricatorApplicationTransaction', 'PhabricatorUserPreferencesTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 'PhabricatorUserProfile' => 'PhabricatorUserDAO', diff --git a/src/applications/conduit/settings/PhabricatorConduitTokensSettingsPanel.php b/src/applications/conduit/settings/PhabricatorConduitTokensSettingsPanel.php index 80e92d45c9..59161d2814 100644 --- a/src/applications/conduit/settings/PhabricatorConduitTokensSettingsPanel.php +++ b/src/applications/conduit/settings/PhabricatorConduitTokensSettingsPanel.php @@ -3,7 +3,11 @@ final class PhabricatorConduitTokensSettingsPanel extends PhabricatorSettingsPanel { - public function isEditableByAdministrators() { + public function isManagementPanel() { + if ($this->getUser()->getIsMailingList()) { + return false; + } + return true; } @@ -20,10 +24,6 @@ final class PhabricatorConduitTokensSettingsPanel } public function isEnabled() { - if ($this->getUser()->getIsMailingList()) { - return false; - } - return true; } diff --git a/src/applications/diffusion/panel/DiffusionSetPasswordSettingsPanel.php b/src/applications/diffusion/panel/DiffusionSetPasswordSettingsPanel.php index aa1ffb2e43..1c42fb027b 100644 --- a/src/applications/diffusion/panel/DiffusionSetPasswordSettingsPanel.php +++ b/src/applications/diffusion/panel/DiffusionSetPasswordSettingsPanel.php @@ -2,7 +2,11 @@ final class DiffusionSetPasswordSettingsPanel extends PhabricatorSettingsPanel { - public function isEditableByAdministrators() { + public function isManagementPanel() { + if ($this->getUser()->getIsMailingList()) { + return false; + } + return true; } @@ -19,10 +23,6 @@ final class DiffusionSetPasswordSettingsPanel extends PhabricatorSettingsPanel { } public function isEnabled() { - if ($this->getUser()->getIsMailingList()) { - return false; - } - return PhabricatorEnv::getEnvConfig('diffusion.allow-http-auth'); } diff --git a/src/applications/people/cache/PhabricatorUserPreferencesCacheType.php b/src/applications/people/cache/PhabricatorUserPreferencesCacheType.php index a2187a4599..1e8999837a 100644 --- a/src/applications/people/cache/PhabricatorUserPreferencesCacheType.php +++ b/src/applications/people/cache/PhabricatorUserPreferencesCacheType.php @@ -24,14 +24,34 @@ final class PhabricatorUserPreferencesCacheType public function newValueForUsers($key, array $users) { $viewer = $this->getViewer(); - $user_phids = mpull($users, 'getPHID'); + $users = mpull($users, null, 'getPHID'); + $user_phids = array_keys($users); $preferences = id(new PhabricatorUserPreferencesQuery()) ->setViewer($viewer) ->withUserPHIDs($user_phids) ->execute(); - $settings = mpull($preferences, 'getPreferences', 'getUserPHID'); + $all_settings = PhabricatorSetting::getAllSettings(); + + $settings = array(); + foreach ($preferences as $preference) { + $user_phid = $preference->getUserPHID(); + foreach ($all_settings as $key => $setting) { + $value = $preference->getSettingValue($key); + + // As an optimization, we omit the value from the cache if it is + // exactly the same as the hardcoded default. + $default_value = id(clone $setting) + ->setViewer($users[$user_phid]) + ->getSettingDefaultValue(); + if ($value === $default_value) { + continue; + } + + $settings[$user_phid][$key] = $value; + } + } $results = array(); foreach ($user_phids as $user_phid) { diff --git a/src/applications/people/controller/PhabricatorPeopleProfileManageController.php b/src/applications/people/controller/PhabricatorPeopleProfileManageController.php index 08b438eaa2..d298660bd3 100644 --- a/src/applications/people/controller/PhabricatorPeopleProfileManageController.php +++ b/src/applications/people/controller/PhabricatorPeopleProfileManageController.php @@ -124,7 +124,7 @@ final class PhabricatorPeopleProfileManageController ->setName(pht('Edit Settings')) ->setDisabled(!$can_edit) ->setWorkflow(!$can_edit) - ->setHref('/settings/'.$user->getID().'/')); + ->setHref('/settings/user/'.$user->getUsername().'/')); if ($user->getIsAdmin()) { $empower_icon = 'fa-arrow-circle-o-down'; diff --git a/src/applications/settings/application/PhabricatorSettingsApplication.php b/src/applications/settings/application/PhabricatorSettingsApplication.php index 5d0ef5c44e..4adf8ee19f 100644 --- a/src/applications/settings/application/PhabricatorSettingsApplication.php +++ b/src/applications/settings/application/PhabricatorSettingsApplication.php @@ -27,12 +27,14 @@ final class PhabricatorSettingsApplication extends PhabricatorApplication { } public function getRoutes() { + $panel_pattern = '(?:page/(?P[^/]+)/(?:(?Psaved)/)?)?'; + return array( '/settings/' => array( - '(?:(?P\d+)/)?'. - '(?:panel/(?P(?P[^/]+))/'. - '(?:(?Psaved)/)?'. - ')?' + $this->getQueryRoutePattern() => 'PhabricatorSettingsListController', + 'user/(?P[^/]+)/'.$panel_pattern + => 'PhabricatorSettingsMainController', + 'builtin/(?Pglobal)/'.$panel_pattern => 'PhabricatorSettingsMainController', 'adjust/' => 'PhabricatorSettingsAdjustController', 'timezone/(?P[^/]+)/' diff --git a/src/applications/settings/controller/PhabricatorSettingsListController.php b/src/applications/settings/controller/PhabricatorSettingsListController.php new file mode 100644 index 0000000000..5e38a534f5 --- /dev/null +++ b/src/applications/settings/controller/PhabricatorSettingsListController.php @@ -0,0 +1,48 @@ +getViewer(); + + // If the viewer isn't an administrator, just redirect them to their own + // settings panel. + if (!$viewer->getIsAdmin()) { + $settings_uri = '/user/'.$viewer->getUsername().'/'; + $settings_uri = $this->getApplicationURI($settings_uri); + return id(new AphrontRedirectResponse()) + ->setURI($settings_uri); + } + + return id(new PhabricatorUserPreferencesSearchEngine()) + ->setController($this) + ->buildResponse(); + } + + protected function buildApplicationCrumbs() { + $crumbs = parent::buildApplicationCrumbs(); + + $viewer = $this->getViewer(); + if ($viewer->getIsAdmin()) { + $builtin_global = PhabricatorUserPreferences::BUILTIN_GLOBAL_DEFAULT; + $global_settings = id(new PhabricatorUserPreferencesQuery()) + ->setViewer($viewer) + ->withBuiltinKeys( + array( + $builtin_global, + )) + ->execute(); + if (!$global_settings) { + $action = id(new PHUIListItemView()) + ->setName(pht('Create Global Defaults')) + ->setHref('/settings/builtin/'.$builtin_global.'/') + ->setIcon('fa-plus'); + $crumbs->addAction($action); + } + } + + return $crumbs; + } + +} diff --git a/src/applications/settings/controller/PhabricatorSettingsMainController.php b/src/applications/settings/controller/PhabricatorSettingsMainController.php index e16a9b2e1d..8c58772fe2 100644 --- a/src/applications/settings/controller/PhabricatorSettingsMainController.php +++ b/src/applications/settings/controller/PhabricatorSettingsMainController.php @@ -4,6 +4,8 @@ final class PhabricatorSettingsMainController extends PhabricatorController { private $user; + private $builtinKey; + private $preferences; private function getUser() { return $this->user; @@ -21,15 +23,39 @@ final class PhabricatorSettingsMainController return ($viewer_phid == $user_phid); } + private function isTemplate() { + return ($this->builtinKey !== null); + } + public function handleRequest(AphrontRequest $request) { $viewer = $this->getViewer(); - $id = $request->getURIData('id'); - $key = $request->getURIData('key'); - if ($id) { + $username = $request->getURIData('username'); + $builtin = $request->getURIData('builtin'); + + $key = $request->getURIData('pageKey'); + + if ($builtin) { + $this->builtinKey = $builtin; + + $preferences = id(new PhabricatorUserPreferencesQuery()) + ->setViewer($viewer) + ->withBuiltinKeys(array($builtin)) + ->requireCapabilities( + array( + PhabricatorPolicyCapability::CAN_VIEW, + PhabricatorPolicyCapability::CAN_EDIT, + )) + ->executeOne(); + if (!$preferences) { + $preferences = id(new PhabricatorUserPreferences()) + ->attachUser(null) + ->setBuiltinKey($builtin); + } + } else { $user = id(new PhabricatorPeopleQuery()) ->setViewer($viewer) - ->withIDs(array($id)) + ->withUsernames(array($username)) ->requireCapabilities( array( PhabricatorPolicyCapability::CAN_VIEW, @@ -41,19 +67,27 @@ final class PhabricatorSettingsMainController return new Aphront404Response(); } + $preferences = PhabricatorUserPreferences::loadUserPreferences($user); $this->user = $user; - } else { - $this->user = $viewer; } - $panels = $this->buildPanels(); + if (!$preferences) { + return new Aphront404Response(); + } + + PhabricatorPolicyFilter::requireCapability( + $viewer, + $preferences, + PhabricatorPolicyCapability::CAN_EDIT); + + $this->preferences = $preferences; + + $panels = $this->buildPanels($preferences); $nav = $this->renderSideNav($panels); $key = $nav->selectFilter($key, head($panels)->getPanelKey()); $panel = $panels[$key] - ->setUser($this->getUser()) - ->setViewer($viewer) ->setController($this) ->setNavigation($nav); @@ -79,22 +113,30 @@ final class PhabricatorSettingsMainController } - private function buildPanels() { + private function buildPanels(PhabricatorUserPreferences $preferences) { $viewer = $this->getViewer(); $panels = PhabricatorSettingsPanel::getAllDisplayPanels(); $result = array(); foreach ($panels as $key => $panel) { $panel - ->setViewer($viewer) - ->setUser($this->user); + ->setPreferences($preferences) + ->setViewer($viewer); + + if ($this->user) { + $panel->setUser($this->user); + } if (!$panel->isEnabled()) { continue; } - if (!$this->isSelf()) { - if (!$panel->isEditableByAdministrators()) { + if ($this->isTemplate()) { + if (!$panel->isTemplatePanel()) { + continue; + } + } else { + if (!$this->isSelf() && !$panel->isManagementPanel()) { continue; } } @@ -120,10 +162,11 @@ final class PhabricatorSettingsMainController private function renderSideNav(array $panels) { $nav = new AphrontSideNavFilterView(); - if ($this->isSelf()) { - $base_uri = 'panel/'; + if ($this->isTemplate()) { + $base_uri = 'builtin/'.$this->builtinKey.'/page/'; } else { - $base_uri = $this->getUser()->getID().'/panel/'; + $user = $this->getUser(); + $base_uri = 'user/'.$user->getUsername().'/page/'; } $nav->setBaseURI(new PhutilURI($this->getApplicationURI($base_uri))); @@ -143,8 +186,11 @@ final class PhabricatorSettingsMainController } public function buildApplicationMenu() { - $panels = $this->buildPanels(); - return $this->renderSideNav($panels)->getMenu(); + if ($this->preferences) { + $panels = $this->buildPanels($this->preferences); + return $this->renderSideNav($panels)->getMenu(); + } + return parent::buildApplicationMenu(); } protected function buildApplicationCrumbs() { diff --git a/src/applications/settings/editor/PhabricatorSettingsEditEngine.php b/src/applications/settings/editor/PhabricatorSettingsEditEngine.php index 69274eb3be..154ef0bac7 100644 --- a/src/applications/settings/editor/PhabricatorSettingsEditEngine.php +++ b/src/applications/settings/editor/PhabricatorSettingsEditEngine.php @@ -67,7 +67,15 @@ final class PhabricatorSettingsEditEngine } protected function getObjectEditShortText($object) { - return pht('Edit Settings'); + if (!$object->getUser()) { + return pht('Global Defaults'); + } else { + if ($this->getIsSelfEdit()) { + return pht('Personal Settings'); + } else { + return pht('Account Settings'); + } + } } protected function getObjectCreateShortText() { @@ -85,7 +93,7 @@ final class PhabricatorSettingsEditEngine } protected function getEditorURI() { - return '/settings/edit/'; + throw new PhutilMethodNotImplementedException(); } protected function getObjectCreateCancelURI($object) { @@ -93,15 +101,22 @@ final class PhabricatorSettingsEditEngine } protected function getObjectViewURI($object) { - // TODO: This isn't correct... - return '/settings/user/'.$this->getViewer()->getUsername().'/'; + return $object->getEditURI(); } protected function getCreateNewObjectPolicy() { return PhabricatorPolicies::POLICY_ADMIN; } + public function getEffectiveObjectEditDoneURI($object) { + return parent::getEffectiveObjectViewURI($object).'saved/'; + } + public function getEffectiveObjectEditCancelURI($object) { + if (!$object->getUser()) { + return '/settings/'; + } + if ($this->getIsSelfEdit()) { return null; } diff --git a/src/applications/settings/panel/PhabricatorAccountSettingsPanel.php b/src/applications/settings/panel/PhabricatorAccountSettingsPanel.php index f1734f1432..a7eb3ad099 100644 --- a/src/applications/settings/panel/PhabricatorAccountSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorAccountSettingsPanel.php @@ -13,7 +13,11 @@ final class PhabricatorAccountSettingsPanel return PhabricatorSettingsAccountPanelGroup::PANELGROUPKEY; } - public function isEditableByAdministrators() { + public function isManagementPanel() { + return true; + } + + public function isTemplatePanel() { return true; } diff --git a/src/applications/settings/panel/PhabricatorActivitySettingsPanel.php b/src/applications/settings/panel/PhabricatorActivitySettingsPanel.php index 23c212bf30..a16984c140 100644 --- a/src/applications/settings/panel/PhabricatorActivitySettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorActivitySettingsPanel.php @@ -2,10 +2,6 @@ final class PhabricatorActivitySettingsPanel extends PhabricatorSettingsPanel { - public function isEditableByAdministrators() { - return true; - } - public function getPanelKey() { return 'activity'; } @@ -61,4 +57,8 @@ final class PhabricatorActivitySettingsPanel extends PhabricatorSettingsPanel { return array($panel, $pager_box); } + public function isManagementPanel() { + return true; + } + } diff --git a/src/applications/settings/panel/PhabricatorConpherencePreferencesSettingsPanel.php b/src/applications/settings/panel/PhabricatorConpherencePreferencesSettingsPanel.php index 68c1e21dfe..87643a08ad 100644 --- a/src/applications/settings/panel/PhabricatorConpherencePreferencesSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorConpherencePreferencesSettingsPanel.php @@ -13,4 +13,8 @@ final class PhabricatorConpherencePreferencesSettingsPanel return PhabricatorSettingsApplicationsPanelGroup::PANELGROUPKEY; } + public function isTemplatePanel() { + return true; + } + } diff --git a/src/applications/settings/panel/PhabricatorDateTimeSettingsPanel.php b/src/applications/settings/panel/PhabricatorDateTimeSettingsPanel.php index 7d554b579c..e5ca46510e 100644 --- a/src/applications/settings/panel/PhabricatorDateTimeSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorDateTimeSettingsPanel.php @@ -13,7 +13,11 @@ final class PhabricatorDateTimeSettingsPanel return PhabricatorSettingsAccountPanelGroup::PANELGROUPKEY; } - public function isEditableByAdministrators() { + public function isManagementPanel() { + return true; + } + + public function isTemplatePanel() { return true; } diff --git a/src/applications/settings/panel/PhabricatorDesktopNotificationsSettingsPanel.php b/src/applications/settings/panel/PhabricatorDesktopNotificationsSettingsPanel.php index 207d2300b2..e987fed4ab 100644 --- a/src/applications/settings/panel/PhabricatorDesktopNotificationsSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorDesktopNotificationsSettingsPanel.php @@ -27,7 +27,7 @@ final class PhabricatorDesktopNotificationsSettingsPanel public function processRequest(AphrontRequest $request) { $viewer = $this->getViewer(); - $preferences = $this->loadTargetPreferences(); + $preferences = $this->getPreferences(); $notifications_key = PhabricatorDesktopNotificationsSetting::SETTINGKEY; $notifications_value = $preferences->getSettingValue($notifications_key); diff --git a/src/applications/settings/panel/PhabricatorDeveloperPreferencesSettingsPanel.php b/src/applications/settings/panel/PhabricatorDeveloperPreferencesSettingsPanel.php index 9f49f4927b..384f7e3be9 100644 --- a/src/applications/settings/panel/PhabricatorDeveloperPreferencesSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorDeveloperPreferencesSettingsPanel.php @@ -13,4 +13,8 @@ final class PhabricatorDeveloperPreferencesSettingsPanel return PhabricatorSettingsDeveloperPanelGroup::PANELGROUPKEY; } + public function isTemplatePanel() { + return true; + } + } diff --git a/src/applications/settings/panel/PhabricatorDiffPreferencesSettingsPanel.php b/src/applications/settings/panel/PhabricatorDiffPreferencesSettingsPanel.php index 40e38461bc..2e055c3408 100644 --- a/src/applications/settings/panel/PhabricatorDiffPreferencesSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorDiffPreferencesSettingsPanel.php @@ -13,4 +13,8 @@ final class PhabricatorDiffPreferencesSettingsPanel return PhabricatorSettingsApplicationsPanelGroup::PANELGROUPKEY; } + public function isTemplatePanel() { + return true; + } + } diff --git a/src/applications/settings/panel/PhabricatorDisplayPreferencesSettingsPanel.php b/src/applications/settings/panel/PhabricatorDisplayPreferencesSettingsPanel.php index 64f9a0c32f..6033ef79e9 100644 --- a/src/applications/settings/panel/PhabricatorDisplayPreferencesSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorDisplayPreferencesSettingsPanel.php @@ -13,4 +13,8 @@ final class PhabricatorDisplayPreferencesSettingsPanel return PhabricatorSettingsApplicationsPanelGroup::PANELGROUPKEY; } + public function isTemplatePanel() { + return true; + } + } diff --git a/src/applications/settings/panel/PhabricatorEditEngineSettingsPanel.php b/src/applications/settings/panel/PhabricatorEditEngineSettingsPanel.php index d542357438..27161218d1 100644 --- a/src/applications/settings/panel/PhabricatorEditEngineSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorEditEngineSettingsPanel.php @@ -7,13 +7,13 @@ abstract class PhabricatorEditEngineSettingsPanel $viewer = $this->getViewer(); $user = $this->getUser(); - if ($user->getPHID() === $viewer->getPHID()) { + if ($user && ($user->getPHID() === $viewer->getPHID())) { $is_self = true; } else { $is_self = false; } - if ($user->getPHID()) { + if ($user && $user->getPHID()) { $profile_uri = '/people/manage/'.$user->getID().'/'; } else { $profile_uri = null; @@ -26,7 +26,7 @@ abstract class PhabricatorEditEngineSettingsPanel ->setIsSelfEdit($is_self) ->setProfileURI($profile_uri); - $preferences = $this->loadTargetPreferences(); + $preferences = $this->getPreferences(); $engine->setTargetObject($preferences); @@ -47,7 +47,7 @@ abstract class PhabricatorEditEngineSettingsPanel $key = $this->getPanelKey(); $label = $this->getPanelName(); - $panel_uri = $this->getPanelURI().'saved/'; + $panel_uri = $this->getPanelURI(); return id(new PhabricatorEditPage()) ->setKey($key) diff --git a/src/applications/settings/panel/PhabricatorEmailDeliverySettingsPanel.php b/src/applications/settings/panel/PhabricatorEmailDeliverySettingsPanel.php index f7380a9d41..86260c1b5a 100644 --- a/src/applications/settings/panel/PhabricatorEmailDeliverySettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorEmailDeliverySettingsPanel.php @@ -13,7 +13,7 @@ final class PhabricatorEmailDeliverySettingsPanel return PhabricatorSettingsEmailPanelGroup::PANELGROUPKEY; } - public function isEditableByAdministrators() { + public function isManagementPanel() { if ($this->getUser()->getIsMailingList()) { return true; } @@ -21,4 +21,8 @@ final class PhabricatorEmailDeliverySettingsPanel return false; } + public function isTemplatePanel() { + return true; + } + } diff --git a/src/applications/settings/panel/PhabricatorEmailFormatSettingsPanel.php b/src/applications/settings/panel/PhabricatorEmailFormatSettingsPanel.php index 2a8bf32b71..5cab5dea41 100644 --- a/src/applications/settings/panel/PhabricatorEmailFormatSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorEmailFormatSettingsPanel.php @@ -13,7 +13,7 @@ final class PhabricatorEmailFormatSettingsPanel return PhabricatorSettingsEmailPanelGroup::PANELGROUPKEY; } - public function isEditableByAdministrators() { + public function isManagementPanel() { if ($this->getUser()->getIsMailingList()) { return true; } @@ -21,4 +21,8 @@ final class PhabricatorEmailFormatSettingsPanel return false; } + public function isTemplatePanel() { + return true; + } + } diff --git a/src/applications/settings/panel/PhabricatorEmailPreferencesSettingsPanel.php b/src/applications/settings/panel/PhabricatorEmailPreferencesSettingsPanel.php index 02dbe7f21c..ee1bac1b1e 100644 --- a/src/applications/settings/panel/PhabricatorEmailPreferencesSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorEmailPreferencesSettingsPanel.php @@ -15,7 +15,7 @@ final class PhabricatorEmailPreferencesSettingsPanel return PhabricatorSettingsEmailPanelGroup::PANELGROUPKEY; } - public function isEditableByAdministrators() { + public function isManagementPanel() { if ($this->getUser()->getIsMailingList()) { return true; } @@ -23,11 +23,15 @@ final class PhabricatorEmailPreferencesSettingsPanel return false; } + public function isTemplatePanel() { + return true; + } + public function processRequest(AphrontRequest $request) { $viewer = $this->getViewer(); $user = $this->getUser(); - $preferences = $this->loadTargetPreferences(); + $preferences = $this->getPreferences(); $value_email = PhabricatorEmailTagsSetting::VALUE_EMAIL; @@ -137,7 +141,7 @@ final class PhabricatorEmailPreferencesSettingsPanel return $form_box; } - private function getAllEditorsWithTags(PhabricatorUser $user) { + private function getAllEditorsWithTags(PhabricatorUser $user = null) { $editors = id(new PhutilClassMapQuery()) ->setAncestorClass('PhabricatorApplicationTransactionEditor') ->setFilterMethod('getMailTagsMap') @@ -146,7 +150,7 @@ final class PhabricatorEmailPreferencesSettingsPanel foreach ($editors as $key => $editor) { // Remove editors for applications which are not installed. $app = $editor->getEditorApplicationClass(); - if ($app !== null) { + if ($app !== null && $user !== null) { if (!PhabricatorApplication::isClassInstalledForViewer($app, $user)) { unset($editors[$key]); } diff --git a/src/applications/settings/panel/PhabricatorHomePreferencesSettingsPanel.php b/src/applications/settings/panel/PhabricatorHomePreferencesSettingsPanel.php index 4aa45abdb5..833a56deb7 100644 --- a/src/applications/settings/panel/PhabricatorHomePreferencesSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorHomePreferencesSettingsPanel.php @@ -15,9 +15,13 @@ final class PhabricatorHomePreferencesSettingsPanel return PhabricatorSettingsApplicationsPanelGroup::PANELGROUPKEY; } + public function isTemplatePanel() { + return true; + } + public function processRequest(AphrontRequest $request) { $viewer = $this->getViewer(); - $preferences = $this->loadTargetPreferences(); + $preferences = $this->getPreferences(); $pinned_key = PhabricatorPinnedApplicationsSetting::SETTINGKEY; $pinned = $preferences->getSettingValue($pinned_key); diff --git a/src/applications/settings/panel/PhabricatorSSHKeysSettingsPanel.php b/src/applications/settings/panel/PhabricatorSSHKeysSettingsPanel.php index f91b17557a..54a217300d 100644 --- a/src/applications/settings/panel/PhabricatorSSHKeysSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorSSHKeysSettingsPanel.php @@ -2,7 +2,11 @@ final class PhabricatorSSHKeysSettingsPanel extends PhabricatorSettingsPanel { - public function isEditableByAdministrators() { + public function isManagementPanel() { + if ($this->getUser()->getIsMailingList()) { + return false; + } + return true; } @@ -18,14 +22,6 @@ final class PhabricatorSSHKeysSettingsPanel extends PhabricatorSettingsPanel { return PhabricatorSettingsAuthenticationPanelGroup::PANELGROUPKEY; } - public function isEnabled() { - if ($this->getUser()->getIsMailingList()) { - return false; - } - - return true; - } - public function processRequest(AphrontRequest $request) { $user = $this->getUser(); $viewer = $request->getUser(); diff --git a/src/applications/settings/panel/PhabricatorSettingsPanel.php b/src/applications/settings/panel/PhabricatorSettingsPanel.php index e2cdc91f00..b66b03f9c8 100644 --- a/src/applications/settings/panel/PhabricatorSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorSettingsPanel.php @@ -18,6 +18,7 @@ abstract class PhabricatorSettingsPanel extends Phobject { private $controller; private $navigation; private $overrideURI; + private $preferences; public function setUser(PhabricatorUser $user) { $this->user = $user; @@ -60,6 +61,15 @@ abstract class PhabricatorSettingsPanel extends Phobject { return $this->navigation; } + public function setPreferences(PhabricatorUserPreferences $preferences) { + $this->preferences = $preferences; + return $this; + } + + public function getPreferences() { + return $this->preferences; + } + final public static function getAllPanels() { $panels = id(new PhutilClassMapQuery()) ->setAncestorClass(__CLASS__) @@ -145,13 +155,24 @@ abstract class PhabricatorSettingsPanel extends Phobject { /** - * Return true if this panel is available to administrators while editing - * system agent accounts. + * Return true if this panel is available to administrators while managing + * bot and mailing list accounts. * - * @return bool True to enable edit by administrators. + * @return bool True to enable management on behalf of accounts. * @task config */ - public function isEditableByAdministrators() { + public function isManagementPanel() { + return false; + } + + + /** + * Return true if this panel is available while editing settings templates. + * + * @return bool True to allow editing in templates. + * @task config + */ + public function isTemplatePanel() { return false; } @@ -194,11 +215,13 @@ abstract class PhabricatorSettingsPanel extends Phobject { $key = $this->getPanelKey(); $key = phutil_escape_uri($key); - if ($this->getUser()->getPHID() != $this->getViewer()->getPHID()) { - $user_id = $this->getUser()->getID(); - return "/settings/{$user_id}/panel/{$key}/{$path}"; + $user = $this->getUser(); + if ($user) { + $username = $user->getUsername(); + return "/settings/user/{$username}/page/{$key}/{$path}"; } else { - return "/settings/panel/{$key}/{$path}"; + $builtin = $this->getPreferences()->getBuiltinKey(); + return "/settings/builtin/{$builtin}/page/{$key}/{$path}"; } } @@ -217,20 +240,6 @@ abstract class PhabricatorSettingsPanel extends Phobject { ->addString($this->getPanelName()); } - protected function loadTargetPreferences() { - $viewer = $this->getViewer(); - $user = $this->getUser(); - - $preferences = PhabricatorUserPreferences::loadUserPreferences($user); - - PhabricatorPolicyFilter::requireCapability( - $viewer, - $preferences, - PhabricatorPolicyCapability::CAN_EDIT); - - return $preferences; - } - protected function newDialog() { return $this->getController()->newDialog(); } diff --git a/src/applications/settings/query/PhabricatorUserPreferencesQuery.php b/src/applications/settings/query/PhabricatorUserPreferencesQuery.php index fc3a332c57..280ca3eea6 100644 --- a/src/applications/settings/query/PhabricatorUserPreferencesQuery.php +++ b/src/applications/settings/query/PhabricatorUserPreferencesQuery.php @@ -6,6 +6,8 @@ final class PhabricatorUserPreferencesQuery private $ids; private $phids; private $userPHIDs; + private $builtinKeys; + private $hasUserPHID; private $users = array(); public function withIDs(array $ids) { @@ -18,6 +20,11 @@ final class PhabricatorUserPreferencesQuery return $this; } + public function withHasUserPHID($is_user) { + $this->hasUserPHID = $is_user; + return $this; + } + public function withUserPHIDs(array $phids) { $this->userPHIDs = $phids; return $this; @@ -30,6 +37,11 @@ final class PhabricatorUserPreferencesQuery return $this; } + public function withBuiltinKeys(array $keys) { + $this->builtinKeys = $keys; + return $this; + } + public function newResultObject() { return new PhabricatorUserPreferences(); } @@ -64,6 +76,7 @@ final class PhabricatorUserPreferencesQuery $users = array(); } + $need_global = array(); foreach ($prefs as $key => $pref) { $user_phid = $pref->getUserPHID(); if (!$user_phid) { @@ -71,6 +84,8 @@ final class PhabricatorUserPreferencesQuery continue; } + $need_global[] = $pref; + $user = idx($users, $user_phid); if (!$user) { $this->didRejectResult($pref); @@ -81,6 +96,23 @@ final class PhabricatorUserPreferencesQuery $pref->attachUser($user); } + // If we loaded any user preferences, load the global defaults and attach + // them if they exist. + if ($need_global) { + $global = id(new self()) + ->setViewer($this->getViewer()) + ->withBuiltinKeys( + array( + PhabricatorUserPreferences::BUILTIN_GLOBAL_DEFAULT, + )) + ->executeOne(); + if ($global) { + foreach ($need_global as $pref) { + $pref->attachDefaultSettings($global); + } + } + } + return $prefs; } @@ -108,6 +140,25 @@ final class PhabricatorUserPreferencesQuery $this->userPHIDs); } + if ($this->builtinKeys !== null) { + $where[] = qsprintf( + $conn, + 'builtinKey IN (%Ls)', + $this->builtinKeys); + } + + if ($this->hasUserPHID !== null) { + if ($this->hasUserPHID) { + $where[] = qsprintf( + $conn, + 'userPHID IS NOT NULL'); + } else { + $where[] = qsprintf( + $conn, + 'userPHID IS NULL'); + } + } + return $where; } diff --git a/src/applications/settings/query/PhabricatorUserPreferencesSearchEngine.php b/src/applications/settings/query/PhabricatorUserPreferencesSearchEngine.php new file mode 100644 index 0000000000..5e9d16f391 --- /dev/null +++ b/src/applications/settings/query/PhabricatorUserPreferencesSearchEngine.php @@ -0,0 +1,86 @@ +withHasUserPHID(false); + } + + protected function buildQueryFromParameters(array $map) { + $query = $this->newQuery(); + + return $query; + } + + protected function buildCustomSearchFields() { + return array(); + } + + protected function getURI($path) { + return '/settings/list/'.$path; + } + + protected function getBuiltinQueryNames() { + $names = array( + 'all' => pht('All Settings'), + ); + + return $names; + } + + public function buildSavedQueryFromBuiltin($query_key) { + $query = $this->newSavedQuery(); + $query->setQueryKey($query_key); + + switch ($query_key) { + case 'all': + return $query; + } + + return parent::buildSavedQueryFromBuiltin($query_key); + } + + protected function renderResultList( + array $settings, + PhabricatorSavedQuery $query, + array $handles) { + assert_instances_of($settings, 'PhabricatorUserPreferences'); + + $viewer = $this->requireViewer(); + + $list = id(new PHUIObjectItemListView()) + ->setViewer($viewer); + foreach ($settings as $setting) { + + $item = id(new PHUIObjectItemView()) + ->setHeader($setting->getDisplayName()) + ->setHref($setting->getEditURI()) + ->setImageURI(PhabricatorUser::getDefaultProfileImageURI()) + ->setIcon('fa-globe') + ->addAttribute(pht('Edit global default settings for all users.')); + + $list->addItem($item); + } + + $list->addItem( + id(new PHUIObjectItemView()) + ->setHeader(pht('Personal Account Settings')) + ->addAttribute(pht('Edit settings for your personal account.')) + ->setImageURI($viewer->getProfileImageURI()) + ->setHref('/settings/user/'.$viewer->getUsername().'/')); + + return id(new PhabricatorApplicationSearchResultView()) + ->setObjectList($list); + } + +} diff --git a/src/applications/settings/setting/PhabricatorPinnedApplicationsSetting.php b/src/applications/settings/setting/PhabricatorPinnedApplicationsSetting.php index 410e64adc3..0609dd9ee8 100644 --- a/src/applications/settings/setting/PhabricatorPinnedApplicationsSetting.php +++ b/src/applications/settings/setting/PhabricatorPinnedApplicationsSetting.php @@ -12,6 +12,11 @@ final class PhabricatorPinnedApplicationsSetting public function getSettingDefaultValue() { $viewer = $this->getViewer(); + // If we're editing a template, just show every available application. + if (!$viewer) { + $viewer = PhabricatorUser::getOmnipotentUser(); + } + $applications = id(new PhabricatorApplicationQuery()) ->setViewer($viewer) ->withInstalled(true) diff --git a/src/applications/settings/setting/PhabricatorSetting.php b/src/applications/settings/setting/PhabricatorSetting.php index 8307d17082..5a25df5fac 100644 --- a/src/applications/settings/setting/PhabricatorSetting.php +++ b/src/applications/settings/setting/PhabricatorSetting.php @@ -2,14 +2,17 @@ abstract class PhabricatorSetting extends Phobject { - private $viewer; + private $viewer = false; - public function setViewer(PhabricatorUser $viewer) { + public function setViewer(PhabricatorUser $viewer = null) { $this->viewer = $viewer; return $this; } public function getViewer() { + if ($this->viewer === false) { + throw new PhutilInvalidStateException('setViewer'); + } return $this->viewer; } diff --git a/src/applications/settings/storage/PhabricatorUserPreferences.php b/src/applications/settings/storage/PhabricatorUserPreferences.php index 81b93b89d7..31719c8195 100644 --- a/src/applications/settings/storage/PhabricatorUserPreferences.php +++ b/src/applications/settings/storage/PhabricatorUserPreferences.php @@ -7,10 +7,14 @@ final class PhabricatorUserPreferences PhabricatorDestructibleInterface, PhabricatorApplicationTransactionInterface { + const BUILTIN_GLOBAL_DEFAULT = 'global'; + protected $userPHID; protected $preferences = array(); + protected $builtinKey; private $user = self::ATTACHABLE; + private $defaultSettings; protected function getConfiguration() { return array( @@ -18,11 +22,19 @@ final class PhabricatorUserPreferences self::CONFIG_SERIALIZATION => array( 'preferences' => self::SERIALIZATION_JSON, ), + self::CONFIG_COLUMN_SCHEMA => array( + 'userPHID' => 'phid?', + 'builtinKey' => 'text32?', + ), self::CONFIG_KEY_SCHEMA => array( - 'userPHID' => array( + 'key_user' => array( 'columns' => array('userPHID'), 'unique' => true, ), + 'key_builtin' => array( + 'columns' => array('builtinKey'), + 'unique' => true, + ), ), ) + parent::getConfiguration(); } @@ -47,6 +59,10 @@ final class PhabricatorUserPreferences } public function getDefaultValue($key) { + if ($this->defaultSettings) { + return $this->defaultSettings->getSettingValue($key); + } + $setting = self::getSettingObject($key); if (!$setting) { @@ -64,9 +80,6 @@ final class PhabricatorUserPreferences return $this->preferences[$key]; } - // TODO: If this setting set inherits from another preference set, - // we would look it up here. - return $this->getDefaultValue($key); } @@ -75,6 +88,11 @@ final class PhabricatorUserPreferences return idx($settings, $key); } + public function attachDefaultSettings(PhabricatorUserPreferences $settings) { + $this->defaultSettings = $settings; + return $this; + } + public function attachUser(PhabricatorUser $user = null) { $this->user = $user; return $this; @@ -127,6 +145,21 @@ final class PhabricatorUserPreferences ->setNewValue($value); } + public function getEditURI() { + if ($this->getUser()) { + return '/settings/user/'.$this->getUser()->getUsername().'/'; + } else { + return '/settings/builtin/'.$this->getBuiltinKey().'/'; + } + } + + public function getDisplayName() { + if ($this->getBuiltinKey()) { + return pht('Global Default Settings'); + } + + return pht('Personal Settings'); + } /* -( PhabricatorPolicyInterface )----------------------------------------- */ diff --git a/src/applications/transactions/editengine/PhabricatorEditEngine.php b/src/applications/transactions/editengine/PhabricatorEditEngine.php index 62cad81531..809d44ebe6 100644 --- a/src/applications/transactions/editengine/PhabricatorEditEngine.php +++ b/src/applications/transactions/editengine/PhabricatorEditEngine.php @@ -532,6 +532,10 @@ abstract class PhabricatorEditEngine return $this->getObjectViewURI($object); } + public function getEffectiveObjectEditDoneURI($object) { + return $this->getEffectiveObjectViewURI($object); + } + public function getEffectiveObjectEditCancelURI($object) { $page = $this->getSelectedPage(); if ($page) { @@ -1169,7 +1173,7 @@ abstract class PhabricatorEditEngine $object, array $xactions) { return id(new AphrontRedirectResponse()) - ->setURI($this->getEffectiveObjectViewURI($object)); + ->setURI($this->getEffectiveObjectEditDoneURI($object)); } private function buildEditForm($object, array $fields) { From 6ac5f84c8bc90abe8e8a377e720081c679f1c349 Mon Sep 17 00:00:00 2001 From: epriestley Date: Sun, 5 Jun 2016 13:35:25 -0700 Subject: [PATCH 33/67] Redirect older "/panel/" settings URIs to modern location Summary: Ref T11098. We have a fair number of these, including links in email, which we can't turn into explicit `/user/` URIs. Just redirect them to the modern places. Test Plan: Clicked "Customize Menu..." on home page. Reviewers: chad, avivey Reviewed By: avivey Subscribers: avivey Maniphest Tasks: T11098 Differential Revision: https://secure.phabricator.com/D16049 --- .../application/PhabricatorSettingsApplication.php | 2 ++ .../PhabricatorSettingsMainController.php | 13 +++++++++++++ 2 files changed, 15 insertions(+) diff --git a/src/applications/settings/application/PhabricatorSettingsApplication.php b/src/applications/settings/application/PhabricatorSettingsApplication.php index 4adf8ee19f..45a694fb59 100644 --- a/src/applications/settings/application/PhabricatorSettingsApplication.php +++ b/src/applications/settings/application/PhabricatorSettingsApplication.php @@ -36,6 +36,8 @@ final class PhabricatorSettingsApplication extends PhabricatorApplication { => 'PhabricatorSettingsMainController', 'builtin/(?Pglobal)/'.$panel_pattern => 'PhabricatorSettingsMainController', + 'panel/(?P[^/]+)/' + => 'PhabricatorSettingsMainController', 'adjust/' => 'PhabricatorSettingsAdjustController', 'timezone/(?P[^/]+)/' => 'PhabricatorSettingsTimezoneController', diff --git a/src/applications/settings/controller/PhabricatorSettingsMainController.php b/src/applications/settings/controller/PhabricatorSettingsMainController.php index 8c58772fe2..2a368d8650 100644 --- a/src/applications/settings/controller/PhabricatorSettingsMainController.php +++ b/src/applications/settings/controller/PhabricatorSettingsMainController.php @@ -30,6 +30,19 @@ final class PhabricatorSettingsMainController public function handleRequest(AphrontRequest $request) { $viewer = $this->getViewer(); + // Redirect "/panel/XYZ/" to the viewer's personal settings panel. This + // was the primary URI before global settings were introduced and allows + // generation of viewer-agnostic URIs for email. + $panel = $request->getURIData('panel'); + if ($panel) { + $panel = phutil_escape_uri($panel); + $username = $viewer->getUsername(); + + $panel_uri = "/user/{$username}/page/{$panel}/"; + $panel_uri = $this->getApplicationURI($panel_uri); + return id(new AphrontRedirectResponse())->setURI($panel_uri); + } + $username = $request->getURIData('username'); $builtin = $request->getURIData('builtin'); From 4936be0868bd14a4f7421d07d2cb95b9088a17c5 Mon Sep 17 00:00:00 2001 From: epriestley Date: Sun, 5 Jun 2016 13:39:39 -0700 Subject: [PATCH 34/67] Fix exception when trying to save global setting mail tags Summary: Ref T11098. Template preferences don't have a user, but this codepath didn't get fully updated to account for that. Test Plan: Saved mail tags in global prefernces. Reviewers: avivey, chad Reviewed By: chad Maniphest Tasks: T11098 Differential Revision: https://secure.phabricator.com/D16050 --- .../settings/panel/PhabricatorEmailPreferencesSettingsPanel.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/settings/panel/PhabricatorEmailPreferencesSettingsPanel.php b/src/applications/settings/panel/PhabricatorEmailPreferencesSettingsPanel.php index ee1bac1b1e..0c775c5a4d 100644 --- a/src/applications/settings/panel/PhabricatorEmailPreferencesSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorEmailPreferencesSettingsPanel.php @@ -160,7 +160,7 @@ final class PhabricatorEmailPreferencesSettingsPanel return $editors; } - private function getAllTags(PhabricatorUser $user) { + private function getAllTags(PhabricatorUser $user = null) { $tags = array(); foreach ($this->getAllEditorsWithTags($user) as $editor) { $tags += $editor->getMailTagsMap(); From b4a07d528fdf982f160d5e045b0d6d04745bf82a Mon Sep 17 00:00:00 2001 From: epriestley Date: Sun, 5 Jun 2016 13:52:52 -0700 Subject: [PATCH 35/67] Allow users to unset "Editor", tailor short error messages properly on settings forms Summary: Ref T11098. - Allow "Editor" to be set to the empty string. - Don't match a validation error to a field unless the actual settings for the field and error match. Test Plan: - Tried to set "Editor" to "", success. - Tried to set "Editor" to "javascript://", only that field got marked "Invalid". Reviewers: avivey, chad Reviewed By: chad Maniphest Tasks: T11098 Differential Revision: https://secure.phabricator.com/D16051 --- .../editor/PhabricatorSettingsEditEngine.php | 39 +++++++++++++++++++ .../setting/PhabricatorEditorSetting.php | 4 ++ .../editengine/PhabricatorEditEngine.php | 19 ++++++--- ...rApplicationTransactionValidationError.php | 2 +- 4 files changed, 57 insertions(+), 7 deletions(-) diff --git a/src/applications/settings/editor/PhabricatorSettingsEditEngine.php b/src/applications/settings/editor/PhabricatorSettingsEditEngine.php index 154ef0bac7..4bcfa08d1d 100644 --- a/src/applications/settings/editor/PhabricatorSettingsEditEngine.php +++ b/src/applications/settings/editor/PhabricatorSettingsEditEngine.php @@ -194,4 +194,43 @@ final class PhabricatorSettingsEditEngine return $fields; } + protected function getValidationExceptionShortMessage( + PhabricatorApplicationTransactionValidationException $ex, + PhabricatorEditField $field) { + + // Settings fields all have the same transaction type so we need to make + // sure the transaction is changing the same setting before matching an + // error to a given field. + $xaction_type = $field->getTransactionType(); + if ($xaction_type == PhabricatorUserPreferencesTransaction::TYPE_SETTING) { + $property = PhabricatorUserPreferencesTransaction::PROPERTY_SETTING; + + $field_setting = idx($field->getMetadata(), $property); + foreach ($ex->getErrors() as $error) { + if ($error->getType() !== $xaction_type) { + continue; + } + + $xaction = $error->getTransaction(); + if (!$xaction) { + continue; + } + + $xaction_setting = $xaction->getMetadataValue($property); + if ($xaction_setting != $field_setting) { + continue; + } + + $short_message = $error->getShortMessage(); + if ($short_message !== null) { + return $short_message; + } + } + + return null; + } + + return parent::getValidationExceptionShortMessage($ex, $field); + } + } diff --git a/src/applications/settings/setting/PhabricatorEditorSetting.php b/src/applications/settings/setting/PhabricatorEditorSetting.php index 30465e9540..818a33a001 100644 --- a/src/applications/settings/setting/PhabricatorEditorSetting.php +++ b/src/applications/settings/setting/PhabricatorEditorSetting.php @@ -39,6 +39,10 @@ final class PhabricatorEditorSetting } public function validateTransactionValue($value) { + if (!strlen($value)) { + return; + } + $ok = PhabricatorHelpEditorProtocolController::hasAllowedProtocol($value); if ($ok) { return; diff --git a/src/applications/transactions/editengine/PhabricatorEditEngine.php b/src/applications/transactions/editengine/PhabricatorEditEngine.php index 809d44ebe6..181369a58f 100644 --- a/src/applications/transactions/editengine/PhabricatorEditEngine.php +++ b/src/applications/transactions/editengine/PhabricatorEditEngine.php @@ -1004,12 +1004,7 @@ abstract class PhabricatorEditEngine $validation_exception = $ex; foreach ($fields as $field) { - $xaction_type = $field->getTransactionType(); - if ($xaction_type === null) { - continue; - } - - $message = $ex->getShortMessage($xaction_type); + $message = $this->getValidationExceptionShortMessage($ex, $field); if ($message === null) { continue; } @@ -2049,6 +2044,18 @@ abstract class PhabricatorEditEngine ->setHref($item_uri); } + protected function getValidationExceptionShortMessage( + PhabricatorApplicationTransactionValidationException $ex, + PhabricatorEditField $field) { + + $xaction_type = $field->getTransactionType(); + if ($xaction_type === null) { + return null; + } + + return $ex->getShortMessage($xaction_type); + } + protected function getCreateNewObjectPolicy() { return PhabricatorPolicies::POLICY_USER; } diff --git a/src/applications/transactions/error/PhabricatorApplicationTransactionValidationError.php b/src/applications/transactions/error/PhabricatorApplicationTransactionValidationError.php index abad50b7bd..a01113483f 100644 --- a/src/applications/transactions/error/PhabricatorApplicationTransactionValidationError.php +++ b/src/applications/transactions/error/PhabricatorApplicationTransactionValidationError.php @@ -26,7 +26,7 @@ final class PhabricatorApplicationTransactionValidationError } public function getTransaction() { - return $this->tranaction; + return $this->transaction; } public function getShortMessage() { From f97d120c3fe6f560b05e94075fa2bde6c4a07855 Mon Sep 17 00:00:00 2001 From: epriestley Date: Sun, 5 Jun 2016 15:57:12 -0700 Subject: [PATCH 36/67] When a task is removed from projects, remove its position on proxy columns for those projects Summary: Fixes T11088. When a task is removed from a project, we don't normally delete its column positions. If you accidentally remove a project and then restore the project, it's nice for the task to stay where you put it. However, we do need to remove its positions in proxy columns to avoid the issue in T11088. Test Plan: - Added a failing unit test, made it pass. - Added a task to "X > Milestone 1", loaded workboard, used "Edit Projects" to move it to "X" instead, loaded workboard. - Before, it stayed in the "Milestone 1" column. - After, it moves to the "Backlog" column. Reviewers: chad Reviewed By: chad Maniphest Tasks: T11088 Differential Revision: https://secure.phabricator.com/D16052 --- .../PhabricatorProjectCoreTestCase.php | 15 ++++++ ...habricatorApplicationTransactionEditor.php | 46 +++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/src/applications/project/__tests__/PhabricatorProjectCoreTestCase.php b/src/applications/project/__tests__/PhabricatorProjectCoreTestCase.php index 0c3f51ef6b..e157211f7b 100644 --- a/src/applications/project/__tests__/PhabricatorProjectCoreTestCase.php +++ b/src/applications/project/__tests__/PhabricatorProjectCoreTestCase.php @@ -1044,6 +1044,21 @@ final class PhabricatorProjectCoreTestCase extends PhabricatorTestCase { $this->moveToColumn($user, $board, $task_a, $column, $column, $a_options); $new_projects = $this->getTaskProjects($task_a); $this->assertEqual($old_projects, $new_projects); + + + // Add the parent project to the task. This should move it out of the + // milestone column and into the parent's backlog. + $this->addProjectTags($user, $task, array($board->getPHID())); + $expect_columns = array( + $backlog->getPHID(), + ); + $this->assertColumns($expect_columns, $user, $board, $task); + + $new_projects = $this->getTaskProjects($task); + $expect_projects = array( + $board->getPHID(), + ); + $this->assertEqual($expect_projects, $new_projects); } public function testColumnExtendedPolicies() { diff --git a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php index 7570507c9f..ae58787503 100644 --- a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php +++ b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php @@ -677,6 +677,8 @@ abstract class PhabricatorApplicationTransactionEditor } $editor->save(); + + $this->updateWorkboardColumns($object, $const, $old, $new); break; case PhabricatorTransactions::TYPE_VIEW_POLICY: case PhabricatorTransactions::TYPE_SPACE: @@ -3603,4 +3605,48 @@ abstract class PhabricatorApplicationTransactionEditor return true; } + private function updateWorkboardColumns($object, $const, $old, $new) { + // If an object is removed from a project, remove it from any proxy + // columns for that project. This allows a task which is moved up from a + // milestone to the parent to move back into the "Backlog" column on the + // parent workboard. + + if ($const != PhabricatorProjectObjectHasProjectEdgeType::EDGECONST) { + return; + } + + // TODO: This should likely be some future WorkboardInterface. + $appears_on_workboards = ($object instanceof ManiphestTask); + if (!$appears_on_workboards) { + return; + } + + $removed_phids = array_keys(array_diff_key($old, $new)); + if (!$removed_phids) { + return; + } + + // Find any proxy columns for the removed projects. + $proxy_columns = id(new PhabricatorProjectColumnQuery()) + ->setViewer(PhabricatorUser::getOmnipotentUser()) + ->withProxyPHIDs($removed_phids) + ->execute(); + if (!$proxy_columns) { + return array(); + } + + $proxy_phids = mpull($proxy_columns, 'getPHID'); + + $position_table = new PhabricatorProjectColumnPosition(); + $conn_w = $position_table->establishConnection('w'); + + queryfx( + $conn_w, + 'DELETE FROM %T WHERE objectPHID = %s AND columnPHID IN (%Ls)', + $position_table->getTableName(), + $object->getPHID(), + $proxy_phids); + } + + } From 0bf337b06c1f00286f0b64594fada116f212ab1f Mon Sep 17 00:00:00 2001 From: epriestley Date: Mon, 6 Jun 2016 06:34:24 -0700 Subject: [PATCH 37/67] Lift ticks declaration to top level in Phabricator daemons Summary: Ref T10811. This is a companion change for D16053, but affects the Phabricator version of this script. Test Plan: Started daemons, ^C'd them, saw them handle the signal. Reviewers: chad Reviewed By: chad Maniphest Tasks: T10811 Differential Revision: https://secure.phabricator.com/D16054 --- scripts/daemon/launch_daemon.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/daemon/launch_daemon.php b/scripts/daemon/launch_daemon.php index 0f9eeee1a6..7c75ab671e 100755 --- a/scripts/daemon/launch_daemon.php +++ b/scripts/daemon/launch_daemon.php @@ -5,6 +5,8 @@ // script, except it loads the Phabricator environment and adds some Phabricator // specific flags. +declare(ticks = 1); + $root = dirname(dirname(dirname(__FILE__))); require_once $root.'/scripts/__init_script__.php'; From bbd5b3a9f6921195b40a89cc5ff920bf2f080770 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Mon, 6 Jun 2016 11:34:44 -0700 Subject: [PATCH 38/67] Fix spelling issue in cluster doc Summary: Fix spelling issue Test Plan: Re-read Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D16058 --- src/docs/user/cluster/cluster.diviner | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/docs/user/cluster/cluster.diviner b/src/docs/user/cluster/cluster.diviner index 29df43ce4c..7704428e9c 100644 --- a/src/docs/user/cluster/cluster.diviner +++ b/src/docs/user/cluster/cluster.diviner @@ -8,7 +8,7 @@ performance. Overview ======== -WARNING: This feature is a prototype. Installs should expect a challening +WARNING: This feature is a prototype. Installs should expect a challenging adventure when deploying clusters. In the best of times, configuring a cluster is complex and requires significant operations experience. From b3477bfc56378123451fb9d7a480971c11d37b58 Mon Sep 17 00:00:00 2001 From: epriestley Date: Mon, 6 Jun 2016 08:16:34 -0700 Subject: [PATCH 39/67] Render sometimes-legible prose diffs in the Phabricator UI Summary: Ref T3353. This hooks the prose engine up to the UI and throws away the hard-wrapping hacks. These are likely still very rough in many cases, but are hopefully a big step forward from the old version in the vast majority of cases. Test Plan: {F1677809} Reviewers: chad Reviewed By: chad Maniphest Tasks: T3353 Differential Revision: https://secure.phabricator.com/D16056 --- resources/celerity/map.php | 12 ++-- ...ApplicationTransactionDetailController.php | 3 +- ...plicationTransactionTextDiffDetailView.php | 67 ++++++++++--------- .../differential/changeset-view.css | 19 +++++- 4 files changed, 61 insertions(+), 40 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index c2bee18ab6..569c570535 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -10,7 +10,7 @@ return array( 'core.pkg.css' => '8aeacc63', 'core.pkg.js' => '3f15fa62', 'darkconsole.pkg.js' => 'e7393ebb', - 'differential.pkg.css' => 'a3a7e5df', + 'differential.pkg.css' => 'f5569f20', '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' => 'febd2372', + 'rsrc/css/application/differential/changeset-view.css' => '3f49a4bd', 'rsrc/css/application/differential/core.css' => '5b7b8ff4', 'rsrc/css/application/differential/phui-inline-comment.css' => '5953c28e', 'rsrc/css/application/differential/revision-comment.css' => '14b8565a', @@ -552,7 +552,7 @@ return array( 'conpherence-update-css' => 'faf6be09', 'conpherence-widget-pane-css' => '775eaaba', 'd3' => 'a11a5ff2', - 'differential-changeset-view-css' => 'febd2372', + 'differential-changeset-view-css' => '3f49a4bd', 'differential-core-view-css' => '5b7b8ff4', 'differential-inline-comment-editor' => '64a5550f', 'differential-revision-add-comment-css' => 'c47f8c40', @@ -1157,6 +1157,9 @@ return array( 'javelin-util', 'javelin-uri', ), + '3f49a4bd' => array( + 'phui-inline-comment-view-css', + ), '3f5d6dbf' => array( 'javelin-behavior', 'javelin-dom', @@ -2200,9 +2203,6 @@ return array( 'fea0eb47' => array( 'javelin-install', ), - 'febd2372' => array( - 'phui-inline-comment-view-css', - ), ), 'packages' => array( 'core.pkg.css' => array( diff --git a/src/applications/transactions/controller/PhabricatorApplicationTransactionDetailController.php b/src/applications/transactions/controller/PhabricatorApplicationTransactionDetailController.php index af6b93bcf1..4529c5a23e 100644 --- a/src/applications/transactions/controller/PhabricatorApplicationTransactionDetailController.php +++ b/src/applications/transactions/controller/PhabricatorApplicationTransactionDetailController.php @@ -24,8 +24,7 @@ final class PhabricatorApplicationTransactionDetailController return $this->newDialog() ->setTitle(pht('Change Details')) - ->setWidth(AphrontDialogView::WIDTH_FULL) - ->setFlush(true) + ->setWidth(AphrontDialogView::WIDTH_FORM) ->appendChild($details) ->addCancelButton($cancel_uri); } diff --git a/src/applications/transactions/view/PhabricatorApplicationTransactionTextDiffDetailView.php b/src/applications/transactions/view/PhabricatorApplicationTransactionTextDiffDetailView.php index 0e84652234..5b337c6d95 100644 --- a/src/applications/transactions/view/PhabricatorApplicationTransactionTextDiffDetailView.php +++ b/src/applications/transactions/view/PhabricatorApplicationTransactionTextDiffDetailView.php @@ -17,41 +17,48 @@ final class PhabricatorApplicationTransactionTextDiffDetailView } public function render() { - $old = $this->oldText; - $new = $this->newText; + $diff = $this->buildDiff(); - // TODO: On mobile, or perhaps by default, we should switch to 1-up once - // that is built. + require_celerity_resource('differential-changeset-view-css'); - if (strlen($old)) { - $old = phutil_utf8_hard_wrap($old, 80); - $old = implode("\n", $old)."\n"; + $result = array(); + foreach ($diff->getParts() as $part) { + $type = $part['type']; + $text = $part['text']; + switch ($type) { + case '-': + $result[] = phutil_tag( + 'span', + array( + 'class' => 'old', + ), + $text); + break; + case '+': + $result[] = phutil_tag( + 'span', + array( + 'class' => 'new', + ), + $text); + break; + case '=': + $result[] = $text; + break; + } } - if (strlen($new)) { - $new = phutil_utf8_hard_wrap($new, 80); - $new = implode("\n", $new)."\n"; - } + return phutil_tag( + 'div', + array( + 'class' => 'prose-diff', + ), + $result); + } - try { - $engine = new PhabricatorDifferenceEngine(); - $changeset = $engine->generateChangesetFromFileContent($old, $new); - - $whitespace_mode = DifferentialChangesetParser::WHITESPACE_SHOW_ALL; - - $markup_engine = new PhabricatorMarkupEngine(); - $markup_engine->setViewer($this->getUser()); - - $parser = new DifferentialChangesetParser(); - $parser->setUser($this->getUser()); - $parser->setChangeset($changeset); - $parser->setMarkupEngine($markup_engine); - $parser->setWhitespaceMode($whitespace_mode); - - return $parser->render(0, PHP_INT_MAX, array()); - } catch (Exception $ex) { - return $ex->getMessage(); - } + private function buildDiff() { + $engine = new PhutilProseDifferenceEngine(); + return $engine->getDiff($this->oldText, $this->newText); } } diff --git a/webroot/rsrc/css/application/differential/changeset-view.css b/webroot/rsrc/css/application/differential/changeset-view.css index 4c3d9f415d..10b5e4962e 100644 --- a/webroot/rsrc/css/application/differential/changeset-view.css +++ b/webroot/rsrc/css/application/differential/changeset-view.css @@ -93,6 +93,19 @@ user-select: none; } +.prose-diff span.old, +.prose-diff span.new { + padding: 0 2px; +} + +.prose-diff span.old { + color: {$redtext}; +} + +.prose-diff span.new { + color: {$greentext}; +} + .differential-diff th.selected { background-color: {$sh-yellowbackground}; } @@ -118,12 +131,14 @@ } .differential-diff td.old-full, -.differential-diff td.old span.bright { +.differential-diff td.old span.bright, +.prose-diff span.old { background: rgba(251, 175, 175, .7); } .differential-diff td.new-full, -.differential-diff td.new span.bright { +.differential-diff td.new span.bright, +.prose-diff span.new { background: rgba(151, 234, 151, .6); } From e1119b3f315bfa957a1bfcf114b5158e8cc1f51f Mon Sep 17 00:00:00 2001 From: Aviv Eyal Date: Mon, 6 Jun 2016 19:58:39 +0000 Subject: [PATCH 40/67] Render more info for binary files in Differential Summary: Ref T10856. The rendering logic was already there, but it was expecting the information under `properties` field, whereas arc puts it under `metadata`. Not sure if that something that changed a long time ago or if it was always like this. Test Plan: {F1252657 size=full} Reviewers: #blessed_reviewers, epriestley Reviewed By: #blessed_reviewers, epriestley Subscribers: Korvin, epriestley Maniphest Tasks: T10856 Differential Revision: https://secure.phabricator.com/D15828 --- .../render/DifferentialChangesetRenderer.php | 14 ++++++++++++++ .../render/DifferentialChangesetTestRenderer.php | 7 +++++++ 2 files changed, 21 insertions(+) diff --git a/src/applications/differential/render/DifferentialChangesetRenderer.php b/src/applications/differential/render/DifferentialChangesetRenderer.php index 2aae7bf1cd..450d160e23 100644 --- a/src/applications/differential/render/DifferentialChangesetRenderer.php +++ b/src/applications/differential/render/DifferentialChangesetRenderer.php @@ -626,6 +626,8 @@ abstract class DifferentialChangesetRenderer extends Phobject { unset($old['unix:filemode']); } + $metadata = $changeset->getMetadata(); + if ($this->hasOldFile()) { $file = $this->getOldFile(); if ($file->getImageWidth()) { @@ -634,6 +636,12 @@ abstract class DifferentialChangesetRenderer extends Phobject { } $old['file:mimetype'] = $file->getMimeType(); $old['file:size'] = phutil_format_bytes($file->getByteSize()); + } else { + $old['file:mimetype'] = idx($metadata, 'old:file:mime-type'); + $size = idx($metadata, 'old:file:size'); + if ($size !== null) { + $old['file:size'] = phutil_format_bytes($size); + } } if ($this->hasNewFile()) { @@ -644,6 +652,12 @@ abstract class DifferentialChangesetRenderer extends Phobject { } $new['file:mimetype'] = $file->getMimeType(); $new['file:size'] = phutil_format_bytes($file->getByteSize()); + } else { + $new['file:mimetype'] = idx($metadata, 'new:file:mime-type'); + $size = idx($metadata, 'new:file:size'); + if ($size !== null) { + $new['file:size'] = phutil_format_bytes($size); + } } return array($old, $new); diff --git a/src/applications/differential/render/DifferentialChangesetTestRenderer.php b/src/applications/differential/render/DifferentialChangesetTestRenderer.php index 429dbeda0a..a0d1fad0eb 100644 --- a/src/applications/differential/render/DifferentialChangesetTestRenderer.php +++ b/src/applications/differential/render/DifferentialChangesetTestRenderer.php @@ -32,6 +32,13 @@ abstract class DifferentialChangesetTestRenderer $changeset = $this->getChangeset(); list($old, $new) = $this->getChangesetProperties($changeset); + foreach (array_keys($old) as $key) { + if ($old[$key] === idx($new, $key)) { + unset($old[$key]); + unset($new[$key]); + } + } + if (!$old && !$new) { return null; } From 511f703657f2c7f533947bd77da385a4c48b51cf Mon Sep 17 00:00:00 2001 From: Chad Little Date: Mon, 6 Jun 2016 20:57:58 +0000 Subject: [PATCH 41/67] Tidy up quick create menu spacing Summary: Just adds a little more space to the quick create menu. Test Plan: Test stock and modded quick create menu. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D16057 --- resources/celerity/map.php | 12 ++++++------ .../css/application/base/main-menu-view.css | 17 ++++++++++++++--- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 569c570535..6b5aceb02b 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -7,7 +7,7 @@ */ return array( 'names' => array( - 'core.pkg.css' => '8aeacc63', + 'core.pkg.css' => 'b9927580', 'core.pkg.js' => '3f15fa62', 'darkconsole.pkg.js' => 'e7393ebb', 'differential.pkg.css' => 'f5569f20', @@ -32,7 +32,7 @@ return array( 'rsrc/css/aphront/typeahead.css' => 'd4f16145', 'rsrc/css/application/almanac/almanac.css' => 'dbb9b3af', 'rsrc/css/application/auth/auth.css' => '0877ed6e', - 'rsrc/css/application/base/main-menu-view.css' => 'd00a795a', + 'rsrc/css/application/base/main-menu-view.css' => 'b623169f', 'rsrc/css/application/base/notification-menu.css' => 'f31c0bde', 'rsrc/css/application/base/phabricator-application-launch-view.css' => '95351601', 'rsrc/css/application/base/phui-theme.css' => '027ba77e', @@ -776,7 +776,7 @@ return array( 'phabricator-flag-css' => '5337623f', 'phabricator-keyboard-shortcut' => '1ae869f2', 'phabricator-keyboard-shortcut-manager' => 'c1700f6f', - 'phabricator-main-menu-view' => 'd00a795a', + 'phabricator-main-menu-view' => 'b623169f', 'phabricator-nav-view-css' => 'ac79a758', 'phabricator-notification' => 'ccf1cbf8', 'phabricator-notification-css' => '3f6c89c9', @@ -1818,6 +1818,9 @@ return array( 'javelin-dom', 'javelin-util', ), + 'b623169f' => array( + 'phui-theme-css', + ), 'b6993408' => array( 'javelin-behavior', 'javelin-stratcom', @@ -1922,9 +1925,6 @@ return array( 'javelin-workflow', 'phabricator-drag-and-drop-file-upload', ), - 'd00a795a' => array( - 'phui-theme-css', - ), 'd0c516d5' => array( 'javelin-behavior', 'javelin-dom', diff --git a/webroot/rsrc/css/application/base/main-menu-view.css b/webroot/rsrc/css/application/base/main-menu-view.css index 2b8fcb1a0b..8be3a97f5e 100644 --- a/webroot/rsrc/css/application/base/main-menu-view.css +++ b/webroot/rsrc/css/application/base/main-menu-view.css @@ -555,16 +555,27 @@ button.phabricator-main-menu-search-dropdown .caret:before { height: 28px; } -.phabricator-main-menu-dropdown { +.phabricator-main-menu-dropdown.phui-list-sidenav { position: absolute; background: #fff; top: 42px; - padding: 2px; + padding: 6px 0; + margin: 0 20px 0 0; box-shadow: {$dropshadow}; - border: 1px solid {$blueborder}; + border: 1px solid {$lightblueborder}; border-radius: 3px; } +.phabricator-main-menu-dropdown.phui-list-sidenav .phui-list-item-has-icon + .phui-list-item-href { + padding: 4px 40px 4px 12px; +} + +.phabricator-main-menu-dropdown.phui-list-sidenav .phui-list-item-type-label + .phui-list-item-name { + padding-left: 12px; +} + /* - Application Menu ---------------------------------------------------------- From e1a9473eda04bd76da1c96814727ec404a1d284e Mon Sep 17 00:00:00 2001 From: epriestley Date: Mon, 6 Jun 2016 13:04:21 -0700 Subject: [PATCH 42/67] Make auth provider autologin modular and implement it for all OAuth2 adapters Summary: Ref T10785. Around the time we launched Phacility SAAS we implemented this weird autologin hack. It works fine, so clean it up, get rid of the `instanceof` stuff, and support it for any OAuth2 provider. (We could conceivably support OAuth1 as well, but no one has expressed an interest in it and I don't think I have any OAuth1 providers configured correctly locally so it would take a little bit to set up and test.) Test Plan: - Configured OAuth2 adapters (Facebook) for auto-login. - Saw no config option on other adapters (LDAP). - Nuked all options but one, did autologin with Facebook and Phabricator. - Logged out, got logout screen. Reviewers: chad Reviewed By: chad Maniphest Tasks: T10785 Differential Revision: https://secure.phabricator.com/D16060 --- .../PhabricatorAuthStartController.php | 45 ++++++++++++++----- .../config/PhabricatorAuthEditController.php | 4 +- .../auth/provider/PhabricatorAuthProvider.php | 8 ++++ .../PhabricatorOAuth2AuthProvider.php | 13 ++++++ 4 files changed, 57 insertions(+), 13 deletions(-) diff --git a/src/applications/auth/controller/PhabricatorAuthStartController.php b/src/applications/auth/controller/PhabricatorAuthStartController.php index 633339a356..982252d29f 100644 --- a/src/applications/auth/controller/PhabricatorAuthStartController.php +++ b/src/applications/auth/controller/PhabricatorAuthStartController.php @@ -113,17 +113,9 @@ final class PhabricatorAuthStartController PhabricatorCookies::setClientIDCookie($request); } - if (!$request->getURIData('loggedout') && count($providers) == 1) { - $auto_login_provider = head($providers); - $auto_login_config = $auto_login_provider->getProviderConfig(); - if ($auto_login_provider instanceof PhabricatorPhabricatorAuthProvider && - $auto_login_config->getShouldAutoLogin()) { - $auto_login_adapter = $provider->getAdapter(); - $auto_login_adapter->setState($provider->getAuthCSRFCode($request)); - return id(new AphrontRedirectResponse()) - ->setIsExternal(true) - ->setURI($provider->getAdapter()->getAuthenticateURI()); - } + $auto_response = $this->tryAutoLogin($providers); + if ($auto_response) { + return $auto_response; } $invite = $this->loadInvite(); @@ -282,4 +274,35 @@ final class PhabricatorAuthStartController array($message)); } + private function tryAutoLogin(array $providers) { + $request = $this->getRequest(); + + // If the user just logged out, don't immediately log them in again. + if ($request->getURIData('loggedout')) { + return null; + } + + // If we have more than one provider, we can't autologin because we + // don't know which one the user wants. + if (count($providers) != 1) { + return null; + } + + $provider = head($providers); + if (!$provider->supportsAutoLogin()) { + return null; + } + + $config = $provider->getProviderConfig(); + if (!$config->getShouldAutoLogin()) { + return null; + } + + $auto_uri = $provider->getAutoLoginURI($request); + + return id(new AphrontRedirectResponse()) + ->setIsExternal(true) + ->setURI($auto_uri); + } + } diff --git a/src/applications/auth/controller/config/PhabricatorAuthEditController.php b/src/applications/auth/controller/config/PhabricatorAuthEditController.php index 049edfacef..ec2941a3fb 100644 --- a/src/applications/auth/controller/config/PhabricatorAuthEditController.php +++ b/src/applications/auth/controller/config/PhabricatorAuthEditController.php @@ -130,7 +130,7 @@ final class PhabricatorAuthEditController PhabricatorAuthProviderConfigTransaction::TYPE_TRUST_EMAILS) ->setNewValue($request->getInt('trustEmails', 0)); - if ($provider instanceof PhabricatorPhabricatorAuthProvider) { + if ($provider->supportsAutoLogin()) { $xactions[] = id(new PhabricatorAuthProviderConfigTransaction()) ->setTransactionType( PhabricatorAuthProviderConfigTransaction::TYPE_AUTO_LOGIN) @@ -314,7 +314,7 @@ final class PhabricatorAuthEditController $v_trust_email)); } - if ($provider instanceof PhabricatorPhabricatorAuthProvider) { + if ($provider->supportsAutoLogin()) { $form->appendChild( id(new AphrontFormCheckboxControl()) ->addCheckbox( diff --git a/src/applications/auth/provider/PhabricatorAuthProvider.php b/src/applications/auth/provider/PhabricatorAuthProvider.php index 9484109943..c949764c9a 100644 --- a/src/applications/auth/provider/PhabricatorAuthProvider.php +++ b/src/applications/auth/provider/PhabricatorAuthProvider.php @@ -495,4 +495,12 @@ abstract class PhabricatorAuthProvider extends Phobject { } } + public function supportsAutoLogin() { + return false; + } + + public function getAutoLoginURI(AphrontRequest $request) { + throw new PhutilMethodNotImplementedException(); + } + } diff --git a/src/applications/auth/provider/PhabricatorOAuth2AuthProvider.php b/src/applications/auth/provider/PhabricatorOAuth2AuthProvider.php index aff6367840..a3300126af 100644 --- a/src/applications/auth/provider/PhabricatorOAuth2AuthProvider.php +++ b/src/applications/auth/provider/PhabricatorOAuth2AuthProvider.php @@ -273,4 +273,17 @@ abstract class PhabricatorOAuth2AuthProvider parent::willRenderLinkedAccount($viewer, $item, $account); } + public function supportsAutoLogin() { + return true; + } + + public function getAutoLoginURI(AphrontRequest $request) { + $csrf_code = $this->getAuthCSRFCode($request); + + $adapter = $this->getAdapter(); + $adapter->setState($csrf_code); + + return $adapter->getAuthenticateURI(); + } + } From 02877c600a750173c66ff88b67f35bac5befc931 Mon Sep 17 00:00:00 2001 From: epriestley Date: Mon, 6 Jun 2016 13:59:03 -0700 Subject: [PATCH 43/67] Use prose diffs in Phriction Summary: Ref T3353. This improves whitespace display of prose diffs and uses them in Phriction. Test Plan: {F1677957} Reviewers: chad, avivey Reviewed By: avivey Subscribers: avivey Maniphest Tasks: T3353 Differential Revision: https://secure.phabricator.com/D16062 --- .../controller/PhrictionDiffController.php | 65 ++++--------------- .../differential/changeset-view.css | 8 +++ 2 files changed, 20 insertions(+), 53 deletions(-) diff --git a/src/applications/phriction/controller/PhrictionDiffController.php b/src/applications/phriction/controller/PhrictionDiffController.php index 5b1a81f940..8ae9618bbd 100644 --- a/src/applications/phriction/controller/PhrictionDiffController.php +++ b/src/applications/phriction/controller/PhrictionDiffController.php @@ -45,61 +45,20 @@ final class PhrictionDiffController extends PhrictionController { $text_l = $content_l->getContent(); $text_r = $content_r->getContent(); - $text_l = phutil_utf8_hard_wrap($text_l, 80); - $text_l = implode("\n", $text_l); - $text_r = phutil_utf8_hard_wrap($text_r, 80); - $text_r = implode("\n", $text_r); + $diff_view = id(new PhabricatorApplicationTransactionTextDiffDetailView()) + ->setOldText($text_l) + ->setNewText($text_r); - $engine = new PhabricatorDifferenceEngine(); - $changeset = $engine->generateChangesetFromFileContent($text_l, $text_r); - - $changeset->setFilename($content_r->getTitle()); - - $changeset->setOldProperties( - array( - 'Title' => $content_l->getTitle(), - )); - $changeset->setNewProperties( - array( - 'Title' => $content_r->getTitle(), - )); - - $whitespace_mode = DifferentialChangesetParser::WHITESPACE_SHOW_ALL; - - $parser = id(new DifferentialChangesetParser()) - ->setUser($viewer) - ->setChangeset($changeset) - ->setRenderingReference("{$l},{$r}"); - - $parser->readParametersFromRequest($request); - $parser->setWhitespaceMode($whitespace_mode); - - $engine = new PhabricatorMarkupEngine(); - $engine->setViewer($viewer); - $engine->process(); - $parser->setMarkupEngine($engine); - - $spec = $request->getStr('range'); - list($range_s, $range_e, $mask) = - DifferentialChangesetParser::parseRangeSpecification($spec); - - $parser->setRange($range_s, $range_e); - $parser->setMask($mask); - - if ($request->isAjax()) { - return id(new PhabricatorChangesetResponse()) - ->setRenderedChangeset($parser->renderChangeset()); - } - - $changes = id(new DifferentialChangesetListView()) - ->setUser($this->getViewer()) - ->setChangesets(array($changeset)) - ->setVisibleChangesets(array($changeset)) - ->setRenderingReferences(array("{$l},{$r}")) - ->setRenderURI('/phriction/diff/'.$document->getID().'/') - ->setTitle(pht('Changes')) + $changes = id(new PHUIObjectBoxView()) + ->setHeaderText(pht('Content Changes')) ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) - ->setParser($parser); + ->appendChild( + phutil_tag( + 'div', + array( + 'class' => 'prose-diff-frame', + ), + $diff_view)); require_celerity_resource('phriction-document-css'); diff --git a/webroot/rsrc/css/application/differential/changeset-view.css b/webroot/rsrc/css/application/differential/changeset-view.css index 10b5e4962e..e6ee35e9b9 100644 --- a/webroot/rsrc/css/application/differential/changeset-view.css +++ b/webroot/rsrc/css/application/differential/changeset-view.css @@ -93,6 +93,14 @@ user-select: none; } +.prose-diff { + white-space: pre-wrap; +} + +.prose-diff-frame { + padding: 12px; +} + .prose-diff span.old, .prose-diff span.new { padding: 0 2px; From d1999557dca222ce3aa2bfabd3442c2db93b39d2 Mon Sep 17 00:00:00 2001 From: epriestley Date: Mon, 6 Jun 2016 14:52:55 -0700 Subject: [PATCH 44/67] Crunchy celery. --- resources/celerity/map.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 6b5aceb02b..96def22720 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -10,7 +10,7 @@ return array( 'core.pkg.css' => 'b9927580', 'core.pkg.js' => '3f15fa62', 'darkconsole.pkg.js' => 'e7393ebb', - 'differential.pkg.css' => 'f5569f20', + 'differential.pkg.css' => 'f3fb8324', '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' => '3f49a4bd', + 'rsrc/css/application/differential/changeset-view.css' => '805f1141', 'rsrc/css/application/differential/core.css' => '5b7b8ff4', 'rsrc/css/application/differential/phui-inline-comment.css' => '5953c28e', 'rsrc/css/application/differential/revision-comment.css' => '14b8565a', @@ -552,7 +552,7 @@ return array( 'conpherence-update-css' => 'faf6be09', 'conpherence-widget-pane-css' => '775eaaba', 'd3' => 'a11a5ff2', - 'differential-changeset-view-css' => '3f49a4bd', + 'differential-changeset-view-css' => '805f1141', 'differential-core-view-css' => '5b7b8ff4', 'differential-inline-comment-editor' => '64a5550f', 'differential-revision-add-comment-css' => 'c47f8c40', @@ -1157,9 +1157,6 @@ return array( 'javelin-util', 'javelin-uri', ), - '3f49a4bd' => array( - 'phui-inline-comment-view-css', - ), '3f5d6dbf' => array( 'javelin-behavior', 'javelin-dom', @@ -1540,6 +1537,9 @@ return array( 'javelin-vector', 'javelin-stratcom', ), + '805f1141' => array( + 'phui-inline-comment-view-css', + ), '834a1173' => array( 'javelin-behavior', 'javelin-scrollbar', From fb2da8bd8b437d15c50ebabe1913f91a5270f5eb Mon Sep 17 00:00:00 2001 From: epriestley Date: Mon, 6 Jun 2016 15:05:48 -0700 Subject: [PATCH 45/67] Add links and diffs for text block edits to mail Summary: Ref T7643. - When a transaction edits a text block, add a link to the changes (for HTML mail). - Also, inline the changes in the mail (for HTML mail). - Do nothing for text mail since I don't think we really have room? And I don't know how we can make the diff look any good. Test Plan: Edited a task description, generated mail, examined mail. - It contained a link leading to a prose diff. - It had a more-or-less reasonable inline text diff. Reviewers: chad Reviewed By: chad Maniphest Tasks: T7643 Differential Revision: https://secure.phabricator.com/D16063 --- ...ApplicationTransactionDetailController.php | 35 +++++++++++- ...habricatorApplicationTransactionEditor.php | 29 +++++++++- .../PhabricatorApplicationTransaction.php | 46 +++++++++++++-- ...plicationTransactionTextDiffDetailView.php | 56 +++++++++++++++++++ .../PhabricatorApplicationTransactionView.php | 2 +- 5 files changed, 157 insertions(+), 11 deletions(-) diff --git a/src/applications/transactions/controller/PhabricatorApplicationTransactionDetailController.php b/src/applications/transactions/controller/PhabricatorApplicationTransactionDetailController.php index 4529c5a23e..5849ae0c5e 100644 --- a/src/applications/transactions/controller/PhabricatorApplicationTransactionDetailController.php +++ b/src/applications/transactions/controller/PhabricatorApplicationTransactionDetailController.php @@ -3,11 +3,16 @@ final class PhabricatorApplicationTransactionDetailController extends PhabricatorApplicationTransactionController { + private $objectHandle; + public function shouldAllowPublic() { return true; } public function handleRequest(AphrontRequest $request) { + // Users can end up on this page directly by following links in email, + // so we try to make it somewhat reasonable as a standalone page. + $viewer = $this->getViewer(); $phid = $request->getURIData('phid'); @@ -20,13 +25,39 @@ final class PhabricatorApplicationTransactionDetailController } $details = $xaction->renderChangeDetails($viewer); - $cancel_uri = $this->guessCancelURI($viewer, $xaction); + + $object_phid = $xaction->getObjectPHID(); + $handles = $viewer->loadHandles(array($object_phid)); + $handle = $handles[$object_phid]; + $this->objectHandle = $handle; + + $cancel_uri = $handle->getURI(); + + if ($request->isAjax()) { + $button_text = pht('Done'); + } else { + $button_text = pht('Continue'); + } return $this->newDialog() ->setTitle(pht('Change Details')) ->setWidth(AphrontDialogView::WIDTH_FORM) ->appendChild($details) - ->addCancelButton($cancel_uri); + ->addCancelButton($cancel_uri, $button_text); } + protected function buildApplicationCrumbs() { + $crumbs = parent::buildApplicationCrumbs(); + + $handle = $this->objectHandle; + if ($handle) { + $crumbs->addTextCrumb( + $handle->getObjectName(), + $handle->getURI()); + } + + return $crumbs; + } + + } diff --git a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php index ae58787503..1a4afa2c9f 100644 --- a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php +++ b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php @@ -2704,7 +2704,9 @@ abstract class PhabricatorApplicationTransactionEditor $object_href = null) { $headers = array(); + $headers_html = array(); $comments = array(); + $details = array(); foreach ($xactions as $xaction) { if ($xaction->shouldHideForMail($xactions)) { @@ -2716,16 +2718,25 @@ abstract class PhabricatorApplicationTransactionEditor $headers[] = $header; } + $header_html = $xaction->getTitleForHTMLMail(); + if ($header_html !== null) { + $headers_html[] = $header_html; + } + $comment = $xaction->getBodyForMail(); if ($comment !== null) { $comments[] = $comment; } + + if ($xaction->hasChangeDetailsForMail()) { + $details[] = $xaction; + } } $headers_text = implode("\n", $headers); $body->addRawPlaintextSection($headers_text); - $headers_html = phutil_implode_html(phutil_tag('br'), $headers); + $headers_html = phutil_implode_html(phutil_tag('br'), $headers_html); $header_button = null; if ($object_label !== null) { @@ -2765,7 +2776,13 @@ abstract class PhabricatorApplicationTransactionEditor array( 'style' => implode(' ', $xactions_style), ), - $headers_html); + array( + $headers_html, + // Add an extra newline to prevent the "View Object" button from + // running into the transaction text in Mail.app text snippet + // previews. + "\n", + )); $headers_html = phutil_tag( 'table', @@ -2777,6 +2794,14 @@ abstract class PhabricatorApplicationTransactionEditor foreach ($comments as $comment) { $body->addRemarkupSection(null, $comment); } + + foreach ($details as $xaction) { + $details = $xaction->renderChangeDetailsForMail($body->getViewer()); + if ($details !== null) { + $body->addHTMLSection(pht('EDIT DETAILS'), $details); + } + } + } /** diff --git a/src/applications/transactions/storage/PhabricatorApplicationTransaction.php b/src/applications/transactions/storage/PhabricatorApplicationTransaction.php index 1e6bb14481..3a7e3841a3 100644 --- a/src/applications/transactions/storage/PhabricatorApplicationTransaction.php +++ b/src/applications/transactions/storage/PhabricatorApplicationTransaction.php @@ -693,6 +693,33 @@ abstract class PhabricatorApplicationTransaction return id(clone $this)->setRenderingTarget('text')->getTitle(); } + public function getTitleForHTMLMail() { + $title = $this->getTitleForMail(); + if ($title === null) { + return null; + } + + if ($this->hasChangeDetails()) { + $details_uri = $this->getChangeDetailsURI(); + $details_uri = PhabricatorEnv::getProductionURI($details_uri); + + $show_details = phutil_tag( + 'a', + array( + 'href' => $details_uri, + ), + pht('(Show Details)')); + + $title = array($title, ' ', $show_details); + } + + return $title; + } + + public function getChangeDetailsURI() { + return '/transactions/detail/'.$this->getPHID().'/'; + } + public function getBodyForMail() { if ($this->isInlineCommentTransaction()) { // We don't return inline comment content as mail body content, because @@ -1307,6 +1334,18 @@ abstract class PhabricatorApplicationTransaction return false; } + public function hasChangeDetailsForMail() { + return $this->hasChangeDetails(); + } + + public function renderChangeDetailsForMail(PhabricatorUser $viewer) { + $view = $this->renderChangeDetails($viewer); + if ($view instanceof PhabricatorApplicationTransactionTextDiffDetailView) { + return $view->renderForMail(); + } + return null; + } + public function renderChangeDetails(PhabricatorUser $viewer) { switch ($this->getTransactionType()) { case PhabricatorTransactions::TYPE_CUSTOMFIELD: @@ -1327,15 +1366,10 @@ abstract class PhabricatorApplicationTransaction PhabricatorUser $viewer, $old, $new) { - - require_celerity_resource('differential-changeset-view-css'); - - $view = id(new PhabricatorApplicationTransactionTextDiffDetailView()) + return id(new PhabricatorApplicationTransactionTextDiffDetailView()) ->setUser($viewer) ->setOldText($old) ->setNewText($new); - - return $view->render(); } public function attachTransactionGroup(array $group) { diff --git a/src/applications/transactions/view/PhabricatorApplicationTransactionTextDiffDetailView.php b/src/applications/transactions/view/PhabricatorApplicationTransactionTextDiffDetailView.php index 5b337c6d95..a8f92866b2 100644 --- a/src/applications/transactions/view/PhabricatorApplicationTransactionTextDiffDetailView.php +++ b/src/applications/transactions/view/PhabricatorApplicationTransactionTextDiffDetailView.php @@ -16,6 +16,62 @@ final class PhabricatorApplicationTransactionTextDiffDetailView return $this; } + public function renderForMail() { + $diff = $this->buildDiff(); + + $old_styles = array( + 'padding: 0 2px;', + 'color: #802b2b;', + 'background: rgba(251, 175, 175, .7);', + ); + $old_styles = implode(' ', $old_styles); + + $new_styles = array( + 'padding: 0 2px;', + 'color: #3e6d35;', + 'background: rgba(151, 234, 151, .6);', + ); + $new_styles = implode(' ', $new_styles); + + $result = array(); + foreach ($diff->getParts() as $part) { + $type = $part['type']; + $text = $part['text']; + switch ($type) { + case '-': + $result[] = phutil_tag( + 'span', + array( + 'style' => $old_styles, + ), + $text); + break; + case '+': + $result[] = phutil_tag( + 'span', + array( + 'style' => $new_styles, + ), + $text); + break; + case '=': + $result[] = $text; + break; + } + } + + $styles = array( + 'white-space: pre-wrap;', + ); + + return phutil_tag( + 'div', + array( + 'style' => implode(' ', $styles), + ), + $result); + } + public function render() { $diff = $this->buildDiff(); diff --git a/src/applications/transactions/view/PhabricatorApplicationTransactionView.php b/src/applications/transactions/view/PhabricatorApplicationTransactionView.php index 8b9955a739..6e27f8540b 100644 --- a/src/applications/transactions/view/PhabricatorApplicationTransactionView.php +++ b/src/applications/transactions/view/PhabricatorApplicationTransactionView.php @@ -259,7 +259,7 @@ class PhabricatorApplicationTransactionView extends AphrontView { return javelin_tag( 'a', array( - 'href' => '/transactions/detail/'.$xaction->getPHID().'/', + 'href' => $xaction->getChangeDetailsURI(), 'sigil' => 'workflow', ), pht('(Show Details)')); From 8a7ded61293a7c4b6c98c33b8f0e8bcb178ab5fd Mon Sep 17 00:00:00 2001 From: epriestley Date: Tue, 7 Jun 2016 06:52:52 -0700 Subject: [PATCH 46/67] Fix one more remarkup line wrapping issue Summary: Ran into this while fixing T11098#179088. The "Transaction Type" details in the conduit autogenerated documentation for `*.edit` endpoints still wraps incorrectly. Test Plan: Purged remarkup cache, reloaded page, got full-width text. Reviewers: avivey, chad Reviewed By: chad Differential Revision: https://secure.phabricator.com/D16065 --- .../editengine/PhabricatorEditEngineAPIMethod.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php b/src/applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php index 7bd09df18a..de693a4049 100644 --- a/src/applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php +++ b/src/applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php @@ -158,6 +158,11 @@ abstract class PhabricatorEditEngineAPIMethod $view = new PHUIRemarkupView($viewer, $remarkup); + $view->setRemarkupOptions( + array( + PHUIRemarkupView::OPTION_PRESERVE_LINEBREAKS => false, + )); + return id(new PHUIBoxView()) ->appendChild($view) ->addPadding(PHUI::PADDING_LARGE); From 814fa135b03606f44f8bc9036f5eaae1b355d083 Mon Sep 17 00:00:00 2001 From: epriestley Date: Tue, 7 Jun 2016 06:52:56 -0700 Subject: [PATCH 47/67] Centralize "this is the current user for the request" code Summary: Ref T11098. This primarily fixes Conduit calls to `*.edit` methods failing when trying to access user preferences. (The actual access is a little weird, since it seems like we're building some UI stuff inside a policy query, but that's an issue for another time.) To fix this, consolidate the "we're about to run some kind of request with this user" code and run it consistently for web, conduit, and SSH sessions. Additionally, make sure we swap things to the user's translation. Test Plan: - Ran `maniphest.edit` via `arc call-conduit`, no more settings exception. - Set translation to ALL CAPS, got all caps output from `ssh` and Conduit. Reviewers: avivey, chad Reviewed By: chad Maniphest Tasks: T11098 Differential Revision: https://secure.phabricator.com/D16066 --- scripts/ssh/ssh-exec.php | 3 +++ .../auth/engine/PhabricatorAuthSessionEngine.php | 9 ++++++++- .../base/controller/PhabricatorController.php | 3 ++- .../controller/PhabricatorConduitAPIController.php | 4 ++++ 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/scripts/ssh/ssh-exec.php b/scripts/ssh/ssh-exec.php index 5d8ede2913..56b13ff142 100755 --- a/scripts/ssh/ssh-exec.php +++ b/scripts/ssh/ssh-exec.php @@ -103,6 +103,9 @@ try { 'Invalid username ("%s"). There is no user with this username.', $user_name)); } + + id(new PhabricatorAuthSessionEngine()) + ->willServeRequestForUser($user); } else if (strlen($device_name)) { if (!$remote_address) { throw new Exception( diff --git a/src/applications/auth/engine/PhabricatorAuthSessionEngine.php b/src/applications/auth/engine/PhabricatorAuthSessionEngine.php index a7f5513056..e7d5c94146 100644 --- a/src/applications/auth/engine/PhabricatorAuthSessionEngine.php +++ b/src/applications/auth/engine/PhabricatorAuthSessionEngine.php @@ -164,7 +164,6 @@ final class PhabricatorAuthSessionEngine extends Phobject { $cache_raw = $this->filterRawCacheData($user, $types_map, $cache_raw); $user->attachRawCacheData($cache_raw); - $user->setAllowInlineCacheGeneration(true); switch ($session_type) { case PhabricatorAuthSession::TYPE_WEB: @@ -832,4 +831,12 @@ final class PhabricatorAuthSessionEngine extends Phobject { return $cache_raw; } + public function willServeRequestForUser(PhabricatorUser $user) { + // We allow the login user to generate any missing cache data inline. + $user->setAllowInlineCacheGeneration(true); + + // Switch to the user's translation. + PhabricatorEnv::setLocaleCode($user->getTranslation()); + } + } diff --git a/src/applications/base/controller/PhabricatorController.php b/src/applications/base/controller/PhabricatorController.php index 83e223d195..08966f29a5 100644 --- a/src/applications/base/controller/PhabricatorController.php +++ b/src/applications/base/controller/PhabricatorController.php @@ -104,7 +104,8 @@ abstract class PhabricatorController extends AphrontController { $request->setUser($user); } - PhabricatorEnv::setLocaleCode($user->getTranslation()); + id(new PhabricatorAuthSessionEngine()) + ->willServeRequestForUser($user); if (PhabricatorEnv::getEnvConfig('darkconsole.enabled')) { $dark_console = PhabricatorDarkConsoleSetting::SETTINGKEY; diff --git a/src/applications/conduit/controller/PhabricatorConduitAPIController.php b/src/applications/conduit/controller/PhabricatorConduitAPIController.php index 690f6cc1da..b9e8b1b15e 100644 --- a/src/applications/conduit/controller/PhabricatorConduitAPIController.php +++ b/src/applications/conduit/controller/PhabricatorConduitAPIController.php @@ -487,6 +487,10 @@ final class PhabricatorConduitAPIController } $request->setUser($user); + + id(new PhabricatorAuthSessionEngine()) + ->willServeRequestForUser($user); + return null; } From f0eb6f4fe080593ae6542113439ee1f8fd5bbc87 Mon Sep 17 00:00:00 2001 From: Asher Baker Date: Mon, 6 Jun 2016 22:01:18 +0000 Subject: [PATCH 48/67] Add client-side check for protocol mismatch Summary: Fixes T10402. I tried about 50 variations on the wording and notification layout, this seemed by far the most reasonable. Didn't implement a way to ignore the warning, which might be required - but figured this is serious and broken enough while being completely invisible 99% of the time that it's worth shouting about. Test Plan: Messed around with $_SERVER['HTTPS'] on the server side and client_uri on the client side - saw reasonable results in all combinations. Reviewers: #blessed_reviewers, epriestley Reviewed By: #blessed_reviewers, epriestley Subscribers: epriestley Maniphest Tasks: T10402 Differential Revision: https://secure.phabricator.com/D16064 --- resources/celerity/map.php | 10 ++++- resources/celerity/packages.php | 1 + src/view/page/PhabricatorStandardPageView.php | 22 +++++++++++ .../js/core/behavior-setup-check-https.js | 39 +++++++++++++++++++ 4 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 webroot/rsrc/js/core/behavior-setup-check-https.js diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 96def22720..5a7668189f 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -8,7 +8,7 @@ return array( 'names' => array( 'core.pkg.css' => 'b9927580', - 'core.pkg.js' => '3f15fa62', + 'core.pkg.js' => '3f2c120d', 'darkconsole.pkg.js' => 'e7393ebb', 'differential.pkg.css' => 'f3fb8324', 'differential.pkg.js' => '4b7d8f19', @@ -507,6 +507,7 @@ return array( 'rsrc/js/core/behavior-search-typeahead.js' => '06c32383', 'rsrc/js/core/behavior-select-content.js' => 'bf5374ef', 'rsrc/js/core/behavior-select-on-click.js' => '4e3e79a6', + 'rsrc/js/core/behavior-setup-check-https.js' => '491416b3', 'rsrc/js/core/behavior-time-typeahead.js' => '522431f7', 'rsrc/js/core/behavior-toggle-class.js' => '92b9ec77', 'rsrc/js/core/behavior-tokenizer.js' => 'b3a4b884', @@ -692,6 +693,7 @@ return array( 'javelin-behavior-search-reorder-queries' => 'e9581f08', 'javelin-behavior-select-content' => 'bf5374ef', 'javelin-behavior-select-on-click' => '4e3e79a6', + 'javelin-behavior-setup-check-https' => '491416b3', 'javelin-behavior-slowvote-embed' => '887ad43f', 'javelin-behavior-stripe-payment-form' => '3f5d6dbf', 'javelin-behavior-test-payment-form' => 'fc91ab6c', @@ -1214,6 +1216,11 @@ return array( 'phabricator-drag-and-drop-file-upload', 'phabricator-textareautils', ), + '491416b3' => array( + 'javelin-behavior', + 'javelin-uri', + 'phabricator-notification', + ), '49b73b36' => array( 'javelin-behavior', 'javelin-dom', @@ -2340,6 +2347,7 @@ return array( 'javelin-behavior-durable-column', 'conpherence-thread-manager', 'javelin-behavior-detect-timezone', + 'javelin-behavior-setup-check-https', ), 'darkconsole.pkg.js' => array( 'javelin-behavior-dark-console', diff --git a/resources/celerity/packages.php b/resources/celerity/packages.php index b44707f407..2de94b5a8c 100644 --- a/resources/celerity/packages.php +++ b/resources/celerity/packages.php @@ -82,6 +82,7 @@ return array( 'javelin-behavior-durable-column', 'conpherence-thread-manager', 'javelin-behavior-detect-timezone', + 'javelin-behavior-setup-check-https', ), 'core.pkg.css' => array( 'phabricator-core-css', diff --git a/src/view/page/PhabricatorStandardPageView.php b/src/view/page/PhabricatorStandardPageView.php index 5bdc3331b6..e37da7c5ea 100644 --- a/src/view/page/PhabricatorStandardPageView.php +++ b/src/view/page/PhabricatorStandardPageView.php @@ -239,6 +239,28 @@ final class PhabricatorStandardPageView extends PhabricatorBarePageView 'ignoreKey' => $ignore_key, 'ignore' => $ignore, )); + + if ($user->getIsAdmin()) { + $server_https = $request->isHTTPS(); + $server_protocol = $server_https ? 'HTTPS' : 'HTTP'; + $client_protocol = $server_https ? 'HTTP' : 'HTTPS'; + + $doc_name = 'Configuring a Preamble Script'; + $doc_href = PhabricatorEnv::getDoclink($doc_name); + + Javelin::initBehavior( + 'setup-check-https', + array( + 'server_https' => $server_https, + 'doc_name' => pht('See Documentation'), + 'doc_href' => $doc_href, + 'message' => pht( + 'Phabricator thinks you are using %s, but your '. + 'client is conviced that it is using %s. This is a serious '. + 'misconfiguration with subtle, but significant, consequences.', + $server_protocol, $client_protocol), + )); + } } $default_img_uri = diff --git a/webroot/rsrc/js/core/behavior-setup-check-https.js b/webroot/rsrc/js/core/behavior-setup-check-https.js new file mode 100644 index 0000000000..01369f0024 --- /dev/null +++ b/webroot/rsrc/js/core/behavior-setup-check-https.js @@ -0,0 +1,39 @@ +/** + * @provides javelin-behavior-setup-check-https + * @requires javelin-behavior + * javelin-uri + * phabricator-notification + */ + +JX.behavior('setup-check-https', function(config) { + + var server_https = config.server_https; + + var client_uri = new JX.URI(window.location.href); + var client_protocol = client_uri.getProtocol(); + var client_https = (client_protocol === 'https'); + + if (server_https === client_https) { + return; + } + + var doc_link = JX.$N( + 'a', + { + href: config.doc_href, + target: '_blank' + }, + config.doc_name); + + var content = [ + config.message, + ' ', + doc_link, + ]; + + new JX.Notification() + .alterClassName('jx-notification-alert', true) + .setContent(content) + .setDuration(0) + .show(); +}); From 411cf1345763fcc6d6be113f69124555fdf35796 Mon Sep 17 00:00:00 2001 From: epriestley Date: Tue, 7 Jun 2016 10:26:00 -0700 Subject: [PATCH 49/67] Add Videos to Remarkup Summary: Ref T6916. Added video to remarkup using D7156 as reference. Test Plan: - Viewed video files (MP4, Ogg) in Safari, Chrome, Firefox (some don't work, e.g., OGG in Safari, but nothing we can really do about that). - Used `alt`. - Used `autoplay`. - Used `loop`. - Used `media=audio`. - Viewed file detail page. Reviewers: nateguchi2, chad, #blessed_reviewers Reviewed By: chad, #blessed_reviewers Subscribers: asherkin, ivo, joshuaspence, Korvin, epriestley Tags: #remarkup Maniphest Tasks: T6916 Differential Revision: https://secure.phabricator.com/D11297 --- .../config/PhabricatorFilesConfigOptions.php | 43 ++++++++++++-- .../PhabricatorFileInfoController.php | 54 +++++++++++++---- .../PhabricatorEmbedFileRemarkupRule.php | 59 ++++++++++++++++--- .../files/storage/PhabricatorFile.php | 10 ++++ src/docs/user/userguide/remarkup.diviner | 19 ++++-- webroot/rsrc/css/core/remarkup.css | 11 ++++ .../rsrc/css/phui/phui-property-list-view.css | 7 +++ 7 files changed, 173 insertions(+), 30 deletions(-) diff --git a/src/applications/files/config/PhabricatorFilesConfigOptions.php b/src/applications/files/config/PhabricatorFilesConfigOptions.php index 43e12eddec..063a6d9138 100644 --- a/src/applications/files/config/PhabricatorFilesConfigOptions.php +++ b/src/applications/files/config/PhabricatorFilesConfigOptions.php @@ -34,9 +34,16 @@ final class PhabricatorFilesConfigOptions 'image/x-icon' => 'image/x-icon', 'image/vnd.microsoft.icon' => 'image/x-icon', - 'audio/x-wav' => 'audio/x-wav', + // This is a generic type for both OGG video and OGG audio. 'application/ogg' => 'application/ogg', - 'audio/mpeg' => 'audio/mpeg', + + 'audio/x-wav' => 'audio/x-wav', + 'audio/mpeg' => 'audio/mpeg', + 'audio/ogg' => 'audio/ogg', + + 'video/mp4' => 'video/mp4', + 'video/ogg' => 'video/ogg', + 'video/webm' => 'video/webm', ); $image_default = array( @@ -49,10 +56,29 @@ final class PhabricatorFilesConfigOptions 'image/vnd.microsoft.icon' => true, ); + + // The "application/ogg" type is listed as both an audio and video type, + // because it may contain either type of content. + $audio_default = array( - 'audio/x-wav' => true, + 'audio/x-wav' => true, + 'audio/mpeg' => true, + 'audio/ogg' => true, + + // These are video or ambiguous types, but can be forced to render as + // audio with `media=audio`, which seems to work properly in browsers. + // (For example, you can embed a music video as audio if you just want + // to set the mood for your task without distracting viewers.) + 'video/mp4' => true, + 'video/ogg' => true, + 'application/ogg' => true, + ); + + $video_default = array( + 'video/mp4' => true, + 'video/ogg' => true, + 'video/webm' => true, 'application/ogg' => true, - 'audio/mpeg' => true, ); // largely lifted from http://en.wikipedia.org/wiki/Internet_media_type @@ -70,6 +96,7 @@ final class PhabricatorFilesConfigOptions // movie file icon 'video/mpeg' => 'fa-file-movie-o', 'video/mp4' => 'fa-file-movie-o', + 'application/ogg' => 'fa-file-movie-o', 'video/ogg' => 'fa-file-movie-o', 'video/quicktime' => 'fa-file-movie-o', 'video/webm' => 'fa-file-movie-o', @@ -122,8 +149,14 @@ final class PhabricatorFilesConfigOptions ->setSummary(pht('Configure which MIME types are audio.')) ->setDescription( pht( - 'List of MIME types which can be used to render an `%s` tag.', + 'List of MIME types which can be rendered with an `%s` tag.', '