Browse Source

SONAR-1378 validate property types + update type of core properties

tags/3.0
Simon Brandhof 12 years ago
parent
commit
f6a1da4560
30 changed files with 803 additions and 413 deletions
  1. 2
    1
      plugins/sonar-checkstyle-plugin/src/main/java/org/sonar/plugins/checkstyle/CheckstylePlugin.java
  2. 170
    172
      plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java
  3. 7
    9
      plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdPlugin.java
  4. 4
    3
      plugins/sonar-dbcleaner-plugin/src/main/java/org/sonar/plugins/dbcleaner/DbCleanerPlugin.java
  5. 2
    1
      plugins/sonar-dbcleaner-plugin/src/main/java/org/sonar/plugins/dbcleaner/DefaultPurgeTask.java
  6. 2
    1
      plugins/sonar-findbugs-plugin/src/main/java/org/sonar/plugins/findbugs/FindbugsPlugin.java
  7. 6
    0
      plugins/sonar-l10n-en-plugin/src/main/resources/org/sonar/l10n/core.properties
  8. 4
    6
      plugins/sonar-squid-java-plugin/src/main/java/org/sonar/plugins/squid/SquidPlugin.java
  9. 1
    8
      sonar-plugin-api/src/main/java/org/sonar/api/Property.java
  10. 24
    0
      sonar-plugin-api/src/main/java/org/sonar/api/PropertyType.java
  11. 2
    1
      sonar-plugin-api/src/main/java/org/sonar/api/config/AesCipher.java
  12. 158
    0
      sonar-plugin-api/src/main/java/org/sonar/api/config/PropertyDefinition.java
  13. 15
    14
      sonar-plugin-api/src/main/java/org/sonar/api/config/PropertyDefinitions.java
  14. 23
    0
      sonar-plugin-api/src/main/java/org/sonar/api/config/package-info.java
  15. 0
    7
      sonar-plugin-api/src/main/java/org/sonar/api/rules/RulesCategory.java
  16. 153
    0
      sonar-plugin-api/src/test/java/org/sonar/api/config/PropertyDefinitionTest.java
  17. 8
    8
      sonar-plugin-api/src/test/java/org/sonar/api/config/PropertyDefinitionsTest.java
  18. 13
    15
      sonar-plugin-api/src/test/java/org/sonar/api/config/SettingsTest.java
  19. 4
    4
      sonar-plugin-api/src/test/java/org/sonar/api/platform/ComponentContainerTest.java
  20. 2
    1
      sonar-server/src/main/java/org/sonar/server/plugins/UpdateCenterClient.java
  21. 5
    5
      sonar-server/src/main/webapp/WEB-INF/app/controllers/project_controller.rb
  22. 26
    26
      sonar-server/src/main/webapp/WEB-INF/app/controllers/settings_controller.rb
  23. 15
    0
      sonar-server/src/main/webapp/WEB-INF/app/models/property.rb
  24. 1
    1
      sonar-server/src/main/webapp/WEB-INF/app/views/project/settings.html.erb
  25. 0
    128
      sonar-server/src/main/webapp/WEB-INF/app/views/settings/_plugins.html.erb
  26. 85
    0
      sonar-server/src/main/webapp/WEB-INF/app/views/settings/_properties.html.erb
  27. 69
    0
      sonar-server/src/main/webapp/WEB-INF/app/views/settings/_settings.html.erb
  28. 0
    0
      sonar-server/src/main/webapp/WEB-INF/app/views/settings/_sidebar.html.erb
  29. 1
    1
      sonar-server/src/main/webapp/WEB-INF/app/views/settings/_type_SINGLE_SELECT_LIST.html.erb
  30. 1
    1
      sonar-server/src/main/webapp/WEB-INF/app/views/settings/index.html.erb

+ 2
- 1
plugins/sonar-checkstyle-plugin/src/main/java/org/sonar/plugins/checkstyle/CheckstylePlugin.java View File

@@ -21,6 +21,7 @@ package org.sonar.plugins.checkstyle;

import org.sonar.api.Properties;
import org.sonar.api.Property;
import org.sonar.api.PropertyType;
import org.sonar.api.SonarPlugin;

