kalindar 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- YjgyYTE3M2NkOTJiODk5NTcxNTkyZjFkODA4NTMyNDlkZmQ3NGFmMw==
4
+ NDY4ODFlYzA2NjAxZjdjZGU1YzdjM2Q1ZDA0OTZkNTA1NTRmMTFlYw==
5
5
  data.tar.gz: !binary |-
6
- YzMyYzI1NTVmNWMyODUxZjljMWRmZjU0NmM5MWFlZTg3ZjM1NzVlMQ==
6
+ NzkxNTk3ODk1ZjI4NDQ2ODJiMzJiNTI3OTE5YTA5NGQ5NDZhMzUwNQ==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- MzNjNDFkNzYzM2EyOTgyODIwMTk5MTkzNDEyNzBhNDc2NWYxODQzZWQ4YTE1
10
- ZjgxNjQ2YTk2OTRkN2M3MjZmNjFiNzI0NGMxNDhjYzM3YjY1Zjk4ZjNmYzY0
11
- YTQwNjQxYTU3YjIzNDA5MzMzZjRhNmJkOTkxZDNmYWM4NTY2M2U=
9
+ ZGZjMTJkYTZjMzY2ZWMzMjlkZjU2ZGQyMmQ1NGU3NGZlZDQ1ZDI4YmQ2MGVl
10
+ ZDVlZmE4YmY2ZTgyODgwNmE0NDczODgzOGNkNDlkY2M4ZDIzMTc1MTdmNjNj
11
+ YmE5NDgyMzc1MWNhNGJmMDlmNjQ4NjM4ZDlmNWExYzE5YzJiZGY=
12
12
  data.tar.gz: !binary |-
13
- OTI4MjAyYzYwOWQ3ZTgzMGZkZGZjNzBjMzQzNzk2MzY4ZTM1OWFiNTNjNzA0
14
- ZDc4Y2E4N2ZhZTc0OWMzMDcyOWUzYWYzYTMxMzkwZWM0OTJkNTZmNDBiNzlj
15
- MTVlNGVkZmVkZDhhYTA2Zjg5NWQ5YTNiYzFmMDdkMjFlOTBhMWU=
13
+ Njk3YzdhMmFhYjNiYjNjNTc3OWU0ZjU2NDcwYTJlOGUzZTU3ODM3MTY0MWE3
14
+ MzdkOTMyY2RmYTk2OWY3NDQxNjViNjYzZjQyOWI3MGYyMGQzOTRmYWJlZDQ0
15
+ YzkyNjI1ZjExMjg4MjIyMTFjZTRlZDI5OTFiMWI0YmM2NTBiNTI=
data/Procfile ADDED
@@ -0,0 +1 @@
1
+ web: bundle exec rackup config.ru -p $PORT
data/kalindar.gemspec CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
10
10
  spec.email = ["felix.wolfsteller@gmail.com"]
11
11
  spec.summary = %q{Web-Interface to ics files}
12
12
  spec.description = %q{Web-Interface to ics files with sinatra}
13
- spec.homepage = ""
13
+ spec.homepage = "https://github.com/fwolfst/kalindar"
14
14
  spec.license = "MIT"
15
15
 
16
16
  spec.files = `git ls-files -z`.split("\x0")
data/lib/kalindar.rb CHANGED
@@ -1,7 +1,8 @@
1
1
  require "kalindar/version"
2
+ require 'kalindar/event'
3
+ require 'kalindar/calendar'
2
4
  require 'kalindar/event_calendar'
3
5
  require 'kalindar/app'
4
- require 'kalindar/event'
5
6
 
6
7
  module Kalindar
7
8
  # Your code goes here...
data/lib/kalindar/app.rb CHANGED
@@ -1,10 +1,12 @@
1
- require 'sinatra/base'
2
- require 'slim'
3
1
  require 'time'
4
2
  require 'json'
3
+ require 'securerandom'
4
+ require 'sinatra/base'
5
+ require 'slim'
5
6
  require 'i18n'
6
7
  require 'i18n/backend/fallbacks'
7
8
 
9
+ # Sinatra App for Kalindar, show ics files.
8
10
  class KalindarApp < Sinatra::Base
9
11
  $conf = JSON.load(File.new('config.json'))
10
12
  $cal = EventCalendar.new($conf['calendar_files'])
@@ -22,6 +24,9 @@ class KalindarApp < Sinatra::Base
22
24
  # We like pretty html indentation
23
25
  set :slim, :pretty => true
24
26
 
27
+ # Allow inclusion in iframe.
28
+ set :protection, :except => :frame_options
29
+
25
30
  helpers do
26
31
  def li_day_class day
27
32
  return "sunday" if day.sunday?
@@ -40,6 +45,17 @@ class KalindarApp < Sinatra::Base
40
45
  redirect '/events'
41
46
  end
42
47
 
48
+ get '/events/:year/:month' do
49
+ @events = {}
50
+ # Events from start time to 31 days later
51
+ date = Date.new(params[:year].to_i, params[:month].to_i, 1)
52
+ (date .. date + 30).each do |day|
53
+ #@events[d] = $cal.events_for(d)
54
+ @events[day] = $cal.find_events day.to_date
55
+ end
56
+ slim :event_list
57
+ end
58
+
43
59
  get '/events' do
