diff options
Diffstat (limited to 'server/sonar-web/src/main')
19 files changed, 585 insertions, 391 deletions
diff --git a/server/sonar-web/src/main/js/app/components/nav/global/GlobalNav.js b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNav.js index 86ed0974c5c..2e2659d7125 100644 --- a/server/sonar-web/src/main/js/app/components/nav/global/GlobalNav.js +++ b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNav.js @@ -23,10 +23,12 @@ import GlobalNavBranding from './GlobalNavBranding'; import GlobalNavMenu from './GlobalNavMenu'; import GlobalNavUserContainer from './GlobalNavUserContainer'; import Search from '../../search/Search'; -import ShortcutsHelpView from './ShortcutsHelpView'; +import ShortcutsHelp from './ShortcutsHelp'; import { getCurrentUser, getAppState } from '../../../../store/rootReducer'; class GlobalNav extends React.PureComponent { + state = { helpOpen: false }; + componentDidMount() { window.addEventListener('keypress', this.onKeyPress); } @@ -46,13 +48,15 @@ class GlobalNav extends React.PureComponent { } }; - openHelp = e => { - if (e) { - e.preventDefault(); - } - new ShortcutsHelpView().render(); + handleHelpClick = event => { + event.preventDefault(); + this.openHelp(); }; + openHelp = () => this.setState({ helpOpen: true }); + + closeHelp = () => this.setState({ helpOpen: false }); + render() { /* eslint-disable max-len */ return ( @@ -65,7 +69,7 @@ class GlobalNav extends React.PureComponent { <ul className="nav navbar-nav navbar-right"> <Search {...this.props} /> <li> - <a className="navbar-help" onClick={this.openHelp} href="#"> + <a className="navbar-help" onClick={this.handleHelpClick} href="#"> <svg width="16" height="16"> <g transform="matrix(0.0364583,0,0,0.0364583,1,-0.166667)"> <path @@ -79,6 +83,8 @@ class GlobalNav extends React.PureComponent { <GlobalNavUserContainer {...this.props} /> </ul> </div> + + {this.state.helpOpen && <ShortcutsHelp onClose={this.closeHelp} />} </nav> ); } diff --git a/server/sonar-web/src/main/js/app/components/nav/global/ShortcutsHelp.js b/server/sonar-web/src/main/js/app/components/nav/global/ShortcutsHelp.js new file mode 100644 index 00000000000..273b370f17a --- /dev/null +++ b/server/sonar-web/src/main/js/app/components/nav/global/ShortcutsHelp.js @@ -0,0 +1,188 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:info 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. + */ +// @flow +import React from 'react'; +import Modal from 'react-modal'; +import { Link } from 'react-router'; +import { translate } from '../../../../helpers/l10n'; + +type Props = { + onClose: () => void +}; + +export default class ShortcutsHelp extends React.PureComponent { + props: Props; + + handleCloseClick = (event: Event) => { + event.preventDefault(); + this.props.onClose(); + }; + + render() { + return ( + <Modal + isOpen={true} + contentLabel="shortcuts help" + className="modal modal-large" + overlayClassName="modal-overlay" + onRequestClose={this.props.onClose}> + + <div className="modal-head"> + <h2>{translate('help')}</h2> + </div> + + <div className="modal-body modal-container"> + <div className="spacer-bottom"> + <a href="http://www.sonarqube.org">{translate('footer.community')}</a>{' - '} + <a href="https://redirect.sonarsource.com/doc/home.html"> + {translate('footer.documentation')} + </a> + {' - '} + <a href="https://redirect.sonarsource.com/doc/community.html"> + {translate('footer.support')} + </a> + {' - '} + <a href="https://redirect.sonarsource.com/doc/plugin-library.html"> + {translate('footer.plugins')} + </a> + {' - '} + <Link to="/web_api" onClick={this.props.onClose}>{translate('footer.web_api')}</Link> + {' - '} + <Link to="/about" onClick={this.props.onClose}>{translate('footer.about')}</Link> + </div> + + <h2 className="spacer-top spacer-bottom">{translate('shortcuts.modal_title')}</h2> + + <div className="columns"> + <div className="column-half"> + <div className="spacer-bottom"> + <h3 className="shortcuts-section-title">{translate('shortcuts.section.global')}</h3> + <ul className="shortcuts-list"> + <li> + <span className="shortcut-button spacer-right">s</span> + {translate('shortcuts.section.global.search')} + </li> + <li> + <span className="shortcut-button spacer-right">?</span> + {translate('shortcuts.section.global.shortcuts')} + </li> + </ul> + </div> + + <h3 className="shortcuts-section-title">{translate('shortcuts.section.rules')}</h3> + <ul className="shortcuts-list"> + <li> + <span className="shortcut-button little-spacer-right">↑</span> + <span className="shortcut-button spacer-right">↓</span> + {translate('shortcuts.section.rules.navigate_between_rules')} + </li> + <li> + <span className="shortcut-button spacer-right">→</span> + {translate('shortcuts.section.rules.open_details')} + </li> + <li> + <span className="shortcut-button spacer-right">←</span> + {translate('shortcuts.section.rules.return_to_list')} + </li> + <li> + <span className="shortcut-button spacer-right">a</span> + {translate('shortcuts.section.rules.activate')} + </li> + <li> + <span className="shortcut-button spacer-right">d</span> + {translate('shortcuts.section.rules.deactivate')} + </li> + </ul> + </div> + + <div className="column-half"> + <h3 className="shortcuts-section-title">{translate('shortcuts.section.issues')}</h3> + <ul className="shortcuts-list"> + <li> + <span className="shortcut-button little-spacer-right">↑</span> + <span className="shortcut-button spacer-right">↓</span> + {translate('shortcuts.section.issues.navigate_between_issues')} + </li> + <li> + <span className="shortcut-button spacer-right">→</span> + {translate('shortcuts.section.issues.open_details')} + </li> + <li> + <span className="shortcut-button spacer-right">←</span> + {translate('shortcuts.section.issues.return_to_list')} + </li> + <li> + <span className="shortcut-button little-spacer-right">alt</span> + <span className="little-spacer-right">+</span> + <span className="shortcut-button little-spacer-right">↑</span> + <span className="shortcut-button spacer-right">↓</span> + {translate('issues.to_navigate_issue_locations')} + </li> + <li> + <span className="shortcut-button little-spacer-right">alt</span> + <span className="little-spacer-right">+</span> + <span className="shortcut-button little-spacer-right">←</span> + <span className="shortcut-button spacer-right">→</span> + {translate('issues.to_switch_flows')} + </li> + <li> + <span className="shortcut-button spacer-right">f</span> + {translate('shortcuts.section.issue.do_transition')} + </li> + <li> + <span className="shortcut-button spacer-right">a</span> + {translate('shortcuts.section.issue.assign')} + </li> + <li> + <span className="shortcut-button spacer-right">m</span> + {translate('shortcuts.section.issue.assign_to_me')} + </li> + <li> + <span className="shortcut-button spacer-right">i</span> + {translate('shortcuts.section.issue.change_severity')} + </li> + <li> + <span className="shortcut-button spacer-right">c</span> + {translate('shortcuts.section.issue.comment')} + </li> + <li> + <span className="shortcut-button little-spacer-right">ctrl</span> + <span className="shortcut-button spacer-right">enter</span> + {translate('shortcuts.section.issue.submit_comment')} + </li> + <li> + <span className="shortcut-button spacer-right">t</span> + {translate('shortcuts.section.issue.change_tags')} + </li> + </ul> + </div> + </div> + </div> + + <div className="modal-foot"> + <a className="js-modal-close" href="#" onClick={this.handleCloseClick}> + {translate('close')} + </a> + </div> + + </Modal> + ); + } +} diff --git a/server/sonar-web/src/main/js/app/components/nav/global/ShortcutsHelpView.js b/server/sonar-web/src/main/js/app/components/nav/global/ShortcutsHelpView.js deleted file mode 100644 index 893e4930ad6..00000000000 --- a/server/sonar-web/src/main/js/app/components/nav/global/ShortcutsHelpView.js +++ /dev/null @@ -1,26 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2017 SonarSource SA - * mailto:info 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 ModalView from '../../../../components/common/modals'; -import ShortcutsHelpTemplate from '../templates/nav-shortcuts-help.hbs'; - -export default ModalView.extend({ - className: 'modal modal-large', - template: ShortcutsHelpTemplate -}); diff --git a/server/sonar-web/src/main/js/app/components/nav/templates/nav-shortcuts-help.hbs b/server/sonar-web/src/main/js/app/components/nav/templates/nav-shortcuts-help.hbs deleted file mode 100644 index 00f5519d887..00000000000 --- a/server/sonar-web/src/main/js/app/components/nav/templates/nav-shortcuts-help.hbs +++ /dev/null @@ -1,70 +0,0 @@ -<div class="modal-head"> - <h2>{{t 'help'}}</h2> -</div> - -<div class="modal-body modal-container"> - <div class="spacer-bottom"> - <a href="http://www.sonarqube.org">Community</a> - - <a href="https://redirect.sonarsource.com/doc/home.html">Documentation</a> - - <a href="https://redirect.sonarsource.com/doc/community.html">Get Support</a> - - <a href="https://redirect.sonarsource.com/doc/plugin-library.html">Plugins</a> - - <a href="{{link '/web_api'}}">Web API</a> - - <a href="{{link '/about'}}">About</a> - </div> - - <h2 class="spacer-top spacer-bottom">{{t 'shortcuts.modal_title'}}</h2> - - <div class="columns"> - <div class="column-half"> - <div class="spacer-bottom"> - <h3 class="shortcuts-section-title">{{t 'shortcuts.section.global'}}</h3> - <ul class="shortcuts-list"> - <li><span class="shortcut-button">s</span> {{t 'shortcuts.section.global.search'}}</li> - <li><span class="shortcut-button">?</span> {{t 'shortcuts.section.global.shortcuts'}}</li> - </ul> - </div> - - <h3 class="shortcuts-section-title">{{t 'shortcuts.section.rules'}}</h3> - <ul class="shortcuts-list"> - <li><span class="shortcut-button">↑</span> <span class="shortcut-button">↓</span> {{t 'shortcuts.section.rules.navigate_between_rules'}}</li> - <li><span class="shortcut-button">→</span> {{t 'shortcuts.section.rules.open_details'}}</li> - <li><span class="shortcut-button">←</span> {{t 'shortcuts.section.rules.return_to_list'}}</li> - <li><span class="shortcut-button">a</span> {{t 'shortcuts.section.rules.activate'}}</li> - <li><span class="shortcut-button">d</span> {{t 'shortcuts.section.rules.deactivate'}}</li> - </ul> - </div> - - <div class="column-half"> - <h3 class="shortcuts-section-title">{{t 'shortcuts.section.issues'}}</h3> - <ul class="shortcuts-list"> - <li><span class="shortcut-button">↑</span> <span class="shortcut-button">↓</span> {{t 'shortcuts.section.issues.navigate_between_issues'}} - </li> - <li><span class="shortcut-button">→</span> {{t 'shortcuts.section.issues.open_details'}}</li> - <li><span class="shortcut-button">←</span> {{t 'shortcuts.section.issues.return_to_list'}}</li> - <li> - <span class="shortcut-button">alt</span> - <span class=>+</span> - <span class="shortcut-button">↑</span> - <span class="shortcut-button">↓</span> {{t 'issues.to_navigate_issue_locations'}} - </li> - <li> - <span class="shortcut-button">alt</span> - <span class=>+</span> - <span class="shortcut-button">←</span> - <span class="shortcut-button">→</span> {{t 'issues.to_switch_flows'}} - </li> - <li><span class="shortcut-button">f</span> {{t 'shortcuts.section.issue.do_transition'}}</li> - <li><span class="shortcut-button">a</span> {{t 'shortcuts.section.issue.assign'}}</li> - <li><span class="shortcut-button">m</span> {{t 'shortcuts.section.issue.assign_to_me'}}</li> - <li><span class="shortcut-button">i</span> {{t 'shortcuts.section.issue.change_severity'}}</li> - <li><span class="shortcut-button">c</span> {{t 'shortcuts.section.issue.comment'}}</li> - <li><span class="shortcut-button">ctrl</span> + <span class="shortcut-button">enter</span> {{t 'shortcuts.section.issue.submit_comment'}}</li> - <li><span class="shortcut-button">t</span> {{t 'shortcuts.section.issue.change_tags'}}</li> - </ul> - </div> - </div> -</div> - -<div class="modal-foot"> - <a class="js-modal-close" href="#">{{t 'close'}}</a> -</div>
\ No newline at end of file diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/ScannerContext.js b/server/sonar-web/src/main/js/apps/background-tasks/components/ScannerContext.js new file mode 100644 index 00000000000..f83644560cc --- /dev/null +++ b/server/sonar-web/src/main/js/apps/background-tasks/components/ScannerContext.js @@ -0,0 +1,102 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:info 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. + */ +// @flow +import React from 'react'; +import Modal from 'react-modal'; +import { getTask } from '../../../api/ce'; +import { translate } from '../../../helpers/l10n'; + +type Props = { + onClose: () => void, + task: { componentName: string, id: string, type: string } +}; + +type State = { + scannerContext: ?string +}; + +export default class ScannerContext extends React.PureComponent { + mounted: boolean; + props: Props; + state: State = { + scannerContext: null + }; + + componentDidMount() { + this.mounted = true; + this.loadScannerContext(); + } + + componentWillUnmount() { + this.mounted = false; + } + + loadScannerContext() { + getTask(this.props.task.id, ['scannerContext']).then(task => { + if (this.mounted) { + this.setState({ scannerContext: task.scannerContext }); + } + }); + } + + handleCloseClick = (event: Event) => { + event.preventDefault(); + this.props.onClose(); + }; + + render() { + const { task } = this.props; + const { scannerContext } = this.state; + + return ( + <Modal + isOpen={true} + contentLabel="scanner context" + className="modal modal-large" + overlayClassName="modal-overlay" + onRequestClose={this.props.onClose}> + + <div className="modal-head"> + <h2> + {translate('background_tasks.scanner_context')} + {': '} + {task.componentName} + {' ['} + {translate('background_task.type', task.type)} + {']'} + </h2> + </div> + + <div className="modal-body modal-container"> + {scannerContext != null + ? <pre className="js-task-scanner-context">{scannerContext}</pre> + : <i className="spinner" />} + </div> + + <div className="modal-foot"> + <a href="#" className="js-modal-close" onClick={this.handleCloseClick}> + {translate('close')} + </a> + </div> + + </Modal> + ); + } +} diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/Stacktrace.js b/server/sonar-web/src/main/js/apps/background-tasks/components/Stacktrace.js new file mode 100644 index 00000000000..d6da81e314c --- /dev/null +++ b/server/sonar-web/src/main/js/apps/background-tasks/components/Stacktrace.js @@ -0,0 +1,114 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:info 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. + */ +// @flow +import React from 'react'; +import Modal from 'react-modal'; +import { getTask } from '../../../api/ce'; +import { translate } from '../../../helpers/l10n'; + +type Props = { + onClose: () => void, + task: { componentName: string, errorMessage: string, id: string, type: string } +}; + +type State = { + loading: boolean, + stacktrace: ?string +}; + +export default class Stacktrace extends React.PureComponent { + mounted: boolean; + props: Props; + state: State = { + loading: true, + stacktrace: null + }; + + componentDidMount() { + this.mounted = true; + this.loadStacktrace(); + } + + componentWillUnmount() { + this.mounted = false; + } + + loadStacktrace() { + getTask(this.props.task.id, ['stacktrace']).then(task => { + if (this.mounted) { + this.setState({ loading: false, stacktrace: task.errorStacktrace }); + } + }); + } + + handleCloseClick = (event: Event) => { + event.preventDefault(); + this.props.onClose(); + }; + + render() { + const { task } = this.props; + const { loading, stacktrace } = this.state; + + return ( + <Modal + isOpen={true} + contentLabel="stacktrace" + className="modal modal-large" + overlayClassName="modal-overlay" + onRequestClose={this.props.onClose}> + + <div className="modal-head"> + <h2> + {translate('background_tasks.error_stacktrace')} + {': '} + {task.componentName} + {' ['} + {translate('background_task.type', task.type)} + {']'} + </h2> + </div> + + <div className="modal-body modal-container"> + {loading + ? <i className="spinner" /> + : stacktrace + ? <div> + <h4 className="spacer-bottom"> + {translate('background_tasks.error_stacktrace')} + </h4> + <pre className="js-task-stacktrace">{stacktrace}</pre> + </div> + : <div> + <h4 className="spacer-bottom">{translate('background_tasks.error_message')}</h4> + <pre className="js-task-error-message">{task.errorMessage}</pre> + </div>} + </div> + + <div className="modal-foot"> + <a href="#" className="js-modal-close" onClick={this.handleCloseClick}> + {translate('close')} + </a> + </div> + + </Modal> + ); + } +} diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/TaskActions.js b/server/sonar-web/src/main/js/apps/background-tasks/components/TaskActions.js index 1acde211d5f..03b8be8455d 100644 --- a/server/sonar-web/src/main/js/apps/background-tasks/components/TaskActions.js +++ b/server/sonar-web/src/main/js/apps/background-tasks/components/TaskActions.js @@ -18,12 +18,17 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import React from 'react'; -import ScannerContextView from '../views/ScannerContextView'; -import StacktraceView from '../views/StacktraceView'; +import ScannerContext from './ScannerContext'; +import Stacktrace from './Stacktrace'; import { STATUSES } from './../constants'; import { translate, translateWithParameters } from '../../../helpers/l10n'; export default class TaskActions extends React.PureComponent { + state = { + scannerContextOpen: false, + stacktraceOpen: false + }; + handleFilterClick(e) { e.preventDefault(); this.props.onFilterTask(this.props.task); @@ -36,14 +41,18 @@ export default class TaskActions extends React.PureComponent { handleShowScannerContextClick(e) { e.preventDefault(); - new ScannerContextView({ task: this.props.task }).render(); + this.setState({ scannerContextOpen: true }); } + closeScannerContext = () => this.setState({ scannerContextOpen: false }); + handleShowStacktraceClick(e) { e.preventDefault(); - new StacktraceView({ task: this.props.task }).render(); + this.setState({ stacktraceOpen: true }); } + closeStacktrace = () => this.setState({ stacktraceOpen: false }); + render() { const { component, task } = this.props; @@ -102,6 +111,11 @@ export default class TaskActions extends React.PureComponent { </li>} </ul> </div> + + {this.state.scannerContextOpen && + <ScannerContext onClose={this.closeScannerContext} task={task} />} + + {this.state.stacktraceOpen && <Stacktrace onClose={this.closeStacktrace} task={task} />} </td> ); } diff --git a/server/sonar-web/src/main/js/apps/background-tasks/views/ScannerContextView.hbs b/server/sonar-web/src/main/js/apps/background-tasks/views/ScannerContextView.hbs deleted file mode 100644 index d1de3da84c7..00000000000 --- a/server/sonar-web/src/main/js/apps/background-tasks/views/ScannerContextView.hbs +++ /dev/null @@ -1,17 +0,0 @@ -<form id="deactivate-user-form" autocomplete="off"> - <div class="modal-head"> - <h2>{{t 'background_tasks.scanner_context'}}: {{task.componentName}} [{{t 'background_task.type' task.type}}]</h2> - </div> - <div class="modal-body modal-container"> - <div class="js-modal-messages"></div> - - {{#if scannerContext}} - <pre class="js-task-scanner-context">{{scannerContext}}</pre> - {{else}} - <i class="spinner"></i> - {{/if}} - </div> - <div class="modal-foot"> - <a href="#" class="js-modal-close">{{t 'close'}}</a> - </div> -</form> diff --git a/server/sonar-web/src/main/js/apps/background-tasks/views/ScannerContextView.js b/server/sonar-web/src/main/js/apps/background-tasks/views/ScannerContextView.js deleted file mode 100644 index ef35cf57125..00000000000 --- a/server/sonar-web/src/main/js/apps/background-tasks/views/ScannerContextView.js +++ /dev/null @@ -1,46 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2017 SonarSource SA - * mailto:info 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 Modal from '../../../components/common/modals'; -import Template from './ScannerContextView.hbs'; -import { getTask } from '../../../api/ce'; - -export default Modal.extend({ - template: Template, - className: 'modal modal-large', - - initialize() { - this.scannerContext = null; - this.loadScannerContext(); - }, - - loadScannerContext() { - getTask(this.options.task.id, ['scannerContext']).then(task => { - this.scannerContext = task.scannerContext; - this.render(); - }); - }, - - serializeData() { - return { - task: this.options.task, - scannerContext: this.scannerContext - }; - } -}); diff --git a/server/sonar-web/src/main/js/apps/background-tasks/views/StacktraceView.hbs b/server/sonar-web/src/main/js/apps/background-tasks/views/StacktraceView.hbs deleted file mode 100644 index 21ce604acb2..00000000000 --- a/server/sonar-web/src/main/js/apps/background-tasks/views/StacktraceView.hbs +++ /dev/null @@ -1,23 +0,0 @@ -<form id="deactivate-user-form" autocomplete="off"> - <div class="modal-head"> - <h2>{{t 'background_tasks.error_stacktrace'}}: {{task.componentName}} [{{t 'background_task.type' task.type}}]</h2> - </div> - <div class="modal-body modal-container"> - <div class="js-modal-messages"></div> - - {{#if loaded}} - {{#if stacktrace}} - <h4 class="spacer-bottom">{{t 'background_tasks.error_stacktrace'}}</h4> - <pre class="js-task-stacktrace">{{stacktrace}}</pre> - {{else}} - <h4 class="spacer-bottom">{{t 'background_tasks.error_message'}}</h4> - <pre class="js-task-error-message">{{task.errorMessage}}</pre> - {{/if}} - {{else}} - <i class="spinner"></i> - {{/if}} - </div> - <div class="modal-foot"> - <a href="#" class="js-modal-close">{{t 'close'}}</a> - </div> -</form> diff --git a/server/sonar-web/src/main/js/apps/background-tasks/views/StacktraceView.js b/server/sonar-web/src/main/js/apps/background-tasks/views/StacktraceView.js deleted file mode 100644 index 866d9a487eb..00000000000 --- a/server/sonar-web/src/main/js/apps/background-tasks/views/StacktraceView.js +++ /dev/null @@ -1,49 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2017 SonarSource SA - * mailto:info 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 Modal from '../../../components/common/modals'; -import Template from './StacktraceView.hbs'; -import { getTask } from '../../../api/ce'; - -export default Modal.extend({ - template: Template, - className: 'modal modal-large', - - initialize() { - this.loaded = false; - this.stacktrace = null; - this.loadStacktrace(); - }, - - loadStacktrace() { - getTask(this.options.task.id, ['stacktrace']).then(task => { - this.loaded = true; - this.stacktrace = task.errorStacktrace; - this.render(); - }); - }, - - serializeData() { - return { - task: this.options.task, - stacktrace: this.stacktrace, - loaded: this.loaded - }; - } -}); diff --git a/server/sonar-web/src/main/js/apps/project-admin/deletion/ConfirmationModal.js b/server/sonar-web/src/main/js/apps/project-admin/deletion/ConfirmationModal.js deleted file mode 100644 index 273850ea38b..00000000000 --- a/server/sonar-web/src/main/js/apps/project-admin/deletion/ConfirmationModal.js +++ /dev/null @@ -1,48 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2017 SonarSource SA - * mailto:info 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 ModalForm from '../../../components/common/modal-form'; -import Template from './ConfirmationModalTemplate.hbs'; -import { deleteProject } from '../../../api/components'; - -export default ModalForm.extend({ - template: Template, - - onFormSubmit() { - ModalForm.prototype.onFormSubmit.apply(this, arguments); - this.disableForm(); - this.showSpinner(); - - deleteProject(this.options.project.key) - .then(() => { - this.trigger('done'); - }) - .catch(function(e) { - e.response.json().then(r => { - this.hideSpinner(); - this.showErrors(r.errors, r.warnings); - this.enableForm(); - }); - }); - }, - - serializeData() { - return { project: this.options.project }; - } -}); diff --git a/server/sonar-web/src/main/js/apps/project-admin/deletion/ConfirmationModalTemplate.hbs b/server/sonar-web/src/main/js/apps/project-admin/deletion/ConfirmationModalTemplate.hbs deleted file mode 100644 index 749e408d5bc..00000000000 --- a/server/sonar-web/src/main/js/apps/project-admin/deletion/ConfirmationModalTemplate.hbs +++ /dev/null @@ -1,14 +0,0 @@ -<form id="deactivate-user-form" autocomplete="off"> - <div class="modal-head"> - <h2>{{t 'qualifiers.delete.TRK'}}</h2> - </div> - <div class="modal-body"> - <div class="js-modal-messages"></div> - {{tp 'project_deletion.delete_resource_confirmation' project.name}} - </div> - <div class="modal-foot"> - <i class="js-modal-spinner spinner spacer-right hidden"></i> - <button id="delete-project-confirm" class="button-red">{{t 'delete'}}</button> - <a href="#" class="js-modal-close">{{t 'cancel'}}</a> - </div> -</form> diff --git a/server/sonar-web/src/main/js/apps/project-admin/deletion/Form.js b/server/sonar-web/src/main/js/apps/project-admin/deletion/Form.js index 1a848accaee..ac8ca55598b 100644 --- a/server/sonar-web/src/main/js/apps/project-admin/deletion/Form.js +++ b/server/sonar-web/src/main/js/apps/project-admin/deletion/Form.js @@ -18,30 +18,97 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import React from 'react'; -import ConfirmationModal from './ConfirmationModal'; -import { translate } from '../../../helpers/l10n'; +import Modal from 'react-modal'; +import { deleteProject } from '../../../api/components'; +import { translate, translateWithParameters } from '../../../helpers/l10n'; export default class Form extends React.PureComponent { static propTypes = { component: React.PropTypes.object.isRequired }; - handleDelete(e) { - e.preventDefault(); - new ConfirmationModal({ project: this.props.component }) - .on('done', () => { - window.location = window.baseUrl + '/'; - }) - .render(); + static contextTypes = { + router: React.PropTypes.object + }; + + state = { loading: false, modalOpen: false }; + + componentDidMount() { + this.mounted = true; + } + + componentWillUnmount() { + this.mounted = false; } + handleDeleteClick = event => { + event.preventDefault(); + this.setState({ modalOpen: true }); + }; + + closeModal = () => this.setState({ modalOpen: false }); + + stopLoading = () => { + if (this.mounted) { + this.setState({ loading: false }); + } + }; + + handleSubmit = event => { + event.preventDefault(); + this.setState({ loading: true }); + deleteProject(this.props.component.key) + .then(() => this.context.router.replace('/')) + .catch(this.stopLoading); + }; + + handleCloseClick = (event: Event) => { + event.preventDefault(); + this.closeModal(); + }; + render() { + const { component } = this.props; + return ( - <form onSubmit={this.handleDelete.bind(this)}> - <button id="delete-project" className="button-red"> + <div> + <button id="delete-project" className="button-red" onClick={this.handleDeleteClick}> {translate('delete')} </button> - </form> + + {this.state.modalOpen && + <Modal + isOpen={true} + contentLabel="project deletion" + className="modal" + overlayClassName="modal-overlay" + onRequestClose={this.closeModal}> + <form onSubmit={this.handleSubmit}> + <div className="modal-head"> + <h2>{translate('qualifiers.delete.TRK')}</h2> + </div> + <div className="modal-body"> + <div className="js-modal-messages" /> + {translateWithParameters( + 'project_deletion.delete_resource_confirmation', + component.name + )} + </div> + <div className="modal-foot"> + {this.state.loading && <i className="js-modal-spinner spinner spacer-right" />} + <button + id="delete-project-confirm" + className="button-red" + disabled={this.state.loading}> + {translate('delete')} + </button> + <a href="#" className="js-modal-close" onClick={this.handleCloseClick}> + {translate('cancel')} + </a> + </div> + </form> + </Modal>} + </div> ); } } diff --git a/server/sonar-web/src/main/js/apps/settings/licenses/LicenseChangeForm.js b/server/sonar-web/src/main/js/apps/settings/licenses/LicenseChangeForm.js index e3e443e51b6..ff068c49d98 100644 --- a/server/sonar-web/src/main/js/apps/settings/licenses/LicenseChangeForm.js +++ b/server/sonar-web/src/main/js/apps/settings/licenses/LicenseChangeForm.js @@ -18,8 +18,8 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import React from 'react'; -import LicenseValueView from './LicenseValueView'; -import { translate } from '../../../helpers/l10n'; +import Modal from 'react-modal'; +import { translate, translateWithParameters } from '../../../helpers/l10n'; export default class LicenseChangeForm extends React.PureComponent { static propTypes = { @@ -27,23 +27,80 @@ export default class LicenseChangeForm extends React.PureComponent { onChange: React.PropTypes.func.isRequired }; + state = { + loading: false, + modalOpen: false + }; + onClick(e) { e.preventDefault(); e.target.blur(); + this.setState({ modalOpen: true }); + } - const { license, onChange } = this.props; + closeModal = () => this.setState({ modalOpen: false }); - new LicenseValueView({ - productName: license.name || license.key, - value: license.value, - onChange - }).render(); - } + handleSubmit = event => { + event.preventDefault(); + if (this.textarea) { + const { value } = this.textarea; + this.setState({ loading: true }); + this.props + .onChange(value) + .then( + () => this.setState({ loading: false, modalOpen: false }), + () => this.setState({ loading: false }) + ); + } + }; + + handleCancelClick = event => { + event.preventDefault(); + this.closeModal(); + }; render() { + const { license } = this.props; + const productName = license.name || license.key; + return ( <button className="js-change" onClick={e => this.onClick(e)}> {translate('update_verb')} + + {this.state.modalOpen && + <Modal + isOpen={true} + contentLabel="license update" + className="modal" + overlayClassName="modal-overlay" + onRequestClose={this.closeModal}> + <form onSubmit={this.handleSubmit}> + <div className="modal-head"> + <h2>{translateWithParameters('licenses.update_license_for_x', productName)}</h2> + </div> + <div className="modal-body"> + <label htmlFor="license-input">{translate('licenses.license_input_label')}</label> + <textarea + autoFocus={true} + className="width-100 spacer-top" + ref={node => (this.textarea = node)} + rows="7" + id="license-input" + defaultValue={license.value} + /> + <div className="spacer-top note">{translate('licenses.license_input_note')}</div> + </div> + <div className="modal-foot"> + {this.state.loading && <i className="js-modal-spinner spinner spacer-right" />} + <button className="js-modal-submit" disabled={this.state.loading}> + {translate('save')} + </button> + <a href="#" className="js-modal-close" onClick={this.handleCancelClick}> + {translate('cancel')} + </a> + </div> + </form> + </Modal>} </button> ); } diff --git a/server/sonar-web/src/main/js/apps/settings/licenses/LicenseRow.js b/server/sonar-web/src/main/js/apps/settings/licenses/LicenseRow.js index 94f12ae7720..9a5a332ac20 100644 --- a/server/sonar-web/src/main/js/apps/settings/licenses/LicenseRow.js +++ b/server/sonar-web/src/main/js/apps/settings/licenses/LicenseRow.js @@ -28,11 +28,7 @@ export default class LicenseRow extends React.PureComponent { setLicense: React.PropTypes.func.isRequired }; - handleSet(value) { - return this.props.setLicense(this.props.license.key, value).catch(() => { - /* do nothing */ - }); - } + handleSet = value => this.props.setLicense(this.props.license.key, value); render() { const { license } = this.props; @@ -59,7 +55,7 @@ export default class LicenseRow extends React.PureComponent { </div> </td> <td className="text-right"> - <LicenseChangeForm license={license} onChange={value => this.handleSet(value)} /> + <LicenseChangeForm license={license} onChange={this.handleSet} /> </td> </tr> ); diff --git a/server/sonar-web/src/main/js/apps/settings/licenses/LicenseValueView.hbs b/server/sonar-web/src/main/js/apps/settings/licenses/LicenseValueView.hbs deleted file mode 100644 index c4aab7ea6f7..00000000000 --- a/server/sonar-web/src/main/js/apps/settings/licenses/LicenseValueView.hbs +++ /dev/null @@ -1,16 +0,0 @@ -<form> - <div class="modal-head"> - <h2>{{tp 'licenses.update_license_for_x' productName}}</h2> - </div> - <div class="modal-body"> - <div class="js-modal-messages"></div> - <label for="license-input">{{t 'licenses.license_input_label'}}</label> - <textarea class="width-100 spacer-top" rows="7" id="license-input">{{value}}</textarea> - <div class="spacer-top note">{{t 'licenses.license_input_note'}}</div> - </div> - <div class="modal-foot"> - <i class="js-modal-spinner spinner spacer-right hidden"></i> - <button class="js-modal-submit">{{t 'save'}}</button> - <a href="#" class="js-modal-close">{{t 'cancel'}}</a> - </div> -</form> diff --git a/server/sonar-web/src/main/js/apps/settings/licenses/LicenseValueView.js b/server/sonar-web/src/main/js/apps/settings/licenses/LicenseValueView.js deleted file mode 100644 index 22b4e3a155b..00000000000 --- a/server/sonar-web/src/main/js/apps/settings/licenses/LicenseValueView.js +++ /dev/null @@ -1,41 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2017 SonarSource SA - * mailto:info 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 ModalForm from '../../../components/common/modal-form'; -import Template from './LicenseValueView.hbs'; - -export default ModalForm.extend({ - template: Template, - - onFormSubmit() { - ModalForm.prototype.onFormSubmit.apply(this, arguments); - this.disableForm(); - this.showSpinner(); - - const value = this.$('textarea').val(); - this.options.onChange(value).then(() => this.destroy()); - }, - - serializeData() { - return { - productName: this.options.productName, - value: this.options.value - }; - } -}); diff --git a/server/sonar-web/src/main/js/apps/settings/store/licenses/actions.js b/server/sonar-web/src/main/js/apps/settings/store/licenses/actions.js index c24cae2d6d2..9ef043c73c7 100644 --- a/server/sonar-web/src/main/js/apps/settings/store/licenses/actions.js +++ b/server/sonar-web/src/main/js/apps/settings/store/licenses/actions.js @@ -56,7 +56,7 @@ export const setLicense = (key, value) => dispatch => { const request = value ? licenses.setLicense(key, value) : licenses.resetLicense(key); return request - .then(() => { + .then(() => licenses.getLicenses().then(licenses => { dispatch(receiveLicenses(licenses)); if (isLicenseFromListInvalid(licenses, key)) { @@ -64,7 +64,7 @@ export const setLicense = (key, value) => dispatch => { } else { dispatch(addGlobalSuccessMessage(translate('licenses.success_message'))); } - }); - }) + }) + ) .catch(handleError(dispatch)); }; |