680 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			Ruby
		
	
	
	
			
		
		
	
	
			680 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			Ruby
		
	
	
	
| # frozen_string_literal: true
 | |
| 
 | |
| # name: discourse-calendar
 | |
| # about: Display a calendar in the first post of a topic
 | |
| # version: 0.2
 | |
| # author: Daniel Waterworth, Joffrey Jaffeux
 | |
| # url: https://github.com/discourse/discourse-calendar
 | |
| # transpile_js: true
 | |
| 
 | |
| libdir = File.join(File.dirname(__FILE__), "vendor/holidays/lib")
 | |
| $LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
 | |
| 
 | |
| gem 'rrule', '0.4.4', require: false
 | |
| 
 | |
| load File.expand_path('../lib/calendar_settings_validator.rb', __FILE__)
 | |
| 
 | |
| enabled_site_setting :calendar_enabled
 | |
| 
 | |
| register_asset 'stylesheets/vendor/fullcalendar.min.css'
 | |
| register_asset 'stylesheets/common/discourse-calendar.scss'
 | |
| register_asset 'stylesheets/common/upcoming-events-calendar.scss'
 | |
| register_asset 'stylesheets/common/discourse-post-event.scss'
 | |
| register_asset 'stylesheets/common/discourse-post-event-preview.scss'
 | |
| register_asset 'stylesheets/common/discourse-post-event-builder.scss'
 | |
| register_asset 'stylesheets/common/discourse-post-event-invitees.scss'
 | |
| register_asset 'stylesheets/common/discourse-post-event-upcoming-events.scss'
 | |
| register_asset 'stylesheets/common/discourse-post-event-core-ext.scss'
 | |
| register_asset 'stylesheets/mobile/discourse-post-event-core-ext.scss', :mobile
 | |
| register_asset 'stylesheets/common/discourse-post-event-bulk-invite-modal.scss'
 | |
| register_asset 'stylesheets/mobile/discourse-calendar.scss', :mobile
 | |
| register_asset 'stylesheets/mobile/discourse-post-event.scss', :mobile
 | |
| register_asset 'stylesheets/desktop/discourse-calendar.scss', :desktop
 | |
| register_asset 'stylesheets/colors.scss', :color_definitions
 | |
| register_asset 'stylesheets/common/user-preferences.scss'
 | |
| register_svg_icon 'fas fa-calendar-day'
 | |
| register_svg_icon 'fas fa-clock'
 | |
| register_svg_icon 'fas fa-file-csv'
 | |
| register_svg_icon 'fas fa-star'
 | |
| register_svg_icon 'fas fa-file-upload'
 | |
| 
 | |
| after_initialize do
 | |
|   module ::DiscourseCalendar
 | |
|     PLUGIN_NAME ||= 'discourse-calendar'
 | |
| 
 | |
|     # Type of calendar ('static' or 'dynamic')
 | |
|     CALENDAR_CUSTOM_FIELD ||= 'calendar'
 | |
| 
 | |
|     # User custom field set when user is on holiday
 | |
|     HOLIDAY_CUSTOM_FIELD ||= 'on_holiday'
 | |
| 
 | |
|     # List of all users on holiday
 | |
|     USERS_ON_HOLIDAY_KEY ||= 'users_on_holiday'
 | |
| 
 | |
|     # User region used in finding holidays
 | |
|     REGION_CUSTOM_FIELD ||= 'holidays-region'
 | |
| 
 | |
|     # List of groups
 | |
|     GROUP_TIMEZONES_CUSTOM_FIELD ||= 'group-timezones'
 | |
| 
 | |
|     def self.users_on_holiday
 | |
|       PluginStore.get(PLUGIN_NAME, USERS_ON_HOLIDAY_KEY)
 | |
|     end
 | |
| 
 | |
|     def self.users_on_holiday=(usernames)
 | |
|       PluginStore.set(PLUGIN_NAME, USERS_ON_HOLIDAY_KEY, usernames)
 | |
|     end
 | |
| 
 | |
|     class Engine < ::Rails::Engine
 | |
|       engine_name PLUGIN_NAME
 | |
|       isolate_namespace DiscourseCalendar
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   module ::DiscoursePostEvent
 | |
|     PLUGIN_NAME ||= 'discourse-post-event'
 | |
| 
 | |
|     # Topic where op has a post event custom field
 | |