44
60
  @events = {}
45
61
  # events from today to in 30 days
@@ -50,51 +66,75 @@ class KalindarApp < Sinatra::Base
50
66
  slim :event_list
51
67
  end
52
68
 
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)
69
+ get '/events/twoday' do
70
+ @events = {}
71
+ # events from today to in 30 days
72
+ (DateTime.now .. DateTime.now + 30).each do |day|
73
+ #@events[d] = $cal.events_for(d)
74
+ @events[day] = $cal.find_events day.to_date
75
+ end
76
+ @events = @events.values.flatten.sort_by {|e| e.start_time}
77
+ slim :twoday_list
76
78
  end
77
79
 
78
- # Add event, save ics file.
80
+ # Add new event, save ics file.
79
81
  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
82
+ errors = EventParamHelper.check_params params
83
+ if !errors.empty?
84
+ slim :new_event, :locals => {'start_date' => Date.parse(params[:start_day])}
85
+ end
86
+ begin
87
+ event = Event.create_from_params params
88
+ rescue
89
+ return 502, "Eingabefehler"
90
+ end
91
+
89
92
  $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
+ $cal.calendars.first.write_back!
93
94
 
94
- redirect back
95
+ if request.xhr?
96
+ @events = {}
97
+ # Events from today to in 30 days
98
+ (DateTime.now .. DateTime.now + 30).each do |day|
99
+ @events[day] = $cal.find_events day.to_date
100
+ end
101
+ slim :event_list, :layout => false
102
+ else
103
+ redirect '/'
104
+ end
95
105
  end
96
106
 
107
+ # Show new event template.
97
108
  get '/event/new/:day' do
98
- slim :new_event, :locals => {'start_date' => nil}
109
+ # Aim is to get a new event in every case
110
+ #@event = Event.create_from_params params
111
+ @event = Event.new(RiCal::Component::Event.new($cal.calendars.first))
112
+ @event.dtstart = Date.parse(params[:day])
113
+ slim :new_event, :locals => {'start_date' => Date.parse(params[:day])}
114
+ end
115
+
116
+ # Yet empty route.
117
+ get '/event/delete/:uuid' do
118
+ redirect back
119
+ end
120
+
121
+ # Show edit view.
122
+ get '/event/edit/:uuid' do
123
+ event = $cal.find_by_uid params[:uuid]
124
+ if event.nil?
125
+ redirect back
126
+ else
127
+ slim :edit_event, :locals => {'event' => event}
128
+ end
129
+ end
130
+
131
+ # Edit/save an event.
132
+ put '/event/edit/:uuid' do
133
+ # validate_params
134
+ puts params
135
+ event = $cal.find_by_uid(params[:uuid])
136
+ event.update params
137
+ $cal.calendars.first.write_back!
138
+ redirect '/'
99
139
  end
100
140
  end
@@ -0,0 +1,12 @@
1
+ require 'delegate'
2
+
3
+ # Delegator with some handy shortcuts
4
+ class Calendar < SimpleDelegator
5
+ attr_accessor :filename
6
+ # Write 'back' to file.
7
+ def write_back!
8
+ File.open(@filename, 'w') do |file|
9
+ export_to file
10
+ end
11
+ end
12
+ end
@@ -1,24 +1,139 @@
1
+ require 'delegate'
2
+
1
3
  # Delegator with some handy shortcuts
2
4
  class Event < SimpleDelegator
5
+ # Time it starts at day, or '...'
3
6
  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
+ #puts "start #{start_time} : #{start_time.class} #{start_time.to_date} #{day}"
8
+ if dtstart.class == Date
9
+ # whole day
10
+ ""
11
+ elsif start_time.to_date == day.to_date
12
+ start_time.strftime('%H:%M')
13
+ else
14
+ "..."
15
+ end
7
16
  end
17
+
18
+ # Time it finishes at day, or '...'
8
19
  def finish_time_f day
9
- return finish_time.strftime('%H:%M') if finish_time.to_date == day.to_date
10
- return "..."
20
+ if dtend.class == Date
21
+ # whole day
22
+ ""
23
+ elsif finish_time.to_date == day.to_date
24
+ finish_time.strftime('%H:%M')
25
+ else
26
+ return "..."
27
+ end
11
28
  end
29
+
30
+ # Time it finishes and or starts at day, or '...'
12
31
  def time_f day
13
32
  start = start_time_f day
14
33
  finish = finish_time_f day
15
- if start == finish && start == "..."
34
+ if start == finish && start == ""
35
+ # whole day
36
+ ""
37
+ elsif start == finish && start == "..."
16
38
  "..."
17
39
  else
18
40
  "#{start_time_f day} - #{finish_time_f day}"
19
41
  end
20
42
  end
43
+
44
+ # Date and time from and to
21
45
  def from_to_f
22
46
  return "#{dtstart.to_datetime.strftime("%d.%m. %H:%M")} - #{dtend.to_datetime.strftime("%d.%m. %H:%M")}"
23
47
  end
