caboose-cms 0.4.109 → 0.4.110

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
- MGJlYTVjNGY4Y2I2NjgwMDA0MDAwOTZhMTJlM2RlNGJlYzEyZjJiNw==
4
+ YTBjODNmMjE5YzM2OTc1YzdlNzQwNGUzYmFmN2RkYzdjNTk1MTRhMA==
5
5
  data.tar.gz: !binary |-
6
- OTgwYmYzMGYwY2MyMDJmNWFkNzI0MTgzNjNlZTA5MjFlYjM4MGJjYg==
6
+ NDc1ZGI2MmNlZTA1NTkxYWExYTEyYzEzMmE1M2Y5NjZiMDk2ZDBhNA==
7
7
  !binary "U0hBNTEy":
8
8
  metadata.gz: !binary |-
9
- NzUyMGIyNmU0M2U0MjkxMTY4MTU5ZDRiYzk1ZmM1N2YzZGMyZDUxYjE3ZWYy
10
- ZGU4NmU2OTAxZmFlMWQxNjVlZmViODgwMmI0MThiOWY1MGRkYTU3ZjUwNWRi
11
- YjFhZGU2ZDUwZTcwMzVlZGNkZTU4YTliNGMzNDljOTgyMDBlOTA=
9
+ NWM4YzA4ZmE0ZmI4NzczMWI1NWEzY2NlODVlMjQ5ZmYxMjA4MWEwMzE3MDE2
10
+ YTU5ODgyNzk3NTc5YjU2NTdjNzNhZDkyOGVmODBmZmZjYWNiYzM1N2Q4MGVj
11
+ NGM5ZWUyMzRhNjM3NTFiMTg2YmFiYzI3MTk2YmY2MDk4ZmMwMjA=
12
12
  data.tar.gz: !binary |-
13
- Yzg2Nzk0NTUyNDU1NzY1MjUzNmM2MzE4MDMzZjBkNmQwMjI1NGU3NmRjNTM5
14
- MjJkZDMwOGRjYTc2YWZhM2ViZGRmY2Y5NjdkYmQwNzMzZjljMmMwMTMxYzY3
15
- Y2U2YjQzOTZjYzY2MjEzNzFhYjM4YTUwYjZhOTM0Y2M1YjEzZGM=
13
+ NDMzMzQ4OTk1ZTViYzYxZDQ4ZmVhNGNlZmFkZmViYjE3NjczZWVhOWZlNzEx
14
+ OTM3ZjAxNDc2ZWFlYzAyY2MyZWRiMzRhMjI0OTY2ZmZhYWI0MTljZDk5MTAy
15
+ NWM2ZGNlNTVjYTljNmQ1ZjBkNjhkODAyOTExYWJhZTU1Zjc4NzA=
@@ -103,7 +103,7 @@ div.mb_container form.mb_file_form div.mb_placeholder span {
103
103
  }
104
104
 