|     TOPIC_POST_EVENT_STARTS_AT ||= 'TopicEventStartsAt'
 | |
|     TOPIC_POST_EVENT_ENDS_AT ||= 'TopicEventEndsAt'
 | |
| 
 | |
|     class Engine < ::Rails::Engine
 | |
|       engine_name PLUGIN_NAME
 | |
|       isolate_namespace DiscoursePostEvent
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   # DISCOURSE POST EVENT
 | |
| 
 | |
|   %w[
 | |
|     ../app/controllers/discourse_post_event_controller.rb
 | |
|     ../app/controllers/discourse_post_event/invitees_controller.rb
 | |
|     ../app/controllers/discourse_post_event/events_controller.rb
 | |
|     ../app/controllers/discourse_post_event/upcoming_events_controller.rb
 | |
|     ../app/models/discourse_post_event/event.rb
 | |
|     ../app/models/discourse_post_event/event_date.rb
 | |
|     ../app/models/discourse_post_event/invitee.rb
 | |
|     ../lib/discourse_post_event/event_parser.rb
 | |
|     ../lib/discourse_post_event/event_validator.rb
 | |
|     ../lib/discourse_post_event/rrule_generator.rb
 | |
|     ../jobs/regular/discourse_post_event/bulk_invite.rb
 | |
|     ../jobs/regular/discourse_post_event/send_reminder.rb
 | |
|     ../lib/discourse_post_event/event_finder.rb
 | |
|     ../app/serializers/discourse_post_event/invitee_serializer.rb
 | |
|     ../app/serializers/discourse_post_event/event_serializer.rb
 | |
|   ].each { |path| load File.expand_path(path, __FILE__) }
 | |
| 
 | |
|   ::ActionController::Base.prepend_view_path File.expand_path(
 | |
|                                                '../app/views',
 | |
|                                                __FILE__
 | |
|                                              )
 | |
| 
 | |
|   Discourse::Application.routes.append do
 | |
|     mount ::DiscoursePostEvent::Engine, at: '/'
 | |
|   end
 | |
| 
 | |
|   DiscoursePostEvent::Engine.routes.draw do
 | |
|     get '/discourse-post-event/events' => 'events#index',
 | |
|         format: :json
 | |
|     get '/discourse-post-event/events/:id' => 'events#show'
 | |
|     delete '/discourse-post-event/events/:id' => 'events#destroy'
 | |
|     post '/discourse-post-event/events' => 'events#create'
 | |
|     post '/discourse-post-event/events/:id/csv-bulk-invite' =>
 | |
|            'events#csv_bulk_invite'
 | |
|     post '/discourse-post-event/events/:id/bulk-invite' => 'events#bulk_invite',
 | |
|          format: :json
 | |
|     post '/discourse-post-event/events/:id/invite' => 'events#invite'
 | |
|     put '/discourse-post-event/events/:post_id/invitees/:id' =>
 | |
|           'invitees#update'
 | |
|     post '/discourse-post-event/events/:post_id/invitees' => 'invitees#create'
 | |
|     get '/discourse-post-event/events/:post_id/invitees' => 'invitees#index'
 | |
|     delete '/discourse-post-event/events/:post_id/invitees/:id' =>
 | |
|              'invitees#destroy'
 | |
|     get '/upcoming-events' => 'upcoming_events#index'
 | |
|   end
 | |
| 
 | |
|   reloadable_patch do
 | |
|     Post.class_eval do
 | |
|       has_one :event,
 | |
|               dependent: :destroy,
 | |
|               class_name: 'DiscoursePostEvent::Event',
 | |
|               foreign_key: :id
 | |
| 
 | |
|       validate :valid_event
 | |
|       def valid_event
 | |
|         return unless self.raw_changed?
 | |
|         validator = DiscoursePostEvent::EventValidator.new(self)
 | |
|         validator.validate_event
 | |
|       end
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   add_to_class(:user, :can_create_discourse_post_event?) do
 | |
|     if defined?(@can_create_discourse_post_event)
 | |
|       return @can_create_discourse_post_event
 | |
|     end
 | |
|     @can_create_discourse_post_event = begin
 | |
|       return true if staff?
 | |
|       allowed_groups = SiteSetting.discourse_post_event_allowed_on_groups.to_s.split('|').compact
 | |
|       allowed_groups.present? && groups.where(id: allowed_groups).exists?
 | |
|     rescue StandardError
 | |
