diff options
author | Stas Vilchik <vilchiks@gmail.com> | 2017-03-28 16:42:37 +0200 |
---|---|---|
committer | Stas Vilchik <stas-vilchik@users.noreply.github.com> | 2017-04-03 10:38:52 +0200 |
commit | adb4265900d35345f8d900dca47cc6c2f490cb21 (patch) | |
tree | 1a3e27f58ff84c51cccd3f36d6a13ce2eec26c24 /server/sonar-web | |
parent | 32a73efa05cb12056a93f08b9124e647213f1f02 (diff) | |
download | sonarqube-adb4265900d35345f8d900dca47cc6c2f490cb21.tar.gz sonarqube-adb4265900d35345f8d900dca47cc6c2f490cb21.zip |
SONAR-9005 support rules for organizations
Diffstat (limited to 'server/sonar-web')
40 files changed, 288 insertions, 156 deletions
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 ( <AboutAppForSonarQubeDotCom + appState={this.props.appState} bugs={bugs} codeSmells={codeSmells} currentUser={this.props.currentUser} @@ -180,7 +185,7 @@ class AboutApp extends React.Component { <AboutQualityGates /> </div> <div className="flex-column flex-column-half about-page-group-boxes"> - <AboutStandards /> + <AboutStandards appState={this.props.appState} /> </div> </div> @@ -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 { </div> </div> - <AboutRulesForSonarQubeDotCom /> + <AboutRulesForSonarQubeDotCom appState={this.props.appState} /> <div className="about-page-container"> {customText != null && @@ -102,7 +106,7 @@ export default class AboutAppForSonarQubeDotCom extends React.Component { <AboutQualityGates /> </div> <div className="flex-column flex-column-half about-page-group-boxes"> - <AboutStandards /> + <AboutStandards appState={this.props.appState} /> </div> </div> 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 ( <div className="sqcom-about-rules"> <div className="about-page-container"> - <Link to={getRulesUrl()} className="sqcom-about-rules-link"> + <Link to={getRulesUrl(null, organization)} className="sqcom-about-rules-link"> +3,000 rules <span className="spacer-left"> <svg width="15" height="36" viewBox="0 0 15 36"> @@ -40,17 +49,27 @@ export default class AboutRulesForSonarQubeDotCom extends React.Component { </svg> </span> </Link> - <Link to={getRulesUrl({ languages: 'js' })} className="sqcom-about-rules-link"> + <Link + to={getRulesUrl({ languages: 'js' }, organization)} + className="sqcom-about-rules-link"> JavaScript </Link> - <Link to={getRulesUrl({ languages: 'java' })} className="sqcom-about-rules-link"> + <Link + to={getRulesUrl({ languages: 'java' }, organization)} + className="sqcom-about-rules-link"> Java </Link> - <Link to={getRulesUrl({ languages: 'c,cpp' })} className="sqcom-about-rules-link"> + <Link + to={getRulesUrl({ languages: 'c,cpp' }, organization)} + className="sqcom-about-rules-link"> C/C++ </Link> - <Link to={getRulesUrl({ languages: 'cs' })} className="sqcom-about-rules-link">C#</Link> - <Link to={getRulesUrl()} className="button">And More</Link> + <Link + to={getRulesUrl({ languages: 'cs' }, organization)} + className="sqcom-about-rules-link"> + C# + </Link> + <Link to={getRulesUrl(null, organization)} className="button">And More</Link> </div> </div> ); 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 ( <div className="boxed-group"> <h2>{translate('about_page.standards')}</h2> @@ -39,34 +51,34 @@ export default class AboutStandards extends React.Component { <div className="spacer-top"> <ul className="list-inline"> <li> - <Link to={getRulesUrl({ tags: 'misra' })} className="link-with-icon"> + <Link to={getRulesUrl({ tags: 'misra' }, organization)} className="link-with-icon"> <i className="icon-tags" /> <span className="little-spacer-left">MISRA</span> </Link> </li> <li> - <Link to={getRulesUrl({ tags: 'cert' })} className="link-with-icon"> + <Link to={getRulesUrl({ tags: 'cert' }, organization)} className="link-with-icon"> <i className="icon-tags" /> <span className="little-spacer-left">CERT</span> </Link> </li> <li> - <Link to={getRulesUrl({ tags: 'cwe' })} className="link-with-icon"> + <Link to={getRulesUrl({ tags: 'cwe' }, organization)} className="link-with-icon"> <i className="icon-tags" /> <span className="little-spacer-left">CWE</span> </Link> </li> <li> - <Link to={getRulesUrl({ tags: owaspTags })} className="link-with-icon"> + <Link + to={getRulesUrl({ tags: owaspTags }, organization)} + className="link-with-icon"> <i className="icon-tags" /> <span className="little-spacer-left">OWASP Top 10</span> </Link> </li> <li> <Link - to={getRulesUrl({ - tags: 'sans-top25-porous,sans-top25-risky,sans-top25-insecure' - })} + to={getRulesUrl({ tags: sans25Tags }, organization)} className="link-with-icon"> <i className="icon-tags" /> <span className="little-spacer-left">SANS Top 25</span> 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 @@ </td> -{{#if canWrite}} +{{#if canDeleteCustomRule}} <td class="coding-rules-detail-list-actions"> <div class="button-group"> <button class="js-delete-custom-rule button-red"> diff --git a/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-custom-rules.hbs b/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-custom-rules.hbs index 36a79d1951a..bd3899109cf 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-custom-rules.hbs +++ b/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-custom-rules.hbs @@ -3,7 +3,7 @@ <h3 class="coding-rules-detail-title">{{t 'coding_rules.custom_rules'}}</h3> - {{#if canWrite}} + {{#if canCreateCustomRule}} <div class="button-group coding-rules-detail-quality-profiles-activation"> <button class="js-create-custom-rule">{{t 'coding_rules.create'}}</button> </div> 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 @@ <div class="coding-rules-detail-description rule-desc markdown">{{{htmlDesc}}}</div> -{{#unless isEditable}} +{{#unless isCustom}} <div class="coding-rules-detail-description coding-rules-detail-description-extra"> <div id="coding-rules-detail-description-extra"> {{#if htmlNote}} <div class="rule-desc spacer-bottom markdown">{{{htmlNote}}}</div> {{/if}} - {{#if canWrite}} + {{#if canCustomizeRule}} <div class="button-group"> <button id="coding-rules-detail-extend-description">{{t 'coding_rules.extend_description'}}</button> </div> {{/if}} </div> - {{#if canWrite}} + {{#if canCustomizeRule}} <div class="coding-rules-detail-extend-description-form hidden"> <table class="width100"> <tbody> diff --git a/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-issues.hbs b/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-issues.hbs index da9a5336d36..f6dc9fe7619 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-issues.hbs +++ b/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-issues.hbs @@ -1,7 +1,7 @@ <div class="coding-rule-section-separator"></div> <h3 class="coding-rules-detail-title"> - {{t 'coding_rules.issues'}} (<a class="js-rule-issues" href="{{baseSearchUrl}}">{{total}}</a>) + {{t 'coding_rules.issues'}} ({{total}}) </h3> {{#notEmpty projects}} @@ -13,7 +13,7 @@ <tr> <td class="coding-rules-detail-list-name">{{name}}</td> <td class="coding-rules-detail-list-parameters"> - <a href="{{../baseSearchUrl}}|projectUuids={{val}}" target="_blank">{{count}}</a> + <a href="{{issuesUrl}}" target="_blank">{{count}}</a> </td> </tr> {{/each}} diff --git a/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-meta.hbs b/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-meta.hbs index fc38adfff0c..255f97fea08 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-meta.hbs +++ b/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-meta.hbs @@ -34,14 +34,14 @@ </li> {{/notEq}} - <li class="coding-rules-detail-property coding-rules-detail-tag-list {{#if canWrite}}coding-rules-detail-tags-change{{/if}}" + <li class="coding-rules-detail-property coding-rules-detail-tag-list {{#if canCustomizeRule}}coding-rules-detail-tags-change{{/if}}" data-toggle="tooltip" data-placement="bottom" title="Rule tags"> <i class="icon-tags"></i> <span>{{#if allTags}}{{join allTags ', '}}{{else}}{{t 'coding_rules.no_tags'}}{{/if}}</span> {{#if canWrite}}<i class="icon-dropdown"></i>{{/if}} </li> - {{#if canWrite}} + {{#if canCustomizeRule}} <li class="coding-rules-detail-property coding-rules-detail-tag-edit hidden"> {{#if sysTags}}<i class="icon-tags"></i> <span>{{join sysTags ', '}}</span>{{/if}} diff --git a/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-profile.hbs b/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-profile.hbs index 929a1e6d454..12d1fe950d5 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-profile.hbs +++ b/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-profile.hbs @@ -1,5 +1,5 @@ <td class="coding-rules-detail-quality-profile-name"> - <a href="{{profileUrl key}}"> + <a href="{{profilePath}}"> {{name}} </a> {{#if parent}} @@ -10,7 +10,7 @@ {{#eq inherit 'INHERITED'}} <i class="icon-inheritance" title="{{tp 'coding_rules.inherits' name parent.name}}"></i> {{/eq}} - <a class="link-base-color" href="{{profileUrl parent.key}}"> + <a class="link-base-color" href="{{parentProfilePath}}"> {{parent.name}} </a> </div> diff --git a/server/sonar-web/src/main/js/helpers/handlebars/profileUrl.js b/server/sonar-web/src/main/js/apps/organizations/components/OrganizationRules.js index dab5f850fc2..c70546464a2 100644 --- a/server/sonar-web/src/main/js/helpers/handlebars/profileUrl.js +++ b/server/sonar-web/src/main/js/apps/organizations/components/OrganizationRules.js @@ -17,6 +17,12 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -module.exports = function(key) { - return window.baseUrl + '/profiles/show?key=' + encodeURIComponent(key); -}; +// @flow +import React from 'react'; +import CodingRulesAppContainer from '../../coding-rules/components/CodingRulesAppContainer'; + +export default class OrganizationRules extends React.PureComponent { + render() { + return <CodingRulesAppContainer {...this.props} />; + } +} diff --git a/server/sonar-web/src/main/js/apps/organizations/navigation/OrganizationNavigation.js b/server/sonar-web/src/main/js/apps/organizations/navigation/OrganizationNavigation.js index fdf72a5009e..0675c1872d8 100644 --- a/server/sonar-web/src/main/js/apps/organizations/navigation/OrganizationNavigation.js +++ b/server/sonar-web/src/main/js/apps/organizations/navigation/OrganizationNavigation.js @@ -161,6 +161,11 @@ export default class OrganizationNavigation extends React.Component { {translate('quality_profiles.page')} </Link> </li> + <li> + <Link to={`/organizations/${organization.key}/rules`} activeClassName="active"> + {translate('coding_rules.page')} + </Link> + </li> {organization.canAdmin && this.renderAdministration()} </ul> </div> diff --git a/server/sonar-web/src/main/js/apps/organizations/navigation/__tests__/__snapshots__/OrganizationNavigation-test.js.snap b/server/sonar-web/src/main/js/apps/organizations/navigation/__tests__/__snapshots__/OrganizationNavigation-test.js.snap index 46358abc847..0d9b7f5706c 100644 --- a/server/sonar-web/src/main/js/apps/organizations/navigation/__tests__/__snapshots__/OrganizationNavigation-test.js.snap +++ b/server/sonar-web/src/main/js/apps/organizations/navigation/__tests__/__snapshots__/OrganizationNavigation-test.js.snap @@ -53,6 +53,15 @@ exports[`test admin 1`] = ` quality_profiles.page </Link> </li> + <li> + <Link + activeClassName="active" + onlyActiveOnIndex={false} + style={Object {}} + to="/organizations/foo/rules"> + coding_rules.page + </Link> + </li> <li className=""> <a @@ -183,6 +192,15 @@ exports[`test regular user 1`] = ` quality_profiles.page </Link> </li> + <li> + <Link + activeClassName="active" + onlyActiveOnIndex={false} + style={Object {}} + to="/organizations/foo/rules"> + coding_rules.page + </Link> + </li> </ul> </div> </div> @@ -244,6 +262,15 @@ exports[`test undeletable org 1`] = ` quality_profiles.page </Link> </li> + <li> + <Link + activeClassName="active" + onlyActiveOnIndex={false} + style={Object {}} + to="/organizations/foo/rules"> + coding_rules.page + </Link> + </li> <li className=""> <a diff --git a/server/sonar-web/src/main/js/apps/organizations/routes.js b/server/sonar-web/src/main/js/apps/organizations/routes.js index 1b7f81eda3d..7f91a2f344d 100644 --- a/server/sonar-web/src/main/js/apps/organizations/routes.js +++ b/server/sonar-web/src/main/js/apps/organizations/routes.js @@ -20,6 +20,7 @@ import OrganizationPage from './components/OrganizationPage'; import OrganizationProjects from './components/OrganizationProjects'; import OrganizationFavoriteProjects from './components/OrganizationFavoriteProjects'; +import OrganizationRules from './components/OrganizationRules'; import OrganizationAdmin from './components/OrganizationAdmin'; import OrganizationEdit from './components/OrganizationEdit'; import OrganizationGroups from './components/OrganizationGroups'; @@ -56,6 +57,10 @@ const routes = [ component: OrganizationMembersContainer }, { + path: 'rules', + component: OrganizationRules + }, + { path: 'quality_profiles', childRoutes: qualityProfilesRoutes }, diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/Changelog.js b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/Changelog.js index 2e84e64d2a3..0996a1d7ce4 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/Changelog.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/Changelog.js @@ -33,7 +33,8 @@ type Props = { params?: {}, ruleKey: string, ruleName: string - }> + }>, + organization: ?string }; export default class Changelog extends React.PureComponent { @@ -74,7 +75,7 @@ export default class Changelog extends React.PureComponent { </td> <td style={{ lineHeight: '1.5' }}> - <Link to={getRulesUrl({ rule_key: event.ruleKey })}> + <Link to={getRulesUrl({ rule_key: event.ruleKey }, this.props.organization)}> {event.ruleName} </Link> </td> diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangelogContainer.js b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangelogContainer.js index c7ea2d89572..a1fe68f50af 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangelogContainer.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangelogContainer.js @@ -170,7 +170,7 @@ export default class ChangelogContainer extends React.PureComponent { {this.state.events != null && this.state.events.length > 0 && - <Changelog events={this.state.events} />} + <Changelog events={this.state.events} organization={this.props.organization} />} {shouldDisplayFooter && <footer className="text-center spacer-top small"> diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonContainer.js b/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonContainer.js index cdc70a050ed..6428c060684 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonContainer.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonContainer.js @@ -122,6 +122,7 @@ export default class ComparisonContainer extends React.PureComponent { inLeft={inLeft} inRight={inRight} modified={modified} + organization={this.props.organization} />} </div> ); diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonResults.js b/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonResults.js index c6acf738546..ad79758344a 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonResults.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonResults.js @@ -32,7 +32,8 @@ type Props = { right: { name: string }, inLeft: Array<*>, inRight: Array<*>, - modified: Array<*> + modified: Array<*>, + organization: ?string }; export default class ComparisonResults extends React.PureComponent { @@ -43,7 +44,7 @@ export default class ComparisonResults extends React.PureComponent { <div> <SeverityIcon severity={severity} /> {' '} - <Link to={getRulesUrl({ rule_key: rule.key })}> + <Link to={getRulesUrl({ rule_key: rule.key }, this.props.organization)}> {rule.name} </Link> </div> diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileActions.js b/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileActions.js index 1101c1560ce..d1398b7f90e 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileActions.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileActions.js @@ -84,11 +84,13 @@ export default class ProfileActions extends React.PureComponent { '/api/qualityprofiles/backup?profileKey=' + encodeURIComponent(profile.key); - // FIXME getRulesUrl - const activateMoreUrl = getRulesUrl({ - qprofile: profile.key, - activation: 'false' - }); + const activateMoreUrl = getRulesUrl( + { + qprofile: profile.key, + activation: 'false' + }, + this.props.organization + ); return ( <ul className="dropdown-menu dropdown-menu-right"> diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRules.js b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRules.js index 7a68617ab25..6b726e51e96 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRules.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRules.js @@ -33,6 +33,7 @@ const TYPES = ['BUG', 'VULNERABILITY', 'CODE_SMELL']; type Props = { canAdmin: boolean, + organization: ?string, profile: Profile }; @@ -133,10 +134,13 @@ export default class ProfileRules extends React.PureComponent { } renderActiveCount() { - const rulesUrl = getRulesUrl({ - qprofile: this.props.profile.key, - activation: 'true' - }); + const rulesUrl = getRulesUrl( + { + qprofile: this.props.profile.key, + activation: 'true' + }, + this.props.organization + ); if (this.state.activatedTotal == null) { return null; @@ -152,10 +156,13 @@ export default class ProfileRules extends React.PureComponent { } renderActiveTotal() { - const rulesUrl = getRulesUrl({ - qprofile: this.props.profile.key, - activation: 'false' - }); + const rulesUrl = getRulesUrl( + { + qprofile: this.props.profile.key, + activation: 'false' + }, + this.props.organization + ); if (this.state.total == null || this.state.activatedTotal == null) { return null; @@ -184,11 +191,14 @@ export default class ProfileRules extends React.PureComponent { } renderCountForType(type: string) { - const rulesUrl = getRulesUrl({ - qprofile: this.props.profile.key, - activation: 'true', - types: type - }); + const rulesUrl = getRulesUrl( + { + qprofile: this.props.profile.key, + activation: 'true', + types: type + }, + this.props.organization + ); const count = this.state.activatedByType && this.state.activatedByType[type] ? this.state.activatedByType[type].count @@ -206,11 +216,14 @@ export default class ProfileRules extends React.PureComponent { } renderTotalForType(type: string) { - const rulesUrl = getRulesUrl({ - qprofile: this.props.profile.key, - activation: 'false', - types: type - }); + const rulesUrl = getRulesUrl( + { + qprofile: this.props.profile.key, + activation: 'false', + types: type + }, + this.props.organization + ); const count = this.state.activatedByType && this.state.activatedByType[type] ? this.state.activatedByType[type].count @@ -242,7 +255,7 @@ export default class ProfileRules extends React.PureComponent { return null; } - const url = getDeprecatedActiveRulesUrl({ qprofile: profile.key }); + const url = getDeprecatedActiveRulesUrl({ qprofile: profile.key }, this.props.organization); return ( <div className="quality-profile-rules-deprecated clearfix"> @@ -259,10 +272,13 @@ export default class ProfileRules extends React.PureComponent { } render() { - const activateMoreUrl = getRulesUrl({ - qprofile: this.props.profile.key, - activation: 'false' - }); + const activateMoreUrl = getRulesUrl( + { + qprofile: this.props.profile.key, + activation: 'false' + }, + this.props.organization + ); return ( <div className="quality-profile-rules"> diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/Evolution.js b/server/sonar-web/src/main/js/apps/quality-profiles/home/Evolution.js index 23995d62e19..eca194a8209 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/home/Evolution.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/Evolution.js @@ -39,7 +39,7 @@ export default class Evolution extends React.PureComponent { <div className="quality-profiles-evolution"> <EvolutionDeprecated organization={organization} profiles={profiles} /> <EvolutionStagnant organization={organization} profiles={profiles} /> - <EvolutionRules /> + <EvolutionRules organization={organization} /> </div> ); } diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionDeprecated.js b/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionDeprecated.js index ce8f4ddafbc..89d9d87beab 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionDeprecated.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionDeprecated.js @@ -35,8 +35,6 @@ export default class EvolutionDeprecated extends React.PureComponent { props: Props; render() { - // FIXME getDeprecatedActiveRulesUrl - const profilesWithDeprecations = this.props.profiles.filter( profile => profile.activeDeprecatedRuleCount > 0 ); @@ -73,7 +71,10 @@ export default class EvolutionDeprecated extends React.PureComponent { {profile.languageName} {', '} <Link - to={getDeprecatedActiveRulesUrl({ qprofile: profile.key })} + to={getDeprecatedActiveRulesUrl( + { qprofile: profile.key }, + this.props.organization + )} className="text-muted"> {translateWithParameters( 'quality_profile.x_rules', diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionRules.js b/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionRules.js index 553e6bdf62c..123b9394b91 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionRules.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionRules.js @@ -39,7 +39,9 @@ function parseRules(r) { }); } -type Props = {}; +type Props = { + organization: ?string +}; export default class EvolutionRules extends React.PureComponent { mounted: boolean; @@ -75,15 +77,16 @@ export default class EvolutionRules extends React.PureComponent { } render() { - // FIXME getRulesUrl - if (!this.state.latestRulesTotal) { return null; } - const newRulesUrl = getRulesUrl({ - available_since: PERIOD_START_MOMENT.format('YYYY-MM-DD') - }); + const newRulesUrl = getRulesUrl( + { + available_since: PERIOD_START_MOMENT.format('YYYY-MM-DD') + }, + this.props.organization + ); return ( <div className="quality-profile-box quality-profiles-evolution-rules"> @@ -96,7 +99,9 @@ export default class EvolutionRules extends React.PureComponent { {this.state.latestRules.map(rule => ( <li key={rule.key} className="spacer-top"> <div className="text-ellipsis"> - <Link to={getRulesUrl({ rule_key: rule.key })} className="link-no-underline"> + <Link + to={getRulesUrl({ rule_key: rule.key }, this.props.organization)} + className="link-no-underline"> {' '} {rule.name} </Link> diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesListRow.js b/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesListRow.js index 3a53ee66e19..a38a5e9954f 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesListRow.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesListRow.js @@ -74,20 +74,24 @@ export default class ProfilesListRow extends React.PureComponent { } renderRules() { - // FIXME getRulesUrl - const { profile } = this.props; - const activeRulesUrl = getRulesUrl({ - qprofile: profile.key, - activation: 'true' - }); + const activeRulesUrl = getRulesUrl( + { + qprofile: profile.key, + activation: 'true' + }, + this.props.organization + ); - const deprecatedRulesUrl = getRulesUrl({ - qprofile: profile.key, - activation: 'true', - statuses: 'DEPRECATED' - }); + const deprecatedRulesUrl = getRulesUrl( + { + qprofile: profile.key, + activation: 'true', + statuses: 'DEPRECATED' + }, + this.props.organization + ); return ( <div> diff --git a/server/sonar-web/src/main/js/components/issue/issue-view.js b/server/sonar-web/src/main/js/components/issue/issue-view.js index eca902adbe9..e9b4c47cfcd 100644 --- a/server/sonar-web/src/main/js/components/issue/issue-view.js +++ b/server/sonar-web/src/main/js/components/issue/issue-view.js @@ -245,7 +245,7 @@ export default Marionette.ItemView.extend({ const ruleKey = this.model.get('rule'); // lazy load Workspace const Workspace = require('../workspace/main').default; - Workspace.openRule({ key: ruleKey }); + Workspace.openRule({ key: ruleKey, organization: this.model.get('projectOrganization') }); }, action(action) { diff --git a/server/sonar-web/src/main/js/components/workspace/main.js b/server/sonar-web/src/main/js/components/workspace/main.js index 8c8cfd05f44..bb834be5a3a 100644 --- a/server/sonar-web/src/main/js/components/workspace/main.js +++ b/server/sonar-web/src/main/js/components/workspace/main.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 Item from './models/item'; import Items from './models/items'; @@ -77,7 +78,7 @@ Workspace.prototype = { return this.open({ ...options, __type__: 'component' }); }, - openRule(options) { + openRule(options: { key: string, organization: string }) { return this.open({ ...options, __type__: 'rule' }); }, diff --git a/server/sonar-web/src/main/js/components/workspace/templates/workspace-rule.hbs b/server/sonar-web/src/main/js/components/workspace/templates/workspace-rule.hbs index 9c43694986b..377035a4d2f 100644 --- a/server/sonar-web/src/main/js/components/workspace/templates/workspace-rule.hbs +++ b/server/sonar-web/src/main/js/components/workspace/templates/workspace-rule.hbs @@ -42,7 +42,7 @@ {{/if}} <li class="pull-right spacer-left"> - <a class="icon-link" target="_blank" href="{{rulePermalink key}}"></a> + <a class="icon-link" target="_blank" href="{{permalink}}"></a> </li> <li class="pull-right"> diff --git a/server/sonar-web/src/main/js/components/workspace/views/rule-view.js b/server/sonar-web/src/main/js/components/workspace/views/rule-view.js index 1fe6440d64c..be86ab3e38d 100644 --- a/server/sonar-web/src/main/js/components/workspace/views/rule-view.js +++ b/server/sonar-web/src/main/js/components/workspace/views/rule-view.js @@ -21,6 +21,8 @@ import { union } from 'lodash'; import Marionette from 'backbone.marionette'; import BaseView from './base-viewer-view'; import Template from '../templates/workspace-rule.hbs'; +import { getRulesUrl } from '../../../helpers/urls'; +import { areThereCustomOrganizations } from '../../../store/organizations/utils'; export default BaseView.extend({ template: Template, @@ -35,9 +37,15 @@ export default BaseView.extend({ }, serializeData() { + const query = { rule_key: this.model.get('key') }; + const permalink = areThereCustomOrganizations() + ? getRulesUrl(query, this.model.get('organization')) + : getRulesUrl(query); + return { ...Marionette.LayoutView.prototype.serializeData.apply(this, arguments), - allTags: union(this.model.get('sysTags'), this.model.get('tags')) + allTags: union(this.model.get('sysTags'), this.model.get('tags')), + permalink }; } }); diff --git a/server/sonar-web/src/main/js/helpers/handlebars/rulePermalink.js b/server/sonar-web/src/main/js/helpers/handlebars/rulePermalink.js deleted file mode 100644 index e338339a804..00000000000 --- a/server/sonar-web/src/main/js/helpers/handlebars/rulePermalink.js +++ /dev/null @@ -1,22 +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. - */ -module.exports = function(ruleKey) { - return window.baseUrl + '/coding_rules#rule_key=' + encodeURIComponent(ruleKey); -}; diff --git a/server/sonar-web/src/main/js/helpers/urls.js b/server/sonar-web/src/main/js/helpers/urls.js index de25aa553ee..1464e336fb7 100644 --- a/server/sonar-web/src/main/js/helpers/urls.js +++ b/server/sonar-web/src/main/js/helpers/urls.js @@ -114,17 +114,19 @@ export function getQualityGateUrl(key) { * @param {object} query * @returns {string} */ -export function getRulesUrl(query) { +export function getRulesUrl(query, organization?: string) { + const path = organization ? `/organizations/${organization}/rules` : '/coding_rules'; + if (query) { const serializedQuery = Object.keys(query) .map(criterion => `${encodeURIComponent(criterion)}=${encodeURIComponent(query[criterion])}`) .join('|'); // return a string (not { pathname }) to help react-router's Link handle this properly - return '/coding_rules#' + serializedQuery; + return path + '#' + serializedQuery; } - return '/coding_rules'; + return path; } /** @@ -132,9 +134,9 @@ export function getRulesUrl(query) { * @param {object} query * @returns {string} */ -export function getDeprecatedActiveRulesUrl(query = {}) { +export function getDeprecatedActiveRulesUrl(query = {}, organization?: string) { const baseQuery = { activation: 'true', statuses: 'DEPRECATED' }; - return getRulesUrl({ ...query, ...baseQuery }); + return getRulesUrl({ ...query, ...baseQuery }, organization); } export const getProjectsUrl = () => { |