48
+
49
+ # Create DateTime from yyyymmdd + h + m .
50
+ def self.start_time_from_params params
51
+ start_day = Date.parse(params['start_day'])
52
+ if !params[:start_time]
53
+ return start_day
54
+ end
55
+
56
+ hour, minute = params[:start_time].match(/(\d\d):(\d\d)/)[1,2]
57
+ start_time = DateTime.new(start_day.year,
58
+ start_day.month, start_day.day, hour.to_i, minute.to_i)
59
+ end
60
+
61
+ def self.start_date_from params
62
+ Date.parse(params['start_day'])
63
+ end
64
+
65
+
66
+ def update params
67
+ begin
68
+ hour, minute = params['start_time'].match(/(\d\d):(\d\d)/)[1,2]
69
+ start_day = Date.parse(params['start_day'])
70
+ start_time = DateTime.new(start_day.year,
71
+ start_day.month, start_day.day, hour.to_i, minute.to_i)
72
+ self.dtstart = start_time
73
+ minutes = EventParamHelper.duration params['duration']
74
+ self.dtend = start_time + Rational(minutes, 1440)
75
+ rescue => e
76
+ STDERR.puts "event#update params: problems with (up)date #{e.message}"
77
+ end
78
+
79
+ self.summary = params['summary'] if params['summary']
80
+ self.description = params['description'] if params['description']
81
+ self.location = params['location'] if params['location']
82
+ end
83
+
84
+ # Create a new event from params as given by new_event form of kalindar.
85
+ # this should eventually go somewhere else, but its better here than in app already.
86
+ def self.create_from_params params
87
+ event = RiCal::Component::Event.new($cal.calendars.first)
88
+ event.uid = SecureRandom.uuid
89
+ if params['summary']
90
+ event.summary = params['summary']
91
+ end
92
+ if params['description']
93
+ event.description = params['description']
94
+ end
95
+ if params['location']
96
+ event.location = params['location']
97
+ end
98
+
99
+ # Access should be made failsafe.
100
+ start_time = start_time_from_params(params)
101
+ event.dtstart = start_time
102
+ minutes = EventParamHelper.duration params['duration']
103
+ event.dtend = start_time + Rational(minutes, 1440)
104
+ Event.new event
105
+ end
106
+
107
+ private
108
+ end
109
+
110
+ module EventParamHelper
111
+
112
+ # minutes for abbrevations
113
+ @@duration_param = {
114
+ '15m' => 15,
115
+ '30m' => 30,
116
+ '60m' => 60,
117
+ '90m' => 90,
118
+ '120m' => 120,
119
+ '1d' => 24 * 60,
120
+ '2d' => 24 * 2 * 60,
121
+ '5d' => 24 * 5 * 60,
122
+ '1w' => 24 * 7 * 60
123
+ }
124
+ def self.duration duration_p
125
+ # throw
126
+ @@duration_param[duration_p]
127
+ end
128
+
129
+ def self.check_params params
130
+ errors = {}
131
+ if not(params[:start_time] =~ /\d\d:\d\d/)
132
+ errors[:start_time] = ''
133
+ end
134
+ if not(duration params[:duration])
135
+ errors[:duration] = ''
136
+ end
137
+ errors
138
+ end
24
139
  end
@@ -1,26 +1,36 @@
1
1
  require 'ri_cal'
2
+ require 'kalindar/calendar'
2
3
 
4
+ # Public facing methods should return Event Decorators,
5
+ # private methods can return "raw" RiCal::Component::Event s.
3
6
  class EventCalendar
4
7
  attr_accessor :calendars
5
- attr_accessor :filenames # decorator?
6
- # also decoreate event for access to calendar?
7
8
 
9
+ # Given filename or array of filenames, initialize the calendar.
8
10
  def initialize filename
9
11
  @calendars = []
10
- @filenames = []
11
12
  if filename.class == Array
12
- filename.each {|file| read_file file}
13
+ filename.each {|file| read_file file, true}
13
14
  else
14
- read_file filename
15
+ read_file filename, true
15
16
  end
16
17
  end
17
18
 
18
- def read_file filename
19
+ # Opens calendar
20
+ # param: create if true, create empty calendar file if not existant.
21
+ def read_file filename, create
22
+ if create && !File.exist?(filename)
23
+ File.open(filename, 'w') do |file|
24
+ (RiCal::Component::Calendar.new).export_to file
25
+ end
26
+ end
19
27
  @calendars << File.open(filename, 'r') do |file|
20
28
  RiCal.parse file
21
- end.flatten
22
- # attention if more than one calendar in file!
23
- @filenames << filename
29
+ end.flatten.map do |calendar|
30
+ c = Calendar.new calendar
31
+ c.filename = filename
32
+ c
33
+ end
24
34
  @calendars.flatten!
25
35
  end
26
36
 
@@ -59,19 +69,33 @@ class EventCalendar
59
69
  events.flatten
60
70
  end
61
71
 
72
+ # Find (non-recuring) events that begin, end or cover the given day.
62
73
  def find_events date
63
74
  #events = @calendars.map &:events
64
75
  @calendars.map do |calendar|
65
76
  calendar.events.select { |event|
66
- event.dtstart.to_date == date || event.dtend.to_date == date ||!event.occurrences(:overlapping => [date, date +1]).empty?
77
+ # If end-date is a Date (vs DateTime) let it be
78
+ # All day/multiple day events
79
+ if event.dtstart.class == Date && event.dtend.class == Date
80
+ event.dtstart.to_date == date || (event.dtstart < date && event.dtend > date)
81
+ else
82
+ event.dtstart.to_date == date || event.dtend.to_date == date || (event.dtstart < date && event.dtend > date)
83
+ # occurrences need to be re-enabled
84
+ #||!event.occurrences(:overlapping => [date, date +1]).empty?
85
+ end
67
86
  }