|       false
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   add_to_class(:guardian, :can_act_on_invitee?) do |invitee|
 | |
|     user && (user.staff? || user.id == invitee.user_id)
 | |
|   end
 | |
| 
 | |
|   add_to_class(:guardian, :can_create_discourse_post_event?) do
 | |
|     user && user.can_create_discourse_post_event?
 | |
|   end
 | |
| 
 | |
|   add_to_serializer(:current_user, :can_create_discourse_post_event) do
 | |
|     object.can_create_discourse_post_event?
 | |
|   end
 | |
| 
 | |
|   add_to_class(:user, :can_act_on_discourse_post_event?) do |event|
 | |
|     if defined?(@can_act_on_discourse_post_event)
 | |
|       return @can_act_on_discourse_post_event
 | |
|     end
 | |
|     @can_act_on_discourse_post_event = begin
 | |
|       return true if staff?
 | |
|       can_create_discourse_post_event? && event.post.user_id == id
 | |
|     rescue StandardError
 | |
|       false
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   add_to_class(:guardian, :can_act_on_discourse_post_event?) do |event|
 | |
|     user && user.can_act_on_discourse_post_event?(event)
 | |
|   end
 | |
| 
 | |
|   add_class_method(:group, :discourse_post_event_allowed_groups) do
 | |
|     where(
 | |
|       id: SiteSetting.discourse_post_event_allowed_on_groups.split('|').compact
 | |
|     )
 | |
|   end
 | |
| 
 | |
|   TopicView.on_preload do |topic_view|
 | |
|     if SiteSetting.discourse_post_event_enabled
 | |
|       topic_view.instance_variable_set(:@posts, topic_view.posts.includes(:event))
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   add_to_serializer(:post, :event) do
 | |
|     DiscoursePostEvent::EventSerializer.new(
 | |
|       object.event,
 | |
|       scope: scope, root: false
 | |
|     )
 | |
|   end
 | |
| 
 | |
|   add_to_serializer(:post, :include_event?) do
 | |
|     SiteSetting.discourse_post_event_enabled && !object.nil? &&
 | |
|       !object.deleted_at.present?
 | |
|   end
 | |
| 
 | |
|   on(:post_process_cooked) do |doc, post|
 | |
|     DiscoursePostEvent::Event.update_from_raw(post)
 | |
|   end
 | |
| 
 | |
|   on(:post_destroyed) do |post|
 | |
|     if SiteSetting.discourse_post_event_enabled && post.event
 | |
|       post.event.update!(deleted_at: Time.now)
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   on(:post_recovered) do |post|
 | |
|     if SiteSetting.discourse_post_event_enabled && post.event
 | |
|       post.event.update!(deleted_at: nil)
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   TopicList.preloaded_custom_fields << DiscoursePostEvent::TOPIC_POST_EVENT_STARTS_AT
 | |
| 
 | |
|   add_to_serializer(:topic_view, :event_starts_at, false) do
 | |
|     object.topic.custom_fields[DiscoursePostEvent::TOPIC_POST_EVENT_STARTS_AT]
 | |
|   end
 | |
| 
 | |
|   add_to_serializer(:topic_view, 'include_event_starts_at?') do
 | |
|     SiteSetting.discourse_post_event_enabled &&
 | |
|     SiteSetting.display_post_event_date_on_topic_title &&
 | |
|     object.topic.custom_fields.keys.include?(
 | |
|       DiscoursePostEvent::TOPIC_POST_EVENT_STARTS_AT
 | |
|     )
 | |
|   end
 | |
| 
 | |
|   add_to_class(:topic, :event_starts_at) do
 | |
|     @event_starts_at ||= custom_fields[DiscoursePostEvent::TOPIC_POST_EVENT_STARTS_AT]
 | |
|   end
 | |
| 
 | |
|   add_to_serializer(:topic_list_item, :event_starts_at, false) do
 | |
|     object.event_starts_at
 | |
|   end
 | |
| 
 | |
|   add_to_serializer(:topic_list_item, 'include_event_starts_at?') do
 | |
|     SiteSetting.discourse_post_event_enabled &&
 | |
|     SiteSetting.display_post_event_date_on_topic_title &&
 | |
|     object.event_starts_at
 | |
|   end
 | |
| 
 | |
|   TopicList.preloaded_custom_fields << DiscoursePostEvent::TOPIC_POST_EVENT_ENDS_AT
 | |
| 
 | |
