diff --git a/app/controllers/i_cal_controller.rb b/app/controllers/i_cal_controller.rb new file mode 100644 index 000000000..e8d1587ec --- /dev/null +++ b/app/controllers/i_cal_controller.rb @@ -0,0 +1,57 @@ +# Copyright (C) 2012-2015 Zammad Foundation, http://zammad-foundation.org/ + +require 'icalendar' + +class ICalController < ApplicationController + before_action { authentication_check( { basic_auth_promt: true, token_action: 'iCal' } ) } + + # @path [GET] /ical + # + # @summary Returns an iCal file with all objects matching the iCal preferences of the current user as events. + # + # @response_message 200 [String] iCal file ready to import in calendar applications. + # @response_message 401 Permission denied. + def all + ical_object = ICal.new( current_user ) + ical = ical_object.all + + send_data( + ical, + filename: 'zammad.ical', + type: 'text/plain', + disposition: 'inline' + ) + rescue => e + logger.error e.message + logger.error e.backtrace.inspect + render json: { error: e.message }, status: :unprocessable_entity + end + + # @path [GET] /ical/:object + # @path [GET] /ical/:object/:method + # + # @summary Returns an iCal file of the given object (and method) matching the iCal preferences of the current user as events. + # + # @response_message 200 [String] iCal file ready to import in calendar applications. + # @response_message 401 Permission denied. + def object + ical_object = ICal.new( current_user ) + + # remove the last char (s/plural) from the object name + object_name = params[:object].to_s[0...-1].to_sym + + ical = ical_object.generic( object_name, params[:method] ) + + send_data( + ical, + filename: 'zammad.ical', + type: 'text/plain', + disposition: 'inline' + ) + rescue => e + logger.error e.message + logger.error e.backtrace.inspect + render json: { error: e.message }, status: :unprocessable_entity + end + +end diff --git a/config/routes/i_cal.rb b/config/routes/i_cal.rb new file mode 100644 index 000000000..db95baa58 --- /dev/null +++ b/config/routes/i_cal.rb @@ -0,0 +1,7 @@ +Zammad::Application.routes.draw do + api_path = Rails.configuration.api_path + + match api_path + '/ical', to: 'i_cal#all', via: :get + match api_path + '/ical/:object', to: 'i_cal#object', via: :get + match api_path + '/ical/:object/:method', to: 'i_cal#object', via: :get +end diff --git a/lib/i_cal.rb b/lib/i_cal.rb index 6ded66b0c..3e260f9e2 100644 --- a/lib/i_cal.rb +++ b/lib/i_cal.rb @@ -1,24 +1,48 @@ # Copyright (C) 2012-2013 Zammad Foundation, http://zammad-foundation.org/ -module ICal +class ICal - def self.preferenced(user) + def initialize(user) + @user = user + @preferences = {} + + if @user.preferences[:ical] && !@user.preferences[:ical].empty? + @preferences = @user.preferences[:ical] + end + end + + def all events_data = [] - - user.preferences[:ical].each { |sub_class, _sub_structure| - - sub_class_name = sub_class.to_s.capitalize - class_name = "ICal::#{sub_class_name}" - - object = Kernel.const_get( class_name ) - events_data += object.preferenced( user ) + @preferences.each { |object_name, _sub_structure| + result = generic_call( object_name ) + events_data = events_data + result } - to_ical( events_data ) end - def self.to_ical(events_data) + def generic(object_name, method_name = 'all') + + events_data = generic_call( object_name, method_name ) + to_ical( events_data ) + end + + def generic_call(object_name, method_name = 'all') + + method_name ||= 'all' + + events_data = [] + if @preferences[ object_name ] && !@preferences[ object_name ].empty? + sub_class_name = object_name.to_s.capitalize + object = Object.const_get('ICal').const_get("ICal#{sub_class_name}") + instance = object.new( @user, @preferences[ object_name ] ) + method = instance.method( method_name ) + events_data += method.call + end + events_data + end + + def to_ical(events_data) cal = Icalendar::Calendar.new diff --git a/lib/i_cal/i_cal_ticket.rb b/lib/i_cal/i_cal_ticket.rb new file mode 100644 index 000000000..a902c8faf --- /dev/null +++ b/lib/i_cal/i_cal_ticket.rb @@ -0,0 +1,168 @@ +# Copyright (C) 2012-2013 Zammad Foundation, http://zammad-foundation.org/ + +class ICal + + class ICalTicket + + def initialize(user, preferences) + @user = user + @preferences = preferences + end + + def all + + events_data = [] + return events_data if @preferences.empty? + + events_data += new_open + events_data += pending + events_data += escalation + + events_data + end + + def owner_ids(method) + + owner_ids = [] + + return owner_ids if @preferences.empty? + return owner_ids if !@preferences[ method ] + return owner_ids if @preferences[ method ].empty? + + preferences = @preferences[ method ] + + if preferences[:own] + owner_ids = [ @user.id ] + end + if preferences[:not_assigned] + owner_ids.push( 1 ) + end + + owner_ids + end + + def new_open + + events_data = [] + owner_ids = owner_ids(:new_open) + return events_data if owner_ids.empty? + + condition = { + 'tickets.owner_id' => owner_ids, + 'tickets.state_id' => Ticket::State.where( + state_type_id: Ticket::StateType.where( + name: %w(new open), + ), + ), + } + + tickets = Ticket.search( + current_user: @user, + condition: condition, + ) + + events_data = [] + tickets.each do |ticket| + + event_data = {} + + event_data[:dtstart] = Icalendar::Values::Date.new( Time.zone.today ) + event_data[:dtend] = Icalendar::Values::Date.new( Time.zone.today ) + event_data[:summary] = "#{ticket.state.name} ticket: '#{ticket.title}'" + event_data[:description] = "T##{ticket.number}" + + events_data.push event_data + end + + events_data + end + + def pending + + events_data = [] + owner_ids = owner_ids(:pending) + return events_data if owner_ids.empty? + + condition = { + 'tickets.owner_id' => owner_ids, + 'tickets.state_id' => Ticket::State.where( + state_type_id: Ticket::StateType.where( + name: [ + 'pending reminder', + 'pending action', + ], + ), + ), + } + + tickets = Ticket.search( + current_user: @user, + condition: condition, + ) + + events_data = [] + tickets.each do |ticket| + + next if !ticket.pending_time + + event_data = {} + + pending_time = ticket.pending_time + if pending_time < Time.zone.today + pending_time = Time.zone.today + end + + # rubocop:disable Rails/TimeZone + event_data[:dtstart] = Icalendar::Values::DateTime.new( pending_time ) + event_data[:dtend] = Icalendar::Values::DateTime.new( pending_time ) + # rubocop:enable Rails/TimeZone + event_data[:summary] = "#{ticket.state.name} ticket: '#{ticket.title}'" + event_data[:description] = "T##{ticket.number}" + + events_data.push event_data + end + + events_data + end + + def escalation + + events_data = [] + owner_ids = owner_ids(:escalation) + return events_data if owner_ids.empty? + + condition = [ + 'tickets.owner_id IN (?) AND tickets.escalation_time IS NOT NULL', owner_ids + ] + + tickets = Ticket.search( + current_user: @user, + condition: condition, + ) + + tickets.each do |ticket| + + next if !ticket.escalation_time + + event_data = {} + + escalation_time = ticket.escalation_time + if escalation_time < Time.zone.today + escalation_time = Time.zone.today + end + + # rubocop:disable Rails/TimeZone + event_data[:dtstart] = Icalendar::Values::DateTime.new( escalation_time ) + event_data[:dtend] = Icalendar::Values::DateTime.new( escalation_time ) + # rubocop:enable Rails/TimeZone + event_data[:summary] = "ticket escalation: '#{ticket.title}'" + event_data[:description] = "T##{ticket.number}" + + events_data.push event_data + end + + events_data + end + end + +end diff --git a/lib/i_cal/ticket.rb b/lib/i_cal/ticket.rb deleted file mode 100644 index 6366d76b7..000000000 --- a/lib/i_cal/ticket.rb +++ /dev/null @@ -1,161 +0,0 @@ -# Copyright (C) 2012-2013 Zammad Foundation, http://zammad-foundation.org/ - -module ICal::Ticket - - def self.preferenced(user) - - events_data = [] - return events_data if !user.preferences[:ical] - return events_data if !user.preferences[:ical][:ticket] - - preferences = user.preferences[:ical][:ticket] - - [:new_open, :pending, :escalation].each { |state_type| - - next if !preferences[ state_type ] - - owner_ids = [] - if preferences[ state_type ][:own] - owner_ids = [ user.id ] - end - if preferences[ state_type ][:not_assigned] - owner_ids.push( 1 ) - end - - next if owner_ids.empty? - - if state_type == :new_open - events_data += new_open(user, owner_ids) - elsif state_type == :pending - events_data += pending(user, owner_ids) - elsif state_type == :escalation - events_data += escalation(user, owner_ids) - end - } - - events_data - end - - private - - def self.new_open(user, owner_ids) - - events_data = [] - return events_data if owner_ids.empty? - - condition = { - 'tickets.owner_id' => owner_ids, - 'tickets.state_id' => Ticket::State.where( - state_type_id: Ticket::StateType.where( - name: %w(new open), - ), - ), - } - - tickets = Ticket.search( - current_user: user, - condition: condition, - ) - - events_data = [] - tickets.each do |ticket| - - event_data = {} - - event_data[:dtstart] = Icalendar::Values::Date.new( Time.zone.today ) - event_data[:dtend] = Icalendar::Values::Date.new( Time.zone.today ) - event_data[:summary] = "#{ticket.state.name} ticket: '#{ticket.title}'" - event_data[:description] = "T##{ticket.number}" - - events_data.push event_data - end - - events_data - end - - def self.pending(user, owner_ids) - - events_data = [] - return events_data if owner_ids.empty? - - condition = { - 'tickets.owner_id' => owner_ids, - 'tickets.state_id' => Ticket::State.where( - state_type_id: Ticket::StateType.where( - name: [ - 'pending reminder', - 'pending action', - ], - ), - ), - } - - tickets = Ticket.search( - current_user: user, - condition: condition, - ) - - events_data = [] - tickets.each do |ticket| - - next if !ticket.pending_time - - event_data = {} - - pending_time = ticket.pending_time - if pending_time < Time.zone.today - pending_time = Time.zone.today - end - - # rubocop:disable Rails/TimeZone - event_data[:dtstart] = Icalendar::Values::DateTime.new( pending_time ) - event_data[:dtend] = Icalendar::Values::DateTime.new( pending_time ) - # rubocop:enable Rails/TimeZone - event_data[:summary] = "#{ticket.state.name} ticket: '#{ticket.title}'" - event_data[:description] = "T##{ticket.number}" - - events_data.push event_data - end - - events_data - end - - def self.escalation(user, owner_ids) - - events_data = [] - return events_data if owner_ids.empty? - - condition = [ - 'tickets.owner_id IN (?) AND tickets.escalation_time IS NOT NULL', owner_ids - ] - - tickets = Ticket.search( - current_user: user, - condition: condition, - ) - - tickets.each do |ticket| - - next if !ticket.escalation_time - - event_data = {} - - escalation_time = ticket.escalation_time - if escalation_time < Time.zone.today - escalation_time = Time.zone.today - end - - # rubocop:disable Rails/TimeZone - event_data[:dtstart] = Icalendar::Values::DateTime.new( escalation_time ) - event_data[:dtend] = Icalendar::Values::DateTime.new( escalation_time ) - # rubocop:enable Rails/TimeZone - event_data[:summary] = "ticket escalation: '#{ticket.title}'" - event_data[:description] = "T##{ticket.number}" - - events_data.push event_data - end - - events_data - end - -end diff --git a/test/integration/i_cal_ticket_test.rb b/test/integration/i_cal_ticket_test.rb index d5ef75906..7c737762b 100644 --- a/test/integration/i_cal_ticket_test.rb +++ b/test/integration/i_cal_ticket_test.rb @@ -244,11 +244,13 @@ class ICalTicketTest < ActiveSupport::TestCase user.preferences[:ical] = {} user.preferences[:ical][:ticket] = test_data[:preferences] - event_data = ICal::Ticket.new_open( user, test_data[:owner_ids] ) + ical_ticket = ICal::ICalTicket.new( user, test_data[:preferences] ) + event_data = ical_ticket.new_open assert_equal( test_data[:count], event_data.length, "#{test_data[:name]} event count" ) - ical = ICal.preferenced( user ) + ical_object = ICal.new( user ) + ical = ical_object.all event_data.each { |event| @@ -316,11 +318,13 @@ class ICalTicketTest < ActiveSupport::TestCase user.preferences[:ical] = {} user.preferences[:ical][:ticket] = test_data[:preferences] - event_data = ICal::Ticket.pending( user, test_data[:owner_ids] ) + ical_ticket = ICal::ICalTicket.new( user, test_data[:preferences] ) + event_data = ical_ticket.pending assert_equal( test_data[:count], event_data.length, "#{test_data[:name]} event count" ) - ical = ICal.preferenced( user ) + ical_object = ICal.new( user ) + ical = ical_object.all event_data.each { |event| @@ -388,11 +392,13 @@ class ICalTicketTest < ActiveSupport::TestCase user.preferences[:ical] = {} user.preferences[:ical][:ticket] = test_data[:preferences] - event_data = ICal::Ticket.escalation( user, test_data[:owner_ids] ) + ical_ticket = ICal::ICalTicket.new( user, test_data[:preferences] ) + event_data = ical_ticket.escalation assert_equal( test_data[:count], event_data.length, "#{test_data[:name]} event count" ) - ical = ICal.preferenced( user ) + ical_object = ICal.new( user ) + ical = ical_object.all event_data.each { |event|