Skip to content

Commit

Permalink
Item9590: a single calendar UI can work with multiple calendars and e…
Browse files Browse the repository at this point in the history
…vent types.

Item10032: example provided on plugin page for generating a list of attendees in FW 1.1.x
Item10047: updating SVN with latest version before doing a perl build.pl update.
Item10063: forcing jQuery.UI.datepicker to use english

git-svn-id: http://svn.foswiki.org/trunk/FullCalendarPlugin@10034 0b4bb1d4-4e5a-0410-9cc4-b2b747904278
  • Loading branch information
DavidPatterson authored and DavidPatterson committed Nov 22, 2010
1 parent 3110511 commit 71c0d9d
Show file tree
Hide file tree
Showing 14 changed files with 362 additions and 191 deletions.
5 changes: 3 additions & 2 deletions data/System/EventObject.txt
Expand Up @@ -3,13 +3,14 @@
---+ %SPACEOUT{%TOPIC%}%

| *Name:* | *Type:* | *Size:* | *Value:* | *Tooltip message:* | *Attributes:* |
| users | select+names+multi | 5 | %CALGROUPLIST% | | |
| location | text | 100 | | | |
| | label | | When creating your own customisation of this event object, the following parameters *must* be present for the calendar event parser to work properly! | | |
| startDate | text | 10 | | | |
| durationDays | text | 10 | | | |
| startTime | text | 15 | | | |
| endTime | text | 15 | | | |
| users | select+names+multi | 5 | %CALGROUPLIST% | | |
| title | text | 100 | | | |
| location | text | 100 | | | |
| category | select | 1 | mission,meeting,leave,milestone | | |
| repeater | select | 1 | ,YMdd,YMndow,Mdd,Mndow,Wdow,D | | |
| every | select | 1 | ,1,2,3,4,5,6,7,8,9,10 | | |
Expand Down
11 changes: 11 additions & 0 deletions data/System/FullCalendarEditTemplate.txt
@@ -0,0 +1,11 @@
%META:TOPICINFO{author="ProjectContributor" date="1275395213" format="1.1" version="1.1"}%
%META:TOPICPARENT{name="FullCalendarPlugin"}%
%TMPL:INCLUDE{edit}%

%TMPL:DEF{"action_buttons"}%%TMPL:P{"button_save"}% %TMPL:P{"button_quietsave"}% %MAKETEXT{"or"}% %TMPL:P{"button_cancel"}%%TMPL:END%

%TMPL:DEF{"toolbar"}%%TMPL:END%

%TMPL:DEF{content}%%TMPL:P{"formstart"}%%TMPL:P{"form"}%
%TMPL:P{"topicinfo"}%
%TMPL:P{"formend"}%%TMPL:END%
67 changes: 47 additions & 20 deletions data/System/FullCalendarEventTemplate.txt

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions data/System/FullCalendarForm.txt
@@ -0,0 +1,8 @@
%META:TOPICINFO{author="ProjectContributor" date="1268316360" format="1.1" version="1.1"}%
%META:TOPICPARENT{name="FullCalendarPlugin"}%

---+ %SPACEOUT{%TOPIC%}%

| *Name:* | *Type:* | *Size:* | *Value:* | *Tooltip message:* | *Attributes:* |
| TopicType | label | 20 | FullCalendarTopic | | |
| SubscribedCalendars | select+multi | 5 | %SEARCH{"TopicType = 'FullCalendarTopic' OR TopicClassification = 'FullCalendarTopic'" type="query" webs="*" format="$web.$topic" separator="," nonoise="on"}% | | |
65 changes: 40 additions & 25 deletions data/System/FullCalendarPlugin.txt
Expand Up @@ -11,18 +11,23 @@ Adam Shaw has produced a great Web 2.0 Calendar UI, the jQuery !FullCalendar Plu
%TOC%

---++ Features
As well as one-off events, repeating events can be created - daily, weekly, monthly, yearly, every n, every dow, until end-date or no end-date. And exceptions can be created in repeating events.
As well as one-off events, repeating events can be created - daily, weekly, monthly, yearly, every n, every day-of-week, until end-date or no end-date. And exceptions can be created in repeating events.

The events from multiple calendars can be displayed on a single interface, and events can be saved to different calendars from a single interface. This means that a single user or group can define their own calendar that hooks into multiple calendars.

Multiple event types can be defined and selected from when creating a new event. The parameters of the selected type will be those as defined in the web of the selected calendar.

