kalindar 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.gitignore +23 -0
- data/.rspec +2 -0
- data/.travis.yml +3 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +48 -0
- data/Rakefile +7 -0
- data/config.json +12 -0
- data/config.ru +2 -0
- data/kalindar.gemspec +29 -0
- data/lib/kalindar.rb +8 -0
- data/lib/kalindar/app.rb +100 -0
- data/lib/kalindar/event.rb +24 -0
- data/lib/kalindar/event_calendar.rb +103 -0
- data/lib/kalindar/locales/de.yml +24 -0
- data/lib/kalindar/locales/en.yml +24 -0
- data/lib/kalindar/public/kalindar.css +70 -0
- data/lib/kalindar/public/kalindar.js +12 -0
- data/lib/kalindar/public/pure-min.css +11 -0
- data/lib/kalindar/public/pure.css +1664 -0
- data/lib/kalindar/public/right-calendar.js +7 -0
- data/lib/kalindar/public/right-dialog.js +7 -0
- data/lib/kalindar/public/right.js +7 -0
- data/lib/kalindar/version.rb +3 -0
- data/lib/kalindar/views/event_list.slim +23 -0
- data/lib/kalindar/views/layout.slim +12 -0
- data/lib/kalindar/views/new_event.slim +37 -0
- data/spec/kalindar_spec.rb +86 -0
- data/spec/spec_helper.rb +2 -0
- data/spec/testcal.ics +72 -0
- metadata +176 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
YjgyYTE3M2NkOTJiODk5NTcxNTkyZjFkODA4NTMyNDlkZmQ3NGFmMw==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
YzMyYzI1NTVmNWMyODUxZjljMWRmZjU0NmM5MWFlZTg3ZjM1NzVlMQ==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
MzNjNDFkNzYzM2EyOTgyODIwMTk5MTkzNDEyNzBhNDc2NWYxODQzZWQ4YTE1
|
10
|
+
ZjgxNjQ2YTk2OTRkN2M3MjZmNjFiNzI0NGMxNDhjYzM3YjY1Zjk4ZjNmYzY0
|
11
|
+
YTQwNjQxYTU3YjIzNDA5MzMzZjRhNmJkOTkxZDNmYWM4NTY2M2U=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
OTI4MjAyYzYwOWQ3ZTgzMGZkZGZjNzBjMzQzNzk2MzY4ZTM1OWFiNTNjNzA0
|
14
|
+
ZDc4Y2E4N2ZhZTc0OWMzMDcyOWUzYWYzYTMxMzkwZWM0OTJkNTZmNDBiNzlj
|
15
|
+
MTVlNGVkZmVkZDhhYTA2Zjg5NWQ5YTNiYzFmMDdkMjFlOTBhMWU=
|
data/.gitignore
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
mkmf.log
|
23
|
+
*.swp
|
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Felix Wolfsteller
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# Kalindar
|
2
|
+
|
3
|
+
Kalindar lets you view ics files via a webbrowser.
|
4
|
+
|
5
|
+
It employs the ri_cal, sinatra, i18n ruby gems, the rightjs JavaScript framework and the Pure CSS framework.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
gem 'kalindar'
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install kalindar
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
bundle exec rackup
|
24
|
+
|
25
|
+
## Naming
|
26
|
+
|
27
|
+
Kali is an Indian goddess of destruction, change and ... time .
|
28
|
+
|
29
|
+
## Caveats
|
30
|
+
|
31
|
+
Be careful. Kalindar might destroy, change or delete your ics file(s)!
|
32
|
+
|
33
|
+
Kalindar does not care about timezones!
|
34
|
+
|
35
|
+
Kalindar gets confused by re-occuring events!
|
36
|
+
|
37
|
+
### Configuration
|
38
|
+
|
39
|
+
Configuration is done in config.json . There one or many calendar (ics) files and the locale can be set.
|
40
|
+
|
41
|
+
## Contributing
|
42
|
+
|
43
|
+
0. Get in touch with me.
|
44
|
+
1. Fork it ( https://github.com/[my-github-username]/kalindar/fork )
|
45
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
46
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
47
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
48
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
data/config.json
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
{
|
2
|
+
// Configuration file for kalindar - web frontend to view ics files.
|
3
|
+
// In JSON format.
|
4
|
+
|
5
|
+
// Specify one or more ics files.
|
6
|
+
// You can skip the list (['one.ics', 'two.ics']) syntax for single ones.
|
7
|
+
// The first calender is the one to be edited.
|
8
|
+
"calendar_files": "kalindar.ics",
|
9
|
+
|
10
|
+
// Specify the locale (currently 'de' or 'en').
|
11
|
+
"locale": "de"
|
12
|
+
}
|
data/config.ru
ADDED
data/kalindar.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'kalindar/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "kalindar"
|
8
|
+
spec.version = Kalindar::VERSION
|
9
|
+
spec.authors = ["Felix Wolfsteller"]
|
10
|
+
spec.email = ["felix.wolfsteller@gmail.com"]
|
11
|
+
spec.summary = %q{Web-Interface to ics files}
|
12
|
+
spec.description = %q{Web-Interface to ics files with sinatra}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "ri_cal", "~> 0.8"
|
22
|
+
spec.add_dependency "sinatra", "~> 1.4"
|
23
|
+
spec.add_dependency "slim", "~> 2.0"
|
24
|
+
spec.add_dependency "i18n", "~> 0.6"
|
25
|
+
|
26
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
27
|
+
spec.add_development_dependency "rake"
|
28
|
+
spec.add_development_dependency "rspec"
|
29
|
+
end
|
data/lib/kalindar.rb
ADDED
data/lib/kalindar/app.rb
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
require 'slim'
|
3
|
+
require 'time'
|
4
|
+
require 'json'
|
5
|
+
require 'i18n'
|
6
|
+
require 'i18n/backend/fallbacks'
|
7
|
+
|
8
|
+
class KalindarApp < Sinatra::Base
|
9
|
+
$conf = JSON.load(File.new('config.json'))
|
10
|
+
$cal = EventCalendar.new($conf['calendar_files'])
|
11
|
+
|
12
|
+
configure do
|
13
|
+
I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
|
14
|
+
I18n.load_path = Dir[File.join(settings.root, 'locales', '*.yml')]
|
15
|
+
I18n.backend.load_translations
|
16
|
+
I18n.default_locale = $conf['locale'].to_sym
|
17
|
+
end
|
18
|
+
|
19
|
+
# Will use http-verb PUT
|
20
|
+
enable :method_override
|
21
|
+
|
22
|
+
# We like pretty html indentation
|
23
|
+
set :slim, :pretty => true
|
24
|
+
|
25
|
+
helpers do
|
26
|
+
def li_day_class day
|
27
|
+
return "sunday" if day.sunday?
|
28
|
+
return "saturday" if day.saturday?
|
29
|
+
"day"
|
30
|
+
end
|
31
|
+
def t(*args)
|
32
|
+
I18n.t(*args)
|
33
|
+
end
|
34
|
+
def l(*args)
|
35
|
+
I18n.l(*args)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
get '/' do
|
40
|
+
redirect '/events'
|
41
|
+
end
|
42
|
+
|
43
|
+
get '/events' do
|
44
|
+
@events = {}
|
45
|
+
# events from today to in 30 days
|
46
|
+
(DateTime.now .. DateTime.now + 30).each do |day|
|
47
|
+
#@events[d] = $cal.events_for(d)
|
48
|
+
@events[day] = $cal.find_events day.to_date
|
49
|
+
end
|
50
|
+
slim :event_list
|
51
|
+
end
|
52
|
+
|
53
|
+
# Create DateTime from yyyymmdd + h + m .
|
54
|
+
def start_time_from_params params
|
55
|
+
hour = params['start_time'][/\d\d/].to_i
|
56
|
+
minute = params['start_time'][/:\d\d/][1,2].to_i
|
57
|
+
start_day = Date.parse(params['start_day'])
|
58
|
+
start_time = DateTime.new(start_day.year,
|
59
|
+
start_day.month, start_day.day, hour, minute)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Adds minutes to start_time.
|
63
|
+
def end_time_from_params params, start_time
|
64
|
+
minutes = case params['duration']
|
65
|
+
when '15m' then 15
|
66
|
+
when '30m' then 30
|
67
|
+
when '60m' then 60
|
68
|
+
when '90m' then 90
|
69
|
+
when '120m' then 120
|
70
|
+
when '1d' then 24 * 60
|
71
|
+
when '2d' then 24 * 2 * 60
|
72
|
+
when '5d' then 24 * 5 * 60
|
73
|
+
when '1w' then 24 * 7 * 60
|
74
|
+
end
|
75
|
+
start_time + Rational(minutes, 1440)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Add event, save ics file.
|
79
|
+
put '/event' do
|
80
|
+
event = RiCal::Component::Event.new($cal.calendars.first)
|
81
|
+
start_time = start_time_from_params params
|
82
|
+
event.dtstart = start_time
|
83
|
+
event.dtend = end_time_from_params params, start_time
|
84
|
+
event.summary = params['summary']
|
85
|
+
event.description = params['description']
|
86
|
+
event.location = params['location']
|
87
|
+
|
88
|
+
# Motivate Calendar Delegate
|
89
|
+
$cal.calendars.first.events << event
|
90
|
+
io = File.open($cal.filename_of($cal.calendars.first), 'w')
|
91
|
+
$cal.calendars.first.export_to io
|
92
|
+
io.close
|
93
|
+
|
94
|
+
redirect back
|
95
|
+
end
|
96
|
+
|
97
|
+
get '/event/new/:day' do
|
98
|
+
slim :new_event, :locals => {'start_date' => nil}
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# Delegator with some handy shortcuts
|
2
|
+
class Event < SimpleDelegator
|
3
|
+
def start_time_f day
|
4
|
+
puts "start #{start_time} : #{start_time.class} #{start_time.to_date} #{day}"
|
5
|
+
return start_time.strftime('%H:%M') if start_time.to_date == day.to_date
|
6
|
+
return "..."
|
7
|
+
end
|
8
|
+
def finish_time_f day
|
9
|
+
return finish_time.strftime('%H:%M') if finish_time.to_date == day.to_date
|
10
|
+
return "..."
|
11
|
+
end
|
12
|
+
def time_f day
|
13
|
+
start = start_time_f day
|
14
|
+
finish = finish_time_f day
|
15
|
+
if start == finish && start == "..."
|
16
|
+
"..."
|
17
|
+
else
|
18
|
+
"#{start_time_f day} - #{finish_time_f day}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
def from_to_f
|
22
|
+
return "#{dtstart.to_datetime.strftime("%d.%m. %H:%M")} - #{dtend.to_datetime.strftime("%d.%m. %H:%M")}"
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'ri_cal'
|
2
|
+
|
3
|
+
class EventCalendar
|
4
|
+
attr_accessor :calendars
|
5
|
+
attr_accessor :filenames # decorator?
|
6
|
+
# also decoreate event for access to calendar?
|
7
|
+
|
8
|
+
def initialize filename
|
9
|
+
@calendars = []
|
10
|
+
@filenames = []
|
11
|
+
if filename.class == Array
|
12
|
+
filename.each {|file| read_file file}
|
13
|
+
else
|
14
|
+
read_file filename
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def read_file filename
|
19
|
+
@calendars << File.open(filename, 'r') do |file|
|
20
|
+
RiCal.parse file
|
21
|
+
end.flatten
|
22
|
+
# attention if more than one calendar in file!
|
23
|
+
@filenames << filename
|
24
|
+
@calendars.flatten!
|
25
|
+
end
|
26
|
+
|
27
|
+
# Catches only certain events
|
28
|
+
def singular_events_for_month year, month
|
29
|
+
@calendar.map do |calendar|
|
30
|
+
calendar.events.select { |event|
|
31
|
+
event_between? event, dtmonth_start(year, month), dtmonth_end(year, month)
|
32
|
+
}
|
33
|
+
end.flatten
|
34
|
+
end
|
35
|
+
|
36
|
+
def events_for_date date
|
37
|
+
events = @calendars.map &:events
|
38
|
+
events.select {|event| event_includes? event, date}.flatten
|
39
|
+
events.map {|event|
|
40
|
+
Event.new event
|
41
|
+
}
|
42
|
+
end
|
43
|
+
|
44
|
+
# Nother optimization potential
|
45
|
+
def events_per_day start_date, end_date
|
46
|
+
map = {}
|
47
|
+
(start_date .. end_date).each do |day|
|
48
|
+
(map[day] ||= []) << find_events(day)
|
49
|
+
end
|
50
|
+
map
|
51
|
+
end
|
52
|
+
|
53
|
+
# Best optimization potential
|
54
|
+
def events_in start_date, end_date
|
55
|
+
events = []
|
56
|
+
(start_date .. end_date).each do |day|
|
57
|
+
events << find_events(day)
|
58
|
+
end
|
59
|
+
events.flatten
|
60
|
+
end
|
61
|
+
|
62
|
+
def find_events date
|
63
|
+
#events = @calendars.map &:events
|
64
|
+
@calendars.map do |calendar|
|
65
|
+
calendar.events.select { |event|
|
66
|
+
event.dtstart.to_date == date || event.dtend.to_date == date ||!event.occurrences(:overlapping => [date, date +1]).empty?
|
67
|
+
}
|
68
|
+
end.flatten.map do |event|
|
69
|
+
Event.new event
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def filename_of calendar
|
74
|
+
@filenames[@calendars.index calendar]
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def dtmonth_start year, month
|
80
|
+
Icalendar::Values::Date.new('20101001')#"#{year}%02d%02d"%[month,01])
|
81
|
+
end
|
82
|
+
|
83
|
+
def dtmonth_end year, month
|
84
|
+
puts "last date for #{year} - #{month}"
|
85
|
+
last_day = Date.civil(year, month, -1)
|
86
|
+
Icalendar::Values::Date.new('20120102')#"#{year}#{month}#{last_day}")
|
87
|
+
end
|
88
|
+
|
89
|
+
def date_between? date, start_date, end_date
|
90
|
+
puts "d #{date} st #{start_date} e #{end_date}"
|
91
|
+
date > start_date && date < end_date
|
92
|
+
end
|
93
|
+
|
94
|
+
def event_between? event, start_date, end_date
|
95
|
+
event.dtstart.class == event.dtend.class && event.dtstart.class == Icalendar::Values::DateTime && (date_between?(event.dtstart, start_date, end_date) || date_between?(event.dtend, start_date, end_date))
|
96
|
+
end
|
97
|
+
|
98
|
+
def event_includes? event, date
|
99
|
+
event.dtstart.class == event.dtend.class && event.dtstart.class == Icalendar::Values::DateTime && (date_between?(date, event.dtstart, event.dtend))
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
|
@@ -0,0 +1,24 @@
|
|
1
|
+
de:
|
2
|
+
create_event: "Neuer Termin"
|
3
|
+
new_event: "Neuer Termin"
|
4
|
+
start_date: "Anfangsdatum"
|
5
|
+
when: "Uhrzeit"
|
6
|
+
summary: "Titel"
|
7
|
+
location: "Ort"
|
8
|
+
description: "Beschreibung"
|
9
|
+
duration: "Dauer"
|
10
|
+
event: "Termin"
|
11
|
+
15m: "15 Minuten"
|
12
|
+
30m: "30 Minuten"
|
13
|
+
60m: "60 Minuten"
|
14
|
+
90m: "90 Minuten"
|
15
|
+
120m: "2 Stunden"
|
16
|
+
whole_day: "Ganztags"
|
17
|
+
1d: "1 Tag"
|
18
|
+
2d: "2 Tage"
|
19
|
+
5d: "5 Tage"
|
20
|
+
1w: "eine Woche"
|
21
|
+
save: "speichern"
|
22
|
+
time:
|
23
|
+
formats:
|
24
|
+
short: "%d.%m.%Y"
|