Commit b94badef159ebcf5b2887357668a037494432765
Committed by
Leandro Santos
1 parent
2baaf088
Exists in
staging
Ticket #117: Adding a Events plugin that exposes a calendar block
Showing
8 changed files
with
480 additions
and
0 deletions
Show diff stats
... | ... | @@ -0,0 +1,23 @@ |
1 | +class EventsPlugin < Noosfero::Plugin | |
2 | + def self.plugin_name | |
3 | + 'EventsPlugin' | |
4 | + end | |
5 | + | |
6 | + def self.plugin_description | |
7 | + _('Adds a block that shows events in a calendar') | |
8 | + end | |
9 | + | |
10 | + def self.extra_blocks | |
11 | + { | |
12 | + EventsPlugin::EventsBlock => { type: [Person, Community, Enterprise] } | |
13 | + } | |
14 | + end | |
15 | + | |
16 | + def self.has_admin_url? | |
17 | + false | |
18 | + end | |
19 | + | |
20 | + def stylesheet? | |
21 | + true | |
22 | + end | |
23 | +end | ... | ... |
... | ... | @@ -0,0 +1,45 @@ |
1 | +class EventsPlugin::EventsBlock < Block | |
2 | + def view_title | |
3 | + self.default_title | |
4 | + end | |
5 | + | |
6 | + def events | |
7 | + owner.events | |
8 | + end | |
9 | + | |
10 | + def extra_option | |
11 | + { } | |
12 | + end | |
13 | + | |
14 | + def self.description | |
15 | + _('Shows events in a calendar.') | |
16 | + end | |
17 | + | |
18 | + def help | |
19 | + _('This block shows events in a calendar.') | |
20 | + end | |
21 | + | |
22 | + def default_title | |
23 | + _('Events Calendar') | |
24 | + end | |
25 | + | |
26 | + def api_content | |
27 | + content = [] | |
28 | + events.each do |event| | |
29 | + content << { title: event.title, id: event.id, date: event.start_date } | |
30 | + end | |
31 | + { events: content } | |
32 | + end | |
33 | + | |
34 | + def display_api_content_by_default? | |
35 | + false | |
36 | + end | |
37 | + | |
38 | + def timeout | |
39 | + 4.hours | |
40 | + end | |
41 | + | |
42 | + def self.expire_on | |
43 | + { profile: [:article] } | |
44 | + end | |
45 | +end | ... | ... |
... | ... | @@ -0,0 +1,194 @@ |
1 | +/** | |
2 | + * @license e-Calendar v0.9.3 | |
3 | + * (c) 2014-2016 - Jhonis de Souza | |
4 | + * License: GNU | |
5 | + */ | |
6 | + | |
7 | +(function ($) { | |
8 | + | |
9 | + var eCalendar = function (options, object) { | |
10 | + // Initializing global variables | |
11 | + var adDay = new Date().getDate(); | |
12 | + var adMonth = new Date().getMonth(); | |
13 | + var adYear = new Date().getFullYear(); | |
14 | + var dDay = adDay; | |
15 | + var dMonth = adMonth; | |
16 | + var dYear = adYear; | |
17 | + var instance = object; | |
18 | + | |
19 | + var settings = $.extend({}, $.fn.eCalendar.defaults, options); | |
20 | + | |
21 | + function lpad(value, length, pad) { | |
22 | + if (typeof pad == 'undefined') { | |
23 | + pad = '0'; | |
24 | + } | |
25 | + var p; | |
26 | + for (var i = 0; i < length; i++) { | |
27 | + p += pad; | |
28 | + } | |
29 | + return (p + value).slice(-length); | |
30 | + } | |
31 | + | |
32 | + var mouseOver = function () { | |
33 | + $(this).addClass('c-nav-btn-over'); | |
34 | + }; | |
35 | + var mouseLeave = function () { | |
36 | + $(this).removeClass('c-nav-btn-over'); | |
37 | + }; | |
38 | + var mouseOverEvent = function () { | |
39 | + $(this).addClass('c-event-over'); | |
40 | + var d = $(this).attr('data-event-day'); | |
41 | + $('div.c-event-item[data-event-day="' + d + '"]').addClass('c-event-over'); | |
42 | + }; | |
43 | + var mouseLeaveEvent = function () { | |
44 | + $(this).removeClass('c-event-over') | |
45 | + var d = $(this).attr('data-event-day'); | |
46 | + $('div.c-event-item[data-event-day="' + d + '"]').removeClass('c-event-over'); | |
47 | + }; | |
48 | + var mouseOverItem = function () { | |
49 | + $(this).addClass('c-event-over'); | |
50 | + var d = $(this).attr('data-event-day'); | |
51 | + $('div.c-event[data-event-day="' + d + '"]').addClass('c-event-over'); | |
52 | + }; | |
53 | + var mouseLeaveItem = function () { | |
54 | + $(this).removeClass('c-event-over') | |
55 | + var d = $(this).attr('data-event-day'); | |
56 | + $('div.c-event[data-event-day="' + d + '"]').removeClass('c-event-over'); | |
57 | + }; | |
58 | + var nextMonth = function () { | |
59 | + if (dMonth < 11) { | |
60 | + dMonth++; | |
61 | + } else { | |
62 | + dMonth = 0; | |
63 | + dYear++; | |
64 | + } | |
65 | + print(); | |
66 | + }; | |
67 | + var previousMonth = function () { | |
68 | + if (dMonth > 0) { | |
69 | + dMonth--; | |
70 | + } else { | |
71 | + dMonth = 11; | |
72 | + dYear--; | |
73 | + } | |
74 | + print(); | |
75 | + }; | |
76 | + | |
77 | + function loadEvents() { | |
78 | + if (typeof settings.url != 'undefined' && settings.url != '') { | |
79 | + $.ajax({url: settings.url, | |
80 | + async: false, | |
81 | + success: function (result) { | |
82 | + settings.events = result; | |
83 | + } | |
84 | + }); | |
85 | + } | |
86 | + } | |
87 | + | |
88 | + function print() { | |
89 | + loadEvents(); | |
90 | + var dWeekDayOfMonthStart = new Date(dYear, dMonth, 1).getDay() - settings.firstDayOfWeek; | |
91 | + if (dWeekDayOfMonthStart < 0) { | |
92 | + dWeekDayOfMonthStart = 6 - ((dWeekDayOfMonthStart + 1) * -1); | |
93 | + } | |
94 | + var dLastDayOfMonth = new Date(dYear, dMonth + 1, 0).getDate(); | |
95 | + var dLastDayOfPreviousMonth = new Date(dYear, dMonth + 1, 0).getDate() - dWeekDayOfMonthStart + 1; | |
96 | + | |
97 | + var cBody = $('<div/>').addClass('c-grid'); | |
98 | + var cEvents = $('<div/>').addClass('c-event-grid'); | |
99 | + var cEventsBody = $('<div/>').addClass('c-event-body'); | |
100 | + cEvents.append($('<div/>').addClass('c-event-title c-pad-top').html(settings.eventTitle)); | |
101 | + cEvents.append(cEventsBody); | |
102 | + var cNext = $('<div/>').addClass('c-next c-grid-title c-pad-top'); | |
103 | + var cMonth = $('<div/>').addClass('c-month c-grid-title c-pad-top'); | |
104 | + var cPrevious = $('<div/>').addClass('c-previous c-grid-title c-pad-top'); | |
105 | + cPrevious.html(settings.textArrows.previous); | |
106 | + cMonth.html(settings.months[dMonth] + ' ' + dYear); | |
107 | + cNext.html(settings.textArrows.next); | |
108 | + | |
109 | + cPrevious.on('mouseover', mouseOver).on('mouseleave', mouseLeave).on('click', previousMonth); | |
110 | + cNext.on('mouseover', mouseOver).on('mouseleave', mouseLeave).on('click', nextMonth); | |
111 | + | |
112 | + cBody.append(cPrevious); | |
113 | + cBody.append(cMonth); | |
114 | + cBody.append(cNext); | |
115 | + var dayOfWeek = settings.firstDayOfWeek; | |
116 | + for (var i = 0; i < 7; i++) { | |
117 | + if (dayOfWeek > 6) { | |
118 | + dayOfWeek = 0; | |
119 | + } | |
120 | + var cWeekDay = $('<div/>').addClass('c-week-day c-pad-top'); | |
121 | + cWeekDay.html(settings.weekDays[dayOfWeek]); | |
122 | + cBody.append(cWeekDay); | |
123 | + dayOfWeek++; | |
124 | + } | |
125 | + var day = 1; | |
126 | + var dayOfNextMonth = 1; | |
127 | + for (var i = 0; i < 42; i++) { | |
128 | + var cDay = $('<div/>'); | |
129 | + if (i < dWeekDayOfMonthStart) { | |
130 | + cDay.addClass('c-day-previous-month c-pad-top'); | |
131 | + cDay.html(dLastDayOfPreviousMonth++); | |
132 | + } else if (day <= dLastDayOfMonth) { | |
133 | + cDay.addClass('c-day c-pad-top'); | |
134 | + if (day == dDay && adMonth == dMonth && adYear == dYear) { | |
135 | + cDay.addClass('c-today'); | |
136 | + } | |
137 | + for (var j = 0; j < settings.events.length; j++) { | |
138 | + var d = settings.events[j].datetime; | |
139 | + if (d.getDate() == day && d.getMonth() == dMonth && d.getFullYear() == dYear) { | |
140 | + cDay.addClass('c-event').attr('data-event-day', d.getDate()); | |
141 | + cDay.on('mouseover', mouseOverEvent).on('mouseleave', mouseLeaveEvent); | |
142 | + } | |
143 | + } | |
144 | + cDay.html(day++); | |
145 | + } else { | |
146 | + cDay.addClass('c-day-next-month c-pad-top'); | |
147 | + cDay.html(dayOfNextMonth++); | |
148 | + } | |
149 | + cBody.append(cDay); | |
150 | + } | |
151 | + var eventList = $('<div/>').addClass('c-event-list'); | |
152 | + for (var i = 0; i < settings.events.length; i++) { | |
153 | + var d = settings.events[i].datetime; | |
154 | + if (d.getMonth() == dMonth && d.getFullYear() == dYear) { | |
155 | + var date = lpad(d.getDate(), 2) + '/' + lpad(d.getMonth() + 1, 2) + ' ' + lpad(d.getHours(), 2) + ':' + lpad(d.getMinutes(), 2); | |
156 | + var item = $('<div/>').addClass('c-event-item'); | |
157 | + var title = $('<div/>').addClass('title').html(date + ' ' + settings.events[i].title + '<br/>'); | |
158 | + var description = $('<div/>').addClass('description').html(settings.events[i].description + '<br/>'); | |
159 | + item.attr('data-event-day', d.getDate()); | |
160 | + item.on('mouseover', mouseOverItem).on('mouseleave', mouseLeaveItem); | |
161 | + item.append(title).append(description); | |
162 | + eventList.append(item); | |
163 | + } | |
164 | + } | |
165 | + $(instance).addClass('calendar'); | |
166 | + cEventsBody.append(eventList); | |
167 | + $(instance).html(cBody).append(cEvents); | |
168 | + } | |
169 | + | |
170 | + return print(); | |
171 | + } | |
172 | + | |
173 | + $.fn.eCalendar = function (oInit) { | |
174 | + return this.each(function () { | |
175 | + return eCalendar(oInit, $(this)); | |
176 | + }); | |
177 | + }; | |
178 | + | |
179 | + // plugin defaults | |
180 | + $.fn.eCalendar.defaults = { | |
181 | + weekDays: ['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sab'], | |
182 | + months: ['Janeiro', 'Fevereiro', 'Março', 'Abril', 'Maio', 'Junho', 'Julho', 'Agosto', 'Setembro', 'Outubro', 'Novembro', 'Dezembro'], | |
183 | + textArrows: {previous: '<', next: '>'}, | |
184 | + eventTitle: 'Eventos', | |
185 | + url: '', | |
186 | + events: [ | |
187 | + {title: 'Evento de Abertura', description: 'Abertura das Olimpíadas Rio 2016', datetime: new Date(2016, new Date().getMonth(), 12, 17)}, | |
188 | + {title: 'Tênis de Mesa', description: 'BRA x ARG - Semifinal', datetime: new Date(2016, new Date().getMonth(), 23, 16)}, | |
189 | + {title: 'Ginástica Olímpica', description: 'Classificatórias de equipes', datetime: new Date(2016, new Date().getMonth(), 31, 16)} | |
190 | + ], | |
191 | + firstDayOfWeek: 0 | |
192 | + }; | |
193 | + | |
194 | +}(jQuery)); | |
0 | 195 | \ No newline at end of file | ... | ... |
... | ... | @@ -0,0 +1,149 @@ |
1 | +.calendar * { | |
2 | + box-sizing: border-box; | |
3 | + font-size: 12px; | |
4 | + color: #000; | |
5 | +} | |
6 | + | |
7 | +.calendar-sm { | |
8 | + cursor: default; | |
9 | + width: 100%; | |
10 | +} | |
11 | + | |
12 | +.calendar { | |
13 | + cursor: default; | |
14 | + width: 100%; | |
15 | +} | |
16 | + | |
17 | +.calendar-sm .c-pad-top { | |
18 | + padding-top: 2%; | |
19 | +} | |
20 | + | |
21 | +.calendar .c-pad-top { | |
22 | + padding-top: 3%; | |
23 | +} | |
24 | + | |
25 | +.c-grid { | |
26 | + height: 270px; | |
27 | +} | |
28 | + | |
29 | +.c-day { | |
30 | + width: 14.28%; | |
31 | + height: 13%; | |
32 | + background-color: #EEE; | |
33 | + float: left; | |
34 | + text-align: center; | |
35 | +} | |
36 | + | |
37 | +.c-day-previous-month { | |
38 | + width: 14.28%; | |
39 | + height: 13%; | |
40 | + background-color: #CCC; | |
41 | + float: left; | |
42 | + text-align: center; | |
43 | + color: #000; | |
44 | +} | |
45 | + | |
46 | +.c-day-next-month { | |
47 | + width: 14.28%; | |
48 | + height: 13%; | |
49 | + background-color: #CCC; | |
50 | + float: left; | |
51 | + text-align: center; | |
52 | + color: #000; | |
53 | +} | |
54 | + | |
55 | +.c-week-day { | |
56 | + width: 14.28%; | |
57 | + height: 10.38%; | |
58 | + background-color: transparent; | |
59 | + color: #000; | |
60 | + float: left; | |
61 | + text-align: center; | |
62 | + padding-top: 1%; | |
63 | +} | |
64 | + | |
65 | +.c-next { | |
66 | + width: 12.5%; | |
67 | + height: 12%; | |
68 | + padding: 2% 2% 0 2%; | |
69 | + text-align: right; | |
70 | + cursor: pointer; | |
71 | +} | |
72 | + | |
73 | +.c-previous { | |
74 | + width: 12.5%; | |
75 | + height: 12%; | |
76 | + padding: 2% 2% 0 2%; | |
77 | + text-align: left; | |
78 | + cursor: pointer; | |
79 | +} | |
80 | + | |
81 | +.c-month { | |
82 | + width: 75%; | |
83 | + height: 12%; | |
84 | + text-align: center; | |
85 | +} | |
86 | + | |
87 | +.c-nav-btn-over { | |
88 | + background-color: #CCC !important; | |
89 | + font-weight: bold; | |
90 | +} | |
91 | + | |
92 | +.c-today { | |
93 | + background-color: #CCC; | |
94 | + color: #FFF; | |
95 | +} | |
96 | + | |
97 | +.c-event { | |
98 | + background-color: #333; | |
99 | + color: white; | |
100 | + font-weight: bold; | |
101 | + cursor: pointer; | |
102 | +} | |
103 | + | |
104 | +.c-grid { | |
105 | + width: 100%; | |
106 | +} | |
107 | + | |
108 | +.c-event-grid { | |
109 | + margin-left: 1px; | |
110 | + width: 100%; | |
111 | + clear: both; | |
112 | +} | |
113 | + | |
114 | +.c-grid-title { | |
115 | + float: left; | |
116 | + background-color: #EEE; | |
117 | + color: #000; | |
118 | +} | |
119 | + | |
120 | +.c-event-title { | |
121 | + width: 100%; | |
122 | + height: 12%; | |
123 | + text-align: center; | |
124 | + background-color: #FFF; | |
125 | + color: #000; | |
126 | +} | |
127 | + | |
128 | +.c-event-body { | |
129 | + height: 88.1%; | |
130 | +} | |
131 | + | |
132 | +.c-event-item { | |
133 | + padding: 0; | |
134 | + margin: 2px 0; | |
135 | +} | |
136 | + | |
137 | +.c-event-over { | |
138 | + background-color: #EEE; | |
139 | + color: black; | |
140 | +} | |
141 | + | |
142 | +.c-event-item .description { | |
143 | + display: none; | |
144 | +} | |
145 | + | |
146 | +.block .calendar a, | |
147 | +.block .calendar a:visited { | |
148 | + color: #000; | |
149 | +} | ... | ... |
... | ... | @@ -0,0 +1 @@ |
1 | +require_relative '../../../test/test_helper' | ... | ... |
... | ... | @@ -0,0 +1,42 @@ |
1 | +require_relative '../test_helper' | |
2 | + | |
3 | +class EventsBlockTest < ActiveSupport::TestCase | |
4 | + should 'describe itself' do | |
5 | + assert_not_equal Block.description, EventsPlugin::EventsBlock.description | |
6 | + end | |
7 | + | |
8 | + should 'is editable' do | |
9 | + block = EventsPlugin::EventsBlock.new | |
10 | + assert block.editable? | |
11 | + end | |
12 | + | |
13 | + should 'return events' do | |
14 | + profile = create(Profile, name: 'Test') | |
15 | + event1 = create(Event, profile: profile) | |
16 | + event2 = create(Event, profile: profile) | |
17 | + event3 = create(Event, profile: create(Profile, name: 'Other')) | |
18 | + | |
19 | + block = EventsPlugin::EventsBlock.new | |
20 | + block.stubs(:owner).returns(profile) | |
21 | + | |
22 | + assert_equal [event1, event2].map(&:id), block.events.map(&:id) | |
23 | + end | |
24 | +end | |
25 | + | |
26 | +require 'boxes_helper' | |
27 | + | |
28 | +class EventsBlockViewTest < ActionView::TestCase | |
29 | + include BoxesHelper | |
30 | + | |
31 | + should 'return events in api_content' do | |
32 | + profile = create(Profile, name: 'Test') | |
33 | + event1 = create(Event, profile: profile) | |
34 | + event2 = create(Event, profile: profile) | |
35 | + event3 = create(Event, profile: create(Profile, name: 'Other')) | |
36 | + | |
37 | + block = EventsPlugin::EventsBlock.new | |
38 | + block.stubs(:owner).returns(profile) | |
39 | + | |
40 | + assert_equal [event1.id, event2.id], block.api_content[:events].map{ |e| e[:id] } | |
41 | + end | |
42 | +end | ... | ... |
... | ... | @@ -0,0 +1,25 @@ |
1 | +<%= javascript_include_tag 'plugins/events/jquery.e-calendar' %> | |
2 | +<%= block_title(block.view_title, block.subtitle) %> | |
3 | + | |
4 | +<div class="events-block" id="events-block-<%= block.id %>"></div> | |
5 | + | |
6 | +<script> | |
7 | + $(document).ready(function () { | |
8 | + $('#events-block-<%= block.id %>').eCalendar({ | |
9 | + weekDays: <%= %w(S M T W T F S).collect{|day| c_(day)}.to_json %>, | |
10 | + months: <%= %w(January February March April May June July August September October November December).collect{|m| c_(m)}.to_json %>, | |
11 | + textArrows: { previous: '<', next: '>' }, | |
12 | + eventTitle: '<%= c_('Events') %>', | |
13 | + url: '', | |
14 | + events: [ | |
15 | + <% block.events.each do |event| %> | |
16 | + { | |
17 | + title: '<%= link_to(event.title, event.view_url) %>', | |
18 | + description: '', | |
19 | + datetime: new Date(<%= event.start_date.to_i %>000) | |
20 | + } | |
21 | + <% end %> | |
22 | + ] | |
23 | + }); | |
24 | + }); | |
25 | +</script> | ... | ... |