105
105
  .mb_container input[type='checkbox'] {
106
- /*position: absolute;*/
106
+ position: absolute;
107
107
  top: 4px;
108
108
  left: 0;
109
109
  z-index: 19;
@@ -0,0 +1,77 @@
1
+
2
+ module Caboose
3
+ class CalendarsController < ApplicationController
4
+
5
+ helper :application
6
+
7
+ def before_action
8
+ @page = Page.page_with_uri(request.host_with_port, '/admin')
9
+ end
10
+
11
+ # GET /admin/calendars
12
+ def admin_index
13
+ return if !user_is_allowed('calendars', 'view')
14
+ render :file => 'caboose/extras/error_invalid_site' and return if @site.nil?
15
+
16
+ @calendars = Calendar.reorder(:name).all
17
+ render :layout => 'caboose/admin'
18
+ end
19
+
20
+ # GET /admin/calendars/:id
21
+ def admin_edit
22
+ return unless user_is_allowed('calendars', 'edit')
23
+ @calendar = Calendar.find(params[:id])
24
+
25
+ @d = params[:d] ? DateTime.iso8601(params[:d]) : DateTime.now
26
+ @d = @d - (@d.strftime('%-d').to_i-1)
27
+
28
+ render :layout => 'caboose/admin'
29
+ end
30
+
31
+ # PUT /admin/calendars/:id
32
+ def admin_update
33
+ return unless user_is_allowed('calendars', 'edit')
34
+
35
+ resp = StdClass.new({'attributes' => {}})
36
+ calendar = Calendar.find(params[:id])
37
+
38
+ save = true
39
+ params.each do |name, value|
40
+ case name
41
+ when 'name' then calendar.name = value
42
+ when 'description' then calendar.description = value
43
+ end
44
+ end
45
+
46
+ resp.success = save && calendar.save
47
+ render :json => resp
48
+ end
49
+
50
+ # POST /admin/calendars
51
+ def admin_add
52
+ return unless user_is_allowed('calendars', 'edit')
53
+
54
+ resp = StdClass.new
55
+ calendar = Calendar.new
56
+ calendar.name = params[:name]
57
+ calendar.site_id = @site.id
58
+
59
+ if calendar.name.nil? || calendar.name.strip.length == 0
60
+ resp.error = "Please enter a calendar name."
61
+ else
62
+ calendar.save
63
+ resp.redirect = "/admin/calendars/#{calendar.id}"
64
+ end
65
+ render :json => resp
66
+ end
67
+
68
+ # DELETE /admin/calendars/:id
69
+ def admin_delete
70
+ return unless user_is_allowed('calendars', 'delete')
71
+ Calendar.find(params[:id]).destroy
72
+ resp = StdClass.new({ 'redirect' => "/admin/calendars" })
73
+ render :json => resp
74
+ end
75
+
76
+ end
77
+ end
@@ -0,0 +1,95 @@
1
+
2
+ module Caboose
3
+ class EventGroupsController < ApplicationController
4
+
5
+ helper :application
6
+
7
+ # GET /admin/calendars/:calendar_id/events/:id
8
+ def admin_edit
9
+ return unless user_is_allowed('calendars', 'edit')
10
+ @event = CalendarEvent.find(params[:id])
11
+ render :layout => 'caboose/modal'
12
+ end
13
+
14
+ # GET /admin/calendars/:calendar_id/events/new
15
+ def admin_new
16
+ return unless user_is_allowed('calendars', 'edit')
17
+ @calendar = Calendar.find(params[:calendar_id])
18
+ @begin_date = DateTime.iso8601(params[:begin_date])
19
+ render :layout => 'caboose/modal'
20
+ end
21
+
22
+ # PUT /admin/calendars/:calendar_id/events/:id
23
+ def admin_update
24
+ return unless user_is_allowed('calendars', 'edit')
25
+
26
+ resp = StdClass.new
27
+ event = CalendarEvent.find(params[:id])
28
+
29
+ save = true
30
+ params.each do |name, value|
31
+ case name
32
+ when 'name' then event.name = value
33
+ when 'description' then event.description = value
34
+ end
35
+ end
36
+
37
+ resp.success = save && event.save
38
+ render :json => resp
39
+ end
40
+
41
+ # POST /admin/calendars/:calendar_id/events
42
+ def admin_add
43
+ return unless user_is_allowed('calendars', 'edit')
44
+
45
+ resp = StdClass.new
46
+ event = CalendarEvent.new
47
+ event.calendar_id = params[:calendar_id]
48
+ event.name = params[:name]
49
+ event.begin_date = DateTime.iso8601("#{params[:begin_date]}T10:00:00-05:00")
50
+ event.end_date = DateTime.iso8601("#{params[:begin_date]}T10:00:00-05:00")
51
+ event.all_day = true
52
+
53
+ if event.name.nil? || event.name.strip.length == 0
54
+ resp.error = "Please enter an event name."
55
+ else
56
+ event.save
57
+ resp.redirect = "/admin/calendars/#{event.calendar_id}/events/#{event.id}"
58
+ end
59
+ render :json => resp
60
+ end
61
+
62
+ # DELETE /admin/calendars/:id
63
+ def admin_delete
64
+ return unless user_is_allowed('calendars', 'delete')
65
+ Calendar.find(params[:id]).destroy
66
+ resp = StdClass.new({ 'redirect' => "/admin/calendars" })
67
+ render :json => resp
68
+ end
69
+
70
+ # GET /admin/event-groups/period-options
71
+ def admin_period_options
72
+ render :json => [
73
+ { 'value' => CalendarEventGroup::PERIOD_DAY , 'text' => CalendarEventGroup::PERIOD_DAY },
74
+ { 'value' => CalendarEventGroup::PERIOD_WEEK , 'text' => CalendarEventGroup::PERIOD_WEEK },
75
+ { 'value' => CalendarEventGroup::PERIOD_MONTH , 'text' => CalendarEventGroup::PERIOD_MONTH },
76
+ { 'value' => CalendarEventGroup::PERIOD_YEAR , 'text' => CalendarEventGroup::PERIOD_YEAR },
77
+ ]
78
+ end
79
+
80
+ # GET /admin/event-groups/frequency-options
81
+ def admin_frequency_options
82
+ arr = (1..30).collect{ |i| { 'value' => i, 'text' => i }}
83
+ render :json => arr
84
+ end
85
+
86
+ # GET /admin/event-groups/repeat-by-options
87
+ def admin_repeat_by_options
88
+ render :json => [
89
+ { 'value' => CalendarEventGroup::REPEAT_BY_DAY_OF_MONTH , 'text' => 'same day of the month' },
90
+ { 'value' => CalendarEventGroup::REPEAT_BY_DAY_OF_WEEK , 'text' => 'same day of the week' },
91
+ ]
92
+ end
93
+
94
+ end
95
+ end
@@ -0,0 +1,99 @@
1
+
2
+ module Caboose
3
+ class EventsController < ApplicationController
4
+
5
+ helper :application
6
+
7
+ # GET /admin/calendars/:calendar_id/events/:id
8
+ def admin_edit
9
+ return unless user_is_allowed('calendars', 'edit')
10
+ @event = CalendarEvent.find(params[:id])
11
+ if @event.calendar_event_group_id.nil?
12
+ g = CalendarEventGroup.create
13
+ @event.calendar_event_group_id = g.id
14
+ @event.save
15
+ end
16
+ render :layout => 'caboose/modal'
17
+ end
18
+
19
+ # GET /admin/calendars/:calendar_id/events/new
20
+ def admin_new
21
+ return unless user_is_allowed('calendars', 'edit')
22
+ @calendar = Calendar.find(params[:calendar_id])
23
+ @begin_date = DateTime.iso8601(params[:begin_date])
24
+ render :layout => 'caboose/modal'
25
+ end
26
+
27
+ # POST /admin/calendars/:calendar_id/events
28
+ def admin_add
29
+ return unless user_is_allowed('calendars', 'edit')
30
+
31
+ resp = StdClass.new
32
+ event = CalendarEvent.new
33
+ event.calendar_id = params[:calendar_id]
34
+ event.name = params[:name]
35
+ event.begin_date = DateTime.iso8601("#{params[:begin_date]}T10:00:00-05:00")
36
+ event.end_date = DateTime.iso8601("#{params[:begin_date]}T10:00:00-05:00")
37
+ event.all_day = true
38
+
39
+ if event.name.nil? || event.name.strip.length == 0
40
+ resp.error = "Please enter an event name."
41
+ else
42
+ event.save
43
+ resp.redirect = "/admin/calendars/#{event.calendar_id}/events/#{event.id}"
44
+ end
45
+ render :json => resp
46
+ end
47
+
48
+ # PUT /admin/calendars/:calendar_id/events/:id
49
+ def admin_update
50
+ return unless user_is_allowed('calendars', 'edit')
51
+
52
+ resp = StdClass.new({ 'attributes' => {} })
53
+ event = CalendarEvent.find(params[:id])
54
+
55
+ save = true
56
+ params.each do |name, value|
57
+ case name
58
+ when 'name' then event.name = value
59
+ when 'location' then event.location = value
60
+ when 'description' then event.description = value
61
+ when 'all_day' then event.all_day = value
62
+ when 'begin_date'
63
+ t = event.begin_date ? event.begin_date.strftime('%H:%M %z') : '10:00 -0500'
64
+ event.begin_date = DateTime.strptime("#{value} #{t}", '%m/%d/%Y %H:%M %z')
65
+ when 'begin_time'
66
+ d = event.begin_date ? event.begin_date.strftime('%Y-%m-%d') : DateTime.now.strftime('%Y-%m-%d')
67
+ event.begin_date = DateTime.strptime("#{d} #{value}", '%Y-%m-%d %H:%M %z')
68
+ when 'end_date'
69
+ t = event.end_date ? event.end_date.strftime('%H:%M %z') : '10:00 -0500'
70
+ event.end_date = DateTime.strptime("#{value} #{t}", '%m/%d/%Y %H:%M %z')
71
+ when 'end_time'
72
+ d = event.end_date ? event.end_date.strftime('%Y-%m-%d') : DateTime.now.strftime('%Y-%m-%d')
73
+ event.end_date = DateTime.strptime("#{d} #{value}", '%Y-%m-%d %H:%M %z')
74
+ when 'repeats'
75
+ g = event.calendar_event_group
76
+ if value == true || value.to_i == 1
77
+ g.date_start = event.begin_date.to_date if g.date_start.nil?
78
+ g.date_end = event.end_date.to_date if g.date_end.nil?
79
+ g.save
80
+ end
81
+ event.repeats = value
82
+
83
+ end
84
+ end
85
+
86
+ resp.success = save && event.save
87
+ render :json => resp
88
+ end
89
+
90
+ # DELETE /admin/calendars/:calendar_id/events/:id
91
+ def admin_delete
92
+ return unless user_is_allowed('calendars', 'delete')
93
+ CalendarEvent.find(params[:id]).destroy
94
+ resp = StdClass.new({ 'redirect' => "/admin/calendars" })
95
+ render :json => resp
96
+ end
97
+
98
+ end
99
+ end
@@ -0,0 +1,7 @@
1
+ module Caboose
2
+ class Calendar < ActiveRecord::Base
3
+ self.table_name = "calendars"
4
+ has_many :calendar_events, :dependent => :destroy
5
+ attr_accessible :id, :site_id, :name, :description
6
+ end
7
+ end
@@ -0,0 +1,26 @@
1
+ module Caboose
2
+ class CalendarEvent < ActiveRecord::Base
3
+ self.table_name = "calendar_events"
4
+
5
+ belongs_to :calendar
6
+ belongs_to :calendar_event_group
7
+ attr_accessible :id ,
8
+ :calendar_id ,
9
+ :calendar_event_group_id ,
10
+ :name ,
11
+ :description ,
12
+ :location ,
13
+ :begin_date ,
14
+ :end_date ,
15
+ :all_day
16
+
17
+ def self.events_for_day(calendar_id, d)
18
+ q = ["calendar_id = ?
19
+ and concat(date_part('year', begin_date),date_part('month', begin_date),date_part('day', begin_date)) <= ?
20
+ and concat(date_part('year', end_date ),date_part('month', end_date ),date_part('day', end_date )) >= ?",
21
+ calendar_id, d.strftime('%Y%m%d'), d.strftime('%Y%m%d')]
22
+ self.where(q).reorder(:begin_date).all
23
+ end
24
+
25
+ end
26
+ end
@@ -0,0 +1,30 @@
1
+ module Caboose
2
+ class CalendarEventGroup < ActiveRecord::Base
3
+ self.table_name = "calendar_event_groups"
4
+
5
+ has_many :calendar_events
6
+ attr_accessible :id ,
7
+ :period , # Daily, weekly, monthly, or yearly
8
+ :frequency ,
9
+ :repeat_by , # Used for monthly repeats
10
+ :sun ,
11
+ :mon ,
12
+ :tue ,
13
+ :wed ,
14
+ :thu ,
15
+ :fri ,
16
+ :sat ,
17
+ :date_start ,
18
+ :repeat_count , # How many times the repeat occurs
19
+ :date_end
20
+
21
+ PERIOD_DAY = 'Day'
22
+ PERIOD_WEEK = 'Week'
23
+ PERIOD_MONTH = 'Month'
24
+ PERIOD_YEAR = 'Year'
25
+
26
+ REPEAT_BY_DAY_OF_MONTH = 'Day of month'
27
+ REPEAT_BY_DAY_OF_WEEK = 'Day of week'
28
+
29
+ end
30
+ end
@@ -30,7 +30,19 @@ class Caboose::Schema < Caboose::Utilities::Schema
30
30
  Caboose::AbOption => [:text],
31
31
  Caboose::User => [:timezone],
32
32
  #Caboose::Field => [:child_block_id],
33
- Caboose::BlockType => [:layout_function]
33
+ Caboose::BlockType => [:layout_function],
34
+ Caboose::CalendarEventGroup => [
35
+ :repeat_period ,
36
+ :repeat_sun ,
37
+ :repeat_mon ,
38
+ :repeat_tue ,
39
+ :repeat_wed ,
40
+ :repeat_thu ,
41
+ :repeat_fri ,
42
+ :repeat_sat ,
43
+ :repeat_start ,
44
+ :repeat_end
45
+ ]
34
46
  }
