spina-admin-conferences 2.0.1 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4422ca6e7b9b65669d3442a22035ef7f22e4ab3ea7ce817ba416fe73682e04a6
4
- data.tar.gz: af3da71b479742ac85a894c2ada05967ee7307a07cde2205d353374898e5ae13
3
+ metadata.gz: da95a8481d7f0d2b5d04a979dda9bc04455b083180c9e70106da172c604fa91c
4
+ data.tar.gz: 66514dbf26e695164135b6b4acd9e6acb6f22a5107b678fac6fc2b9e6e6cc22c
5
5
  SHA512:
6
- metadata.gz: 92eefe536ddfffab205b45a0888f3f9a88c133a26352810788b5cf927909c83cb016215f4614bed25f3f5fc93e4377f6a081c02359dc854d69a24d26337920c8
7
- data.tar.gz: d8e69093d00ecf8f14c1f6cedbfdfd1f419de19d8e485f466fb57b9974229b883120949230d1af63b3aa34dcfc9751ceeacd4bcaf9f432ce58c17cfd6a4658e1
6
+ metadata.gz: 9305b4aa9ae413dd88b35f4a4ff1d9e5363639289d7943d014d2f8f7f8e3c96085c48da590bed5cdf03986f8b7c21218a04ca9968068054219dfa3cb1ca0a779
7
+ data.tar.gz: 3296d9701c96c415b8219807591baee28631425448c6a4302e933d668a25b2e50c85448fcea6e324e2a36cebccbfefd88a37863d47ffd43b881d16f2c2677604
@@ -12,7 +12,7 @@ module Spina
12
12
  #
13
13
  # = Translations
14
14
  # - {#name}
15
- class Conference < ApplicationRecord
15
+ class Conference < ApplicationRecord # rubocop:disable Metrics/ClassLength
16
16
  include AttrJson::Record
17
17
  include AttrJson::NestedAttributes
18
18
  include Spina::Partable
@@ -70,6 +70,67 @@ module Spina
70
70
  validates :name, :start_date, :finish_date, :year, presence: true
71
71
  validates :finish_date, 'spina/admin/conferences/finish_date': true, unless: proc { |conference| conference.start_date.blank? }
72
72
 
73
+ class << self
74
+ # @return [Array<TZInfo::TimezonePeriod>] the time zone periods for the conferences
75
+ def time_zone_periods
76
+ pluck(:dates).compact.collect(&:begin).collect(&:at_beginning_of_day).collect(&:period)
77
+ end
78
+
79
+ # @return [String] a set of conferences as an iCalendar
80
+ def to_ics
81
+ Rails.cache.fetch [all, 'calendar'] do
82
+ calendar = Icalendar::Calendar.new
83
+ calendar.x_wr_calname = (Current.account || Spina::Account.first).name
84
+ calendar.add_timezone(ical_timezone)
85
+ conference_events.each { |event| calendar.add_event(event) }
86
+ calendar.publish
87
+ calendar.to_ical
88
+ end
89
+ end
90
+
91
+ private
92
+
93
+ def conference_events
94
+ Rails.cache.fetch [all, 'events'] do
95
+ all.in_batches.each_record.collect do |conference|
96
+ Rails.cache.fetch([conference, 'event']) { conference.to_event }
97
+ end
98
+ end
99
+ end
100
+
101
+ def ical_timezone
102
+ Rails.cache.fetch [self, 'time_zone'] do
103
+ Icalendar::Timezone.new do |timezone|
104
+ timezone.tzid = Time.zone.name
105
+ daylight_periods.each { |daylight_period| timezone.add_daylight(daylight_period) }
106
+ standard_periods.each { |standard_period| timezone.add_daylight(standard_period) }
107
+ end
108
+ end
109
+ end
110
+
111
+ def daylight_periods
112
+ time_zone_periods.select(&:dst?).each do |period|
113
+ Icalendar::Timezone::Daylight.new do |ical_period|
114
+ ical_period.tzoffsetfrom = period.offset.ical_offset
115
+ ical_period.tzoffsetto = period.offset.ical_offset
116
+ ical_period.tzname = period.abbreviation
117
+ ical_period.dtstart = period.starts_at.to_datetime
118
+ end
119
+ end
120
+ end
121
+
122
+ def standard_periods
123
+ time_zone_periods.reject(&:dst?).each do |period|
124
+ Icalendar::Timezone::Standard.new do |ical_period|
125
+ ical_period.tzoffsetfrom = period.offset.ical_offset
126
+ ical_period.tzoffsetto = period.offset.ical_offset
127
+ ical_period.tzname = period.abbreviation
128
+ ical_period.dtstart = period.starts_at.to_datetime
129
+ end
130
+ end
131
+ end
132
+ end
133
+
73
134
  # @return [Date, nil] the start date of the conference. Nil if the conference has no dates
