aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-server/src/main/coffee/coding-rules
diff options
context:
space:
mode:
Diffstat (limited to 'sonar-server/src/main/coffee/coding-rules')
-rw-r--r--sonar-server/src/main/coffee/coding-rules/app.coffee343
-rw-r--r--sonar-server/src/main/coffee/coding-rules/layout.coffee29
-rw-r--r--sonar-server/src/main/coffee/coding-rules/mockjax.coffee332
-rw-r--r--sonar-server/src/main/coffee/coding-rules/router.coffee36
-rw-r--r--sonar-server/src/main/coffee/coding-rules/views/actions-view.coffee62
-rw-r--r--sonar-server/src/main/coffee/coding-rules/views/coding-rules-bulk-change-dropdown-view.coffee51
-rw-r--r--sonar-server/src/main/coffee/coding-rules/views/coding-rules-bulk-change-view.coffee103
-rw-r--r--sonar-server/src/main/coffee/coding-rules/views/coding-rules-detail-quality-profile-view.coffee47
-rw-r--r--sonar-server/src/main/coffee/coding-rules/views/coding-rules-detail-quality-profiles-view.coffee14
-rw-r--r--sonar-server/src/main/coffee/coding-rules/views/coding-rules-detail-view.coffee131
-rw-r--r--sonar-server/src/main/coffee/coding-rules/views/coding-rules-list-empty-view.coffee12
-rw-r--r--sonar-server/src/main/coffee/coding-rules/views/coding-rules-list-item-view.coffee34
-rw-r--r--sonar-server/src/main/coffee/coding-rules/views/coding-rules-list-view.coffee23
-rw-r--r--sonar-server/src/main/coffee/coding-rules/views/coding-rules-quality-profile-activation-view.coffee85
-rw-r--r--sonar-server/src/main/coffee/coding-rules/views/filter-bar-view.coffee71
-rw-r--r--sonar-server/src/main/coffee/coding-rules/views/filters/activation-filter-view.coffee41
-rw-r--r--sonar-server/src/main/coffee/coding-rules/views/filters/characteristic-filter-view.coffee12
-rw-r--r--sonar-server/src/main/coffee/coding-rules/views/filters/inheritance-filter-view.coffee52
-rw-r--r--sonar-server/src/main/coffee/coding-rules/views/filters/quality-profile-filter-view.coffee51
-rw-r--r--sonar-server/src/main/coffee/coding-rules/views/header-view.coffee18
20 files changed, 1547 insertions, 0 deletions
diff --git a/sonar-server/src/main/coffee/coding-rules/app.coffee b/sonar-server/src/main/coffee/coding-rules/app.coffee
new file mode 100644
index 00000000000..b5029b0f577
--- /dev/null
+++ b/sonar-server/src/main/coffee/coding-rules/app.coffee
@@ -0,0 +1,343 @@
+requirejs.config
+ baseUrl: "#{baseUrl}/js"
+
+ paths:
+ 'backbone': 'third-party/backbone'
+ 'backbone.marionette': 'third-party/backbone.marionette'
+ 'handlebars': 'third-party/handlebars'
+ 'jquery.mockjax': 'third-party/jquery.mockjax'
+
+ shim:
+ 'backbone.marionette':
+ deps: ['backbone']
+ exports: 'Marionette'
+ 'backbone':
+ exports: 'Backbone'
+ 'handlebars':
+ exports: 'Handlebars'
+
+
+requirejs [
+ 'backbone', 'backbone.marionette',
+
+ 'coding-rules/layout',
+ 'coding-rules/router',
+
+ # views
+ 'coding-rules/views/header-view',
+ 'coding-rules/views/actions-view',
+ 'coding-rules/views/filter-bar-view',
+ 'coding-rules/views/coding-rules-list-view',
+ 'coding-rules/views/coding-rules-bulk-change-view',
+ 'coding-rules/views/coding-rules-quality-profile-activation-view',
+ 'coding-rules/views/coding-rules-bulk-change-dropdown-view'
+
+ # filters
+ 'navigator/filters/base-filters',
+ 'navigator/filters/choice-filters',
+ 'navigator/filters/string-filters',
+ 'navigator/filters/date-filter-view',
+ 'coding-rules/views/filters/quality-profile-filter-view',
+ 'coding-rules/views/filters/inheritance-filter-view',
+ 'coding-rules/views/filters/activation-filter-view',
+ 'coding-rules/views/filters/characteristic-filter-view',
+
+ 'coding-rules/mockjax',
+ 'common/handlebars-extensions'
+], (
+ Backbone, Marionette,
+
+ CodingRulesLayout,
+ CodingRulesRouter,
+
+ # views
+ CodingRulesHeaderView,
+ CodingRulesActionsView,
+ CodingRulesFilterBarView,
+ CodingRulesListView,
+ CodingRulesBulkChangeView,
+ CodingRulesQualityProfileActivationView,
+ CodingRulesBulkChangeDropdownView
+
+ # filters
+ BaseFilters,
+ ChoiceFilters,
+ StringFilterView,
+ DateFilterView,
+ QualityProfileFilterView,
+ InheritanceFilterView
+ ActivationFilterView
+ CharacteristicFilterView
+) ->
+
+ # Create a generic error handler for ajax requests
+ jQuery.ajaxSetup
+ error: (jqXHR) ->
+ text = jqXHR.responseText
+ errorBox = jQuery('.modal-error')
+ if jqXHR.responseJSON?.errors?
+ text = _.pluck(jqXHR.responseJSON.errors, 'msg').join '. '
+ if errorBox.length > 0
+ errorBox.show().text text
+ else
+ alert text
+
+
+ # Add html class to mark the page as navigator page
+ jQuery('html').addClass('navigator-page coding-rules-page');
+
+
+ # Create an Application
+ App = new Marionette.Application
+
+
+ App.getQuery = ->
+ @filterBarView.getQuery()
+
+
+ App.restoreSorting = ->
+
+
+
+ App.storeQuery = (query, sorting) ->
+ if sorting
+ _.extend query,
+ sort: sorting.sort
+ asc: '' + sorting.asc
+ queryString = _.map query, (v, k) -> "#{k}=#{encodeURIComponent(v)}"
+ @router.navigate queryString.join('|'), replace: true
+
+
+
+ App.fetchList = (firstPage) ->
+ query = @getQuery()
+ fetchQuery = _.extend { pageIndex: @pageIndex }, query
+
+ if @codingRules.sorting
+ _.extend fetchQuery,
+ sort: @codingRules.sorting.sort,
+ asc: @codingRules.sorting.asc
+
+ @storeQuery query, @codingRules.sorting
+
+ @layout.showSpinner 'resultsRegion'
+ jQuery.ajax
+ url: "#{baseUrl}/api/codingrules/search"
+ data: fetchQuery
+ .done (r) =>
+ if firstPage
+ @codingRules.reset r.codingrules
+ else
+ @codingRules.add r.codingrules
+ @codingRules.paging = r.paging
+ @codingRulesListView = new CodingRulesListView
+ app: @
+ collection: @codingRules
+ @layout.resultsRegion.show @codingRulesListView
+ @codingRulesListView.selectFirst()
+
+
+
+ App.fetchFirstPage = ->
+ @pageIndex = 1
+ App.fetchList true
+
+
+ App.fetchNextPage = ->
+ if @pageIndex < @codingRules.paging.pages
+ @pageIndex++
+ App.fetchList false
+
+
+ App.getQualityProfile = ->
+ value = @qualityProfileFilter.get('value')
+ if value? && value.length == 1 then value[0] else null
+
+
+ # Construct layout
+ App.addInitializer ->
+ @layout = new CodingRulesLayout app: @
+ jQuery('body').append @layout.render().el
+
+
+ # Construct header
+ App.addInitializer ->
+ @codingRulesHeaderView = new CodingRulesHeaderView app: @
+ @layout.headerRegion.show @codingRulesHeaderView
+
+
+ # Define coding rules
+ App.addInitializer ->
+ @codingRules = new Backbone.Collection
+ @codingRules.sorting = sort: 'CREATION_DATE', asc: false
+
+
+ # Construct status bar
+ App.addInitializer ->
+ @codingRulesActionsView = new CodingRulesActionsView
+ app: @
+ collection: @codingRules
+ @layout.actionsRegion.show @codingRulesActionsView
+
+
+ # Construct bulk change views
+ App.addInitializer ->
+ @codingRulesBulkChangeView = new CodingRulesBulkChangeView app: @
+ @codingRulesBulkChangeDropdownView = new CodingRulesBulkChangeDropdownView app: @
+
+
+ # Construct quality profile activation view
+ App.addInitializer ->
+ @codingRulesQualityProfileActivationView = new CodingRulesQualityProfileActivationView app: @
+
+
+ # Define filters
+ App.addInitializer ->
+ @filters = new BaseFilters.Filters
+
+ @filters.add new BaseFilters.Filter
+ name: t 'coding_rules.filters.name'
+ property: 'name'
+ type: StringFilterView
+
+ @filters.add new BaseFilters.Filter
+ name: t 'coding_rules.filters.language'
+ property: 'languages'
+ type: ChoiceFilters.ChoiceFilterView
+ choices: @languages
+
+ @filters.add new BaseFilters.Filter
+ name: t 'coding_rules.filters.severity'
+ property: 'severities'
+ type: ChoiceFilters.ChoiceFilterView
+ choices:
+ 'BLOCKER': t 'severity.BLOCKER'
+ 'CRITICAL': t 'severity.CRITICAL'
+ 'MAJOR': t 'severity.MAJOR'
+ 'MINOR': t 'severity.MINOR'
+ 'INFO': t 'severity.INFO'
+ choiceIcons:
+ 'BLOCKER': 'severity-blocker'
+ 'CRITICAL': 'severity-critical'
+ 'MAJOR': 'severity-major'
+ 'MINOR': 'severity-minor'
+ 'INFO': 'severity-info'
+
+ @filters.add new BaseFilters.Filter
+ name: t 'coding_rules.filters.tag'
+ property: 'tags'
+ type: ChoiceFilters.ChoiceFilterView
+ choices: @tags
+
+ @filters.add new BaseFilters.Filter
+ name: t 'coding_rules.filters.characteristic'
+ property: 'characteristic'
+ type: CharacteristicFilterView
+ choices: @characteristics
+ multiple: false
+
+ @qualityProfileFilter = new BaseFilters.Filter
+ name: t 'coding_rules.filters.quality_profile'
+ property: 'quality_profile'
+ type: QualityProfileFilterView
+ multiple: false
+ @filters.add @qualityProfileFilter
+
+
+ @filters.add new BaseFilters.Filter
+ name: t 'coding_rules.filters.activation'
+ property: 'activation'
+ type: ActivationFilterView
+ enabled: false
+ optional: false
+ multiple: false
+ qualityProfileFilter: @qualityProfileFilter
+ choices:
+ 'active': t 'coding_rules.filters.activation.active'
+ 'inactive': t 'coding_rules.filters.activation.inactive'
+
+ @filters.add new BaseFilters.Filter
+ name: t 'coding_rules.filters.availableSince'
+ property: 'availableSince'
+ type: DateFilterView
+ enabled: false
+ optional: true
+
+ @filters.add new BaseFilters.Filter
+ name: t 'coding_rules.filters.description'
+ property: 'description'
+ type: StringFilterView
+ enabled: false
+ optional: true
+
+ @filters.add new BaseFilters.Filter
+ name: t 'coding_rules.filters.inheritance'
+ property: 'inheritance'
+ type: InheritanceFilterView
+ enabled: false
+ optional: true
+ multiple: false
+ qualityProfileFilter: @qualityProfileFilter
+ choices:
+ 'not_inhertited': t 'coding_rules.filters.inheritance.not_inherited'
+ 'inhertited': t 'coding_rules.filters.inheritance.inherited'
+ 'overriden': t 'coding_rules.filters.inheritance.overriden'
+
+ @filters.add new BaseFilters.Filter
+ name: t 'coding_rules.filters.key'
+ property: 'key'
+ type: StringFilterView
+ enabled: false
+ optional: true
+
+ @filters.add new BaseFilters.Filter
+ name: t 'coding_rules.filters.repository'
+ property: 'repositories'
+ type: ChoiceFilters.ChoiceFilterView
+ enabled: false
+ optional: true
+ choices: @repositories
+
+ @filters.add new BaseFilters.Filter
+ name: t 'coding_rules.filters.status'
+ property: 'statuses'
+ type: ChoiceFilters.ChoiceFilterView
+ enabled: false
+ optional: true
+ choices: @statuses
+
+
+ @filterBarView = new CodingRulesFilterBarView
+ app: @
+ collection: @filters,
+ extra: sort: '', asc: false
+ @layout.filtersRegion.show @filterBarView
+
+
+ # Start router
+ App.addInitializer ->
+ @router = new CodingRulesRouter app: @
+ Backbone.history.start()
+
+
+ # Call app before start the application
+ appXHR = jQuery.ajax
+ url: "#{baseUrl}/api/codingrules/app"
+
+ jQuery.when(appXHR)
+ .done (r) ->
+ App.appState = new Backbone.Model
+ App.state = new Backbone.Model
+ App.qualityProfiles = r.qualityprofiles
+ App.languages = r.languages
+ App.repositories = r.repositories
+ App.statuses = r.statuses
+ App.tags = r.tags
+ App.characteristics = r.characteristics
+ window.messages = r.messages
+
+ # Remove the initial spinner
+ jQuery('#coding-rules-page-loader').remove()
+
+ # Start the application
+ App.start() \ No newline at end of file
diff --git a/sonar-server/src/main/coffee/coding-rules/layout.coffee b/sonar-server/src/main/coffee/coding-rules/layout.coffee
new file mode 100644
index 00000000000..dbe8633a152
--- /dev/null
+++ b/sonar-server/src/main/coffee/coding-rules/layout.coffee
@@ -0,0 +1,29 @@
+define [
+ 'backbone.marionette',
+ 'templates/coding-rules'
+], (
+ Marionette,
+ Templates
+) ->
+
+ class AppLayout extends Marionette.Layout
+ className: 'navigator coding-rules-navigator'
+ template: Templates['coding-rules-layout']
+ spinner: '<i class="spinner"></i>'
+
+
+ regions:
+ headerRegion: '.navigator-header'
+ actionsRegion: '.navigator-actions'
+ resultsRegion: '.navigator-results'
+ detailsRegion: '.navigator-details'
+ filtersRegion: '.navigator-filters'
+
+
+ onRender: ->
+ # Adjust details region height
+ @$(@detailsRegion.el).css 'bottom', jQuery('#footer').outerHeight()
+
+
+ showSpinner: (region) ->
+ @$(@[region].el).html @spinner
diff --git a/sonar-server/src/main/coffee/coding-rules/mockjax.coffee b/sonar-server/src/main/coffee/coding-rules/mockjax.coffee
new file mode 100644
index 00000000000..2163fac2a64
--- /dev/null
+++ b/sonar-server/src/main/coffee/coding-rules/mockjax.coffee
@@ -0,0 +1,332 @@
+define ['jquery.mockjax'], ->
+
+ jQuery.mockjaxSettings.contentType = 'text/json';
+ jQuery.mockjaxSettings.responseTime = 250;
+
+ # GET /api/codingrules/app
+ jQuery.mockjax
+ url: "#{baseUrl}/api/codingrules/app"
+ responseText: JSON.stringify
+ qualityprofiles: [
+ { key: 'sonarway', name: 'Sonar Way', lang: 'Java', parent: null },
+ { key: 'qualityprofile1', name: 'Quality Profile 1', lang: 'Java', parent: 'sonarway' },
+ { key: 'qualityprofile2', name: 'Quality Profile 2', lang: 'JavaScript', parent: 'sonarway' },
+ { key: 'qualityprofile3', name: 'Quality Profile 3', lang: 'Java', parent: null },
+ ]
+ languages:
+ java: 'Java'
+ javascript: 'JavaScript'
+ repositories:
+ 'checkstyle': 'Checkstyle'
+ 'common-java': 'Common SonarQube'
+ 'findbugs': 'FindBugs'
+ 'pmd': 'PMD'
+ 'pmd-unit-tests': 'PMD Unit Tests'
+ 'squid': 'SonarQube'
+ statuses:
+ 'BETA': 'Beta'
+ 'DEPRECATED': 'Deprecated'
+ 'READY': 'Ready'
+ tags:
+ 'brain-overload': 'brain-overload'
+ 'bug': 'bug'
+ 'comment': 'comment'
+ 'convention': 'convention'
+ 'error-handling': 'error-handling'
+ 'formatting': 'formatting'
+ 'java8': 'java8'
+ 'multithreading': 'multithreading'
+ 'naming': 'naming'
+ 'pitfall': 'pitfall'
+ 'security': 'security'
+ 'size': 'size'
+ 'unused': 'unused'
+ 'unused-code': 'unused-code'
+ characteristics:
+ '1469': 'Changeability'
+ '1441': 'Changeability: Architecture related changeability'
+ '1470': 'Changeability: Data related changeability'
+ '1475': 'Changeability: Logic related changeability'
+ '1392': 'Efficiency'
+ '1377': 'Efficiency: Memory use'
+ '2965': 'Efficiency: Network use'
+ '1393': 'Efficiency: Processor use'
+ '1154': 'Maintainability'
+ '1022': 'Maintainability: Readability'
+ '1155': 'Maintainability: Understandability'
+ '988': 'Portability'
+ '977': 'Portability: Compiler related portability'
+ '989': 'Portability: Hardware related portability'
+ '994': 'Portability: Language related portability'
+ '1000': 'Portability: OS related portability'
+ '1006': 'Portability: Software related portability'
+ '1021': 'Portability: Time zone related portability'
+ '1551': 'Reliability'
+ '1496': 'Reliability: Architecture related reliability'
+ '1552': 'Reliability: Data related reliability'
+ '1596': 'Reliability: Exception handling'
+ '1622': 'Reliability: Fault tolerance'
+ '1629': 'Reliability: Instruction related reliability'
+ '1759': 'Reliability: Logic related reliability'
+ '2948': 'Reliability: Resource'
+ '1874': 'Reliability: Synchronization related reliability'
+ '1925': 'Reliability: Unit tests'
+ '975': 'Reusability'
+ '974': 'Reusability: Modularity'
+ '976': 'Reusability: Transportability'
+ '1345': 'Security'
+ '1335': 'Security: API abuse'
+ '1346': 'Security: Errors'
+ '1349': 'Security: Input validation and representation'
+ '1364': 'Security: Security features'
+ '1933': 'Testability'
+ '1932': 'Testability: Integration level testability'
+ '1934': 'Testability: Unit level testability'
+ messages:
+ 'all': 'All'
+ 'any': 'Any'
+ 'apply': 'Apply'
+ 'bold': 'Bold'
+ 'bulk_change': 'Bulk Change'
+ 'bulleted_point': 'Bulleted point'
+ 'cancel': 'Cancel'
+ 'change': 'Change'
+ 'code': 'Code'
+ 'delete': 'Delete'
+ 'done': 'Done'
+ 'edit': 'Edit'
+ 'markdown.helplink': 'Markdown Help'
+ 'moreCriteria': '+ More Criteria'
+ 'save': 'Save'
+ 'search_verb': 'Search'
+ 'severity': 'Severity'
+ 'update': 'Update'
+
+ 'severity.BLOCKER': 'Blocker'
+ 'severity.CRITICAL': 'Critical'
+ 'severity.MAJOR': 'Major'
+ 'severity.MINOR': 'Minor'
+ 'severity.INFO': 'Info'
+
+ 'coding_rules.activate': 'Activate'
+ 'coding_rules.activate_in': 'Activate In'
+ 'coding_rules.activate_in_quality_profile': 'Activate In Quality Profile'
+ 'coding_rules.add_note': 'Add Note'
+ 'coding_rules.available_since': 'Available Since'
+ 'coding_rules.bulk_change': 'Bulk Change'
+ 'coding_rules.change_severity': 'Change Severity'
+ 'coding_rules.change_severity_in': 'Change Severity In'
+ 'coding_rules.change_details': 'Change Details of Quality Profile'
+ 'coding_rules.extend_description': 'Extend Description'
+ 'coding_rules.deactivate_in': 'Deactivate In'
+ 'coding_rules.deactivate': 'Deactivate'
+ 'coding_rules.deactivate_in_quality_profile': 'Deactivate In Quality Profile'
+ 'coding_rules.found': 'Found'
+ 'coding_rules._inherits': 'inherits'
+ 'coding_rules.key': 'Key:'
+ 'coding_rules.new_search': 'New Search'
+ 'coding_rules.no_results': 'No Coding Rules'
+ 'coding_rules.order': 'Order'
+ 'coding_rules.ordered_by': 'Ordered By'
+ 'coding_rules.original': 'Original:'
+ 'coding_rules.page': 'Coding Rules'
+ 'coding_rules.parameters': 'Parameters'
+ 'coding_rules.parameters.default_value': 'Default Value:'
+ 'coding_rules.quality_profiles': 'Quality Profiles'
+ 'coding_rules.quality_profile': 'Quality Profile'
+ 'coding_rules.repository': 'Repository:'
+ 'coding_rules.revert_to_parent_definition': 'Revert to Parent Definition'
+ 'coding_rules._rules': 'rules'
+ 'coding_rules.select_tag': 'Select Tag'
+
+ 'coding_rules.filters.activation': 'Activation'
+ 'coding_rules.filters.activation.active': 'Active'
+ 'coding_rules.filters.activation.inactive': 'Inactive'
+ 'coding_rules.filters.activation.help': 'Activation criterion is available when a quality profile is selected'
+ 'coding_rules.filters.availableSince': 'Available Since'
+ 'coding_rules.filters.characteristic': 'Characteristic'
+ 'coding_rules.filters.description': 'Description'
+ 'coding_rules.filters.quality_profile': 'Quality Profile'
+ 'coding_rules.filters.inheritance': 'Inheritance'
+ 'coding_rules.filters.inheritance.inactive': 'Inheritance criterion is available when an inherited quality profile is selected'
+ 'coding_rules.filters.inheritance.not_inherited': 'Not Inherited'
+ 'coding_rules.filters.inheritance.inherited': 'Inherited'
+ 'coding_rules.filters.inheritance.overriden': 'Overriden'
+ 'coding_rules.filters.key': 'Key'
+ 'coding_rules.filters.language': 'Language'
+ 'coding_rules.filters.name': 'Name'
+ 'coding_rules.filters.repository': 'Repository'
+ 'coding_rules.filters.severity': 'Severity'
+ 'coding_rules.filters.status': 'Status'
+ 'coding_rules.filters.tag': 'Tag'
+
+ 'coding_rules.sort.creation_date': 'Creation Date'
+ 'coding_rules.sort.name': 'Name'
+
+
+ # GET /api/codingrules/search
+ jQuery.mockjax
+ url: "#{baseUrl}/api/codingrules/search"
+ responseText: JSON.stringify
+ codingrules: [
+ {
+ name: 'Array designators "[]" should be located after the type in method signatures'
+ language: 'Java'
+ severity: 'MAJOR'
+ status: 'DEPRECATED'
+ },
+ {
+ name: 'Avoid Array Loops'
+ language: 'Java'
+ severity: 'CRITICAL'
+ status: 'READY'
+ },
+ {
+ name: 'Bad practice - Abstract class defines covariant compareTo() method'
+ language: 'Java'
+ severity: 'MAJOR'
+ status: 'READY'
+ },
+ {
+ name: 'Correctness - Use of class without a hashCode() method in a hashed data structure'
+ language: 'Java'
+ severity: 'MINOR'
+ status: 'BETA'
+ },
+ {
+ name: 'Useless Operation On Immutable'
+ language: 'Java'
+ severity: 'MAJOR'
+ status: 'READY'
+ }
+ ]
+ paging:
+ total: 5
+ fTotal: '5'
+
+
+
+ # GET /api/codingrules/show
+ jQuery.mockjax
+ url: "#{baseUrl}/api/codingrules/show"
+ responseText: JSON.stringify
+ codingrule:
+ name: 'Array designators "[]" should be located after the type in method signatures'
+ language: 'Java'
+ tags: ['bug', 'comment', 'java8']
+ creationDate: '2013-10-15'
+ fCreationDate: 'Oct 15, 2013'
+ status: 'DEPRECATED'
+ repositoryName: 'SonarQube'
+ repositoryKey: 'squid'
+ characteristic: 'Reliability'
+ subcharacteristic: 'Data related reliability'
+ key: 'S1190'
+ parameters: [
+ { key: 'someParameter', type: 'INT', default: 4, description: 'Some parameter description' }
+ ]
+ description: '''
+ <p>
+ According to the Java Language Specification:
+ </p>
+
+ <pre>For compatibility with older versions of the Java SE platform,
+ the declaration of a method that returns an array is allowed to place (some or all of)
+ the empty bracket pairs that form the declaration of the array type after
+ the formal parameter list. This obsolescent syntax should not be used in new code.
+ </pre>
+
+ <p>The following code snippet illustrates this rule:</p>
+
+ <pre>public int getVector()[] { /* ... */ } // Non-Compliant
+
+ public int[] getVector() { /* ... */ } // Compliant
+
+ public int[] getMatrix()[] { /* ... */ } // Non-Compliant
+
+ public int[][] getMatrix() { /* ... */ } // Compliant
+ </pre>'''
+ extra: '''This note is here <b>only for test purposes</b>.'''
+ extraRaw: '''This note is here *only for test purposes*.'''
+
+ qualityProfiles: [
+ {
+ name: 'SonarWay'
+ key: 'sonarway'
+ severity: 'MINOR'
+ parameters: [
+ { key: 'someParameter', value: 8 }
+ ]
+ note:
+ username: 'Admin Admin'
+ html: '''<p>This note is here <b>only for test purposes</b>.</p>'''
+ raw: '''This note is here *only for test purposes*.'''
+ fCreationDate: 'less than a minute'
+ },
+ {
+ name: 'Quality Profile 1'
+ key: 'qualityprofile1'
+ severity: 'MAJOR'
+ parameters: [
+ { key: 'someParameter', value: 6 }
+ ]
+ inherits: 'sonarway'
+ }
+ ]
+
+
+
+ # POST /api/codingrules/extend_description
+ jQuery.mockjax
+ url: "#{baseUrl}/api/codingrules/extend_description"
+ responseText: JSON.stringify
+ extra: '''This note is here <i>only for test purposes</i>.'''
+ extraRaw: '''This note is here *only for test purposes*.'''
+
+
+ # POST /api/codingrules/bulk_change
+ jQuery.mockjax
+ url: "#{baseUrl}/api/codingrules/bulk_change"
+
+
+ # POST /api/codingrules/set_tags
+ jQuery.mockjax
+ url: "#{baseUrl}/api/codingrules/set_tags"
+
+
+ # POST /api/codingrules/activate
+ jQuery.mockjax
+ url: "#{baseUrl}/api/codingrules/activate"
+
+
+ # POST /api/codingrules/note
+ jQuery.mockjax
+ url: "#{baseUrl}/api/codingrules/note"
+ responseText: JSON.stringify
+ note:
+ username: 'Admin Admin'
+ html: '''<p>This note is here <b>only for test purposes</b>.</p>'''
+ raw: '''This note is here *only for test purposes*.'''
+ fCreationDate: 'less than a minute'
+
+
+ # GET /api/qualityprofiles/list
+ jQuery.mockjax
+ url: "#{baseUrl}/api/qualityprofiles/list"
+ responseText: JSON.stringify
+ more: false
+ results: [
+ { id: 'sonarway', text: 'Sonar Way', category: 'Java', parent: null },
+ { id: 'qp1', text: 'Quality Profile 1', category: 'Java', parent: 'sonarway' },
+ { id: 'qp2', text: 'Quality Profile 2', category: 'JavaScript', parent: 'sonarway' },
+ { id: 'qp3', text: 'Quality Profile 3', category: 'Java', parent: null },
+ ]
+
+
+ # GET /api/qualityprofiles/show
+ jQuery.mockjax
+ url: "#{baseUrl}/api/qualityprofiles/show"
+ responseText: JSON.stringify
+ qualityprofile:
+ id: 'sonarway', text: 'Sonar Way', category: 'Java', parent: null
+
diff --git a/sonar-server/src/main/coffee/coding-rules/router.coffee b/sonar-server/src/main/coffee/coding-rules/router.coffee
new file mode 100644
index 00000000000..1788ce1799e
--- /dev/null
+++ b/sonar-server/src/main/coffee/coding-rules/router.coffee
@@ -0,0 +1,36 @@
+define [
+ 'backbone',
+], (
+ Backbone,
+) ->
+
+ class AppRouter extends Backbone.Router
+
+ routes:
+ '': 'index'
+ ':query': 'index'
+
+
+ initialize: (options) ->
+ @app = options.app
+
+
+ parseQuery: (query, separator) ->
+ (query || '').split(separator || '|').map (t) ->
+ tokens = t.split('=')
+ key: tokens[0], value: decodeURIComponent(tokens[1])
+
+
+ emptyQuery: ->
+ @navigate '', trigger: true, replace: true
+
+
+ index: (query) ->
+ params = this.parseQuery(query)
+ @loadResults(params)
+
+
+ loadResults: (params) ->
+ @app.filterBarView.restoreFromQuery(params)
+ @app.restoreSorting(params)
+ @app.fetchFirstPage()
diff --git a/sonar-server/src/main/coffee/coding-rules/views/actions-view.coffee b/sonar-server/src/main/coffee/coding-rules/views/actions-view.coffee
new file mode 100644
index 00000000000..96e306ed8e8
--- /dev/null
+++ b/sonar-server/src/main/coffee/coding-rules/views/actions-view.coffee
@@ -0,0 +1,62 @@
+define [
+ 'backbone.marionette'
+ 'templates/coding-rules'
+], (
+ Marionette
+ Templates
+) ->
+
+ class CodingRulesStatusView extends Marionette.ItemView
+ template: Templates['coding-rules-actions']
+
+
+ collectionEvents:
+ 'all': 'render'
+
+
+ ui:
+ orderChoices: '.navigator-actions-order-choices'
+ bulkChange: '.navigator-actions-bulk'
+
+
+ events:
+ 'click .navigator-actions-order': 'toggleOrderChoices'
+ 'click @ui.orderChoices': 'sort'
+ 'click @ui.bulkChange': 'bulkChange'
+
+
+ onRender: ->
+ unless @collection.sorting.sortText
+ @collection.sorting.sortText = @$('[data-sort=' + @collection.sorting.sort + ']:first').text()
+ @render()
+
+
+ toggleOrderChoices: (e) ->
+ e.stopPropagation()
+ @ui.orderChoices.toggleClass 'open'
+ if @ui.orderChoices.is '.open'
+ jQuery('body').on 'click.coding_rules_actions', =>
+ @ui.orderChoices.removeClass 'open'
+
+
+ sort: (e) ->
+ e.stopPropagation()
+ @ui.orderChoices.removeClass 'open'
+ jQuery('body').off 'click.coding_rules_actions'
+ el = jQuery(e.target)
+ sort = el.data 'sort'
+ asc = el.data 'asc'
+ if sort != null && asc != null
+ @collection.sorting = sort: sort, sortText: el.text(), asc: asc
+ @options.app.fetchFirstPage()
+
+
+ bulkChange: (e) ->
+ e.stopPropagation()
+ @options.app.codingRulesBulkChangeDropdownView.toggle()
+
+
+ serializeData: ->
+ _.extend super,
+ paging: @collection.paging
+ sorting: @collection.sorting
diff --git a/sonar-server/src/main/coffee/coding-rules/views/coding-rules-bulk-change-dropdown-view.coffee b/sonar-server/src/main/coffee/coding-rules/views/coding-rules-bulk-change-dropdown-view.coffee
new file mode 100644
index 00000000000..881b70c6293
--- /dev/null
+++ b/sonar-server/src/main/coffee/coding-rules/views/coding-rules-bulk-change-dropdown-view.coffee
@@ -0,0 +1,51 @@
+define [
+ 'backbone.marionette',
+ 'templates/coding-rules'
+], (
+ Marionette,
+ Templates
+) ->
+
+ class CodingRulesBulkChangeDropdownView extends Marionette.ItemView
+ className: 'coding-rules-bulk-change-dropdown'
+ template: Templates['coding-rules-bulk-change-dropdown']
+
+
+ events:
+ 'click .coding-rules-bulk-change-dropdown-link': 'doAction'
+
+
+ doAction: (e) ->
+ action = jQuery(e.currentTarget).data 'action'
+ param = jQuery(e.currentTarget).data 'param'
+ unless param
+ @options.app.codingRulesBulkChangeView.show action
+ else
+ query = @options.app.getQuery()
+ switch action
+ when 'activate' then _.extend query, bulk_activate: [param]
+ when 'deactivate' then _.extend query, bulk_deactivate: [param]
+ @options.app.codingRulesBulkChangeView.bulkChange query
+
+
+ onRender: ->
+ jQuery('body').append @el
+ jQuery('body').off('click.bulk-change').on 'click.bulk-change', => @hide()
+
+
+ toggle: ->
+ if @$el.is(':visible') then @hide() else @show()
+
+
+ show: ->
+ @render()
+ @$el.show()
+
+
+ hide: ->
+ @$el.hide()
+
+
+ serializeData: ->
+ qualityProfile: @options.app.getQualityProfile()
+ qualityProfileName: @options.app.qualityProfileFilter.view.renderValue() \ No newline at end of file
diff --git a/sonar-server/src/main/coffee/coding-rules/views/coding-rules-bulk-change-view.coffee b/sonar-server/src/main/coffee/coding-rules/views/coding-rules-bulk-change-view.coffee
new file mode 100644
index 00000000000..144cbfafb37
--- /dev/null
+++ b/sonar-server/src/main/coffee/coding-rules/views/coding-rules-bulk-change-view.coffee
@@ -0,0 +1,103 @@
+define [
+ 'backbone.marionette',
+ 'templates/coding-rules'
+], (
+ Marionette,
+ Templates
+) ->
+
+ class CodingRulesBulkChangeView extends Marionette.ItemView
+ className: 'modal'
+ template: Templates['coding-rules-bulk-change']
+
+
+ events:
+ 'submit form': 'onSubmit'
+ 'click #coding-rules-cancel-bulk-change': 'hide'
+ 'change select': 'enableAction'
+
+
+ onRender: ->
+ @$el.dialog
+ dialogClass: 'no-close',
+ width: '600px',
+ draggable: false,
+ autoOpen: false,
+ modal: true,
+ minHeight: 50,
+ resizable: false,
+ title: null
+
+ @$('#coding-rules-bulk-change-activate-on, #coding-rules-bulk-change-deactivate-on').select2
+ width: '250px'
+ minimumResultsForSearch: 1
+
+ format = (state) ->
+ return state.text unless state.id
+ "<i class='icon-severity-#{state.id.toLowerCase()}'></i> #{state.text}"
+ @$('#coding-rules-bulk-change-severity').select2
+ width: '250px'
+ minimumResultsForSearch: 999
+ formatResult: format
+ formatSelection: format
+ escapeMarkup: (m) -> m
+
+
+ show: (action) ->
+ @action = action
+ @render()
+ @$el.dialog 'open'
+
+
+ hide: ->
+ @$el.dialog 'close'
+
+
+ prepareQuery: ->
+ query = @options.app.getQuery()
+
+ if @action == 'activate'
+ if @$('#coding-rules-bulk-change-activate-all').is ':checked'
+ _.extend query, bulk_activate: _.pluck @options.app.qualityProfiles, 'key'
+ else
+ _.extend query, bulk_activate: @$('#coding-rules-bulk-change-activate-on').val()
+
+ if @action == 'deactivate'
+ if @$('#coding-rules-bulk-change-deactivate-all').is ':checked'
+ _.extend query, bulk_deactivate: _.pluck @options.app.qualityProfiles, 'key'
+ else
+ _.extend query, bulk_deactivate: @$('#coding-rules-bulk-change-deactivate-on').val()
+
+ if @action == 'change-severity'
+ _.extend query, bulk_change_severity: @$('#coding-rules-bulk-change-severity').val()
+
+ query
+
+
+ bulkChange: (query) ->
+ jQuery.ajax
+ type: 'POST'
+ url: "#{baseUrl}/api/codingrules/bulk_change"
+ data: query
+ .done =>
+ @options.app.fetchFirstPage()
+
+
+ onSubmit: (e) ->
+ e.preventDefault()
+ @bulkChange(@prepareQuery()).done => @hide()
+
+
+ serializeData: ->
+ action: @action
+
+ paging: @options.app.codingRules.paging
+ qualityProfiles: @options.app.qualityProfiles
+
+ qualityProfile: @options.app.getQualityProfile()
+ qualityProfileName: @options.app.qualityProfileFilter.view.renderValue()
+
+ activateOnQualityProfiles: @options.app.qualityProfiles
+ deactivateOnQualityProfiles: _.reject @options.app.qualityProfiles, (q) => q.key == @options.app.getQualityProfile()
+
+ severities: ['BLOCKER', 'CRITICAL', 'MAJOR', 'MINOR', 'INFO']
diff --git a/sonar-server/src/main/coffee/coding-rules/views/coding-rules-detail-quality-profile-view.coffee b/sonar-server/src/main/coffee/coding-rules/views/coding-rules-detail-quality-profile-view.coffee
new file mode 100644
index 00000000000..063b8378929
--- /dev/null
+++ b/sonar-server/src/main/coffee/coding-rules/views/coding-rules-detail-quality-profile-view.coffee
@@ -0,0 +1,47 @@
+define [
+ 'backbone.marionette',
+ 'templates/coding-rules'
+], (
+ Marionette,
+ Templates
+) ->
+
+ class CodingRulesDetailQualityProfileView extends Marionette.ItemView
+ className: 'coding-rules-detail-quality-profile'
+ template: Templates['coding-rules-detail-quality-profile']
+
+
+ ui:
+ change: '.coding-rules-detail-quality-profile-change'
+
+
+ events:
+ 'click @ui.change': 'change'
+
+
+ change: ->
+ @options.app.codingRulesQualityProfileActivationView.model = @model
+ @options.app.codingRulesQualityProfileActivationView.show()
+
+
+ enableUpdate: ->
+ @ui.update.prop 'disabled', false
+
+
+ getParent: ->
+ return null unless @model.get 'inherits'
+ @options.qualityProfiles.findWhere(key: @model.get('inherits')).toJSON()
+
+
+ enhanceParameters: ->
+ parent = @getParent()
+ parameters = @model.get 'parameters'
+ return parameters unless parent
+ parameters.map (p) ->
+ _.extend p, original: _.findWhere(parent.parameters, key: p.key).value
+
+
+ serializeData: ->
+ _.extend super,
+ parent: @getParent()
+ parameters: @enhanceParameters() \ No newline at end of file
diff --git a/sonar-server/src/main/coffee/coding-rules/views/coding-rules-detail-quality-profiles-view.coffee b/sonar-server/src/main/coffee/coding-rules/views/coding-rules-detail-quality-profiles-view.coffee
new file mode 100644
index 00000000000..71b0b311062
--- /dev/null
+++ b/sonar-server/src/main/coffee/coding-rules/views/coding-rules-detail-quality-profiles-view.coffee
@@ -0,0 +1,14 @@
+define [
+ 'backbone.marionette'
+ 'coding-rules/views/coding-rules-detail-quality-profile-view'
+], (
+ Marionette,
+ CodingRulesDetailQualityProfileView
+) ->
+
+ class CodingRulesDetailQualityProfilesView extends Marionette.CollectionView
+ itemView: CodingRulesDetailQualityProfileView
+
+ itemViewOptions: ->
+ app: @options.app
+ qualityProfiles: @collection \ No newline at end of file
diff --git a/sonar-server/src/main/coffee/coding-rules/views/coding-rules-detail-view.coffee b/sonar-server/src/main/coffee/coding-rules/views/coding-rules-detail-view.coffee
new file mode 100644
index 00000000000..4cf361c7a5c
--- /dev/null
+++ b/sonar-server/src/main/coffee/coding-rules/views/coding-rules-detail-view.coffee
@@ -0,0 +1,131 @@
+define [
+ 'backbone',
+ 'backbone.marionette',
+ 'coding-rules/views/coding-rules-detail-quality-profiles-view'
+ 'templates/coding-rules'
+], (
+ Backbone,
+ Marionette,
+ CodingRulesDetailQualityProfilesView,
+ Templates
+) ->
+
+ class CodingRulesDetailView extends Marionette.Layout
+ template: Templates['coding-rules-detail']
+
+
+ regions:
+ qualityProfilesRegion: '#coding-rules-detail-quality-profiles'
+
+
+ ui:
+ tagsChange: '.coding-rules-detail-tags-change'
+ tagInput: '.coding-rules-detail-tag-input'
+ tagsEdit: '.coding-rules-detail-tag-edit'
+ tagsEditDone: '.coding-rules-detail-tag-edit-done'
+ tagsList: '.coding-rules-detail-tag-list'
+
+ descriptionExtra: '#coding-rules-detail-description-extra'
+ extendDescriptionLink: '#coding-rules-detail-extend-description'
+ extendDescriptionForm: '#coding-rules-detail-extend-description-form'
+ extendDescriptionSubmit: '#coding-rules-detail-extend-description-submit'
+ extendDescriptionText: '#coding-rules-detail-extend-description-text'
+ extendDescriptionSpinner: '#coding-rules-detail-extend-description-spinner'
+ cancelExtendDescription: '#coding-rules-detail-extend-description-cancel'
+
+ activateQualityProfile: '#coding-rules-quality-profile-activate'
+ changeQualityProfile: '.coding-rules-detail-quality-profile-update'
+
+
+ events:
+ 'click @ui.tagsChange': 'changeTags'
+ 'click @ui.tagsEditDone': 'editDone'
+
+ 'click @ui.extendDescriptionLink': 'showExtendDescriptionForm'
+ 'click @ui.cancelExtendDescription': 'hideExtendDescriptionForm'
+ 'click @ui.extendDescriptionSubmit': 'submitExtendDescription'
+
+ 'click @ui.activateQualityProfile': 'activateQualityProfile'
+ 'click @ui.changeQualityProfile': 'changeQualityProfile'
+
+
+ initialize: (options) ->
+ @qualityProfilesView = new CodingRulesDetailQualityProfilesView
+ app: @options.app
+ collection: new Backbone.Collection options.model.get 'qualityProfiles'
+
+
+ onRender: ->
+ @qualityProfilesRegion.show @qualityProfilesView
+
+ @ui.tagInput.select2
+ tags: _.difference @options.app.tags, @model.get 'tags'
+ width: '300px'
+ @ui.tagsEdit.hide()
+
+ @ui.extendDescriptionForm.hide()
+ @ui.extendDescriptionSpinner.hide()
+
+
+ changeTags: ->
+ @ui.tagsEdit.show()
+ @ui.tagsList.hide()
+
+
+ editDone: ->
+ @ui.tagsEdit.html '<i class="spinner"></i>'
+ tags = @ui.tagInput.val()
+ jQuery.ajax
+ type: 'POST'
+ url: "#{baseUrl}/api/codingrules/set_tags"
+ data: tags: tags
+ .done =>
+ @model.set 'tags', tags.split ','
+ @render()
+
+
+ showExtendDescriptionForm: ->
+ @ui.descriptionExtra.hide()
+ @ui.extendDescriptionForm.show()
+
+
+ hideExtendDescriptionForm: ->
+ @ui.descriptionExtra.show()
+ @ui.extendDescriptionForm.hide()
+
+
+ submitExtendDescription: ->
+ @ui.extendDescriptionForm.hide()
+ @ui.extendDescriptionSpinner.show()
+ jQuery.ajax
+ type: 'POST'
+ url: "#{baseUrl}/api/codingrules/extend_description"
+ dataType: 'json'
+ data: text: @ui.extendDescriptionText.val()
+ .done (r) =>
+ @model.set extra: r.extra, extraRaw: r.extraRaw
+ @render()
+
+
+ getContextQualilyProfile: ->
+ contextQualityProfile = @options.app.getQualityProfile()
+ _.findWhere @model.get('qualityProfiles'), key: contextQualityProfile
+
+
+ activateQualityProfile: ->
+ @options.app.codingRulesQualityProfileActivationView.model = null
+ @options.app.codingRulesQualityProfileActivationView.show()
+
+
+ changeQualityProfile: ->
+ @options.app.codingRulesQualityProfileActivationView.model = new Backbone.Model @getContextQualilyProfile()
+ @options.app.codingRulesQualityProfileActivationView.show()
+
+
+ serializeData: ->
+ contextQualityProfile = @options.app.getQualityProfile()
+
+ _.extend super,
+ contextQualityProfile: contextQualityProfile
+ contextQualityProfileName: @options.app.qualityProfileFilter.view.renderValue()
+ qualityProfile: @getContextQualilyProfile() \ No newline at end of file
diff --git a/sonar-server/src/main/coffee/coding-rules/views/coding-rules-list-empty-view.coffee b/sonar-server/src/main/coffee/coding-rules/views/coding-rules-list-empty-view.coffee
new file mode 100644
index 00000000000..c3eb8d48c4e
--- /dev/null
+++ b/sonar-server/src/main/coffee/coding-rules/views/coding-rules-list-empty-view.coffee
@@ -0,0 +1,12 @@
+define [
+ 'backbone.marionette',
+ 'templates/coding-rules'
+], (
+ Marionette,
+ Templates
+) ->
+
+ class CodingRulesListEmptyView extends Marionette.ItemView
+ tagName: 'li'
+ className: 'navigator-results-no-results'
+ template: Templates['coding-rules-list-empty']
diff --git a/sonar-server/src/main/coffee/coding-rules/views/coding-rules-list-item-view.coffee b/sonar-server/src/main/coffee/coding-rules/views/coding-rules-list-item-view.coffee
new file mode 100644
index 00000000000..69a9e5fdaff
--- /dev/null
+++ b/sonar-server/src/main/coffee/coding-rules/views/coding-rules-list-item-view.coffee
@@ -0,0 +1,34 @@
+define [
+ 'backbone.marionette',
+ 'coding-rules/views/coding-rules-detail-view',
+ 'templates/coding-rules'
+], (
+ Marionette,
+ CodingRulesDetailView,
+ Templates
+) ->
+
+ class CodingRulesListItemView extends Marionette.ItemView
+ tagName: 'li'
+ template: Templates['coding-rules-list-item']
+ activeClass: 'active'
+
+
+ events: ->
+ 'click': 'showDetail'
+
+
+ showDetail: ->
+ @$el.siblings().removeClass @activeClass
+ @$el.addClass @activeClass
+
+ @options.app.layout.showSpinner 'detailsRegion'
+ jQuery.ajax
+ url: "#{baseUrl}/api/codingrules/show"
+ .done (r) =>
+ @model.set r.codingrule
+ @options.app.codingRulesQualityProfileActivationView.rule = @model
+ detailView = new CodingRulesDetailView
+ app: @options.app
+ model: @model
+ @options.app.layout.detailsRegion.show detailView
diff --git a/sonar-server/src/main/coffee/coding-rules/views/coding-rules-list-view.coffee b/sonar-server/src/main/coffee/coding-rules/views/coding-rules-list-view.coffee
new file mode 100644
index 00000000000..93db305b2b5
--- /dev/null
+++ b/sonar-server/src/main/coffee/coding-rules/views/coding-rules-list-view.coffee
@@ -0,0 +1,23 @@
+define [
+ 'backbone.marionette',
+ 'coding-rules/views/coding-rules-list-item-view',
+ 'coding-rules/views/coding-rules-list-empty-view'
+], (
+ Marionette,
+ CodingRulesListItemView,
+ CodingRulesListEmptyView
+) ->
+
+ class CodingRulesListView extends Marionette.CollectionView
+ tagName: 'ol'
+ className: 'navigator-results-list'
+ itemView: CodingRulesListItemView,
+ emptyView: CodingRulesListEmptyView,
+
+
+ itemViewOptions: ->
+ listView: @, app: @options.app
+
+
+ selectFirst: ->
+ @$el.find('*:first').click()
diff --git a/sonar-server/src/main/coffee/coding-rules/views/coding-rules-quality-profile-activation-view.coffee b/sonar-server/src/main/coffee/coding-rules/views/coding-rules-quality-profile-activation-view.coffee
new file mode 100644
index 00000000000..70aa55a5daf
--- /dev/null
+++ b/sonar-server/src/main/coffee/coding-rules/views/coding-rules-quality-profile-activation-view.coffee
@@ -0,0 +1,85 @@
+define [
+ 'backbone.marionette',
+ 'templates/coding-rules'
+], (
+ Marionette,
+ Templates
+) ->
+
+ class CodingRulesQualityProfileActivationView extends Marionette.ItemView
+ className: 'modal'
+ template: Templates['coding-rules-quality-profile-activation']
+
+
+ ui:
+ qualityProfileSelect: '#coding-rules-quality-profile-activation-select'
+ qualityProfileSeverity: '#coding-rules-quality-profile-activation-severity'
+ qualityProfileActivate: '#coding-rules-quality-profile-activation-activate'
+
+
+ events:
+ 'click #coding-rules-quality-profile-activation-cancel': 'hide'
+ 'click @ui.qualityProfileActivate': 'activate'
+
+
+ activate: ->
+ @$('.modal-foot').html '<i class="spinner"></i>'
+ jQuery.ajax
+ type: 'POST'
+ url: "#{baseUrl}/api/codingrules/activate"
+ data: id: 1
+ .done =>
+ jQuery('.navigator-results-list .active').click()
+ @hide()
+
+
+ onRender: ->
+ @$el.dialog
+ dialogClass: 'no-close',
+ width: '600px',
+ draggable: false,
+ autoOpen: false,
+ modal: true,
+ minHeight: 50,
+ resizable: false,
+ title: null
+
+ @ui.qualityProfileSelect.select2
+ width: '250px'
+ minimumResultsForSearch: 5
+
+ format = (state) ->
+ return state.text unless state.id
+ "<i class='icon-severity-#{state.id.toLowerCase()}'></i> #{state.text}"
+
+ severity = if @model then @model.get 'severity' else @rule.get 'severity'
+ @ui.qualityProfileSeverity.val severity
+ @ui.qualityProfileSeverity.select2
+ width: '250px'
+ minimumResultsForSearch: 999
+ formatResult: format
+ formatSelection: format
+
+
+ show: ->
+ @render()
+ @$el.dialog 'open'
+
+
+ hide: ->
+ @$el.dialog 'close'
+
+
+ getAvailableQualityProfiles: ->
+ _.reject @options.app.qualityProfiles, (profile) =>
+ _.findWhere @rule.get('qualityProfiles'), key: profile.key
+
+
+ serializeData: ->
+ parameters = if @model then @model.get('parameters') else @rule.get('parameters')
+
+ _.extend super,
+ rule: @rule.toJSON()
+ parameters: parameters
+ qualityProfiles: @getAvailableQualityProfiles()
+ severities: ['BLOCKER', 'CRITICAL', 'MAJOR', 'MINOR', 'INFO']
diff --git a/sonar-server/src/main/coffee/coding-rules/views/filter-bar-view.coffee b/sonar-server/src/main/coffee/coding-rules/views/filter-bar-view.coffee
new file mode 100644
index 00000000000..6a719be85aa
--- /dev/null
+++ b/sonar-server/src/main/coffee/coding-rules/views/filter-bar-view.coffee
@@ -0,0 +1,71 @@
+define [
+ 'navigator/filters/filter-bar',
+ 'navigator/filters/base-filters',
+ 'navigator/filters/favorite-filters',
+ 'navigator/filters/more-criteria-filters',
+ 'templates/coding-rules'
+], (
+ FilterBarView,
+ BaseFilters,
+ FavoriteFiltersModule,
+ MoreCriteriaFilters,
+ Templates
+) ->
+
+ class CodingRulesFilterBarView extends FilterBarView
+ template: Templates['coding-rules-filter-bar']
+
+ collectionEvents:
+ 'change:enabled': 'changeEnabled'
+
+
+ events:
+ 'click .navigator-filter-submit': 'search'
+
+
+ getQuery: ->
+ query = {}
+ @collection.each (filter) ->
+ _.extend query, filter.view.formatValue()
+ query
+
+
+ onAfterItemAdded: (itemView) ->
+ if itemView.model.get('type') == FavoriteFiltersModule.FavoriteFilterView
+ jQuery('.navigator-header').addClass 'navigator-header-favorite'
+
+
+ addMoreCriteriaFilter: ->
+ disabledFilters = this.collection.where enabled: false
+ if disabledFilters.length > 0
+ @moreCriteriaFilter = new BaseFilters.Filter
+ type: MoreCriteriaFilters.MoreCriteriaFilterView,
+ enabled: true,
+ optional: false,
+ filters: disabledFilters
+ @collection.add @moreCriteriaFilter
+
+
+ changeEnabled: ->
+ if @moreCriteriaFilter?
+ disabledFilters = _.reject @collection.where(enabled: false), (filter) ->
+ filter.get('type') == MoreCriteriaFilters.MoreCriteriaFilterView
+
+ if disabledFilters.length == 0
+ @moreCriteriaFilter.set { enabled: false }, { silent: true }
+ else
+ @moreCriteriaFilter.set { enabled: true }, { silent: true }
+
+ @moreCriteriaFilter.set { filters: disabledFilters }, { silent: true }
+ @moreCriteriaFilter.trigger 'change:filters'
+
+
+ search: ->
+ @options.app.state.set
+ query: this.options.app.getQuery(),
+ search: true
+ @options.app.fetchFirstPage()
+
+
+ fetchNextPage: ->
+ @options.app.fetchNextPage()
diff --git a/sonar-server/src/main/coffee/coding-rules/views/filters/activation-filter-view.coffee b/sonar-server/src/main/coffee/coding-rules/views/filters/activation-filter-view.coffee
new file mode 100644
index 00000000000..896ee64c050
--- /dev/null
+++ b/sonar-server/src/main/coffee/coding-rules/views/filters/activation-filter-view.coffee
@@ -0,0 +1,41 @@
+define [
+ 'navigator/filters/choice-filters'
+ 'coding-rules/views/filters/inheritance-filter-view'
+], (
+ ChoiceFilters
+ InheritanceFilterView
+) ->
+
+ class DetailsActivationFilterView extends ChoiceFilters.DetailsChoiceFilterView
+
+ onCheck: (e) ->
+ id = jQuery(e.target).val()
+ selected = @options.filterView.choices.findWhere checked: true
+ unless id == selected
+ @options.filterView.choices.each (item) -> item.set 'checked', item.id == id
+ else
+ e.preventDefault()
+ @updateValue()
+ @updateLists()
+
+
+
+ class ActivationFilterView extends InheritanceFilterView
+ tooltip: 'coding_rules.filters.activation.help'
+
+
+ initialize: ->
+ super detailsView: DetailsActivationFilterView
+
+
+ onChangeQualityProfile: ->
+ qualityProfile = @qualityProfileFilter.get 'value'
+ if _.isArray(qualityProfile) && qualityProfile.length == 1 then @makeActive() else @makeInactive()
+
+
+ makeActive: ->
+ @choices.each (item) -> item.set 'checked', item.id == 'active'
+ @detailsView.updateValue()
+ @detailsView.updateLists()
+ @render()
+ super \ No newline at end of file
diff --git a/sonar-server/src/main/coffee/coding-rules/views/filters/characteristic-filter-view.coffee b/sonar-server/src/main/coffee/coding-rules/views/filters/characteristic-filter-view.coffee
new file mode 100644
index 00000000000..efa29a733ff
--- /dev/null
+++ b/sonar-server/src/main/coffee/coding-rules/views/filters/characteristic-filter-view.coffee
@@ -0,0 +1,12 @@
+define [
+ 'navigator/filters/choice-filters'
+], (
+ ChoiceFilters
+) ->
+
+ class CharacteriticFilterView extends ChoiceFilters.ChoiceFilterView
+
+ initialize: ->
+ super
+ @choices.comparator = 'text'
+ @choices.sort()
diff --git a/sonar-server/src/main/coffee/coding-rules/views/filters/inheritance-filter-view.coffee b/sonar-server/src/main/coffee/coding-rules/views/filters/inheritance-filter-view.coffee
new file mode 100644
index 00000000000..8e575c20268
--- /dev/null
+++ b/sonar-server/src/main/coffee/coding-rules/views/filters/inheritance-filter-view.coffee
@@ -0,0 +1,52 @@
+define [
+ 'navigator/filters/choice-filters'
+], (
+ ChoiceFilters
+) ->
+
+ class InheritanceFilterView extends ChoiceFilters.ChoiceFilterView
+ tooltip: 'coding_rules.filters.inheritance.inactive'
+
+
+ initialize: ->
+ super
+ @qualityProfileFilter = @model.get 'qualityProfileFilter'
+ @listenTo @qualityProfileFilter, 'change:value', @onChangeQualityProfile
+ @onChangeQualityProfile()
+
+
+ onChangeQualityProfile: ->
+ qualityProfile = @qualityProfileFilter.get 'value'
+ parentQualityProfile = @qualityProfileFilter.get 'parentQualityProfile'
+ if _.isArray(qualityProfile) && qualityProfile.length == 1 && parentQualityProfile
+ @makeActive()
+ else
+ @makeInactive()
+
+
+ makeActive: ->
+ @model.set inactive: false, title: ''
+ @model.trigger 'change:enabled'
+ @$el.removeClass('navigator-filter-inactive').prop 'title', ''
+
+
+ makeInactive: ->
+ @model.set inactive: true, title: t @tooltip
+ @model.trigger 'change:enabled'
+ @choices.each (model) -> model.set 'checked', false
+ @detailsView.updateLists()
+ @detailsView.updateValue()
+ @$el.addClass('navigator-filter-inactive').prop 'title', t @tooltip
+
+
+ showDetails: ->
+ super unless @$el.is '.navigator-filter-inactive'
+
+
+ restore: (value) ->
+ value = value.split(',') if _.isString(value)
+ if @choices && value.length > 0
+ @model.set value: value, enabled: true
+ @onChangeQualityProfile()
+ else
+ @clear()
diff --git a/sonar-server/src/main/coffee/coding-rules/views/filters/quality-profile-filter-view.coffee b/sonar-server/src/main/coffee/coding-rules/views/filters/quality-profile-filter-view.coffee
new file mode 100644
index 00000000000..eff55a9e618
--- /dev/null
+++ b/sonar-server/src/main/coffee/coding-rules/views/filters/quality-profile-filter-view.coffee
@@ -0,0 +1,51 @@
+define [
+ 'navigator/filters/ajax-select-filters'
+], (
+ AjaxSelectFilters
+) ->
+
+ class QualityProfileSuggestions extends AjaxSelectFilters.Suggestions
+
+ url: ->
+ "#{baseUrl}/api/qualityprofiles/list"
+
+
+
+ class QualityProfileFilterView extends AjaxSelectFilters.AjaxSelectFilterView
+
+ initialize: ->
+ super
+ @choices = new QualityProfileSuggestions
+ @listenTo @model, 'change:value', @onValueChange
+
+
+ onValueChange: ->
+ @updateParentQualityProfile()
+ @highlightContext()
+
+
+ updateParentQualityProfile: ->
+ selected = @getSelected()
+ if selected.length == 1
+ @model.set 'parentQualityProfile', selected[0].get('parent')
+ else
+ @model.unset 'parentQualityProfile'
+
+
+ highlightContext: ->
+ hasContext = _.isArray(@model.get('value')) && @model.get('value').length > 0
+ @$el.toggleClass 'navigator-filter-context', hasContext
+
+
+ createRequest: (v) ->
+ jQuery.ajax
+ url: baseUrl + '/api/qualityprofiles/show'
+ type: 'GET'
+ data: key: v
+ .done (r) =>
+ @choices.add new Backbone.Model
+ id: r.qualityprofile.id,
+ text: r.qualityprofile.text,
+ parent: r.qualityprofile.parent,
+ checked: true
+
diff --git a/sonar-server/src/main/coffee/coding-rules/views/header-view.coffee b/sonar-server/src/main/coffee/coding-rules/views/header-view.coffee
new file mode 100644
index 00000000000..ae0f8718732
--- /dev/null
+++ b/sonar-server/src/main/coffee/coding-rules/views/header-view.coffee
@@ -0,0 +1,18 @@
+define [
+ 'backbone.marionette',
+ 'templates/coding-rules'
+], (
+ Marionette,
+ Templates
+) ->
+
+ class CodingRulesHeaderView extends Marionette.ItemView
+ template: Templates['coding-rules-header']
+
+
+ events:
+ 'click #coding-rules-new-search': 'newSearch'
+
+
+ newSearch: ->
+ @options.app.router.navigate '', trigger: true