Init version of calendar management.
This commit is contained in:
parent
b170312d8e
commit
5509f61549
8 changed files with 463 additions and 13 deletions
37
app/assets/javascripts/app/controllers/calendar.js.coffee
Normal file
37
app/assets/javascripts/app/controllers/calendar.js.coffee
Normal 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' )
|
4
app/assets/javascripts/app/models/calendar.js.coffee
Normal file
4
app/assets/javascripts/app/models/calendar.js.coffee
Normal 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'
|
59
app/assets/javascripts/app/views/calendar.jst.eco
Normal file
59
app/assets/javascripts/app/views/calendar.jst.eco
Normal 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 %>
|
|
@ -287,19 +287,7 @@ class ApplicationController < ActionController::Base
|
|||
}
|
||||
|
||||
# get all time zones
|
||||
config['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
|
||||
}
|
||||
config['timezones'] = Calendar.timezones
|
||||
|
||||
# remember if we can to swich back to user
|
||||
if session[:switched_from_user_id]
|
||||
|
|
30
app/controllers/calendars_controller.rb
Normal file
30
app/controllers/calendars_controller.rb
Normal 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
197
app/models/calendar.rb
Normal 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
11
config/routes/calendar.rb
Normal 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
|
124
db/migrate/20150968000001_create_calendar.rb
Normal file
124
db/migrate/20150968000001_create_calendar.rb
Normal 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
|
Loading…
Reference in a new issue