diff --git a/src/__celerity_resource_map__.php b/src/__celerity_resource_map__.php index 2f10ff2515..e190e040aa 100644 --- a/src/__celerity_resource_map__.php +++ b/src/__celerity_resource_map__.php @@ -756,6 +756,20 @@ celerity_register_resource_map(array( ), 'disk' => '/rsrc/js/javelin/lib/behavior.js', ), + 'javelin-behavior-aphlict-dropdown' => + array( + 'uri' => '/res/d0025c08/rsrc/js/application/aphlict/behavior-aphlict-dropdown.js', + 'type' => 'js', + 'requires' => + array( + 0 => 'javelin-behavior', + 1 => 'javelin-aphlict', + 2 => 'javelin-util', + 3 => 'javelin-request', + 4 => 'javelin-stratcom', + ), + 'disk' => '/rsrc/js/application/aphlict/behavior-aphlict-dropdown.js', + ), 'javelin-behavior-aphlict-listen' => array( 'uri' => '/res/6388e057/rsrc/js/application/aphlict/behavior-aphlict-listen.js', @@ -2242,7 +2256,7 @@ celerity_register_resource_map(array( ), 'phabricator-standard-page-view' => array( - 'uri' => '/res/07d5f4cb/rsrc/css/application/base/standard-page-view.css', + 'uri' => '/res/479161fe/rsrc/css/application/base/standard-page-view.css', 'type' => 'css', 'requires' => array( @@ -2497,7 +2511,7 @@ celerity_register_resource_map(array( ), array( 'packages' => array( - '4984f83f' => + 'f6b9a4d4' => array( 'name' => 'core.pkg.css', 'symbols' => @@ -2526,7 +2540,7 @@ celerity_register_resource_map(array( 21 => 'phabricator-flag-css', 22 => 'aphront-error-view-css', ), - 'uri' => '/res/pkg/4984f83f/core.pkg.css', + 'uri' => '/res/pkg/f6b9a4d4/core.pkg.css', 'type' => 'css', ), '0c96375e' => @@ -2693,20 +2707,20 @@ celerity_register_resource_map(array( 'reverse' => array( 'aphront-attached-file-view-css' => '7839ae2d', - 'aphront-crumbs-view-css' => '4984f83f', - 'aphront-dialog-view-css' => '4984f83f', - 'aphront-error-view-css' => '4984f83f', - 'aphront-form-view-css' => '4984f83f', + 'aphront-crumbs-view-css' => 'f6b9a4d4', + 'aphront-dialog-view-css' => 'f6b9a4d4', + 'aphront-error-view-css' => 'f6b9a4d4', + 'aphront-form-view-css' => 'f6b9a4d4', 'aphront-headsup-action-list-view-css' => '32f461a4', - 'aphront-headsup-view-css' => '4984f83f', - 'aphront-list-filter-view-css' => '4984f83f', - 'aphront-pager-view-css' => '4984f83f', - 'aphront-panel-view-css' => '4984f83f', - 'aphront-side-nav-view-css' => '4984f83f', - 'aphront-table-view-css' => '4984f83f', - 'aphront-tokenizer-control-css' => '4984f83f', - 'aphront-tooltip-css' => '4984f83f', - 'aphront-typeahead-control-css' => '4984f83f', + 'aphront-headsup-view-css' => 'f6b9a4d4', + 'aphront-list-filter-view-css' => 'f6b9a4d4', + 'aphront-pager-view-css' => 'f6b9a4d4', + 'aphront-panel-view-css' => 'f6b9a4d4', + 'aphront-side-nav-view-css' => 'f6b9a4d4', + 'aphront-table-view-css' => 'f6b9a4d4', + 'aphront-tokenizer-control-css' => 'f6b9a4d4', + 'aphront-tooltip-css' => 'f6b9a4d4', + 'aphront-typeahead-control-css' => 'f6b9a4d4', 'differential-changeset-view-css' => '32f461a4', 'differential-core-view-css' => '32f461a4', 'differential-inline-comment-editor' => '86f654e2', @@ -2772,15 +2786,15 @@ celerity_register_resource_map(array( 'javelin-workflow' => '0c96375e', 'maniphest-task-summary-css' => '7839ae2d', 'maniphest-transaction-detail-css' => '7839ae2d', - 'phabricator-app-buttons-css' => '4984f83f', + 'phabricator-app-buttons-css' => 'f6b9a4d4', 'phabricator-content-source-view-css' => '32f461a4', - 'phabricator-core-buttons-css' => '4984f83f', - 'phabricator-core-css' => '4984f83f', - 'phabricator-directory-css' => '4984f83f', + 'phabricator-core-buttons-css' => 'f6b9a4d4', + 'phabricator-core-css' => 'f6b9a4d4', + 'phabricator-directory-css' => 'f6b9a4d4', 'phabricator-drag-and-drop-file-upload' => '86f654e2', 'phabricator-dropdown-menu' => '0c96375e', - 'phabricator-flag-css' => '4984f83f', - 'phabricator-jump-nav' => '4984f83f', + 'phabricator-flag-css' => 'f6b9a4d4', + 'phabricator-jump-nav' => 'f6b9a4d4', 'phabricator-keyboard-shortcut' => '0c96375e', 'phabricator-keyboard-shortcut-manager' => '0c96375e', 'phabricator-menu-item' => '0c96375e', @@ -2788,11 +2802,11 @@ celerity_register_resource_map(array( 'phabricator-paste-file-upload' => '0c96375e', 'phabricator-prefab' => '0c96375e', 'phabricator-project-tag-css' => '7839ae2d', - 'phabricator-remarkup-css' => '4984f83f', + 'phabricator-remarkup-css' => 'f6b9a4d4', 'phabricator-shaped-request' => '86f654e2', - 'phabricator-standard-page-view' => '4984f83f', + 'phabricator-standard-page-view' => 'f6b9a4d4', 'phabricator-tooltip' => '0c96375e', - 'phabricator-transaction-view-css' => '4984f83f', - 'syntax-highlighting-css' => '4984f83f', + 'phabricator-transaction-view-css' => 'f6b9a4d4', + 'syntax-highlighting-css' => 'f6b9a4d4', ), )); diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index f2b2a160cb..db77d89d53 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -525,6 +525,9 @@ phutil_register_library_map(array( 'ManiphestView' => 'applications/maniphest/view/ManiphestView.php', 'MetaMTAConstants' => 'applications/metamta/constants/MetaMTAConstants.php', 'MetaMTANotificationType' => 'applications/metamta/constants/MetaMTANotificationType.php', + 'NotificationMessage' => 'applications/notification/constants/message/NotificationMessage.php', + 'NotificationPathname' => 'applications/notification/constants/pathname/NotificationPathname.php', + 'NotificationType' => 'applications/notification/constants/type/NotificationType.php', 'OwnersPackageReplyHandler' => 'applications/owners/OwnersPackageReplyHandler.php', 'PackageCreateMail' => 'applications/owners/mail/PackageCreateMail.php', 'PackageDeleteMail' => 'applications/owners/mail/PackageDeleteMail.php', @@ -735,8 +738,12 @@ phutil_register_library_map(array( 'PhabricatorMustVerifyEmailController' => 'applications/auth/controller/PhabricatorMustVerifyEmailController.php', 'PhabricatorMySQLFileStorageEngine' => 'applications/files/engine/PhabricatorMySQLFileStorageEngine.php', 'PhabricatorNotificationBuilder' => 'applications/notification/builder/PhabricatorNotificationBuilder.php', + 'PhabricatorNotificationConstants' => 'applications/notification/constants/base/PhabricatorNotificationConstants.php', 'PhabricatorNotificationController' => 'applications/notification/controller/PhabricatorNotificationController.php', + 'PhabricatorNotificationPanelController' => 'applications/notification/controller/PhabricatorNotificationPanelController.php', 'PhabricatorNotificationQuery' => 'applications/notification/PhabricatorNotificationQuery.php', + 'PhabricatorNotificationStory' => 'applications/notification/base/PhabricatorNotificationStory.php', + 'PhabricatorNotificationStoryTypeConstants' => 'applications/notification/constants/story/PhabricatorNotificationStoryTypeConstants.php', 'PhabricatorNotificationStoryView' => 'applications/notification/view/PhabricatorNotificationStoryView.php', 'PhabricatorNotificationTestController' => 'applications/notification/controller/PhabricatorNotificationTestController.php', 'PhabricatorNotificationView' => 'applications/notification/view/PhabricatorNotificationView.php', @@ -972,6 +979,7 @@ phutil_register_library_map(array( 'PhabricatorUserEmailPreferenceSettingsPanelController' => 'applications/people/controller/settings/panels/PhabricatorUserEmailPreferenceSettingsPanelController.php', 'PhabricatorUserEmailSettingsPanelController' => 'applications/people/controller/settings/panels/PhabricatorUserEmailSettingsPanelController.php', 'PhabricatorUserLog' => 'applications/people/storage/PhabricatorUserLog.php', + 'PhabricatorUserNotificationPreferenceSettingsPanelController' => 'applications/people/controller/settings/panels/PhabricatorUserNotificationPreferenceSettingsPanelController.php', 'PhabricatorUserOAuthInfo' => 'applications/people/storage/PhabricatorUserOAuthInfo.php', 'PhabricatorUserOAuthSettingsPanelController' => 'applications/people/controller/settings/panels/PhabricatorUserOAuthSettingsPanelController.php', 'PhabricatorUserPasswordSettingsPanelController' => 'applications/people/controller/settings/panels/PhabricatorUserPasswordSettingsPanelController.php', @@ -1518,6 +1526,9 @@ phutil_register_library_map(array( 'ManiphestTransactionType' => 'ManiphestConstants', 'ManiphestView' => 'AphrontView', 'MetaMTANotificationType' => 'MetaMTAConstants', + 'NotificationMessage' => 'PhabricatorNotificationConstants', + 'NotificationPathname' => 'PhabricatorNotificationConstants', + 'NotificationType' => 'PhabricatorNotificationConstants', 'OwnersPackageReplyHandler' => 'PhabricatorMailReplyHandler', 'PackageCreateMail' => 'PackageMail', 'PackageDeleteMail' => 'PackageMail', @@ -1697,6 +1708,8 @@ phutil_register_library_map(array( 'PhabricatorMustVerifyEmailController' => 'PhabricatorAuthController', 'PhabricatorMySQLFileStorageEngine' => 'PhabricatorFileStorageEngine', 'PhabricatorNotificationController' => 'PhabricatorController', + 'PhabricatorNotificationPanelController' => 'PhabricatorNotificationController', + 'PhabricatorNotificationStoryTypeConstants' => 'PhabricatorNotificationConstants', 'PhabricatorNotificationStoryView' => 'PhabricatorNotificationView', 'PhabricatorNotificationTestController' => 'PhabricatorNotificationController', 'PhabricatorNotificationView' => 'AphrontView', @@ -1898,6 +1911,7 @@ phutil_register_library_map(array( 'PhabricatorUserEmailPreferenceSettingsPanelController' => 'PhabricatorUserSettingsPanelController', 'PhabricatorUserEmailSettingsPanelController' => 'PhabricatorUserSettingsPanelController', 'PhabricatorUserLog' => 'PhabricatorUserDAO', + 'PhabricatorUserNotificationPreferenceSettingsPanelController' => 'PhabricatorUserSettingsPanelController', 'PhabricatorUserOAuthInfo' => 'PhabricatorUserDAO', 'PhabricatorUserOAuthSettingsPanelController' => 'PhabricatorUserSettingsPanelController', 'PhabricatorUserPasswordSettingsPanelController' => 'PhabricatorUserSettingsPanelController', diff --git a/src/aphront/configuration/AphrontDefaultApplicationConfiguration.php b/src/aphront/configuration/AphrontDefaultApplicationConfiguration.php index 2608e0b2df..ebabff25b5 100644 --- a/src/aphront/configuration/AphrontDefaultApplicationConfiguration.php +++ b/src/aphront/configuration/AphrontDefaultApplicationConfiguration.php @@ -422,6 +422,7 @@ class AphrontDefaultApplicationConfiguration ), '/notification/test/' => 'PhabricatorNotificationTestController', + '/notification/panel/' => 'PhabricatorNotificationPanelController', '/flag/' => array( '' => 'PhabricatorFlagListController', 'view/(?P[^/]+)/' => 'PhabricatorFlagListController', diff --git a/src/applications/notification/builder/PhabricatorNotificationBuilder.php b/src/applications/notification/builder/PhabricatorNotificationBuilder.php index 1e123f88ad..8204dfa9de 100644 --- a/src/applications/notification/builder/PhabricatorNotificationBuilder.php +++ b/src/applications/notification/builder/PhabricatorNotificationBuilder.php @@ -39,6 +39,7 @@ final class PhabricatorNotificationBuilder { $null_view = new AphrontNullView(); + //TODO ADD NOTIFICATIONS HEADER foreach ($stories as $story) { $story->setHandles($handles); $view = $story->renderNotificationView(); diff --git a/src/applications/notification/controller/PhabricatorNotificationPanelController.php b/src/applications/notification/controller/PhabricatorNotificationPanelController.php new file mode 100644 index 0000000000..8cbcb606ea --- /dev/null +++ b/src/applications/notification/controller/PhabricatorNotificationPanelController.php @@ -0,0 +1,44 @@ +getRequest(); + $user = $request->getUser(); + + $query = new PhabricatorNotificationQuery(); + $query->setUserPHID($user->getPHID()); + $query->setLimit(15); + + $stories = $query->execute(); + + $builder = new PhabricatorNotificationBuilder($stories); + $notifications_view = $builder->buildView(); + + $json = array( + "content" => $stories ? + $notifications_view->render() : + "You currently have no notifications", + ); + + return id(new AphrontAjaxResponse())->setContent($json); + } +} diff --git a/src/applications/notification/storage/PhabricatorFeedStoryNotification.php b/src/applications/notification/storage/PhabricatorFeedStoryNotification.php index ca2416b3aa..c239c1e834 100644 --- a/src/applications/notification/storage/PhabricatorFeedStoryNotification.php +++ b/src/applications/notification/storage/PhabricatorFeedStoryNotification.php @@ -54,4 +54,22 @@ final class PhabricatorFeedStoryNotification extends PhabricatorFeedDAO { } } + /* should only be called when notifications are enabled */ + 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']; + } + } diff --git a/src/view/page/PhabricatorStandardPageView.php b/src/view/page/PhabricatorStandardPageView.php index ea98182c4e..1ece236fe3 100644 --- a/src/view/page/PhabricatorStandardPageView.php +++ b/src/view/page/PhabricatorStandardPageView.php @@ -371,6 +371,54 @@ final class PhabricatorStandardPageView extends AphrontPageView { ' '); } + $notification_header = ''; + $notification_dropdown = ''; + + if (PhabricatorEnv::getEnvConfig('notification.enabled') && + $user->isLoggedIn()) { + $aphlict_object_id = 'aphlictswfobject'; + + $aphlict_content = phutil_render_tag( + 'object', + array( + 'classid' => 'clsid:d27cdb6e-ae6d-11cf-96b8-444553540000', + ), + ''. + ''. + ''. + ''); + + Javelin::initBehavior('aphlict-dropdown', array()); + + + $notification_indicator = + javelin_render_tag( + 'td', + array( + 'sigil' => 'aphlict-indicator', + 'id' => 'phabricator-notification-indicator', + ), + id(new PhabricatorFeedStoryNotification) + ->countUnread($user)); + + $notification_header = + $notification_indicator. + ''. + '
'. + $aphlict_content. + '
'. + ''; + $notification_dropdown = + javelin_render_tag( + 'div', + array( + 'sigil' => 'aphlict-dropdown', + 'id' => 'phabricator-notification-dropdown', + ), + ''); + } + $header_chrome = null; $footer_chrome = null; if ($this->getShowChrome()) { @@ -400,8 +448,10 @@ final class PhabricatorStandardPageView extends AphrontPageView { ''. $login_stuff. ''. + $notification_header. ''. - ''; + ''. + $notification_dropdown; $footer_chrome = '
'. $foot_links. diff --git a/webroot/rsrc/css/application/base/standard-page-view.css b/webroot/rsrc/css/application/base/standard-page-view.css index c5c3d64700..296d8c2e47 100644 --- a/webroot/rsrc/css/application/base/standard-page-view.css +++ b/webroot/rsrc/css/application/base/standard-page-view.css @@ -238,3 +238,54 @@ a.handle-disabled { cursor: pointer; } + +#phabricator-notification-indicator { + background-color: #0069a6; + text-align: center; + font-size: 20px; + vertical-align: middle; + height: 40px; + width: 40px; + + -moz-user-select: -moz-none; + -khtml-user-select: none; + -webkit-user-select: none; + -o-user-select: none; +} + +#phabricator-notification-indicator:hover { + cursor: pointer; + background-color: #0089d9; +} + +#phabricator-notification-dropdown { + + word-wrap: break-word; + + overflow-y: auto; + position: absolute; + width: 600px; + height: 300px; + right: 0px; + + + background-color: #f0f0f0; + border: 1px solid darkgrey; + box-shadow: 3px 3px #ccc; + -webkit-box-shadow: 3px 3px #ccc; + -moz-box-shadow: 3px 3px #ccc; + + z-index: 3; +} + +.phabricator-notification-story-head { + border-bottom: 1px solid darkgray; + padding-top: 2px; + padding-bottom: 2px; +} + + +.phabricator-notification-frame { + padding-left: 5px; + padding-right: 10px; +} diff --git a/webroot/rsrc/js/application/aphlict/behavior-aphlict-dropdown.js b/webroot/rsrc/js/application/aphlict/behavior-aphlict-dropdown.js new file mode 100644 index 0000000000..29e9c185cc --- /dev/null +++ b/webroot/rsrc/js/application/aphlict/behavior-aphlict-dropdown.js @@ -0,0 +1,54 @@ +/** + * @provides javelin-behavior-aphlict-dropdown + * @requires javelin-behavior + * javelin-aphlict + * javelin-util + * javelin-request + * javelin-stratcom + */ + +JX.behavior('aphlict-dropdown', function(config) { + var dropdown = JX.$('phabricator-notification-dropdown'); + var indicator = JX.$('phabricator-notification-indicator'); + var visible = false; + + + JX.DOM.hide(dropdown); + + //populate panel + (new JX.Request('/notification/panel/', + function(response) { + JX.DOM.setContent(dropdown, JX.$H(response.content)); + })).send(); + + + JX.Stratcom.listen( + 'click', + null, + function(e) { + if(e.getNode('aphlict-dropdown') || + e.getNode('aphlict-indicator')) { + // Click is inside the dropdown, or on indicator + return; + } + + JX.DOM.hide(dropdown); + visible = false; + }); + + + JX.DOM.listen( + indicator, + 'click', + null, + function(e) { + if(visible) { + JX.DOM.hide(dropdown); + } else { + JX.DOM.show(dropdown); + } + visible = !visible; + } + ) + +});