Init version of calendar management.

This commit is contained in:
Martin Edenhofer 2015-09-09 08:52:05 +02:00
parent b170312d8e
commit 5509f61549
8 changed files with 463 additions and 13 deletions

View file

@ -0,0 +1,37 @@
class Index extends App.ControllerContent
constructor: ->
super
# check authentication
return if !@authenticate()
@subscribeId = App.Calendar.subscribe(@render)
App.Calendar.fetch()
render: =>
calendars = App.Calendar.all()
for calendar in calendars
# get preview public holidays
public_holidays_preview = {}
if calendar.public_holidays
from = new Date().setTime(new Date().getTime() - (5*24*60*60*1000))
till = new Date().setTime(new Date().getTime() + (90*24*60*60*1000))
keys = Object.keys(calendar.public_holidays).reverse()
#for day, comment of calendar.public_holidays
for day in keys
itemTime = new Date( Date.parse( "#{day}T00:00:00Z" ) )
if itemTime < till && itemTime > from
public_holidays_preview[day] = calendar.public_holidays[day]
calendar.public_holidays_preview = public_holidays_preview
@html App.view('calendar')(
calendars: calendars
)
release: =>
if @subscribeId
App.Calendar.unsubscribe(@subscribeId)
App.Config.set( 'Calendars', { prio: 2400, name: 'Calendars', parent: '#manage', target: '#manage/calendars', controller: Index, role: ['Admin'] }, 'NavBarAdmin' )

View file

@ -0,0 +1,4 @@
class App.Calendar extends App.Model
@configure 'Calendar', 'name', 'timezone', 'default', 'business_hours', 'ical_url', 'public_holidays'
@extend Spine.Model.Ajax
@url: @apiPath + '/calendars'

View file

@ -0,0 +1,59 @@
<div class="page-header">
<div class="page-header-title">
<h1><%- @T('Calendar') %> <small><%- @T('Management') %></small></h1>
</div>
<div class="page-header-meta">
<a class="btn btn--success" data-type="new"><%- @T('New Calendar') %></a>
</div>
</div>
<% for calendar in @calendars: %>
<div class="action">
<div class="action-flow">
<div class="action-block">
<h3><%= calendar.name %></h3>
<%- @T('Timezone') %>: <%= calendar.timezone %>
</div>
<div class="action-block">
<%- @T('Business Hours') %>:<br>
<table>
<tr>
<td><%- @T('Monday') %></td><td><% if _.isEmpty(calendar.business_hours['mon']): %>-<% else: %><% for from, till of calendar.business_hours['mon']: %><%= from %>:<%= till %> </td><td><% end %><% end %></td>
</tr>
<tr>
<td><%- @T('Tuesday') %></td><td><% if _.isEmpty(calendar.business_hours['tue']): %>-<% else: %><% for from, till of calendar.business_hours['tue']: %><%= from %>:<%= till %> </td><td><% end %><% end %></td>
</tr>
<tr>
<td><%- @T('Wednesday') %></td><td><% if _.isEmpty(calendar.business_hours['wed']): %>-<% else: %><% for from, till of calendar.business_hours['wed']: %><%= from %>:<%= till %> </td><td><% end %><% end %></td>
</tr>
<tr>
<td><%- @T('Thursday') %></td><td><% if _.isEmpty(calendar.business_hours['thu']): %>-<% else: %><% for from, till of calendar.business_hours['thu']: %><%= from %>:<%= till %> </td><td><% end %><% end %></td>
</tr>
<tr>
<td><%- @T('Friday') %></td><td><% if _.isEmpty(calendar.business_hours['fri']): %>-<% else: %><% for from, till of calendar.business_hours['fri']: %><%= from %>:<%= till %> </td><td><% end %><% end %></td>
</tr>
<tr>
<td><%- @T('Saturday') %></td><td><% if _.isEmpty(calendar.business_hours['sat']): %>-<% else: %><% for from, till of calendar.business_hours['sat']: %><%= from %>:<%= till %> </td><td><% end %><% end %></td>
</tr>
<tr>
<td><%- @T('Sunday') %></td><td><% if _.isEmpty(calendar.business_hours['sun']): %>-<% else: %><% for from, till of calendar.business_hours['sun']: %><%= from %>:<%= till %> </td><td><% end %><% end %></td>
</tr>
</table>
</div>
<div class="action-block">
<%- @T('Public Holidays') %>:<br>
<% for holiday, meta of calendar.public_holidays_preview: %>
<%= holiday %>: <%= meta.summary %><br>
<% end %>
</div>
</div>
<div class="action-controls">
<% if !calendar.default: %>
<div class="sla-toggle btn btn--danger btn--secondary js-toggle"><%- @T('Delete') %></div>
<div class="sla-toggle btn btn--secondary js-toggle"><%- @T('Set as Default') %></div>
<% end %>
<div class="sla-edit btn js-edit"><%- @T('Edit') %></div>
</div>
</div>
<% end %>