---
---++ The FULLCALENDAR macro
You can have multiple calendars per page and the macro can be called with or without parameters.
|*Parameter*|*Description*|*Default*|
|"..." or calendartopic="..."|Optional. This is the topic where created events will be stored and where they will be searched for when displaying events. The format is "[<web>.]<topic>".|WebCalendar|
|"..." or calendartopic="..."|Optional. A comma separated list of topics which will be searched for events to be displayed. Also defines a select list of calendars on the input form to which a new event can be saved. The format is "[<web>.]<topic>[,[<web>.]<topic>]".|WebCalendar|
|type="..."|Optional. A comma separated list of event types that can be created from the calendar interface. See Customisation below.|event|
|reltopic="..."|Optional. All objects managed by the ObjectPlugin _can_ have a reltopic parameter that points to a related <web>.<topic>, this is so that a single topic can act as the cache for all objects with the aim of helping performance. The !FullCalendarPlugin uses this feature.|If the current topic matches the specification of the calendartopic, then there is no specified reltopic. Else, reltopic is the current topic.|
|viewall="..."|Optional. 1 or 0. Depending on the setting of reltopic, all events stored in the calendartopic can be viewed or just the ones that match the reltopic setting.| |

---++ The Calendar Event Object
The event attributes are defined in the EventObject topic. The default definition is in the =%<nop>SYSTEMWEB%=, but can be overridden by a new !EventObject in the =%<nop>USERSWEB%= and finally by one defined in the current web.
---+++ Subscribing to multiple calendars
There must be many ways that the =calendartopic= parameter can be populated. One example is provided with this installation. The supplied FullCalendarForm defines two fields, !TopicType which is !FullCalendarTopic, and !SubscribedCalendars which provides a list of !FullCalendarTopic topics to choose from when editing. The FullCalendarViewTemplate populates the =calendartopic= parameter using the FORMFIELD macro, and the FullCalendarEditTemplate restricts editing to the form only where calendars can be subscribed to.

---++ Creating and Editing Calendar Events
Just click on a day in the calendar to create a new event via the input form. Click on an existing event to edit its properties via the input form. If the selected event is part of a repeating sequence, the options are to:
Expand All @@ -32,40 +37,49 @@ Just click on a day in the calendar to create a new event via the input form. Cl

*Note* that all newly created events will be done so with the matching =reltopic= setting.

If the =calendartopic= parameter has been defined in the macro, the user has the possibility of choosing the calendar to save to.

If the =type= parameter has been defined in the macro, the user has the possibility of choosing the type of event to create.

All events inherit the access controls of the calendar they belong to which means you may be able to see an event on the calendar but not edit it.

---++ The Calendar Event Object
The event attributes are defined in the EventObject topic. The default definition is in the =%<nop>SYSTEMWEB%=, but can be overridden by a new !EventObject in the =%<nop>USERSWEB%= and ultimately by one defined in the current web. See Customisation below.

---++ Other Event Sources
All Foswiki Macros defined in the =calendartopic= topic are expanded. If they produce suitably formatted event records, these are then included in the displayed event set. *Note that a dynamic search could have a significant impact on performance when browsing the calendar.* The format should be as follows:
All Foswiki Macros defined in the =calendartopic= topic(s) are expanded. If they produce suitably formatted event records, these are then included in the displayed event set. *Note that a dynamic search could have a significant impact on performance when browsing the calendar.* The format should be as follows:
<verbatim>
%OBJECT{...}%<description>%ENDOBJECT%
</verbatim>
|*Parameter* |*Description* |
|type="event" |Required to be recognised as an event object. |
|uid="..."|Required. A unique ID of the event or event group.|
|category="external"|Required as these events will not be editable.|
|allDay="..."|Required. 0 or 1. All day event or not.|
|durationDays="..."|Required. The number of days the event lasts for. Starts at 0.|
|startDate="..."|Required. ISO8601 date format, YYYY-MM-DD.|
|startTime="..."|Required. ISO8601 time format, HH:MM:SS.|
|endTime="..."|Required. ISO8601 time format, HH:MM:SS.|
|title="..."|Required.|
|url="..."|Optional. A URL to launch in a new tab/window if the event is clicked on the calendar.|
|eventSource="..."|Optional. A text string defining the source of the event.|
|*Parameter* |*Description* |*Default* |
|category="external"|Strongly recommended (Required) as these events will not be editable.| |
|allDay="..."|Optional. 1 or 0. All day event or not.|If a startTime is not defined, then 1, else 0. |
|durationDays="..."|Optional. The number of days the event lasts for. Starts at 0.|0 |
|startDate="..."|Required. ISO8601 date format, YYYY-MM-DD.|This is the only hard criteria for an object to appear on a calendar, it must have a =startDate= parameter. |
|startTime="..."|Optional. ISO8601 time format, HH:MM:SS.| 00:00:00 |
|endTime="..."|Optional. ISO8601 time format, HH:MM:SS.| 00:00:00 |
|title="..."|Recommended.| 'no title' |
|url="..."|Optional. A URL to launch in a new tab/window if the event is clicked on the calendar.| |
|eventSource="..."|Optional. A text string defining the source of the event.| |