74
135
  def start_date
75
136
  return if dates.blank?
@@ -127,10 +188,8 @@ module Spina
127
188
  event = Icalendar::Event.new
128
189
  return event if invalid?
129
190
 
130
- event.dtstart = start_date
131
- event.dtstart.ical_param(:value, 'DATE')
132
- event.dtend = finish_date
133
- event.dtend.ical_param(:value, 'DATE')
191
+ event.dtstart = Icalendar::Values::Date.new(dates.begin)
192
+ event.dtend = Icalendar::Values::Date.new(dates.exclude_end? ? dates.end : dates.end + 1.day)
134
193
  event.location = location
135
194
  event.contact = Spina::Account.first.email
136
195
  event.categories = Conference.model_name.human(count: 0)
@@ -138,10 +197,71 @@ module Spina
138
197
  event
139
198
  end
140
199
 
141
- # @param (see #to_event)
142
- # @deprecated Use {#to_event} instead
200
+ # @return [TZInfo::TimezonePeriod, nil] the time zone period for the conference event
201
+ def time_zone_period
202
+ return if start_date.blank?
203
+
204
+ start_date.at_beginning_of_day.period
205
+ end
206
+
207
+ # @return [String, nil] the conference as an iCalendar
143
208
  def to_ics
144
- to_event
209
+ Rails.cache.fetch [self, presentations, 'calendar'] do
210
+ calendar = Icalendar::Calendar.new
211
+ return if invalid?
212
+
213
+ calendar.x_wr_calname = name
214
+ calendar.add_timezone(ical_timezone)
215
+ eventables_events.each { |event| calendar.add_event(event) }
216
+ calendar.publish
217
+ calendar.to_ical
218
+ end
219
+ end
220
+
221
+ private
222
+
223
+ def eventables
224
+ [self, *events, *presentations]
225
+ end
226
+
227
+ def eventables_events
228
+ Rails.cache.fetch [self, *presentations, 'events'] do
229
+ eventables.collect do |eventable|
230
+ Rails.cache.fetch([eventable, 'event']) { eventable.to_event }
231
+ end
232
+ end
233
+ end
234
+
235
+ def ical_timezone
236
+ Rails.cache.fetch [self, presentations, 'time_zone'] do
237
+ Icalendar::Timezone.new do |timezone|
238
+ timezone.tzid = Time.zone.name
239
+ daylight_periods.each { |daylight_period| timezone.add_daylight(daylight_period) }
240
+ standard_periods.each { |standard_period| timezone.add_daylight(standard_period) }
241
+ end
242
+ end
243
+ end
244
+
245
+ def daylight_periods
246
+ time_zone_periods.select(&:dst?).each do |period|
247
+ Icalendar::Timezone::Daylight.new do |ical_period|
248
+ ical_period.tzoffsetfrom = period.offset.ical_offset
249
+ ical_period.tzoffsetto = period.offset.ical_offset
250
+ ical_period.tzname = period.abbreviation
251
+ ical_period.dtstart = period.starts_at.to_datetime
252
+ end
253
+ end
254
+ end
255
+
256
+ def standard_periods
257
+ time_zone_periods.reject(&:dst?).each do |period|
258
+ Icalendar::Timezone::Standard.new do |ical_period|
259
+ ical_period.tzoffsetfrom = period.offset.ical_offset
260
+ ical_period.tzoffsetto = period.offset.ical_offset
261
+ ical_period.tzname = period.abbreviation
262
+ ical_period.dtstart = period.starts_at.to_datetime
263
+ end
264
+ end
145
265
  end