35
47
  end
36
48
 
@@ -119,6 +131,37 @@ class Caboose::Schema < Caboose::Utilities::Schema
119
131
  [ :name , :string ],
120
132
  [ :description , :string ]
121
133
  ],
134
+ Caboose::Calendar => [
135
+ [ :site_id , :integer ],
136
+ [ :name , :string ],
137
+ [ :description , :text ]
138
+ ],
139
+ Caboose::CalendarEvent => [
140
+ [ :calendar_id , :integer ],
141
+ [ :calendar_event_group_id , :integer ],
142
+ [ :name , :string ],
143
+ [ :description , :text ],
144
+ [ :location , :string ],
145
+ [ :begin_date , :datetime ],
146
+ [ :end_date , :datetime ],
147
+ [ :all_day , :boolean , { :default => false }],
148
+ [ :repeats , :boolean , { :default => false }]
149
+ ],
150
+ Caboose::CalendarEventGroup => [
151
+ [ :frequency , :integer , { :default => 1 }],
152
+ [ :period , :string , { :default => 'Week' }],
153
+ [ :repeat_by , :string ],
154
+ [ :sun , :boolean , { :default => false }],
155
+ [ :mon , :boolean , { :default => false }],
156
+ [ :tue , :boolean , { :default => false }],
157
+ [ :wed , :boolean , { :default => false }],
158
+ [ :thu , :boolean , { :default => false }],
159
+ [ :fri , :boolean , { :default => false }],
160
+ [ :sat , :boolean , { :default => false }],
161
+ [ :date_start , :date ],
162
+ [ :repeat_count , :integer ],
163
+ [ :date_end , :date ]
164
+ ],
122
165
  Caboose::DatabaseSession => [
123
166
  [ :session_id , :string , :null => false ],
124
167
  [ :data , :text ],
@@ -0,0 +1,123 @@
1
+
2
+ <h1>Edit Calendar</h1>
3
+ <p><div id='calendar_<%= @calendar.id %>_name' ></div></p>
4
+ <p><div id='calendar_<%= @calendar.id %>_description' ></div></p>
5
+
6
+ <div id='calendar'>
7
+ <h2><%= @d.strftime('%B %Y') %></h2>
8
+ <p>
9
+ <input type='button' value='< Previous Month' onclick="window.location='/admin/calendars/<%= @calendar.id %>?d=<%= (@d - 1.month).strftime('%Y-%m-%d') %>';" />
10
+ <input type='button' value='Current Month' onclick="window.location='/admin/calendars/<%= @calendar.id %>?d=<%= DateTime.now.strftime('%Y-%m-%d') %>';" />
11
+ <input type='button' value='Next Month >' onclick="window.location='/admin/calendars/<%= @calendar.id %>?d=<%= (@d + 1.month).strftime('%Y-%m-%d') %>';" />
12
+ </p>
13
+ <p><a href='/admin/calendars/<%= @calendar.id %>/events/new' id='new_event'>New Event</a></p>
14
+ <table>
15
+ <tr>
16
+ <th>Sun</th>
17
+ <th>Mon</th>
18
+ <th>Tue</th>
19
+ <th>Wed</th>
20
+ <th>Thu</th>
21
+ <th>Fri</th>
22
+ <th>Sat</th>
23
+ </tr>
24
+ <%
25
+ days_in_previous_month = (@d - (@d - 1.month)).to_f.ceil.to_i
26
+ days_in_month = ((@d + 1.month) - @d).to_f.ceil.to_i
27
+ start_day = @d.strftime('%w').to_i
28
+ %>
29
+ <tr>
30
+ <% (0...start_day).each do |i| %><td class='blank'><span class='day'><%= days_in_previous_month - (start_day - i) %></span></td><% end %>
31
+ <% day = 1 %>
32
+ <% while day <= days_in_month %>
33
+ <% d = @d + (day-1).days %>
34
+ <% if (day + start_day-1) % 7 == 0 %></tr><% if day < days_in_month %><tr><% end %><% end %>
35
+ <td id='day_<%= d.strftime('%Y-%m-%d') %>'>
36
+ <span class='day'><%= day %></span>
37
+ <% events = Caboose::CalendarEvent.events_for_day(@calendar.id, d) %>
38
+ <% if events.count > 0 %>
39
+ <ul>
40
+ <% events.each do |ev| %><li><%= ev.name %></li><% end %>
41
+ </ul>
42
+ <% end %>
43
+ </td>
44
+ <% day = day + 1 %>
45
+ <% end %>
46
+ <% last_day = (start_day + days_in_month) % 7 %>
47
+ <% remaining_days = 7 - last_day %>
48
+ <% if last_day > 0 %><% (0...remaining_days).each do |i| %><td class='blank'><span class='day'><%= (i + 1) %></span></td><% end %><% end %>
49
+ <% if (start_day + days_in_month) != 0 %></tr><% end %>
50
+ </table><br />
51
+ </div>
52
+
53
+ <div id='message'></div>
54
+ <div id='controls'>
55
+ <input type='button' value='Back' onclick="window.location='/admin/calendars';" />
56
+ <input type='button' value='Delete Calendar' onclick="delete_calendar(<%= @calendar.id %>);" />
57
+ </div>
58
+
59
+ <% content_for :caboose_js do %>
60
+ <%= javascript_include_tag "caboose/model/all" %>
61
+ <script type="text/javascript">
62
+
63
+ $(document).ready(function() {
64
+ new ModelBinder({
65
+ name: 'Calendar',
66
+ id: <%= @calendar.id %>,
67
+ update_url: '/admin/calendars/<%= @calendar.id %>',
68
+ authenticity_token: '<%= form_authenticity_token %>',
69
+ attributes: [
70
+ { name: 'name' , nice_name: 'Name' , type: 'text' , value: <%= raw Caboose.json(@calendar.name ) %>, width: 400 },
71
+ { name: 'description' , nice_name: 'Description' , type: 'textarea', value: <%= raw Caboose.json(@calendar.description ) %>, width: 400, height: 100 }
72
+ ]
73
+ });
74
+ $('#calendar td')
75
+ .mouseover(function(e) { $(this).addClass('over'); })
76
+ .mouseout(function(e) { $(this).removeClass('over'); })
77
+ .click(function(e) {
78
+ e.preventDefault();
79
+ if (!$(this).hasClass('blank'))
80
+ {
81
+ var d = $(this).attr('id').replace('day_', '');
82
+ caboose_modal_url('/admin/calendars/<%= @calendar.id %>/events/new?begin_date=' + d);
83
+ }
84
+ });
85
+ });
86
+
87
+ function delete_calendar(calendar_id, confirm)
88
+ {
89
+ if (!confirm)
90
+ {
91
+ var p = $('<p/>').addClass('note confirm')
92
+ .append('Are you sure you want to delete the calendar? ')
93
+ .append($('<input/>').attr('type','button').val('Yes').click(function() { delete_calendar(calendar_id, true); })).append(' ')
94
+ .append($('<input/>').attr('type','button').val('No').click(function() { $('#message').empty(); }));
95
+ $('#message').empty().append(p);
96
+ return;
97
+ }
98
+ $('#message').html("<p class='loading'>Deleting calendar...</p>");
99
+ $.ajax({
100
+ url: '/admin/calendars/' + calendar_id,
101
+ type: 'delete',
102
+ success: function(resp) {
103
+ if (resp.error) $('#message').html("<p class='note error'>" + resp.error + "</p>");
104
+ if (resp.redirect) window.location = resp.redirect;
105
+ }
106
+ });
107
+ }
108
+
109
+ </script>
110
+ <% end %>
111
+
112
+ <% content_for :caboose_css do %>
113
+ <style type='text/css'>
114
+
115
+ #calendar table { border-collapse: collapse; width: 95%; }
116
+ #calendar th { border: #666 1px solid; background: #666; color: #fff; margin: 0; padding: 4px 8px; }
117
+ #calendar td { border: #666 1px solid; position: relative; margin: 0; padding: 0; height: 100px; vertical-align: top; }
118
+ #calendar td.blank { background: #efefef; border: #666 1px solid; }
119
+ #calendar td.over { background: #ffcc99; }
120
+ #calendar td span.day { display: block; float: left; border-right: #666 1px solid; border-bottom: #666 1px solid; width: 20px; text-align: center; }
121
+
122
+ </style>
123
+ <% end %>
@@ -0,0 +1,63 @@
1
+ <h1>Calendars</h1>
2
+
3
+ <form action='/admin/calendars' method='get' class='search_form'>
4
+ <input type='text' name='name_like' placeholder='Name' />
5
+ <input type='submit' value='Search' />
6
+ </form>
7
+
8
+ <p><a href='/admin/calendars/new' id='new_calendar'>New Calendar</a></p>
9
+
10
+ <form action='/admin/calendars' method='post' id='new_calendar_form'>
11
+ <input type='hidden' name='authenticity_token' value='<%= form_authenticity_token %>'>
12
+ <p><input type='text' name='name' id='name' placeholder='Name' style='width: 500px;'></p>
13
+ <div id='new_message'></div>
14
+ <p>
15
+ <input type='button' value='Cancel' onclick="$('#new_calendar_form').slideToggle();">
16
+ <input type='submit' value='Add New Calendar' onclick="add_calendar(); return false;">
17
+ </p>
18
+ </form>
19
+
20
+ <% if @calendars.count > 0 %>
21
+ <table class='data'>
22
+ <tr>
23
+ <th>Name</th>
24
+ <th>Description</th>
25
+ </tr>
26
+ <% @calendars.each do |calendar| %>
27
+ <tr onclick="window.location='/admin/calendars/<%= calendar.id %>';">
28
+ <td><%= calendar.name %></td>
29
+ <td><%= calendar.description %></td>
30
+ </tr>
31
+ <% end %>
32
+ </table>
33
+ <% else %>
34
+ <p>There are no calendars right now.</p>
35
+ <% end %>
36
+
37
+ <% content_for :caboose_js do %>
38
+ <script type='text/javascript'>
39
+
40
+ $(document).ready(function() {
41
+ $('#new_calendar_form').hide();
42
+ $('#new_calendar').click(function(e) {
43
+ e.preventDefault();
44
+ $('#new_calendar_form').slideToggle();
45
+ });
46
+ });
47
+
48
+ function add_calendar()
49
+ {
50
+ $('#new_message').html("<p class='loading'>Adding calendar...</p>");
51
+ $.ajax({
52
+ url: '/admin/calendars',
53
+ type: 'post',
54
+ data: $('#new_calendar_form').serialize(),
55
+ success: function(resp) {
56
+ if (resp.error) $('#new_message').html("<p class='note error'>" + resp.error + "</p>");
57
+ if (resp.redirect) window.location = resp.redirect;
58
+ }
59
+ });
60
+ }
61
+
62
+ </script>
63
+ <% end %>
@@ -0,0 +1,195 @@
1
+ <%
2
+ e = @event
3
+ c = @event.calendar
4
+ g = @event.calendar_event_group
5
+ %>
6
+ <h1>Edit Event</h1>
7
+ <p><div id='calendarevent_<%= e.id %>_name' ></div></p>
8
+ <p><div id='calendarevent_<%= e.id %>_location' ></div></p>
9
+ <p><div id='calendarevent_<%= @event.id %>_description' ></div></p>
10
+ <div id='datetime_container' class='<%= @event.all_day ? 'all_day' : 'non_all_day' %>'>
11
+ <div id='calendarevent_<%= e.id %>_begin_date' ></div>
12
+ <div id='calendarevent_<%= e.id %>_begin_time' ></div>
13
+ <div id='calendarevent_<%= e.id %>_end_date' ></div>
14
+ <div id='calendarevent_<%= e.id %>_end_time' ></div>
15
+ <div class='spacer'></div>
16
+ </div>
17
+ <p id='all_day_repeats_container'>
18
+ <div id='calendarevent_<%= e.id %>_all_day' ></div>
19
+ <div id='calendarevent_<%= e.id %>_repeats' ></div>
20
+ </p>
21
+ <div id='repeat_container' <% if !e.repeats %>style='display: none;'<% end %>>
22
+ <p id='repeat_every'>Repeat every:</p>
23
+ <div id='calendareventgroup_<%= g.id %>_frequency' ></div>
24
+ <div id='calendareventgroup_<%= g.id %>_period' ></div>
25
+ <div id='repeat_by_container' <% if g.repeat_by != Caboose::CalendarEventGroup::PERIOD_MONTH %>style='display: none;'<% end %>>
26
+ <div id='calendareventgroup_<%= g.id %>_repeat_by' ></div>
27
+ </div>
28
+ <div id='week_container' <% if g.repeat_by != Caboose::CalendarEventGroup::PERIOD_WEEK %>style='display: none;'<% end %>>
29
+ <table class='data'>
30
+ <tr><th>S</th><th>M</th><th>T</th><th>W</th><th>R</th><th>F</th><th>S</th></tr>
31
+ <tr>
32
+ <td><div id='calendareventgroup_<%= g.id %>_sun'></div></td>
33
+ <td><div id='calendareventgroup_<%= g.id %>_mon'></div></td>
34
+ <td><div id='calendareventgroup_<%= g.id %>_tue'></div></td>
35
+ <td><div id='calendareventgroup_<%= g.id %>_wed'></div></td>
36
+ <td><div id='calendareventgroup_<%= g.id %>_thu'></div></td>
37
+ <td><div id='calendareventgroup_<%= g.id %>_fri'></div></td>
38
+ <td><div id='calendareventgroup_<%= g.id %>_sat'></div></td>
39
+ </tr>
40
+ </table>
41
+ </div>
42
+ <p id='repeat_dates_container'>
43
+ <div id='calendareventgroup_<%= g.id %>_date_start' ></div>
44
+ <div id='calendareventgroup_<%= g.id %>_date_end' ></div>
45
+ </p>
46
+ </div>
47
+
48
+
49
+ <div id='message'></div>
50
+ <p>
51
+ <input type='button' value='Close' onclick="modal.close();" />
52
+ <input type='button' value='Delete Event' onclick="delete_event(<%= @event.id %>);" />
53
+ </p>
54
+
55
+ <% content_for :caboose_js do %>
56
+ <%= javascript_include_tag "caboose/model/all" %>
57
+ <%= javascript_include_tag "jquery-ui/datepicker" %>
58
+ <script type="text/javascript">
59
+
60
+ var modal = false;
61
+ $(window).ready(function() {
62
+ modal = new CabooseModal(600);
63
+ });
64
+
65
+ $(document).ready(function() {
66
+ new ModelBinder({
67
+ name: 'CalendarEvent',
68
+ id: <%= @event.id %>,
69
+ update_url: '/admin/calendars/<%= c.id %>/events/<%= e.id %>',
70
+ authenticity_token: '<%= form_authenticity_token %>',
71
+ attributes: [
72
+ { name: 'name' , nice_name: 'Name' , type: 'text' , value: <%= raw Caboose.json(e.name ) %>, width: 430 },
73
+ { name: 'description' , nice_name: 'Description' , type: 'textarea' , value: <%= raw Caboose.json(e.description ) %>, width: 430, height: 100 },
74
+ { name: 'location' , nice_name: 'Location' , type: 'text' , value: <%= raw Caboose.json(e.location ) %>, width: 430 },
75
+ { name: 'begin_date' , nice_name: 'Begin date' , type: 'date' , value: <%= raw Caboose.json(e.begin_date.strftime('%m/%d/%Y') ) %>, width: 200 },
76
+ { name: 'begin_time' , nice_name: 'Begin time' , type: 'time' , value: <%= raw Caboose.json(e.begin_date.strftime('%I:%M %P') ) %>, width: 200 , fixed_placeholder: false },
77
+ { name: 'end_date' , nice_name: 'End date' , type: 'date' , value: <%= raw Caboose.json(e.end_date.strftime('%m/%d/%Y') ) %>, width: 200 },
78
+ { name: 'end_time' , nice_name: 'End time' , type: 'time' , value: <%= raw Caboose.json(e.end_date.strftime('%I:%M %P') ) %>, width: 200 , fixed_placeholder: false },
79
+ { name: 'all_day' , nice_name: 'All day' , type: 'checkbox' , value: <%= raw e.all_day ? 1 : 0 %>, width: 100, after_update: after_all_day_update },
80
+ { name: 'repeats' , nice_name: 'Repeats' , type: 'checkbox' , value: <%= raw e.repeats ? 1 : 0 %>, width: 100, after_update: after_repeats_update }
81
+ ],
82
+ on_load: function() { modal.autosize(); }
83
+ });
84
+ new ModelBinder({
85
+ name: 'CalendarEventGroup',
86
+ id: <%= g.id %>,
87
+ update_url: '/admin/calendars/<%= c.id %>/event-groups/<%= g.id %>',
88
+ authenticity_token: '<%= form_authenticity_token %>',
89
+ attributes: [
90
+ { name: 'frequency' , nice_name: 'Repeat every' , type: 'select' , value: <%= raw Caboose.json(g.frequency ) %>, width: 40 , fixed_placeholder: false, options_url: '/admin/event-groups/frequency-options' },
91
+ { name: 'period' , nice_name: 'Repeats' , type: 'select' , value: <%= raw Caboose.json(g.period ) %>, width: 80 , fixed_placeholder: false, options_url: '/admin/event-groups/period-options', after_update: after_period_update },
92
+ { name: 'repeat_by' , nice_name: 'Repeat by' , type: 'select' , value: <%= raw Caboose.json(g.repeat_by ) %>, width: 140 , fixed_placeholder: false, options_url: '/admin/event-groups/repeat-by-options' },
93
+ { name: 'date_start' , nice_name: 'Start' , type: 'date' , value: <%= raw Caboose.json(g.date_start ? g.date_start.strftime('%m/%d/%Y') : '') %>, width: 200, align: 'right' },
94
+ { name: 'date_end' , nice_name: 'End' , type: 'date' , value: <%= raw Caboose.json(g.date_end ? g.date_end.strftime( '%m/%d/%Y') : '') %>, width: 200, align: 'right' },
95
+ { name: 'sun' , nice_name: 'sun' , type: 'checkbox' , value: <%= raw g.sun ? 1 : 0 %>, width: 21 , fixed_placeholder: false },
96
+ { name: 'mon' , nice_name: 'mon' , type: 'checkbox' , value: <%= raw g.mon ? 1 : 0 %>, width: 21 , fixed_placeholder: false },
97
+ { name: 'tue' , nice_name: 'tue' , type: 'checkbox' , value: <%= raw g.tue ? 1 : 0 %>, width: 21 , fixed_placeholder: false },
98
+ { name: 'wed' , nice_name: 'wed' , type: 'checkbox' , value: <%= raw g.wed ? 1 : 0 %>, width: 21 , fixed_placeholder: false },
99
+ { name: 'thu' , nice_name: 'thu' , type: 'checkbox' , value: <%= raw g.thu ? 1 : 0 %>, width: 21 , fixed_placeholder: false },
100
+ { name: 'fri' , nice_name: 'fri' , type: 'checkbox' , value: <%= raw g.fri ? 1 : 0 %>, width: 21 , fixed_placeholder: false },
101
+ { name: 'sat' , nice_name: 'sat' , type: 'checkbox' , value: <%= raw g.sat ? 1 : 0 %>, width: 21 , fixed_placeholder: false }
102
+ ],
103
+ on_load: function() { modal.autosize(); }
104
+ });
105
+ });
106
+
107
+ function delete_event(event_id, confirm)
108
+ {
109
+ if (!confirm)
110
+ {
111
+ var p = $('<p/>').addClass('note confirm')
112
+ .append('Are you sure you want to delete the event? ')
113
+ .append($('<input/>').attr('type','button').val('Yes').click(function() { delete_event(event_id, true); })).append(' ')
114
+ .append($('<input/>').attr('type','button').val('No').click(function() { $('#message').empty(); }));
115
+ modal.autosize(p);
116
+ return;
117
+ }
118
+ modal.autosize("<p class='loading'>Deleting event...</p>");
119
+ $.ajax({
120
+ url: '/admin/calendars/<%= c.id %>/events/' + event_id,
121
+ type: 'delete',
122
+ success: function(resp) {
123
+ if (resp.error) modal.autosize("<p class='note error'>" + resp.error + "</p>");
124
+ if (resp.redirect) window.location = resp.redirect;
125
+ }
126
+ });
127
+ }
128
+
129
+ function after_all_day_update()
130
+ {
131
+ var el = $('#datetime_container');
132
+ if (el.hasClass('all_day'))
133
+ el.removeClass('all_day').addClass('non_all_day');
134
+ else
135
+ el.removeClass('non_all_day').addClass('all_day');
136
+ modal.autosize();
137
+ }
138
+
139
+ function after_repeats_update()
140
+ {
141
+ var el = $('#repeat_container');
142
+ el.slideToggle(function() { modal.autosize(); });
143
+ }
144
+
145
+ function after_period_update()
146
+ {
147
+ var period = $('#calendareventgroup_<%= g.id %>_period').val();
148
+ if (period == 'Week')
149
+ $('#week_container').show();
150
+ else
151
+ $('#week_container').hide();
152
+ if (period == 'Month')
153
+ $('#repeat_by_container').show();
154
+ else
155
+ $('#repeat_by_container').hide();
156
+ modal.autosize();
157
+ }
158
+
159
+ </script>
160
+ <% end %>
161
+
162
+ <% content_for :caboose_css do %>
163
+ <%= stylesheet_link_tag "jquery-ui/datepicker" %>
164
+ <style type='text/css'>
165
+
166
+ #datetime_container { }
167
+
168
+ #calendarevent_<%= e.id %>_all_day_container { float: left; width: 130px; }
169
+ #calendarevent_<%= e.id %>_repeats_container { width: 200px; }
170
+
171
+ div.all_day #calendarevent_<%= e.id %>_begin_date_container { float: left; width: 230px; }
172
+ div.all_day #calendarevent_<%= e.id %>_begin_time_container { display: none; }
173
+ div.all_day #calendarevent_<%= e.id %>_end_date_container { width: 230px; }
174
+ div.all_day #calendarevent_<%= e.id %>_end_time_container { display: none; }
175
+ div.all_day .spacer { clear: left; }
176
+
177
+ div.non_all_day #calendarevent_<%= e.id %>_begin_date_container { float: left; width: 230px; margin-bottom: 10px; }
178
+ div.non_all_day #calendarevent_<%= e.id %>_begin_time_container { width: 230px; margin-bottom: 10px; }
179
+ div.non_all_day #calendarevent_<%= e.id %>_end_date_container { float: left; width: 230px; clear: left; }
180
+ div.non_all_day #calendarevent_<%= e.id %>_end_time_container { float: left; width: 230px; }
181
+ div.non_all_day .spacer { clear: left; }
182
+
183
+ #repeat_every { float: left; width: 110px; }
184
+ #calendareventgroup_<%= g.id %>_frequency_container { float: left; width: 70px; }
185
+ #calendareventgroup_<%= g.id %>_period_container { float: left; width: 110px; }
186
+ #calendareventgroup_<%= g.id %>_repeat_by { float: left; width: 100px; }
187
+
188
+ #repeat_dates_container { clear: left; }
189
+ #calendareventgroup_<%= g.id %>_date_start_container { float: left; width: 230px; }
190
+ #calendareventgroup_<%= g.id %>_date_end_container { width: 230px; }
191
+
192
+ #week_container { clear: left; padding-top: 10px; }
193
+
194
+ </style>
195
+ <% end %>
@@ -0,0 +1,67 @@
1
+ <h1>Calendars</h1>
2
+
3
+ <form action='/admin/calendars' method='get' class='search_form'>
4
+ <input type='text' name='name_like' placeholder='Name' />
5
+ <input type='submit' value='Search' />
6
+ </form>
7
+
8
+ <p><a href='/admin/calendars/new' id='new_calendar'>New Calendar</a></p>
9
+
10
+ <form action='/admin/calendars' method='post' id='new_calendar_form'>
11
+ <input type='hidden' name='authenticity_token' value='<%= form_authenticity_token %>'>
12
+ <p><input type='text' name='name' id='name' placeholder='Name' style='width: 500px;'></p>
13
+ <div id='new_message'></div>
14
+ <p>
15
+ <input type='button' value='Cancel' onclick="$('#new_calendar_form').slideToggle();">
16
+ <input type='submit' value='Add New Calendar' onclick="add_calendar(); return false;">
17
+ </p>
18
+ </form>
19
+
20
+ <% if @calendars.count > 0 %>
21
+ <table class='data'>
22
+ <tr>
23
+ <%= raw @gen.sortable_table_headings({
24
+ 'name' => 'Name',
25
+ 'description' => 'Description'
26
+ })
27
+ %>
28
+ </tr>
29
+ <% @calendars.each do |calendar| %>
30
+ <tr onclick="window.location='/admin/calendars/<%= calendar.id %>';">
31
+ <td><%= calendar.name %></td>
32
+ <td><%= calendar.description %></td>
33
+ </tr>
34
+ <% end %>
35
+ </table>
36
+ <p><%= raw @gen.generate %></p>
37
+ <% else %>
38
+ <p>There are no calendars right now.</p>
39
+ <% end %>
40
+
41
+ <% content_for :caboose_js do %>
42
+ <script type='text/javascript'>
43
+
44
+ $(document).ready(function() {
45
+ $('#new_calendar_form').hide();
46
+ $('#new_calendar').click(function(e) {
47
+ e.preventDefault();
48
+ $('#new_calendar_form').slideToggle();
49
+ });
50
+ });
51
+
52
+ function add_calendar()
53
+ {
54
+ $('#new_message').html("<p class='loading'>Adding calendar...</p>");
55
+ $.ajax({
56
+ url: '/admin/calendars',
57
+ type: 'post',
58
+ data: $('#new_calendar_form').serialize(),
59
+ success: function(resp) {
60
+ if (resp.error) $('#new_message').html("<p class='note error'>" + resp.error + "</p>");
61
+ if (resp.redirect) window.location = resp.redirect;
62
+ }
63
+ });
64
+ }
65
+
66
+ </script>
67
+ <% end %>
@@ -0,0 +1,38 @@
1
+
2
+ <h1>New Event</h1>
3
+
4
+ <form action='/admin/calendars/<%= @calendar.id %>' method='post' id='new_event_form'>
5
+ <input type='hidden' name='authenticity_token' value='<%= form_authenticity_token %>'>
6
+ <input type='hidden' name='begin_date' value='<%= @begin_date.strftime('%Y-%m-%d') %>'>
7
+ <p><input type='text' name='name' id='name' placeholder='Name' style='width: 500px;'></p>
8
+ <div id='message'></div>
9
+ <p>
10
+ <input type='button' value='Cancel' onclick="modal.close();" />
11
+ <input type='submit' value='Add New Event' onclick="add_event(); return false;" />
12
+ </p>
13
+ </form>
14
+
15
+ <% content_for :caboose_js do %>
16
+ <%= javascript_include_tag "caboose/model/all" %>
17
+ <script type="text/javascript">
18
+
19
+ $(window).load(function() {
20
+ modal = new CabooseModal(600);
21
+ });
22
+
23
+ function add_event()
24
+ {
25
+ $('#message').html("<p class='loading'>Adding event...</p>");
26
+ $.ajax({
27
+ url: '/admin/calendars/<%= @calendar.id %>/events',
28
+ type: 'post',
29
+ data: $('#new_event_form').serialize(),
30
+ success: function(resp) {
31
+ if (resp.error) $('#message').html("<p class='note error'>" + resp.error + "</p>");
32
+ if (resp.redirect) window.location = resp.redirect;
33
+ }
34
+ });
35
+ }
36
+
37
+ </script>
38
+ <% end %>
@@ -39,7 +39,14 @@
39
39
  :logged_in_user => @logged_in_user,
40
40
  :site => @site
41
41
  })