---++ Customisation
The EventObject and FullCalendarEventTemplate topics are provided as the default installation. View WebCalendar to see the result. As indicated above, you can create your own EventObject with attributes relevant to your installation and in UserCalEventTemplate you can customise the =userFormElements= TMPL to match your new event object.
The EventObject and FullCalendarEventTemplate topics are provided as the default installation. View WebCalendar to see the result. As indicated above, you can create your own EventObject (in the =%<nop>USERSWEB%= for the whole site, or per web) with attributes relevant to your installation and in UserFullCalEventTemplate (in the =%<nop>USERSWEB%= for the whole site, or per web) you can customise the =FormElements:event= TMPL to match your new event object.

You can create multiple object types, e.g. !MyeventObject (note the formatting of the topic name, uppercase first letter and uppercase 'O' of 'Object' only), and then create respective !FormElements templates in UserFullCalEventTemplate, e.g. =FormElements:myevent= . For an event to appear on a calendar, it must have the =startDate= parameter, everything else is optional. See ObjectPlugin for information on defining new objects.

%X% *Note, the default FullCalendarEventTemplate expects the =%USERGROUP%= variable to be set to a Group topic that gets parsed to provide a multi-select list of attendees. You can, off course, change this to be a free text field with auto-complete.*
---+++ User list
The default FullCalendarEventTemplate expects the =%USERGROUP%= variable to be set to a Group topic that gets parsed to provide a multi-select list of attendees. You can, off course, change this to be something else such as a free text field with auto-complete.

%X% Note, if you have a Foswiki 1.1.x installation, you are advised to add the following TMPL:DEF to UserFullCalEventTemplate to override the default template in FullCalendarEventTemplate.
<verbatim>
%TMPL:DEF{apptUserList}%<select multiple size="5" id="appt_users" name="users" class="userlist urlParam" resetmultiple="5">%GROUPINFO{"%USERGROUP%" format="<option value='$wikiname'>$percntSPACEOUT{$quot$wikiname$quot}$percnt" separator=" "}%</select>%TMPL:DEF%
</verbatim>

---++ Future Improvements

---+++ Integration with ActionTrackerPlugin
This feature is already coded but currently commented out because it should be configurable via =configure= .

---+++ Multiple event edit forms per page/web
Currently only one event form is ever loaded per page so if you load different calendars from different webs (which can have different edit forms) it's first come, first served.

---+++ User Specific Calendars
Each user can have their own calendar on which they can view the events of other calendars they are subscribed to.

---+++ Agenda List View
The events displayed on the calendar will also be displayed in a chronological list.

Expand All @@ -80,6 +94,7 @@ The events displayed on the calendar will also be displayed in a chronological l
| Release: | %$RELEASE% |
| Version: | %$VERSION% |
| Change History: | |
| 18 Nov 2010 | Problem caused by localisation. User specific calendars. Multiple event edit forms per page/web. |
| 06 Aug 2010 | First release |
| 15 Apr 2010 | Demo version |
| Dependencies: | %$DEPENDENCIES% |
Expand Down
2 changes: 1 addition & 1 deletion data/System/FullCalendarViewTemplate.txt
Expand Up @@ -3,5 +3,5 @@
%TMPL:INCLUDE{view}%

%TMPL:DEF{content}%
%FULLCALENDAR%
%FULLCALENDAR{%FORMFIELD{"SubscribedCalendars" format="calendartopic=$quot$value$quot"}%}%
%TMPL:END%
1 change: 1 addition & 0 deletions data/System/WebCalendar.txt
Expand Up @@ -2,3 +2,4 @@
%META:TOPICPARENT{name="FullCalendarPlugin"}%

%META:PREFERENCE{name="VIEW_TEMPLATE" title="VIEW_TEMPLATE" type="Set" value="FullCalendarView"}%
%META:PREFERENCE{name="EDIT_TEMPLATE" title="EDIT_TEMPLATE" type="Set" value="FullCalendarEdit"}%
126 changes: 75 additions & 51 deletions lib/Foswiki/Plugins/FullCalendarPlugin.pm
Expand Up @@ -56,33 +56,75 @@ sub initPlugin {
return 1;
};