View file

@ -287,19 +287,7 @@ class ApplicationController < ActionController::Base
} }
# get all time zones # get all time zones
config['timezones'] = {} config['timezones'] = Calendar.timezones
TZInfo::Timezone.all.each { |t|
# ignore the following time zones
next if t.name =~ /^GMT/
next if t.name =~ /^Etc/
next if t.name =~ /^MET/
next if t.name =~ /^MST/
next if t.name =~ /^ROC/
next if t.name =~ /^ROK/
diff = t.current_period.utc_total_offset / 60 / 60
config['timezones'][ t.name ] = diff
}
# remember if we can to swich back to user # remember if we can to swich back to user
if session[:switched_from_user_id] if session[:switched_from_user_id]

View file

@ -0,0 +1,30 @@
# Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/
class CalendarsController < ApplicationController
before_action :authentication_check
def index
return if deny_if_not_role(Z_ROLENAME_ADMIN)
model_index_render(Calendar, params)
end
def show
return if deny_if_not_role(Z_ROLENAME_ADMIN)
model_show_render(Calendar, params)
end
def create
return if deny_if_not_role(Z_ROLENAME_ADMIN)
model_create_render(Calendar, params)
end
def update
return if deny_if_not_role(Z_ROLENAME_ADMIN)
model_update_render(Calendar, params)
end
def destroy
return if deny_if_not_role(Z_ROLENAME_ADMIN)
model_destory_render(Calendar, params)
end
end

197
app/models/calendar.rb Normal file
View file