42
- str.gsub!('|CABOOSE_CSS|' , yield(:css))
42
+
43
+ protocol = request.protocol
44
+ css = yield(:css)
45
+ css.gsub!("<link href=\"//", "<link href=\"#{protocol}")
46
+ css.gsub!("<link href='//" , "<link href='#{protocol}")
47
+
48
+ str.gsub!('|CABOOSE_CSS|' , css)
49
+ str.gsub!('|CABOOSE_PROTOCOL|' , protocol)
43
50
  str.gsub!('|CABOOSE_JAVASCRIPT|' , yield(:js))
44
51
  str.gsub!('|CABOOSE_CSRF|' , csrf_meta_tags)
45
52
 
data/config/routes.rb CHANGED
@@ -208,6 +208,23 @@ Caboose::Engine.routes.draw do
208
208
  get "admin/posts" => "posts#admin_index"
209
209
  post "admin/posts" => "posts#admin_add"
210
210
  delete "admin/posts/:id" => "posts#admin_delete"
211
+
212
+ get "admin/calendars" => "calendars#admin_index"
213
+ get "admin/calendars/:id" => "calendars#admin_edit"
214
+ put "admin/calendars/:id" => "calendars#admin_update"
215
+ post "admin/calendars" => "calendars#admin_add"
216
+ delete "admin/calendars" => "calendars#admin_delete"
217
+
218
+ get "admin/calendars/:calendar_id/events" => "events#admin_index"
219
+ get "admin/calendars/:calendar_id/events/new" => "events#admin_new"
220
+ get "admin/calendars/:calendar_id/events/:id" => "events#admin_edit"
221
+ put "admin/calendars/:calendar_id/events/:id" => "events#admin_update"
222
+ post "admin/calendars/:calendar_id/events" => "events#admin_add"
223
+ delete "admin/calendars/:calendar_id/events" => "events#admin_delete"
224
+
225
+ get "admin/event-groups/period-options" => "event_groups#admin_period_options"
226
+ get "admin/event-groups/frequency-options" => "event_groups#admin_frequency_options"
227
+ get "admin/event-groups/repeat-by-options" => "event_groups#admin_repeat_by_options"
211
228
 
