Patch by Takashi Kato. git-svn-id: https://svn.redmine.org/redmine/trunk@22283 e93f8b46-1217-0410-a6f0-8f06a7374b81tags/5.1.0
@@ -33,7 +33,6 @@ class MyController < ApplicationController | |||
helper :custom_fields | |||
helper :queries | |||
helper :activities | |||
helper :calendars | |||
def index | |||
page |
@@ -1,29 +0,0 @@ | |||
# frozen_string_literal: true | |||
# Redmine - project management software | |||
# Copyright (C) 2006-2023 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. | |||
module CalendarsHelper | |||
include Redmine::Utils::DateCalculation | |||
def calendar_day_css_classes(calendar, day) | |||
css = day.month==calendar.month ? +'even' : +'odd' | |||
css << " today" if User.current.today == day | |||
css << " nwday" if non_working_week_days.include?(day.cwday) | |||
css | |||
end | |||
end |
@@ -1,48 +1,37 @@ | |||
<%= form_tag({}, :data => {:cm_url => issues_context_menu_path}) do -%> | |||
<%= hidden_field_tag 'back_url', url_for(:params => request.query_parameters), :id => nil %> | |||
<table class="cal"> | |||
<thead> | |||
<tr> | |||
<th scope="col" title="<%= l(:label_week) %>" class="week-number"></th> | |||
<% 7.times do |i| %> | |||
<th scope="col"><%= day_name((calendar.first_wday + i) % 7) %></th> | |||
<% end %> | |||
</tr> | |||
</thead> | |||
<tbody> | |||
<tr> | |||
<% day = calendar.startdt %> | |||
<% while day <= calendar.enddt %> | |||
<% if day.cwday == calendar.first_wday %> | |||
<td class='week-number' title='<%= l(:label_week) %>'> | |||
<%= (day + (11 - day.cwday) % 7).cweek %> | |||
</td> | |||
<ul class="cal"> | |||
<li scope="col" title="<%= l(:label_week) %>" class="calhead week-number"></li> | |||
<% 7.times do |i| %> | |||
<li scope="col" class="calhead"><%= day_name((calendar.first_wday + i) % 7) %></li> | |||
<% end %> | |||
<td class="<%= calendar_day_css_classes(calendar, day) %>"> | |||
<p class="day-num"><%= day.day %></p> | |||
<% calendar.events_on(day).each do |i| %> | |||
<% if i.is_a? Issue %> | |||
<div class="<%= i.css_classes %> <%= 'starting' if day == i.start_date %> <%= 'ending' if day == i.due_date %> tooltip hascontextmenu"> | |||
<%= "#{i.project} -" unless @project && @project == i.project %> | |||
<%= link_to_issue i, :truncate => 30 %> | |||
<span class="tip"><%= render_issue_tooltip i %></span> | |||
<%= check_box_tag 'ids[]', i.id, false, :style => 'display:none;', :id => nil %> | |||
</div> | |||
<% else %> | |||
<span class="icon icon-package"> | |||
<%= "#{i.project} -" unless @project && @project == i.project %> | |||
<%= link_to_version i %> | |||
</span> | |||
<% calendar.format_month.each_slice(7) do |week| %> | |||
<li class='week-number'> | |||
<span class="label-week"><%= l(:label_week) %></span> <%= calendar.week_number week.first %> | |||
</li> | |||
<% week.each do |day| %> | |||
<li class="<%= calendar.day_css_classes day %> calbody"> | |||
<p class="day-num"><%= day.day %> | |||
<span class="abbr-day">(<%= abbr_day_name(day.cwday) %>)</span> | |||
</p> | |||
<% calendar.events_on(day).each do |i| %> | |||
<% if i.is_a? Issue %> | |||
<%= tag.div class: [ i.css_classes, 'tooltip hascontextmenu', starting: day == i.start_date, ending: day == i.due_date] do %> | |||
<%= "#{i.project} -" unless @project && @project == i.project %> | |||
<%= link_to_issue i, :truncate => 30 %> | |||
<span class="tip"><%= render_issue_tooltip i %></span> | |||
<%= check_box_tag 'ids[]', i.id, false, :style => 'display:none;', :id => nil %> | |||
<% end %> | |||
<% else %> | |||
<span class="icon icon-package"> | |||
<%= "#{i.project} -" unless @project && @project == i.project %> | |||
<%= link_to_version i %> | |||
</span> | |||
<% end %> | |||
<% end %> | |||
</li> | |||
<% end %> | |||
<% end %> | |||
<% end %> | |||
</td> | |||
<% if day.cwday==calendar.last_wday and day!=calendar.enddt %> | |||
</tr><tr> | |||
<% end %> | |||
<% day = day + 1 %> | |||
<% end %> | |||
</tr> | |||
</tbody> | |||
</table> | |||
</ul> | |||
<% end %> | |||
<%= context_menu %> |
@@ -23,6 +23,8 @@ module Redmine | |||
# Simple class to compute the start and end dates of a calendar | |||
class Calendar | |||
include Redmine::I18n | |||
include Redmine::Utils::DateCalculation | |||
attr_reader :startdt, :enddt | |||
def initialize(date, lang = current_language, period = :month) | |||
@@ -47,6 +49,21 @@ module Redmine | |||
end | |||
end | |||
def format_month | |||
(@startdt..@enddt).to_a | |||
end | |||
def week_number(day) | |||
(day + (11 - day.cwday) % 7).cweek | |||
end | |||
def day_css_classes(day) | |||
css = day.month==month ? +'even' : +'odd' | |||
css << " today" if User.current.today == day | |||
css << " nwday" if non_working_week_days.include?(day.cwday) | |||
css | |||
end | |||
# Sets calendar events | |||
def events=(events) | |||
@events = events |
@@ -103,6 +103,10 @@ module Redmine | |||
::I18n.t('date.day_names')[day % 7] | |||
end | |||
def abbr_day_name(day) | |||
::I18n.t('date.abbr_day_names')[day % 7] | |||
end | |||
def day_letter(day) | |||
::I18n.t('date.abbr_day_names')[day % 7].first | |||
end |
@@ -1100,21 +1100,60 @@ vertical-align: bottom; | |||
} | |||
/***** Calendar *****/ | |||
table.cal {width: 100%; margin: 0 0 6px 0; border: 1px solid #c0c0c0; border-spacing: 0; border-radius: 3px;} | |||
table.cal thead th {width: 14%; background-color:#EEEEEE; padding: 4px; } | |||
table.cal thead th.week-number {width: auto;} | |||
table.cal tbody tr {height: 100px;} | |||
table.cal td .icon {padding-top: 2px; padding-bottom: 3px;} | |||
table.cal td {border: 1px solid #d7d7d7; vertical-align: top; font-size: 0.9em; border-bottom: 0; border-right: 0;} | |||
table.cal td.week-number { background-color:#EEEEEE; padding: 4px; border:none; font-size: 1em;} | |||
table.cal td p.day-num {font-size: 1.1em; text-align:right;} | |||
table.cal td.odd p.day-num {color: #bbb;} | |||
table.cal td.today {background:#ffffdd;} | |||
table.cal td.today p.day-num {font-weight: bold;} | |||
table.cal td.nwday:not(.odd) {background-color:#f1f1f1;} | |||
table.cal .starting a.issue, p.cal.legend .starting {background: url(../images/bullet_go.png) no-repeat -1px -2px; padding-left:16px;} | |||
table.cal .ending a.issue, p.cal.legend .ending {background: url(../images/bullet_end.png) no-repeat -1px -2px; padding-left:16px;} | |||
table.cal .starting.ending a.issue, p.cal.legend .starting.ending {background: url(../images/bullet_diamond.png) no-repeat -1px -2px; padding-left:16px;} | |||
ul.cal { | |||
list-style: none; | |||
width: 100%; | |||
padding: 0; | |||
display: grid; | |||
grid-template-columns: 2rem repeat(7, 1fr); | |||
margin: 0; | |||
border: 1px solid #c0c0c0; | |||
border-spacing: 0; | |||
border-radius: 3px; | |||
} | |||
.cal .calhead { | |||
background-color:#eee; | |||
text-align: center; | |||
font-weight: bold; | |||
padding: 4px | |||
} | |||
.cal .week-number { | |||
background-color:#eee; | |||
border:none; | |||
font-size: 1em; | |||
padding: 4px; | |||
text-align: center; | |||
} | |||
.cal .week-number .label-week { | |||
display: none; | |||
} | |||
.cal .calbody { | |||
border: 1px solid #d7d7d7; | |||
vertical-align: top; | |||
font-size: 0.9em; | |||
border-bottom: 0; | |||
border-right: 0; | |||
line-height: 1.2; | |||
min-height: calc(1.2em * 6); | |||
padding: 2px; | |||
} | |||
.cal .calbody p.day-num {font-size: 1.1em; text-align:right;} | |||
.cal .calbody .abbr-day {display:none} | |||
.cal .calbody.odd p.day-num {color: #bbb;} | |||
.cal .calbody.today {background:#ffd;} | |||
.cal .calbody.today p.day-num {font-weight: bold;} | |||
.cal .calbody .icon {padding-top: 2px; padding-bottom: 3px;} | |||
.cal .calbody.nwday:not(.odd) {background-color:#f1f1f1;} | |||
.cal .starting a.issue, p.cal.legend .starting {background: url(../images/bullet_go.png) no-repeat -1px -2px; padding-left:16px;} | |||
.cal .ending a.issue, p.cal.legend .ending {background: url(../images/bullet_end.png) no-repeat -1px -2px; padding-left:16px;} | |||
.cal .starting.ending a.issue, p.cal.legend .starting.ending {background: url(../images/bullet_diamond.png) no-repeat -1px -2px; padding-left:16px;} | |||
p.cal.legend span {display:block;} | |||
.controller-calendars p.buttons {margin-top: unset;} | |||
@@ -819,6 +819,38 @@ | |||
margin-left: 0; | |||
width: 100%; | |||
} | |||
/* Calendar */ | |||
ul.cal { | |||
display: block | |||
} | |||
.cal .calhead { | |||
display: none | |||
} | |||
.cal .calbody { | |||
min-height: calc(1.2em * 3); | |||
} | |||
.cal .calbody .abbr-day { | |||
display: inline; | |||
} | |||
.cal .week-number { | |||
border: 1px solid #c0c0c0; | |||
text-align: left; | |||
font-weight: bold; | |||
background-color: #def; | |||
} | |||
.cal .week-number .label-week { | |||
display: inline; | |||
} | |||
.cal .calbody p.day-num { | |||
font-size: 1.1em; | |||
text-align: left; | |||
} | |||
} | |||
@media all and (max-width: 599px) { |
@@ -65,19 +65,17 @@ class CalendarsControllerTest < Redmine::ControllerTest | |||
# Assert context menu on issues | |||
assert_select 'form[data-cm-url=?]', '/issues/context_menu' | |||
assert_select 'table.cal' do | |||
assert_select 'tr' do | |||
assert_select 'td' do | |||
assert_select( | |||
'div.issue.hascontextmenu.tooltip.starting', | |||
:text => /Add ingredients categories/ | |||
) do | |||
assert_select 'a.issue[href=?]', '/issues/2', :text => 'Feature request #2' | |||
assert_select 'span.tip' do | |||
assert_select 'img[class="gravatar"]' | |||
end | |||
assert_select 'input[name=?][type=?][value=?]', 'ids[]', 'checkbox', '2' | |||
assert_select 'ul.cal' do | |||
assert_select 'li' do | |||
assert_select( | |||
'div.issue.hascontextmenu.tooltip.starting', | |||
:text => /Add ingredients categories/ | |||
) do | |||
assert_select 'a.issue[href=?]', '/issues/2', :text => 'Feature request #2' | |||
assert_select 'span.tip' do | |||
assert_select 'img[class="gravatar"]' | |||
end | |||
assert_select 'input[name=?][type=?][value=?]', 'ids[]', 'checkbox', '2' | |||
end | |||
end | |||
end | |||
@@ -89,16 +87,14 @@ class CalendarsControllerTest < Redmine::ControllerTest | |||
get(:show, :params => {:project_id => 1}) | |||
assert_response :success | |||
assert_select 'table.cal' do | |||
assert_select 'tr' do | |||
assert_select 'td' do | |||
assert_select( | |||
'div.issue.hascontextmenu.tooltip.ending', | |||
:text => /Cannot print recipes/ | |||
) do | |||
assert_select 'a.issue[href=?]', '/issues/1', :text => 'Bug #1' | |||
assert_select 'input[name=?][type=?][value=?]', 'ids[]', 'checkbox', '1' | |||
end | |||
assert_select 'ul.cal' do | |||
assert_select 'li' do | |||
assert_select( | |||
'div.issue.hascontextmenu.tooltip.ending', | |||
:text => /Cannot print recipes/ | |||
) do | |||
assert_select 'a.issue[href=?]', '/issues/1', :text => 'Bug #1' | |||
assert_select 'input[name=?][type=?][value=?]', 'ids[]', 'checkbox', '1' | |||
end | |||
end | |||
end | |||
@@ -120,24 +116,22 @@ class CalendarsControllerTest < Redmine::ControllerTest | |||
) | |||
assert_response :success | |||
assert_select 'table.cal' do | |||
assert_select 'tr' do | |||
assert_select 'td' do | |||
assert_select 'ul.cal' do | |||
assert_select 'li' do | |||
assert_select( | |||
'div.issue.hascontextmenu.tooltip.starting.ending', | |||
:text => /#{subject}/ | |||
) do | |||
assert_select( | |||
'div.issue.hascontextmenu.tooltip.starting.ending', | |||
:text => /#{subject}/ | |||
) do | |||
assert_select( | |||
'a.issue[href=?]', "/issues/#{issue.id}", | |||
:text => "Bug ##{issue.id}" | |||
) | |||
assert_select( | |||
'input[name=?][type=?][value=?]', | |||
'ids[]', | |||
'checkbox', | |||
issue.id.to_s | |||
) | |||
end | |||
'a.issue[href=?]', "/issues/#{issue.id}", | |||
:text => "Bug ##{issue.id}" | |||
) | |||
assert_select( | |||
'input[name=?][type=?][value=?]', | |||
'ids[]', | |||
'checkbox', | |||
issue.id.to_s | |||
) | |||
end | |||
end | |||
end | |||
@@ -149,14 +143,12 @@ class CalendarsControllerTest < Redmine::ControllerTest | |||
get(:show, :params => {:project_id => 1}) | |||
assert_response :success | |||
assert_select 'table.cal' do | |||
assert_select 'tr' do | |||
assert_select 'td' do | |||
assert_select( | |||
'span.icon.icon-package' | |||
) do | |||
assert_select 'a[href=?]', '/versions/2', :text => '1.0' | |||
end | |||
assert_select 'ul.cal' do | |||
assert_select 'li' do | |||
assert_select( | |||
'span.icon.icon-package' | |||
) do | |||
assert_select 'a[href=?]', '/versions/2', :text => '1.0' | |||
end | |||
end | |||
end | |||
@@ -179,16 +171,14 @@ class CalendarsControllerTest < Redmine::ControllerTest | |||
get :show | |||
assert_response :success | |||
assert_select 'table.cal' do | |||
assert_select 'tr' do | |||
assert_select 'td' do | |||
assert_select( | |||
'div.issue.hascontextmenu.tooltip.starting', | |||
:text => /eCookbook.*Add ingredients categories/m | |||
) do | |||
assert_select 'a.issue[href=?]', '/issues/2', :text => 'Feature request #2' | |||
assert_select 'input[name=?][type=?][value=?]', 'ids[]', 'checkbox', '2' | |||
end | |||
assert_select 'ul.cal' do | |||
assert_select 'li' do | |||
assert_select( | |||
'div.issue.hascontextmenu.tooltip.starting', | |||
:text => /eCookbook.*Add ingredients categories/m | |||
) do | |||
assert_select 'a.issue[href=?]', '/issues/2', :text => 'Feature request #2' | |||
assert_select 'input[name=?][type=?][value=?]', 'ids[]', 'checkbox', '2' | |||
end | |||
end | |||
end | |||
@@ -200,17 +190,15 @@ class CalendarsControllerTest < Redmine::ControllerTest | |||
get :show | |||
assert_response :success | |||
assert_select 'table.cal' do | |||
assert_select 'tr' do | |||
assert_select 'td' do | |||
assert_select 'ul.cal' do | |||
assert_select 'li' do | |||
assert_select( | |||
'span.icon.icon-package' | |||
) do | |||
assert_select( | |||
'span.icon.icon-package' | |||
) do | |||
assert_select( | |||
'a[href=?]', '/versions/2', | |||
:text => 'eCookbook - 1.0' | |||
) | |||
end | |||
'a[href=?]', '/versions/2', | |||
:text => 'eCookbook - 1.0' | |||
) | |||
end | |||
end | |||
end | |||
@@ -228,16 +216,16 @@ class CalendarsControllerTest < Redmine::ControllerTest | |||
assert_response :success | |||
end | |||
assert_select 'tr' do | |||
assert_select 'td.week-number', :text => '53' | |||
assert_select 'td.odd', :text => '27' | |||
assert_select 'td.even', :text => '2' | |||
assert_select 'ul' do | |||
assert_select 'li.week-number:nth-of-type(2)', :text => /53$/ | |||
assert_select 'li.odd', :text => /^27/ | |||
assert_select 'li.even', :text => /^2/ | |||
end | |||
assert_select 'tr' do | |||
assert_select 'td.week-number', :text => '1' | |||
assert_select 'td.odd', :text => '3' | |||
assert_select 'td.even', :text => '9' | |||
assert_select 'ul' do | |||
assert_select 'li.week-number', :text => /1$/ | |||
assert_select 'li.odd', :text => /^3/ | |||
assert_select 'li.even', :text => /^9/ | |||
end | |||
with_settings :start_of_week => 1 do | |||
@@ -251,16 +239,16 @@ class CalendarsControllerTest < Redmine::ControllerTest | |||
assert_response :success | |||
end | |||
assert_select 'tr' do | |||
assert_select 'td.week-number', :text => '53' | |||
assert_select 'td.even', :text => '28' | |||
assert_select 'td.even', :text => '3' | |||
assert_select 'ul' do | |||
assert_select 'li.week-number:nth-of-type(2)', :text => /53$/ | |||
assert_select 'li.even', :text => /^28/ | |||
assert_select 'li.even', :text => /^3/ | |||
end | |||
assert_select 'tr' do | |||
assert_select 'td.week-number', :text => '1' | |||
assert_select 'td.even', :text => '4' | |||
assert_select 'td.even', :text => '10' | |||
assert_select 'ul' do | |||
assert_select 'li.week-number', :text => /1$/ | |||
assert_select 'li.even', :text => /^4/ | |||
assert_select 'li.even', :text => /^10/ | |||
end | |||
end | |||
@@ -296,12 +284,12 @@ class CalendarsControllerTest < Redmine::ControllerTest | |||
) | |||
assert_response :success | |||
assert_select 'tr:nth-child(2)' do | |||
assert_select 'td.week-number', :text => '49' | |||
assert_select 'ul' do | |||
assert_select 'li.week-number:nth-of-type(2)', :text => /48$/ | |||
# non working days should have "nwday" CSS class | |||
assert_select 'td.nwday', 2 | |||
assert_select 'td.nwday', :text => '4' | |||
assert_select 'td.nwday', :text => '10' | |||
assert_select 'li.nwday', 10 | |||
assert_select 'li.nwday', :text => /^4/ | |||
assert_select 'li.nwday', :text => /^10/ | |||
end | |||
end | |||
end |
@@ -444,24 +444,22 @@ class MyControllerTest < Redmine::ControllerTest | |||
assert_select 'form[data-cm-url=?]', '/issues/context_menu' | |||
assert_select 'table.cal' do | |||
assert_select 'tr' do | |||
assert_select 'td' do | |||
assert_select 'ul.cal' do | |||
assert_select 'li' do | |||
assert_select( | |||
'div.issue.hascontextmenu.tooltip.starting.ending', | |||
:text => /eCookbook.*#{subject}/m | |||
) do | |||
assert_select( | |||
'a.issue[href=?]', "/issues/#{issue.id}", | |||
:text => "Bug ##{issue.id}" | |||
) | |||
assert_select( | |||
'div.issue.hascontextmenu.tooltip.starting.ending', | |||
:text => /eCookbook.*#{subject}/m | |||
) do | |||
assert_select( | |||
'a.issue[href=?]', "/issues/#{issue.id}", | |||
:text => "Bug ##{issue.id}" | |||
) | |||
assert_select( | |||
'input[name=?][type=?][value=?]', | |||
'ids[]', | |||
'checkbox', | |||
issue.id.to_s | |||
) | |||
end | |||
'input[name=?][type=?][value=?]', | |||
'ids[]', | |||
'checkbox', | |||
issue.id.to_s | |||
) | |||
end | |||
end | |||
end |