sub _eventsRESTHandler {
my( $session ) = @_;
return Foswiki::Plugins::ObjectPlugin::objectResponse($session,\&_dateRangeSearch);
}

sub _editEventRESTHandler {
my $session = shift;
return Foswiki::Plugins::ObjectPlugin::objectResponse($session,\&_eventEdit);
}

sub _eventFormRESTHandler {
my $session = shift;
return Foswiki::Plugins::ObjectPlugin::objectResponse($session,\&_getEventForm);
}

sub _getEventForm {
my ($web, $topic, $query) = @_;
my $type = $query->param('eventType');
my $form = $query->param('eventForm');
my $response = {};

my $templates = Foswiki::Func::loadTemplate( 'FullCalendarEvent' );
if ($form) {
$form = Foswiki::Func::expandTemplate( 'eventForm' );
$response->{eventForm} = Foswiki::Func::renderText(Foswiki::Func::expandCommonVariables($form));
}
my $elements = Foswiki::Func::expandTemplate( 'FormElements:'.$type );
unless ($elements) {
$elements = Foswiki::Func::expandTemplate( 'FormElements:event' );
$type = 'event';
}
$response->{elements} =
Foswiki::Func::renderText(Foswiki::Func::expandCommonVariables($elements));
$response->{type} = $type;
return $response;
}