|   add_to_serializer(:topic_view, :event_ends_at, false) do
 | |
|     object.topic.custom_fields[DiscoursePostEvent::TOPIC_POST_EVENT_ENDS_AT]
 | |
|   end
 | |
| 
 | |
|   add_to_serializer(:topic_view, 'include_event_ends_at?') do
 | |
|     SiteSetting.discourse_post_event_enabled &&
 | |
|     SiteSetting.display_post_event_date_on_topic_title &&
 | |
|     object.topic.custom_fields.keys.include?(
 | |
|       DiscoursePostEvent::TOPIC_POST_EVENT_ENDS_AT
 | |
|     )
 | |
|   end
 | |
| 
 | |
|   add_to_class(:topic, :event_ends_at) do
 | |
|     @event_ends_at ||= custom_fields[DiscoursePostEvent::TOPIC_POST_EVENT_ENDS_AT]
 | |
|   end
 | |
| 
 | |
|   add_to_serializer(:topic_list_item, :event_ends_at, false) do
 | |
|     object.event_ends_at
 | |
|   end
 | |
| 
 | |
|   add_to_serializer(:topic_list_item, 'include_event_ends_at?') do
 | |
|     SiteSetting.discourse_post_event_enabled &&
 | |
|     SiteSetting.display_post_event_date_on_topic_title &&
 | |
|     object.event_ends_at
 | |
|   end
 | |
| 
 | |
|   # DISCOURSE CALENDAR
 | |
| 
 | |
|   %w[
 | |
|     ../app/models/calendar_event.rb
 | |
|     ../app/serializers/user_timezone_serializer.rb
 | |
|     ../jobs/scheduled/create_holiday_events.rb
 | |
|     ../jobs/scheduled/destroy_past_events.rb
 | |
|     ../jobs/scheduled/update_holiday_usernames.rb
 | |
|     ../jobs/scheduled/monitor_event_dates.rb
 | |
|     ../lib/calendar_validator.rb
 | |
|     ../lib/calendar.rb
 | |
|     ../lib/event_validator.rb
 | |
|     ../lib/group_timezones.rb
 | |
|     ../lib/time_sniffer.rb
 | |
|   ].each { |path| load File.expand_path(path, __FILE__) }
 | |
| 
 | |
|   register_post_custom_field_type(
 | |
|     DiscourseCalendar::CALENDAR_CUSTOM_FIELD,
 | |
|     :string
 | |
|   )
 | |
|   register_post_custom_field_type(
 | |
|     DiscourseCalendar::GROUP_TIMEZONES_CUSTOM_FIELD,
 | |
|     :json
 | |
|   )
 | |
|   TopicView.default_post_custom_fields <<
 | |
|     DiscourseCalendar::GROUP_TIMEZONES_CUSTOM_FIELD
 | |
| 
 | |
|   register_user_custom_field_type(
 | |
|     DiscourseCalendar::HOLIDAY_CUSTOM_FIELD,
 | |
|     :boolean
 | |
|   )
 | |
| 
 | |
|   allow_staff_user_custom_field(DiscourseCalendar::HOLIDAY_CUSTOM_FIELD)
 | |
|   allow_staff_user_custom_field(DiscourseCalendar::REGION_CUSTOM_FIELD)
 | |
|   register_editable_user_custom_field(DiscourseCalendar::REGION_CUSTOM_FIELD)
 | |
| 
 | |
|   on(:site_setting_changed) do |name, old_value, new_value|
 | |
|     unless %i[all_day_event_start_time all_day_event_end_time].include? name
 | |
|       next
 | |
|     end
 | |
| 
 | |
|     Post.where(id: CalendarEvent.select(:post_id).distinct).each do |post|
 | |
|       CalendarEvent.update(post)
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   on(:post_process_cooked) do |doc, post|
 | |
|     DiscourseCalendar::Calendar.update(post)
 | |
|     DiscourseCalendar::GroupTimezones.update(post)
 | |
|     CalendarEvent.update(post)
 | |
|   end
 | |
| 
 | |
|   on(:post_recovered) do |post, _, _|
 | |
|     DiscourseCalendar::Calendar.update(post)
 | |
|     DiscourseCalendar::GroupTimezones.update(post)
 | |
|     CalendarEvent.update(post)
 | |
|   end
 | |
| 
 | |
|   on(:post_destroyed) do |post, _, _|
 | |
