diff options
author | simonbrandhof <simon.brandhof@gmail.com> | 2010-09-07 23:23:23 +0000 |
---|---|---|
committer | simonbrandhof <simon.brandhof@gmail.com> | 2010-09-07 23:23:23 +0000 |
commit | 3cc63963cfd20176c605c27f023fb6e70db69b80 (patch) | |
tree | eed6fd559c6877052a4ef3ce2c0ebb845c41ea66 | |
parent | 0b9d0acd74b08a9076cd982684f0be8285c94251 (diff) | |
download | sonarqube-3cc63963cfd20176c605c27f023fb6e70db69b80.tar.gz sonarqube-3cc63963cfd20176c605c27f023fb6e70db69b80.zip |
SONAR-1549 The Sonar profile creation form doesn't allow to import checkstyle,pmd and Findbugs conf files when several langages are defined
SONAR-440 add warnings to checkstyle when importing configuration
25 files changed, 520 insertions, 301 deletions
diff --git a/plugins/sonar-checkstyle-plugin/src/main/java/org/sonar/plugins/checkstyle/CheckstyleProfileImporter.java b/plugins/sonar-checkstyle-plugin/src/main/java/org/sonar/plugins/checkstyle/CheckstyleProfileImporter.java index c9561aea69e..e397e5bbdeb 100644 --- a/plugins/sonar-checkstyle-plugin/src/main/java/org/sonar/plugins/checkstyle/CheckstyleProfileImporter.java +++ b/plugins/sonar-checkstyle-plugin/src/main/java/org/sonar/plugins/checkstyle/CheckstyleProfileImporter.java @@ -64,7 +64,7 @@ public class CheckstyleProfileImporter extends ProfileImporter { } } } catch (XMLStreamException e) { - messages.addError("unvalidXml", "XML is not valid: " + e.getMessage()); + messages.addError("checkstyle.import.unvalidXml", "XML is not valid: " + e.getMessage()); } return profile; } @@ -80,23 +80,42 @@ public class CheckstyleProfileImporter extends ProfileImporter { } private void processModule(ProfilePrototype profile, String path, SMInputCursor moduleCursor, ValidationMessages messages) throws XMLStreamException { - String configKey = path + moduleCursor.getAttrValue("name"); - ProfilePrototype.RulePrototype rule = ProfilePrototype.RulePrototype.createByConfigKey(CheckstyleConstants.REPOSITORY_KEY, configKey); + String configKey = moduleCursor.getAttrValue("name"); + if (isFilter(configKey)) { + messages.addWarning("checkstyle.import.filtersNotSupported", "Checkstyle filters are not imported: " + configKey); + } else if (isIgnored(configKey)) { + + } else { + ProfilePrototype.RulePrototype rule = ProfilePrototype.RulePrototype.createByConfigKey(CheckstyleConstants.REPOSITORY_KEY, path + configKey); + processProperties(moduleCursor, messages, rule); + profile.activateRule(rule); + } + } + + static boolean isIgnored(String configKey) { + return StringUtils.equals(configKey, "FileContentsHolder"); + } + + static boolean isFilter(String configKey) { + return StringUtils.equals(configKey, "SuppressionCommentFilter") || + StringUtils.equals(configKey, "SeverityMatchFilter") || + StringUtils.equals(configKey, "SuppressionFilter") || + StringUtils.equals(configKey, "SuppressWithNearbyCommentFilter"); + } + + private void processProperties(SMInputCursor moduleCursor, ValidationMessages messages, ProfilePrototype.RulePrototype rule) throws XMLStreamException { SMInputCursor propertyCursor = moduleCursor.childElementCursor("property"); while (propertyCursor.getNext() != null) { processProperty(rule, propertyCursor, messages); } - - profile.activateRule(rule); - } private void processProperty(ProfilePrototype.RulePrototype rule, SMInputCursor propertyCursor, ValidationMessages messages) throws XMLStreamException { String key = propertyCursor.getAttrValue("name"); String value = propertyCursor.getAttrValue("value"); if (StringUtils.equals("id", key)) { - messages.addWarning("checkstyle.idPropertyNotSupported", "The property 'id' is not supported."); + messages.addWarning("checkstyle.import.idPropertyNotSupported", "The checkstyle property 'id' is not supported in the rule: " + rule.getConfigKey()); } else if (StringUtils.equals("severity", key)) { rule.setPriority(CheckstyleSeverityUtils.fromSeverity(value)); diff --git a/plugins/sonar-checkstyle-plugin/src/main/resources/org/sonar/plugins/checkstyle/profile-sun-conventions.xml b/plugins/sonar-checkstyle-plugin/src/main/resources/org/sonar/plugins/checkstyle/profile-sun-conventions.xml index bbeff27d91c..d6fe70f3ff5 100644 --- a/plugins/sonar-checkstyle-plugin/src/main/resources/org/sonar/plugins/checkstyle/profile-sun-conventions.xml +++ b/plugins/sonar-checkstyle-plugin/src/main/resources/org/sonar/plugins/checkstyle/profile-sun-conventions.xml @@ -12,16 +12,6 @@ </rule>
<rule>
<repositoryKey>checkstyle</repositoryKey>
- <key>com.puppycrawl.tools.checkstyle.checks.whitespace.NoWhitespaceAfterCheck</key>
- <parameters>
- <parameter>
- <key>tokens</key>
- <value>COMMA,SEMI,TYPECAST</value>
- </parameter>
- </parameters>
- </rule>
- <rule>
- <repositoryKey>checkstyle</repositoryKey>
<key>com.puppycrawl.tools.checkstyle.checks.whitespace.WhitespaceAfterCheck</key>
<parameters>
<parameter>
diff --git a/plugins/sonar-checkstyle-plugin/src/test/java/org/sonar/plugins/checkstyle/CheckstyleProfileImporterTest.java b/plugins/sonar-checkstyle-plugin/src/test/java/org/sonar/plugins/checkstyle/CheckstyleProfileImporterTest.java index 63d9c2d40d9..1e53bba97ab 100644 --- a/plugins/sonar-checkstyle-plugin/src/test/java/org/sonar/plugins/checkstyle/CheckstyleProfileImporterTest.java +++ b/plugins/sonar-checkstyle-plugin/src/test/java/org/sonar/plugins/checkstyle/CheckstyleProfileImporterTest.java @@ -32,6 +32,7 @@ import java.io.StringReader; import static org.hamcrest.core.Is.is; import static org.hamcrest.core.IsNull.nullValue; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; public class CheckstyleProfileImporterTest { @@ -102,4 +103,16 @@ public class CheckstyleProfileImporterTest { importer.importProfile(reader, messages); assertThat(messages.getErrors().size(), is(1)); } + + @Test + public void importingFiltersIsNotSupported() { + Reader reader = new StringReader(TestUtils.getResourceContent("/org/sonar/plugins/checkstyle/CheckstyleProfileImporterTest/importingFiltersIsNotSupported.xml")); + ProfilePrototype profile = importer.importProfile(reader, messages); + + assertNull(profile.getRuleByConfigKey("checkstyle", "Checker/SuppressionCommentFilter")); + assertNull(profile.getRuleByConfigKey("checkstyle", "Checker/TreeWalker/FileContentsHolder")); + assertThat(profile.getRules().size(), is(2)); + assertThat(messages.getWarnings().size(), is(1)); // no warning for FileContentsHolder + } + } diff --git a/plugins/sonar-checkstyle-plugin/src/test/resources/org/sonar/plugins/checkstyle/CheckstyleProfileImporterTest/importingFiltersIsNotSupported.xml b/plugins/sonar-checkstyle-plugin/src/test/resources/org/sonar/plugins/checkstyle/CheckstyleProfileImporterTest/importingFiltersIsNotSupported.xml new file mode 100644 index 00000000000..806ec61fae0 --- /dev/null +++ b/plugins/sonar-checkstyle-plugin/src/test/resources/org/sonar/plugins/checkstyle/CheckstyleProfileImporterTest/importingFiltersIsNotSupported.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module name="Checker"> + <module name="SuppressionCommentFilter"/> + <module name="NewlineAtEndOfFile"/> + <module name="TreeWalker"> + <module name="FileContentsHolder"/> + <module name="InterfaceIsType"/> + </module> +</module>
\ No newline at end of file diff --git a/sonar-core/src/main/java/org/sonar/core/rule/DefaultRuleProvider.java b/sonar-core/src/main/java/org/sonar/core/rule/DefaultRuleProvider.java index faac5971355..0512cff60e9 100644 --- a/sonar-core/src/main/java/org/sonar/core/rule/DefaultRuleProvider.java +++ b/sonar-core/src/main/java/org/sonar/core/rule/DefaultRuleProvider.java @@ -20,6 +20,7 @@ package org.sonar.core.rule; import org.apache.commons.lang.StringUtils; +import org.sonar.api.database.DatabaseSession; import org.sonar.api.rules.Rule; import org.sonar.api.rules.RuleProvider; import org.sonar.api.rules.RuleQuery; @@ -43,30 +44,33 @@ public class DefaultRuleProvider implements RuleProvider { } public Rule find(RuleQuery query) { - return (Rule) createHqlQuery(query).getSingleResult(); + DatabaseSession session = sessionFactory.getSession(); + return (Rule)session.getSingleResult(createHqlQuery(session, query), null); + } public Collection<Rule> findAll(RuleQuery query) { - return createHqlQuery(query).getResultList(); + DatabaseSession session = sessionFactory.getSession(); + return createHqlQuery(session, query).getResultList(); } - private Query createHqlQuery(RuleQuery query) { + private Query createHqlQuery(DatabaseSession session, RuleQuery query) { StringBuilder hql = new StringBuilder().append("from ").append(Rule.class.getSimpleName()).append(" where enabled=true "); Map<String,Object> params = new HashMap<String,Object>(); if (StringUtils.isNotBlank(query.getRepositoryKey())) { - hql.append("AND pluginName=:repositoryKey"); + hql.append("AND pluginName=:repositoryKey "); params.put("repositoryKey", query.getRepositoryKey()); } if (StringUtils.isNotBlank(query.getKey())) { - hql.append("AND key=:key"); + hql.append("AND key=:key "); params.put("key", query.getKey()); } if (StringUtils.isNotBlank(query.getConfigKey())) { - hql.append("AND configKey=:configKey"); + hql.append("AND configKey=:configKey "); params.put("configKey", query.getConfigKey()); } - Query hqlQuery = sessionFactory.getSession().createQuery(hql.toString()); + Query hqlQuery = session.createQuery(hql.toString()); for (Map.Entry<String, Object> entry : params.entrySet()) { hqlQuery.setParameter(entry.getKey(), entry.getValue()); } diff --git a/sonar-core/src/test/java/org/sonar/core/rule/DefaultRuleProviderTest.java b/sonar-core/src/test/java/org/sonar/core/rule/DefaultRuleProviderTest.java index 10830c4256f..69287804e8c 100644 --- a/sonar-core/src/test/java/org/sonar/core/rule/DefaultRuleProviderTest.java +++ b/sonar-core/src/test/java/org/sonar/core/rule/DefaultRuleProviderTest.java @@ -45,6 +45,14 @@ public class DefaultRuleProviderTest extends AbstractDbUnitTestCase { } @Test + public void findReturnsNullIfNoResults() { + setupData("shared"); + DefaultRuleProvider provider = new DefaultRuleProvider(getSessionFactory()); + assertNull(provider.findByKey("checkstyle", "unknown")); + assertNull(provider.find(RuleQuery.create().withRepositoryKey("checkstyle").withConfigKey("unknown"))); + } + + @Test public void findRepositoryRules() { setupData("shared"); DefaultRuleProvider provider = new DefaultRuleProvider(getSessionFactory()); diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/profiles/ProfilePrototype.java b/sonar-plugin-api/src/main/java/org/sonar/api/profiles/ProfilePrototype.java index c4cc93cef93..979981a85b8 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/profiles/ProfilePrototype.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/profiles/ProfilePrototype.java @@ -165,7 +165,14 @@ public final class ProfilePrototype { @Override public String toString() { - return new StringBuilder().append("[repository=").append(repositoryKey).append(",key=").append(key).append("]").toString(); + StringBuilder sb = new StringBuilder().append("[repository=").append(repositoryKey); + if (StringUtils.isNotBlank(key)) { + sb.append(",key=").append(key); + } + if (StringUtils.isNotBlank(configKey)) { + sb.append(",configKey=").append(configKey); + } + return sb.append("]").toString(); } } } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/utils/ValidationMessages.java b/sonar-plugin-api/src/main/java/org/sonar/api/utils/ValidationMessages.java index bff9074bd12..4813f447fd3 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/utils/ValidationMessages.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/utils/ValidationMessages.java @@ -38,21 +38,22 @@ public final class ValidationMessages { public boolean hasErrors() { return !errors.isEmpty(); } + public List<Message> getErrors() { return errors; } + public ValidationMessages addError(String key, String label) { + errors.add(new Message(key, label)); + return this; + } + public List<Message> getWarnings() { return warnings; } - public List<Message> getInfos() { - return infos; - } - - public ValidationMessages addError(String key, String label) { - errors.add(new Message(key, label)); - return this; + public boolean hasWarnings() { + return !warnings.isEmpty(); } public ValidationMessages addWarning(String key, String label) { @@ -60,6 +61,14 @@ public final class ValidationMessages { return this; } + public List<Message> getInfos() { + return infos; + } + + public boolean hasInfos() { + return !infos.isEmpty(); + } + public ValidationMessages addInfo(String key, String label) { infos.add(new Message(key, label)); return this; diff --git a/sonar-server/src/main/java/org/sonar/server/configuration/ProfilesManager.java b/sonar-server/src/main/java/org/sonar/server/configuration/ProfilesManager.java index 2041e4c03bd..d5143a5ebf1 100644 --- a/sonar-server/src/main/java/org/sonar/server/configuration/ProfilesManager.java +++ b/sonar-server/src/main/java/org/sonar/server/configuration/ProfilesManager.java @@ -19,16 +19,13 @@ */ package org.sonar.server.configuration; -import org.sonar.api.Plugin; import org.sonar.api.Plugins; import org.sonar.api.database.DatabaseSession; -import org.sonar.jpa.dao.RulesDao; -import org.sonar.jpa.dao.BaseDao; import org.sonar.api.database.model.ResourceModel; import org.sonar.api.profiles.RulesProfile; -import org.sonar.api.rules.*; - -import java.util.List; +import org.sonar.api.rules.DefaultRulesManager; +import org.sonar.jpa.dao.BaseDao; +import org.sonar.jpa.dao.RulesDao; public class ProfilesManager extends BaseDao { @@ -54,19 +51,6 @@ public class ProfilesManager extends BaseDao { getSession().commit(); } - - public void importProfile(String pluginKey, int profileId, String configuration) { - for (RulesRepository rulesRepository : rulesManager.getRulesRepositories()) { - Plugin plugin = plugins.getPluginByExtension(rulesRepository); - if (rulesRepository instanceof ConfigurationImportable && pluginKey.equals(plugin.getKey())) { - List<ActiveRule> activeRules = ((ConfigurationImportable) rulesRepository).importConfiguration(configuration, rulesDao.getRulesByPlugin(pluginKey)); - rulesDao.addActiveRulesToProfile(activeRules, profileId, pluginKey); - getSession().commit(); - break; - } - } - } - public void deleteProfile(int profileId) { RulesProfile profile = getSession().getEntity(RulesProfile.class, profileId); if (profile != null && !profile.getProvided()) { diff --git a/sonar-server/src/main/java/org/sonar/server/rules/ProfilesConsole.java b/sonar-server/src/main/java/org/sonar/server/rules/ProfilesConsole.java index 7e92a8078db..02dc2b0bf2d 100644 --- a/sonar-server/src/main/java/org/sonar/server/rules/ProfilesConsole.java +++ b/sonar-server/src/main/java/org/sonar/server/rules/ProfilesConsole.java @@ -23,27 +23,36 @@ import org.apache.commons.lang.ArrayUtils; import org.apache.commons.lang.StringUtils; import org.sonar.api.ServerComponent; import org.sonar.api.database.DatabaseSession; -import org.sonar.api.profiles.ProfileExporter; -import org.sonar.api.profiles.RulesProfile; -import org.sonar.api.profiles.XMLProfileExporter; +import org.sonar.api.profiles.*; +import org.sonar.api.rules.ActiveRule; +import org.sonar.api.rules.Rule; +import org.sonar.api.rules.RuleProvider; +import org.sonar.api.rules.RuleQuery; +import org.sonar.api.utils.ValidationMessages; import org.sonar.jpa.session.DatabaseSessionFactory; +import java.io.StringReader; import java.io.StringWriter; import java.io.Writer; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Map; public final class ProfilesConsole implements ServerComponent { private DatabaseSessionFactory sessionFactory; + private RuleProvider ruleProvider; private List<ProfileExporter> profileExporters = new ArrayList<ProfileExporter>(); + private List<ProfileImporter> profileImporters = new ArrayList<ProfileImporter>(); - - public ProfilesConsole(DatabaseSessionFactory sessionFactory, - ProfileExporter[] exporters, DeprecatedProfileExporters deprecatedExporters) { + public ProfilesConsole(DatabaseSessionFactory sessionFactory, RuleProvider ruleProvider, + ProfileExporter[] exporters, DeprecatedProfileExporters deprecatedExporters, + ProfileImporter[] importers) { + this.ruleProvider = ruleProvider; this.sessionFactory = sessionFactory; initProfileExporters(exporters, deprecatedExporters); + this.profileImporters.addAll(Arrays.asList(importers)); } private void initProfileExporters(ProfileExporter[] exporters, DeprecatedProfileExporters deprecatedExporters) { @@ -54,7 +63,8 @@ public final class ProfilesConsole implements ServerComponent { } public String backupProfile(int profileId) { - RulesProfile profile = loadProfile(profileId); + DatabaseSession session = sessionFactory.getSession(); + RulesProfile profile = loadProfile(session, profileId); if (profile != null) { Writer writer = new StringWriter(); XMLProfileExporter.create().exportProfile(profile, writer); @@ -63,10 +73,48 @@ public final class ProfilesConsole implements ServerComponent { return null; } - private RulesProfile loadProfile(int profileId) { + public ValidationMessages restoreProfile(String profileName, String language, String xmlBackup) { DatabaseSession session = sessionFactory.getSession(); - RulesProfile profile = session.getSingleResult(RulesProfile.class, "id", profileId); - return profile; + RulesProfile profile = session.getSingleResult(RulesProfile.class, "name", profileName, "language", language); + if (profile != null) { + session.remove(session); + } + profile = RulesProfile.create(profileName, language); + ValidationMessages messages = ValidationMessages.create(); + ProfilePrototype prototype = XMLProfileImporter.create().importProfile(new StringReader(xmlBackup), messages); + completeProfileWithPrototype(profile, prototype, messages); + session.saveWithoutFlush(profile); + session.commit(); + return messages; + } + + private void completeProfileWithPrototype(RulesProfile profile, ProfilePrototype prototype, ValidationMessages messages) { + for (ProfilePrototype.RulePrototype rulePrototype : prototype.getRules()) { + Rule rule = findRule(rulePrototype); + if (rule == null) { + messages.addWarning("profiles.missingRule", "The following rule has been ignored: " + rulePrototype); + + } else { + ActiveRule activeRule = profile.activateRule(rule, rulePrototype.getPriority()); + for (Map.Entry<String, String> entry : rulePrototype.getParameters().entrySet()) { + activeRule.setParameter(entry.getKey(), entry.getValue()); + } + } + } + } + + private Rule findRule(ProfilePrototype.RulePrototype rulePrototype) { + if (StringUtils.isNotBlank(rulePrototype.getKey())) { + return ruleProvider.findByKey(rulePrototype.getRepositoryKey(), rulePrototype.getKey()); + } + if (StringUtils.isNotBlank(rulePrototype.getConfigKey())) { + return ruleProvider.find(RuleQuery.create().withRepositoryKey(rulePrototype.getRepositoryKey()).withConfigKey(rulePrototype.getConfigKey())); + } + return null; + } + + private RulesProfile loadProfile(DatabaseSession session, int profileId) { + return session.getSingleResult(RulesProfile.class, "id", profileId); } public List<ProfileExporter> getProfileExportersForLanguage(String language) { @@ -79,8 +127,19 @@ public final class ProfilesConsole implements ServerComponent { return result; } + public List<ProfileImporter> getProfileImportersForLanguage(String language) { + List<ProfileImporter> result = new ArrayList<ProfileImporter>(); + for (ProfileImporter importer : profileImporters) { + if (importer.getSupportedLanguages() == null || importer.getSupportedLanguages().length == 0 || ArrayUtils.contains(importer.getSupportedLanguages(), language)) { + result.add(importer); + } + } + return result; + } + public String exportProfile(int profileId, String exporterKey) { - RulesProfile profile = loadProfile(profileId); + DatabaseSession session = sessionFactory.getSession(); + RulesProfile profile = loadProfile(session, profileId); if (profile != null) { ProfileExporter exporter = getProfileExporter(exporterKey); Writer writer = new StringWriter(); @@ -90,6 +149,20 @@ public final class ProfilesConsole implements ServerComponent { return null; } + public ValidationMessages importProfile(int profileId, String importerKey, String profileDefinition) { + ValidationMessages messages = ValidationMessages.create(); + ProfileImporter importer = getProfileImporter(importerKey); + ProfilePrototype prototype = importer.importProfile(new StringReader(profileDefinition), messages); + if (!messages.hasErrors()) { + DatabaseSession session = sessionFactory.getSession(); + RulesProfile profile = loadProfile(session, profileId); // the profile has been create in the ruby controller + completeProfileWithPrototype(profile, prototype, messages); + session.saveWithoutFlush(profile); + session.commit(); + } + return messages; + } + public ProfileExporter getProfileExporter(String exporterKey) { for (ProfileExporter exporter : profileExporters) { if (StringUtils.equals(exporterKey, exporter.getKey())) { @@ -98,4 +171,13 @@ public final class ProfilesConsole implements ServerComponent { } return null; } + + public ProfileImporter getProfileImporter(String exporterKey) { + for (ProfileImporter importer : profileImporters) { + if (StringUtils.equals(exporterKey, importer.getKey())) { + return importer; + } + } + return null; + } } diff --git a/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java b/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java index 9654bd04bbf..404e2dac725 100644 --- a/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java +++ b/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java @@ -26,10 +26,11 @@ import org.sonar.api.Plugin; import org.sonar.api.Plugins; import org.sonar.api.Property; import org.sonar.api.profiles.ProfileExporter; +import org.sonar.api.profiles.ProfileImporter; import org.sonar.api.resources.Language; -import org.sonar.api.resources.Languages; import org.sonar.api.rules.DefaultRulesManager; import org.sonar.api.rules.RuleRepository; +import org.sonar.api.utils.ValidationMessages; import org.sonar.api.web.*; import org.sonar.jpa.dao.AsyncMeasuresService; import org.sonar.jpa.dialect.Dialect; @@ -50,7 +51,6 @@ import org.sonar.updatecenter.common.Version; import java.util.Collection; import java.util.List; -import java.util.Map; public class JRubyFacade { @@ -123,9 +123,10 @@ public class JRubyFacade { return getContainer().getComponent(Plugins.class).getPlugins(); } - public List<Plugin> getPluginsWithConfigurationImportable(Language language) { - return getRulesManager().getImportablePlugins(language); - } + + + + /* PROFILES CONSOLE : RULES AND METRIC THRESHOLDS */ public List<RuleRepository> getRuleRepositoriesByLanguage(String languageKey) { return getContainer().getComponent(RulesConsole.class).getRepositoriesByLanguage(languageKey); @@ -135,20 +136,28 @@ public class JRubyFacade { return getContainer().getComponent(ProfilesConsole.class).backupProfile(profileId); } + public ValidationMessages restoreProfile(String profileName, String language, String xmlBackup) { + return getContainer().getComponent(ProfilesConsole.class).restoreProfile(profileName, language, xmlBackup); + } + public List<ProfileExporter> getProfileExportersForLanguage(String language) { return getContainer().getComponent(ProfilesConsole.class).getProfileExportersForLanguage(language); } + public List<ProfileImporter> getProfileImportersForLanguage(String language) { + return getContainer().getComponent(ProfilesConsole.class).getProfileImportersForLanguage(language); + } + public String exportProfile(int profileId, String exporterKey) { return getContainer().getComponent(ProfilesConsole.class).exportProfile(profileId, exporterKey); } - public String getProfileExporterMimeType(String exporterKey) { - return getContainer().getComponent(ProfilesConsole.class).getProfileExporter(exporterKey).getMimeType(); + public ValidationMessages importProfile(int profileId, String importerKey, String fileContent) { + return getContainer().getComponent(ProfilesConsole.class).importProfile(profileId, importerKey, fileContent); } - public void importConfiguration(String pluginKey, long profileId, String configuration) { - getProfilesManager().importProfile(pluginKey, (int) profileId, configuration); + public String getProfileExporterMimeType(String exporterKey) { + return getContainer().getComponent(ProfilesConsole.class).getProfileExporter(exporterKey).getMimeType(); } public void copyProfile(long profileId, String newProfileName) { diff --git a/sonar-server/src/main/webapp/WEB-INF/app/controllers/alerts_controller.rb b/sonar-server/src/main/webapp/WEB-INF/app/controllers/alerts_controller.rb index 9015845ae74..487b371d866 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/controllers/alerts_controller.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/controllers/alerts_controller.rb @@ -20,45 +20,39 @@ class AlertsController < ApplicationController SECTION=Navigation::SECTION_CONFIGURATION + verify :method => :post, :only => ['create', 'update', 'delete'], :redirect_to => { :action => 'index' } before_filter :admin_required, :except => [ 'index' ] - before_filter :load_profile - - protected - - def load_profile - @profile = Profile.find(params[:profile_id]) - end - - public - - # GET /profiles/:profile_id/alerts - # GET /profiles/:profile_id/alerts.xml + + + # + # + # GET /alerts/index/<profile_id> + # + # def index + @profile = Profile.find(params[:id]) @alerts = @profile.alerts.sort @alert=Alert.new - - respond_to do |format| - format.html # index.html.erb - format.xml { render :xml => @alerts } - end end - # GET /profiles/:profile_id/alerts/1 - # GET /profiles/:profile_id/alerts/1.xml + # + # + # GET /alerts/show/<alert id> + # + # def show @alert = @profile.alerts.find(params[:id]) - - respond_to do |format| - format.html # show.html.erb - format.xml { render :xml => @alert } - end end - # GET /profiles/:profile_id/alerts/new - # GET /profiles/:profile_id/alerts/new.xml + + # + # + # GET /alerts/new?profile_id=<profile id> + # + # def new + @profile = Profile.find(params[:profile_id]) @alert = @profile.alerts.build(params[:alert]) - respond_to do |format| format.js { render :update do |page| @@ -70,23 +64,32 @@ class AlertsController < ApplicationController end end - # GET /profiles/:profile_id/alerts/edit + + # + # + # GET /alerts/edit/<alert id> + # + # def edit @alert = @profile.alerts.find(params[:id]) end - # POST /profiles/:profile_id/alerts - # POST /profiles/:profile_id/alerts.xml + + # + # + # POST /alerts/create?profile_id=<profile id>&... + # + # def create + @profile = Profile.find(params[:profile_id]) @alert = @profile.alerts.build(params[:alert]) respond_to do |format| if @alert.save flash[:notice] = 'Alert is created.' - format.html { redirect_to profile_alerts_path(@profile) } - format.xml { render :xml => @alert, :status => :created, :location => profile_alert_path(profile, @alert) } + format.html { redirect_to :action => 'index', :id=>@profile.id } format.js { render :update do |page| - page.redirect_to profile_alerts_path(@profile) + page.redirect_to :action => 'index', :id=>@profile.id end} else @@ -101,18 +104,22 @@ class AlertsController < ApplicationController end end - # PUT /profiles/:profile_id/alerts/1 - # PUT /profiles/:profile_id/alerts/1.xml + # + # + # POST /alerts/update/<alert id>?profile_id=<profile id> + # + # def update + @profile = Profile.find(params[:profile_id]) @alerts=@profile.alerts alert = @alerts.find(params[:id]) respond_to do |format| if alert.update_attributes(params[:alert]) flash[:notice] = 'Alert is updated.' - format.html { redirect_to profile_alerts_path(@profile) } + format.html { redirect_to :action => 'index', :id=>@profile.id } format.xml { head :ok } - format.js { render :update do |page| page.redirect_to profile_alerts_path(@profile) end} + format.js { render :update do |page| page.redirect_to :action => 'index', :id=>@profile.id end} else @alert=Alert.new format.html { render :action => "index" } @@ -124,16 +131,21 @@ class AlertsController < ApplicationController end end - # DELETE /profiles/:profile_id/alerts/1 - # DELETE /profiles/:profile_id/alerts/1.xml - def destroy + # + # + # POST /alerts/delete/<alert id>?profile_id=<profile id> + # + # + def delete + @profile = Profile.find(params[:profile_id]) @alert = @profile.alerts.find(params[:id]) @alert.destroy flash[:notice] = 'Alert is deleted.' respond_to do |format| - format.html { redirect_to(profile_alerts_path(@profile)) } + format.html { redirect_to(:action => 'index', :id=>@profile.id) } format.xml { head :ok } end end + end diff --git a/sonar-server/src/main/webapp/WEB-INF/app/controllers/profiles_controller.rb b/sonar-server/src/main/webapp/WEB-INF/app/controllers/profiles_controller.rb index 033a2e87a4c..097947ba703 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/controllers/profiles_controller.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/controllers/profiles_controller.rb @@ -19,114 +19,90 @@ # class ProfilesController < ApplicationController SECTION=Navigation::SECTION_CONFIGURATION - + + verify :method => :post, :only => ['create', 'delete', 'copy', 'set_as_default', 'restore', 'backup', 'set_projects'], :redirect_to => { :action => 'index' } before_filter :admin_required, :except => [ 'index', 'show', 'projects' ] - # GET /profiles - # GET /profiles.xml + # + # + # GET /profiles/index + # + # def index - @profiles = Profile.find(:all, :order => 'name') - - respond_to do |format| - format.html # index.html.erb - format.xml { render :xml => @profiles } - end + @profiles = Profile.find(:all, :order => 'name') end - # GET /profiles/1 - # GET /profiles/1.xml + + # + # + # GET /profiles/show/<id> + # + # def show @profile = Profile.find(params[:id]) - - respond_to do |format| - format.html # show.html.erb - format.xml { render :xml => @profile } - end end - # GET /profiles/new - # GET /profiles/new.xml - def new - @profile = Profile.new - @plugins_by_language = {} - java_facade.getLanguages().each do |language| - @plugins_by_language[language.getKey()] = java_facade.getPluginsWithConfigurationImportable(language) - end - respond_to do |format| - format.js { - render :update do |page| - page.replace_html('create_profile_row', :partial => 'new') - end - } - format.html # new.html.erb - format.xml { render :xml => @profile } - end - end - - # GET /profiles/1/edit - def edit - @profile = Profile.find(params[:id]) - end + # + # + # POST /profiles/create?name=<profile name>&language=<language> + # + # def create - profile = RulesProfile.new(:name => params[:name], :language => params[:language], :default_profile => false) - profile.save - if profile.errors.empty? - java_facade.getLanguages().select{|l| l.getKey()==profile.language}.each do |language| - java_facade.getPluginsWithConfigurationImportable(language).each do |plugin| - file = params[plugin.getKey()] - configuration = read_configuration(file) - if not configuration.nil? - import_configuration(profile.id, configuration, plugin.getKey()) + profile_name=params[:name] + language=params[:language] + if profile_name.blank?|| language.blank? + flash[:warning]='Please type a profile name.' + else + profile=Profile.find(:first, :conditions => {:name => profile_name, :language => language}) + if profile + flash[:error]="This profile already exists: #{profile_name}" + + else + profile = Profile.create(:name => profile_name, :language => language, :default_profile => false) + ok=profile.errors.empty? + if ok && params[:backup] + params[:backup].each_pair do |importer_key, file| + if !file.blank? && ok + messages = java_facade.importProfile(profile.id, importer_key, read_file_param(file)) + flash_validation_messages(messages) + ok &= !messages.hasErrors() + end end end + if ok + flash[:notice]= "Profile '#{profile.name}' created. Set it as default or link it to a project to use it for next measures." + else + profile.reload + profile.destroy + end end - flash[:notice]= "Profile '#{profile.name}' created. Set it as default or link it to a project to use it for next measures." - else - flash[:error] = profile.errors.full_messages.first end redirect_to :action => 'index' - - rescue NativeException - profile.destroy - flash[:error] = "No profile created. Please check your configuration files." - redirect_to :action => 'index' end - # PUT /profiles/1 - # PUT /profiles/1.xml - def update - @profile = Profile.find(params[:id]) - - respond_to do |format| - if @profile.update_attributes(params[:profile]) - flash[:notice] = 'Profile was successfully updated.' - format.html { redirect_to(@profile) } - format.xml { head :ok } - else - format.html { render :action => "edit" } - format.xml { render :xml => @profile.errors, :status => :unprocessable_entity } - end - end - end - - # DELETE /profiles/1 - # DELETE /profiles/1.xml - def destroy + # + # + # POST /profiles/delete/<id> + # + # + def delete @profile = Profile.find(params[:id]) if @profile && !@profile.provided? && !@profile.default_profile? java_facade.deleteProfile(@profile.id) flash[:notice]="Profile '#{@profile.name}' is deleted." end - - respond_to do |format| - format.html { redirect_to(:controller => 'profiles', :action => 'index') } - format.xml { head :ok } - end + redirect_to(:controller => 'profiles', :action => 'index') end + + # + # + # POST /profiles/set_as_default/<id> + # + # def set_as_default profile = Profile.find(params[:id]) profile.set_as_default @@ -134,6 +110,13 @@ class ProfilesController < ApplicationController redirect_to :action => 'index' end + + + # + # + # POST /profiles/copy/<id>?name=<name of new profile> + # + # def copy profile = Profile.find(params[:id]) name = params['copy_' + profile.id.to_s] @@ -149,6 +132,67 @@ class ProfilesController < ApplicationController redirect_to :action => 'index' end + + + # + # + # POST /profiles/backup/<id> + # + # + def backup + profile = Profile.find(params[:id]) + xml = java_facade.backupProfile(profile.id) + send_data(xml, :type => 'text/xml', :disposition => "attachment; filename=#{profile.name}_#{profile.language}.xml") + end + + + + # + # + # POST /profiles/restore/<id> + # + # + def restore + profile_name=params[:name] + language=params[:language] + profile=Profile.find(:first, :conditions => {:name => profile_name, :language => language}) + if profile + flash[:error]='An existing profile can not be restored. Please delete it before.' + elsif params[:backup].blank? + flash[:warning]='Please upload a backup file.' + else + messages=java_facade.restoreProfile(profile_name, language, read_file_param(params[:backup])) + flash_validation_messages(messages) + end + redirect_to :action => 'index' + end + + + + # + # + # GET /profiles/export?name=<profile name>&language=<language>&format<exporter key> + # + # + def export + name = CGI::unescape(params[:name]) + language = params[:language] + if (name != 'active') + profile = Profile.find_by_name_and_language(name, language) + else + profile = Profile.find_active_profile_by_language(language) + end + exporter_key = params[:format] + result = java_facade.exportProfile(profile.id, exporter_key) + send_data(result, :type => java_facade.getProfileExporterMimeType(exporter_key), :disposition => 'inline') + end + + + # + # + # GET /profiles/projects/<id> + # + # def projects @profile = Profile.find(params[:id]) @available_projects=Project.find(:all, @@ -158,7 +202,13 @@ class ProfilesController < ApplicationController @available_projects-=@profile.projects end - + + + # + # + # POST /profiles/set_projects/<id>?projects=<project ids> + # + # def set_projects @profile = Profile.find(params[:id]) @profile.projects.clear @@ -169,17 +219,10 @@ class ProfilesController < ApplicationController redirect_to :action => 'projects', :id => @profile.id end + private - def language_names_by_key - languages_by_key = {} - java_facade.getLanguages().each do |language| - languages_by_key[language.getKey()] = language.getName() - end - languages_by_key - end - - def read_configuration(configuration_file) + def read_file_param(configuration_file) # configuration file is a StringIO if configuration_file.respond_to?(:read) return configuration_file.read @@ -188,7 +231,15 @@ class ProfilesController < ApplicationController nil end - def import_configuration(profile_id, configuration, plugin_key) - java_facade.importConfiguration(plugin_key, profile_id, configuration) + def flash_validation_messages(messages) + if messages.hasErrors() + flash[:error]=messages.getErrors().map{|m| m.getLabel()}.join('<br/>') + end + if messages.hasWarnings() + flash[:warning]=messages.getWarnings().map{|m| m.getLabel()}.join('<br/>') + end + if messages.hasInfos() + flash[:notice]=messages.getInfos().map{|m| m.getLabel()}.join('<br/>') + end end end diff --git a/sonar-server/src/main/webapp/WEB-INF/app/controllers/rules_configuration_controller.rb b/sonar-server/src/main/webapp/WEB-INF/app/controllers/rules_configuration_controller.rb index 580ed989693..949402c6a34 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/controllers/rules_configuration_controller.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/controllers/rules_configuration_controller.rb @@ -29,7 +29,7 @@ class RulesConfigurationController < ApplicationController RULE_PRIORITIES = Sonar::RulePriority.as_options.reverse
# GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
- verify :method => :post, :only => ['activate_rule', 'update_param', 'bulk_edit', 'create', 'update', 'delete', 'backup'], :redirect_to => { :action => 'index' }
+ verify :method => :post, :only => ['activate_rule', 'update_param', 'bulk_edit', 'create', 'update', 'delete'], :redirect_to => { :action => 'index' }
before_filter :admin_required, :except => [ 'index', 'export' ]
@@ -252,37 +252,6 @@ class RulesConfigurationController < ApplicationController end
- #
- #
- # POST /rules_configuration/backup?id=<profile_id>
- #
- #
- def backup
- profile = RulesProfile.find(params[:id])
- xml = java_facade.backupProfile(profile.id)
- send_data(xml, :type => 'text/xml', :disposition => "attachment; filename=#{profile.name}_#{profile.language}.xml")
- end
-
-
-
- #
- #
- # GET /rules_configuration/export?name=<profile name>&language=<language>&format<exporter key>
- #
- #
- def export
- name = CGI::unescape(params[:name])
- language = params[:language]
- if (name != 'active')
- profile = RulesProfile.find_by_name_and_language(name, language)
- else
- profile = RulesProfile.find_active_profile_by_language(language)
- end
- exporter_key = params[:format]
- result = java_facade.exportProfile(profile.id, exporter_key)
- send_data(result, :type => java_facade.getProfileExporterMimeType(exporter_key), :disposition => 'inline')
- end
-
def update_param
is_admin=true # security has already been checked by controller filters
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/helpers/alerts_helper.rb b/sonar-server/src/main/webapp/WEB-INF/app/helpers/alerts_helper.rb index eab126a02eb..e86b999221a 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/helpers/alerts_helper.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/helpers/alerts_helper.rb @@ -25,7 +25,7 @@ module AlertsHelper def operators_for_select(alert) if alert.metric.nil? - [] + {} elsif alert.metric.numeric? NUMERIC_THRESOLD_OPERATORS @@ -38,7 +38,7 @@ module AlertsHelper elsif alert.metric.val_type==Metric::VALUE_TYPE_LEVEL LEVEL_THRESOLD_OPERATORS else - [] + {} end end @@ -68,23 +68,23 @@ module AlertsHelper end - def value_field(form, alert, fieldname) + def value_field(alert, value, fieldname) if alert.metric.nil? - form.text_field fieldname, :size => 5 + text_field_tag fieldname, value, :size => 5 elsif alert.metric.numeric? - form.text_field fieldname, :size => 5 + text_field_tag fieldname, value, :size => 5 elsif alert.metric.val_type==Metric::VALUE_TYPE_BOOLEAN - form.select fieldname, {'' => '', 'Yes' => '1', 'No' => '0'} + select_tag fieldname, {'' => '', 'Yes' => '1', 'No' => '0'} elsif alert.metric.val_type==Metric::VALUE_TYPE_STRING - form.text_field fieldname, :size => 5 + text_field_tag fieldname, value, :size => 5 elsif alert.metric.val_type==Metric::VALUE_TYPE_LEVEL - form.select fieldname, {'' => '', 'OK' => Metric::TYPE_LEVEL_OK, 'Error' => Metric::TYPE_LEVEL_ERROR, 'Warning' => Metric::TYPE_LEVEL_WARN} + select_tag fieldname, {'' => '', 'OK' => Metric::TYPE_LEVEL_OK, 'Error' => Metric::TYPE_LEVEL_ERROR, 'Warning' => Metric::TYPE_LEVEL_WARN} else - form.hidden_field fieldname + hidden_field_tag fieldname, value end end diff --git a/sonar-server/src/main/webapp/WEB-INF/app/models/profile.rb b/sonar-server/src/main/webapp/WEB-INF/app/models/profile.rb index d93bf560f35..bf615b3fc64 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/models/profile.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/models/profile.rb @@ -48,6 +48,14 @@ class Profile < ActiveRecord::Base new_rule_profile.errors end + def self.find_by_name_and_language(name, language) + Profile.find(:first, :conditions => {:name => name, :language => language}) + end + + def self.find_active_profile_by_language(language) + Profile.find(:first, :conditions => {:default_profile => true, :language => language}) + end + def self.default_profile Profile.find(:first, :conditions => {:default_profile => true}) end diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/alerts/_edit.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/alerts/_edit.html.erb index efb51c0ab99..47c531322a3 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/views/alerts/_edit.html.erb +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/alerts/_edit.html.erb @@ -1,23 +1,27 @@ -<% remote_form_for([@profile, alert]) do |f| %> +<%= form_remote_tag :url => {:action => 'update', :id => alert.id, :profile_id => @profile.id}, :html => {:id => "edit_alert_#{alert.id}"} %> <table> <tr> <td nowrap> <%= h alert.name %> </td> <td width="10%" nowrap> - <%= f.select 'operator', operators_for_select(alert) %> + <select id="alert_operator" name="alert[operator]"> + <% operators_for_select(alert).each_pair do |name, key| %> + <option value="<%= key -%>" <%= 'selected' if alert.operator==key -%>><%= name -%></option> + <% end %> + </select> </td> <td width="20%" align="left"> <%= image_tag 'levels/warn.png', :alt => 'Warning threshold' %> - <%= value_field(f, alert, 'value_warning') %> <%= alert.metric.suffix if alert.metric %> + <%= value_field(alert, alert.value_warning, 'alert[value_warning]') %> <%= alert.metric.suffix if alert.metric %> </td> <td width="20%" align="left"> <%= image_tag 'levels/error.png', :alt => 'Error threshold' %> - <%= value_field(f, alert, 'value_error') %> <%= alert.metric.suffix if alert.metric %> + <%= value_field(alert, alert.value_error, 'alert[value_error]') %> <%= alert.metric.suffix if alert.metric %> </td> <td width="120px" nowrap> - <%= f.submit "Update" %> - <%= link_to 'Delete', [@profile, alert], :confirm => 'Are you sure?', :method => :delete, :class => 'action', :id => "delete_#{u alert.name}" %> + <input id="alert_submit" type="submit" value="Update"></input> + <%= link_to 'Delete', {:action => 'delete', :id => alert.id, :profile_id =>@profile.id}, :confirm => 'Are you sure?', :method => :post, :class => 'action', :id => "delete_#{u alert.name}" %> </td> </tr> </table> @@ -30,4 +34,4 @@ </ul> </div> <% end %> -<% end %>
\ No newline at end of file +</form>
\ No newline at end of file diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/alerts/_new.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/alerts/_new.html.erb index 0720f325cd8..a96fe2aa5cf 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/views/alerts/_new.html.erb +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/alerts/_new.html.erb @@ -1,5 +1,5 @@ <h3>Create alert</h3> -<% remote_form_for [@profile, @alert] do |f| %> +<%= form_remote_tag :url => {:action => 'create', :profile_id => @profile.id}, :html => {:id => 'new_alert_form'} %> <table class="spaced"> <tr> <td valign="top"> @@ -24,25 +24,31 @@ </td> <td width="10%" valign="top"> - <%= f.select 'operator', operators_for_select(@alert), :selected => default_operator(@alert) %> + <select id="alert_operator" name="alert[operator]"> + <% + default_op=default_operator(@alert) + operators_for_select(@alert).each_pair do |name, key| %> + <option value="<%= key -%>" <%= 'selected' if default_op==key -%>><%= name -%></option> + <% end %> + </select> </td> <td width="20%" valign="top"> <%= image_tag 'levels/warn.png', :alt => 'A warning is triggered when this value is reached.' %> - <%= value_field(f, @alert, 'value_warning') %> + <%= value_field(@alert, '', 'alert[value_warning]') %> <%= @alert.metric.suffix if @alert.metric %><br/> <span class="note">Warning threshold</span> </td> <td width="20%" valign="top"> <%= image_tag 'levels/error.png', :alt => 'An error is triggered when this value is reached.' %> - <%= value_field(f, @alert, 'value_error') %> + <%= value_field(@alert, '', 'alert[value_error]') %> <%= @alert.metric.suffix if @alert.metric %><br/> <span class="note">Error threshold</span> </td> <td width="120px" nowrap valign="top"> - <%= f.submit 'Create', :id =>"submit_create" %> + <input type="submit" value="Create" id="submit_create"></input> </td> </tr> </table> @@ -55,4 +61,4 @@ </ul> </div> <% end %> -<% end %> +</form> diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/alerts/_show.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/alerts/_show.html.erb index df96e553a14..77ee3fb5c89 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/views/alerts/_show.html.erb +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/alerts/_show.html.erb @@ -7,13 +7,13 @@ <%= operators_for_select(alert).invert[alert.operator] %> </td> <td width="20%"> - <% if alert.metric and not alert.value_warning.blank? %> + <% if alert.metric && !alert.value_warning.blank? %> <%= image_tag 'levels/warn.png', :alt => 'Warning threshold' %> <%= alert.value_warning %> <%= alert.metric.suffix if alert.metric %> <% end %> </td> <td width="20%"> - <% if alert.metric and not alert.value_error.blank? %> + <% if alert.metric && !alert.value_error.blank? %> <%= image_tag 'levels/error.png', :alt => 'Error threshold' %> <%= alert.value_error %> <%= alert.metric.suffix %> <% end %> diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/alerts/index.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/alerts/index.html.erb index 8b2199cb4a9..6eeebbdf823 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/views/alerts/index.html.erb +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/alerts/index.html.erb @@ -1,4 +1,4 @@ -<h1 class="marginbottom10"><%= link_to 'Quality profiles', profiles_path %> / <%= h @profile.language -%> / <%= h @profile.name %></h1> +<h1 class="marginbottom10"><%= link_to 'Quality profiles', :controller => 'profiles', :action => 'index' -%> / <%= h @profile.language -%> / <%= h @profile.name %></h1> <%= render :partial => 'profiles/tabs', :locals => {:selected_tab=>'Alerts'} %> <% if is_admin? %> diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/profiles/_tabs.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/profiles/_tabs.html.erb index e02c19a8bf0..6eda63ef204 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/views/profiles/_tabs.html.erb +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/profiles/_tabs.html.erb @@ -7,7 +7,7 @@ <a href="<%= url_for :controller => 'rules_configuration', :action => 'index', :id => @profile.id %>" <%= "class='selected'" if selected_tab=='Coding rules' -%>>Coding rules</a> </li> <li> - <a href="<%= profile_alerts_path @profile.id -%>" <%= "class='selected'" if selected_tab=='Alerts' -%>>Alerts</a> + <a href="<%= url_for :controller => 'alerts', :action => 'index', :id => @profile.id -%>" <%= "class='selected'" if selected_tab=='Alerts' -%>>Alerts</a> </li> <li> <a href="<%= url_for :controller => 'profiles', :action => 'projects', :id => @profile.id -%>" <%= "class='selected'" if selected_tab=='Projects' -%>>Projects</a> diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/profiles/index.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/profiles/index.html.erb index a4bbfd86301..4beb03a9591 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/views/profiles/index.html.erb +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/profiles/index.html.erb @@ -1,6 +1,7 @@ <% languages.each do |language| - exporters=controller.java_facade.getProfileExportersForLanguage(language.getKey()) + exporters=controller.java_facade.getProfileExportersForLanguage(language.getKey()) + importers=controller.java_facade.getProfileImportersForLanguage(language.getKey()) %> <table class="data2 width100 marginbottom10" id="profiles_<%= language.getKey() -%>"> @@ -8,15 +9,25 @@ <tr> <th><h2><%= language.getName() %></h2></th> <th class="right"></th> + <th class="right"></th> <th class="right">Export</th> - <th class="right">Alerts</th> <th class="right">Default</th> <th class="right">Projects</th> - <th width="1%"> </th> - <th width="1%"> </th> - <th width="1%"> </th> + <th width="1%" class="right" colspan="3">Operations</th> </tr> </thead> + <% if administrator? %> + <tfoot id="footer-<%= language.getKey() -%>"> + <tr> + <td colspan="9"> + <a href="#" onClick="$('footer-<%= language.getKey() -%>').hide();$('create-form-<%= language.getKey() -%>').show();return false;">Create <%= language.getName() -%> profile</a> + | + <a href="#" onclick="$('footer-<%= language.getKey() -%>').hide();$('restore-form-<%= language.getKey() -%>').show();return false;">Restore <%= language.getName() -%> profile</a> + </td> + </tr> + </tfoot> + <% end %> + <tbody> <% @profiles.select{|p| p.language==language.getKey()}.each do |profile| %> <tr class="<%= cycle 'even', 'odd', :name => language.getKey() -%>" id="<%= u profile.key %>"> <td><a href="<%= url_for :controller => 'rules_configuration', :action => 'index', :id => profile.id -%>"><%= h profile.name %></a></td> @@ -26,18 +37,17 @@ <span id="activated_rules_<%= u profile.key -%>"><%= profile.active_rules.count -%> rules</span> </a> </td> + + <td align="right"><%= link_to pluralize(profile.alerts.size, 'alert'), url_for(:controller => 'alerts', :action => 'index', :id => profile.id), :id => "alerts_#{u profile.key}" %></td> <td align="right"> <% exporters.each do |exporter| %> <%= link_to exporter.getName(), - {:controller => 'rules_configuration', :action => 'export', :language => profile.language, - :name => url_encode(profile.name), :format => exporter.getKey()}, - :id => "export_" + exporter.getKey().to_s + "_" + u(profile.key) %> + {:action => 'export', :language => profile.language, :name => url_encode(profile.name), :format => exporter.getKey()}, :id => "export_" + exporter.getKey().to_s + "_" + u(profile.key) %> <% end %> </td> - <td align="right"><%= link_to pluralize(profile.alerts.size, 'alert'), profile_alerts_path(profile), :id => "alerts_#{u profile.key}" %></td> - + <td align="right"> <% if (!profile.default_profile? && administrator?) %> <%= button_to 'Set as default', { :action => 'set_as_default', :id => profile.id }, :class => 'action', @@ -61,8 +71,7 @@ <td align="right"> <% if (!profile.provided? && administrator?) %> - <% form_tag(:controller => 'rules_configuration', :action => 'backup', :id => profile.id) do -%> - <%= hidden_field_tag 'backup_' + profile.id.to_s %> + <% form_tag(:action => 'backup', :id => profile.id) do -%> <input type="submit" name="button_backup" id="backup_<%= u profile.key %>" value="Backup"></input> <% end end %> @@ -79,33 +88,62 @@ <td> <% if (!(profile.provided?) && !(profile.default_profile?) && administrator?) %> - <%= button_to "Delete", { :action => 'destroy', :id => profile.id }, :class => 'action', + <%= button_to "Delete", { :action => 'delete', :id => profile.id }, :class => 'action', :id => "delete_#{u profile.key}", :confirm => "Are you sure that you want to delete the profile '#{profile.name}' ?", - :method => :delete %> + :method => :post %> <% end %> </td> </tr> <% end %> + </tbody> </table> -<% end %> -<% if administrator? %> -<div id="create_profile_row"> - <% form_remote_tag :url => {:action => 'new'} do %> - <%= submit_tag 'Create profile', :id => 'create_profile' %> - <% end %> -</div> -<% end %> + <% if administrator? %> + <form class="admin" id="create-form-<%= language.getKey()-%>" action="<%= url_for :action => 'create' -%>" style="display: none" enctype="multipart/form-data" method="post"> + <input type="hidden" name="language" value="<%= language.getKey() -%>"></input> + <table class="spaced width100"> + <tr> + <td width="1%" nowrap>Name: </td> + <td><input type="text" name="name"></input></td> + </tr> + <% importers.each do |importer| %> + <tr> + <td width="1%" nowrap><%= importer.getName() -%>: </td> + <td> + <%= file_field_tag "backup[#{importer.getKey()}]" %></input> + <span class="note">Optional configuration file</span> + </td> + </tr> + <% end %> -<script type="text/javascript"> - function changeLanguage(){ - $$('.language_class').each(function(element) { - element.style.display = 'none'; - }); - elementTable = $($('language').value); - if (elementTable){ - elementTable.toggle(); - } - }; -</script> + <tr> + <td colspan="2"> + <input type="submit" value="Create <%= language.getName() %> profile"></input> + <a href="#" onclick="$('create-form-<%= language.getKey()-%>').reset();$('create-form-<%= language.getKey()-%>').hide();$('footer-<%= language.getKey()-%>').show();return false;">Cancel</a> + </td> + </tr> + </table> + </form> + <form class="admin" id="restore-form-<%= language.getKey()-%>" action="<%= url_for :action => 'restore' -%>" enctype="multipart/form-data" style="display: none" method="post"> + <input type="hidden" name="language" value="<%= language.getKey() -%>"></input> + <table class="spaced width100"> + <tr> + <td width="1%" nowrap>Name: </td> + <td><input type="text" name="name"></input></td> + </tr> + <tr> + <td width="1%" nowrap>Backup: </td> + <td><%= file_field_tag 'backup' %></td> + </tr> + + <tr> + <td colspan="2"> + <input type="submit" value="Restore <%= language.getName() %> profile"></input> + <a href="#" onclick="$('restore-form-<%= language.getKey()-%>').reset();$('restore-form-<%= language.getKey()-%>').hide();$('footer-<%= language.getKey()-%>').show();return false;">Cancel</a> + </td> + </tr> + </table> + </form> + <% end %> +<% end %> diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/profiles/projects.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/profiles/projects.html.erb index c28977511a0..59afeb3554d 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/views/profiles/projects.html.erb +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/profiles/projects.html.erb @@ -1,4 +1,4 @@ -<h1 class="marginbottom10"><%= link_to 'Quality profiles', profiles_path %> / <%= h @profile.language -%> / <%= h @profile.name %></h1> +<h1 class="marginbottom10"><%= link_to 'Quality profiles', :controller => 'profiles', :action => 'index' -%> / <%= h @profile.language -%> / <%= h @profile.name %></h1> <%= render :partial => 'profiles/tabs', :locals => {:selected_tab=>'Projects'} %> <div class="tabs-panel"> diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/rules_configuration/index.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/rules_configuration/index.html.erb index 8af20de052f..59fd4e04eb8 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/views/rules_configuration/index.html.erb +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/rules_configuration/index.html.erb @@ -26,7 +26,7 @@ } </script> -<h1 class="marginbottom10"><%= link_to 'Quality profiles', profiles_path %> / <%= h @profile.language -%> / <%= h @profile.name %></h1> +<h1 class="marginbottom10"><%= link_to 'Quality profiles', :controller => 'profiles', :action => 'index' -%> / <%= h @profile.language -%> / <%= h @profile.name %></h1> <%= render :partial => 'profiles/tabs', :locals => {:selected_tab=>'Coding rules'} %> diff --git a/sonar-server/src/main/webapp/WEB-INF/config/routes.rb b/sonar-server/src/main/webapp/WEB-INF/config/routes.rb index 7fef97fe5b0..1eb2b53a1f4 100644 --- a/sonar-server/src/main/webapp/WEB-INF/config/routes.rb +++ b/sonar-server/src/main/webapp/WEB-INF/config/routes.rb @@ -13,9 +13,6 @@ ActionController::Routing::Routes.draw do |map| api.resources :favorites, :only => [:index, :show, :create, :destroy], :requirements => { :id => /.*/ } end - map.resources :alerts - map.resources :profiles, :has_many => :alerts - map.connect 'api/metrics', :controller => 'api/metrics', :action => 'index', :conditions => { :method => :get } map.connect 'api/metrics/:id', :controller => 'api/metrics', :action => 'show', :conditions => { :method => :get } map.connect 'api/metrics/:id', :controller => 'api/metrics', :action => 'create', :conditions => { :method => :post } |