import java.util.Arrays;
@@ -35,7 +36,7 @@ import java.util.List;
+ " See <a href='http://checkstyle.sourceforge.net/config.html'>Checkstyle configuration page</a> to get more information on those filters.",
project = false,
global = true,
type = Property.Type.TEXT)})
type = PropertyType.TEXT)})
public final class CheckstylePlugin extends SonarPlugin {

public List getExtensions() {

+ 170
- 172
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java View File

@@ -20,11 +20,9 @@
package org.sonar.plugins.core;

import com.google.common.collect.Lists;
import org.sonar.api.CoreProperties;
import org.sonar.api.Properties;
import org.sonar.api.Property;
import org.sonar.api.SonarPlugin;
import org.sonar.api.*;
import org.sonar.api.checks.NoSonarFilter;
import org.sonar.api.PropertyType;
import org.sonar.api.resources.Java;
import org.sonar.plugins.core.batch.ExcludedResourceFilter;
import org.sonar.plugins.core.batch.IndexProjectPostJob;
@@ -50,177 +48,177 @@ import org.sonar.plugins.core.widgets.reviews.*;
import java.util.List;

@Properties({
@Property(
key = CoreProperties.SERVER_BASE_URL,
defaultValue = CoreProperties.SERVER_BASE_URL_DEFAULT_VALUE,
name = "Server base URL",
description = "HTTP URL of this Sonar server, such as <i>http://yourhost.yourdomain/sonar</i>. This value is used i.e. to create links in emails.",
project = false,
global = true,
category = CoreProperties.CATEGORY_GENERAL),
@Property(
key = CoreProperties.CORE_COVERAGE_PLUGIN_PROPERTY,
defaultValue = "cobertura",
name = "Code coverage plugin",
description = "Key of the code coverage plugin to use.",
project = true,
global = true,
category = CoreProperties.CATEGORY_CODE_COVERAGE),
@Property(
key = CoreProperties.CORE_IMPORT_SOURCES_PROPERTY,
defaultValue = "" + CoreProperties.CORE_IMPORT_SOURCES_DEFAULT_VALUE,
name = "Import sources",
description = "Set to false if sources should not be displayed, e.g. for security reasons.",
project = true,
module = true,
global = true,
category = CoreProperties.CATEGORY_SECURITY,
type = Property.Type.BOOLEAN),
@Property(
key = CoreProperties.CORE_TENDENCY_DEPTH_PROPERTY,
defaultValue = "" + CoreProperties.CORE_TENDENCY_DEPTH_DEFAULT_VALUE,
name = "Tendency period",
description = TendencyDecorator.PROP_DAYS_DESCRIPTION,
project = false,
global = true,
category = CoreProperties.CATEGORY_DIFFERENTIAL_VIEWS,
type = Property.Type.INTEGER),
@Property(
key = CoreProperties.SKIP_TENDENCIES_PROPERTY,
defaultValue = "" + CoreProperties.SKIP_TENDENCIES_DEFAULT_VALUE,
name = "Skip tendencies",
description = "Skip calculation of measure tendencies",
project = true,
module = false,
global = true,
category = CoreProperties.CATEGORY_DIFFERENTIAL_VIEWS,
type = Property.Type.BOOLEAN),
@Property(
key = CoreProperties.CORE_SKIPPED_MODULES_PROPERTY,
name = "Exclude modules",
description = "Maven artifact ids of modules to exclude (comma-separated).",
project = true,
global = false,
category = CoreProperties.CATEGORY_GENERAL),
@Property(
key = CoreProperties.CORE_FORCE_AUTHENTICATION_PROPERTY,
defaultValue = "" + CoreProperties.CORE_FORCE_AUTHENTICATION_DEFAULT_VALUE,
name = "Force user authentication",
description = "Forcing user authentication stops un-logged users to access Sonar.",
project = false,
global = true,
category = CoreProperties.CATEGORY_SECURITY,
type = Property.Type.BOOLEAN),
@Property(
key = CoreProperties.CORE_ALLOW_USERS_TO_SIGNUP_PROPERTY,
defaultValue = "" + CoreProperties.CORE_ALLOW_USERS_TO_SIGNUP_DEAULT_VALUE,
name = "Allow users to sign up online",
description = "Users can sign up online.",
project = false,
global = true,
category = CoreProperties.CATEGORY_SECURITY,
type = Property.Type.BOOLEAN),
@Property(
key = CoreProperties.CORE_DEFAULT_GROUP,
defaultValue = CoreProperties.CORE_DEFAULT_GROUP_DEFAULT_VALUE,
name = "Default user group",
description = "Any new users will automatically join this group.",
project = false,
global = true,
category = CoreProperties.CATEGORY_SECURITY),
@Property(
key = CoreProperties.CORE_VIOLATION_LOCALE_PROPERTY,
defaultValue = "en",
name = "Locale used for violation messages",
description = "Locale to be used when generating violation messages. It's up to each rule engine to support this global internationalization property",
project = true,
global = true,
category = CoreProperties.CATEGORY_L10N),
@Property(
key = "sonar.timemachine.period1",
name = "Period 1",
description = "Period used to compare measures and track new violations. Values are : <ul class='bullet'><li>Number of days before " +
"analysis, for example 5.</li><li>A custom date. Format is yyyy-MM-dd, for example 2010-12-25</li><li>'previous_analysis' to " +
"compare to previous analysis</li></ul>" +
"Changing this property only take effect after subsequent project inspections.",
project = false,
global = true,
defaultValue = CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_1,
category = CoreProperties.CATEGORY_DIFFERENTIAL_VIEWS),
@Property(
key = "sonar.timemachine.period2",
name = "Period 2",
description = "See the property 'Period 1'",
project = false,
global = true,
defaultValue = CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_2,
category = CoreProperties.CATEGORY_DIFFERENTIAL_VIEWS),
@Property(
key = "sonar.timemachine.period3",
name = "Period 3",
description = "See the property 'Period 1'",
project = false,
global = true,
defaultValue = CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_3,
category = CoreProperties.CATEGORY_DIFFERENTIAL_VIEWS),
@Property(
key = "sonar.timemachine.period4",
name = "Period 4",
description = "Period used to compare measures and track new violations. This property is specific to the project. Values are : " +
"<ul class='bullet'><li>Number of days before analysis, for example 5.</li><li>A custom date. Format is yyyy-MM-dd, " +
"for example 2010-12-25</li><li>'previous_analysis' to compare to previous analysis</li><li>A version, for example 1.2</li></ul>" +
"Changing this property only take effect after subsequent project inspection.",
project = true,
global = false,
defaultValue = CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_4,
category = CoreProperties.CATEGORY_DIFFERENTIAL_VIEWS),
@Property(
key = "sonar.timemachine.period5",
name = "Period 5",
description = "See the property 'Period 4'",
project = true,
global = false,
defaultValue = CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_5,
category = CoreProperties.CATEGORY_DIFFERENTIAL_VIEWS),
@Property(
key = CoreProperties.SERVER_BASE_URL,
defaultValue = CoreProperties.SERVER_BASE_URL_DEFAULT_VALUE,
name = "Server base URL",
description = "HTTP URL of this Sonar server, such as <i>http://yourhost.yourdomain/sonar</i>. This value is used i.e. to create links in emails.",
project = false,
global = true,
category = CoreProperties.CATEGORY_GENERAL),
@Property(
key = CoreProperties.CORE_COVERAGE_PLUGIN_PROPERTY,
defaultValue = "cobertura",
name = "Code coverage plugin",
description = "Key of the code coverage plugin to use.",
project = true,
global = true,
category = CoreProperties.CATEGORY_CODE_COVERAGE),
@Property(
key = CoreProperties.CORE_IMPORT_SOURCES_PROPERTY,
defaultValue = "" + CoreProperties.CORE_IMPORT_SOURCES_DEFAULT_VALUE,
name = "Import sources",
description = "Set to false if sources should not be displayed, e.g. for security reasons.",
project = true,
module = true,
global = true,
category = CoreProperties.CATEGORY_SECURITY,
type = PropertyType.BOOLEAN),
@Property(
key = CoreProperties.CORE_TENDENCY_DEPTH_PROPERTY,
defaultValue = "" + CoreProperties.CORE_TENDENCY_DEPTH_DEFAULT_VALUE,
name = "Tendency period",
description = TendencyDecorator.PROP_DAYS_DESCRIPTION,
project = false,
global = true,
category = CoreProperties.CATEGORY_DIFFERENTIAL_VIEWS,
type = PropertyType.INTEGER),
@Property(
key = CoreProperties.SKIP_TENDENCIES_PROPERTY,
defaultValue = "" + CoreProperties.SKIP_TENDENCIES_DEFAULT_VALUE,
name = "Skip tendencies",
description = "Skip calculation of measure tendencies",
project = true,
module = false,
global = true,
category = CoreProperties.CATEGORY_DIFFERENTIAL_VIEWS,
type = PropertyType.BOOLEAN),
@Property(
key = CoreProperties.CORE_SKIPPED_MODULES_PROPERTY,
name = "Exclude modules",
description = "Maven artifact ids of modules to exclude (comma-separated).",
project = true,
global = false,
category = CoreProperties.CATEGORY_GENERAL),
@Property(
key = CoreProperties.CORE_FORCE_AUTHENTICATION_PROPERTY,
defaultValue = "" + CoreProperties.CORE_FORCE_AUTHENTICATION_DEFAULT_VALUE,
name = "Force user authentication",
description = "Forcing user authentication stops un-logged users to access Sonar.",
project = false,
global = true,
category = CoreProperties.CATEGORY_SECURITY,
type = PropertyType.BOOLEAN),
@Property(
key = CoreProperties.CORE_ALLOW_USERS_TO_SIGNUP_PROPERTY,
defaultValue = "" + CoreProperties.CORE_ALLOW_USERS_TO_SIGNUP_DEAULT_VALUE,
name = "Allow users to sign up online",
description = "Users can sign up online.",
project = false,
global = true,
category = CoreProperties.CATEGORY_SECURITY,
type = PropertyType.BOOLEAN),
@Property(
key = CoreProperties.CORE_DEFAULT_GROUP,
defaultValue = CoreProperties.CORE_DEFAULT_GROUP_DEFAULT_VALUE,
name = "Default user group",
description = "Any new users will automatically join this group.",
project = false,
global = true,
category = CoreProperties.CATEGORY_SECURITY),
@Property(
key = CoreProperties.CORE_VIOLATION_LOCALE_PROPERTY,
defaultValue = "en",
name = "Locale used for violation messages",
description = "Locale to be used when generating violation messages. It's up to each rule engine to support this global internationalization property",
project = true,
global = true,
category = CoreProperties.CATEGORY_L10N),
@Property(
key = "sonar.timemachine.period1",
name = "Period 1",
description = "Period used to compare measures and track new violations. Values are : <ul class='bullet'><li>Number of days before " +
"analysis, for example 5.</li><li>A custom date. Format is yyyy-MM-dd, for example 2010-12-25</li><li>'previous_analysis' to " +
"compare to previous analysis</li></ul>" +
"Changing this property only take effect after subsequent project inspections.",
project = false,
global = true,
defaultValue = CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_1,
category = CoreProperties.CATEGORY_DIFFERENTIAL_VIEWS),
@Property(
key = "sonar.timemachine.period2",
name = "Period 2",
description = "See the property 'Period 1'",
project = false,
global = true,
defaultValue = CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_2,
category = CoreProperties.CATEGORY_DIFFERENTIAL_VIEWS),
@Property(
key = "sonar.timemachine.period3",
name = "Period 3",
description = "See the property 'Period 1'",
project = false,
global = true,
defaultValue = CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_3,
category = CoreProperties.CATEGORY_DIFFERENTIAL_VIEWS),
@Property(
key = "sonar.timemachine.period4",
name = "Period 4",
description = "Period used to compare measures and track new violations. This property is specific to the project. Values are : " +
"<ul class='bullet'><li>Number of days before analysis, for example 5.</li><li>A custom date. Format is yyyy-MM-dd, " +
"for example 2010-12-25</li><li>'previous_analysis' to compare to previous analysis</li><li>A version, for example 1.2</li></ul>" +
"Changing this property only take effect after subsequent project inspection.",
project = true,
global = false,
defaultValue = CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_4,
category = CoreProperties.CATEGORY_DIFFERENTIAL_VIEWS),
@Property(
key = "sonar.timemachine.period5",
name = "Period 5",
description = "See the property 'Period 4'",
project = true,
global = false,
defaultValue = CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_5,
category = CoreProperties.CATEGORY_DIFFERENTIAL_VIEWS),


// SERVER-SIDE TECHNICAL PROPERTIES
// SERVER-SIDE TECHNICAL PROPERTIES

@Property(
key = "sonar.security.realm",
name = "Security Realm",
project = false,
global = false
),
@Property(
key = "sonar.security.savePassword",
name = "Save external password",
project = false,
global = false
),
@Property(
key = "sonar.authenticator.downcase",
name = "Downcase login",
description = "Downcase login during user authentication, typically for Active Directory",
project = false,
global = false,
defaultValue = "false",
type = Property.Type.BOOLEAN),
@Property(
key = CoreProperties.CORE_AUTHENTICATOR_CREATE_USERS,
name = "Create user accounts",
description = "Create accounts when authenticating users via an external system",
project = false,
global = false,
defaultValue = "true",
type = Property.Type.BOOLEAN),
@Property(
key = CoreProperties.CORE_AUTHENTICATOR_IGNORE_STARTUP_FAILURE,
name = "Ignore failures during authenticator startup",
defaultValue = "false",
project = false,
global = false,
type = Property.Type.BOOLEAN)
@Property(
key = "sonar.security.realm",
name = "Security Realm",
project = false,
global = false
),
@Property(
key = "sonar.security.savePassword",
name = "Save external password",
project = false,
global = false
),
@Property(
key = "sonar.authenticator.downcase",
name = "Downcase login",
description = "Downcase login during user authentication, typically for Active Directory",
project = false,
global = false,
defaultValue = "false",
type = PropertyType.BOOLEAN),
@Property(
key = CoreProperties.CORE_AUTHENTICATOR_CREATE_USERS,
name = "Create user accounts",
description = "Create accounts when authenticating users via an external system",
project = false,
global = false,
defaultValue = "true",
type = PropertyType.BOOLEAN),
@Property(
key = CoreProperties.CORE_AUTHENTICATOR_IGNORE_STARTUP_FAILURE,
name = "Ignore failures during authenticator startup",
defaultValue = "false",
project = false,
global = false,
type = PropertyType.BOOLEAN)
})
public final class CorePlugin extends SonarPlugin {


+ 7
- 9
plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdPlugin.java View File

@@ -19,10 +19,8 @@
*/
package org.sonar.plugins.cpd;

import org.sonar.api.CoreProperties;
import org.sonar.api.Properties;
import org.sonar.api.Property;
import org.sonar.api.SonarPlugin;
import org.sonar.api.*;
import org.sonar.api.PropertyType;
import org.sonar.plugins.cpd.decorators.DuplicationDensityDecorator;
import org.sonar.plugins.cpd.decorators.SumDuplicationsDecorator;
import org.sonar.plugins.cpd.index.IndexFactory;
@@ -43,7 +41,7 @@ import java.util.List;
module = true,
global = true,
category = CoreProperties.CATEGORY_DUPLICATIONS,
type = Property.Type.SINGLE_SELECT_LIST,
type = PropertyType.SINGLE_SELECT_LIST,
options = {"sonar", "pmd"}),
@Property(
key = CoreProperties.CPD_CROSS_RPOJECT,
@@ -56,7 +54,7 @@ import java.util.List;
module = true,
global = true,
category = CoreProperties.CATEGORY_DUPLICATIONS,
type = Property.Type.BOOLEAN),
type = PropertyType.BOOLEAN),
@Property(
key = CoreProperties.CPD_MINIMUM_TOKENS_PROPERTY,
defaultValue = CoreProperties.CPD_MINIMUM_TOKENS_DEFAULT_VALUE + "",
@@ -67,7 +65,7 @@ import java.util.List;
module = true,
global = true,
category = CoreProperties.CATEGORY_DUPLICATIONS,
type = Property.Type.INTEGER),
type = PropertyType.INTEGER),
@Property(
key = CoreProperties.CPD_IGNORE_LITERALS_PROPERTY,
defaultValue = CoreProperties.CPD_IGNORE_LITERALS_DEFAULT_VALUE + "",
@@ -79,7 +77,7 @@ import java.util.List;
module = true,
global = true,
category = CoreProperties.CATEGORY_DUPLICATIONS,
type = Property.Type.BOOLEAN),
type = PropertyType.BOOLEAN),
@Property(
key = CoreProperties.CPD_IGNORE_IDENTIFIERS_PROPERTY,
defaultValue = CoreProperties.CPD_IGNORE_IDENTIFIERS_DEFAULT_VALUE + "",
@@ -90,7 +88,7 @@ import java.util.List;
module = true,
global = true,
category = CoreProperties.CATEGORY_DUPLICATIONS,
type = Property.Type.BOOLEAN)
type = PropertyType.BOOLEAN)
})
public final class CpdPlugin extends SonarPlugin {


+ 4
- 3
plugins/sonar-dbcleaner-plugin/src/main/java/org/sonar/plugins/dbcleaner/DbCleanerPlugin.java View File

@@ -21,6 +21,7 @@ package org.sonar.plugins.dbcleaner;

import org.sonar.api.Properties;
import org.sonar.api.Property;
import org.sonar.api.PropertyType;
import org.sonar.api.SonarPlugin;
import org.sonar.plugins.dbcleaner.api.DbCleanerConstants;
import org.sonar.plugins.dbcleaner.period.DefaultPeriodCleaner;
@@ -35,20 +36,20 @@ import java.util.List;
+ "the DbCleaner keeps the first one and fully delete the other ones.",
global = true,
project = true,
type = Property.Type.INTEGER),
type = PropertyType.INTEGER),
@Property(key = DbCleanerConstants.WEEKS_BEFORE_KEEPING_ONLY_ONE_SNAPSHOT_BY_MONTH, defaultValue = "52",
name = "Number of weeks before starting to keep only one snapshot by month",
description = "After this number of weeks, if there are several snapshots during the same month, "
+ "the DbCleaner keeps the first one and fully delete the other ones.",
global = true,
project = true,
type = Property.Type.INTEGER),
type = PropertyType.INTEGER),
@Property(key = DbCleanerConstants.WEEKS_BEFORE_DELETING_ALL_SNAPSHOTS, defaultValue = "260",
name = "Number of weeks before starting to delete all remaining snapshots",
description = "After this number of weeks, all snapshots are fully deleted.",
global = true,
project = true,
type = Property.Type.INTEGER)
type = PropertyType.INTEGER)
}
)
public final class DbCleanerPlugin extends SonarPlugin {

+ 2
- 1
plugins/sonar-dbcleaner-plugin/src/main/java/org/sonar/plugins/dbcleaner/DefaultPurgeTask.java View File

@@ -23,6 +23,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.Properties;
import org.sonar.api.Property;
import org.sonar.api.PropertyType;
import org.sonar.api.config.Settings;
import org.sonar.api.resources.Scopes;
import org.sonar.core.purge.PurgeDao;
@@ -41,7 +42,7 @@ import org.sonar.plugins.dbcleaner.period.DefaultPeriodCleaner;
global = true,
project = true,
module = false,
type = Property.Type.BOOLEAN)
type = PropertyType.BOOLEAN)
})
public class DefaultPurgeTask implements PurgeTask {
private static final Logger LOG = LoggerFactory.getLogger(DefaultPurgeTask.class);

+ 2
- 1
plugins/sonar-findbugs-plugin/src/main/java/org/sonar/plugins/findbugs/FindbugsPlugin.java View File

@@ -20,6 +20,7 @@
package org.sonar.plugins.findbugs;

import org.sonar.api.*;
import org.sonar.api.PropertyType;

import java.util.ArrayList;
import java.util.List;
@@ -43,7 +44,7 @@ import java.util.List;
project = true,
module = true,
global = true,
type = Property.Type.INTEGER),
type = PropertyType.INTEGER),
@Property(
key = FindbugsConstants.EXCLUDES_FILTERS_PROPERTY,
name = "Excludes Filters",

+ 6
- 0
plugins/sonar-l10n-en-plugin/src/main/resources/org/sonar/l10n/core.properties View File

@@ -549,6 +549,12 @@ property.category.duplications=Duplications
property.category.localization=Localization
property.category.server_id=Server ID

property.error.notBoolean=Valid options are "true" and "false"
property.error.notInteger=Only digits are allowed
property.error.notFloat=Not a floating point number
property.error.notInOptions=Not a valid option



#------------------------------------------------------------------------------
#

+ 4
- 6
plugins/sonar-squid-java-plugin/src/main/java/org/sonar/plugins/squid/SquidPlugin.java View File

@@ -19,10 +19,8 @@
*/
package org.sonar.plugins.squid;

import org.sonar.api.CoreProperties;
import org.sonar.api.Properties;
import org.sonar.api.Property;
import org.sonar.api.SonarPlugin;
import org.sonar.api.*;
import org.sonar.api.PropertyType;
import org.sonar.plugins.squid.decorators.*;

import java.util.Arrays;
@@ -37,7 +35,7 @@ import java.util.List;
project = true,
global = true,
category = CoreProperties.CATEGORY_JAVA,
type = Property.Type.BOOLEAN),
type = PropertyType.BOOLEAN),
@Property(key = SquidPluginProperties.FIELDS_TO_EXCLUDE_FROM_LCOM4_COMPUTATION,
defaultValue = SquidPluginProperties.FIELDS_TO_EXCLUDE_FROM_LCOM4_COMPUTATION_DEFAULT_VALUE,
name = "List of fields to exclude from LCOM4 computation",
@@ -55,7 +53,7 @@ import java.util.List;
project = true,
global = true,
category = CoreProperties.CATEGORY_JAVA,
type = Property.Type.BOOLEAN)
type = PropertyType.BOOLEAN)
})
public final class SquidPlugin extends SonarPlugin {


+ 1
- 8
sonar-plugin-api/src/main/java/org/sonar/api/Property.java View File

@@ -45,13 +45,6 @@ import java.lang.annotation.Target;
@Target(ElementType.TYPE)
public @interface Property {

/**
* @since 2.15
*/
public static enum Type {
STRING, TEXT, PASSWORD, BOOLEAN, INTEGER, FLOAT, SINGLE_SELECT_LIST
}

/**
* Unique key within all plugins. It's recommended to prefix the key by 'sonar.' and the plugin name. Examples :
* 'sonar.cobertura.reportPath' and 'sonar.cpd.minimumTokens'.
@@ -87,7 +80,7 @@ public @interface Property {
/**
* @since 2.15
*/
Type type() default Type.STRING;
PropertyType type() default PropertyType.STRING;

/**
* Options for *_LIST types

+ 24
- 0
sonar-plugin-api/src/main/java/org/sonar/api/PropertyType.java View File

@@ -0,0 +1,24 @@
/*
* Sonar, open source software quality management tool.
* Copyright (C) 2008-2012 SonarSource
* mailto:contact AT sonarsource DOT com
*
* Sonar 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.
*
* Sonar 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 Sonar; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
*/
package org.sonar.api;

public enum PropertyType {
STRING, TEXT, PASSWORD, BOOLEAN, INTEGER, FLOAT, SINGLE_SELECT_LIST
}

+ 2
- 1
sonar-plugin-api/src/main/java/org/sonar/api/config/AesCipher.java View File

@@ -27,6 +27,7 @@ import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.sonar.api.CoreProperties;

import javax.annotation.Nullable;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
@@ -92,7 +93,7 @@ final class AesCipher extends Cipher {
}

@VisibleForTesting
Key loadSecretFileFromFile(String path) throws NoSuchAlgorithmException, InvalidKeySpecException, IOException, InvalidKeyException {
Key loadSecretFileFromFile(@Nullable String path) throws NoSuchAlgorithmException, InvalidKeySpecException, IOException, InvalidKeyException {
if (StringUtils.isBlank(path)) {
throw new IllegalStateException("Secret key not found. Please set the property " + CoreProperties.ENCRYPTION_SECRET_KEY_FILE);
}

+ 158
- 0
sonar-plugin-api/src/main/java/org/sonar/api/config/PropertyDefinition.java View File

@@ -0,0 +1,158 @@
/*
* Sonar, open source software quality management tool.
* Copyright (C) 2008-2012 SonarSource
* mailto:contact AT sonarsource DOT com
*
* Sonar 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.
*
* Sonar 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 Sonar; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
*/
package org.sonar.api.config;

import com.google.common.annotations.VisibleForTesting;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.sonar.api.Property;
import org.sonar.api.PropertyType;

import javax.annotation.Nullable;

/**
* @since 2.15
*/
public final class PropertyDefinition {

public static final class Result {
private static final Result SUCCESS = new Result(null);

private String errorKey = null;

private static Result newError(String key) {
return new Result(key);
}

private Result(String errorKey) {
this.errorKey = errorKey;
}

public boolean isValid() {
return StringUtils.isBlank(errorKey);
}

public @Nullable String getErrorKey() {
return errorKey;
}
}

private String key;
private String defaultValue;
private String name;
private PropertyType type = PropertyType.STRING;
private String[] options;
private String description;
private String category;
private boolean onProject = false;
private boolean onModule = false;
private boolean isGlobal = true;

private PropertyDefinition(Property annotation) {
this.key = annotation.key();
this.name = annotation.name();
this.defaultValue = annotation.defaultValue();
this.description = annotation.description();
this.isGlobal = annotation.global();
this.onProject = annotation.project();
this.onModule = annotation.module();
this.category = annotation.category();
this.type = annotation.type();
this.options = annotation.options();
}

@VisibleForTesting
PropertyDefinition(PropertyType type, String[] options) {
this.type = type;
this.options = options;
}

public static PropertyDefinition create(Property annotation) {
return new PropertyDefinition(annotation);
}

public Result validate(@Nullable String value) {
// TODO REFACTORING REQUIRED HERE
Result result = Result.SUCCESS;
if (StringUtils.isNotBlank(value)) {
if (type == PropertyType.BOOLEAN) {
if (!StringUtils.equalsIgnoreCase(value, "true") && !StringUtils.equalsIgnoreCase(value, "false")) {
result = Result.newError("notBoolean");
}
} else if (type == PropertyType.INTEGER) {
if (!NumberUtils.isDigits(value)) {
result = Result.newError("notInteger");
}
} else if (type == PropertyType.FLOAT) {
try {
Double.parseDouble(value);
} catch (NumberFormatException e) {
result = Result.newError("notFloat");
}
} else if (type == PropertyType.SINGLE_SELECT_LIST) {
if (!ArrayUtils.contains(options, value)) {
result = Result.newError("notInOptions");
}
}
}
return result;
}

public String getKey() {
return key;
}

public String getDefaultValue() {
return defaultValue;
}

public String getName() {
return name;
}

public PropertyType getType() {
return type;
}

public String[] getOptions() {
return options;
}

public String getDescription() {
return description;
}

public String getCategory() {
return category;
}

public boolean isOnProject() {
return onProject;
}

public boolean isOnModule() {
return onModule;
}

public boolean isGlobal() {
return isGlobal;
}
}

+ 15
- 14
sonar-plugin-api/src/main/java/org/sonar/api/config/PropertyDefinitions.java View File

@@ -38,7 +38,7 @@ import java.util.Map;
*/
public final class PropertyDefinitions implements BatchComponent, ServerComponent {

private Map<String, Property> properties = Maps.newHashMap();
private Map<String, PropertyDefinition> definitions = Maps.newHashMap();
private Map<String, String> categories = Maps.newHashMap();

public PropertyDefinitions(Object... components) {
@@ -76,30 +76,31 @@ public final class PropertyDefinitions implements BatchComponent, ServerComponen
return this;
}

PropertyDefinitions addProperty(Property property) {
return addProperty(property, "");
private PropertyDefinitions addProperty(Property property, String defaultCategory) {
PropertyDefinition definition = PropertyDefinition.create(property);
return add(definition, defaultCategory);
}

PropertyDefinitions addProperty(Property property, String defaultCategory) {
if (!properties.containsKey(property.key())) {
properties.put(property.key(), property);
categories.put(property.key(), StringUtils.defaultIfBlank(property.category(), defaultCategory));
private PropertyDefinitions add(PropertyDefinition definition, String defaultCategory) {
if (!definitions.containsKey(definition.getKey())) {
definitions.put(definition.getKey(), definition);
categories.put(definition.getKey(), StringUtils.defaultIfBlank(definition.getCategory(), defaultCategory));
}
return this;
}

public Property getProperty(String key) {
return properties.get(key);
public PropertyDefinition get(String key) {
return definitions.get(key);
}

public Collection<Property> getProperties() {
return properties.values();
public Collection<PropertyDefinition> getAll() {
return definitions.values();
}

public String getDefaultValue(String key) {
Property prop = getProperty(key);
if (prop != null) {
return StringUtils.defaultIfEmpty(prop.defaultValue(), null);
PropertyDefinition def = get(key);
if (def != null) {
return StringUtils.defaultIfEmpty(def.getDefaultValue(), null);
}
return null;
}

+ 23
- 0
sonar-plugin-api/src/main/java/org/sonar/api/config/package-info.java View File

@@ -0,0 +1,23 @@
/*
* Sonar, open source software quality management tool.
* Copyright (C) 2008-2012 SonarSource
* mailto:contact AT sonarsource DOT com
*
* Sonar 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.
*
* Sonar 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 Sonar; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
*/
@ParametersAreNonnullByDefault
package org.sonar.api.config;

import javax.annotation.ParametersAreNonnullByDefault;

+ 0
- 7
sonar-plugin-api/src/main/java/org/sonar/api/rules/RulesCategory.java View File

@@ -22,15 +22,8 @@ package org.sonar.api.rules;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.annotations.Immutable;
import org.sonar.api.database.BaseIdentifiable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;

/**
* @deprecated since 2.5 See http://jira.codehaus.org/browse/SONAR-2007
*/

+ 153
- 0
sonar-plugin-api/src/test/java/org/sonar/api/config/PropertyDefinitionTest.java View File

@@ -0,0 +1,153 @@
/*
* Sonar, open source software quality management tool.
* Copyright (C) 2008-2012 SonarSource
* mailto:contact AT sonarsource DOT com
*
* Sonar 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.
*
* Sonar 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 Sonar; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
*/
package org.sonar.api.config;

import org.hamcrest.core.Is;
import org.junit.Test;
import org.sonar.api.Properties;
import org.sonar.api.Property;
import org.sonar.api.PropertyType;
import org.sonar.api.utils.AnnotationUtils;

import java.util.Arrays;

import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;
import static org.junit.matchers.JUnitMatchers.hasItems;

public class PropertyDefinitionTest {

@Test
public void createFromAnnotation() {
Properties props = AnnotationUtils.getClassAnnotation(Init.class, Properties.class);
Property prop = props.value()[0];

PropertyDefinition def = PropertyDefinition.create(prop);

assertThat(def.getKey(), Is.is("hello"));
assertThat(def.getName(), Is.is("Hello"));
assertThat(def.getDefaultValue(), Is.is("world"));
assertThat(def.getCategory(), Is.is("categ"));
assertThat(def.getOptions().length, Is.is(2));
assertThat(Arrays.asList(def.getOptions()), hasItems("de", "en"));
assertThat(def.getDescription(), Is.is("desc"));
assertThat(def.getType(), Is.is(PropertyType.FLOAT));
assertThat(def.isGlobal(), Is.is(false));
assertThat(def.isOnProject(), Is.is(true));
assertThat(def.isOnModule(), Is.is(true));
}

@Properties({
@Property(key = "hello", name = "Hello", defaultValue = "world", description = "desc",
options = {"de", "en"}, category = "categ", type = PropertyType.FLOAT, global = false, project = true, module = true)
})
static class Init {
}

@Test
public void createFromAnnotation_default_values() {
Properties props = AnnotationUtils.getClassAnnotation(DefaultValues.class, Properties.class);
Property prop = props.value()[0];

PropertyDefinition def = PropertyDefinition.create(prop);

assertThat(def.getKey(), Is.is("hello"));
assertThat(def.getName(), Is.is("Hello"));
assertThat(def.getDefaultValue(), Is.is(""));
assertThat(def.getCategory(), Is.is(""));
assertThat(def.getOptions().length, Is.is(0));
assertThat(def.getDescription(), Is.is(""));
assertThat(def.getType(), Is.is(PropertyType.STRING));
assertThat(def.isGlobal(), Is.is(true));
assertThat(def.isOnProject(), Is.is(false));
assertThat(def.isOnModule(), Is.is(false));
}

@Properties({
@Property(key = "hello", name = "Hello")
})
static class DefaultValues {
}

@Test
public void validate_string() {
PropertyDefinition def = new PropertyDefinition(PropertyType.STRING, new String[0]);

assertThat(def.validate(null).isValid(), is(true));
assertThat(def.validate("").isValid(), is(true));
assertThat(def.validate(" ").isValid(), is(true));
assertThat(def.validate("foo").isValid(), is(true));
}

@Test
public void validate_boolean() {
PropertyDefinition def = new PropertyDefinition(PropertyType.BOOLEAN, new String[0]);

assertThat(def.validate(null).isValid(), is(true));
assertThat(def.validate("").isValid(), is(true));
assertThat(def.validate(" ").isValid(), is(true));
assertThat(def.validate("true").isValid(), is(true));
assertThat(def.validate("false").isValid(), is(true));

assertThat(def.validate("foo").isValid(), is(false));
assertThat(def.validate("foo").getErrorKey(), is("notBoolean"));
}

@Test
public void validate_integer() {
PropertyDefinition def = new PropertyDefinition(PropertyType.INTEGER, new String[0]);

assertThat(def.validate(null).isValid(), is(true));
assertThat(def.validate("").isValid(), is(true));
assertThat(def.validate(" ").isValid(), is(true));
assertThat(def.validate("123456").isValid(), is(true));

assertThat(def.validate("foo").isValid(), is(false));
assertThat(def.validate("foo").getErrorKey(), is("notInteger"));
}

@Test
public void validate_float() {
PropertyDefinition def = new PropertyDefinition(PropertyType.FLOAT, new String[0]);

assertThat(def.validate(null).isValid(), is(true));
assertThat(def.validate("").isValid(), is(true));
assertThat(def.validate(" ").isValid(), is(true));
assertThat(def.validate("123456").isValid(), is(true));
assertThat(def.validate("3.14").isValid(), is(true));

assertThat(def.validate("foo").isValid(), is(false));
assertThat(def.validate("foo").getErrorKey(), is("notFloat"));
}

@Test
public void validate_single_select_list() {
PropertyDefinition def = new PropertyDefinition(PropertyType.SINGLE_SELECT_LIST, new String[]{"de", "en"});

assertThat(def.validate(null).isValid(), is(true));
assertThat(def.validate("").isValid(), is(true));
assertThat(def.validate(" ").isValid(), is(true));
assertThat(def.validate("de").isValid(), is(true));
assertThat(def.validate("en").isValid(), is(true));

assertThat(def.validate("fr").isValid(), is(false));
assertThat(def.validate("fr").getErrorKey(), is("notInOptions"));
}
}

+ 8
- 8
sonar-plugin-api/src/test/java/org/sonar/api/config/PropertyDefinitionsTest.java View File

@@ -43,16 +43,16 @@ public class PropertyDefinitionsTest {
assertProperties(def);
}

private void assertProperties(PropertyDefinitions def) {
assertThat(def.getProperty("foo").name(), is("Foo"));
assertThat(def.getProperty("one").name(), is("One"));
assertThat(def.getProperty("two").name(), is("Two"));
assertThat(def.getProperty("unknown"), nullValue());
private void assertProperties(PropertyDefinitions definitions) {
assertThat(definitions.get("foo").getName(), is("Foo"));
assertThat(definitions.get("one").getName(), is("One"));
assertThat(definitions.get("two").getName(), is("Two"));
assertThat(definitions.get("unknown"), nullValue());

assertThat(def.getDefaultValue("foo"), nullValue());
assertThat(def.getDefaultValue("two"), is("2"));
assertThat(definitions.getDefaultValue("foo"), nullValue());
assertThat(definitions.getDefaultValue("two"), is("2"));

assertThat(def.getProperties().size(), is(3));
assertThat(definitions.getAll().size(), is(3));
}

@Property(key = "foo", name = "Foo")

+ 13
- 15
sonar-plugin-api/src/test/java/org/sonar/api/config/SettingsTest.java View File

@@ -23,34 +23,32 @@ import com.google.common.collect.ImmutableMap;
import org.hamcrest.CoreMatchers;
import org.junit.Before;
import org.junit.Test;
import org.sonar.api.Properties;
import org.sonar.api.Property;

import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

public class SettingsTest {

private PropertyDefinitions definitions;

@Properties({
@Property(key = "hello", name = "Hello", defaultValue = "world"),
@Property(key = "date", name = "Date", defaultValue = "2010-05-18"),
@Property(key = "boolean", name = "Boolean", defaultValue = "true"),
@Property(key = "falseboolean", name = "False Boolean", defaultValue = "false"),
@Property(key = "integer", name = "Integer", defaultValue = "12345"),
@Property(key = "array", name = "Array", defaultValue = "one,two,three")
})
static class Init {
}

@Before
public void initDefinitions() {
definitions = new PropertyDefinitions();
definitions.addProperty(newProperty("hello", "world"));
definitions.addProperty(newProperty("date", "2010-05-18"));
definitions.addProperty(newProperty("boolean", "true"));
definitions.addProperty(newProperty("falseboolean", "false"));
definitions.addProperty(newProperty("integer", "12345"));
definitions.addProperty(newProperty("array", "one,two,three"));
}

private Property newProperty(String key, String defaultValue) {
Property prop = mock(Property.class);
when(prop.key()).thenReturn(key);
when(prop.defaultValue()).thenReturn(defaultValue);
return prop;
definitions.addComponent(Init.class);
}

@Test

+ 4
- 4
sonar-plugin-api/src/test/java/org/sonar/api/platform/ComponentContainerTest.java View File

@@ -102,8 +102,8 @@ public class ComponentContainerTest {
container.addSingleton(ComponentWithProperty.class);

PropertyDefinitions propertyDefinitions = container.getComponentByType(PropertyDefinitions.class);
assertThat(propertyDefinitions.getProperty("foo"), notNullValue());
assertThat(propertyDefinitions.getProperty("foo").defaultValue(), is("bar"));
assertThat(propertyDefinitions.get("foo"), notNullValue());
assertThat(propertyDefinitions.get("foo").getDefaultValue(), is("bar"));
}

@Test
@@ -113,7 +113,7 @@ public class ComponentContainerTest {
container.declareExtension(plugin, ComponentWithProperty.class);

PropertyDefinitions propertyDefinitions = container.getComponentByType(PropertyDefinitions.class);
assertThat(propertyDefinitions.getProperty("foo"), notNullValue());
assertThat(propertyDefinitions.get("foo"), notNullValue());
assertThat(container.getComponentByType(ComponentWithProperty.class), nullValue());
}

@@ -124,7 +124,7 @@ public class ComponentContainerTest {
container.addExtension(plugin, ComponentWithProperty.class);

PropertyDefinitions propertyDefinitions = container.getComponentByType(PropertyDefinitions.class);
assertThat(propertyDefinitions.getProperty("foo"), notNullValue());
assertThat(propertyDefinitions.get("foo"), notNullValue());
assertThat(container.getComponentByType(ComponentWithProperty.class), notNullValue());
}


+ 2
- 1
sonar-server/src/main/java/org/sonar/server/plugins/UpdateCenterClient.java View File

@@ -23,6 +23,7 @@ import org.apache.commons.io.IOUtils;
import org.slf4j.LoggerFactory;
import org.sonar.api.Properties;
import org.sonar.api.Property;
import org.sonar.api.PropertyType;
import org.sonar.api.ServerComponent;
import org.sonar.api.config.Settings;
import org.sonar.api.utils.HttpDownloader;
@@ -49,7 +50,7 @@ import java.util.Date;
project = false,
global = false, // hidden from UI
category = "Update Center",
type = Property.Type.BOOLEAN),
type = PropertyType.BOOLEAN),
@Property(
key = UpdateCenterClient.URL_PROPERTY,
defaultValue = "http://update.sonarsource.org/update-center.properties",

+ 5
- 5
sonar-server/src/main/webapp/WEB-INF/app/controllers/project_controller.rb View File

@@ -126,13 +126,13 @@ class ProjectController < ApplicationController
end

@category=params[:category] ||= 'general'
@properties_per_category={}
@definitions_per_category={}
definitions = java_facade.getPropertyDefinitions()
properties = definitions.getProperties().select { |property| (@project.module? && property.module()) || (@project.project? && property.project()) }
properties = definitions.getAll().select { |property| (@project.module? && property.isOnModule()) || (@project.project? && property.isOnProject()) }
properties.each do |property|
category = definitions.getCategory(property.key())
@properties_per_category[category]||=[]
@properties_per_category[category]<<property
category = definitions.getCategory(property.getKey())
@definitions_per_category[category]||=[]
@definitions_per_category[category]<<property
end
end


+ 26
- 26
sonar-server/src/main/webapp/WEB-INF/app/controllers/settings_controller.rb View File

@@ -20,7 +20,7 @@
class SettingsController < ApplicationController

SECTION=Navigation::SECTION_CONFIGURATION
SPECIAL_CATEGORIES=['email', 'encryption', 'server_id']

verify :method => :post, :only => ['update'], :redirect_to => {:action => :index}
@@ -32,39 +32,39 @@ class SettingsController < ApplicationController
end

def update
project=nil
@project=nil
if params[:resource_id]
project=Project.by_key(params[:resource_id])
access_denied unless (project && is_admin?(project))
@project=Project.by_key(params[:resource_id])
access_denied unless (@project && is_admin?(@project))
else
access_denied unless is_admin?
end

load_properties(project)
load_properties(@project)

@persisted_properties_per_key={}
if @category && @definitions_per_category[@category]
@definitions_per_category[@category].each do |property|
value=params[property.getKey()]

if @category && @properties_per_category[@category]
@properties_per_category[@category].each do |property|
value=params[property.key()]
persisted_property = Property.find(:first, :conditions => {:prop_key=> property.key(), :resource_id => (project ? project.id : nil), :user_id => nil})
persisted_property = Property.find(:first, :conditions => {:prop_key=> property.key(), :resource_id => (@project ? @project.id : nil), :user_id => nil})
if persisted_property
if value.empty?
Property.delete_all('prop_key' => property.key(), 'resource_id' => (project ? project.id : nil), 'user_id' => nil)
Property.delete_all('prop_key' => property.key(), 'resource_id' => (@project ? @project.id : nil), 'user_id' => nil)
elsif persisted_property.text_value != value.to_s
persisted_property.text_value = value.to_s
persisted_property.save!
persisted_property.save
@persisted_properties_per_key[persisted_property.key]=persisted_property
end
elsif !value.blank?
Property.create(:prop_key => property.key(), :text_value => value.to_s, :resource_id => (project ? project.id : nil))
persisted_property=Property.create(:prop_key => property.key(), :text_value => value.to_s, :resource_id => (@project ? @project.id : nil))
@persisted_properties_per_key[persisted_property.key]=persisted_property
end
end
java_facade.reloadConfiguration()
flash[:notice] = 'Parameters updated'
end

if project
redirect_to :controller => 'project', :action => 'settings', :id => project.id, :category => @category
else
redirect_to :controller => 'settings', :action => 'index', :category => @category
params[:layout]='false'
render :partial => 'settings/properties'
end
end

@@ -72,18 +72,18 @@ class SettingsController < ApplicationController

def load_properties(project)
@category=params[:category]
@properties_per_category={}
@definitions_per_category={}
definitions = java_facade.getPropertyDefinitions()
definitions.getProperties().select {|property|
(project.nil? && property.global) || (project && project.module? && property.module()) || (project && project.project? && property.project())
}.each do |property|
category = definitions.getCategory(property.key())
@properties_per_category[category]||=[]
@properties_per_category[category]<<property
definitions.getAll().select { |property_definition|
(project.nil? && property_definition.isGlobal()) || (project && project.module? && property_definition.isOnModule()) || (project && project.project? && property_definition.isOnProject())
}.each do |property_definition|
category = definitions.getCategory(property_definition.getKey())
@definitions_per_category[category]||=[]
@definitions_per_category[category]<<property_definition
end

SPECIAL_CATEGORIES.each do |category|
@properties_per_category[category]=[]
@definitions_per_category[category]=[]
end
end
end

+ 15
- 0
sonar-server/src/main/webapp/WEB-INF/app/models/property.rb View File

@@ -18,6 +18,7 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
#
class Property < ActiveRecord::Base
validates_presence_of :prop_key

def key
prop_key
@@ -87,8 +88,22 @@ class Property < ActiveRecord::Base
xml
end

def java_definition
@java_definition ||=
begin
Java::OrgSonarServerUi::JRubyFacade.getInstance().getPropertyDefinitions().get(key)
end
end

private

def validate
if java_definition
validation_result=java_definition.validate(text_value)
errors.add_to_base(validation_result.getErrorKey()) unless validation_result.isValid()
end
end

def self.reload_java_configuration
Java::OrgSonarServerUi::JRubyFacade.getInstance().reloadConfiguration()
end

+ 1
- 1
sonar-server/src/main/webapp/WEB-INF/app/views/project/settings.html.erb View File

@@ -3,7 +3,7 @@
<h1 class="marginbottom10">Settings</h1>
<div class="yui-g widget" id="widget_plugins">
<%= render :partial => 'settings/plugins', :locals => {:project=>@project} %>
<%= render :partial => 'settings/settings', :locals => {:project=>@project} %>
</div>

</div>

+ 0
- 128
sonar-server/src/main/webapp/WEB-INF/app/views/settings/_plugins.html.erb View File

@@ -1,128 +0,0 @@
<style type="text/css">
#plugins .plugin {
padding: 5px;
border: 1px solid #ddd;
background-color: #fff;
}

#plugins .plugin h2 {
margin-left: 10px;
font-size: 122%;
color: #333;
}

#plugins .plugin h3 {
margin-left: 5px;
}

#plugins .plugin p {
padding: 5px 5px;
}

#plugins .plugin img {
padding: 5px 0 0 5px;
}
</style>
<script type="text/javascript">
function enlargeTextInput(propertyKey) {
var eltId = 'input_' + propertyKey;
var textValue = $F(eltId);
var textArea = '<textarea class="width100" id="' + propertyKey + '" name="' + propertyKey + '" rows="10" id="input_' + propertyKey + '">' + textValue + '</textarea>';
$(eltId).parentNode.replace(textArea);
}
</script>
<div id="plugins">
<table width="100%">
<tr>
<td width="1%" nowrap class="column first">
<table class="data selector">
<thead>
<tr>
<th>
<span>Category</span>
</th>
</tr>
</thead>
<tbody>
<%
@properties_per_category.keys.sort_by { |category| message("property.category.#{category}", :default => category).upcase }.each do |category|
if !@properties_per_category[category].empty? || SettingsController::SPECIAL_CATEGORIES.include?(category)
%>
<tr class="select <%= cycle('even', 'odd', :name => 'category') -%> <%= 'selected' if @category==category -%>" id="select_<%= category -%>">
<td><%= link_to message("property.category.#{category}", :default => category), :overwrite_params => {:category => category} -%></td>
</tr>
<% end
end
%>
</tbody>
</table>
<br/>
</td>

<td class="column">
<% if @category && @properties_per_category[@category]
category_name = message("property.category.#{@category}", :default => @category)
if SettingsController::SPECIAL_CATEGORIES.include?(@category)
%>
<%= render :partial => 'special', :locals => {:url => url_for(:controller => "#{@category}_configuration")} -%>
<%
elsif !@properties_per_category[@category].empty?
%>
<% form_tag :controller => :settings, :action => :update do %>
<%= hidden_field_tag('category', @category) -%>
<% if @project %>
<input type="hidden" name="resource_id" value="<%= @project.id -%>"/>
<% end %>
<table class="data marginbottom10">
<thead>
<tr>
<th>
<span><%= h(category_name) -%></span>
</th>
</tr>
</thead>
<tbody>
<%
if @properties_per_category[@category]
@properties_per_category[@category].each do |property|
value = Property.value(property.key(), (@project ? @project.id : nil), '')

# for backward-compatibility with properties that do not define the type TEXT
property_type = value.include?("\n") ? 'TEXT' : property.type
%>
<tr class="<%= cycle('even', 'odd', :name => 'properties') -%>">
<td style="padding: 10px">
<h3>
<%= message("property.#{property.key()}.name", :default => property.name()) -%>
<br/><span class="note"><%= property.key() -%></span>
</h3>
<%
desc=message("property.#{property.key()}.description", :default => property.description())
if desc.present? %>
<p class="marginbottom10"><%= desc -%></p>
<% end %>
<div><%= render :partial => "settings/type_#{property_type}", :locals => {:property => property, :value => value} -%></div>
<p>
<%
default_prop_value = (@project ? Property.value(property.key(), nil, property.defaultValue()) : property.defaultValue())
unless default_prop_value.blank? %>
<span class="note">Default : <%= h default_prop_value -%></span>
<% end %>
</p>
</td>
</tr>
<% end
end
%>
</tbody>
</table>
<% save_message=message('settings.save_category', :params => [category_name]) %>
<%= submit_tag(save_message, :disable_with => save_message, :id => 'save') -%>
<% end %>
<% end
end
%>
</td>
</tr>
</table>
</div>

+ 85
- 0
sonar-server/src/main/webapp/WEB-INF/app/views/settings/_properties.html.erb View File

@@ -0,0 +1,85 @@
<% if @category && @definitions_per_category[@category]
category_name = message("property.category.#{@category}", :default => @category)
if SettingsController::SPECIAL_CATEGORIES.include?(@category)
%>
<%= render :partial => 'special', :locals => {:url => url_for(:controller => "#{@category}_configuration")} -%>
<%
elsif !@definitions_per_category[@category].empty?
%>
<% form_remote_tag :url => {:controller => 'settings', :action => 'update', :category => @category, :resource_id => @project ? @project.id : nil},
:method => :post,
:before => "$('submit_settings').hide();$('loading_settings').show()",
:update => 'properties' do -%>

<table class="data marginbottom10">
<thead>
<tr>
<th>
<span><%= h(category_name) -%></span>
</th>
</tr>
</thead>
<tbody>
<%
if @definitions_per_category[@category]
@definitions_per_category[@category].each do |property|
value = nil

# set when form has been submitted but some errors have been raised
if @persisted_properties_per_key
p = @persisted_properties_per_key[property.key]
if p
value = p.text_value
end
end


# if fresh form or no error, get the current value
value = Property.value(property.getKey(), (@project ? @project.id : nil), '') unless value

# for backward-compatibility with properties that do not define the type TEXT
property_type = value.include?("\n") ? 'TEXT' : property.getType()
%>
<tr class="<%= cycle('even', 'odd', :name => 'properties') -%>">
<td style="padding: 10px" id="foo_<%= property.getKey() -%>">
<h3>
<%= message("property.#{property.key()}.name", :default => property.name()) -%>
<br/><span class="note"><%= property.getKey() -%></span>
</h3>
<%
desc=message("property.#{property.key()}.description", :default => property.description())
if desc.present? %>
<p class="marginbottom10"><%= desc -%></p>
<% end %>
<div><%= render :partial => "settings/type_#{property_type}", :locals => {:property => property, :value => value} -%></div>
<%
if p
p.errors.each_full do |error|
%>
<div class="error"><%= message("property.error.#{error}") -%></div>
<%
end
end
%>
<p>
<%
default_prop_value = (@project ? Property.value(property.key(), nil, property.defaultValue()) : property.defaultValue())
unless default_prop_value.blank? %>
<span class="note">Default : <%= h default_prop_value -%></span>
<% end %>
</p>
</td>
</tr>
<% end
end
%>
</tbody>
</table>
<div>
<%= submit_tag(message('settings.save_category', :params => [category_name]), :id => 'submit_settings') -%>
<img src="<%= ApplicationController.root_context -%>/images/loading.gif" id="loading_settings" style="display:none">
</div>
<% end %>
<% end
end
%>

+ 69
- 0
sonar-server/src/main/webapp/WEB-INF/app/views/settings/_settings.html.erb View File

@@ -0,0 +1,69 @@
<style type="text/css">
#plugins .plugin {
padding: 5px;
border: 1px solid #ddd;
background-color: #fff;
}

#plugins .plugin h2 {
margin-left: 10px;
font-size: 122%;
color: #333;
}

#plugins .plugin h3 {
margin-left: 5px;
}

#plugins .plugin p {
padding: 5px 5px;
}

#plugins .plugin img {
padding: 5px 0 0 5px;
}
</style>
<script type="text/javascript">
function enlargeTextInput(propertyKey) {
var eltId = 'input_' + propertyKey;
var textValue = $F(eltId);
var textArea = '<textarea class="width100" id="' + propertyKey + '" name="' + propertyKey + '" rows="10" id="input_' + propertyKey + '">' + textValue + '</textarea>';
$(eltId).parentNode.replace(textArea);
}
</script>
<div id="plugins">
<table width="100%">
<tr>
<td width="1%" nowrap class="column first">
<table class="data selector">
<thead>
<tr>
<th>
<span>Category</span>
</th>
</tr>
</thead>
<tbody>
<%
@definitions_per_category.keys.sort_by { |category| message("property.category.#{category}", :default => category).upcase }.each do |category|
if !@definitions_per_category[category].empty? || SettingsController::SPECIAL_CATEGORIES.include?(category)
%>
<tr class="select <%= cycle('even', 'odd', :name => 'category') -%> <%= 'selected' if @category==category -%>" id="select_<%= category -%>">
<td><%= link_to message("property.category.#{category}", :default => category), :overwrite_params => {:category => category} -%></td>
</tr>
<% end
end
%>
</tbody>
</table>
<br/>
</td>

<td class="column">
<div id="properties">
<%= render :partial => 'settings/properties' -%>
</div>
</td>
</tr>
</table>
</div>

+ 0
- 0
sonar-server/src/main/webapp/WEB-INF/app/views/settings/_sidebar.html.erb View File


+ 1
- 1
sonar-server/src/main/webapp/WEB-INF/app/views/settings/_type_SINGLE_SELECT_LIST.html.erb View File

@@ -1,6 +1,6 @@
<select name="<%= h property.key -%>" id="input_<%= h property.key-%>">
<option value=""><%= message('default') -%></option>
<% property.options.each do |option| %>
<option value="<%= h option -%>"><%= h option -%></option>
<option value="<%= h option -%>" <%= 'selected' if value && value==option -%>><%= h option -%></option>
<% end %>
</select>

+ 1
- 1
sonar-server/src/main/webapp/WEB-INF/app/views/settings/index.html.erb View File

@@ -1 +1 @@
<%= render :partial => 'plugins', :locals => {:project=>nil} %>
<%= render :partial => 'settings', :locals => {:project=>nil} %>

Loading…
Cancel
Save