|     DiscourseCalendar::Calendar.destroy(post)
 | |
|     CalendarEvent.where(post_id: post.id).destroy_all
 | |
|   end
 | |
| 
 | |
|   validate(:post, :validate_calendar) do |force = nil|
 | |
|     return unless self.raw_changed? || force
 | |
| 
 | |
|     validator = DiscourseCalendar::CalendarValidator.new(self)
 | |
|     validator.validate_calendar
 | |
|   end
 | |
| 
 | |
|   validate(:post, :validate_event) do |force = nil|
 | |
|     return unless self.raw_changed? || force
 | |
|     return if self.is_first_post?
 | |
| 
 | |
|     # Skip if not a calendar topic
 | |
|     if !self&.topic&.first_post&.custom_fields&.[](
 | |
|          DiscourseCalendar::CALENDAR_CUSTOM_FIELD
 | |
|        )
 | |
|       return
 | |
|     end
 | |
| 
 | |
|     validator = DiscourseCalendar::EventValidator.new(self)
 | |
|     validator.validate_event
 | |
|   end
 | |
| 
 | |
|   add_to_class(:post, :has_group_timezones?) do
 | |
|     custom_fields[DiscourseCalendar::GROUP_TIMEZONES_CUSTOM_FIELD].present?
 | |
|   end
 | |
| 
 | |
|   add_to_class(:post, :group_timezones) do
 | |
|     custom_fields[DiscourseCalendar::GROUP_TIMEZONES_CUSTOM_FIELD] || {}
 | |
|   end
 | |
| 
 | |
|   add_to_class(:post, :group_timezones=) do |val|
 | |
|     if val.present?
 | |
|       custom_fields[DiscourseCalendar::GROUP_TIMEZONES_CUSTOM_FIELD] = val
 | |
|     else
 | |
|       custom_fields.delete(DiscourseCalendar::GROUP_TIMEZONES_CUSTOM_FIELD)
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   add_to_serializer(:post, :calendar_details) do
 | |
|     grouped = {}
 | |
|     standalones = []
 | |
| 
 | |
|     CalendarEvent.where(topic_id: object.topic_id).order(:start_date, :end_date).each do |event|
 | |
|       if event.post_id
 | |
|         standalones << {
 | |
|           type: :standalone,
 | |
|           post_number: event.post_number,
 | |
|           message: event.description,
 | |
|           from: event.start_date,
 | |
|           to: event.end_date,
 | |
|           username: event.username,
 | |
|           recurring: event.recurrence,
 | |
|           post_url: Post.url("-", event.topic_id, event.post_number),
 | |
|           timezone: event.timezone
 | |
|         }
 | |
|       else
 | |
|         identifier = "#{event.region.split("_").first}-#{event.start_date.strftime("%j")}"
 | |
| 
 | |
|         grouped[identifier] ||= {
 | |
|           type: :grouped,
 | |
|           from: event.start_date,
 | |
|           name: [],
 | |
|           usernames: []
 | |
|         }
 | |
| 
 | |
|         grouped[identifier][:name] << event.description
 | |
|         grouped[identifier][:usernames] << event.username
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     grouped.each do |_, v|
 | |
|       v[:name].sort!.uniq!
 | |
|       v[:name] = v[:name].join(", ")
 | |
|       v[:usernames].sort!.uniq!
 | |
|     end
 | |
| 
 | |
|     standalones + grouped.values
 | |
|   end
 | |
| 
 | |
|   add_to_serializer(:post, :include_calendar_details?) { object.is_first_post? }
 | |
| 
 | |
|   add_to_serializer(:post, :group_timezones) do
 | |
|     result = {}
 | |
|     group_timezones = post_custom_fields[DiscourseCalendar::GROUP_TIMEZONES_CUSTOM_FIELD] || {}
 | |
|     group_names = group_timezones['groups'] || []
 | |
| 
 | |
|     if group_names.present?
 | |
|       users =
 | |
|         User.joins(:groups, :user_option).where("groups.name": group_names)
 | |
|           .select(
 | |
|           'users.*',
 | |
|           'groups.name AS group_name',
 | |
|           'user_options.timezone'
 | |
|         )
 | |
| 
 | |
|       users.each do |u|
 | |
|         result[u.group_name] ||= []
 | |
|         result[u.group_name] << UserTimezoneSerializer.new(u, root: false).as_json
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     result
 | |
|   end
 | |