sub _handleFullCalendar {
my( $session, $attrs, $topic, $web ) = @_;

my $calendartopic = $attrs->{_DEFAULT} || $attrs->{calendartopic} || 'WebCalendar';
$calendartopic =~ s/\s*//;
my ($cw, $ct) = Foswiki::Func::normalizeWebTopicName($web, $calendartopic);
$calendartopic = "$cw.$ct";
my @calendars = split(/\s*,\s*/,$calendartopic);
my ($cw, $ct) = ('', '');
foreach (@calendars) {
($cw, $ct) = Foswiki::Func::normalizeWebTopicName($web, $_);
$_ = $cw.'.'.$ct;
}
$calendartopic = join(',',@calendars);
my $reltopic = $attrs->{reltopic} || $topic;
my ($rw, $rt) = Foswiki::Func::normalizeWebTopicName($web, $reltopic);
$reltopic = "$rw.$rt" eq $calendartopic ? '' : "$rw.$rt";
$reltopic = (scalar(@calendars) == 1) && ($rw.'.'.$rt eq $calendartopic) ? '' : $rw.'.'.$rt;
my $viewall = $attrs->{viewall} || '';
my $templates = Foswiki::Func::loadTemplate( 'FullCalendarEvent' );
my $fcpCSS = Foswiki::Func::expandTemplate( 'fcpCSS' );
my $fcpJS = Foswiki::Func::expandTemplate( 'fcpJS' );
my $rand = int rand(999999);
my $calendar = <<"HERE";
<noautolink>
%ADDTOZONE{"head" requires="JQUERYPLUGIN::THEME" text="$fcpCSS"}%
<div id="calendar$rand" class="fcp_calendar"></div>
%ADDTOZONE{"body" requires="HIJAXPLUGIN_JS" text="$fcpJS
my $type = $attrs->{type} || 'event'; # event type
$type = lc($type);
($cw, $ct) = split(/./,$calendars[0]);
Foswiki::Func::addToZone('head','FCP_CSS',$fcpCSS,'JQUERYPLUGIN::THEME');
Foswiki::Func::addToZone('body','FCP_JS',$fcpJS,'HIJAXPLUGIN_JS');
Foswiki::Func::addToZone('body','calendar'.$rand,<<"HERE",'FCP_JS');
<script type='text/javascript'>
jQuery(function(){
foswiki.FullCalendar.init('#calendar$rand','$calendartopic','$reltopic','$viewall');
foswiki.FullCalendar.init('#calendar$rand','$cw','$ct','$type','$calendartopic','$reltopic','$viewall');
});
</script>"}%
</noautolink>
</script>
HERE
my $calendar = '<div id="calendar'.$rand.'" class="fcp_calendar"></div>';
return $calendar;
}

Expand Down Expand Up @@ -139,51 +181,25 @@ sub getMatchingActions {
return $os;
}

sub _eventsRESTHandler {
my( $session ) = @_;
return Foswiki::Plugins::ObjectPlugin::objectResponse($session,\&_dateRangeSearch);
}

sub _editEventRESTHandler {
my $session = shift;
return Foswiki::Plugins::ObjectPlugin::objectResponse($session,\&_eventEdit);
}

sub _eventFormRESTHandler {
my $session = shift;
my $templates = Foswiki::Func::loadTemplate( 'FullCalendarEvent' );
my $form = Foswiki::Func::expandTemplate( 'eventForm' );
return Foswiki::Func::renderText(Foswiki::Func::expandCommonVariables($form));
}

sub _findSingleEvent {
my ( $web, $topic, $uid ) = @_;

my $es = Foswiki::Plugins::FullCalendarPlugin::EventSet::load($web, $topic);

foreach my $event (@{$es->{OBJECTS}}) {
if (ref($event)) {
if ($event->{uid} eq $uid) {
return $event;
}
}
}
return {}; # no match
}

sub _dateRangeSearch {
my ($web, $topic, $query) = @_;

my $start = $query->param('start');
my $end = $query->param('end');
my $reltopic = $query->param('reltopic') || '';
my $calendartopic = $query->param('calendartopic');
($web, $topic) = Foswiki::Func::normalizeWebTopicName($web, $calendartopic);
# writeDebug("$start $end");
$start = Foswiki::Time::formatTime($start, 'iso');
$end = Foswiki::Time::formatTime($end, 'iso');
writeDebug("$start $end $topic");
my $es = Foswiki::Plugins::FullCalendarPlugin::EventSet::dateRangeSearch( $web, $topic, $start, $end, $reltopic );
my $reltopic = $query->param('reltopic') || '';
my $calendartopic = $query->param('calendartopic') || 'WebCalendar';
my @calendars = split(/\s*,\s*/,$calendartopic);
my $es = new Foswiki::Plugins::FullCalendarPlugin::EventSet();
foreach (@calendars) {
my ($cw, $ct) = Foswiki::Func::normalizeWebTopicName($web, $_);
my $events = Foswiki::Plugins::FullCalendarPlugin::EventSet::dateRangeSearch(
$cw, $ct, $start, $end, $reltopic );
$es->concat($events);
}
# writeDebug(Dumper(@{$es->{OBJECTS}}));
my $startObj = parseISOdate($start);
my $endObj = parseISOdate($end);
Expand Down Expand Up @@ -234,9 +250,16 @@ sub _dateRangeSearch {
sub _eventEdit {
my ($web, $topic, $query) = @_;
my $uid = $query->param('uid');
my ($event, $pre, $post, $meta) = Foswiki::Plugins::ObjectPlugin::ObjectSet::loadToFind(
$uid, $web, $topic, undef, 'VIEW', 0, 'Foswiki::Plugins::FullCalendarPlugin::Event' );
# my $event = _findSingleEvent($web, $topic, $uid);
my ($event, $pre, $post, $meta);
my $editable = 1;
try {
($event, $pre, $post, $meta) = Foswiki::Plugins::ObjectPlugin::ObjectSet::loadToFind(
$uid, $web, $topic, undef, 'CHANGE', 0, 'Foswiki::Plugins::FullCalendarPlugin::Event' );
} catch Foswiki::AccessControlException with {
($event, $pre, $post, $meta) = Foswiki::Plugins::ObjectPlugin::ObjectSet::loadToFind(
$uid, $web, $topic, undef, 'VIEW', 0, 'Foswiki::Plugins::FullCalendarPlugin::Event' );
$editable = 0;
};
if (defined $event) {
# expand the found event to match the date on which it was selected in the calendar
# - like a mini dateRangeSearch
Expand All @@ -248,6 +271,7 @@ sub _eventEdit {
my $startObj = parseISOdate($start);
my $endObj = parseISOdate($end);
push(@{$startObj->{ympair}},"$startObj->{Y},$startObj->{M}");
$event->{editable} = $editable;
$event->setFullCalendarAttrs();
my $eventSet = new Foswiki::Plugins::FullCalendarPlugin::EventSet();
$event->expand($eventSet, $startObj, $endObj, $query->param('asobject'));
Expand Down
2 changes: 1 addition & 1 deletion lib/Foswiki/Plugins/FullCalendarPlugin/DEPENDENCIES
@@ -1,3 +1,3 @@
# Dependencies for FullCalendarPlugin
Date::Calc,>=6.13,cpan,Required.
Date::Calc,>=6.3,cpan,Required.
Foswiki::Plugins::ObjectPlugin,>=1.0,perl,Required.

0 comments on commit 71c0d9d

Please sign in to comment.