diff --git a/resources/sql/patches/20130319.conpherence.sql b/resources/sql/patches/20130319.conpherence.sql new file mode 100644 index 0000000000..e18590e6bd --- /dev/null +++ b/resources/sql/patches/20130319.conpherence.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_conpherence.conpherence_participant + ADD settings LONGTEXT NOT NULL COLLATE utf8_bin AFTER behindTransactionPHID; diff --git a/src/__celerity_resource_map__.php b/src/__celerity_resource_map__.php index f7b035838e..580cc14da1 100644 --- a/src/__celerity_resource_map__.php +++ b/src/__celerity_resource_map__.php @@ -840,7 +840,7 @@ celerity_register_resource_map(array( ), 'conpherence-widget-pane-css' => array( - 'uri' => '/res/0e4a8ded/rsrc/css/application/conpherence/widget-pane.css', + 'uri' => '/res/bd8ca250/rsrc/css/application/conpherence/widget-pane.css', 'type' => 'css', 'requires' => array( @@ -1206,7 +1206,7 @@ celerity_register_resource_map(array( ), 'javelin-behavior-conpherence-pontificate' => array( - 'uri' => '/res/15263692/rsrc/js/application/conpherence/behavior-pontificate.js', + 'uri' => '/res/fe634761/rsrc/js/application/conpherence/behavior-pontificate.js', 'type' => 'js', 'requires' => array( @@ -1219,13 +1219,16 @@ celerity_register_resource_map(array( ), 'javelin-behavior-conpherence-widget-pane' => array( - 'uri' => '/res/43a0fe1b/rsrc/js/application/conpherence/behavior-widget-pane.js', + 'uri' => '/res/52b80633/rsrc/js/application/conpherence/behavior-widget-pane.js', 'type' => 'js', 'requires' => array( 0 => 'javelin-behavior', 1 => 'javelin-dom', 2 => 'javelin-stratcom', + 3 => 'javelin-workflow', + 4 => 'javelin-util', + 5 => 'phabricator-notification', ), 'disk' => '/rsrc/js/application/conpherence/behavior-widget-pane.js', ), diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index fe54457a79..5a6886f6c6 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -240,6 +240,7 @@ phutil_register_library_map(array( 'ConpherencePeopleMenuEventListener' => 'applications/conpherence/events/ConpherencePeopleMenuEventListener.php', 'ConpherencePontificateControl' => 'applications/conpherence/view/ConpherencePontificateControl.php', 'ConpherenceReplyHandler' => 'applications/conpherence/mail/ConpherenceReplyHandler.php', + 'ConpherenceSettings' => 'applications/conpherence/constants/ConpherenceSettings.php', 'ConpherenceThread' => 'applications/conpherence/storage/ConpherenceThread.php', 'ConpherenceThreadQuery' => 'applications/conpherence/query/ConpherenceThreadQuery.php', 'ConpherenceTransaction' => 'applications/conpherence/storage/ConpherenceTransaction.php', @@ -1301,6 +1302,7 @@ phutil_register_library_map(array( 'PhabricatorSettingsPanel' => 'applications/settings/panel/PhabricatorSettingsPanel.php', 'PhabricatorSettingsPanelAccount' => 'applications/settings/panel/PhabricatorSettingsPanelAccount.php', 'PhabricatorSettingsPanelConduit' => 'applications/settings/panel/PhabricatorSettingsPanelConduit.php', + 'PhabricatorSettingsPanelConpherencePreferences' => 'applications/settings/panel/PhabricatorSettingsPanelConpherencePreferences.php', 'PhabricatorSettingsPanelDiffPreferences' => 'applications/settings/panel/PhabricatorSettingsPanelDiffPreferences.php', 'PhabricatorSettingsPanelDisplayPreferences' => 'applications/settings/panel/PhabricatorSettingsPanelDisplayPreferences.php', 'PhabricatorSettingsPanelEmailAddresses' => 'applications/settings/panel/PhabricatorSettingsPanelEmailAddresses.php', @@ -1907,6 +1909,7 @@ phutil_register_library_map(array( 'ConpherencePeopleMenuEventListener' => 'PhutilEventListener', 'ConpherencePontificateControl' => 'AphrontFormControl', 'ConpherenceReplyHandler' => 'PhabricatorMailReplyHandler', + 'ConpherenceSettings' => 'ConpherenceConstants', 'ConpherenceThread' => array( 0 => 'ConpherenceDAO', @@ -2907,6 +2910,7 @@ phutil_register_library_map(array( 'PhabricatorSettingsMainController' => 'PhabricatorController', 'PhabricatorSettingsPanelAccount' => 'PhabricatorSettingsPanel', 'PhabricatorSettingsPanelConduit' => 'PhabricatorSettingsPanel', + 'PhabricatorSettingsPanelConpherencePreferences' => 'PhabricatorSettingsPanel', 'PhabricatorSettingsPanelDiffPreferences' => 'PhabricatorSettingsPanel', 'PhabricatorSettingsPanelDisplayPreferences' => 'PhabricatorSettingsPanel', 'PhabricatorSettingsPanelEmailAddresses' => 'PhabricatorSettingsPanel', diff --git a/src/applications/conpherence/constants/ConpherenceSettings.php b/src/applications/conpherence/constants/ConpherenceSettings.php new file mode 100644 index 0000000000..0413ba536a --- /dev/null +++ b/src/applications/conpherence/constants/ConpherenceSettings.php @@ -0,0 +1,22 @@ +getStr('notifications'); + $participant = $conpherence->getParticipant($user->getPHID()); + $participant->setSettings(array('notifications' => $notifications)); + $participant->save(); + $result = pht( + 'Updated notification settings to "%s".', + ConpherenceSettings::getHumanString($notifications)); + return id(new AphrontAjaxResponse()) + ->setContent($result); + break; case 'metadata': $xactions = array(); $top = $request->getInt('image_y'); diff --git a/src/applications/conpherence/controller/ConpherenceWidgetController.php b/src/applications/conpherence/controller/ConpherenceWidgetController.php index 65a55e599b..c343269faa 100644 --- a/src/applications/conpherence/controller/ConpherenceWidgetController.php +++ b/src/applications/conpherence/controller/ConpherenceWidgetController.php @@ -8,6 +8,15 @@ final class ConpherenceWidgetController extends private $conpherenceID; private $conpherence; + private $userPreferences; + + public function setUserPreferences(PhabricatorUserPreferences $pref) { + $this->userPreferences = $pref; + return $this; + } + public function getUserPreferences() { + return $this->userPreferences; + } public function setConpherence(ConpherenceThread $conpherence) { $this->conpherence = $conpherence; @@ -44,6 +53,8 @@ final class ConpherenceWidgetController extends ->executeOne(); $this->setConpherence($conpherence); + $this->setUserPreferences($user->loadPreferences()); + $widgets = $this->renderWidgetPaneContent(); $content = $widgets; return id(new AphrontAjaxResponse())->setContent($content); @@ -57,8 +68,8 @@ final class ConpherenceWidgetController extends Javelin::initBehavior( 'conpherence-widget-pane', array( - 'form_pane' => 'conpherence-form', 'file_widget' => 'widgets-files', + 'settings_widget' => 'widgets-settings', 'widgetRegistery' => array( 'widgets-conpherence-list' => $cant_toggle, 'widgets-conversation' => $cant_toggle, @@ -201,7 +212,63 @@ final class ConpherenceWidgetController extends } private function renderSettingsWidgetPaneContent() { - return 'TODO - settings'; + $user = $this->getRequest()->getUser(); + $conpherence = $this->getConpherence(); + $participants = $conpherence->getParticipants(); + $participant = $participants[$user->getPHID()]; + $default = ConpherenceSettings::EMAIL_ALWAYS; + $preference = $this->getUserPreferences(); + if ($preference) { + $default = $preference->getPreference( + PhabricatorUserPreferences::PREFERENCE_CONPH_NOTIFICATIONS, + ConpherenceSettings::EMAIL_ALWAYS); + } + $settings = $participant->getSettings(); + $notifications = idx( + $settings, + 'notifications', + $default); + $options = id(new AphrontFormRadioButtonControl()) + ->addButton( + ConpherenceSettings::EMAIL_ALWAYS, + ConpherenceSettings::getHumanString( + ConpherenceSettings::EMAIL_ALWAYS), + '') + ->addButton( + ConpherenceSettings::NOTIFICATIONS_ONLY, + ConpherenceSettings::getHumanString( + ConpherenceSettings::NOTIFICATIONS_ONLY), + '') + ->setName('notifications') + ->setValue($notifications); + + $href = $this->getApplicationURI( + 'update/'.$conpherence->getID().'/'); + $layout = array( + $options, + phutil_tag( + 'input', + array( + 'type' => 'hidden', + 'name' => 'action', + 'value' => 'notifications' + )), + javelin_tag( + 'button', + array( + 'sigil' => 'notifications-update', + 'class' => 'notifications-update grey', + ), + pht('Update Notifications')) + ); + + return phabricator_form( + $user, + array( + 'method' => 'POST', + 'action' => $href, + ), + $layout); } private function renderCalendarWidgetPaneContent() { diff --git a/src/applications/conpherence/editor/ConpherenceEditor.php b/src/applications/conpherence/editor/ConpherenceEditor.php index 1a1426ea44..292a369851 100644 --- a/src/applications/conpherence/editor/ConpherenceEditor.php +++ b/src/applications/conpherence/editor/ConpherenceEditor.php @@ -159,13 +159,15 @@ final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor { } break; case ConpherenceTransactionType::TYPE_PARTICIPANTS: + $participants = array(); foreach ($xaction->getNewValue() as $participant) { if ($participant == $this->getActor()->getPHID()) { $status = ConpherenceParticipationStatus::UP_TO_DATE; } else { $status = ConpherenceParticipationStatus::BEHIND; } - id(new ConpherenceParticipant()) + $participants[] = + id(new ConpherenceParticipant()) ->setConpherencePHID($object->getPHID()) ->setParticipantPHID($participant) ->setParticipationStatus($status) @@ -173,6 +175,8 @@ final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor { ->setBehindTransactionPHID($xaction->getPHID()) ->save(); } + $participants = mpull($participants, null, 'getParticipantPHID'); + $object->attachParticipants($participants); break; } } @@ -221,7 +225,28 @@ final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor { protected function getMailTo(PhabricatorLiskDAO $object) { $participants = $object->getParticipants(); - return array_keys($participants); + $preferences = id(new PhabricatorUserPreferences()) + ->loadAllWhere('userPHID in (%Ls)', array_keys($participants)); + $preferences = mpull($preferences, null, 'getUserPHID'); + $to_phids = array(); + 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); + } + $settings = $participant->getSettings(); + $notifications = idx( + $settings, + 'notifications', + $default); + if ($notifications == ConpherenceSettings::EMAIL_ALWAYS) { + $to_phids[] = $phid; + } + } + return $to_phids; } protected function getMailCC(PhabricatorLiskDAO $object) { @@ -251,5 +276,4 @@ final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor { protected function supportsSearch() { return false; } - } diff --git a/src/applications/conpherence/storage/ConpherenceParticipant.php b/src/applications/conpherence/storage/ConpherenceParticipant.php index be897c11fe..075a00502f 100644 --- a/src/applications/conpherence/storage/ConpherenceParticipant.php +++ b/src/applications/conpherence/storage/ConpherenceParticipant.php @@ -10,6 +10,19 @@ final class ConpherenceParticipant extends ConpherenceDAO { protected $participationStatus; protected $behindTransactionPHID; protected $dateTouched; + protected $settings = array(); + + public function getConfiguration() { + return array( + self::CONFIG_SERIALIZATION => array( + 'settings' => self::SERIALIZATION_JSON, + ), + ) + parent::getConfiguration(); + } + + public function getSettings() { + return nonempty($this->settings, array()); + } public function markUpToDate(ConpherenceTransaction $xaction) { if (!$this->isUpToDate()) { diff --git a/src/applications/settings/panel/PhabricatorSettingsPanelConpherencePreferences.php b/src/applications/settings/panel/PhabricatorSettingsPanelConpherencePreferences.php new file mode 100644 index 0000000000..778f0c3888 --- /dev/null +++ b/src/applications/settings/panel/PhabricatorSettingsPanelConpherencePreferences.php @@ -0,0 +1,84 @@ +isBeta(); + $allow_beta = + PhabricatorEnv::getEnvConfig('phabricator.show-beta-applications'); + return ($is_prod || $allow_beta) && $app->isInstalled(); + } + + public function getPanelKey() { + return 'conpherence'; + } + + public function getPanelName() { + return pht('Conpherence Preferences'); + } + + public function getPanelGroup() { + return pht('Application Settings'); + } + + public function processRequest(AphrontRequest $request) { + $user = $request->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'))); + + $panel = new AphrontPanelView(); + $panel->setHeader(pht('Conpherence Preferences')); + $panel->appendChild($form); + $panel->setNoBackground(); + + $error_view = null; + if ($request->getBool('saved')) { + $error_view = id(new AphrontErrorView()) + ->setTitle(pht('Preferences Saved')) + ->setSeverity(AphrontErrorView::SEVERITY_NOTICE) + ->setErrors(array(pht('Your preferences have been saved.'))); + } + + return array( + $error_view, + $panel, + ); + } +} + diff --git a/src/applications/settings/storage/PhabricatorUserPreferences.php b/src/applications/settings/storage/PhabricatorUserPreferences.php index 9f5196a215..2f695bd40d 100644 --- a/src/applications/settings/storage/PhabricatorUserPreferences.php +++ b/src/applications/settings/storage/PhabricatorUserPreferences.php @@ -25,6 +25,8 @@ final class PhabricatorUserPreferences extends PhabricatorUserDAO { const PREFERENCE_DIFF_FILETREE = 'diff-filetree'; + const PREFERENCE_CONPH_NOTIFICATIONS = 'conph-notifications'; + protected $userPHID; protected $preferences = array(); diff --git a/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php b/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php index acf82627a2..803fa1dfbf 100644 --- a/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php +++ b/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php @@ -1180,7 +1180,11 @@ final class PhabricatorBuiltinPatchList extends PhabricatorSQLPatchList { '20130319.phabricatorfileexplicitupload.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath( - '20130319.phabricatorfileexplicitupload.sql'), + '20130319.phabricatorfileexplicitupload.sql') + ), + '20130319.conpherence.sql' => array( + 'type' => 'sql', + 'name' => $this->getPatchPath('20130319.conpherence.sql'), ), '20130320.phlux.sql' => array( 'type' => 'sql', diff --git a/webroot/rsrc/css/application/conpherence/widget-pane.css b/webroot/rsrc/css/application/conpherence/widget-pane.css index 6484157637..7c85d7e868 100644 --- a/webroot/rsrc/css/application/conpherence/widget-pane.css +++ b/webroot/rsrc/css/application/conpherence/widget-pane.css @@ -26,6 +26,11 @@ width: 100%; } +.conpherence-widget-pane .aphront-form-inset { + border: 0; + background: url('/rsrc/image/texture/dust_background.jpg'); +} + .conpherence-widget-pane .widgets-header { background-color: #d8dce2; box-shadow: 0px 2px 2px rgba(0,0,0,0.15); @@ -186,3 +191,8 @@ .conpherence-widget-pane .phabricator-remarkup-embed-layout-link { padding-bottom: 1px; } + +/* settings widget */ +.conpherence-widget-pane .notifications-update { + margin: 2px 0px 0px 8px; +} diff --git a/webroot/rsrc/js/application/conpherence/behavior-pontificate.js b/webroot/rsrc/js/application/conpherence/behavior-pontificate.js index b1fc9df43d..9425a23f41 100644 --- a/webroot/rsrc/js/application/conpherence/behavior-pontificate.js +++ b/webroot/rsrc/js/application/conpherence/behavior-pontificate.js @@ -50,13 +50,6 @@ JX.behavior('conpherence-pontificate', function(config) { .start(); }; - JX.DOM.listen( - root, - ['submit', 'didSyntheticSubmit'], - null, - onsubmit - ); - JX.DOM.listen( root, ['click'], diff --git a/webroot/rsrc/js/application/conpherence/behavior-widget-pane.js b/webroot/rsrc/js/application/conpherence/behavior-widget-pane.js index ec3af523b6..036dee5417 100644 --- a/webroot/rsrc/js/application/conpherence/behavior-widget-pane.js +++ b/webroot/rsrc/js/application/conpherence/behavior-widget-pane.js @@ -1,8 +1,11 @@ /** - * @provides javelin-behavior-conpherence-widget-pane * @requires javelin-behavior * javelin-dom * javelin-stratcom + * javelin-workflow + * javelin-util + * phabricator-notification + * @provides javelin-behavior-conpherence-widget-pane */ JX.behavior('conpherence-widget-pane', function(config) { @@ -36,4 +39,29 @@ JX.behavior('conpherence-widget-pane', function(config) { } ); + var settingsRoot = JX.$(config.settings_widget); + + var onsubmitSettings = function (e) { + e.kill(); + var form = JX.DOM.find(settingsRoot, 'form'); + var button = JX.DOM.find(form, 'button'); + JX.Workflow.newFromForm(form) + .setHandler(JX.bind(this, function (r) { + new JX.Notification() + .setDuration(6000) + .setContent(r) + .show(); + button.disabled = ''; + JX.DOM.alterClass(button, 'disabled', false); + })) + .start(); + }; + + JX.DOM.listen( + settingsRoot, + ['click'], + 'notifications-update', + onsubmitSettings + ); + });