calendar.rb 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. # Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/
  2. class Calendar < ApplicationModel
  3. store :business_hours
  4. store :public_holidays
  5. before_create :validate_public_holidays, :fetch_ical
  6. before_update :validate_public_holidays, :fetch_ical
  7. after_create :sync_default, :min_one_check
  8. after_update :sync_default, :min_one_check
  9. after_destroy :min_one_check
  10. notify_clients_support
  11. =begin
  12. set inital default calendar
  13. calendar = Calendar.init_setup
  14. returns calendar object
  15. =end
  16. def self.init_setup(ip = nil)
  17. # ignore client ip if not public ip
  18. if ip && ip =~ /^(::1|127\.|10\.|172\.1[6-9]\.|172\.2[0-9]\.|172\.3[0-1]\.|192\.168\.)/
  19. ip = nil
  20. end
  21. # prevent multible setups for same ip
  22. cache = Cache.get('Calendar.init_setup.done')
  23. return if cache && cache[:ip] == ip
  24. Cache.write('Calendar.init_setup.done', { ip: ip }, { expires_in: 1.hour })
  25. # call for calendar suggestion
  26. calendar_details = Service::GeoCalendar.location(ip)
  27. return if !calendar_details
  28. calendar_details['name'] = Calendar.genrate_uniq_name(calendar_details['name'])
  29. calendar_details['default'] = true
  30. calendar_details['created_by_id'] = 1
  31. calendar_details['updated_by_id'] = 1
  32. # find if auto generated calendar exists
  33. calendar = Calendar.find_by(default: true, updated_by_id: 1, created_by_id: 1)
  34. if calendar
  35. calendar.update_attributes(calendar_details)
  36. return calendar
  37. end
  38. create(calendar_details)
  39. end
  40. =begin
  41. get default calendar
  42. calendar = Calendar.default
  43. returns calendar object
  44. =end
  45. def self.default
  46. find_by(default: true)
  47. end
  48. =begin
  49. returnes preset of ical feeds
  50. feeds = Calendar.ical_feeds
  51. returns
  52. {
  53. 'US' => 'http://www.google.com/calendar/ical/en.usa%23holiday%40group.v.calendar.google.com/public/basic.ics',
  54. ...
  55. }
  56. =end
  57. def self.ical_feeds
  58. gfeeds = {
  59. 'Australia' => 'en.australian',
  60. 'Austria' => 'de.austrian',
  61. 'Argentina' => 'en.ar',
  62. 'Bahamas' => 'en.bs',
  63. 'Belarus' => 'en.by',
  64. 'Brazil' => 'en.brazilian',
  65. 'Bulgaria' => 'en.bulgarian',
  66. 'Canada' => 'en.canadian',
  67. 'China' => 'en.china',
  68. 'Chile' => 'en.cl',
  69. 'Costa Rica' => 'en.cr',
  70. 'Colombia' => 'en.co',
  71. 'Croatia' => 'en.croatian',
  72. 'Cuba' => 'en.cu',
  73. 'Cyprus' => 'de.cy',
  74. 'Switzerland' => 'de.ch',
  75. 'Denmark' => 'da.danish',
  76. 'Netherlands' => 'nl.dutch',
  77. 'Egypt' => 'en.eg',
  78. 'Ethiopia' => 'en.et',
  79. 'Ecuador' => 'en.ec',
  80. 'Estonia' => 'en.ee',
  81. 'Finland' => 'en.finnish',
  82. 'France' => 'en.french',
  83. 'Germany' => 'de.german',
  84. 'Greece' => 'en.greek',
  85. 'Ghana' => 'en.gh',
  86. 'Hong Kong' => 'en.hong_kong',
  87. 'Haiti' => 'en.ht',
  88. 'Hungary' => 'en.hungarian',
  89. 'India' => 'en.indian',
  90. 'Indonesia' => 'en.indonesian',
  91. 'Iran' => 'en.ir',
  92. 'Ireland' => 'en.irish',
  93. 'Italy' => 'it.italian',
  94. 'Israel' => 'en.jewish',
  95. 'Japan' => 'en.japanese',
  96. 'Kuwait' => 'en.kw',
  97. 'Latvia' => 'en.latvian',
  98. 'Liechtenstein' => 'en.li',
  99. 'Lithuania' => 'en.lithuanian',
  100. 'Luxembourg' => 'en.lu',
  101. 'Malaysia' => 'en.malaysia',
  102. 'Mexico' => 'en.mexican',
  103. 'Morocco' => 'en.ma',
  104. 'Mauritius' => 'en.mu',
  105. 'Moldova' => 'en.md',
  106. 'New Zealand' => 'en.new_zealand',
  107. 'Norway' => 'en.norwegian',
  108. 'Philippines' => 'en.philippines',
  109. 'Poland' => 'en.polish',
  110. 'Portugal' => 'en.portuguese',
  111. 'Pakistan' => 'en.pk',
  112. 'Russia' => 'en.russian',
  113. 'Senegal' => 'en.sn',
  114. 'Singapore' => 'en.singapore',
  115. 'South Africa' => 'en.sa',
  116. 'South Korean' => 'en.south_korea',
  117. 'Spain' => 'en.spain',
  118. 'Slovakia' => 'en.slovak',
  119. 'Serbia' => 'en.rs',
  120. 'Slovenia' => 'en.slovenian',
  121. 'Sweden' => 'en.swedish',
  122. 'Taiwan' => 'en.taiwan',
  123. 'Thai' => 'en.th',
  124. 'Turkey' => 'en.turkish',
  125. 'UK' => 'en.uk',
  126. 'US' => 'en.usa',
  127. 'Ukraine' => 'en.ukrainian',
  128. 'Uruguay' => 'en.uy',
  129. 'Vietnam' => 'en.vietnamese',
  130. 'Venezuela' => 'en.ve',
  131. }
  132. all_feeds = {}
  133. gfeeds.each {|key, name|
  134. all_feeds["http://www.google.com/calendar/ical/#{name}%23holiday%40group.v.calendar.google.com/public/basic.ics"] = key
  135. }
  136. all_feeds
  137. end
  138. =begin
  139. get list of available timezones and UTC offsets
  140. list = Calendar.timezones
  141. returns
  142. {
  143. 'America/Los_Angeles' => -7
  144. ...
  145. }
  146. =end
  147. def self.timezones
  148. list = {}
  149. TZInfo::Timezone.all_country_zone_identifiers.each { |timezone|
  150. t = TZInfo::Timezone.get(timezone)
  151. diff = t.current_period.utc_total_offset / 60 / 60
  152. list[ timezone ] = diff
  153. }
  154. list
  155. end
  156. =begin
  157. syn all calendars with ical feeds
  158. success = Calendar.sync
  159. returns
  160. true # or false
  161. =end
  162. def self.sync
  163. Calendar.all.each(&:sync)
  164. true
  165. end
  166. =begin
  167. syn one calendars with ical feed
  168. calendar = Calendar.find(4711)
  169. success = calendar.sync
  170. returns
  171. true # or false
  172. =end
  173. def sync(without_save = nil)
  174. return if !ical_url
  175. begin
  176. events = {}
  177. if ical_url && !ical_url.empty?
  178. events = Calendar.parse(ical_url)
  179. end
  180. # sync with public_holidays
  181. if !public_holidays
  182. self.public_holidays = {}
  183. end
  184. # remove old ical entries if feed has changed
  185. public_holidays.each {|day, meta|
  186. next if !public_holidays[day]['feed']
  187. next if meta['feed'] == Digest::MD5.hexdigest(ical_url)
  188. public_holidays.delete(day)
  189. }
  190. # sync new ical feed dates
  191. events.each {|day, summary|
  192. if !public_holidays[day]
  193. public_holidays[day] = {}
  194. end
  195. # ignore if already added or changed
  196. next if public_holidays[day].key?('active')
  197. # create new entry
  198. public_holidays[day] = {
  199. active: true,
  200. summary: summary,
  201. feed: Digest::MD5.hexdigest(ical_url)
  202. }
  203. }
  204. self.last_log = nil
  205. rescue => e
  206. self.last_log = e.inspect
  207. end
  208. self.last_sync = Time.zone.now
  209. if !without_save
  210. save
  211. end
  212. true
  213. end
  214. def self.parse(location)
  215. if location =~ /^http/i
  216. result = UserAgent.get(location)
  217. if !result.success?
  218. raise result.error
  219. end
  220. cal_file = result.body
  221. else
  222. cal_file = File.open(location)
  223. end
  224. cals = Icalendar.parse(cal_file)
  225. cal = cals.first
  226. events = {}
  227. cal.events.each {|event|
  228. next if event.dtstart < Time.zone.now - 1.year
  229. next if event.dtstart > Time.zone.now + 3.years
  230. day = "#{event.dtstart.year}-#{format('%02d', event.dtstart.month)}-#{format('%02d', event.dtstart.day)}"
  231. comment = event.summary || event.description
  232. comment = Encode.conv( 'utf8', comment.to_s.force_encoding('utf-8') )
  233. if !comment.valid_encoding?
  234. comment = comment.encode('utf-8', 'binary', invalid: :replace, undef: :replace, replace: '?')
  235. end
  236. # ignore daylight saving time entries
  237. next if comment =~ /(daylight saving|sommerzeit|summertime)/i
  238. events[day] = comment
  239. }
  240. events.sort.to_h
  241. end
  242. private
  243. # if changed calendar is default, set all others default to false
  244. def sync_default
  245. return if !default
  246. Calendar.all.each {|calendar|
  247. next if calendar.id == id
  248. next if !calendar.default
  249. calendar.default = false
  250. calendar.save
  251. }
  252. end
  253. # check if min one is set to default true
  254. def min_one_check
  255. Calendar.all.each {|calendar|
  256. return true if calendar.default
  257. }
  258. first = Calendar.order(:created_at, :id).limit(1).first
  259. first.default = true
  260. first.save
  261. # check if sla's are refer to an existing calendar
  262. Sla.all.each {|sla|
  263. if !sla.calendar_id
  264. sla.calendar_id = first.id
  265. sla.save
  266. next
  267. end
  268. if !Calendar.find_by(id: sla.calendar_id)
  269. sla.calendar_id = first.id
  270. sla.save
  271. end
  272. }
  273. end
  274. # fetch ical feed
  275. def fetch_ical
  276. sync(true)
  277. end
  278. # validate format of public holidays
  279. def validate_public_holidays
  280. # fillup feed info
  281. public_holidays.each {|day, meta|
  282. if public_holidays_was && public_holidays_was[day] && public_holidays_was[day]['feed']
  283. meta['feed'] = public_holidays_was[day]['feed']
  284. end
  285. meta['active'] = if meta['active']
  286. true
  287. else
  288. false
  289. end
  290. }
  291. end
  292. end