146
266
  end
147
267
  end
@@ -20,14 +20,15 @@ module Spina
20
20
  # @!attribute [rw] name
21
21
  # @return [String, nil] the translated name of the event
22
22
  # @!attribute [rw] description
23
- # @return [String, nil] the translated description of the event
23
+ # @return [ActionText::RichText, nil] the translated description of the event
24
24
  # @!attribute [rw] start_datetime
25
25
  # @return [ActiveSupport::TimeWithZone, nil] the start time of the event
26
26
  # @!attribute [rw] finish_datetime
27
27
  # @return [ActiveSupport::TimeWithZone, nil] the finish time of the event
28
28
  # @!attribute [rw] location
29
29
  # @return [String, nil] the translated location of the event
30
- translates :name, :description, :location, fallbacks: true
30
+ translates :name, :location, fallbacks: true
31
+ translates :description, backend: :action_text, fallbacks: true
31
32
 
32
33
  # @return [ActiveRecord::Relation] all events, ordered by name
33
34
  scope :sorted, -> { i18n.order :name }
@@ -56,27 +57,28 @@ module Spina
56
57
  start_datetime.to_date
57
58
  end
58
59
 
60
+ # @return [TZInfo::TimezonePeriod, nil] the time zone period for the event
61
+ def time_zone_period
62
+ return if start_datetime.blank?
63
+
64
+ start_datetime.period
65
+ end
66
+
59
67
  # @return [Icalendar::Event] the event as an iCal event
60
68
  def to_event # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
61
69
  event = Icalendar::Event.new
62
70
  return event if invalid?
63
71
 
64
- event.dtstart = start_datetime
65
- event.dtend = finish_datetime
72
+ event.dtstart = Icalendar::Values::DateTime.new(start_datetime, tzid: start_datetime.time_zone.tzinfo.name)
73
+ event.dtend = Icalendar::Values::DateTime.new(finish_datetime, tzid: start_datetime.time_zone.tzinfo.name)
66
74
  event.location = location
67
75
  event.contact = Spina::Account.first.email
68
76
  event.categories = Event.model_name.human(count: 0)
69
77
  event.summary = name
70
- event.append_custom_property('alt_description', description.try(:html_safe))
71
- event.description = description.try(:gsub, %r{</?[^>]*>}, '')
78
+ event.append_custom_property('alt_description', description.to_s)
79
+ event.description = description.try(:to_plain_text)
72
80
  event
73
81
  end
74
-
75
- # @param (see #to_event)
76
- # @deprecated Use {#to_event} instead
77
- def to_ics
78
- to_event
79
- end
80
82
  end
81
83
  end
82
84
  end
@@ -28,8 +28,9 @@ module Spina
28
28
  # @!attribute [rw] title
29
29
  # @return [String, nil] the presentation title
30
30
  # @!attribute [rw] abstract
31
- # @return [String, nil] the presentation abstract
32
- translates :title, :abstract, fallbacks: true
31
+ # @return [ActionText::RichText, nil] the presentation abstract
32
+ translates :title, fallbacks: true
33
+ translates :abstract, backend: :action_text, fallbacks: true
33
34
 
34
35
  # @return [ActiveRecord::Relation] all conferences, ordered by date
35
36
  scope :sorted, -> { order start_datetime: :desc }
@@ -98,27 +99,35 @@ module Spina
98
99
  start_datetime.to_date
99
100
  end
100
101
 
102
+ # @return [ActiveSupport::TimeWithZone, nil] the presentation end time. Nil if the presentation has no start date and time
103
+ def finish_datetime
104
+ return if start_datetime.blank?
105
+
106
+ start_datetime + presentation_type.duration
107
+ end
108
+
109
+ # @return [TZInfo::TimezonePeriod, nil] the time zone period for the presentation
110
+ def time_zone_period
111
+ return if start_datetime.blank?
112
+
113
+ start_datetime.period
114
+ end
115
+
101
116
  # @return [Icalendar::Event] the presentation as an iCal event
