summaryrefslogtreecommitdiffstats
path: root/app
diff options
context:
space:
mode:
authorJean-Philippe Lang <jp_lang@yahoo.fr>2012-12-09 17:57:18 +0000
committerJean-Philippe Lang <jp_lang@yahoo.fr>2012-12-09 17:57:18 +0000
commitf8895a7cdd9c53f3335825a45be1d53862693370 (patch)
tree2d75806f7fda54ccc9118cb1f69ff26c6fb541cd /app
parent10998c9bae314057b5c2adf97418618f68efb3d7 (diff)
downloadredmine-f8895a7cdd9c53f3335825a45be1d53862693370.tar.gz
redmine-f8895a7cdd9c53f3335825a45be1d53862693370.zip
Adds TimeEntryQuery for listing time entries.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10967 e93f8b46-1217-0410-a6f0-8f06a7374b81
Diffstat (limited to 'app')
-rw-r--r--app/controllers/timelog_controller.rb37
-rw-r--r--app/models/query.rb48
-rw-r--r--app/models/time_entry_query.rb65
-rw-r--r--app/views/timelog/_date_range.html.erb45
4 files changed, 141 insertions, 54 deletions
diff --git a/app/controllers/timelog_controller.rb b/app/controllers/timelog_controller.rb
index a0c694bc4..a2dcfac7d 100644
--- a/app/controllers/timelog_controller.rb
+++ b/app/controllers/timelog_controller.rb
@@ -36,24 +36,14 @@ class TimelogController < ApplicationController
include TimelogHelper
helper :custom_fields
include CustomFieldsHelper
+ helper :queries
def index
- sort_init 'spent_on', 'desc'
- sort_update 'spent_on' => ['spent_on', "#{TimeEntry.table_name}.created_on"],
- 'user' => 'user_id',
- 'activity' => 'activity_id',
- 'project' => "#{Project.table_name}.name",
- 'issue' => 'issue_id',
- 'hours' => 'hours'
+ @query = TimeEntryQuery.build_from_params(params, :name => '_')
+ scope = time_entry_scope
- retrieve_date_range
-
- scope = TimeEntry.visible.spent_between(@from, @to)
- if @issue
- scope = scope.on_issue(@issue)
- elsif @project
- scope = scope.on_project(@project, Setting.display_subprojects_issues?)
- end
+ sort_init(@query.sort_criteria.empty? ? [['spent_on', 'desc']] : @query.sort_criteria)
+ sort_update(@query.sortable_columns)
respond_to do |format|
format.html {
@@ -100,8 +90,10 @@ class TimelogController < ApplicationController
end
def report
- retrieve_date_range
- @report = Redmine::Helpers::TimeReport.new(@project, @issue, params[:criteria], params[:columns], @from, @to)
+ @query = TimeEntryQuery.build_from_params(params, :name => '_')
+ scope = time_entry_scope
+
+ @report = Redmine::Helpers::TimeReport.new(@project, @issue, params[:criteria], params[:columns], scope)
respond_to do |format|
format.html { render :layout => !request.xhr? }
@@ -291,6 +283,17 @@ private
end
end
+ # Returns the TimeEntry scope for index and report actions
+ def time_entry_scope
+ scope = TimeEntry.visible.where(@query.statement)
+ if @issue
+ scope = scope.on_issue(@issue)
+ elsif @project
+ scope = scope.on_project(@project, Setting.display_subprojects_issues?)
+ end
+ scope
+ end
+
# Retrieves the date range based on predefined ranges or specific from/to param dates
def retrieve_date_range
@free_period = false
diff --git a/app/models/query.rb b/app/models/query.rb
index 81eeb9f3b..814d11846 100644
--- a/app/models/query.rb
+++ b/app/models/query.rb
@@ -117,7 +117,13 @@ class Query < ActiveRecord::Base
"><t+"=> :label_in_the_next_days,
"t+" => :label_in,
"t" => :label_today,
+ "ld" => :label_yesterday,
"w" => :label_this_week,
+ "lw" => :label_last_week,
+ "l2w" => [:label_last_n_weeks, :count => 2],
+ "m" => :label_this_month,
+ "lm" => :label_last_month,
+ "y" => :label_this_year,
">t-" => :label_less_than_ago,
"<t-" => :label_more_than_ago,
"><t-"=> :label_in_the_past_days,
@@ -135,8 +141,8 @@ class Query < ActiveRecord::Base
:list_status => [ "o", "=", "!", "c", "*" ],
:list_optional => [ "=", "!", "!*", "*" ],
:list_subprojects => [ "*", "!*", "=" ],
- :date => [ "=", ">=", "<=", "><", "<t+", ">t+", "><t+", "t+", "t", "w", ">t-", "<t-", "><t-", "t-", "!*", "*" ],
- :date_past => [ "=", ">=", "<=", "><", ">t-", "<t-", "><t-", "t-", "t", "w", "!*", "*" ],
+ :date => [ "=", ">=", "<=", "><", "<t+", ">t+", "><t+", "t+", "t", "ld", "w", "lw", "l2w", "m", "lm", "y", ">t-", "<t-", "><t-", "t-", "!*", "*" ],
+ :date_past => [ "=", ">=", "<=", "><", ">t-", "<t-", "><t-", "t-", "t", "ld", "w", "lw", "l2w", "m", "lm", "y", "!*", "*" ],
:string => [ "=", "~", "!", "!~", "!*", "*" ],
:text => [ "~", "!~", "!*", "*" ],
:integer => [ "=", ">=", "<=", "><", "!*", "*" ],
@@ -173,6 +179,11 @@ class Query < ActiveRecord::Base
self
end
+ # Builds a new query from the given params and attributes
+ def self.build_from_params(params, attributes={})
+ new(attributes).build_from_params(params)
+ end
+
def validate_query_filters
filters.each_key do |field|
if values_for(field)
@@ -195,7 +206,7 @@ class Query < ActiveRecord::Base
# filter requires one or more values
(values_for(field) and !values_for(field).first.blank?) or
# filter doesn't require any value
- ["o", "c", "!*", "*", "t", "w"].include? operator_for(field)
+ ["o", "c", "!*", "*", "t", "ld", "w", "lw", "l2w", "m", "lm", "y"].include? operator_for(field)
end if filters
end
@@ -218,7 +229,7 @@ class Query < ActiveRecord::Base
# Returns a hash of localized labels for all filter operators
def self.operators_labels
- operators.inject({}) {|h, operator| h[operator.first] = l(operator.last); h}
+ operators.inject({}) {|h, operator| h[operator.first] = l(*operator.last); h}
end
# Returns a representation of the available filters for JSON serialization
@@ -245,7 +256,7 @@ class Query < ActiveRecord::Base
@all_projects_values = values
end
- def add_filter(field, operator, values)
+ def add_filter(field, operator, values=nil)
# values must be an array
return unless values.nil? || values.is_a?(Array)
# check if field is defined as an available filter
@@ -612,12 +623,39 @@ class Query < ActiveRecord::Base
when "t"
# = today
sql = relative_date_clause(db_table, db_field, 0, 0)
+ when "ld"
+ # = yesterday
+ sql = relative_date_clause(db_table, db_field, -1, -1)
when "w"
# = this week
first_day_of_week = l(:general_first_day_of_week).to_i
day_of_week = Date.today.cwday
days_ago = (day_of_week >= first_day_of_week ? day_of_week - first_day_of_week : day_of_week + 7 - first_day_of_week)
sql = relative_date_clause(db_table, db_field, - days_ago, - days_ago + 6)
+ when "lw"
+ # = last week
+ first_day_of_week = l(:general_first_day_of_week).to_i
+ day_of_week = Date.today.cwday
+ days_ago = (day_of_week >= first_day_of_week ? day_of_week - first_day_of_week : day_of_week + 7 - first_day_of_week)
+ sql = relative_date_clause(db_table, db_field, - days_ago - 7, - days_ago - 1)
+ when "l2w"
+ # = last 2 weeks
+ first_day_of_week = l(:general_first_day_of_week).to_i
+ day_of_week = Date.today.cwday
+ days_ago = (day_of_week >= first_day_of_week ? day_of_week - first_day_of_week : day_of_week + 7 - first_day_of_week)
+ sql = relative_date_clause(db_table, db_field, - days_ago - 14, - days_ago - 1)
+ when "m"
+ # = this month
+ date = Date.today
+ sql = date_clause(db_table, db_field, date.beginning_of_month, date.end_of_month)
+ when "lm"
+ # = last month
+ date = Date.today.prev_month
+ sql = date_clause(db_table, db_field, date.beginning_of_month, date.end_of_month)
+ when "y"
+ # = this year
+ date = Date.today
+ sql = date_clause(db_table, db_field, date.beginning_of_year, date.end_of_year)
when "~"
sql = "LOWER(#{db_table}.#{db_field}) LIKE '%#{connection.quote_string(value.first.to_s.downcase)}%'"
when "!~"
diff --git a/app/models/time_entry_query.rb b/app/models/time_entry_query.rb
new file mode 100644
index 000000000..c2fea35fe
--- /dev/null
+++ b/app/models/time_entry_query.rb
@@ -0,0 +1,65 @@
+# Redmine - project management software
+# Copyright (C) 2006-2012 Jean-Philippe Lang
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+class TimeEntryQuery < Query
+
+ self.queried_class = TimeEntry
+
+ self.available_columns = [
+ QueryColumn.new(:project, :sortable => "#{Project.table_name}.name", :groupable => true),
+ QueryColumn.new(:spent_on, :sortable => ["#{TimeEntry.table_name}.spent_on", "#{TimeEntry.table_name}.created_on"]),
+ QueryColumn.new(:user, :sortable => lambda {User.fields_for_order_statement}, :groupable => true),
+ QueryColumn.new(:activity, :sortable => "#{TimeEntryActivity.table_name}.position", :groupable => true),
+ QueryColumn.new(:issue, :sortable => "#{Issue.table_name}.id"),
+ QueryColumn.new(:comments),
+ QueryColumn.new(:hours, :sortable => "#{TimeEntry.table_name}.hours"),
+ ]
+
+ def initialize(attributes=nil, *args)
+ super attributes
+ self.filters ||= {}
+ add_filter('spent_on', '*') unless filters.present?
+ end
+
+ def available_filters
+ return @available_filters if @available_filters
+ @available_filters = {
+ "spent_on" => { :type => :date_past, :order => 0 }
+ }
+ @available_filters.each do |field, options|
+ options[:name] ||= l(options[:label] || "field_#{field}".gsub(/_id$/, ''))
+ end
+ @available_filters
+ end
+
+ def default_columns_names
+ @default_columns_names ||= [:project, :spent_on, :user, :activity, :issue, :comments, :hours]
+ end
+
+ # Accepts :from/:to params as shortcut filters
+ def build_from_params(params)
+ super
+ if params[:from].present? && params[:to].present?
+ add_filter('spent_on', '><', [params[:from], params[:to]])
+ elsif params[:from].present?
+ add_filter('spent_on', '>=', [params[:from]])
+ elsif params[:to].present?
+ add_filter('spent_on', '<=', [params[:to]])
+ end
+ self
+ end
+end
diff --git a/app/views/timelog/_date_range.html.erb b/app/views/timelog/_date_range.html.erb
index 17a34b99b..a0c180fc8 100644
--- a/app/views/timelog/_date_range.html.erb
+++ b/app/views/timelog/_date_range.html.erb
@@ -1,42 +1,23 @@
-<fieldset id="date-range" class="collapsible">
-<legend onclick="toggleFieldset(this);"><%= l(:label_date_range) %></legend>
-<div>
-<p>
-<%= label_tag "period_type_list", l(:description_date_range_list), :class => "hidden-for-sighted" %>
-<%= radio_button_tag 'period_type', '1', !@free_period, :onclick => '$("#from,#to").attr("disabled", true);$("#period").removeAttr("disabled");', :id => "period_type_list"%>
-<%= select_tag 'period', options_for_period_select(params[:period]),
- :onchange => 'this.form.submit();',
- :onfocus => '$("#period_type_1").attr("checked", true);',
- :disabled => @free_period %>
-</p>
-<p>
-<%= label_tag "period_type_interval", l(:description_date_range_interval), :class => "hidden-for-sighted" %>
-<%= radio_button_tag 'period_type', '2', @free_period, :onclick => '$("#from,#to").removeAttr("disabled");$("#period").attr("disabled", true);', :id => "period_type_interval" %>
-<%= l(:label_date_from_to,
- :start => ((label_tag "from", l(:description_date_from), :class => "hidden-for-sighted") +
- text_field_tag('from', @from, :size => 10, :disabled => !@free_period) + calendar_for('from')),
- :end => ((label_tag "to", l(:description_date_to), :class => "hidden-for-sighted") +
- text_field_tag('to', @to, :size => 10, :disabled => !@free_period) + calendar_for('to'))).html_safe %>
-</p>
-</div>
+<div id="query_form_content" class="hide-when-print">
+<fieldset id="filters" class="collapsible <%= @query.new_record? ? "" : "collapsed" %>">
+ <legend onclick="toggleFieldset(this);"><%= l(:label_filter_plural) %></legend>
+ <div style="<%= @query.new_record? ? "" : "display: none;" %>">
+ <%= render :partial => 'queries/filters', :locals => {:query => @query} %>
+ </div>
</fieldset>
-<p class="buttons">
+</div>
+
+<p class="buttons hide-when-print">
<%= link_to_function l(:button_apply), '$("#query_form").submit()', :class => 'icon icon-checked' %>
- <%= link_to l(:button_clear), {:controller => controller_name, :action => action_name, :project_id => @project, :issue_id => @issue}, :class => 'icon icon-reload' %>
+ <%= link_to l(:button_clear), {:project_id => @project, :issue_id => @issue}, :class => 'icon icon-reload' %>
</p>
<div class="tabs">
-<% url_params = @free_period ? { :from => @from, :to => @to } : { :period => params[:period] } %>
+<% query_params = params.slice(:f, :op, :v, :sort) %>
<ul>
- <li><%= link_to(l(:label_details), url_params.merge({:controller => 'timelog', :action => 'index', :project_id => @project, :issue_id => @issue }),
+ <li><%= link_to(l(:label_details), query_params.merge({:controller => 'timelog', :action => 'index', :project_id => @project, :issue_id => @issue }),
:class => (action_name == 'index' ? 'selected' : nil)) %></li>
- <li><%= link_to(l(:label_report), url_params.merge({:controller => 'timelog', :action => 'report', :project_id => @project, :issue_id => @issue}),
+ <li><%= link_to(l(:label_report), query_params.merge({:controller => 'timelog', :action => 'report', :project_id => @project, :issue_id => @issue}),
:class => (action_name == 'report' ? 'selected' : nil)) %></li>
</ul>
</div>
-
-<%= javascript_tag do %>
-$('#from, #to').change(function(){
- $('#period_type_interval').attr('checked', true); $('#from,#to').removeAttr('disabled'); $('#period').attr('disabled', true);
-});
-<% end %>