From: Stas Vilchik Date: Tue, 26 Sep 2017 12:41:01 +0000 (+0200) Subject: SONAR-1330 Allow rule changes only for quality profile admins X-Git-Tag: 6.6-RC1~98 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=6a3ae4ac87b1e12a9bcd496c57bb6e2918cc345e;p=sonarqube.git SONAR-1330 Allow rule changes only for quality profile admins --- diff --git a/server/sonar-web/src/main/js/api/rules.ts b/server/sonar-web/src/main/js/api/rules.ts index 20a0b26f5a2..ded000324d1 100644 --- a/server/sonar-web/src/main/js/api/rules.ts +++ b/server/sonar-web/src/main/js/api/rules.ts @@ -18,6 +18,15 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import { getJSON, RequestData } from '../helpers/request'; +import throwGlobalError from '../app/utils/throwGlobalError'; + +export interface GetRulesAppResponse { + respositories: Array<{ key: string; language: string; name: string }>; +} + +export function getRulesApp(): Promise { + return getJSON('/api/rules/app').catch(throwGlobalError); +} export function searchRules(data: RequestData) { return getJSON('/api/rules/search', data); diff --git a/server/sonar-web/src/main/js/apps/coding-rules/bulk-change-modal-view.js b/server/sonar-web/src/main/js/apps/coding-rules/bulk-change-modal-view.js index 07277f983f6..953c9c7900e 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/bulk-change-modal-view.js +++ b/server/sonar-web/src/main/js/apps/coding-rules/bulk-change-modal-view.js @@ -107,9 +107,11 @@ export default ModalFormView.extend({ const languages = queryLanguages && queryLanguages.length > 0 ? queryLanguages.split(',') : []; let profiles = this.options.app.qualityProfiles; if (languages.length > 0) { - profiles = profiles.filter(profile => languages.indexOf(profile.lang) !== -1); + profiles = profiles.filter(profile => languages.indexOf(profile.language) !== -1); } - return profiles.filter(profile => !profile.isBuiltIn); + return profiles + .filter(profile => profile.actions && profile.actions.edit) + .filter(profile => !profile.isBuiltIn); }, serializeData() { diff --git a/server/sonar-web/src/main/js/apps/coding-rules/bulk-change-popup-view.js b/server/sonar-web/src/main/js/apps/coding-rules/bulk-change-popup-view.js index 50c6c6d4676..d038bf5c9df 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/bulk-change-popup-view.js +++ b/server/sonar-web/src/main/js/apps/coding-rules/bulk-change-popup-view.js @@ -44,12 +44,14 @@ export default PopupView.extend({ const profileKey = query.qprofile; const profile = this.options.app.qualityProfiles.find(p => p.key === profileKey); const activation = '' + query.activation; + const canChangeProfile = + profile != null && !profile.isBuiltIn && profile.actions && profile.actions.edit; return { qualityProfile: profileKey, qualityProfileName: profile != null ? profile.name : null, - allowActivateOnProfile: profile != null && activation === 'false' && !profile.isBuiltIn, - allowDeactivateOnProfile: profileKey != null && activation === 'true' + allowActivateOnProfile: canChangeProfile && activation === 'false', + allowDeactivateOnProfile: canChangeProfile && activation === 'true' }; } }); diff --git a/server/sonar-web/src/main/js/apps/coding-rules/facets/quality-profile-facet.js b/server/sonar-web/src/main/js/apps/coding-rules/facets/quality-profile-facet.js index 3efdac89cfb..d0329e4caaa 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/facets/quality-profile-facet.js +++ b/server/sonar-web/src/main/js/apps/coding-rules/facets/quality-profile-facet.js @@ -50,9 +50,9 @@ export default BaseFacet.extend({ const languages = languagesQuery != null ? languagesQuery.split(',') : []; const lang = languages.length === 1 ? languages[0] : null; const values = this.options.app.qualityProfiles - .filter(profile => (lang != null ? profile.lang === lang : true)) + .filter(profile => (lang != null ? profile.language === lang : true)) .map(profile => ({ - extra: that.options.app.languages[profile.lang], + extra: that.options.app.languages[profile.language], isBuiltIn: profile.isBuiltIn, label: profile.name, val: profile.key 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 36d7f608fda..ed38639c9e0 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 @@ -32,6 +32,8 @@ import Router from '../../components/navigator/router'; import WorkspaceListView from './workspace-list-view'; import WorkspaceHeaderView from './workspace-header-view'; import FacetsView from './facets-view'; +import { searchQualityProfiles } from '../../api/quality-profiles'; +import { getRulesApp } from '../../api/rules'; import { areThereCustomOrganizations } from '../../store/organizations/utils'; const App = new Marionette.Application(); @@ -45,20 +47,16 @@ App.on('start', function( ) { App.organization = options.organization; const data = options.organization ? { organization: options.organization } : {}; - $.get(window.baseUrl + '/api/rules/app', data) - .done(r => { + Promise.all([getRulesApp(data), searchQualityProfiles(data)]) + .then(([appResponse, profilesResponse]) => { App.customRules = !areThereCustomOrganizations(); - App.canWrite = r.canWrite; + App.canWrite = appResponse.canWrite; App.organization = options.organization; - App.qualityProfiles = sortBy(r.qualityprofiles, ['name', 'lang']); - App.languages = { ...r.languages, none: 'None' }; - App.qualityProfiles.forEach(profile => { - profile.language = App.languages[profile.lang]; - }); - App.repositories = r.repositories; - App.statuses = r.statuses; - }) - .done(() => { + App.qualityProfiles = sortBy(profilesResponse.profiles, ['name', 'lang']); + App.languages = { ...appResponse.languages, none: 'None' }; + App.repositories = appResponse.repositories; + App.statuses = appResponse.statuses; + this.layout = new Layout({ el: options.el }); this.layout.render(); $('#footer').addClass('page-footer-with-sidebar'); @@ -109,6 +107,9 @@ App.on('start', function( app: this }); Backbone.history.start(); + }) + .catch(() => { + // do nothing in case of WS error }); }); diff --git a/server/sonar-web/src/main/js/apps/coding-rules/rule/profile-activation-view.js b/server/sonar-web/src/main/js/apps/coding-rules/rule/profile-activation-view.js index f55361b621f..c4c34051150 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/rule/profile-activation-view.js +++ b/server/sonar-web/src/main/js/apps/coding-rules/rule/profile-activation-view.js @@ -132,8 +132,10 @@ export default ModalForm.extend({ const inactiveProfiles = this.options.app.qualityProfiles.filter( profile => !activeQualityProfiles.findWhere({ key: profile.key }) ); + // choose QP which a user can administrate, which are the same language and which are not built-in return inactiveProfiles - .filter(profile => profile.lang === lang) + .filter(profile => profile.actions && profile.actions.edit) + .filter(profile => profile.language === lang) .filter(profile => !profile.isBuiltIn); }, 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 916af2e5f03..3ddbfd50961 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 @@ -164,12 +164,13 @@ export default Marionette.ItemView.extend({ return { ...Marionette.ItemView.prototype.serializeData.apply(this, arguments), parent, + actions: this.model.get('actions') || {}, canWrite: this.options.app.canWrite, parameters: this.enhanceParameters(parent), templateKey: this.options.rule.get('templateKey'), isTemplate: this.options.rule.get('isTemplate'), - profilePath: this.getProfilePath(this.model.get('lang'), this.model.get('name')), - parentProfilePath: parent && this.getProfilePath(parent.lang, parent.name) + profilePath: this.getProfilePath(this.model.get('language'), this.model.get('name')), + parentProfilePath: parent && this.getProfilePath(parent.language, parent.name) }; } }); diff --git a/server/sonar-web/src/main/js/apps/coding-rules/rule/rule-profiles-view.js b/server/sonar-web/src/main/js/apps/coding-rules/rule/rule-profiles-view.js index dadabe8ea18..81781afab8b 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/rule/rule-profiles-view.js +++ b/server/sonar-web/src/main/js/apps/coding-rules/rule/rule-profiles-view.js @@ -85,9 +85,14 @@ export default Marionette.CompositeView.extend({ }, serializeData() { + // show "Activate" button only if user has at least one QP which he administates + const canActivate = this.options.app.qualityProfiles.some( + profile => profile.actions && profile.actions.edit + ); + return { ...Marionette.ItemView.prototype.serializeData.apply(this, arguments), - canWrite: this.options.app.canWrite + canActivate }; } }); diff --git a/server/sonar-web/src/main/js/apps/coding-rules/templates/_coding-rules-workspace-list-item-activation.hbs b/server/sonar-web/src/main/js/apps/coding-rules/templates/_coding-rules-workspace-list-item-activation.hbs deleted file mode 100644 index 84f89974491..00000000000 --- a/server/sonar-web/src/main/js/apps/coding-rules/templates/_coding-rules-workspace-list-item-activation.hbs +++ /dev/null @@ -1,29 +0,0 @@ -{{#each activeProfiles}} - {{severityIcon severity}} - - {{#eq inherit 'OVERRIDES'}} - - {{/eq}} - {{#eq inherit 'INHERITED'}} - - {{/eq}} - - {{#if ../canWrite}} -
- {{#unless isTemplate}} - - {{/unless}} - {{#if parent}} - {{#eq inherit 'OVERRIDES'}} - - {{/eq}} - {{else}} - - {{/if}} -
- {{/if}} -{{/each}} diff --git a/server/sonar-web/src/main/js/apps/coding-rules/templates/coding-rules-workspace-header.hbs b/server/sonar-web/src/main/js/apps/coding-rules/templates/coding-rules-workspace-header.hbs index 53dcba3b520..97623f1e77a 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/templates/coding-rules-workspace-header.hbs +++ b/server/sonar-web/src/main/js/apps/coding-rules/templates/coding-rules-workspace-header.hbs @@ -29,7 +29,7 @@
- {{#if canWrite}} + {{#if canBulkChange}} {{/if}}
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/templates/coding-rules-workspace-list-item.hbs b/server/sonar-web/src/main/js/apps/coding-rules/templates/coding-rules-workspace-list-item.hbs index 23324db3e9e..139d46cdcb6 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/templates/coding-rules-workspace-list-item.hbs +++ b/server/sonar-web/src/main/js/apps/coding-rules/templates/coding-rules-workspace-list-item.hbs @@ -46,16 +46,15 @@ {{#any activation selectedProfile}} - {{#if canWrite}} + {{#if canEditQualityProfile}} {{#unless isSelectedProfileBuiltIn}}
{{#if activation}} - {{#eq activation.inherit 'NONE'}} - - {{/eq}} + {{else}} {{#unless isTemplate}} 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 00a1c805411..e65ab663892 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 @@ -52,7 +52,7 @@ {{/unless}} - {{#if canWrite}} + {{#if actions.edit}} {{#unless isBuiltIn}}
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-profiles.hbs b/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-profiles.hbs index 2a28c69d62e..efd296d2074 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-profiles.hbs +++ b/server/sonar-web/src/main/js/apps/coding-rules/templates/rule/coding-rules-rule-profiles.hbs @@ -3,7 +3,7 @@

{{t 'coding_rules.quality_profiles'}}

- {{#if canWrite}} + {{#if canActivate}} {{#unless isTemplate}}
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/workspace-header-view.js b/server/sonar-web/src/main/js/apps/coding-rules/workspace-header-view.js index a5ac0165f28..708164d62ae 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/workspace-header-view.js +++ b/server/sonar-web/src/main/js/apps/coding-rules/workspace-header-view.js @@ -58,9 +58,14 @@ export default WorkspaceHeaderView.extend({ }, serializeData() { + // show "Bulk Change" button only if user has at least one QP which he administates + const canBulkChange = this.options.app.qualityProfiles.some( + profile => profile.actions && profile.actions.edit + ); + return { ...WorkspaceHeaderView.prototype.serializeData.apply(this, arguments), - canWrite: this.options.app.canWrite + canBulkChange }; } }); diff --git a/server/sonar-web/src/main/js/apps/coding-rules/workspace-list-item-view.js b/server/sonar-web/src/main/js/apps/coding-rules/workspace-list-item-view.js index b5d0fafe2ee..6235c15c7ab 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/workspace-list-item-view.js +++ b/server/sonar-web/src/main/js/apps/coding-rules/workspace-list-item-view.js @@ -118,10 +118,13 @@ export default WorkspaceListItemView.extend(RuleFilterMixin).extend({ this.options.app.qualityProfiles.find(profile => profile.key === selectedProfileKey); const isSelectedProfileBuiltIn = selectedProfile != null && selectedProfile.isBuiltIn; + const canEditQualityProfile = + selectedProfile && selectedProfile.actions && selectedProfile.actions.edit; + return { ...WorkspaceListItemView.prototype.serializeData.apply(this, arguments), + canEditQualityProfile, tags: union(this.model.get('sysTags'), this.model.get('tags')), - canWrite: this.options.app.canWrite, selectedProfile: selectedProfileKey, isSelectedProfileBuiltIn }; diff --git a/server/sonar-web/src/main/less/components/search-navigator.less b/server/sonar-web/src/main/less/components/search-navigator.less index 268629f8e3c..e6c37014795 100644 --- a/server/sonar-web/src/main/less/components/search-navigator.less +++ b/server/sonar-web/src/main/less/components/search-navigator.less @@ -193,10 +193,10 @@ float: left; height: 16px; line-height: 16px; + margin-top: -1px; padding: 0 5px; - border-radius: 3px; + border-radius: 2px; font-size: 11px; - font-weight: 300; text-transform: lowercase; &:hover { diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties index 973400b7713..661dde6c15e 100644 --- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties +++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties @@ -1674,6 +1674,7 @@ coding_rules.available_since=Available Since coding_rules.bulk_change=Bulk Change coding_rules.bulk_change.success={2} rule(s) changed in profile {0} - {1} coding_rules.bulk_change.warning={2} rule(s) changed, {3} rule(s) ignored in profile {0} - {1} +coding_rules.can_not_deactivate=This rule is inherited and can not be deactivated. coding_rules.change_severity=Change Severity coding_rules.change_severity_in=Change Severity In coding_rules.change_details=Change Details of Quality Profile