diff options
12 files changed, 170 insertions, 60 deletions
diff --git a/server/sonar-web/src/main/js/api/system.js b/server/sonar-web/src/main/js/api/system.js index 59ef364d9ee..226f8724ef8 100644 --- a/server/sonar-web/src/main/js/api/system.js +++ b/server/sonar-web/src/main/js/api/system.js @@ -1,6 +1,13 @@ +import { getJSON } from '../helpers/request.js'; import $ from 'jquery'; +// TODO migrate to fetch() export function setLogLevel (level) { let url = baseUrl + '/api/system/change_log_level'; return $.post(url, { level }); } + +export function getSystemInfo () { + let url = baseUrl + '/api/system/info'; + return getJSON(url); +} diff --git a/server/sonar-web/src/main/js/apps/system/app.js b/server/sonar-web/src/main/js/apps/system/app.js index 391c313d9ec..b4b9a61c4a6 100644 --- a/server/sonar-web/src/main/js/apps/system/app.js +++ b/server/sonar-web/src/main/js/apps/system/app.js @@ -1,57 +1,9 @@ -import $ from 'jquery'; -import {setLogLevel} from '../../api/system'; +import React from 'react'; +import Main from './main'; -const LOG_LEVELS = ['INFO', 'DEBUG', 'TRACE']; - -window.sonarqube.appStarted.then(() => { - let cell = $('#sonarqube-logs-level').find('td:last-child'); - if (cell.length) { - let currentValue = cell.text().trim(); - cell.empty(); - - let select = $('<select>'); - cell.append(select); - - LOG_LEVELS.forEach(logLevel => { - let option = $('<option>'); - option.prop('value', logLevel); - option.text(logLevel); - option.prop('selected', logLevel === currentValue); - select.append(option); - }); - - let format = (state) => { - let className = state.id !== 'INFO' ? 'text-danger' : ''; - return `<span class="${className}">${state.id}</span>`; - }; - - let warning = $('<div>') - .addClass('spacer-top text-danger') - .text(window.t('system.log_level.warning')); - - let placeWarning = function () { - if (select.val() === 'INFO') { - warning.detach(); - } else { - warning.insertAfter(select); - } - }; - - placeWarning(); - - $(select) - .select2({ - width: '120px', - minimumResultsForSearch: 999, - formatResult: format, - formatSelection: format - }) - .on('change', () => { - let newValue = select.val(); - setLogLevel(newValue); - placeWarning(); - }); - } +window.sonarqube.appStarted.then(options => { + var el = document.querySelector(options.el); + React.render(<Main/>, el); }); diff --git a/server/sonar-web/src/main/js/apps/system/item-boolean.js b/server/sonar-web/src/main/js/apps/system/item-boolean.js new file mode 100644 index 00000000000..58f6dd7d9b8 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/system/item-boolean.js @@ -0,0 +1,11 @@ +import React from 'react'; + +export default React.createClass({ + render() { + if (this.props.value) { + return <i className="icon-check"/>; + } else { + return <i className="icon-delete"/>; + } + } +}); diff --git a/server/sonar-web/src/main/js/apps/system/item-log-level.js b/server/sonar-web/src/main/js/apps/system/item-log-level.js new file mode 100644 index 00000000000..2f888d7100b --- /dev/null +++ b/server/sonar-web/src/main/js/apps/system/item-log-level.js @@ -0,0 +1,20 @@ +import React from 'react'; +import { setLogLevel } from '../../api/system'; + +const LOG_LEVELS = ['INFO', 'DEBUG', 'TRACE']; + +export default React.createClass({ + onChange() { + let newValue = React.findDOMNode(this.refs.select).value; + setLogLevel(newValue); + }, + + render() { + let options = LOG_LEVELS.map(level => { + return <option key={level} value={level}>{level}</option>; + }); + return <select ref="select" + onChange={this.onChange} + defaultValue={this.props.value}>{options}</select>; + } +}); diff --git a/server/sonar-web/src/main/js/apps/system/item-object.js b/server/sonar-web/src/main/js/apps/system/item-object.js new file mode 100644 index 00000000000..15583def4bb --- /dev/null +++ b/server/sonar-web/src/main/js/apps/system/item-object.js @@ -0,0 +1,14 @@ +import React from 'react'; +import ItemValue from './item-value'; + +export default React.createClass({ + render() { + let rows = Object.keys(this.props.value).map(key => { + return <tr key={key}> + <td className="thin nowrap">{key}</td> + <td><ItemValue value={this.props.value[key]}/></td> + </tr>; + }); + return <table className="data">{rows}</table>; + } +}); diff --git a/server/sonar-web/src/main/js/apps/system/item-value.js b/server/sonar-web/src/main/js/apps/system/item-value.js new file mode 100644 index 00000000000..a4d2da93154 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/system/item-value.js @@ -0,0 +1,26 @@ +import React from 'react'; +import ItemBoolean from './item-boolean'; +import ItemObject from './item-object'; +import ItemLogLevel from './item-log-level'; + +export default React.createClass({ + render() { + if (this.props.name === 'Logs Level') { + return <ItemLogLevel value={this.props.value}/>; + } + + let rawValue = this.props.value, + formattedValue; + switch (typeof this.props.value) { + case 'boolean': + formattedValue = <ItemBoolean value={rawValue}/>; + break; + case 'object': + formattedValue = <ItemObject value={rawValue}/>; + break; + default: + formattedValue = <code>{rawValue}</code>; + } + return formattedValue; + } +}); diff --git a/server/sonar-web/src/main/js/apps/system/main.js b/server/sonar-web/src/main/js/apps/system/main.js new file mode 100644 index 00000000000..fe69d83a6c0 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/system/main.js @@ -0,0 +1,55 @@ +import _ from 'underscore'; +import React from 'react'; +import { getSystemInfo } from '../../api/system'; +import Section from './section'; + +const SECTIONS_ORDER = ['SonarQube', 'Database', 'Plugins', 'System', 'ElasticSearch', 'JvmProperties', + 'ComputeEngineQueue']; + +export default React.createClass({ + componentDidMount() { + getSystemInfo().then(info => this.setState({ sections: this.parseSections(info) })); + }, + + parseSections (data) { + let sections = Object.keys(data).map(section => { + return { name: section, items: this.parseItems(data[section]) }; + }); + return this.orderSections(sections); + }, + + orderSections (sections) { + return _.sortBy(sections, section => SECTIONS_ORDER.indexOf(section.name)); + }, + + parseItems (data) { + let items = Object.keys(data).map(item => { + return { name: item, value: data[item] }; + }); + return this.orderItems(items); + }, + + orderItems (items) { + return _.sortBy(items, 'name'); + }, + + render() { + let sections = null; + if (this.state && this.state.sections) { + sections = this.state.sections.map(section => { + return <Section key={section.name} section={section.name} items={section.items}/>; + }); + } + + return <div className="page"> + <header className="page-header"> + <h1 className="page-title">{window.t('system_info.page')}</h1> + <div className="page-actions"> + <a className="spacer-right" href={window.baseUrl + '/api/system/logs'} id="logs-link">Logs</a> + <a href={window.baseUrl + '/api/system/info'} id="download-link">Download</a> + </div> + </header> + {sections} + </div>; + } +}); diff --git a/server/sonar-web/src/main/js/apps/system/section.js b/server/sonar-web/src/main/js/apps/system/section.js new file mode 100644 index 00000000000..63b05a45520 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/system/section.js @@ -0,0 +1,22 @@ +import React from 'react'; +import ItemValue from './item-value'; + +export default React.createClass({ + render() { + let items = this.props.items.map(item => { + return <tr key={item.name}> + <td className="thin"> + <div style={{ width: '25vw', overflow: 'hidden', textOverflow: 'ellipsis' }}>{item.name}</div> + </td> + <td style={{ wordBreak: 'break-all' }}><ItemValue name={item.name} value={item.value}/></td> + </tr>; + }); + + return <div className="big-spacer-bottom"> + <h3 className="spacer-bottom">{this.props.section}</h3> + <table className="data zebra" id={this.props.section}> + <tbody>{items}</tbody> + </table> + </div>; + } +}); diff --git a/server/sonar-web/src/main/js/main/nav/settings/settings-nav.js b/server/sonar-web/src/main/js/main/nav/settings/settings-nav.js index 13fae339cda..5f186c494b0 100644 --- a/server/sonar-web/src/main/js/main/nav/settings/settings-nav.js +++ b/server/sonar-web/src/main/js/main/nav/settings/settings-nav.js @@ -57,7 +57,8 @@ export default React.createClass({ </a> <ul className="dropdown-menu"> {this.renderLink('/updatecenter', window.t('update_center.page'))} - {this.renderLink('/system', window.t('system_info.page'))} + {this.renderLink('/system/index', window.t('system_info.page'))} + {this.renderNewLink('/system/new', window.t('system_info.page'))} </ul> </li> </ul> diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/system_controller.rb b/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/system_controller.rb index 929d1a6fcc9..0234fe68808 100644 --- a/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/system_controller.rb +++ b/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/system_controller.rb @@ -32,4 +32,8 @@ class SystemController < ApplicationController ] end + def new + + end + end diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/system/index.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/system/index.html.erb index f80abacd882..51e1c98c663 100644 --- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/system/index.html.erb +++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/system/index.html.erb @@ -14,9 +14,3 @@ <% end %> </div> - - -<% content_for :extra_script do %> - <script src="<%= ApplicationController.root_context -%>/js/bundles/system.js?v=<%= sonar_version -%>"></script> -<% end %> - diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/system/new.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/system/new.html.erb new file mode 100644 index 00000000000..8e9f0861c90 --- /dev/null +++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/system/new.html.erb @@ -0,0 +1,4 @@ +<% content_for :extra_script do %> + <script src="<%= ApplicationController.root_context -%>/js/bundles/system.js?v=<%= sonar_version -%>"></script> +<% end %> + |