| 
 | |
|   add_to_serializer(:post, :include_group_timezones?) do
 | |
|     post_custom_fields[DiscourseCalendar::GROUP_TIMEZONES_CUSTOM_FIELD].present?
 | |
|   end
 | |
| 
 | |
|   add_to_serializer(:site, :users_on_holiday) do
 | |
|     DiscourseCalendar.users_on_holiday
 | |
|   end
 | |
| 
 | |
|   add_to_serializer(:site, :include_users_on_holiday?) { scope.is_staff? }
 | |
| 
 | |
|   reloadable_patch do
 | |
|     module DiscoursePostEvent::ExportCsvControllerExtension
 | |
|       def export_entity
 | |
|         if post_event_export? && ensure_can_export_post_event
 | |
|           Jobs.enqueue(
 | |
|             :export_csv_file,
 | |
|             entity: export_params[:entity],
 | |
|             user_id: current_user.id,
 | |
|             args: export_params[:args]
 | |
|           )
 | |
|           StaffActionLogger.new(current_user).log_entity_export(
 | |
|             export_params[:entity]
 | |
|           )
 | |
|           render json: success_json
 | |
|         else
 | |
|           super
 | |
|         end
 | |
|       end
 | |
| 
 | |
|       private
 | |
| 
 | |
|       def export_params
 | |
|         if post_event_export?
 | |
|           @_export_params ||=
 | |
|             begin
 | |
|               params.require(:entity)
 | |
|               params.permit(:entity, args: %i[id]).to_h
 | |
|             end
 | |
|         else
 | |
|           super
 | |
|         end
 | |
|       end
 | |
| 
 | |
|       def post_event_export?
 | |
|         params[:entity] === 'post_event'
 | |
|       end
 | |
| 
 | |
|       def ensure_can_export_post_event
 | |
|         return if !SiteSetting.discourse_post_event_enabled
 | |
| 
 | |
|         post_event = DiscoursePostEvent::Event.find(export_params[:args][:id])
 | |
|         post_event && guardian.can_act_on_discourse_post_event?(post_event)
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     require_dependency 'export_csv_controller'
 | |
|     class ::ExportCsvController
 | |
|       prepend DiscoursePostEvent::ExportCsvControllerExtension
 | |
|     end
 | |
| 
 | |
|     module ExportPostEventCsvReportExtension
 | |
|       def post_event_export(&block)
 | |
|         return enum_for(:post_event_export) unless block_given?
 | |
| 
 | |
|         guardian = Guardian.new(current_user)
 | |
| 
 | |
|         event = DiscoursePostEvent::Event.includes(invitees: :user).find(@extra[:id])
 | |
| 
 | |
|         guardian.ensure_can_act_on_discourse_post_event!(event)
 | |
| 
 | |
|         event.invitees.order(:id).each do |invitee|
 | |
|           yield [
 | |
|             invitee.user.username,
 | |
|             DiscoursePostEvent::Invitee.statuses[invitee.status],
 | |
|             invitee.created_at,
 | |
|             invitee.updated_at
 | |
|           ]
 | |
|         end
 | |
|       end
 | |
| 
 | |
|       def get_header(entity)
 | |
|         if SiteSetting.discourse_post_event_enabled && entity === 'post_event'
 | |
|           %w[username status first_answered_at last_updated_at]
 | |
|         else
 | |
|           super
 | |
|         end
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     class Jobs::ExportCsvFile
 | |
|       prepend ExportPostEventCsvReportExtension
 | |
|     end
 | |
| 
 | |
|     on(:reduce_cooked) do |fragment, post|
 | |
|       if SiteSetting.discourse_post_event_enabled
 | |
|         fragment.css('.discourse-post-event').each do |event_node|
 | |
|           starts_at = event_node['data-start']
 | |
|           ends_at = event_node['data-end']
 | |
|           dates = "#{starts_at} (UTC)"
 | |
|           dates = "#{dates} → #{ends_at} (UTC)" if ends_at
 | |
| 
 | |
|           event_name = event_node['data-name'] || post.topic.title
 | |
|           event_node.replace <<~TXT
 | |
|             <div style='border:1px solid #dedede'>
 | |
|               <p><a href="#{
 | |
|             Discourse.base_url
 | |
|           }#{post.url}">#{event_name}</a></p>
 | |
|               <p>#{
 | |
|             dates
 | |
|           }</p>
 | |