68
87
  end.flatten.map do |event|
69
88
  Event.new event
70
89
  end
90
+ # check flat_map enumerable method
71
91
  end
72
92
 
73
- def filename_of calendar
74
- @filenames[@calendars.index calendar]
93
+ def find_by_uid uuid
94
+ # we want to pick only the first! whats the method? detect is one, find another
95
+ @calendars.map(&:events).flatten.each do |event|
96
+ return Event.new(event) if event.uid == uuid
97
+ end
98
+ nil
75
99
  end
76
100
 
77
101
  private
@@ -81,13 +105,13 @@ class EventCalendar
81
105
  end
82
106
 
83
107
  def dtmonth_end year, month
84
- puts "last date for #{year} - #{month}"
108
+ #puts "last date for #{year} - #{month}"
85
109
  last_day = Date.civil(year, month, -1)
86
110
  Icalendar::Values::Date.new('20120102')#"#{year}#{month}#{last_day}")
87
111
  end
88
112
 
89
113
  def date_between? date, start_date, end_date
90
- puts "d #{date} st #{start_date} e #{end_date}"
114
+ #puts "d #{date} st #{start_date} e #{end_date}"
91
115
  date > start_date && date < end_date
92
116
  end
93
117
 
@@ -96,7 +120,8 @@ class EventCalendar
96
120
  end
97
121
 
98
122
  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))
123
+ incl = event.dtstart.class == event.dtend.class && event.dtstart.class == Icalendar::Values::DateTime && (date_between?(date, event.dtstart, event.dtend))
124
+ incl
100
125
  end
101
126
  end
102
127
 
@@ -1,6 +1,8 @@
1
1
  de:
2
2
  create_event: "Neuer Termin"
3
3
  new_event: "Neuer Termin"
4
+ edit: "ändern"
5
+ edit_event: "Termin ändern"
4
6
  start_date: "Anfangsdatum"
5
7
  when: "Uhrzeit"
6
8
  summary: "Titel"
@@ -19,6 +21,22 @@ de:
19
21
  5d: "5 Tage"
20
22
  1w: "eine Woche"
21
23
  save: "speichern"
24
+ cancel: "abbrechen"
22
25
  time:
23
26
  formats:
24
27
  short: "%d.%m.%Y"
28
+ hm: "%H:%M"
29
+ # Seems better option for names (with e.g. %a)
30
+ # http://blog.lingohub.com/developers/2013/08/internationalization-for-ruby-i18n-gem/
31
+ date:
32
+ day:
33
+ day_names:
34
+ - Montag
35
+ - Dienstag
36
+ - Mittwoch
37
+ - Donnerstag
38
+ - Freitag
39
+ - Samstag
40
+ - Sonntag
41
+ formats:
42
+ short: "%d.%m.%Y"
@@ -1,6 +1,8 @@
1
1
  en:
2
2
  create_event: "New Event"
3
3
  new_event: "New Event"
4
+ edit: "edit"
5
+ edit_event: "edit event"
4
6
  start_date: "Start Date"
5
7
  when: "Time"
6
8
  summary: "Summary"
@@ -19,6 +21,20 @@ en:
19
21
  5d: "5 days"
20
22
  1w: "a week"
21
23
  save: "save"
24
+ cancel: "cancel"
22
25
  time:
23
26
  formats:
24
27
  short: "%Y/%m/%d"
28
+ hm: "%H:%M"
29
+ date:
30
+ day:
31
+ day_names:
32
+ - Monday
33
+ - Tuesday
34
+ - Wednesday
35
+ - Thursday
36
+ - Friday
37
+ - Saturday
38
+ - Sunday
39
+ formats:
40
+ short: "%Y/%m/%d"
@@ -1,46 +1,56 @@
1
+ #main a{
2
+ font-size: smaller;
3
+ }
4
+ .dayname {
5
+ font-size: larger;
6
+ margin: 0 0 0 0;
7
+ padding: 0 0 0 0;
8
+ border-bottom: solid 1px black;
9
+ }
1
10
  ul.day_list {
2
11
  list-style: none;
3
- border-top: solid 1px;
4
- border-left: solid 1px;
5
- border-right: solid 1px;
6
- background-color: #FEEEFE;
7
12
  padding-left: 0em;
8
13
  margin-left: 0.3em;
9
14
  width: 18em;
15
+ margin-top: 0.1em;
16
+ margin-bottom: 0.1em;
10
17
  }
11
18
 
12
19
  li.day {
13
20
  border-top: solid 1px;
21
+ border-bottom: solid 1px;
14
22
  border-left: solid 1px;
15
- background-color: #EEEEEE;
23
+ border-right: solid 1px;
24
+ background-color: #F4F4F4;
25
+ margin-bottom: 0.5em;
16
26
  }
17
27
 