@ -0,0 +1,197 @@
# Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/
class Calendar < ApplicationModel
store :business_hours
store :public_holidays
=begin
get default calendar
calendar = Calendar.default
returns calendar object
=end
def self.default
find_by(default: true)
end
=begin
returnes preset of ical feeds
feeds = Calendar.ical_feeds
returns
{
'US Holidays' => 'http://www.google.com/calendar/ical/en.usa%23holiday%40group.v.calendar.google.com/public/basic.ics',
...
}
=end
def self.ical_feeds
gfeeds = {
'Australian Holidays' => 'en.australian',
'Austrian Holidays' => 'de.austrian',
'Brazilian Holidays' => 'en.brazilian',
'Canadian Holidays' => 'en.canadian',
'China Holidays' => 'en.china',
'Switzerland Holidays' => 'de.ch',
'Christian Holidays' => 'en.christian',
'Danish Holidays' => 'da.danish',
'Dutch Holidays' => 'nl.dutch',
'Finnish Holidays' => 'en.finnish',
'French Holidays' => 'fe.french',
'German Holidays' => 'de.german',
'Greek Holidays' => 'en.greek',
'Hong Kong Holidays' => 'en.hong_kong',
'Indian Holidays' => 'en.indian',
'Indonesian Holidays' => 'en.indonesian',
'Iranian Holidays' => 'en.iranian',
'Irish Holidays' => 'en.irish',
'Islamic Holidays' => 'en.islamic',
'Italian Holidays' => 'it.italian',
'Japanese Holidays' => 'en.japanese',
'Jewish Holidays' => 'en.jewish',
'Malaysian Holidays' => 'en.malaysia',
'Mexican Holidays' => 'en.mexican',
'New Zealand Holidays' => 'en.new_zealand',
'Norwegian Holidays' => 'en.norwegian',
'Philippines Holidays' => 'en.philippines',
'Polish Holidays' => 'en.polish',
'Portuguese Holidays' => 'en.portuguese',
'Russian Holidays' => 'en.russian',
'Singapore Holidays' => 'en.singapore',
'South Africa Holidays' => 'en.sa',
'South Korean Holidays' => 'en.south_korea',
'Spain Holidays' => 'en.spain',
'Swedish Holidays' => 'en.swedish',
'Taiwan Holidays' => 'en.taiwan',
'Thai Holidays' => 'en.thai',
'UK Holidays' => 'en.uk',
'US Holidays' => 'en.usa',
'Vietnamese Holidays' => 'en.vietnamese',
}
gfeeds.each {|key, name|
gfeeds[key] = "http://www.google.com/calendar/ical/#{name}%23holiday%40group.v.calendar.google.com/public/basic.ics"
}
gfeeds
end
=begin
get list of available timezones and UTC offsets
list = Calendar.timezones
returns
{
'America/Los_Angeles' => -7
...
}
=end
def self.timezones
list = {}
TZInfo::Timezone.all_country_zone_identifiers.each { |timezone|
# ignore the following time zones
#next if t.name =~ /^GMT/
#next if t.name =~ /^Etc/
#next if t.name !~ /\//
t = TZInfo::Timezone.get(timezone)
diff = t.current_period.utc_total_offset / 60 / 60
list[ timezone ] = diff
}
list
end
=begin
syn all calendars with ical feeds
success = Calendar.sync
returns
true # or false
=end
def self.sync
Calendar.all.each(&:sync)
true
end
=begin
syn one calendars with ical feed
calendar = Calendar.find(4711)
success = calendar.sync
returns
true # or false
=end
def sync
return if !ical_url
events = Calendar.parse(ical_url)
# sync with public_holidays
if !public_holidays
self.public_holidays = {}
end
events.each {|day, summary|
if !public_holidays[day]
public_holidays[day] = {}
end
# ignore if already added or changed
next if public_holidays[day].key?('active')
# create new entry
public_holidays[day] = {
active: true,
summary: summary,
}
}
self.last_log = ''
self.last_sync = Time.zone.now
save
true
end
def self.parse(location)
if location =~ /^http/i
result = UserAgent.get(location)
cal_file = result.body
else
cal_file = File.open(location)
end
cals = Icalendar.parse(cal_file)
cal = cals.first
events = {}
cal.events.each {|event|
next if event.dtstart < Time.zone.now - 1.year
next if event.dtstart > Time.zone.now + 3.year
day = "#{event.dtstart.year}-#{format('%02d', event.dtstart.month)}-#{format('%02d', event.dtstart.day)}"
comment = event.summary || event.description
comment = Encode.conv( 'utf8', comment.to_s.force_encoding('utf-8') )
if !comment.valid_encoding?
comment = comment.encode('utf-8', 'binary', invalid: :replace, undef: :replace, replace: '?')
end
events[day] = comment
}
events.sort.to_h
end
end

11
config/routes/calendar.rb Normal file
View file

@ -0,0 +1,11 @@
Zammad::Application.routes.draw do
api_path = Rails.configuration.api_path
# calendars
match api_path + '/calendars', to: 'calendars#index', via: :get
match api_path + '/calendars/:id', to: 'calendars#show', via: :get
match api_path + '/calendars', to: 'calendars#create', via: :post
match api_path + '/calendars/:id', to: 'calendars#update', via: :put
match api_path + '/calendars/:id', to: 'calendars#destroy', via: :delete
end

View file