|             </div>
 | |
|           TXT
 | |
|         end
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     on(:user_destroyed) do |user|
 | |
|       DiscoursePostEvent::Invitee.where(user_id: user.id).destroy_all
 | |
|     end
 | |
| 
 | |
|     if respond_to?(:add_post_revision_notifier_recipients)
 | |
|       add_post_revision_notifier_recipients do |post_revision|
 | |
|         # next if no modifications
 | |
|         next if !post_revision.modifications.present?
 | |
| 
 | |
|         # do no notify recipients when only updating tags
 | |
|         next if post_revision.modifications.keys == ['tags']
 | |
| 
 | |
|         ids = []
 | |
|         post = post_revision.post
 | |
| 
 | |
|         if post && post.is_first_post? && post.event
 | |
|           ids.concat(post.event.on_going_event_invitees.pluck(:user_id))
 | |
|         end
 | |
| 
 | |
|         ids
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     on(:site_setting_changed) do |name, old_val, new_val|
 | |
|       next if name != :discourse_post_event_allowed_custom_fields
 | |
| 
 | |
|       previous_fields = old_val.split('|')
 | |
|       new_fields = new_val.split('|')
 | |
|       removed_fields = previous_fields - new_fields
 | |
| 
 | |
|       next if removed_fields.empty?
 | |
| 
 | |
|       DiscoursePostEvent::Event.all.find_each do |event|
 | |
|         removed_fields.each { |field| event.custom_fields.delete(field) }
 | |
|         event.save
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     if defined?(DiscourseAutomation)
 | |
|       on(:discourse_post_event_event_started) do |event|
 | |
|         DiscourseAutomation::Automation
 | |
|           .where(enabled: true, trigger: 'event_started')
 | |
|           .each do |automation|
 | |
|           fields = automation.serialized_fields
 | |
|           topic_id = fields.dig('topic_id', 'value')
 | |
| 
 | |
|           unless event.post.topic.id.to_s == topic_id
 | |
|             next
 | |
|           end
 | |
| 
 | |
|           automation.trigger!(
 | |
|             'kind' => 'event_started',
 | |
|             'event' => event,
 | |
|             'placeholders' => {
 | |
|               'event_url' => event.url
 | |
|             }
 | |
|           )
 | |
|         end
 | |
|       end
 | |
| 
 | |
|       add_triggerable_to_scriptable('event_started', 'send_chat_message')
 | |
| 
 | |
|       add_automation_triggerable('event_started') do
 | |
|         placeholder :event_url
 | |
| 
 | |
|         field :topic_id, component: :text
 | |
|       end
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   query = Proc.new do |notifications, data|
 | |
|     notifications
 | |
|       .where("data::json ->> 'topic_title' = ?", data[:topic_title].to_s)
 | |
|       .where("data::json ->> 'message' = ?", data[:message].to_s)
 | |
|   end
 | |
| 
 | |
|   reminders_consolidation_plan = Notifications::DeletePreviousNotifications.new(
 | |
|     type: Notification.types[:event_reminder],
 | |
|     previous_query_blk: query
 | |
|   )
 | |
| 
 | |
|   invitation_consolidation_plan = Notifications::DeletePreviousNotifications.new(
 | |
|     type: Notification.types[:event_invitation],
 | |
|     previous_query_blk: query
 | |
|   )
 | |
| 
 | |
|   register_notification_consolidation_plan(reminders_consolidation_plan)
 | |
|   register_notification_consolidation_plan(invitation_consolidation_plan)
 | |
| 
 | |
|   Report.add_report('currently_away') do |report|
 | |
|     group_filter = report.filters.dig(:group) || Group::AUTO_GROUPS[:staff]
 | |
|     report.add_filter('group', type: 'group', default: group_filter)
 | |
| 
 | |
|     return unless group = Group.find_by(id: group_filter)
 | |
| 
 | |
|     report.labels = [
 | |
|       {
 | |
|         property: :username,
 | |
|         title: I18n.t('reports.currently_away.labels.username')
 | |
|       },
 | |
|     ]
 | |
| 
 | |
|     group_usernames = group.users.pluck(:username)
 | |
|     on_holiday_usernames = DiscourseCalendar.users_on_holiday
 | |
|     report.data = (group_usernames & on_holiday_usernames).map { |username| { username: username } }
 | |
|     report.total = report.data.count
 | |
|   end
 | |
| end
 |