18
28
  li.saturday {
19
29
  border-top: solid 1px;
20
- border-left: solid 1px;
30
+ border-bottom: solid 1px;
21
31
  background-color: #DADAEE;
32
+ margin-bottom: 1em;
22
33
  }
23
34
 
24
35
  li.sunday {
25
36
  border-top: solid 1px;
26
- border-left: solid 1px;
37
+ border-bottom: solid 1px;
27
38
  background-color: #BBBBEE;
39
+ margin-bottom: 3em;
28
40
  }
29
41
 
30
42
  ul.event_list {
31
43
  list-style: none;
32
- border-top: solid 1px;
33
- border-bottom: solid 1px;
34
44
  background-color: #FEFEFE;
35
- margin-left: 0.5em;
36
45
  padding-left: 0.5em;
37
- padding-right: 0em;
46
+ padding-right: 0.5em;
38
47
  padding-top: 0em;
39
48
  padding-bottom: 0em;
49
+ margin-top: 0.1em;
50
+ margin-bottom: 0.1em;
40
51
  }
41
52
 
42
53
  ul.event_list li {
43
- border-bottom: solid 1px;
44
54
  margin-left: 0.1em;
45
55
  clear: both;
46
56
  }
@@ -51,11 +61,15 @@ ul.event_list li {
51
61
 
52
62
  #time {
53
63
  float: left;
64
+ font-size: smaller;
65
+ /*
54
66
  width: 6.8em;
67
+ */
55
68
  }
56
69
 
57
70
  #summary {
58
71
  font-weight: bold;
72
+ text-align: center;
59
73
  }
60
74
 
61
75
  .hidden_new_event_mask {
@@ -68,3 +82,7 @@ ul.event_list li {
68
82
  margin-left: 0.5em;
69
83
  font-size: 80%;
70
84
  }
85
+
86
+ #edit_event_link {
87
+ margin-left: 1em;
88
+ }
@@ -1,7 +1,7 @@
1
1
  "a#new_event_link".onClick(function () {
2
2
  console.log(this);
3
3
  //$('hidden_new_event_mask').fade();
4
- this.parent().parent().first('.hidden_new_event_mask').fade();
4
+ this.parent().first('.hidden_new_event_mask').fade();
5
5
  // Do not follow link.
6
6
  return false;
7
7
  });
@@ -10,3 +10,28 @@
10
10
  console.log(this.parent());
11
11
  this.first("#hidden_description").fade();
12
12
  });
13
+
14
+ "#cancel".onClick(function() {
15
+ this.parent.parent().fade();
16
+ });
17
+
18
+ "#show_new_event_link".onClick(function() {
19
+ console.log(this.parent().first('#day_hidden_input').value());
20
+ day = this.parent().first('#day_hidden_input').value();
21
+ this.parent().first('.new_event_mask').load('/event/new/' + day);
22
+ this.parent().first('.new_event_mask').fade();
23
+ return false;
24
+ });
25
+
26
+ "#new_event_form".onSubmit(function(event) {
27
+ event.stop();
28
+ var me = this;
29
+ this.send({
30
+ /*onComplete: function() {alert("complete");}*/
31
+ onSuccess: function(resp) {
32
+ console.log(resp);
33
+ $('main').html(resp.responseText);
34
+ },
35
+ onFailure: function(resp) {alert(resp.responseText);},
36
+ });
37
+ });
@@ -1,3 +1,3 @@
1
1
  module Kalindar
2
- VERSION = "0.0.1"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -0,0 +1,37 @@
1
+ form.pure-form.pure-form-stacked action="/event/edit/#{event.uid}" method="post"
2
+ input type="hidden" name="_method" value="put"
3
+ input type="hidden" name="uid" value=event.uid
4
+
5
+ fieldset
6
+ legend = t 'edit_event'
7
+
8
+ label for="start_day" = t 'start_date'
9
+ input type="text" readonly=true name="start_day" value="#{l event.start_time, :format => :short}"
10
+
11
+ label for="start_time" = t 'when'
12
+ input type="text" name="start_time" placeholder="HH:MM" value="#{l event.start_time, :format => :hm}"
13
+
14
+ label for="start" = t 'summary'
15
+ input type="text" name="summary" placeholder="summary" value=event.summary
16
+
17
+ label for="location" = t 'location'
18
+ input type="text" name="location" placeholder="place" value=event.location
19
+
20
+ label for="description" = t 'description'
21
+ textarea name="description" rows=3 width=40
22
+ = event.description
23
+
24
+ label for="duration" = t 'duration'
25
+ select name="duration"
26
+ option value="15m" = t '15m'
27
+ option value="30m" = t '30m'
28
+ option value="60m" = t '60m'
29
+ option value="90m" = t '90m'
30
+ option value="120m" = t '120m'
31
+ option value="whole" = t 'whole_day'
32
+ option value="1d" = t '1d'
33
+ option value="2d" = t '2d'
34
+ option value="5d" = t '5d'
35
+ option value="1w" = t '1w'
36
+ button.pure-button type="submit" value="save" = t 'save'
37
+ button.pure-button type="submit" value="cancel" = t 'cancel'
@@ -1,10 +1,11 @@
1
1
  ul.day_list
2
2
  - @events.each do |day, evs|
3
3
  li class=li_day_class(day)
