From b94badef159ebcf5b2887357668a037494432765 Mon Sep 17 00:00:00 2001 From: Caio SBA Date: Mon, 15 Aug 2016 17:40:11 -0300 Subject: [PATCH] Ticket #117: Adding a Events plugin that exposes a calendar block --- plugins/events/lib/events_plugin.rb | 23 +++++++++++++++++++++++ plugins/events/lib/events_plugin/events_block.rb | 45 +++++++++++++++++++++++++++++++++++++++++++++ plugins/events/public/jquery.e-calendar.js | 194 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ plugins/events/public/style.css | 149 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ plugins/events/test/test_helper.rb | 1 + plugins/events/test/unit/events_block_test.rb | 42 ++++++++++++++++++++++++++++++++++++++++++ plugins/events/views/blocks/events.html.erb | 25 +++++++++++++++++++++++++ plugins/events/views/profile_design | 1 + 8 files changed, 480 insertions(+), 0 deletions(-) create mode 100644 plugins/events/lib/events_plugin.rb create mode 100644 plugins/events/lib/events_plugin/events_block.rb create mode 100644 plugins/events/public/jquery.e-calendar.js create mode 100644 plugins/events/public/style.css create mode 100644 plugins/events/test/test_helper.rb create mode 100644 plugins/events/test/unit/events_block_test.rb create mode 100644 plugins/events/views/blocks/events.html.erb create mode 120000 plugins/events/views/profile_design diff --git a/plugins/events/lib/events_plugin.rb b/plugins/events/lib/events_plugin.rb new file mode 100644 index 0000000..075c65a --- /dev/null +++ b/plugins/events/lib/events_plugin.rb @@ -0,0 +1,23 @@ +class EventsPlugin < Noosfero::Plugin + def self.plugin_name + 'EventsPlugin' + end + + def self.plugin_description + _('Adds a block that shows events in a calendar') + end + + def self.extra_blocks + { + EventsPlugin::EventsBlock => { type: [Person, Community, Enterprise] } + } + end + + def self.has_admin_url? + false + end + + def stylesheet? + true + end +end diff --git a/plugins/events/lib/events_plugin/events_block.rb b/plugins/events/lib/events_plugin/events_block.rb new file mode 100644 index 0000000..27e99c6 --- /dev/null +++ b/plugins/events/lib/events_plugin/events_block.rb @@ -0,0 +1,45 @@ +class EventsPlugin::EventsBlock < Block + def view_title + self.default_title + end + + def events + owner.events + end + + def extra_option + { } + end + + def self.description + _('Shows events in a calendar.') + end + + def help + _('This block shows events in a calendar.') + end + + def default_title + _('Events Calendar') + end + + def api_content + content = [] + events.each do |event| + content << { title: event.title, id: event.id, date: event.start_date } + end + { events: content } + end + + def display_api_content_by_default? + false + end + + def timeout + 4.hours + end + + def self.expire_on + { profile: [:article] } + end +end diff --git a/plugins/events/public/jquery.e-calendar.js b/plugins/events/public/jquery.e-calendar.js new file mode 100644 index 0000000..cbecdc2 --- /dev/null +++ b/plugins/events/public/jquery.e-calendar.js @@ -0,0 +1,194 @@ +/** + * @license e-Calendar v0.9.3 + * (c) 2014-2016 - Jhonis de Souza + * License: GNU + */ + +(function ($) { + + var eCalendar = function (options, object) { + // Initializing global variables + var adDay = new Date().getDate(); + var adMonth = new Date().getMonth(); + var adYear = new Date().getFullYear(); + var dDay = adDay; + var dMonth = adMonth; + var dYear = adYear; + var instance = object; + + var settings = $.extend({}, $.fn.eCalendar.defaults, options); + + function lpad(value, length, pad) { + if (typeof pad == 'undefined') { + pad = '0'; + } + var p; + for (var i = 0; i < length; i++) { + p += pad; + } + return (p + value).slice(-length); + } + + var mouseOver = function () { + $(this).addClass('c-nav-btn-over'); + }; + var mouseLeave = function () { + $(this).removeClass('c-nav-btn-over'); + }; + var mouseOverEvent = function () { + $(this).addClass('c-event-over'); + var d = $(this).attr('data-event-day'); + $('div.c-event-item[data-event-day="' + d + '"]').addClass('c-event-over'); + }; + var mouseLeaveEvent = function () { + $(this).removeClass('c-event-over') + var d = $(this).attr('data-event-day'); + $('div.c-event-item[data-event-day="' + d + '"]').removeClass('c-event-over'); + }; + var mouseOverItem = function () { + $(this).addClass('c-event-over'); + var d = $(this).attr('data-event-day'); + $('div.c-event[data-event-day="' + d + '"]').addClass('c-event-over'); + }; + var mouseLeaveItem = function () { + $(this).removeClass('c-event-over') + var d = $(this).attr('data-event-day'); + $('div.c-event[data-event-day="' + d + '"]').removeClass('c-event-over'); + }; + var nextMonth = function () { + if (dMonth < 11) { + dMonth++; + } else { + dMonth = 0; + dYear++; + } + print(); + }; + var previousMonth = function () { + if (dMonth > 0) { + dMonth--; + } else { + dMonth = 11; + dYear--; + } + print(); + }; + + function loadEvents() { + if (typeof settings.url != 'undefined' && settings.url != '') { + $.ajax({url: settings.url, + async: false, + success: function (result) { + settings.events = result; + } + }); + } + } + + function print() { + loadEvents(); + var dWeekDayOfMonthStart = new Date(dYear, dMonth, 1).getDay() - settings.firstDayOfWeek; + if (dWeekDayOfMonthStart < 0) { + dWeekDayOfMonthStart = 6 - ((dWeekDayOfMonthStart + 1) * -1); + } + var dLastDayOfMonth = new Date(dYear, dMonth + 1, 0).getDate(); + var dLastDayOfPreviousMonth = new Date(dYear, dMonth + 1, 0).getDate() - dWeekDayOfMonthStart + 1; + + var cBody = $('
').addClass('c-grid'); + var cEvents = $('
').addClass('c-event-grid'); + var cEventsBody = $('
').addClass('c-event-body'); + cEvents.append($('
').addClass('c-event-title c-pad-top').html(settings.eventTitle)); + cEvents.append(cEventsBody); + var cNext = $('
').addClass('c-next c-grid-title c-pad-top'); + var cMonth = $('
').addClass('c-month c-grid-title c-pad-top'); + var cPrevious = $('
').addClass('c-previous c-grid-title c-pad-top'); + cPrevious.html(settings.textArrows.previous); + cMonth.html(settings.months[dMonth] + ' ' + dYear); + cNext.html(settings.textArrows.next); + + cPrevious.on('mouseover', mouseOver).on('mouseleave', mouseLeave).on('click', previousMonth); + cNext.on('mouseover', mouseOver).on('mouseleave', mouseLeave).on('click', nextMonth); + + cBody.append(cPrevious); + cBody.append(cMonth); + cBody.append(cNext); + var dayOfWeek = settings.firstDayOfWeek; + for (var i = 0; i < 7; i++) { + if (dayOfWeek > 6) { + dayOfWeek = 0; + } + var cWeekDay = $('
').addClass('c-week-day c-pad-top'); + cWeekDay.html(settings.weekDays[dayOfWeek]); + cBody.append(cWeekDay); + dayOfWeek++; + } + var day = 1; + var dayOfNextMonth = 1; + for (var i = 0; i < 42; i++) { + var cDay = $('
'); + if (i < dWeekDayOfMonthStart) { + cDay.addClass('c-day-previous-month c-pad-top'); + cDay.html(dLastDayOfPreviousMonth++); + } else if (day <= dLastDayOfMonth) { + cDay.addClass('c-day c-pad-top'); + if (day == dDay && adMonth == dMonth && adYear == dYear) { + cDay.addClass('c-today'); + } + for (var j = 0; j < settings.events.length; j++) { + var d = settings.events[j].datetime; + if (d.getDate() == day && d.getMonth() == dMonth && d.getFullYear() == dYear) { + cDay.addClass('c-event').attr('data-event-day', d.getDate()); + cDay.on('mouseover', mouseOverEvent).on('mouseleave', mouseLeaveEvent); + } + } + cDay.html(day++); + } else { + cDay.addClass('c-day-next-month c-pad-top'); + cDay.html(dayOfNextMonth++); + } + cBody.append(cDay); + } + var eventList = $('
').addClass('c-event-list'); + for (var i = 0; i < settings.events.length; i++) { + var d = settings.events[i].datetime; + if (d.getMonth() == dMonth && d.getFullYear() == dYear) { + var date = lpad(d.getDate(), 2) + '/' + lpad(d.getMonth() + 1, 2) + ' ' + lpad(d.getHours(), 2) + ':' + lpad(d.getMinutes(), 2); + var item = $('
').addClass('c-event-item'); + var title = $('
').addClass('title').html(date + ' ' + settings.events[i].title + '
'); + var description = $('
').addClass('description').html(settings.events[i].description + '
'); + item.attr('data-event-day', d.getDate()); + item.on('mouseover', mouseOverItem).on('mouseleave', mouseLeaveItem); + item.append(title).append(description); + eventList.append(item); + } + } + $(instance).addClass('calendar'); + cEventsBody.append(eventList); + $(instance).html(cBody).append(cEvents); + } + + return print(); + } + + $.fn.eCalendar = function (oInit) { + return this.each(function () { + return eCalendar(oInit, $(this)); + }); + }; + + // plugin defaults + $.fn.eCalendar.defaults = { + weekDays: ['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sab'], + months: ['Janeiro', 'Fevereiro', 'Março', 'Abril', 'Maio', 'Junho', 'Julho', 'Agosto', 'Setembro', 'Outubro', 'Novembro', 'Dezembro'], + textArrows: {previous: '<', next: '>'}, + eventTitle: 'Eventos', + url: '', + events: [ + {title: 'Evento de Abertura', description: 'Abertura das Olimpíadas Rio 2016', datetime: new Date(2016, new Date().getMonth(), 12, 17)}, + {title: 'Tênis de Mesa', description: 'BRA x ARG - Semifinal', datetime: new Date(2016, new Date().getMonth(), 23, 16)}, + {title: 'Ginástica Olímpica', description: 'Classificatórias de equipes', datetime: new Date(2016, new Date().getMonth(), 31, 16)} + ], + firstDayOfWeek: 0 + }; + +}(jQuery)); \ No newline at end of file diff --git a/plugins/events/public/style.css b/plugins/events/public/style.css new file mode 100644 index 0000000..1ba3035 --- /dev/null +++ b/plugins/events/public/style.css @@ -0,0 +1,149 @@ +.calendar * { + box-sizing: border-box; + font-size: 12px; + color: #000; +} + +.calendar-sm { + cursor: default; + width: 100%; +} + +.calendar { + cursor: default; + width: 100%; +} + +.calendar-sm .c-pad-top { + padding-top: 2%; +} + +.calendar .c-pad-top { + padding-top: 3%; +} + +.c-grid { + height: 270px; +} + +.c-day { + width: 14.28%; + height: 13%; + background-color: #EEE; + float: left; + text-align: center; +} + +.c-day-previous-month { + width: 14.28%; + height: 13%; + background-color: #CCC; + float: left; + text-align: center; + color: #000; +} + +.c-day-next-month { + width: 14.28%; + height: 13%; + background-color: #CCC; + float: left; + text-align: center; + color: #000; +} + +.c-week-day { + width: 14.28%; + height: 10.38%; + background-color: transparent; + color: #000; + float: left; + text-align: center; + padding-top: 1%; +} + +.c-next { + width: 12.5%; + height: 12%; + padding: 2% 2% 0 2%; + text-align: right; + cursor: pointer; +} + +.c-previous { + width: 12.5%; + height: 12%; + padding: 2% 2% 0 2%; + text-align: left; + cursor: pointer; +} + +.c-month { + width: 75%; + height: 12%; + text-align: center; +} + +.c-nav-btn-over { + background-color: #CCC !important; + font-weight: bold; +} + +.c-today { + background-color: #CCC; + color: #FFF; +} + +.c-event { + background-color: #333; + color: white; + font-weight: bold; + cursor: pointer; +} + +.c-grid { + width: 100%; +} + +.c-event-grid { + margin-left: 1px; + width: 100%; + clear: both; +} + +.c-grid-title { + float: left; + background-color: #EEE; + color: #000; +} + +.c-event-title { + width: 100%; + height: 12%; + text-align: center; + background-color: #FFF; + color: #000; +} + +.c-event-body { + height: 88.1%; +} + +.c-event-item { + padding: 0; + margin: 2px 0; +} + +.c-event-over { + background-color: #EEE; + color: black; +} + +.c-event-item .description { + display: none; +} + +.block .calendar a, +.block .calendar a:visited { + color: #000; +} diff --git a/plugins/events/test/test_helper.rb b/plugins/events/test/test_helper.rb new file mode 100644 index 0000000..70322cf --- /dev/null +++ b/plugins/events/test/test_helper.rb @@ -0,0 +1 @@ +require_relative '../../../test/test_helper' diff --git a/plugins/events/test/unit/events_block_test.rb b/plugins/events/test/unit/events_block_test.rb new file mode 100644 index 0000000..720b7f1 --- /dev/null +++ b/plugins/events/test/unit/events_block_test.rb @@ -0,0 +1,42 @@ +require_relative '../test_helper' + +class EventsBlockTest < ActiveSupport::TestCase + should 'describe itself' do + assert_not_equal Block.description, EventsPlugin::EventsBlock.description + end + + should 'is editable' do + block = EventsPlugin::EventsBlock.new + assert block.editable? + end + + should 'return events' do + profile = create(Profile, name: 'Test') + event1 = create(Event, profile: profile) + event2 = create(Event, profile: profile) + event3 = create(Event, profile: create(Profile, name: 'Other')) + + block = EventsPlugin::EventsBlock.new + block.stubs(:owner).returns(profile) + + assert_equal [event1, event2].map(&:id), block.events.map(&:id) + end +end + +require 'boxes_helper' + +class EventsBlockViewTest < ActionView::TestCase + include BoxesHelper + + should 'return events in api_content' do + profile = create(Profile, name: 'Test') + event1 = create(Event, profile: profile) + event2 = create(Event, profile: profile) + event3 = create(Event, profile: create(Profile, name: 'Other')) + + block = EventsPlugin::EventsBlock.new + block.stubs(:owner).returns(profile) + + assert_equal [event1.id, event2.id], block.api_content[:events].map{ |e| e[:id] } + end +end diff --git a/plugins/events/views/blocks/events.html.erb b/plugins/events/views/blocks/events.html.erb new file mode 100644 index 0000000..a4ecccb --- /dev/null +++ b/plugins/events/views/blocks/events.html.erb @@ -0,0 +1,25 @@ +<%= javascript_include_tag 'plugins/events/jquery.e-calendar' %> +<%= block_title(block.view_title, block.subtitle) %> + +
+ + diff --git a/plugins/events/views/profile_design b/plugins/events/views/profile_design new file mode 120000 index 0000000..1b8d625 --- /dev/null +++ b/plugins/events/views/profile_design @@ -0,0 +1 @@ +box_organizer/ \ No newline at end of file -- libgit2 0.21.2