[#40941] Meeting Time in iCalendar is wrong

https://community.openproject.org/work_packages/40941
pull/10549/head
Andreas Pfohl 3 years ago committed by Oliver Günther
parent 4112d7bce9
commit 5356118cf6
  1. 2
      lib/core_extensions.rb
  2. 36
      lib/core_extensions/time_with_zone.rb
  3. 15
      modules/meeting/app/controllers/meetings_controller.rb
  4. 82
      modules/meeting/app/mailers/meeting_mailer.rb
  5. 2
      modules/meeting/app/views/meeting_mailer/icalendar_notification.html.erb
  6. 2
      modules/meeting/app/views/meeting_mailer/icalendar_notification.text.erb
  7. 5
      modules/meeting/spec/mailers/meeting_mailer_spec.rb

@ -27,5 +27,7 @@
#++
require 'core_extensions/string'
require 'core_extensions/time_with_zone'
::String.prepend CoreExtensions::String
::ActiveSupport::TimeWithZone.include CoreExtensions::TimeWithZone

@ -0,0 +1,36 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2022 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# 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.
#
# See COPYRIGHT and LICENSE files for more details.
#++
module CoreExtensions
module TimeWithZone
def utc_offest_for_timezone(timezone)
period = timezone.period_for_local(self)
period.offset.utc_total_offset
end
end
end

@ -45,7 +45,7 @@ class MeetingsController < ApplicationController
# from params => today's page otherwise => first page as fallback
tomorrows_meetings_count = scope.from_tomorrow.count
@page_of_today = 1 + tomorrows_meetings_count / per_page_param
@page_of_today = 1 + (tomorrows_meetings_count / per_page_param)
page = params['page'] ? page_param : @page_of_today
@ -119,18 +119,15 @@ class MeetingsController < ApplicationController
private
def set_time_zone
old_time_zone = Time.zone
def set_time_zone(&block)
zone = User.current.time_zone
if zone.nil?
localzone = Time.now.utc_offset
localzone -= 3600 if Time.now.dst?
localzone = Time.current.utc_offset
localzone -= 3600 if Time.current.dst?
zone = ::ActiveSupport::TimeZone[localzone]
end
Time.zone = zone
yield
ensure
Time.zone = old_time_zone
Time.use_zone(zone, &block)
end
def find_project

@ -48,41 +48,63 @@ class MeetingMailer < UserMailer
@meeting = content.meeting
@content_type = content_type
open_project_headers 'Project' => @meeting.project.identifier,
'Meeting-Id' => @meeting.id
set_headers @meeting
User.execute_as(user) do
timezone = Time.zone || Time.zone_default
@formatted_timezone = format_timezone_offset timezone, @meeting.start_time
attachments['meeting.ics'] = generate_ical timezone, @meeting, @content_type
mail(to: user.mail, subject: ical_subject(@meeting, @content_type))
end
end
private
def set_headers(meeting)
open_project_headers 'Project' => meeting.project.identifier, 'Meeting-Id' => meeting.id
headers['Content-Type'] = 'text/calendar; charset=utf-8; method="PUBLISH"; name="meeting.ics"'
headers['Content-Transfer-Encoding'] = '8bit'
end
author = Icalendar::Values::CalAddress.new("mailto:#{@meeting.author.mail}",
cn: @meeting.author.name)
def format_timezone_offset(timezone, time)
offset = ::ActiveSupport::TimeZone.seconds_to_utc_offset time.utc_offest_for_timezone(timezone), true
"(GMT#{offset}) #{timezone.name}"
end
# Create a calendar with an event (standard method)
entry = ::Icalendar::Calendar.new
def ical_subject(meeting, content_type)
"[#{meeting.project.name}] #{I18n.t(:"label_#{content_type}")}: #{meeting.title}"
end
User.execute_as(user) do
subject = "[#{@meeting.project.name}] #{I18n.t(:"label_#{@content_type}")}: #{@meeting.title}"
timezone = Time.zone || Time.zone_default
# Get the tzinfo object from the rails timezone
tzinfo = timezone.tzinfo
# Get the global identifier like Europe/Berlin
tzid = tzinfo.canonical_identifier
entry.add_timezone tzinfo.ical_timezone(@meeting.start_time)
entry.event do |e|
e.dtstart = Icalendar::Values::DateTime.new @meeting.start_time, 'tzid' => tzid
e.dtend = Icalendar::Values::DateTime.new @meeting.end_time, 'tzid' => tzid
e.url = meeting_url(@meeting)
e.summary = "[#{@meeting.project.name}] #{@meeting.title}"
e.description = subject
e.uid = "#{@meeting.id}@#{@meeting.project.identifier}"
e.organizer = author
end
# add needed 'METHOD:PUBLISH' to ics file
entry.publish
attachments['meeting.ics'] = entry.to_ical
mail(to: user.mail, subject: subject)
# rubocop:disable Metrics/AbcSize
def generate_ical(timezone, meeting, content_type)
calendar = ::Icalendar::Calendar.new
tzinfo = timezone.tzinfo
calendar.add_timezone tzinfo.ical_timezone(meeting.start_time)
tzid = tzinfo.canonical_identifier
calendar.event do |e|
e.dtstart = ical_datetime meeting.start_time, tzid
e.dtend = ical_datetime meeting.end_time, tzid
e.url = meeting_url(meeting)
e.summary = "[#{meeting.project.name}] #{meeting.title}"
e.description = ical_subject(meeting, content_type)
e.uid = "#{meeting.id}@#{meeting.project.identifier}"
e.organizer = ical_organizer meeting
end
calendar.publish
calendar.to_ical
end
# rubocop:enable Metrics/AbcSize
def ical_datetime(time, timezone_id)
Icalendar::Values::DateTime.new time.in_time_zone(timezone_id), 'tzid' => timezone_id
end
def ical_organizer(meeting)
Icalendar::Values::CalAddress.new("mailto:#{meeting.author.mail}", cn: meeting.author.name)
end
end

@ -4,7 +4,7 @@
<p><%= t(:text_notificiation_invited) %></p>
<ul>
<li><%=t :label_meeting_date_time %>: <%= format_time_as_date @meeting.start_time %> <%= format_time @meeting.start_time, false %>-<%= format_time @meeting.end_time, false %> <%= Time.zone %></li>
<li><%=t :label_meeting_date_time %>: <%= format_time_as_date @meeting.start_time %> <%= format_time @meeting.start_time, false %>-<%= format_time @meeting.end_time, false %> <%= @formatted_timezone %></li>
<li><%=Meeting.human_attribute_name(:location) %>: <%= @meeting.location %></li>
<li><%=Meeting.human_attribute_name(:participants_invited) %>: <%= @meeting.participants.invited.sort.join("; ") %></li>
<li><%=Meeting.human_attribute_name(:participants_attended) %>: <%= @meeting.participants.attended.sort.join("; ") %></li>

@ -3,7 +3,7 @@
<%= t(:text_notificiation_invited) %>
<%=t :label_meeting_date_time %>: <%= format_time_as_date @meeting.start_time %> <%= format_time @meeting.start_time, false %>-<%= format_time @meeting.end_time, false %> <%= Time.zone %>
<%=t :label_meeting_date_time %>: <%= format_time_as_date @meeting.start_time %> <%= format_time @meeting.start_time, false %>-<%= format_time @meeting.end_time, false %> <%= @formatted_timezone %>
<%=Meeting.human_attribute_name(:location) %>: <%= @meeting.location %>
<%=Meeting.human_attribute_name(:participants_invited) %>: <%= @meeting.participants.invited.sort.join("; ") %>
<%=Meeting.human_attribute_name(:participants_attended) %>: <%= @meeting.participants.attended.sort.join("; ") %>

@ -185,6 +185,11 @@ describe MeetingMailer, type: :mailer do
expect(entry.summary).to eq '[My project] Important meeting'
expect(entry.description).to eq "[My project] Agenda: Important meeting"
end
it 'has the correct time matching the timezone' do
expect(entry.dtstart).to eq "2021-01-19T10:00:00Z".to_time(:utc).in_time_zone("Europe/Berlin")
expect(entry.dtend).to eq ("2021-01-19T10:00:00Z".to_time(:utc) + 1.hour).in_time_zone("Europe/Berlin")
end
end
context 'with a recipient with another time zone' do

Loading…
Cancel
Save