aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-server/src
diff options
context:
space:
mode:
authorDavid Gageot <david@gageot.net>2012-09-20 17:05:20 +0200
committerDavid Gageot <david@gageot.net>2012-09-20 17:08:29 +0200
commitbb889f52fb08f10a175ff64b65edd0929a5a69c6 (patch)
treecbbc50b4af05e3b62c92db250bd694b78e7452d8 /sonar-server/src
parent1b000a65c28f43a3242c55f91a0cc7f6a210e979 (diff)
downloadsonarqube-bb889f52fb08f10a175ff64b65edd0929a5a69c6.tar.gz
sonarqube-bb889f52fb08f10a175ff64b65edd0929a5a69c6.zip
SONAR-3754 API: ability to define a cardinality on a property
Diffstat (limited to 'sonar-server/src')
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/app/controllers/api/authentication_controller.rb2
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/app/controllers/api/user_properties_controller.rb7
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/app/controllers/project_controller.rb98
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/app/controllers/settings_controller.rb96
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/app/helpers/project_helper.rb1
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/app/helpers/settings_helper.rb56
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/app/models/property.rb80
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/app/views/settings/_multi_value.html.erb5
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/app/views/settings/_properties.html.erb144
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/app/views/settings/_settings.html.erb86
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/app/views/settings/_sidebar.html.erb0
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/app/views/settings/_type_STRING.html.erb4
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/app/views/settings/index.html.erb2
-rw-r--r--sonar-server/src/main/webapp/stylesheets/style.css5
14 files changed, 291 insertions, 295 deletions
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/authentication_controller.rb b/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/authentication_controller.rb
index 92d1c85d95c..5554c8f4728 100644
--- a/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/authentication_controller.rb
+++ b/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/authentication_controller.rb
@@ -49,7 +49,7 @@ class Api::AuthenticationController < Api::ApiController
end
def force_authentication?
- property = Property.find(:first, :conditions => {:prop_key => org.sonar.api.CoreProperties.CORE_FORCE_AUTHENTICATION_PROPERTY, :resource_id => nil, :user_id => nil})
+ property = Property.by_key(org.sonar.api.CoreProperties.CORE_FORCE_AUTHENTICATION_PROPERTY)
property ? property.value == 'true' : false
end
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/user_properties_controller.rb b/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/user_properties_controller.rb
index fb98e5b6f1f..4cab0f299cf 100644
--- a/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/user_properties_controller.rb
+++ b/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/user_properties_controller.rb
@@ -43,7 +43,7 @@ class Api::UserPropertiesController < Api::ApiController
# curl http://localhost:9000/api/user_properties/<key> -v -u admin:admin
#
def show
- property = Property.find(:first, :conditions => ['user_id=? and prop_key=?', current_user.id, params[:id]])
+ property = Property.by_key(params[:id], nil, current_user.id)
if property
respond_to do |format|
format.json { render :json => jsonp(properties_to_json([property])) }
@@ -65,7 +65,7 @@ class Api::UserPropertiesController < Api::ApiController
value = params[:value]
if key
begin
- Property.delete_all(['prop_key=? AND user_id=?', key,current_user.id])
+ Property.clear(key, nil, current_user.id)
property=Property.create(:prop_key => key, :text_value => value, :user_id => current_user.id)
respond_to do |format|
format.json { render :json => jsonp(properties_to_json([property])) }
@@ -88,10 +88,9 @@ class Api::UserPropertiesController < Api::ApiController
def destroy
begin
if params[:id]
- Property.delete_all(['prop_key=? AND user_id=?', params[:id], current_user.id])
+ Property.clear(params[:id], nil, current_user.id)
end
render_success("Property deleted")
-
rescue Exception => e
logger.error("Fails to execute #{request.url} : #{e.message}")
render_error(e.message)
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/controllers/project_controller.rb b/sonar-server/src/main/webapp/WEB-INF/app/controllers/project_controller.rb
index de19cb7b14f..76918622d64 100644
--- a/sonar-server/src/main/webapp/WEB-INF/app/controllers/project_controller.rb
+++ b/sonar-server/src/main/webapp/WEB-INF/app/controllers/project_controller.rb
@@ -18,7 +18,7 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
#
class ProjectController < ApplicationController
- verify :method => :post, :only => [:set_links, :set_exclusions, :delete_exclusions, :update_key, :perform_key_bulk_update, :update_quality_profile],
+ verify :method => :post, :only => [:set_links, :set_exclusions, :delete_exclusions, :update_key, :perform_key_bulk_update, :update_quality_profile],
:redirect_to => {:action => :index}
verify :method => :delete, :only => [:delete], :redirect_to => {:action => :index}
@@ -33,11 +33,11 @@ class ProjectController < ApplicationController
if java_facade.getResourceTypeBooleanProperty(@project.qualifier, 'deletable')
deletion_manager = ResourceDeletionManager.instance
- if deletion_manager.currently_deleting_resources? ||
+ if deletion_manager.currently_deleting_resources? ||
(!deletion_manager.currently_deleting_resources? && deletion_manager.deletion_failures_occured?)
# a deletion is happening or it has just finished with errors => display the message from the Resource Deletion Manager
render :template => 'project/pending_deletion'
- else
+ else
@snapshot=@project.last_snapshot
end
else
@@ -47,19 +47,19 @@ class ProjectController < ApplicationController
def delete
@project = get_current_project(params[:id])
-
+
# Ask the resource deletion manager to start the migration
# => this is an asynchronous AJAX call
ResourceDeletionManager.instance.delete_resources([@project.id])
-
+
# and return some text that will actually never be displayed
render :text => ResourceDeletionManager.instance.message
end
-
+
def pending_deletion
deletion_manager = ResourceDeletionManager.instance
-
- if deletion_manager.currently_deleting_resources? ||
+
+ if deletion_manager.currently_deleting_resources? ||
(!deletion_manager.currently_deleting_resources? && deletion_manager.deletion_failures_occured?)
# display the same page again and again
# => implicit render "pending_deletion.html.erb"
@@ -67,24 +67,24 @@ class ProjectController < ApplicationController
redirect_to_default
end
end
-
+
def dismiss_deletion_message
# It is important to reinit the ResourceDeletionManager so that the deletion screens can be available again
ResourceDeletionManager.instance.reinit
-
+
redirect_to :action => 'deletion', :id => params[:id]
end
-
+
def quality_profile
@project = get_current_project(params[:id])
@profiles = Profile.find(:all, :conditions => {:language => @project.language, :enabled => true})
end
-
+
def update_quality_profile
project = get_current_project(params[:id])
-
+
selected_profile = Profile.find(:first, :conditions => {:id => params[:quality_profile].to_i})
- if selected_profile && selected_profile.language == project.language
+ if selected_profile && selected_profile.language == project.language
project.profile = selected_profile
project.save!
flash[:notice] = message('project_quality_profile.profile_successfully_updated')
@@ -92,17 +92,17 @@ class ProjectController < ApplicationController
selected_profile_name = selected_profile ? selected_profile.name + "(" + selected_profile.language + ")" : "Unknown profile"
flash[:error] = message('project_quality_profile.project_cannot_be_update_with_profile_x', :params => selected_profile_name)
end
-
+
redirect_to :action => 'quality_profile', :id => project.id
end
-
+
def key
@project = get_current_project(params[:id])
end
-
+
def update_key
project = get_current_project(params[:id])
-
+
new_key = params[:new_key].strip
if new_key.blank?
flash[:error] = message('update_key.new_key_cant_be_blank_for_x', :params => project.key)
@@ -115,17 +115,17 @@ class ProjectController < ApplicationController
java_facade.updateResourceKey(project.id, new_key)
flash[:notice] = message('update_key.key_updated')
rescue Exception => e
- flash[:error] = message('update_key.error_occured_while_renaming_key_of_x',
+ flash[:error] = message('update_key.error_occured_while_renaming_key_of_x',
:params => [project.key, Api::Utils.exception_message(e, :backtrace => false)])
end
end
-
+
redirect_to :action => 'key', :id => project.root_project.id
end
-
+
def prepare_key_bulk_update
@project = get_current_project(params[:id])
-
+
@string_to_replace = params[:string_to_replace].strip
@replacement_string = params[:replacement_string].strip
if @string_to_replace.blank? || @replacement_string.blank?
@@ -148,20 +148,20 @@ class ProjectController < ApplicationController
def perform_key_bulk_update
project = get_current_project(params[:id])
-
+
string_to_replace = params[:string_to_replace].strip
replacement_string = params[:replacement_string].strip
-
+
unless string_to_replace.blank? || replacement_string.blank?
begin
java_facade.bulkUpdateKey(project.id, string_to_replace, replacement_string)
flash[:notice] = message('update_key.key_updated')
rescue Exception => e
- flash[:error] = message('update_key.error_occured_while_renaming_key_of_x',
+ flash[:error] = message('update_key.error_occured_while_renaming_key_of_x',
:params => [project.key, Api::Utils.exception_message(e, :backtrace => false)])
end
end
-
+
redirect_to :action => 'key', :id => project.id
end
@@ -224,32 +224,34 @@ class ProjectController < ApplicationController
redirect_to :action => 'links', :id => project.id
end
-
def settings
- @project = get_current_project(params[:id])
+ @resource = get_current_project(params[:id])
- @snapshot=@project.last_snapshot
- if !@project.project? && !@project.module?
+ @snapshot = @resource.last_snapshot
+ if !@resource.project? && !@resource.module?
redirect_to :action => 'index', :id => params[:id]
end
- @category=params[:category] ||= 'general'
- @definitions_per_category={}
- definitions = java_facade.getPropertyDefinitions()
- properties = definitions.getAll().select { |property| (@project.module? && property.isOnModule()) || (@project.project? && property.isOnProject()) }
- properties.each do |property|
- category = definitions.getCategory(property.getKey())
- @definitions_per_category[category]||=[]
- @definitions_per_category[category]<<property
+ if @resource.nil?
+ definitions_per_category = java_facade.propertyDefinitions.globalPropertiesByCategory
+ elsif @resource.project?
+ definitions_per_category = java_facade.propertyDefinitions.projectPropertiesByCategory
+ elsif @resource.module?
+ definitions_per_category = java_facade.propertyDefinitions.modulePropertiesByCategory
end
- end
+ @category = params[:category] || 'general'
+ @categories = definitions_per_category.keys
+ @definitions = definitions_per_category[@category] || []
+
+ not_found('category') unless @categories.include? @category
+ end
def events
@categories = EventCategory.categories(true)
@snapshot = Snapshot.find(params[:id])
@category = params[:category]
-
+
conditions = "resource_id=:resource_id"
values = {:resource_id => @snapshot.project_id}
unless @category.blank?
@@ -260,7 +262,7 @@ class ProjectController < ApplicationController
snapshots_to_be_deleted = Snapshot.find(:all, :conditions => ["status='U' AND project_id=?", @snapshot.project_id])
unless snapshots_to_be_deleted.empty?
conditions << " AND snapshot_id NOT IN (:sids)"
- values[:sids] = snapshots_to_be_deleted.map {|s| s.id}
+ values[:sids] = snapshots_to_be_deleted.map { |s| s.id }
end
category_names=@categories.map { |cat| cat.name }
@@ -342,7 +344,7 @@ class ProjectController < ApplicationController
snapshot=Snapshot.find(params[:sid])
not_found("Snapshot not found") unless snapshot
access_denied unless is_admin?(snapshot)
-
+
# We update all the related snapshots to have the same version as the next snapshot
next_snapshot = Snapshot.find(:first, :conditions => ['created_at>? and project_id=?', snapshot.created_at, snapshot.project_id], :order => 'created_at asc')
snapshots = find_project_snapshots(snapshot.id)
@@ -371,11 +373,11 @@ class ProjectController < ApplicationController
else
snapshots = find_project_snapshots(snapshot.id)
snapshots.each do |s|
- e = Event.new({:name => params[:event_name],
- :category => EventCategory::KEY_OTHER,
- :snapshot => s,
- :resource_id => s.project_id,
- :event_date => s.created_at})
+ e = Event.new({:name => params[:event_name],
+ :category => EventCategory::KEY_OTHER,
+ :snapshot => s,
+ :resource_id => s.project_id,
+ :event_date => s.created_at})
e.save!
end
flash[:notice] = message('project_history.event_created', :params => params[:event_name])
@@ -425,7 +427,7 @@ class ProjectController < ApplicationController
access_denied unless is_admin?(project)
project
end
-
+
def find_project_snapshots(root_snapshot_id)
snapshots = Snapshot.find(:all, :include => 'events', :conditions => ["(root_snapshot_id = ? OR id = ?) AND scope = 'PRJ'", root_snapshot_id, root_snapshot_id])
end
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/controllers/settings_controller.rb b/sonar-server/src/main/webapp/WEB-INF/app/controllers/settings_controller.rb
index 949775f5e82..50282117749 100644
--- a/sonar-server/src/main/webapp/WEB-INF/app/controllers/settings_controller.rb
+++ b/sonar-server/src/main/webapp/WEB-INF/app/controllers/settings_controller.rb
@@ -21,80 +21,54 @@ class SettingsController < ApplicationController
SECTION=Navigation::SECTION_CONFIGURATION
- SPECIAL_CATEGORIES=['email', 'encryption', 'server_id']
+ SPECIAL_CATEGORIES=%w(email encryption server_id)
- verify :method => :post, :only => ['update'], :redirect_to => {:action => :index}
+ verify :method => :post, :only => %w(update), :redirect_to => {:action => :index}
+ before_filter :admin_required, :only => %w(index)
def index
- access_denied unless is_admin?
- load_properties(nil)
- @category ||= 'general'
+ load_properties()
end
def update
- @project=nil
- resource_id=nil
- if params[:resource_id]
- @project=Project.by_key(params[:resource_id])
- access_denied unless (@project && is_admin?(@project))
- resource_id=@project.id
- else
- access_denied unless is_admin?
- end
- is_global=(@project.nil?)
-
- load_properties(@project)
-
- @persisted_properties_per_key={}
- if @category && @definitions_per_category[@category]
- @definitions_per_category[@category].each do |property|
- value=params[property.getKey()]
- persisted_property = Property.find(:first, :conditions => {:prop_key=> property.key(), :resource_id => resource_id, :user_id => nil})
-
- # update the property
- if persisted_property
- if value.empty?
- Property.delete_all('prop_key' => property.key(), 'resource_id' => resource_id, 'user_id' => nil)
- java_facade.setGlobalProperty(property.getKey(), nil) if is_global
- elsif persisted_property.text_value != value.to_s
- persisted_property.text_value = value.to_s
- if persisted_property.save && is_global
- java_facade.setGlobalProperty(property.getKey(), value.to_s)
- end
- @persisted_properties_per_key[persisted_property.key]=persisted_property
- end
-
- # create the property
- elsif value.present?
- persisted_property=Property.new(:prop_key => property.key(), :text_value => value.to_s, :resource_id => resource_id)
- if persisted_property.save && is_global
- java_facade.setGlobalProperty(property.getKey(), value.to_s)
- end
- @persisted_properties_per_key[persisted_property.key]=persisted_property
- end
- end
+ resource_id = params[:resource_id]
+ @resource = Project.by_key(resource_id) if resource_id
+
+ access_denied if (@resource && !is_admin?(@resource))
+ access_denied if (@resource.nil? && !is_admin?)
- params[:layout]='false'
- render :partial => 'settings/properties'
+ load_properties()
+
+ @definitions.map(&:key).each do |key|
+ value = params[key]
+
+ if value.blank?
+ Property.clear(key, resource_id)
+ else
+ Property.set(key, value, resource_id)
+ end
end
+
+ render :partial => 'settings/properties'
end
private
- def load_properties(project)
- @category=params[:category]
- @definitions_per_category={}
- definitions = java_facade.getPropertyDefinitions()
- 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
+ def load_properties
+ @category = params[:category] || 'general'
- SPECIAL_CATEGORIES.each do |category|
- @definitions_per_category[category]=[]
+ if @resource.nil?
+ definitions_per_category = java_facade.propertyDefinitions.globalPropertiesByCategory
+ elsif @resource.project?
+ definitions_per_category = java_facade.propertyDefinitions.projectPropertiesByCategory
+ elsif @resource.module?
+ definitions_per_category = java_facade.propertyDefinitions.modulePropertiesByCategory
end
+
+ @categories = definitions_per_category.keys + SPECIAL_CATEGORIES
+ @definitions = definitions_per_category[@category] || []
+
+ not_found('category') unless @categories.include? @category
end
+
end
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/helpers/project_helper.rb b/sonar-server/src/main/webapp/WEB-INF/app/helpers/project_helper.rb
index e4028760ddb..90ca275324d 100644
--- a/sonar-server/src/main/webapp/WEB-INF/app/helpers/project_helper.rb
+++ b/sonar-server/src/main/webapp/WEB-INF/app/helpers/project_helper.rb
@@ -19,6 +19,7 @@
#
module ProjectHelper
include ActionView::Helpers::UrlHelper
+ include SettingsHelper
def formatted_value(measure, default='')
measure ? measure.formatted_value : default
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/helpers/settings_helper.rb b/sonar-server/src/main/webapp/WEB-INF/app/helpers/settings_helper.rb
new file mode 100644
index 00000000000..b6dfedd5fc5
--- /dev/null
+++ b/sonar-server/src/main/webapp/WEB-INF/app/helpers/settings_helper.rb
@@ -0,0 +1,56 @@
+#
+# Sonar, entreprise quality control 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
+#
+module SettingsHelper
+ def category_name(category)
+ message_or_default("property.category.#{category}", category)
+ end
+
+ def property_name(property)
+ message_or_default("property.#{property.key()}.name", property.name())
+ end
+
+ def property_description(property)
+ message_or_default("property.#{property.key()}.description", property.description())
+ end
+
+ def property_value(property)
+ if property.multi_values
+ Property.values(property.key, @resource ? @resource.id : nil)
+ else
+ Property.value(property.key, @resource ? @resource.id : nil, '')
+ end
+ end
+
+ # for backward-compatibility with properties that do not define the type TEXT
+ def property_type(property, value)
+ if property.getType().to_s=='STRING' && value && value.include?('\n')
+ return 'TEXT'
+ end
+ property.getType()
+ end
+
+ def message_or_default(message_key, default)
+ message(message_key, :default => default)
+ end
+
+ def by_name(categories)
+ categories.sort_by { |category| category_name(category) }
+ end
+end
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/models/property.rb b/sonar-server/src/main/webapp/WEB-INF/app/models/property.rb
index 3f8dfb72de6..39f617657ee 100644
--- a/sonar-server/src/main/webapp/WEB-INF/app/models/property.rb
+++ b/sonar-server/src/main/webapp/WEB-INF/app/models/property.rb
@@ -20,6 +20,10 @@
class Property < ActiveRecord::Base
validates_presence_of :prop_key
+ named_scope :with_key, lambda { |value| {:conditions => {:prop_key, value}} }
+ named_scope :with_resource, lambda { |value| {:conditions => {:resource_id => value}} }
+ named_scope :with_user, lambda { |value| {:conditions => {:user_id => value}} }
+
def key
prop_key
end
@@ -28,33 +32,48 @@ class Property < ActiveRecord::Base
text_value
end
- def self.hash(resource_id=nil)
- hash={}
- Property.find(:all, :conditions => {'resource_id' => resource_id, 'user_id' => nil}).each do |prop|
- hash[prop.key]=prop.value
- end
- hash
+ def self.hash(resource_id=nil, user_id=nil)
+ properties = Property.with_resource(resource_id).with_user(user_id)
+
+ Hash[properties.map { |prop| [prop.key, prop.value] }]
end
- def self.value(key, resource_id=nil, default_value=nil)
- prop=Property.find(:first, :conditions => {'prop_key' => key, 'resource_id' => resource_id, 'user_id' => nil})
- if prop
- prop.text_value || default_value
- else
- default_value
- end
+ def self.clear(key, resource_id=nil, user_id=nil)
+ all(key, resource_id, user_id).delete_all
+ Java::OrgSonarServerUi::JRubyFacade.getInstance().setGlobalProperty(key, nil) unless resource_id
+ end
+
+ def self.by_key(key, resource_id=nil, user_id=nil)
+ all(key, resource_id, user_id).first
+ end
+
+ def self.by_key_prefix(prefix)
+ Property.find(:all, :conditions => ['prop_key like ?', prefix + '%'])
+ end
+
+ def self.value(key, resource_id=nil, default_value=nil, user_id=nil)
+ property = by_key(key, resource_id, user_id)
+ return default_value unless property
+
+ property.text_value || default_value
end
- def self.values(key, resource_id=nil)
- Property.find(:all, :conditions => {'prop_key' => key, 'resource_id' => resource_id, 'user_id' => nil}).collect { |p| p.text_value }
+ def self.values(key, resource_id=nil, user_id=nil)
+ value = value(key, resource_id, '', user_id)
+ values = value.split(',')
+ values.empty? ? [nil] : values.map { |v| v.gsub('%2C', ',') }
end
- def self.set(key, value, resource_id=nil)
+ def self.set(key, value, resource_id=nil, user_id=nil)
+ if value.kind_of? Array
+ value = value.map { |v| v.gsub(',', '%2C') }.join(',')
+ end
+
text_value = (value.nil? ? nil : value.to_s)
- prop = Property.new(:prop_key => key, :text_value => text_value, :resource_id => resource_id)
+ prop = Property.new(:prop_key => key, :text_value => text_value, :resource_id => resource_id, :user_id => user_id)
if prop.valid?
Property.transaction do
- Property.delete_all('prop_key' => key, 'resource_id' => resource_id, 'user_id' => nil)
+ Property.delete_all(:prop_key => key, :resource_id => resource_id, :user_id => user_id)
prop.save
end
Java::OrgSonarServerUi::JRubyFacade.getInstance().setGlobalProperty(key, text_value) unless resource_id
@@ -62,25 +81,8 @@ class Property < ActiveRecord::Base
prop
end
- def self.clear(key, resource_id=nil)
- Property.delete_all('prop_key' => key, 'resource_id' => resource_id, 'user_id' => nil)
- Java::OrgSonarServerUi::JRubyFacade.getInstance().setGlobalProperty(key, nil) unless resource_id
- end
-
- def self.by_key(key, resource_id=nil)
- Property.find(:first, :conditions => {'prop_key' => key, 'resource_id' => resource_id, 'user_id' => nil})
- end
-
- def self.by_key_prefix(prefix)
- Property.find(:all, :conditions => ["prop_key like ?", prefix + '%'])
- end
-
- def self.update(key, value)
- property = Property.find(:first, :conditions => {:prop_key => key, :resource_id => nil, :user_id => nil});
- property.text_value = value
- property.save
- Java::OrgSonarServerUi::JRubyFacade.getInstance().setGlobalProperty(key, value)
- property
+ def self.update(key, value, resource_id=nil, user_id=nil)
+ set(key, value, resource_id, user_id)
end
def to_hash_json
@@ -112,6 +114,10 @@ class Property < ActiveRecord::Base
private
+ def self.all(key, resource_id=nil, user_id=nil)
+ Property.with_key(key).with_resource(resource_id).with_user(user_id)
+ end
+
def validate
if java_definition
validation_result=java_definition.validate(text_value)
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/settings/_multi_value.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/settings/_multi_value.html.erb
new file mode 100644
index 00000000000..a571f937090
--- /dev/null
+++ b/sonar-server/src/main/webapp/WEB-INF/app/views/settings/_multi_value.html.erb
@@ -0,0 +1,5 @@
+<div class="multi_value">
+ <%= render "settings/type_#{property_type(property, value)}", :property => property, :value => value -%>
+ <a href="#" class="delete"></a>
+ <br/>
+</div>
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/settings/_properties.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/settings/_properties.html.erb
index e77b7583790..3e9602d4820 100644
--- a/sonar-server/src/main/webapp/WEB-INF/app/views/settings/_properties.html.erb
+++ b/sonar-server/src/main/webapp/WEB-INF/app/views/settings/_properties.html.erb
@@ -1,87 +1,67 @@
-<% 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 -%>
+<% if SettingsController::SPECIAL_CATEGORIES.include?(@category) -%>
+ <%= render 'special', :url => url_for(:controller => "#{@category}_configuration") -%>
+<% else -%>
+ <% form_remote_tag :url => {:controller => 'settings', :action => 'update', :category => @category, :resource_id => @resource ? @resource.id : nil},
+ :method => :post,
+ :before => "$('submit_settings').hide();$('loading_settings').show();",
+ :update => 'properties',
+ :script => false do -%>
- <table class="data marginbottom10" style="margin: 10px">
- <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
+ <table class="data marginbottom10" style="margin: 10px;">
+ <thead>
+ <tr>
+ <th><%= h category_name(@category) -%></th>
+ </tr>
+ </thead>
+ <tbody>
+ <% @definitions.each do |property| -%>
+ <tr class="<%= cycle('even', 'odd', :name => 'properties') -%>">
+ <td style="padding: 10px" id="block_<%= property.key -%>">
+ <h3>
+ <div><%= property_name(property) -%></div>
+ <div class="note"><%= property.key -%></div>
+ </h3>
+ <% desc=property_description(property) -%>
+ <% unless desc.blank? %>
+ <p class="marginbottom10"><%= desc -%></p>
+ <% end -%>
+ <% value = property_value(property) -%>
+ <% if property.multi_values -%>
+ <% value.each do |sub_value| -%>
+ <%= render "settings/multi_value", :property => property, :value => sub_value -%>
+ <% end -%>
+ <div class="template" style="display:none;">
+ <%= render "settings/multi_value", :property => property, :value => nil -%>
+ </div>
+ <a href="#" class="add"><%= message('settings.add') -%></a>
+ <% else -%>
+ <%= render "settings/type_#{property_type(property, value)}", :property => property, :value => value -%>
+ <% end -%>
- # if fresh form or no error, get the current value
- value = Property.value(property.getKey(), (@project ? @project.id : nil), '') unless value
+ <% default_prop_value = (@resource ? Property.value(property.key, nil, property.defaultValue) : property.defaultValue) -%>
+ <% unless default_prop_value.blank? %>
+ <p class="note">Default: <%= property.type.to_s=='PASSWORD' ? '********' : h(default_prop_value) -%></p>
+ <% end -%>
+ </td>
+ </tr>
+ <% end -%>
+ </tbody>
+ </table>
+ <div style="padding-left: 16px;">
+ <%= submit_tag(message('settings.save_category', :params => [category_name(@category)]), :id => 'submit_settings') -%>
+ <img src="<%= ApplicationController.root_context -%>/images/loading.gif" id="loading_settings" style="display:none;">
+ </div>
+ <% end -%>
+<% end -%>
- # for backward-compatibility with properties that do not define the type TEXT
- property_type = property.getType()
- if property_type.to_s=='STRING' && value.include?("\n")
- property_type = 'TEXT'
- end
+<script type="text/javascript">
+ $j('.delete').live('click', function () {
+ $j(this).parent('.multi_value').remove();
+ });
- %>
- <tr class="<%= cycle('even', 'odd', :name => 'properties') -%>">
- <td style="padding: 10px" id="block_<%= 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.valid?
- %>
- <div class="error"><%= p.validation_error_message -%></div>
- <%
- end
- %>
- <p>
- <%
- default_prop_value = (@project ? Property.value(property.key(), nil, property.defaultValue()) : property.defaultValue())
- unless default_prop_value.blank? %>
- <span class="note">Default : <%= property.getType().to_s=='PASSWORD' ? '********' : h(default_prop_value) -%></span>
- <% end %>
- </p>
- </td>
- </tr>
- <% end
- end
- %>
- </tbody>
- </table>
- <div style="padding-left: 16px">
- <%= 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
- %>
+ $j('.add').live('click', function () {
+ var template = $j(this).siblings('.template');
+ template.before(template.html());
+ });
+</script> \ No newline at end of file
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/settings/_settings.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/settings/_settings.html.erb
index b9975d9d0ab..3411c37143a 100644
--- a/sonar-server/src/main/webapp/WEB-INF/app/views/settings/_settings.html.erb
+++ b/sonar-server/src/main/webapp/WEB-INF/app/views/settings/_settings.html.erb
@@ -1,28 +1,33 @@
-<style type="text/css">
- #plugins .plugin {
- padding: 5px;
- border: 1px solid #ddd;
- background-color: #fff;
- }
+<h1 class="marginbottom10"><%= message('settings.page') -%></h1>
- #plugins .plugin h2 {
- margin-left: 10px;
- font-size: 122%;
- color: #333;
- }
+<table width="100%">
+ <tr>
+ <td width="1%" nowrap class="column first">
+ <table class="data selector">
+ <thead>
+ <tr>
+ <th><%= message('category') -%></th>
+ </tr>
+ </thead>
+ <tbody>
+ <% by_name(@categories).each do |category| -%>
+ <tr id="select_<%= category -%>" class="select <%= cycle('even', 'odd', :name => 'category') -%> <%= 'selected' if @category==category -%>">
+ <td><%= link_to category_name(category), :category => category -%></td>
+ </tr>
+ <% end -%>
+ </tbody>
+ </table>
+ <br/>
+ </td>
- #plugins .plugin h3 {
- margin-left: 5px;
- }
+ <td class="column">
+ <div id="properties">
+ <%= render 'settings/properties' -%>
+ </div>
+ </td>
+ </tr>
+</table>
- #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;
@@ -31,40 +36,3 @@
$(eltId).parentNode.replace(textArea);
}
</script>
-<div id="plugins">
- <h1 class="marginbottom10"><%= message('settings.page') -%></h1>
- <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" style="width:99%">
- <%= render :partial => 'settings/properties' -%>
- </div>
- </td>
- </tr>
- </table>
-</div>
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/settings/_sidebar.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/settings/_sidebar.html.erb
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/sonar-server/src/main/webapp/WEB-INF/app/views/settings/_sidebar.html.erb
+++ /dev/null
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/settings/_type_STRING.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/settings/_type_STRING.html.erb
index b4c3b451f4d..7292d327860 100644
--- a/sonar-server/src/main/webapp/WEB-INF/app/views/settings/_type_STRING.html.erb
+++ b/sonar-server/src/main/webapp/WEB-INF/app/views/settings/_type_STRING.html.erb
@@ -1,2 +1,2 @@
-<input type="text" name="<%= h property.getKey() -%>" value="<%= h value if value -%>" size="50" id="input_<%= h property.getKey() -%>"/>
-<%= link_to_function(image_tag('zoom.png'), "enlargeTextInput('#{property.getKey()}')", :class => 'nolink') -%> \ No newline at end of file
+<input type="text" name="<%= h property.key -%>[]" value="<%= h value if value -%>" size="50" id="input_<%= h property.key -%>"/>
+<%= link_to_function(image_tag('zoom.png'), "enlargeTextInput('#{property.key}')", :class => 'nolink') -%> \ No newline at end of file
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/settings/index.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/settings/index.html.erb
index 81fa7362a09..d2c6ebcae2c 100644
--- a/sonar-server/src/main/webapp/WEB-INF/app/views/settings/index.html.erb
+++ b/sonar-server/src/main/webapp/WEB-INF/app/views/settings/index.html.erb
@@ -1 +1 @@
-<%= render :partial => 'settings', :locals => {:project=>nil} %> \ No newline at end of file
+<%= render 'settings', :project => nil %> \ No newline at end of file
diff --git a/sonar-server/src/main/webapp/stylesheets/style.css b/sonar-server/src/main/webapp/stylesheets/style.css
index 74584a1093b..ff58b01e301 100644
--- a/sonar-server/src/main/webapp/stylesheets/style.css
+++ b/sonar-server/src/main/webapp/stylesheets/style.css
@@ -2087,6 +2087,11 @@ table.nowrap td, td.nowrap, th.nowrap {
padding: 2px 0 2px 20px;
}
+.delete {
+ background: url("../images/cross.png") no-repeat scroll left 50% transparent;
+ padding: 2px 0 2px 20px;
+}
+
.restore {
display: block;
background: url("../images/restore.gif") no-repeat scroll left 50% transparent;