@ -0,0 +1,124 @@
class CreateCalendar < ActiveRecord::Migration
def up
create_table :calendars do |t|
t.string :name, limit: 250, null: true
t.string :timezone, limit: 250, null: true
t.string :business_hours, limit: 1200, null: true
t.boolean :default, null: false, default: false
t.string :ical_url, limit: 500, null: true
t.text :public_holidays, limit: 500.kilobytes + 1, null: true
t.text :last_log, limit: 500.kilobytes + 1, null: true
t.timestamp :last_sync, null: true
t.integer :updated_by_id, null: false
t.integer :created_by_id, null: false
t.timestamps
end
add_index :calendars, [:name], unique: true
Calendar.create_or_update(
name: 'US',
timezone: 'America/Los_Angeles',
business_hours: {
mon: { '09:00' => '17:00' },
tue: { '09:00' => '17:00' },
wed: { '09:00' => '17:00' },
thu: { '09:00' => '17:00' },
fri: { '09:00' => '17:00' }
},
default: true,
ical_url: 'http://www.google.com/calendar/ical/en.usa%23holiday%40group.v.calendar.google.com/public/basic.ics',
updated_by_id: 1,
created_by_id: 1,
)
Calendar.create_or_update(
name: 'Germany',
timezone: 'Europe/Berlin',
business_hours: {
mon: { '09:00' => '17:00' },
tue: { '09:00' => '17:00' },
wed: { '09:00' => '17:00' },
thu: { '09:00' => '17:00' },
fri: { '09:00' => '17:00' }
},
default: false,
ical_url: 'http://www.google.com/calendar/ical/de.german%23holiday%40group.v.calendar.google.com/public/basic.ics',
updated_by_id: 1,
created_by_id: 1,
)
=begin
Calendar.create_or_update(
name: 'French',
timezone: 'Europe/Paris',
business_hours: {
mon: { '09:00' => '17:00' },
tue: { '09:00' => '17:00' },
wed: { '09:00' => '17:00' },
thu: { '09:00' => '12:00', '13:00' => '17:00' },
fri: { '09:00' => '17:00' }
},
default: false,
ical_url: 'http://www.google.com/calendar/ical/fr.french%23holiday%40group.v.calendar.google.com/public/basic.ics',
updated_by_id: 1,
created_by_id: 1,
)
Calendar.create_or_update(
name: 'Switzerland',
timezone: 'Europe/Zurich',
business_hours: {
mon: { '09:00' => '17:00' },
tue: { '09:00' => '17:00' },
wed: { '09:00' => '17:00' },
thu: { '09:00' => '12:00', '13:00' => '17:00' },
fri: { '09:00' => '17:00' }
},
default: false,
ical_url: 'http://www.google.com/calendar/ical/de.ch%23holiday%40group.v.calendar.google.com/public/basic.ics',
updated_by_id: 1,
created_by_id: 1,
)
Calendar.create_or_update(
name: 'Austria',
timezone: 'Europe/Vienna',
business_hours: {
mon: { '09:00' => '17:00' },
tue: { '09:00' => '17:00' },
wed: { '09:00' => '17:00' },
thu: { '09:00' => '17:00' },
fri: { '09:00' => '17:00' }
},
default: false,
ical_url: 'http://www.google.com/calendar/ical/de.austrian%23holiday%40group.v.calendar.google.com/public/basic.ics',
updated_by_id: 1,
created_by_id: 1,
)
Calendar.create_or_update(
name: 'Italian',
timezone: 'Europe/Roma',
business_hours: {
mon: { '09:00' => '17:00' },
tue: { '09:00' => '17:00' },
wed: { '09:00' => '17:00' },
thu: { '09:00' => '17:00' },
fri: { '09:00' => '17:00' }
},
default: false,
ical_url: 'http://www.google.com/calendar/ical/it.italian%23holiday%40group.v.calendar.google.com/public/basic.ics',
updated_by_id: 1,
created_by_id: 1,
)
=end
Scheduler.create_or_update(
name: 'Sync calendars with ical feeds.',
method: 'Calendar.sync',
period: 1.day,
prio: 2,
active: true,
updated_by_id: 1,
created_by_id: 1,
)
end
def down
drop_table :calendars
end
end