#
#------------------------------------------------------------------------------
+action_plans.page=Action Plans
backup.page=Backup
clouds.page=Clouds
components.page=Components
reviews.resolution.FALSE-POSITIVE=False-positive
reviews.resolution.FIXED=Fixed
+
+#------------------------------------------------------------------------------
+#
+# ACTION PLANS
+#
+#------------------------------------------------------------------------------
+
+action_plans.page_title=Manage Action plans
+action_plans.add_action_plan=Add action plan
+action_plans.col.status=St.
+action_plans.col.name=Name
+action_plans.col.due_for=Due for
+action_plans.col.progress=Progress
+action_plans.col.description=Description
+action_plans.col.author=Author
+action_plans.col.operations=Operations
+action_plans.no_action_plan=No action plan
+action_plans.confirm_delete=Delete this action plan? Associated reviews will not be deleted.
+action_plans.create_new_action_plan=Create a new action plan
+action_plans.date_format_help=The date should be entered using the following pattern: 'day/month/year'. For instance, '31/12/2011'.
+action_plans.date_not_valid=Date not valid
+action_plans.date_cant_be_in_past=The dead-line can't be in the past
+action_plans.x_out_of_x_reviews_solved={0} of {1} reviews solved
+
+
#------------------------------------------------------------------------------
#
# DEPENDENCIES
- complete the Derby DDL file used for unit tests : sonar-testing-harness/src/main/resources/org/sonar/test/persistence/sonar-test.ddl
*/
- public static final int LAST_VERSION = 237;
+ public static final int LAST_VERSION = 238;
public final static String TABLE_NAME = "schema_migrations";
--- /dev/null
+#
+# Sonar, entreprise quality control tool.
+# Copyright (C) 2008-2011 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
+#
+
+class ActionPlansController < ApplicationController
+
+ SECTION=Navigation::SECTION_RESOURCE
+ before_filter :load_resource
+ verify :method => :post, :only => [:save, :delete], :redirect_to => {:action => :index}
+
+ def index
+ @action_plans = ActionPlan.find(:all, :conditions => ['project_id=?', @resource.id], :include => 'reviews', :order => 'dead_line ASC')
+ end
+
+ def new
+ if params[:name] || params[:description] || params[:dead_line]
+ @action_plan = ActionPlan.new
+ elsif params[:plan_id]
+ @action_plan = ActionPlan.find params[:plan_id]
+ end
+ end
+
+ def save
+ @action_plan = ActionPlan.find params[:plan_id] unless params[:plan_id].blank?
+ unless @action_plan
+ @action_plan = ActionPlan.new(:user_login => current_user.login,
+ :project_id => @resource.id,
+ :status => ActionPlan::STATUS_OPEN)
+ end
+ @action_plan.name = params[:name]
+ @action_plan.description = params[:description]
+ begin
+ @action_plan.dead_line = Date.strptime(params[:dead_line], '%d/%m/%Y') unless params[:dead_line].blank?
+ rescue
+ date_not_valid = message('action_plans.date_not_valid')
+ end
+
+ if date_not_valid || !@action_plan.valid?
+ @action_plan.errors.add :dead_line, date_not_valid if date_not_valid
+ render :action => :new, :id => @resource.id
+ else
+ @action_plan.save
+ redirect_to :action => 'index', :id => @resource.id
+ end
+ end
+
+ def delete
+ action_plan = ActionPlan.find params[:plan_id]
+ action_plan.destroy
+ redirect_to :action => 'index', :id => @resource.id
+ end
+
+ private
+
+ def load_resource
+ @resource=Project.by_key(params[:id])
+ return redirect_to home_path unless @resource
+ access_denied unless has_role?(:admin, @resource)
+ end
+
+end
--- /dev/null
+#
+# Sonar, entreprise quality control tool.
+# Copyright (C) 2008-2011 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
+#
+
+class ActionPlan < ActiveRecord::Base
+ belongs_to :project
+ has_and_belongs_to_many :reviews
+
+ validates_uniqueness_of :name
+ validates_length_of :name, :within => 1..200
+ validates_length_of :description, :maximum => 1000, :allow_blank => true, :allow_nil => true
+ validates_presence_of :user_login, :message => "can't be empty"
+ validates_presence_of :status, :message => "can't be empty"
+ validates_presence_of :project, :message => "can't be empty"
+ validate :dead_line_cannot_be_in_the_past
+
+ STATUS_OPEN = 'OPEN'
+ STATUS_CLOSED = 'CLOSED'
+
+ def user
+ @user ||=
+ begin
+ user_login ? User.find(:first, :conditions => ['login=?', user_login]) : nil
+ end
+ end
+
+ def closed?
+ status == STATUS_CLOSED
+ end
+
+ def open?
+ status == STATUS_OPEN
+ end
+
+ def progress
+ total_reviews = reviews.size
+ open_reviews = reviews.select{|r| r.open? || r.reopened?}.size
+ {:total => total_reviews, :open => open_reviews}
+ end
+
+ private
+
+ def dead_line_cannot_be_in_the_past
+ if !dead_line.blank? and dead_line < Date.today
+ errors.add(:dead_line, Api::Utils.message('action_plans.date_cant_be_in_past'))
+ end
+ end
+
+end
belongs_to :rule
has_many :review_comments, :order => "created_at", :dependent => :destroy
alias_attribute :comments, :review_comments
+ has_and_belongs_to_many :action_plans
validates_presence_of :user, :message => "can't be empty"
validates_presence_of :status, :message => "can't be empty"
--- /dev/null
+<%
+ open_reviews_url = link_to action_plan.progress[:open].to_s,
+ :controller => 'reviews', :action => 'index',
+ :action_plans => [action_plan.id], :statuses => [Review::STATUS_OPEN, Review::STATUS_REOPENED], :assignee_login => ''
+
+ total_reviews_url = link_to action_plan.progress[:total].to_s,
+ :controller => 'reviews', :action => 'index',
+ :action_plans => [action_plan.id], :statuses => [''], :assignee_login => ''
+%>
+<div class="progress">
+ <div class="bar">
+ <div style="width:<%= action_plan.progress[:total]==0 ? '0' : action_plan.progress[:open]*100/action_plan.progress[:total] -%>%;"> </div>
+ </div>
+ <div class="note"><%= message('action_plans.x_out_of_x_reviews_solved', :params => [open_reviews_url, total_reviews_url]) -%></div>
+</div>
\ No newline at end of file
--- /dev/null
+<style type="text/css">
+ #actionPlans td {
+ vertical-align: top;
+ }
+ #actionPlans td.progress {
+ padding: 0px 40px;
+ }
+</style>
+
+<div class="line-block marginbottom10">
+ <ul class="operations">
+ <li class="last">
+ <%= image_tag 'add.png' -%>
+ <%= link_to message('action_plans.add_action_plan'), {:action => 'new', :id => @resource.id}, {:id => 'addActionPlan'} -%>
+ </li>
+ </ul>
+ <h1><%= message('action_plans.page_title') -%></h1>
+</div>
+
+<table class="width100 data sortable" id="actionPlans">
+ <thead>
+ <tr>
+ <th class="thin nowrap"><%= message('action_plans.col.status') -%></th>
+ <th class="thin nowrap"><%= message('action_plans.col.name') -%></th>
+ <th class="thin nowrap righticon" style="text-align: right"><%= message('action_plans.col.due_for') -%></th>
+ <th class="nowrap nosort center"><%= message('action_plans.col.progress') -%></th>
+ <th class="nowrap"><%= message('action_plans.col.description') -%></th>
+ <th class="nowrap"><%= message('action_plans.col.author') -%></th>
+ <th class="thin nowrap nosort"><%= message('action_plans.col.operations') -%></th>
+ </tr>
+ </thead>
+ <tbody>
+ <% if @action_plans.empty? %>
+ <td colspan="7" class="even"><%= message('action_plans.no_action_plan') -%></td>
+ <% end %>
+ <%
+ @action_plans.each do |plan|
+ %>
+ <tr>
+ <td class="thin nowrap center"><img src="<%= ApplicationController.root_context -%>/images/status/<%= plan.status -%>.png" title="<%= message(plan.status.downcase).capitalize -%>"/></td>
+ <td class="thin nowrap"><%= h(plan.name) -%></td>
+ <td class="thin nowrap" align="right"><%= plan.dead_line ? plan.dead_line.strftime('%Y/%m/%d') : ' ' -%></td>
+ <td class="progress thin">
+ <%= render :partial => 'progress', :locals => {:action_plan => plan} -%>
+ </td>
+ <td id="desc"><%= h(plan.description) -%></td>
+ <td id="desc"><%= h(plan.user.name) -%></td>
+ <td class="thin nowrap">
+ <a href="<%= url_for :action => 'new', :id => @resource.id, :plan_id => plan.id -%>"><%= message('edit') -%></a>
+ <%= link_to message('delete'), {:action => 'delete', :id => @resource.id, :plan_id => plan.id}, {:method => 'POST', :confirm => message('action_plans.confirm_delete'), :class => 'action'} -%>
+ </td>
+ </tr>
+ <% end %>
+ </tbody>
+</table>
+<script>TableKit.Sortable.init('actionPlans');</script>
--- /dev/null
+<h1 class="marginbottom10"><%= message('action_plans.create_new_action_plan') -%></h1>
+
+<% if @action_plan && @action_plan.errors.on_base
+ @action_plan.errors.on_base.each do |error| %>
+ <div class="error"><%= error -%></div>
+<% end
+ end
+%>
+
+<form action="<%= url_for :action => 'save' -%>" method="POST" id="createForm">
+ <input type="hidden" name="id" value="<%= @resource.id -%>"/>
+ <input type="hidden" name="plan_id" value="<%= @action_plan.id if @action_plan -%>"/>
+ <table class="width100 form">
+ <tbody>
+ <tr>
+ <td class="keyCell">
+ <%= message('action_plans.col.name') -%>:
+ </td>
+ <td>
+ <input type="text" name="name" id="name" value="<%= @action_plan ? @action_plan.name : '' -%>"/>
+ <% if @action_plan && @action_plan.errors.on('name')
+ @action_plan.errors.on('name').each do |error| %>
+ <span class="error"><%= error -%></span>
+ <% end
+ end %>
+ </td>
+ </tr>
+ <tr>
+ <td class="keyCell">
+ <%= message('action_plans.col.due_for') -%>:
+ </td>
+ <td>
+ <input type="text" name="dead_line" id="dead_line" value="<%= @action_plan && @action_plan.dead_line ? @action_plan.dead_line.strftime('%d/%m/%Y') : params[:dead_line] -%>"/>
+ <span class="note"><%= message('action_plans.date_format_help') -%></span>
+ <% if @action_plan && @action_plan.errors.on('dead_line')
+ @action_plan.errors.on('dead_line').each do |error| %>
+ <span class="error"><%= error -%></span>
+ <% end
+ end %>
+ </td>
+ </tr>
+ <tr>
+ <td class="keyCell">
+ <%= message('action_plans.col.description') -%>:
+ </td>
+ <td>
+ <textarea rows="5" cols="80" name="description" id="description" class="width100"><%= @action_plan ? @action_plan.description : '' -%></textarea>
+ <% if @action_plan && @action_plan.errors.on('description')
+ @action_plan.errors.on('description').each do |error| %>
+ <span class="error"><%= error -%></span>
+ <% end
+ end %>
+ </td>
+ </tr>
+ <tr>
+ <td class="keyCell">
+ </td>
+ <td>
+ <input type="submit" value="<%= message('save') -%>"/>
+ <%= link_to message('cancel'), :action => 'index', :id => @resource.id -%>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+</form>
\ No newline at end of file
<li class="h2"><%= message('sidebar.project_settings') -%></li>
<li class="<%= 'selected' if request.request_uri.include?('/manual_measures') -%>">
<a href="<%= ApplicationController.root_context -%>/manual_measures/index/<%= @project.id -%>"><%= message('manual_measures.page') -%></a></li>
+ <li class="<%= 'selected' if request.request_uri.include?('/action_plans') -%>">
+ <a href="<%= ApplicationController.root_context -%>/action_plans/index/<%= @project.id -%>"><%= message('action_plans.page') -%></a></li>
<% if (@project.project? || @project.module?) %>
<li class="<%= 'selected' if request.request_uri.include?('/project/settings') -%>">
<a href="<%= ApplicationController.root_context -%>/project/settings/<%= @project.id -%>"><%= message('project_settings.page') -%></a></li>
--- /dev/null
+#
+# Sonar, entreprise quality control tool.
+# Copyright (C) 2008-2011 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
+#
+
+#
+# Sonar 2.13
+#
+class CreateActionPlans < ActiveRecord::Migration
+
+ def self.up
+ create_table 'action_plans' do |t|
+ t.timestamps
+ t.column 'name', :string, :null => true, :limit => 200
+ t.column 'description', :string, :null => true, :limit => 1000
+ t.column 'dead_line', :datetime, :null => true
+ t.column 'user_login', :string, :null => true, :limit => 40
+ t.column 'project_id', :integer, :null => true
+ t.column 'status', :string, :null => true, :limit => 10
+ end
+ alter_to_big_primary_key('action_plans')
+ add_index "action_plans", "project_id"
+
+ create_table :action_plans_reviews, :id => false do |t|
+ t.integer :action_plan_id
+ t.integer :review_id
+ end
+ add_index "action_plans_reviews", "action_plan_id"
+ add_index "action_plans_reviews", "review_id"
+
+ end
+
+end
width: 1%;
}
+.max-width {
+ width: 100%;
+}
+
td.sep {
width: 10px;
}
font-size: 90%;
}
-.max-width {
+/* ACTION PLANS */
+
+div.progress {
+ width: 400px;
+ margin: 4px;
+}
+
+div.progress div.bar {
width: 100%;
+ background-color: #CC0000;
+ border: none;
+ margin-bottom: 4px;
}
+div.progress > div > div {
+ height: 10px;
+ background-color: #078C00;
+}
+
+div.progress div.note {
+ color: #777777;
+ font-size: 93%;
+ font-weight: normal;
+ white-space: nowrap;
+}
+
+
/* AUTOCOMPLETE FIELDS */
div.autocomplete {
position: absolute;