4
- center
5
- | #{l day, :format => :short}
6
- br
7
- a#new_event_link href="/event/new/#{day.strftime('%Y%m%d')}" #{t 'create_event'}
4
+ .dayname
5
+ | #{t(:"date.day.day_names")[day.wday-1]} #{l day, :format => :short}
6
+ input#day_hidden_input type="hidden" name="day" value="#{day.strftime('%Y%m%d')}"
7
+ a#show_new_event_link href="/event/new/#{day.strftime('%Y%m%d')}" #{t 'create_event'}
8
+ .new_event_mask style="display: none;"
8
9
  .hidden_new_event_mask
9
10
  == slim :new_event, :layout => false, :locals => {'start_date' => day.strftime("%Y%m%d")}
10
11
  ul.event_list
@@ -13,11 +14,16 @@ ul.day_list
13
14
  #event
14
15
  #time
15
16
  | #{ev.time_f day}
17
+ - if ev.location && !ev.location.empty?
18
+ | [#{ev.location}]
19
+ br
16
20
  #summary
17
- | #{ev.summary}
18
- - if ev.location
19
- | [#{ev.location}]
21
+ | #{ev.summary}
22
+ a#edit_event_link href="/event/edit/#{ev.uid}" = t 'edit'
20
23
  #hidden_description
21
24
  | (#{ev.from_to_f})
22
25
  br
23
26
  | #{ev.description}
27
+ hr(width="20%")
28
+ #nav
29
+
@@ -9,4 +9,5 @@ html
9
9
  link type="text/css" rel="stylesheet" href="/kalindar.css"
10
10
  link type="text/css" rel="stylesheet" href="/pure.css"
11
11
  body
12
- ==yield
12
+ #main
13
+ ==yield
@@ -1,4 +1,4 @@
1
- form.pure-form.pure-form-stacked action="/event" method="post"
1
+ form.pure-form.pure-form-stacked#new_event_form action="/event" method="post"
2
2
  input type="hidden" name="_method" value="put"
3
3
  fieldset
4
4
  legend = t 'new_event'
@@ -8,7 +8,7 @@ form.pure-form.pure-form-stacked action="/event" method="post"
8
8
  input type="text" readonly=true name="start_day" value="#{start_date}"
9
9
  - else
10
10
  label for="start_day" = t 'start_date'
11
- input type="text" name="start_day" value="" placeholder="YYYYMMDD")
11
+ input type="text" name="start_day" value=event.start_date placeholder="YYYYMMDD")
12
12
 
13
13
  label for="start_time" = t 'when'
14
14
  input type="text" name="start_time" value="" placeholder="HH:MM"
@@ -35,3 +35,4 @@ form.pure-form.pure-form-stacked action="/event" method="post"
35
35
  option value="5d" = t '5d'
36
36
  option value="1w" = t '1w'
37
37
  button.pure-button type="submit" value="save" = t 'save'
38
+ button.pure-button#cancel type="submit" value="save" = t 'cancel'
@@ -7,80 +7,144 @@ describe Kalindar do
7
7
  end
8
8
 
9
9
  describe EventCalendar do
10
- it 'parses an ics file' do
11
- cal = EventCalendar.new 'spec/testcal.ics'
12
- end
10
+ subject { EventCalendar.new 'spec/testcal.ics' }
13
11
 
14
- it 'initializes alternatively with a list of ics files' do
15
- cal = EventCalendar.new ['spec/testcal.ics', 'spec/testcal2.ics']
16
- expect(cal.calendars.length).to eql 2
12
+ describe "#new" do
13
+ it 'parses an ics file' do
14
+ cal = EventCalendar.new 'spec/testcal.ics'
15
+ end
16
+ it 'initializes alternatively with a list of ics files' do
17
+ cal = EventCalendar.new ['spec/testcal.ics', 'spec/testcal2.ics']
18
+ expect(cal.calendars.length).to eql 2
19
+ end
17
20
  end
18
21
 
19
22
  it 'exposes filename of parsed calendar' do
20
23
  cal = EventCalendar.new ['spec/testcal.ics', 'spec/testcal2.ics']
21
- expect(cal.filename_of cal.calendars.first).to eql 'spec/testcal.ics'
24
+ expect(cal.calendars.first.filename).to eql 'spec/testcal.ics'
22
25
  end
23
26
 
24
- it 'finds events given date' do
25
- cal = EventCalendar.new 'spec/testcal.ics'
26
- events = cal.find_events (Date.new(2014, 07, 27))
27
- event_names = events.map(&:summary)
28
- expect(event_names.include? "onehour").to eq true
29
- expect(event_names.include? "allday").to eq true
27
+ describe "#find_events" do
28
+ it 'finds events given date' do
29
+ events = subject.find_events (Date.new(2014, 07, 27))
30
+ event_names = events.map(&:summary)
31
+ expect(event_names.include? "onehour").to eq true
32
+ expect(event_names.include? "allday").to eq true
33
+ end
34
+ it 'handles whole day endtime correctly (ends next day)' do
35
+ events = subject.find_events (Date.new(2014, 07, 28))
36
+ event_names = events.map(&:summary)
37
+ expect(event_names.include? "allday").to eq false
38
+ end
39
+ it 'finds multiday events that cover the given date' do
40
+ events = subject.find_events (Date.new(2014, 07, 27))
41
+ expect(events.map(&:summary).include? "multidays").to eq true
42
+ end
43
+ it 'wraps events as Event delegates' do
44
+ events = subject.find_events (Date.new(2014, 07, 27))
45
+ events.each do |event|
46
+ expect(event.is_a? Event).to eq true
47
+ end
48
+ end
30
49
  end
31
50
 
32
51
  it 'finds events that reocur' do
33
- cal = EventCalendar.new 'spec/testcal.ics'
34
- events = cal.find_events (Date.new(2014, 07, 27))
52
+ events = subject.find_events (Date.new(2014, 07, 27))
35
53
  event_names = events.map(&:summary)
36
54
  expect(event_names.include? "daily").to eq true
37
55
  end
38
56
 
39
- it '#events_in' do
40
- cal = EventCalendar.new 'spec/testcal.ics'
41
- events = cal.events_in(Date.new(2014, 07, 27), Date.new(2014, 07, 28))
42
- event_names = events.map(&:summary)
43
- expect(event_names).to eq ["allday", "onehour", "daily", "allday", "daily"]
57
+ describe "#events_in" do
58
+ it 'accesses events between two dates' do
59
+ events = subject.events_in(Date.new(2014, 07, 27), Date.new(2014, 07, 28))
60
+ event_names = events.map(&:summary)
61
+ expect(event_names).to eq ["allday", "onehour", "daily", "daily"]
62
+ end
63
+ end
64
+
65
+ describe "#events_in" do
66
+ it '#events_in by day' do
67
+ events = subject.events_in(Date.new(2014, 7, 27), Date.new(2014, 7, 28))
68
+ event_names = events.map(&:summary)
69
+ expect(event_names).to eq ["allday", "onehour", "daily", "allday", "daily"]
70
+ expect(event_names.class).to eq({}.class)
71
+ end
72
+ it 'wraps in Event Delegate' do
73
+ events = subject.events_in(Date.new(2014, 7, 27), Date.new(2014, 7, 28))
74
+ expect(events.collect{|e| e.is_a? Event}.length).to eq events.length
75
+ end
76
+ end
77
+
78
+ describe "#find_by_uid" do
79
+ it 'finds by uuid' do
80
+ event = subject.find_by_uid 'cb523dc2-eab8-49c9-a99f-ed69ac3b65d0'
81
+ expect(event.summary).to eq 'allday'
82
+ end
83
+ it 'wraps in Event Delegate' do
84
+ event = subject.find_by_uid 'cb523dc2-eab8-49c9-a99f-ed69ac3b65d0'
85
+ expect(event.is_a? Event).to eq true
86
+ end
44
87
  end
88
+ end
45
89
 
46
- it '#events_in by day' do
90
+ describe "Event" do
91
+ subject(:allday_event) {}
92
+ subject(:events) {
47
93
  cal = EventCalendar.new 'spec/testcal.ics'
48
- events = cal.events_in(Date.new(2014, 7, 27), Date.new(2014, 7, 28))
49
- event_names = events.map(&:summary)
50
- expect(event_names).to eq ["allday", "onehour", "daily", "allday", "daily"]
51
- expect(event_names.class).to eq({}.class)
94
+ cal.events_in(Date.new(2014, 8, 27), Date.new(2014, 8, 28))
95
+ }
96
+ subject(:allday_event) {
97
+ cal = EventCalendar.new 'spec/testcal.ics'
98
+ cal.find_by_uid("cb523dc2-eab8-49c9-a99f-ed69ac3b65d0")
99
+ }
100
+ subject(:multiday_event) {
101
+ cal = EventCalendar.new 'spec/testcal.ics'
102
+ cal.find_by_uid("4a129461-cd74-4b3a-a307-faa1e8846cc2")
103
+ }
104
+
105
+ describe "#start_time_f" do
106
+ it "returns the time if given day is start day" do
107
+ expect(events[0].start_time_f Date.new(2014, 8, 27)).to eq "12:00"
108
+ expect(events[0].start_time_f Date.new(2014, 8, 28)).to eq "..."
109
+ end
52
110
  end
53
111
 
54
- describe "Event" do
55
- describe "#start_time_f" do
56
- it "returns the time if given day is start day" do
57
- cal = EventCalendar.new 'spec/testcal.ics'
58
- events = cal.events_in(Date.new(2014, 8, 27), Date.new(2014, 8, 28))
59
- expect(Event.new(events[0]).start_time_f Date.new(2014, 8, 27)).to eq "12:00"
60
- expect(Event.new(events[0]).start_time_f Date.new(2014, 8, 28)).to eq ""
61
- end
112
+ describe "#finish_time_f" do
113
+ it "returns the time if given day is end day" do
114
+ expect(events[0].finish_time_f Date.new(2014, 8, 27)).to eq "..."
115
+ expect(events[0].finish_time_f Date.new(2014, 8, 28)).to eq "13:00"
62
116
  end
63
- describe "#finish_time_f" do
64
- it "returns the time if given day is end day" do
65
- cal = EventCalendar.new 'spec/testcal.ics'
66
- events = cal.events_in(Date.new(2014, 8, 27), Date.new(2014, 8, 28))
67
- expect(Event.new(events[0]).finish_time_f Date.new(2014, 8, 27)).to eq ""
68
- expect(Event.new(events[0]).finish_time_f Date.new(2014, 8, 28)).to eq "13:00"
69
- end
117
+ end
118
+
119
+ describe "#time_f" do
120
+ it "returns the from to time for given day" do
121
+ expect(allday_event.time_f Date.new(2014, 7, 27)).to eq ""
70
122
  end
71
- describe "#time_f" do
72
- it "returns the from to time for given day" do
73
- cal = EventCalendar.new 'spec/testcal.ics'
74
- events = cal.events_in(Date.new(2014, 8, 27), Date.new(2014, 8, 28))
75
- expect(Event.new(events[0]).time_f Date.new(2014, 8, 27)).to eq "12:00 - "
76
- end
123
+ it "renders multiday in between with ... " do
124
+ expect(multiday_event.time_f Date.new(2014, 7, 27)).to eq "..."
77
125
  end
78
- describe "#time_f" do
79
- it "returns the from to time" do
80
- cal = EventCalendar.new 'spec/testcal.ics'
81
- events = cal.events_in(Date.new(2014, 8, 27), Date.new(2014, 8, 28))
82
- expect(Event.new(events[0]).from_to_f).to eq "27.08. 12:00 - 28.08. 13:00"
83
- end
126
+ end
127
+ describe "#from_to_f" do
128
+ it "returns the from to time" do
129
+ expect(events[0].from_to_f).to eq "27.08. 12:00 - 28.08. 13:00"
130
+ end
131
+ end
132
+
133
+ describe "#update" do
134
+ it "updates from params" do
135
+ # should be fail safe, too
136
+ cal = EventCalendar.new 'spec/testcal.ics'
137
+ event = cal.find_by_uid("aea8d217-8025-4d9b-88e6-3df9e6abd33c")
138
+
139
+ params = {'location' => 'a place', 'description' => 'exact description', 'summary'=> 'synopsis', 'duration' => '15m', 'start_day' => '20140808', 'start_time' => '10:10'}
140
+ event.update params
141
+
142
+ event_fetched = cal.find_by_uid("aea8d217-8025-4d9b-88e6-3df9e6abd33c")
143
+ expect(event_fetched.location).to eql 'a place'
144
+ expect(event_fetched.description).to eql 'exact description'
145
+ expect(event_fetched.summary).to eql 'synopsis'
146
+ expect(event_fetched.dtstart).to eq DateTime.new(2014, 8, 8, 10, 10)
147
+ expect(event_fetched.dtend).to eq DateTime.new(2014, 8, 8, 10, 25)
84
148
  end
85
149
  end
86
150
  end
data/spec/testcal.ics CHANGED
@@ -51,6 +51,17 @@ UID:aea8d217-8025-4d9b-88e6-3df9e6abd33c
51
51
  SUMMARY:2days
52
52
  DTSTART;TZID=Europe/Berlin:20140827T120000
53
53
  DTEND;TZID=Europe/Berlin:20140828T130000
54
+ CLASS:PUBLIC
55
+ TRANSP:OPAQUE
56
+ END:VEVENT
57
+ BEGIN:VEVENT
58
+ CREATED:20140814T060630Z
59
+ LAST-MODIFIED:20140814T060654Z
60
+ DTSTAMP:20140814T060654Z
61
+ UID:4a129461-cd74-4b3a-a307-faa1e8846cc2
62
+ SUMMARY:multidays
63
+ DTSTART;TZID=Europe/Berlin:20140726T090000
64
+ DTEND;TZID=Europe/Berlin:20140729T100000
54
65
  TRANSP:OPAQUE
55
66
  CLASS:PUBLIC
56
67
  END:VEVENT
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kalindar
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Felix Wolfsteller
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-07-30 00:00:00.000000000 Z
11
+ date: 2014-08-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ri_cal
@@ -119,7 +119,9 @@ files:
119
119
  - .rspec
120
120
  - .travis.yml
121
121
  - Gemfile
122
+ - Gemfile.lock
122
123
  - LICENSE.txt
124
+ - Procfile
123
125
  - README.md
124
126
  - Rakefile
125
127
  - config.json
@@ -127,6 +129,7 @@ files:
127
129
  - kalindar.gemspec
128
130
  - lib/kalindar.rb
129
131
  - lib/kalindar/app.rb
132
+ - lib/kalindar/calendar.rb
130
133
  - lib/kalindar/event.rb
131
134
  - lib/kalindar/event_calendar.rb
132
135
  - lib/kalindar/locales/de.yml
@@ -140,13 +143,14 @@ files:
140
143
  - lib/kalindar/public/right.js
141
144
  - lib/kalindar/version.rb
142
145
  - lib/kalindar/views/.layout.haml.swp
146
+ - lib/kalindar/views/edit_event.slim
143
147
  - lib/kalindar/views/event_list.slim
144
148
  - lib/kalindar/views/layout.slim
145
149
  - lib/kalindar/views/new_event.slim
146
150
  - spec/kalindar_spec.rb
147
151
  - spec/spec_helper.rb
148
152
  - spec/testcal.ics
149
- homepage: ''
153
+ homepage: https://github.com/fwolfst/kalindar
150
154
  licenses:
151
155
  - MIT
152
156
  metadata: {}