Commit b94badef159ebcf5b2887357668a037494432765

Authored by Caio Almeida
Committed by Leandro Santos
1 parent 2baaf088
Exists in staging

Ticket #117: Adding a Events plugin that exposes a calendar block

plugins/events/lib/events_plugin.rb 0 → 100644
... ... @@ -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
... ...
plugins/events/lib/events_plugin/events_block.rb 0 → 100644
... ... @@ -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
... ...
plugins/events/public/jquery.e-calendar.js 0 → 100644
... ... @@ -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
... ...
plugins/events/public/style.css 0 → 100644
... ... @@ -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 +}
... ...
plugins/events/test/test_helper.rb 0 → 100644
... ... @@ -0,0 +1 @@
  1 +require_relative '../../../test/test_helper'
... ...
plugins/events/test/unit/events_block_test.rb 0 → 100644
... ... @@ -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
... ...
plugins/events/views/blocks/events.html.erb 0 → 100644
... ... @@ -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>
... ...
plugins/events/views/profile_design 0 → 120000
... ... @@ -0,0 +1 @@
  1 +box_organizer/
0 2 \ No newline at end of file
... ...