102
117
  def to_event # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
103
118
  event = Icalendar::Event.new
104
119
  return event if invalid?
105
120
 
106
- event.dtstart = start_datetime
107
- event.dtend = start_datetime + presentation_type.duration
121
+ event.dtstart = Icalendar::Values::DateTime.new(start_datetime, tzid: start_datetime.time_zone.tzinfo.name)
122
+ event.dtend = Icalendar::Values::DateTime.new(finish_datetime, tzid: finish_datetime.time_zone.tzinfo.name)
108
123
  event.location = session.room_name
109
124
  presenters.each { |presenter| event.contact = presenter.full_name_and_institution }
110
125
  event.categories = Presentation.model_name.human(count: 0)
111
126
  event.summary = title
112
- event.append_custom_property('alt_description', abstract.try(:html_safe))
113
- event.description = abstract.try(:gsub, %r{</?[^>]*>}, '')
127
+ event.append_custom_property('alt_description', abstract.to_s)
128
+ event.description = abstract.try(:to_plain_text)
114
129
  event
115
130
  end
116
-
117
- # @param (see #to_event)
118
- # @deprecated Use {#to_event} instead
119
- def to_ics
120
- to_event
121
- end
122
131
  end
123
132
  end
124
133
  end
@@ -22,8 +22,7 @@
22
22
  .structure-form-part
23
23
  .page-form-label= Spina::Admin::Conferences::Event.human_attribute_name :description
24
24
  .page-form-content
25
- .page-form-rich-text
26
- = render 'spina/admin/shared/rich_text_field', f: f, field: :description
25
+ .page-form-rich-text= f.rich_text_area :description
27
26
 
28
27
  = f.hidden_field :id
29
28
 
@@ -23,8 +23,7 @@
23
23
  .page-form-group
24
24
  .page-form-label= Spina::Admin::Conferences::Presentation.human_attribute_name :abstract
25
25
  .page-form-content
26
- .page-form-rich-text
27
- = render 'spina/admin/shared/rich_text_field', f: f, field: :abstract
26
+ .page-form-rich-text= f.rich_text_area :abstract
28
27
 
29
28
  .page-form-group
30
29
  .page-form-label= Spina::Admin::Conferences::Presentation.human_attribute_name :presenters
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddLocaleToActionTextRichTexts < ActiveRecord::Migration[6.1]
4
+ def change
5
+ add_column :action_text_rich_texts, :locale, :string
6
+ remove_index :action_text_rich_texts, column: %i[record_type record_id name], name: :index_action_text_rich_texts_uniqueness,
7
+ unique: true
8
+ add_index :action_text_rich_texts, %i[record_type record_id name locale], name: :index_action_text_rich_texts_uniqueness,
9
+ unique: true
10
+ end
11
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ class MoveTextsToActionTextRichTexts < ActiveRecord::Migration[6.1] # :nodoc:
4
+ def change
5
+ create_mobility_tables
6
+
7
+ Spina.config.locales.each do |locale|
8
+ I18n.with_locale(locale) do
9
+ move_presentation_abstracts
10
+ move_event_descriptions
11
+ end
12
+ end
13
+
14
+ remove_column :spina_conferences_presentation_translations, :abstract, :text
15
+ remove_column :spina_conferences_event_translations, :description, :text
16
+ end
17
+
18
+ private
19
+
20
+ def create_mobility_tables
21
+ create_table :mobility_text_translations do |t| # rubocop:disable Rails/CreateTableWithTimestamps
22
+ t.string :record
23
+ end
24
+ create_table :mobility_string_translations do |t| # rubocop:disable Rails/CreateTableWithTimestamps
25
+ t.string :record
26
+ end
27
+ end
28
+
29
+ def move_presentation_abstracts
30
+ reversible do |dir|
31
+ Spina::Admin::Conferences::Presentation.in_batches.each_record do |presentation|
32
+ dir.up { presentation.update(abstract: presentation.presentation_translation.abstract) }
33
+ dir.down do
34
+ break if presentation.abstract.blank?
35
+
36
+ presentation.presentation_translation.update(abstract: presentation.abstract.read_attribute_before_type_cast('body'))
37
+ presentation.abstract.destroy
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ def move_event_descriptions
44
+ reversible do |dir|
45
+ Spina::Admin::Conferences::Event.in_batches.each_record do |event|
46
+ dir.up { event.update(description: event.event_translation.description) }
47
+ dir.down do
48
+ break if event.description.blank?
49
+
50
+ event.event_translation.update(description: event.description.read_attribute_before_type_cast('body'))
51
+ event.description.destroy
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ module Spina
59
+ module Admin
60
+ module Conferences
61
+ class PresentationTranslation < ApplicationRecord
62
+ belongs_to :presentation, foreign_key: 'spina_conferences_presentation_id'
63
+ end
64
+
65
+ class EventTranslation < ApplicationRecord
66
+ belongs_to :event, foreign_key: 'spina_conferences_event_id'
67
+ end
68
+
69
+ class Presentation < ApplicationRecord
70
+ has_one :presentation_translation, -> { where locale: I18n.locale }, foreign_key: 'spina_conferences_presentation_id'
71
+
72
+ translates :abstract, backend: :action_text
73
+ end
74
+
75
+ class Event < ApplicationRecord
76
+ has_one :event_translation, -> { where locale: I18n.locale }, foreign_key: 'spina_conferences_event_id'
77
+
78
+ translates :description, backend: :action_text
79
+ end
80
+ end
81
+ end
82
+ end
@@ -7,6 +7,7 @@ require 'rails-i18n'
7
7
  require 'stimulus-rails'
