]> source.dussan.org Git - sonarqube.git/commitdiff
Merge remote-tracking branch 'origin/branch-4.4'
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Thu, 17 Jul 2014 07:19:57 +0000 (09:19 +0200)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Thu, 17 Jul 2014 07:19:57 +0000 (09:19 +0200)
Conflicts:
sonar-server/src/main/hbs/common/_markdown-tips.hbs
sonar-server/src/main/java/org/sonar/server/test/ws/TestsShowAction.java

1  2 
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileService.java
server/sonar-web/src/main/coffee/coding-rules/views/filter-bar-view.coffee
server/sonar-web/src/main/webapp/WEB-INF/app/controllers/api/profiles_controller.rb
server/sonar-web/src/main/webapp/WEB-INF/app/views/profiles/index.html.erb

index 7f496b39ea3518dc9bfd787d8e8ac94462db388a,0000000000000000000000000000000000000000..2fb0b470d1939a2744f7e7443ccd171dcb80fda1
mode 100644,000000..100644
--- /dev/null
@@@ -1,233 -1,0 +1,237 @@@
-   public String getDefault(String language) {
-     QualityProfileDto profile = factory.getDefault(language);
-     return profile != null ? profile.getKey() : null;
 +/*
 + * SonarQube, open source software quality management tool.
 + * Copyright (C) 2008-2014 SonarSource
 + * mailto:contact AT sonarsource DOT com
 + *
 + * SonarQube 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.
 + *
 + * SonarQube 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.
 + */
 +package org.sonar.server.qualityprofile;
 +
 +import org.elasticsearch.action.search.SearchResponse;
 +import org.elasticsearch.index.query.FilterBuilders;
 +import org.elasticsearch.index.query.OrFilterBuilder;
 +import org.elasticsearch.index.query.QueryBuilders;
 +import org.elasticsearch.search.SearchHit;
 +import org.sonar.api.ServerComponent;
 +import org.sonar.core.permission.GlobalPermissions;
 +import org.sonar.core.persistence.DbSession;
 +import org.sonar.core.qualityprofile.db.ActiveRuleKey;
 +import org.sonar.core.qualityprofile.db.QualityProfileDto;
 +import org.sonar.core.rule.RuleDto;
 +import org.sonar.core.user.UserDto;
 +import org.sonar.server.activity.index.ActivityIndex;
 +import org.sonar.server.db.DbClient;
 +import org.sonar.server.rule.index.RuleQuery;
 +import org.sonar.server.search.IndexClient;
 +import org.sonar.server.search.QueryOptions;
 +import org.sonar.server.search.Result;
 +import org.sonar.server.user.UserSession;
 +
 +import javax.annotation.CheckForNull;
 +import javax.annotation.Nullable;
 +
 +import java.io.Reader;
 +import java.io.StringReader;
 +import java.io.StringWriter;
 +import java.io.Writer;
 +import java.util.Collection;
 +import java.util.List;
 +
 +public class QProfileService implements ServerComponent {
 +
 +  private final DbClient db;
 +  private final IndexClient index;
 +  private final RuleActivator ruleActivator;
 +  private final QProfileFactory factory;
 +  private final QProfileBackuper backuper;
 +  private final QProfileCopier copier;
 +  private final QProfileReset reset;
 +
 +  public QProfileService(DbClient db, IndexClient index, RuleActivator ruleActivator, QProfileFactory factory,
 +    QProfileBackuper backuper, QProfileCopier copier, QProfileReset reset) {
 +    this.db = db;
 +    this.index = index;
 +    this.ruleActivator = ruleActivator;
 +    this.factory = factory;
 +    this.backuper = backuper;
 +    this.copier = copier;
 +    this.reset = reset;
 +  }
 +
 +  public QualityProfileDto create(QProfileName name) {
 +    verifyAdminPermission();
 +    DbSession dbSession = db.openSession(false);
 +    try {
 +      QualityProfileDto profile = factory.create(dbSession, name);
 +      dbSession.commit();
 +      return profile;
 +    } finally {
 +      dbSession.close();
 +    }
 +  }
 +
 +  /**
 +   * Activate a rule on a Quality profile. Update configuration (severity/parameters) if the rule is already
 +   * activated.
 +   */
 +  public List<ActiveRuleChange> activate(String profileKey, RuleActivation activation) {
 +    verifyAdminPermission();
 +    DbSession dbSession = db.openSession(false);
 +    try {
 +      List<ActiveRuleChange> changes = ruleActivator.activate(dbSession, activation, profileKey);
 +      dbSession.commit();
 +      return changes;
 +    } finally {
 +      dbSession.close();
 +    }
 +  }
 +
 +  /**
 +   * Deactivate a rule on a Quality profile. Does nothing if the rule is not activated, but
 +   * fails if the rule or the profile does not exist.
 +   */
 +  public List<ActiveRuleChange> deactivate(ActiveRuleKey key) {
 +    verifyAdminPermission();
 +    return ruleActivator.deactivate(key);
 +  }
 +
 +  public BulkChangeResult bulkActivate(RuleQuery ruleQuery, String profile, @Nullable String severity) {
 +    verifyAdminPermission();
 +    return ruleActivator.bulkActivate(ruleQuery, profile, severity);
 +  }
 +
 +  public BulkChangeResult bulkDeactivate(RuleQuery ruleQuery, String profile) {
 +    verifyAdminPermission();
 +    return ruleActivator.bulkDeactivate(ruleQuery, profile);
 +  }
 +
 +  public void backup(String profileKey, Writer writer) {
 +    // Allowed to non-admin users (see http://jira.codehaus.org/browse/SONAR-2039)
 +    backuper.backup(profileKey, writer);
 +  }
 +
 +  /**
 +   * @deprecated used only by Ruby on Rails. Use {@link #backup(String, java.io.Writer)}
 +   */
 +  @Deprecated
 +  public String backup(String profileKey) {
 +    StringWriter output = new StringWriter();
 +    backup(profileKey, output);
 +    return output.toString();
 +  }
 +
 +  public void restore(Reader backup) {
 +    verifyAdminPermission();
 +    backuper.restore(backup, null);
 +  }
 +
 +  /**
 +   * @deprecated used only by Ruby on Rails. Use {@link #restore(java.io.Reader)}
 +   */
 +  @Deprecated
 +  public void restore(String backup) {
 +    restore(new StringReader(backup));
 +  }
 +
 +  public void restoreBuiltInProfilesForLanguage(String lang) {
 +    verifyAdminPermission();
 +    reset.resetLanguage(lang);
 +  }
 +
 +  /**
 +   * Currently used by Ruby on Rails
 +   */
 +  public Collection<String> builtInProfileNamesForLanguage(String lang) {
 +    return reset.builtInProfileNamesForLanguage(lang);
 +  }
 +
 +  public void copyToName(String fromKey, String toName) {
 +    verifyAdminPermission();
 +    copier.copyToName(fromKey, toName);
 +  }
 +
 +  public void delete(String key) {
 +    verifyAdminPermission();
 +    factory.delete(key);
 +  }
 +
 +  public void rename(String key, String newName) {
 +    verifyAdminPermission();
 +    factory.rename(key, newName);
 +  }
 +
 +  /**
 +   * Set or unset parent profile.
 +   *
 +   * @param key       key of existing profile
 +   * @param parentKey key of parent profile to be inherited from. Or <code>null</code> to unset the parent.
 +   */
 +  public void setParent(String key, @Nullable String parentKey) {
 +    verifyAdminPermission();
 +    ruleActivator.setParent(key, parentKey);
 +  }
 +
 +  /**
 +   * Set the given quality profile as default for the related language
 +   */
 +  public void setDefault(String key) {
 +    verifyAdminPermission();
 +    factory.setDefault(key);
 +  }
 +
++  /**
++   * Used in /api/profiles and in /profiles/export
++   * @param language
++   * @return
++   */
 +  @CheckForNull
++  public QualityProfileDto getDefault(String language) {
++    return factory.getDefault(language);
 +  }
 +
 +  private void verifyAdminPermission() {
 +    UserSession.get().checkLoggedIn();
 +    UserSession.get().checkGlobalPermission(GlobalPermissions.QUALITY_PROFILE_ADMIN);
 +  }
 +
 +  public Result<QProfileActivity> searchActivities(QProfileActivityQuery query, QueryOptions options) {
 +    DbSession session = db.openSession(false);
 +    try {
 +      OrFilterBuilder activityFilter = FilterBuilders.orFilter();
 +      for (String profileKey : query.getQprofileKeys()) {
 +        activityFilter.add(FilterBuilders.nestedFilter("details",
 +          QueryBuilders.matchQuery("details.profileKey", profileKey)));
 +      }
 +
 +      SearchResponse response = index.get(ActivityIndex.class).search(query, options, activityFilter);
 +      Result<QProfileActivity> result = new Result<QProfileActivity>(response);
 +      for (SearchHit hit : response.getHits().getHits()) {
 +        QProfileActivity profileActivity = new QProfileActivity(hit.getSource());
 +        RuleDto ruleDto = db.ruleDao().getNullableByKey(session, profileActivity.ruleKey());
 +        profileActivity.ruleName(ruleDto != null ? ruleDto.getName() : null);
 +
 +        String login = profileActivity.login();
 +        if (login != null) {
 +          UserDto user = db.userDao().selectActiveUserByLogin(login, session);
 +          profileActivity.authorName(user != null ? user.getName() : null);
 +        }
 +        result.getHits().add(profileActivity);
 +      }
 +      return result;
 +    } finally {
 +      session.close();
 +    }
 +  }
 +}
index 84641dd998e727fe72ad4525d545d859fa019e54,0000000000000000000000000000000000000000..4c9c455f2ca20ba1a3b22196b69a9a3da60cc25a
mode 100644,000000..100644
--- /dev/null
@@@ -1,76 -1,0 +1,79 @@@
-       disabledFilters = this.collection.where enabled: false
 +define [
 +  'navigator/filters/filter-bar',
 +  'navigator/filters/base-filters',
 +  'navigator/filters/favorite-filters',
 +  'navigator/filters/more-criteria-filters',
++  'navigator/filters/read-only-filters',
 +  'templates/coding-rules'
 +], (
 +  FilterBarView,
 +  BaseFilters,
 +  FavoriteFiltersModule,
 +  MoreCriteriaFilters,
++  ReadOnlyFilterView,
 +  Templates
 +) ->
 +
 +  class CodingRulesFilterBarView extends FilterBarView
 +    template: Templates['coding-rules-filter-bar']
 +
 +    collectionEvents:
 +      'change:enabled': 'changeEnabled'
 +
 +
 +    events:
 +      'click .navigator-filter-submit': 'search'
 +
 +
 +    onRender: ->
 +      @selectFirst()
 +
 +
 +    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: ->
-           filter.get('type') == MoreCriteriaFilters.MoreCriteriaFilterView
++      readOnlyFilters = @collection.where(type: ReadOnlyFilterView)
++      disabledFilters = _.difference(@collection.where(enabled: false), readOnlyFilters)
 +      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') in [MoreCriteriaFilters.MoreCriteriaFilterView, ReadOnlyFilterView]
 +
 +        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: ->
 +      @$('.navigator-filter-submit').blur()
 +      @options.app.state.set
 +        query: this.options.app.getQuery(),
 +        search: true
 +      @options.app.fetchFirstPage()
 +
 +
 +    fetchNextPage: ->
 +      @options.app.fetchNextPage()
index d3fecdb935e951c6787ba8d2d6178db88bc00fdf,0000000000000000000000000000000000000000..5897a32b2227879bddf1f31b1b9c1f6ce563dbfd
mode 100644,000000..100644
--- /dev/null
@@@ -1,251 -1,0 +1,251 @@@
-         default_profile_by_language[language] = Internal.quality_profiles.defaultProfile(language)
 +#
 +# SonarQube, open source software quality management tool.
 +# Copyright (C) 2008-2014 SonarSource
 +# mailto:contact AT sonarsource DOT com
 +#
 +# SonarQube 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.
 +#
 +# SonarQube 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.
 +#
 +require 'json'
 +
 +class Api::ProfilesController < Api::ApiController
 +
 +  # GET /api/profiles/list?[language=<language][&project=<project id or key>]
 +  #
 +  # Since v.3.3
 +  #
 +  # ==== Examples
 +  # - get all the profiles : GET /api/profiles/list
 +  # - get all the Java profiles : GET /api/profiles/list?language=java
 +  # - get the profiles used by the project 'foo' : GET /api/profiles/list?project=foo
 +  # - get the Java profile used by the project 'foo' : GET /api/profiles/list?project=foo&language=java
 +  def list
 +    language = params[:language]
 +    project_key = params[:project]
 +
 +    profiles = []
 +    default_profile_by_language = {}
 +    if project_key.present?
 +      project = Project.by_key(project_key)
 +      not_found('Unknown project') unless project
 +      if language.present?
-           default_profile_by_language[language.getKey()] = Internal.quality_profiles.defaultProfile(language.getKey())
++        default_profile_by_language[language] = Internal.qprofile_service.getDefault(language)
 +        profile = Internal.quality_profiles.findProfileByProjectAndLanguage(project.id, language)
 +        profiles << profile if profile
 +        # Return default profile if the project is not associate to a profile
 +        profiles << default_profile_by_language[language] unless profile
 +      else
 +        Api::Utils.languages.each do |language|
-         default_profile_by_language[lang] = Internal.quality_profiles.defaultProfile(lang.to_s)
++          default_profile_by_language[language.getKey()] = Internal.qprofile_service.getDefault(language.getKey())
 +          profile = Internal.quality_profiles.findProfileByProjectAndLanguage(project.id, language.getKey())
 +          profiles << profile if profile
 +          # Return default profile if the project is not associate to a profile
 +          profiles << default_profile_by_language[language.getKey()] unless profile
 +        end
 +      end
 +    elsif language.present?
 +      profiles = Internal.quality_profiles.profilesByLanguage(language).to_a
 +    else
 +      profiles = Internal.quality_profiles.allProfiles().to_a
 +    end
 +
 +    # Populate the map of default profile by language by searching for all profiles languages
 +    # We have to do that as the profiles list do not contain this information (maybe we should add it?)
 +    profiles.each do |p|
 +      lang = p.language
 +      unless default_profile_by_language[lang]
-     profile_key=Java::OrgSonarCoreQualityprofileDb::QualityProfileKey.of(params[:name], params[:language])
++        default_profile_by_language[lang] = Internal.qprofile_service.getDefault(lang.to_s)
 +      end
 +    end
 +
 +    json = profiles.compact.map { |profile| {:name => profile.name, :language => profile.language, :default => default_profile_by_language[profile.language].name == profile.name } }
 +    respond_to do |format|
 +      format.json { render :json => jsonp(json) }
 +      format.xml { render :xml => xml_not_supported }
 +      format.text { render :text => text_not_supported }
 +    end
 +  end
 +
 +  # POST /api/profiles/destroy?language=<language>&name=<name>
 +  def destroy
 +    verify_post_request
 +    access_denied unless has_role?(:profileadmin)
 +    require_parameters :language, :name
 +
-       Internal.component(Java::OrgSonarServerQualityprofile::QProfileService.java_class).delete(profile_key)
 +    call_backend do
-     profile_key=Java::OrgSonarCoreQualityprofileDb::QualityProfileKey.of(profile.name, profile.language)
-     Internal.component(Java::OrgSonarServerQualityprofile::QProfileService.java_class).setDefault(profile_key)
++      profile = Internal.quality_profiles.profile(params[:name], params[:language])
++      not_found('Profile not found') unless profile
++      Internal.component(Java::OrgSonarServerQualityprofile::QProfileService.java_class).delete(profile.key)
 +    end
 +    render_success('Profile destroyed')
 +  end
 +
 +  # POST /api/profiles/set_as_default?language=<language>&name=<name>
 +  #
 +  # Since v.3.3
 +  def set_as_default
 +    verify_post_request
 +    profile = Internal.quality_profiles.profile(params[:name], params[:language])
 +    not_found('Profile not found') unless profile
-       profile = Internal.quality_profiles.defaultProfile(language)
++    Internal.component(Java::OrgSonarServerQualityprofile::QProfileService.java_class).setDefault(profile.key)
 +    render_success
 +  end
 +
 +  # GET /api/profiles?language=<language>[&name=<name>]
 +  def index
 +    require_parameters :language
 +
 +    language=params[:language]
 +    name=params[:name]
 +    if name.blank?
 +      @profile=Profile.by_default(language)
 +    else
 +      @profile=Profile.find_by_name_and_language(name, language)
 +    end
 +    not_found('Profile not found') unless @profile
 +
 +    @active_rules=filter_rules()
 +
 +    respond_to do |format|
 +      format.json { render :json => jsonp(to_json) }
 +      format.xml { render :xml => to_xml }
 +      format.text { render :text => text_not_supported }
 +    end
 +  end
 +
 +  # Backup a profile. If output format is xml, then backup is directly returned.
 +  # GET /api/profiles/backup?language=<language>[&name=my_profile] -v
 +  def backup
 +    require_parameters :language
 +
 +    language = params[:language]
 +    if (params[:name].blank?)
-     profile_key=Java::OrgSonarCoreQualityprofileDb::QualityProfileKey.of(profile.name, profile.language)
-     backup = Internal.component(Java::OrgSonarServerQualityprofile::QProfileService.java_class).backup(profile_key)
++      profile = Internal.qprofile_service.getDefault(language)
 +    else
 +      profile = Internal.quality_profiles.profile(params[:name], params[:language])
 +    end
 +
-       xml.parent(@profile.parent_name) if @profile.parent_name.present?
++    not_found('Profile not found') unless profile
++    backup = Internal.component(Java::OrgSonarServerQualityprofile::QProfileService.java_class).backup(profile.key)
 +
 +    respond_to do |format|
 +      format.xml { render :xml => backup }
 +      format.json { render :json => jsonp({:backup => backup}) }
 +    end
 +  end
 +
 +  # Restore a profile backup.
 +  # curl -X POST -u admin:admin -F 'backup=<my>backup</my>' -v http://localhost:9000/api/profiles/restore
 +  # curl -X POST -u admin:admin -F 'backup=@backup.xml' -v http://localhost:9000/api/profiles/restore
 +  def restore
 +    verify_post_request
 +    require_parameters :backup
 +
 +    backup = Api::Utils.read_post_request_param(params[:backup])
 +    Internal.component(Java::OrgSonarServerQualityprofile::QProfileService.java_class).restore(backup)
 +
 +    respond_to do |format|
 +      #TODO format.json { render :json => jsonp(validation_result_to_json(result)), :status => 200 }
 +      format.json { render :json => jsonp({}), :status => 200 }
 +    end
 +  end
 +
 +  private
 +
 +  def validation_messages_to_json(messages)
 +    hash={}
 +    hash[:errors]=messages.getErrors().to_a.map { |message| message }
 +    hash[:warnings]=messages.getWarnings().to_a.map { |message| message }
 +    hash[:infos]=messages.getInfos().to_a.map { |message| message }
 +    hash
 +  end
 +
 +  def validation_result_to_json(result)
 +    hash={}
 +    hash[:warnings]=result.warnings().to_a.map { |message| message }
 +    hash[:infos]=result.infos().to_a.map { |message| message }
 +    hash
 +  end
 +
 +  def filter_rules
 +    conditions=['active_rules.profile_id=?']
 +    condition_values=[@profile.id]
 +
 +    if params[:rule_repositories].present?
 +      conditions<<'rules.plugin_name in (?)'
 +      condition_values<<params[:rule_repositories].split(',')
 +    end
 +
 +    if params[:rule_severities].present?
 +      conditions<<'failure_level in (?)'
 +      condition_values<<params[:rule_severities].split(',').map { |severity| Sonar::RulePriority.id(severity) }
 +    end
 +
 +    ActiveRule.find(:all, :include => [:rule, {:active_rule_parameters => :rules_parameter}], :conditions => [conditions.join(' AND ')].concat(condition_values))
 +  end
 +
 +  def to_json
 +    result={}
 +    result[:name]=@profile.name
 +    result[:language]=@profile.language
 +    result[:parent]=@profile.parent_kee if @profile.parent_kee.present?
 +    result[:default]=@profile.default_profile?
 +
 +    rules=[]
 +    @active_rules.each do |active_rule|
 +      hash={}
 +      hash[:key]=active_rule.rule.plugin_rule_key
 +      hash[:repo]=active_rule.rule.plugin_name
 +      hash[:severity]=active_rule.priority_text
 +      hash[:inheritance]=active_rule.inheritance if active_rule.inheritance
 +      params_hash=[]
 +      active_rule.active_rule_parameters.each do |param|
 +        params_hash<<{:key => param.name, :value => param.value}
 +      end
 +      hash[:params]=params_hash unless params_hash.empty?
 +      rules<<hash
 +    end
 +    result[:rules]=rules unless rules.empty?
 +
 +    [result]
 +  end
 +
 +  def to_xml
 +    xml = Builder::XmlMarkup.new(:indent => 0)
 +    xml.instruct!
 +
 +    xml.profile do
 +      xml.name(@profile.name)
 +      xml.language(@profile.language)
++      xml.parent(@profile.parent_kee) if @profile.parent_kee.present?
 +      xml.default(@profile.default_profile?)
 +
 +      @active_rules.each do |active_rule|
 +        xml.rule do
 +          xml.key(active_rule.rule.plugin_rule_key)
 +          xml.repo(active_rule.rule.plugin_name)
 +          xml.severity(active_rule.priority_text)
 +          xml.inheritance(active_rule.inheritance) if active_rule.inheritance
 +          active_rule.active_rule_parameters.each do |param|
 +            xml.param do
 +              xml.key(param.name)
 +              xml.value(param.value)
 +            end
 +          end
 +        end
 +      end
 +    end
 +  end
 +
 +end
index ab14ed861ddca1a8d2dc04468f422eb3f6c66162,0000000000000000000000000000000000000000..e8a77e22cd98952f77525cc036be3e710a2cca64
mode 100644,000000..100644
--- /dev/null
@@@ -1,132 -1,0 +1,132 @@@
-        is_default_profile = default_profile && default_profile==profile.key()
 +<div class="page">
 +<div class="line-block marginbottom10">
 +  <ul style="float: right" class="operations">
 +    <li>
 +      <i class="icon-compare"></i>
 +      <a href="profiles/compare" id="compare-link"><%= message('quality_profiles.compare_profiles') -%></a>
 +    </li>
 +    <% if profiles_administrator? %>
 +      <li class="last">
 +        <i class="icon-restore"></i>
 +        <a href="profiles/restore_form" class="open-modal" id="restore-link"><%= message('quality_profiles.restore_profile') -%></a>
 +      </li>
 +    <% end %>
 +  </ul>
 +</div>
 +
 +<%
 +   Api::Utils.insensitive_sort(languages){|l| l.getName()}.each do |language|
 +      default_profile = Internal.component(Java::OrgSonarServerQualityprofile::QProfileService.java_class).getDefault(language.getKey())
 +%>
 +  <div class="line-block">
 +    <% if profiles_administrator? %>
 +      <ul style="float: right" class="horizontal">
 +        <li class="marginleft10">
 +          <i class="icon-plus"></i>
 +          <a id="create-link-<%= language.getKey() -%>" href="<%= ApplicationController.root_context -%>/profiles/create_form?language=<%= u language.getKey() -%>"
 +             class="open-modal link-action"><%= message('create') -%></a>
 +        </li>
 +      </ul>
 +      <ul style="float: right" class="horizontal">
 +        <li class="marginleft10">
 +          <a id="create-link-<%= language.getKey() -%>" href="<%= ApplicationController.root_context -%>/profiles/restore_built_in_form?language=<%= u language.getKey() -%>"
 +             class="open-modal link-action"><%= message('quality_profiles.restore_built_in_profiles') -%></a>
 +        </li>
 +      </ul>
 +    <% end %>
 +    <h2><%= message('quality_profiles.x_language_profiles', :params => language.getName()) -%></h2>
 +  </div>
 +
 +  <table class="data width100" id="profiles_<%= language.getKey() -%>">
 +    <thead>
 +    <tr>
 +      <th class="left"><%= message('name') -%></th>
 +      <th class="right"><%= message('rules') -%></th>
 +      <th class="right"><%= message('projects') -%></th>
 +      <th class="right"><%= message('default') -%></th>
 +      <% if profiles_administrator? %>
 +        <th width="1%" class="right" colspan="4"><%= message('operations') -%></th>
 +      <% end %>
 +    </tr>
 +    </thead>
 +    <tbody>
 +    <% @profiles.select { |p| p.language == language.getKey() }.each do |profile|
 +       projects_count = projects_count(profile)
++       is_default_profile = default_profile && default_profile.key()==profile.key()
 +    %>
 +      <tr class="<%= cycle 'even', 'odd', :name => language.getKey() -%> hoverable" id="<%= u profile.key() %>">
 +        <td  width="40%">
 +          <a href="<%= url_for :controller => 'profiles', :action => 'show', :key => profile.key() -%>"
 +             id="rules-<%= profile.key() -%>"><%= h profile.name() -%></a>
 +        </td>
 +
 +        <td align="right" width="10%">
 +          <%
 +             rules_tooltip = message('quality_profiles.see_rules_tooltip_x_profile', :params => [profile.name()])
 +             rules_tooltip = message('quality_profiles.manage_rules_tooltip_x_profile', :params => [profile.name()]) if profiles_administrator?
 +          %>
 +          <a class="widget-link"
 +             href="<%= "#{ApplicationController.root_context}/coding_rules#qprofile=#{profile.key()}|activation=true|languages=#{profile.language()}" -%>"
 +             title="<%= rules_tooltip %>">
 +            <span id="activated_rules_<%= u profile.key() -%>">
 +            <%= @active_rule_counts[profile.key()] || 0 -%>
 +          </span>
 +          </a>
 +        </td>
 +
 +        <td align="right" width="10%" nowrap>
 +          <% unless is_default_profile %>
 +            <span id="projects_<%= u profile.key() -%>"><%= projects_count -%></span>
 +          <% end %>
 +        </td>
 +
 +        <td align="right" width="10%" nowrap>
 +          <% if !is_default_profile && profiles_administrator? %>
 +            <%= link_to_action message('set_as_default'), "#{ApplicationController.root_context}/profiles/set_as_default?id=#{profile.id()}",
 +                               :id => "activate_#{profile.key().parameterize}",
 +                               :class => 'link-action',
 +                               :confirm_title => message('set_as_default'),
 +                               :confirm_msg => message('quality_profiles.are_you_sure_want_x_profile_as_default', :params => [profile.name()]),
 +                               :confirm_button => message('set_as_default')
 +            -%>
 +          <% end %>
 +          <% if is_default_profile %>
 +            <i class="icon-check" id='<%= "is_active_#{u profile.key()}" -%>'></i>
 +          <% end %>
 +        </td>
 +
 +        <td align="right" nowrap>
 +          <form method="post" action="<%= ApplicationController.root_context -%>/profiles/backup" id="backup-<%= profile.key() -%>-form">
 +            <input type="hidden" name="key" value="<%= profile.key() -%>"/>
 +            <a href="#" class="link-action" name="button_backup" id="backup_<%= u profile.key() -%>" onclick="$j('#backup-<%= profile.key() -%>-form').submit();return false;"><%= message('backup_verb') -%></a>
 +          </form>
 +        </td>
 +        <% if profiles_administrator? %>
 +          <td align="right">
 +            <a id="rename-<%= profile.key().parameterize -%>" href="<%= ApplicationController.root_context -%>/profiles/rename_form/<%= profile.id() -%>" class="link-action open-modal"><%= message('rename') -%></a>
 +          </td>
 +
 +          <td align="right">
 +            <a id="copy-<%= profile.key().parameterize -%>" href="<%= ApplicationController.root_context -%>/profiles/copy_form/<%= profile.id() -%>" class="link-action open-modal"><%= message('copy') -%></a>
 +          </td>
 +
 +          <td>
 +            <% if !is_default_profile %>
 +              <%= link_to_action message('delete'), "#{ApplicationController.root_context}/profiles/delete/#{profile.id()}",
 +                                 :class => 'link-action link-red',
 +                                 :id => "delete_#{profile.key().parameterize}",
 +                                 :confirm_button => message('delete'),
 +                                 :confirm_title => 'quality_profiles.delete_confirm_title',
 +                                 :confirm_msg => 'quality_profiles.are_you_sure_want_delete_profile_x_and_descendants',
 +                                 :confirm_msg_params => [profile.name()]
 +              -%>
 +            <% end %>
 +          </td>
 +        <% end %>
 +      </tr>
 +    <% end %>
 +    </tbody>
 +  </table>
 +  <br/><br/>
 +<% end %>
 +</div>