diff options
author | Stas Vilchik <vilchiks@gmail.com> | 2016-01-26 14:34:51 +0100 |
---|---|---|
committer | Stas Vilchik <vilchiks@gmail.com> | 2016-01-28 16:14:24 +0100 |
commit | de0b07fffd01cc2552c9a4a50ebbc994fb684400 (patch) | |
tree | c66be7f9c8ae4761ef7b4cc64f96f7e51cd954b4 | |
parent | 2a981676b2f034933c01b7fd7b745a61263690ac (diff) | |
download | sonarqube-de0b07fffd01cc2552c9a4a50ebbc994fb684400.tar.gz sonarqube-de0b07fffd01cc2552c9a4a50ebbc994fb684400.zip |
SONAR-7230 Move user notifications into separate page of "My Account" space
18 files changed, 529 insertions, 27 deletions
diff --git a/it/it-tests/src/test/java/it/qualityGate/QualityGateNotificationTest.java b/it/it-tests/src/test/java/it/qualityGate/QualityGateNotificationTest.java index 37271141fbe..5920d677138 100644 --- a/it/it-tests/src/test/java/it/qualityGate/QualityGateNotificationTest.java +++ b/it/it-tests/src/test/java/it/qualityGate/QualityGateNotificationTest.java @@ -70,7 +70,6 @@ public class QualityGateNotificationTest { } @Test - @Ignore("waiting for SONAR-7230") public void status_on_metric_variation_and_send_notifications() throws Exception { Wiser smtpServer = new Wiser(NetworkUtils.getNextAvailablePort()); try { diff --git a/it/it-tests/src/test/resources/qualityGate/notifications/activate_notification_channels.html b/it/it-tests/src/test/resources/qualityGate/notifications/activate_notification_channels.html index b72649cf564..fc1b770918e 100644 --- a/it/it-tests/src/test/resources/qualityGate/notifications/activate_notification_channels.html +++ b/it/it-tests/src/test/resources/qualityGate/notifications/activate_notification_channels.html @@ -35,17 +35,22 @@ </tr> <tr> <td>open</td> - <td>/account/index</td> + <td>/sonar/account/notifications</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>id=global_notifs_NewAlerts_EmailNotificationChannel</td> <td></td> </tr> <tr> <td>check</td> - <td>global_notifs_NewAlerts.EmailNotificationChannel</td> + <td>id=global_notifs_NewAlerts_EmailNotificationChannel</td> <td></td> </tr> <tr> <td>clickAndWait</td> - <td>//input[@value='Save changes']</td> + <td>id=submit-notifications</td> <td></td> </tr> </tbody> diff --git a/server/sonar-web/src/main/js/api/components.js b/server/sonar-web/src/main/js/api/components.js index 89a6e35ffec..32ed9f744cd 100644 --- a/server/sonar-web/src/main/js/api/components.js +++ b/server/sonar-web/src/main/js/api/components.js @@ -91,3 +91,13 @@ export function getBreadcrumbs ({ id, key }) { return [...reversedAncestors, r.component]; }); } + +export function getProjectsWithInternalId (query) { + const url = window.baseUrl + '/api/resources/search'; + const data = { + f: 's2', + q: 'TRK', + s: query + }; + return getJSON(url, data).then(r => r.results); +} diff --git a/server/sonar-web/src/main/js/apps/account/app.js b/server/sonar-web/src/main/js/apps/account/app.js index 5af38f5b6c3..4a78a6a04fb 100644 --- a/server/sonar-web/src/main/js/apps/account/app.js +++ b/server/sonar-web/src/main/js/apps/account/app.js @@ -21,10 +21,12 @@ import React from 'react'; import { render } from 'react-dom'; import { Router, Route, IndexRoute, Redirect } from 'react-router'; import { createHistory, useBasename } from 'history'; +import { Provider } from 'react-redux'; +import configureStore from './store/configureStore'; import AccountApp from './containers/AccountApp'; import Home from './components/Home'; -import Notifications from './components/Notifications'; +import NotificationsContainer from './containers/NotificationsContainer'; window.sonarqube.appStarted.then(options => { const el = document.querySelector(options.el); @@ -33,16 +35,21 @@ window.sonarqube.appStarted.then(options => { basename: window.baseUrl + '/account' }); + const store = configureStore(); + document.querySelector('html').classList.add('dashboard-page'); document.querySelector('#container').classList.add('page-wrapper-context'); render(( - <Router history={history}> - <Route path="/" component={AccountApp}> - <IndexRoute component={Home}/> + <Provider store={store}> + <Router history={history}> + <Route path="/" component={AccountApp}> + <IndexRoute component={Home}/> + <Route path="notifications" component={NotificationsContainer}/> - <Redirect from="/index" to="/"/> - </Route> - </Router> + <Redirect from="/index" to="/"/> + </Route> + </Router> + </Provider> ), el); }); diff --git a/server/sonar-web/src/main/js/apps/account/components/GlobalNotifications.js b/server/sonar-web/src/main/js/apps/account/components/GlobalNotifications.js new file mode 100644 index 00000000000..5f5de5de9b4 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/account/components/GlobalNotifications.js @@ -0,0 +1,53 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import React from 'react'; + +import NotificationsList from './NotificationsList'; +import { translate } from '../../../helpers/l10n'; + +export default function GlobalNotifications ({ notifications, channels }) { + return ( + <div> + <header className="page-header"> + <h2 className="page-title"> + {translate('my_profile.overall_notifications.title')} + </h2> + </header> + + <table className="form"> + <thead> + <tr> + <th></th> + {channels.map(channel => ( + <th key={channel} className="text-center"> + <h4>{translate('notification.channel', channel)}</h4> + </th> + ))} + </tr> + </thead> + + <NotificationsList + notifications={notifications} + checkboxId={(d, c) => `global_notifs_${d}_${c}`} + checkboxName={(d, c) => `global_notifs[${d}.${c}]`}/> + </table> + </div> + ); +} diff --git a/server/sonar-web/src/main/js/apps/account/components/Nav.js b/server/sonar-web/src/main/js/apps/account/components/Nav.js index 3a85c7da307..e19213d3062 100644 --- a/server/sonar-web/src/main/js/apps/account/components/Nav.js +++ b/server/sonar-web/src/main/js/apps/account/components/Nav.js @@ -38,6 +38,11 @@ const Nav = () => ( <i className="icon-home"/> </IndexLink> </li> + <li> + <IndexLink to="notifications" activeClassName="active"> + {translate('my_account.notifications')} + </IndexLink> + </li> </ul> </div> </nav> diff --git a/server/sonar-web/src/main/js/apps/account/components/Notifications.js b/server/sonar-web/src/main/js/apps/account/components/Notifications.js index 195a134c15c..45a9d5839eb 100644 --- a/server/sonar-web/src/main/js/apps/account/components/Notifications.js +++ b/server/sonar-web/src/main/js/apps/account/components/Notifications.js @@ -19,8 +19,41 @@ */ import React from 'react'; -const Notifications = () => ( - <h1>Notifications</h1> -); +import GlobalNotifications from './GlobalNotifications'; +import ProjectNotifications from './ProjectNotifications'; +import { translate } from '../../../helpers/l10n'; -export default Notifications; +export default function Notifications ({ globalNotifications, projectNotifications, onAddProject, onRemoveProject }) { + const channels = globalNotifications[0].channels.map(c => c.id); + + return ( + <div> + <p className="big-spacer-bottom"> + {translate('notification.dispatcher.information')} + </p> + <form id="notif_form" method="post" action={`${window.baseUrl}/account/update_notifications`}> + <div className="columns"> + <div className="column-half"> + <GlobalNotifications + notifications={globalNotifications} + channels={channels}/> + </div> + + <div className="column-half"> + <ProjectNotifications + notifications={projectNotifications} + channels={channels} + onAddProject={onAddProject} + onRemoveProject={onRemoveProject}/> + </div> + </div> + + <p className="big-spacer-top panel panel-vertical bordered-top text-right"> + <button id="submit-notifications" type="submit"> + {translate('my_profile.notifications.submit')} + </button> + </p> + </form> + </div> + ); +} diff --git a/server/sonar-web/src/main/js/apps/account/components/NotificationsList.js b/server/sonar-web/src/main/js/apps/account/components/NotificationsList.js new file mode 100644 index 00000000000..a8044b4b142 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/account/components/NotificationsList.js @@ -0,0 +1,42 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import React from 'react'; + +import { translate } from '../../../helpers/l10n'; + +export default function NotificationsList ({ notifications, checkboxName, checkboxId }) { + return ( + <tbody> + {notifications.map(notification => ( + <tr key={notification.dispatcher}> + <td>{translate('notification.dispatcher', notification.dispatcher)}</td> + {notification.channels.map(channel => ( + <td key={channel.id} className="text-center"> + <input defaultChecked={channel.checked} + id={checkboxId(notification.dispatcher, channel.id)} + name={checkboxName(notification.dispatcher, channel.id)} + type="checkbox"/> + </td> + ))} + </tr> + ))} + </tbody> + ); +} diff --git a/server/sonar-web/src/main/js/apps/account/components/ProjectNotifications.js b/server/sonar-web/src/main/js/apps/account/components/ProjectNotifications.js new file mode 100644 index 00000000000..5683e50e1d8 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/account/components/ProjectNotifications.js @@ -0,0 +1,102 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import React from 'react'; +import Select from 'react-select'; + +import NotificationsList from './NotificationsList'; +import { translate } from '../../../helpers/l10n'; +import { getProjectsWithInternalId } from '../../../api/components'; + +export default function ProjectNotifications ({ notifications, channels, onAddProject, onRemoveProject }) { + const loadOptions = (query) => { + return getProjectsWithInternalId(query) + .then(results => results.map(r => { + return { + value: r.id, + label: r.text + }; + })) + .then(options => { + return { options }; + }); + }; + + const handleAddProject = (selected) => { + const project = { + internalId: selected.value, + name: selected.label + }; + onAddProject(project); + }; + + const handleRemoveProject = (project) => ( + (e) => { + e.preventDefault; + onRemoveProject(project); + } + ); + + return ( + <div> + <header className="page-header"> + <h2 className="page-title"> + {translate('my_profile.per_project_notifications.title')} + </h2> + <div className="pull-right"> + <Select.Async + name="new_project" + style={{ width: '150px' }} + loadOptions={loadOptions} + onChange={handleAddProject} + placeholder="Add Project"/> + </div> + </header> + + {!notifications.length && ( + <div className="note"> + {translate('my_account.no_project_notifications')} + </div> + )} + + {notifications.map(p => ( + <table key={p.project.internalId} className="form spacer-bottom"> + <thead> + <tr> + <th> + <a onClick={handleRemoveProject(p.project)} + className="spacer-right icon-delete js-delete-project" href="#"></a> + <h3 className="display-inline-block">{p.project.name}</h3> + </th> + {channels.map(channel => ( + <th key={channel} className="text-center"> + <h4>{translate('notification.channel', channel)}</h4> + </th> + ))} + </tr> + </thead> + <NotificationsList + notifications={p.notifications} + checkboxId={(d, c) => `project_notifs_${p.project.internalId}_${d}_${c}`} + checkboxName={(d, c) => `project_notifs[${p.project.internalId}][${d}][${c}]`}/> + </table> + ))} + </div> + ); +} diff --git a/server/sonar-web/src/main/js/apps/account/containers/NotificationsContainer.js b/server/sonar-web/src/main/js/apps/account/containers/NotificationsContainer.js new file mode 100644 index 00000000000..84328295905 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/account/containers/NotificationsContainer.js @@ -0,0 +1,42 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import { connect } from 'react-redux'; + +import { addProjectNotifications, removeProjectNotifications } from '../store/actions'; +import Notifications from './../components/Notifications'; + +function mapStateToProps (state) { + return { + globalNotifications: window.sonarqube.notifications.global, + projectNotifications: state.projectNotifications + }; +} + +function mapDispatchToProps (dispatch) { + return { + onAddProject: (project) => dispatch(addProjectNotifications(project)), + onRemoveProject: (project) => dispatch(removeProjectNotifications(project)) + }; +} + +export default connect( + mapStateToProps, + mapDispatchToProps +)(Notifications); diff --git a/server/sonar-web/src/main/js/apps/account/store/actions.js b/server/sonar-web/src/main/js/apps/account/store/actions.js new file mode 100644 index 00000000000..bee94192a5c --- /dev/null +++ b/server/sonar-web/src/main/js/apps/account/store/actions.js @@ -0,0 +1,35 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +export const ADD_PROJECT_NOTIFICATIONS = 'ADD_PROJECT_NOTIFICATIONS'; +export const REMOVE_PROJECT_NOTIFICATIONS = 'REMOVE_PROJECT_NOTIFICATIONS'; + +export function addProjectNotifications (project) { + return { + type: ADD_PROJECT_NOTIFICATIONS, + project + }; +} + +export function removeProjectNotifications (project) { + return { + type: REMOVE_PROJECT_NOTIFICATIONS, + project + }; +} diff --git a/server/sonar-web/src/main/js/apps/account/store/configureStore.js b/server/sonar-web/src/main/js/apps/account/store/configureStore.js new file mode 100644 index 00000000000..e412913dc76 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/account/store/configureStore.js @@ -0,0 +1,36 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import { createStore, applyMiddleware } from 'redux'; +import thunk from 'redux-thunk'; +import createLogger from 'redux-logger'; +import rootReducer from './reducers'; + +const logger = createLogger({ + predicate: () => process.env.NODE_ENV !== 'production' +}); + +const createStoreWithMiddleware = applyMiddleware( + thunk, + logger +)(createStore); + +export default function configureStore () { + return createStoreWithMiddleware(rootReducer); +} diff --git a/server/sonar-web/src/main/js/apps/account/store/reducers.js b/server/sonar-web/src/main/js/apps/account/store/reducers.js new file mode 100644 index 00000000000..23921840d8b --- /dev/null +++ b/server/sonar-web/src/main/js/apps/account/store/reducers.js @@ -0,0 +1,69 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import { ADD_PROJECT_NOTIFICATIONS, REMOVE_PROJECT_NOTIFICATIONS } from './actions'; + +function addProjectNotifications (state, project) { + const found = state.find(notification => { + return notification.project.internalId === project.internalId; + }); + + if (found) { + return state; + } + + const newProjectNotification = { + project, + notifications: window.sonarqube.notifications.projectDispatchers.map(dispatcher => { + const channels = window.sonarqube.notifications.channels.map(channel => { + return { id: channel, checked: false }; + }); + return { dispatcher, channels }; + }) + }; + + return [...state, newProjectNotification]; +} + +function removeProjectNotifications (state, project) { + return state.filter(notification => { + return notification.project.internalId !== project.internalId; + }); +} + +export const initialState = { + projectNotifications: window.sonarqube.notifications.project +}; + +export default function (state = initialState, action) { + switch (action.type) { + case ADD_PROJECT_NOTIFICATIONS: + return { + ...state, + projectNotifications: addProjectNotifications(state.projectNotifications, action.project) + }; + case REMOVE_PROJECT_NOTIFICATIONS: + return { + ...state, + projectNotifications: removeProjectNotifications(state.projectNotifications, action.project) + }; + default: + return state; + } +} diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/account_controller.rb b/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/account_controller.rb index aa803c1f9b9..508c5ac7ae1 100644 --- a/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/account_controller.rb +++ b/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/account_controller.rb @@ -22,10 +22,6 @@ class AccountController < ApplicationController before_filter :login_required def index - - end - - def notifications @channels = notification_service.getChannels() @global_dispatchers = dispatchers_for_scope("globalNotification") @per_project_dispatchers = dispatchers_for_scope("perProjectNotification") @@ -84,13 +80,7 @@ class AccountController < ApplicationController end end - # New project added - new_params = {} - unless params[:new_project].blank? - new_params[:new_project] = params[:new_project] - end - - redirect_to :action => 'index', :params => new_params + redirect_to "#{ApplicationController.root_context}/account/notifications" end private diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/api/resources_controller.rb b/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/api/resources_controller.rb index b85e5c7adbb..64da4002f0c 100644 --- a/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/api/resources_controller.rb +++ b/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/api/resources_controller.rb @@ -39,7 +39,6 @@ class Api::ResourcesController < Api::ApiController qualifiers=[] end - bad_request("Minimum search is #{ResourceIndex::MIN_SEARCH_SIZE} characters") if search_text.size<ResourceIndex::MIN_SEARCH_SIZE bad_request("Page index must be greater than 0") if page<=0 bad_request("Page size must be greater than 0") if page_size<=0 diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/account/index.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/account/index.html.erb index 4ffc62880f0..f5a9a100226 100644 --- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/account/index.html.erb +++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/account/index.html.erb @@ -33,6 +33,77 @@ <% end %> ] }; + window.sonarqube.notifications = { + channels: [ + <% for channel in @channels -%> + '<%= escape_javascript channel.getKey() -%>', + <% end %> + ], + + globalDispatchers: [ + <% for dispatcher in @global_dispatchers -%> + '<%= escape_javascript dispatcher -%>', + <% end %> + ], + + projectDispatchers: [ + <% for dispatcher in @per_project_dispatchers -%> + '<%= escape_javascript dispatcher -%>', + <% end %> + ], + + global: [ + <% for dispatcher in @global_dispatchers %> + { + dispatcher: '<%= escape_javascript dispatcher -%>', + channels: [ + <% + for channel in @channels + notification_id = dispatcher + '.' + channel.getKey() + check_box_checked = @global_notifications[notification_id] + -%> + { + id: '<%= escape_javascript channel.getKey() -%>', + checked: <%= check_box_checked ? 'true' : 'false' %> + }, + <% end %> + ] + }, + <% end %> + ], + + project: [ + <% @per_project_notifications.each do |project_key, notification| %> + <% project = Project.by_key(project_key) %> + { + project: { + internalId: <%= project.id -%>, + id: '<%= escape_javascript project.uuid -%>', + key: '<%= escape_javascript project.key -%>', + name: '<%= escape_javascript project.name -%>' + }, + notifications: [ + <% @per_project_dispatchers.each do |dispatcher| %> + { + dispatcher: '<%= escape_javascript dispatcher -%>', + channels: [ + <% + for channel in @channels + check_box_checked = notification[dispatcher].include?(channel.getKey()) + -%> + { + id: '<%= escape_javascript channel.getKey() -%>', + checked: <%= check_box_checked ? 'true' : 'false' %> + }, + <% end %> + ] + }, + <% end %> + ] + }, + <% end %> + ] + }; </script> <script src="<%= ApplicationController.root_context -%>/js/bundles/account.js?v=<%= sonar_version -%>"></script> <% end %> diff --git a/server/sonar-web/src/main/webapp/WEB-INF/config/routes.rb b/server/sonar-web/src/main/webapp/WEB-INF/config/routes.rb index 36e2931ee2f..69814d108fb 100644 --- a/server/sonar-web/src/main/webapp/WEB-INF/config/routes.rb +++ b/server/sonar-web/src/main/webapp/WEB-INF/config/routes.rb @@ -34,6 +34,8 @@ ActionController::Routing::Routes.draw do |map| map.connect 'api_documentation/*other', :controller => 'api_documentation', :action => 'index' map.connect 'quality_gates/*other', :controller => 'quality_gates', :action => 'index' map.connect 'overview/*other', :controller => 'overview', :action => 'index' + map.connect 'account/update_notifications', :controller => 'account', :action => 'update_notifications' + map.connect 'account/*other', :controller => 'account', :action => 'index' # Install the default route as the lowest priority. map.connect ':controller/:action/:id', :requirements => { :id => /.*/ } diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties index c8f3f6da8fd..fad0339a29d 100644 --- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties +++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties @@ -2159,6 +2159,8 @@ my_account.page=My Account my_account.favorite_components=Favorite Components my_account.favorite_issue_filters=Favorite Issue Filters my_account.favorite_measure_filters=Favorite Measure Filters +my_account.notifications=Notifications +my_account.no_project_notifications=You have not set project notifications yet. |