8
8
  require 'turbo-rails'
9
9
  require 'icalendar'
10
+ require 'icalendar/tzinfo'
10
11
 
11
12
  # Spina content management system.
12
13
  # @see https://www.spinacms.com Spina website
@@ -20,12 +20,10 @@ module Spina
20
20
  Spina::Part.register(Spina::Parts::Admin::Conferences::EmailAddress)
21
21
  Spina::Part.register(Spina::Parts::Admin::Conferences::Time)
22
22
  Spina::Part.register(Spina::Parts::Admin::Conferences::Url)
23
+ end
23
24
 
24
- ActiveSupport::Deprecation
25
- .new('2.0', 'Spina::Admin::Conferences')
26
- .tap { |deprecator| deprecator.deprecate_methods(Conference, to_ics: :to_event) }
27
- .tap { |deprecator| deprecator.deprecate_methods(Presentation, to_ics: :to_event) }
28
- .tap { |deprecator| deprecator.deprecate_methods(Event, to_ics: :to_event) }
25
+ config.to_prepare do
26
+ require 'mobility/action_text'
29
27
  end
30
28
  end
31
29
  end
@@ -4,7 +4,7 @@ module Spina
4
4
  module Admin
5
5
  module Conferences
6
6
  # Version number.
7
- VERSION = '2.0.1'
7
+ VERSION = '2.1.0'
8
8
  end
9
9
  end
10
10
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spina-admin-conferences
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.1
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin Malčić
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-04-16 00:00:00.000000000 Z
11
+ date: 2021-04-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: icalendar
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '2.5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: mobility-actiontext
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '='
32
+ - !ruby/object:Gem::Version
33
+ version: 0.2.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '='
39
+ - !ruby/object:Gem::Version
40
+ version: 0.2.0
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: rails
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -553,6 +567,8 @@ files:
553
567
  - db/migrate/20210315164409_add_json_attributes_to_spina_conferences_presentations.rb
554
568
  - db/migrate/20210315164410_add_json_attributes_to_spina_conferences_conferences.rb
555
569
  - db/migrate/20210315164411_convert_partables_to_json.rb
570
+ - db/migrate/20210417102513_add_locale_to_action_text_rich_texts.rb
571
+ - db/migrate/20210417102514_move_texts_to_action_text_rich_texts.rb
556
572
  - lib/spina/admin/conferences.rb
557
573
  - lib/spina/admin/conferences/engine.rb
558
574
  - lib/spina/admin/conferences/migration/renaming.rb