212
229
  get "admin/ab-variants" => "ab_variants#admin_index"
213
230
  get "admin/ab-variants/new" => "ab_variants#admin_new"
@@ -1,3 +1,3 @@
1
1
  module Caboose
2
- VERSION = '0.4.109'
2
+ VERSION = '0.4.110'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: caboose-cms
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.109
4
+ version: 0.4.110
5
5
  platform: ruby
6
6
  authors:
7
7
  - William Barry
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-09-08 00:00:00.000000000 Z
11
+ date: 2014-09-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -268,6 +268,9 @@ files:
268
268
  - app/controllers/caboose/block_type_store_controller.rb
269
269
  - app/controllers/caboose/block_types_controller.rb
270
270
  - app/controllers/caboose/blocks_controller.rb
271
+ - app/controllers/caboose/calendars_controller.rb
272
+ - app/controllers/caboose/event_groups_controller.rb
273
+ - app/controllers/caboose/events_controller.rb
271
274
  - app/controllers/caboose/images_controller.rb
272
275
  - app/controllers/caboose/login_controller.rb
273
276
  - app/controllers/caboose/logout_controller.rb
@@ -303,6 +306,9 @@ files:
303
306
  - app/models/caboose/block_type_source.rb
304
307
  - app/models/caboose/block_type_summary.rb
305
308
  - app/models/caboose/caboose_plugin.rb
309
+ - app/models/caboose/calendar.rb
310
+ - app/models/caboose/calendar_event.rb
311
+ - app/models/caboose/calendar_event_group.rb
306
312
  - app/models/caboose/core_plugin.rb
307
313
  - app/models/caboose/crumbtrail.rb
308
314
  - app/models/caboose/database_session.rb
@@ -388,6 +394,11 @@ files:
388
394
  - app/views/caboose/blocks/admin_edit_richtext.html.erb
389
395
  - app/views/caboose/blocks/admin_new.html.erb
390
396
  - app/views/caboose/blocks/admin_render_second_level.json.erb
397
+ - app/views/caboose/calendars/admin_edit.html.erb
398
+ - app/views/caboose/calendars/admin_index.html.erb
399
+ - app/views/caboose/events/admin_edit.html.erb
400
+ - app/views/caboose/events/admin_index.html.erb
401
+ - app/views/caboose/events/admin_new.html.erb
391
402
  - app/views/caboose/extras/error.html.erb
392
403
  - app/views/caboose/extras/error404.html.erb
393
404
  - app/views/caboose/extras/error_invalid_site.html.erb