From adb4265900d35345f8d900dca47cc6c2f490cb21 Mon Sep 17 00:00:00 2001 From: Stas Vilchik Date: Tue, 28 Mar 2017 16:42:37 +0200 Subject: [PATCH] SONAR-9005 support rules for organizations --- .../main/js/apps/about/components/AboutApp.js | 10 ++- .../components/AboutAppForSonarQubeDotCom.js | 8 ++- .../AboutRulesForSonarQubeDotCom.js | 31 ++++++++-- .../apps/about/components/AboutStandards.js | 26 +++++--- .../components/CodingRulesAppContainer.js | 16 ++++- .../src/main/js/apps/coding-rules/init.js | 18 ++++-- .../js/apps/coding-rules/rule-details-view.js | 3 +- .../coding-rules/rule/custom-rule-view.js | 2 +- .../coding-rules/rule/custom-rules-view.js | 2 +- .../rule/rule-description-view.js | 6 +- .../coding-rules/rule/rule-issues-view.js | 22 ++++--- .../apps/coding-rules/rule/rule-meta-view.js | 8 ++- .../coding-rules/rule/rule-parameters-view.js | 10 --- .../coding-rules/rule/rule-profile-view.js | 19 +++++- .../rule/coding-rules-custom-rule.hbs | 2 +- .../rule/coding-rules-custom-rules.hbs | 2 +- .../rule/coding-rules-rule-description.hbs | 6 +- .../rule/coding-rules-rule-issues.hbs | 4 +- .../templates/rule/coding-rules-rule-meta.hbs | 4 +- .../rule/coding-rules-rule-profile.hbs | 4 +- .../components/OrganizationRules.js} | 12 +++- .../navigation/OrganizationNavigation.js | 5 ++ .../OrganizationNavigation-test.js.snap | 27 ++++++++ .../src/main/js/apps/organizations/routes.js | 5 ++ .../quality-profiles/changelog/Changelog.js | 5 +- .../changelog/ChangelogContainer.js | 2 +- .../compare/ComparisonContainer.js | 1 + .../compare/ComparisonResults.js | 5 +- .../components/ProfileActions.js | 12 ++-- .../quality-profiles/details/ProfileRules.js | 62 ++++++++++++------- .../apps/quality-profiles/home/Evolution.js | 2 +- .../home/EvolutionDeprecated.js | 7 ++- .../quality-profiles/home/EvolutionRules.js | 19 +++--- .../quality-profiles/home/ProfilesListRow.js | 26 ++++---- .../main/js/components/issue/issue-view.js | 2 +- .../src/main/js/components/workspace/main.js | 3 +- .../workspace/templates/workspace-rule.hbs | 2 +- .../components/workspace/views/rule-view.js | 10 ++- .../js/helpers/handlebars/rulePermalink.js | 22 ------- server/sonar-web/src/main/js/helpers/urls.js | 12 ++-- 40 files changed, 288 insertions(+), 156 deletions(-) rename server/sonar-web/src/main/js/{helpers/handlebars/profileUrl.js => apps/organizations/components/OrganizationRules.js} (75%) delete mode 100644 server/sonar-web/src/main/js/helpers/handlebars/rulePermalink.js diff --git a/server/sonar-web/src/main/js/apps/about/components/AboutApp.js b/server/sonar-web/src/main/js/apps/about/components/AboutApp.js index a0f80af5dca..d76e6e3b4ba 100644 --- a/server/sonar-web/src/main/js/apps/about/components/AboutApp.js +++ b/server/sonar-web/src/main/js/apps/about/components/AboutApp.js @@ -33,7 +33,7 @@ import AboutStandards from './AboutStandards'; import AboutScanners from './AboutScanners'; import { searchProjects } from '../../../api/components'; import { getFacet } from '../../../api/issues'; -import { getCurrentUser, getSettingValue } from '../../../store/rootReducer'; +import { getAppState, getCurrentUser, getSettingValue } from '../../../store/rootReducer'; import { translate } from '../../../helpers/l10n'; import { fetchAboutPageSettings } from '../actions'; import AboutAppForSonarQubeDotCom from './AboutAppForSonarQubeDotCom'; @@ -53,6 +53,10 @@ class AboutApp extends React.Component { mounted: boolean; props: { + appState: { + defaultOrganization: string, + organizationsEnabled: boolean + }, currentUser: { isLoggedIn: boolean }, customText?: string, fetchAboutPageSettings: () => Promise<*>, @@ -115,6 +119,7 @@ class AboutApp extends React.Component { if (sonarqubeDotCom && sonarqubeDotCom.value === 'true') { return (
- +
@@ -192,6 +197,7 @@ class AboutApp extends React.Component { } const mapStateToProps = state => ({ + appState: getAppState(state), currentUser: getCurrentUser(state), customText: getSettingValue(state, 'sonar.lf.aboutText'), sonarqubeDotCom: getSettingValue(state, 'sonar.lf.sonarqube.com.enabled') diff --git a/server/sonar-web/src/main/js/apps/about/components/AboutAppForSonarQubeDotCom.js b/server/sonar-web/src/main/js/apps/about/components/AboutAppForSonarQubeDotCom.js index 6d43c38a8f7..addce0a2496 100644 --- a/server/sonar-web/src/main/js/apps/about/components/AboutAppForSonarQubeDotCom.js +++ b/server/sonar-web/src/main/js/apps/about/components/AboutAppForSonarQubeDotCom.js @@ -34,6 +34,10 @@ import '../sonarqube-dot-com-styles.css'; export default class AboutAppForSonarQubeDotCom extends React.Component { props: { + appState: { + defaultOrganization: string, + organizationsEnabled: boolean + }, bugs: number, codeSmells: number, currentUser: { isLoggedIn: boolean }, @@ -76,7 +80,7 @@ export default class AboutAppForSonarQubeDotCom extends React.Component { - +
{customText != null && @@ -102,7 +106,7 @@ export default class AboutAppForSonarQubeDotCom extends React.Component {
- +
diff --git a/server/sonar-web/src/main/js/apps/about/components/AboutRulesForSonarQubeDotCom.js b/server/sonar-web/src/main/js/apps/about/components/AboutRulesForSonarQubeDotCom.js index 140ab0444d0..59fef3421c9 100644 --- a/server/sonar-web/src/main/js/apps/about/components/AboutRulesForSonarQubeDotCom.js +++ b/server/sonar-web/src/main/js/apps/about/components/AboutRulesForSonarQubeDotCom.js @@ -23,11 +23,20 @@ import { Link } from 'react-router'; import { getRulesUrl } from '../../../helpers/urls'; export default class AboutRulesForSonarQubeDotCom extends React.Component { + props: { + appState: { + defaultOrganization: string, + organizationsEnabled: boolean + } + }; + render() { + const organization = this.props.appState.defaultOrganization; + return (
- + +3,000 rules @@ -40,17 +49,27 @@ export default class AboutRulesForSonarQubeDotCom extends React.Component { - + JavaScript - + Java - + C/C++ - C# - And More + + C# + + And More
); diff --git a/server/sonar-web/src/main/js/apps/about/components/AboutStandards.js b/server/sonar-web/src/main/js/apps/about/components/AboutStandards.js index 295fabe38df..a2991500f31 100644 --- a/server/sonar-web/src/main/js/apps/about/components/AboutStandards.js +++ b/server/sonar-web/src/main/js/apps/about/components/AboutStandards.js @@ -27,9 +27,21 @@ const link = 'https://redirect.sonarsource.com/doc/rules.html'; const owaspTags = 'owasp-a1,owasp-a2,owasp-a3,owasp-a4,owasp-a5,' + 'owasp-a6,owasp-a7,owasp-a8,owasp-a9,owasp-a10'; +const sans25Tags = 'sans-top25-porous,sans-top25-risky,sans-top25-insecure'; export default class AboutStandards extends React.Component { + props: { + appState: { + defaultOrganization: string, + organizationsEnabled: boolean + } + }; + render() { + const organization = this.props.appState.organizationsEnabled + ? this.props.appState.defaultOrganization + : null; + return (

{translate('about_page.standards')}

@@ -39,34 +51,34 @@ export default class AboutStandards extends React.Component {
  • - + MISRA
  • - + CERT
  • - + CWE
  • - + OWASP Top 10
  • SANS Top 25 diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/CodingRulesAppContainer.js b/server/sonar-web/src/main/js/apps/coding-rules/components/CodingRulesAppContainer.js index 91b561597b9..c7bb6c70ed3 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/components/CodingRulesAppContainer.js +++ b/server/sonar-web/src/main/js/apps/coding-rules/components/CodingRulesAppContainer.js @@ -17,16 +17,26 @@ * 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 init from '../init'; -export default class CodingRulesAppContainer extends React.Component { +export default class CodingRulesAppContainer extends React.PureComponent { + stop: ?() => void; + props: { + params: { + organizationKey?: string + } + }; + componentDidMount() { - this.stop = init(this.refs.container); + this.stop = init(this.refs.container, this.props.params.organizationKey); } componentWillUnmount() { - this.stop(); + if (this.stop) { + this.stop(); + } } render() { diff --git a/server/sonar-web/src/main/js/apps/coding-rules/init.js b/server/sonar-web/src/main/js/apps/coding-rules/init.js index f25d2ad0c10..1a92814b70c 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/init.js +++ b/server/sonar-web/src/main/js/apps/coding-rules/init.js @@ -17,6 +17,7 @@ * 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 $ from 'jquery'; import { sortBy } from 'lodash'; import Backbone from 'backbone'; @@ -34,10 +35,14 @@ import FiltersView from './filters-view'; const App = new Marionette.Application(); -App.on('start', function(el) { - $.get(window.baseUrl + '/api/rules/app') +App.on('start', function(options: { el: HTMLElement, organization: ?string }) { + const data = options.organization ? { organization: options.organization } : {}; + $.get(window.baseUrl + '/api/rules/app', data) .done(r => { + App.canCreateCustomRule = r.canCreateCustomRule; + App.canCustomizeRule = r.canCustomizeRule; App.canWrite = r.canWrite; + App.organization = options.organization; App.qualityProfiles = sortBy(r.qualityprofiles, ['name', 'lang']); App.languages = { ...r.languages, none: 'None' }; App.qualityProfiles.forEach(profile => { @@ -47,7 +52,7 @@ App.on('start', function(el) { App.statuses = r.statuses; }) .done(() => { - this.layout = new Layout({ el }); + this.layout = new Layout({ el: options.el }); this.layout.render(); $('#footer').addClass('search-navigator-footer'); @@ -81,7 +86,7 @@ App.on('start', function(el) { }); this.layout.filtersRegion.show(this.filtersView); - key.setScope('list'); + window.key.setScope('list'); this.router = new Router({ app: this }); @@ -89,10 +94,11 @@ App.on('start', function(el) { }); }); -export default function(el) { - App.start(el); +export default function(el: HTMLElement, organization: ?string) { + App.start({ el, organization }); return () => { + // $FlowFixMe Backbone.history.stop(); App.layout.destroy(); $('#footer').removeClass('search-navigator-footer'); diff --git a/server/sonar-web/src/main/js/apps/coding-rules/rule-details-view.js b/server/sonar-web/src/main/js/apps/coding-rules/rule-details-view.js index 2fa5eb3f2f4..11ed2ef6b74 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/rule-details-view.js +++ b/server/sonar-web/src/main/js/apps/coding-rules/rule-details-view.js @@ -171,7 +171,7 @@ export default Marionette.LayoutView.extend({ serializeData() { const isCustom = this.model.has('templateKey'); - const isEditable = this.options.app.canWrite && isCustom; + const isEditable = this.options.app.canCustomizeRule && isCustom; let qualityProfilesVisible = true; if (this.model.get('isTemplate')) { @@ -182,7 +182,6 @@ export default Marionette.LayoutView.extend({ ...Marionette.ItemView.prototype.serializeData.apply(this, arguments), isEditable, qualityProfilesVisible, - canWrite: this.options.app.canWrite, allTags: union(this.model.get('sysTags'), this.model.get('tags')) }; } diff --git a/server/sonar-web/src/main/js/apps/coding-rules/rule/custom-rule-view.js b/server/sonar-web/src/main/js/apps/coding-rules/rule/custom-rule-view.js index 1a5ff0ccdfe..295590a3bcb 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/rule/custom-rule-view.js +++ b/server/sonar-web/src/main/js/apps/coding-rules/rule/custom-rule-view.js @@ -47,7 +47,7 @@ export default Marionette.ItemView.extend({ serializeData() { return { ...Marionette.ItemView.prototype.serializeData.apply(this, arguments), - canWrite: this.options.app.canWrite, + canDeleteCustomRule: this.options.app.canCreateCustomRule, templateRule: this.options.templateRule, permalink: window.baseUrl + '/coding_rules/#rule_key=' + encodeURIComponent(this.model.id) }; diff --git a/server/sonar-web/src/main/js/apps/coding-rules/rule/custom-rules-view.js b/server/sonar-web/src/main/js/apps/coding-rules/rule/custom-rules-view.js index e8f9c0752c1..d6ec8a461cd 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/rule/custom-rules-view.js +++ b/server/sonar-web/src/main/js/apps/coding-rules/rule/custom-rules-view.js @@ -56,7 +56,7 @@ export default Marionette.CompositeView.extend({ serializeData() { return { ...Marionette.ItemView.prototype.serializeData.apply(this, arguments), - canWrite: this.options.app.canWrite + canCreateCustomRule: this.options.app.canCreateCustomRule }; } }); diff --git a/server/sonar-web/src/main/js/apps/coding-rules/rule/rule-description-view.js b/server/sonar-web/src/main/js/apps/coding-rules/rule/rule-description-view.js index 62d516c543d..0e942ef4bfb 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/rule/rule-description-view.js +++ b/server/sonar-web/src/main/js/apps/coding-rules/rule/rule-description-view.js @@ -94,12 +94,10 @@ export default Marionette.ItemView.extend({ }, serializeData() { - const isEditable = this.options.app.canWrite && this.model.get('isCustom'); - return { ...Marionette.ItemView.prototype.serializeData.apply(this, arguments), - isEditable, - canWrite: this.options.app.canWrite + isCustom: this.model.get('isCustom'), + canCustomizeRule: this.options.app.canCustomizeRule }; } }); diff --git a/server/sonar-web/src/main/js/apps/coding-rules/rule/rule-issues-view.js b/server/sonar-web/src/main/js/apps/coding-rules/rule/rule-issues-view.js index d13deae57e0..384270c2919 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/rule/rule-issues-view.js +++ b/server/sonar-web/src/main/js/apps/coding-rules/rule/rule-issues-view.js @@ -20,6 +20,7 @@ import $ from 'jquery'; import Marionette from 'backbone.marionette'; import Template from '../templates/rule/coding-rules-rule-issues.hbs'; +import { getComponentIssuesUrl } from '../../../helpers/urls'; export default Marionette.ItemView.extend({ template: Template, @@ -34,7 +35,6 @@ export default Marionette.ItemView.extend({ }, requestIssues() { - const that = this; const url = window.baseUrl + '/api/issues/search'; const options = { rules: this.model.id, @@ -42,6 +42,10 @@ export default Marionette.ItemView.extend({ ps: 1, facets: 'projectUuids' }; + const { organization } = this.options.app; + if (organization) { + Object.assign(options, { organization }); + } return $.get(url, options).done(r => { const projectsFacet = r.facets.find(facet => facet.property === 'projectUuids'); let projects = projectsFacet != null ? projectsFacet.values : []; @@ -49,11 +53,16 @@ export default Marionette.ItemView.extend({ const projectBase = r.components.find(component => component.uuid === project.val); return { ...project, - name: projectBase != null ? projectBase.longName : '' + name: projectBase != null ? projectBase.longName : '', + issuesUrl: projectBase != null && + getComponentIssuesUrl(projectBase.key, { + resolved: 'false', + rules: this.model.id + }) }; }); - that.projects = projects; - that.total = r.total; + this.projects = projects; + this.total = r.total; }); }, @@ -61,10 +70,7 @@ export default Marionette.ItemView.extend({ return { ...Marionette.ItemView.prototype.serializeData.apply(this, arguments), total: this.total, - projects: this.projects, - baseSearchUrl: window.baseUrl + - '/issues/search#resolved=false|rules=' + - encodeURIComponent(this.model.id) + projects: this.projects }; } }); diff --git a/server/sonar-web/src/main/js/apps/coding-rules/rule/rule-meta-view.js b/server/sonar-web/src/main/js/apps/coding-rules/rule/rule-meta-view.js index ba8ed5311ba..0a0b22ade85 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/rule/rule-meta-view.js +++ b/server/sonar-web/src/main/js/apps/coding-rules/rule/rule-meta-view.js @@ -106,11 +106,15 @@ export default Marionette.ItemView.extend(RuleFilterMixin).extend({ }, serializeData() { + const permalinkPath = this.options.app.organization + ? `/organizations/${this.options.app.organization}/rules` + : '/coding_rules'; + return { ...Marionette.ItemView.prototype.serializeData.apply(this, arguments), - canWrite: this.options.app.canWrite, + canCustomizeRule: this.options.app.canCustomizeRule, allTags: union(this.model.get('sysTags'), this.model.get('tags')), - permalink: window.baseUrl + '/coding_rules#rule_key=' + encodeURIComponent(this.model.id) + permalink: window.baseUrl + permalinkPath + '#rule_key=' + encodeURIComponent(this.model.id) }; } }); diff --git a/server/sonar-web/src/main/js/apps/coding-rules/rule/rule-parameters-view.js b/server/sonar-web/src/main/js/apps/coding-rules/rule/rule-parameters-view.js index 8923b74d374..209860ab4cd 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/rule/rule-parameters-view.js +++ b/server/sonar-web/src/main/js/apps/coding-rules/rule/rule-parameters-view.js @@ -30,15 +30,5 @@ export default Marionette.ItemView.extend({ onRender() { const params = this.model.get('params'); this.$el.toggleClass('hidden', params == null || params.length === 0); - }, - - serializeData() { - const isEditable = this.options.app.canWrite && this.model.get('isCustom'); - - return { - ...Marionette.ItemView.prototype.serializeData.apply(this, arguments), - isEditable, - canWrite: this.options.app.canWrite - }; } }); diff --git a/server/sonar-web/src/main/js/apps/coding-rules/rule/rule-profile-view.js b/server/sonar-web/src/main/js/apps/coding-rules/rule/rule-profile-view.js index eab8d1be804..8120484ae41 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/rule/rule-profile-view.js +++ b/server/sonar-web/src/main/js/apps/coding-rules/rule/rule-profile-view.js @@ -123,6 +123,9 @@ export default Marionette.ItemView.extend({ const myProfile = this.options.app.qualityProfiles.find( p => p.key === this.model.get('qProfile') ); + if (!myProfile) { + return null; + } const parentKey = myProfile.parentKey; const parent = { ...this.options.app.qualityProfiles.find(p => p.key === parentKey) }; const parentActiveInfo = this.model.collection.findWhere({ qProfile: parentKey }) || @@ -147,14 +150,26 @@ export default Marionette.ItemView.extend({ }); }, + getProfilePath(profileKey) { + const { organization } = this.options.app; + const encodedKey = encodeURIComponent(profileKey); + return organization + ? `${window.baseUrl}/organizations/${organization}/quality_profiles/show?key=${encodedKey}` + : `${window.baseUrl}/profiles/show?key=${encodedKey}`; + }, + serializeData() { + const parent = this.getParent(); + return { ...Marionette.ItemView.prototype.serializeData.apply(this, arguments), + parent, canWrite: this.options.app.canWrite, - parent: this.getParent(), parameters: this.enhanceParameters(), templateKey: this.options.rule.get('templateKey'), - isTemplate: this.options.rule.get('isTemplate') + isTemplate: this.options.rule.get('isTemplate'), + profilePath: this.getProfilePath(this.model.get('key')), + parentProfilePath: parent && this.getProfilePath(parent.key) }; } }); diff --git a/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-custom-rule.hbs b/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-custom-rule.hbs index b5371ee37f3..eaff503fc17 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-custom-rule.hbs +++ b/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-custom-rule.hbs @@ -17,7 +17,7 @@   -{{#if canWrite}} +{{#if canDeleteCustomRule}}
    diff --git a/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-description.hbs b/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-description.hbs index 3b86bc6d54e..d0ab5457eeb 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-description.hbs +++ b/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-description.hbs @@ -1,19 +1,19 @@
    {{{htmlDesc}}}
    -{{#unless isEditable}} +{{#unless isCustom}}
    {{#if htmlNote}}
    {{{htmlNote}}}
    {{/if}} - {{#if canWrite}} + {{#if canCustomizeRule}}
    {{/if}}
    - {{#if canWrite}} + {{#if canCustomizeRule}}