todo: calender choosing, timezone, improve parser

This commit is contained in:
Christoph Haas 2012-11-12 22:57:50 +00:00
parent d0544409ec
commit 2008e71e4a
8 changed files with 1011 additions and 19 deletions

View File

@ -24,12 +24,10 @@ Zarafa.plugins.calendarimporter.dialogs.ImportContentPanel = Ext.extend(Zarafa.c
width : 400, width : 400,
height : 300, height : 300,
//Add panel //Add panel
items : [/* items : [
{ {
xtype : 'owncloudrcvattachment.folderpanel', xtype : 'calendarimporter.importpanel'
ref : 'treePanel', }
response : config.record
}*/
] ]
}); });
@ -38,4 +36,4 @@ Zarafa.plugins.calendarimporter.dialogs.ImportContentPanel = Ext.extend(Zarafa.c
}); });
Ext.reg('calendarimportercontentpanel' ,Zarafa.plugins.calendarimporter.dialogs.ImportContentPanel); Ext.reg('calendarimporter.contentpanel' ,Zarafa.plugins.calendarimporter.dialogs.ImportContentPanel);

299
js/dialogs/ImportPanel.js Normal file
View File

@ -0,0 +1,299 @@
Ext.namespace("Zarafa.plugins.calendarimporter.dialogs");
/**
* @class Zarafa.plugins.calendarimporter.dialogs.ImportPanel
* @extends Ext.form.FormPanel
*/
Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.form.FormPanel, {
/**
* @constructor
* @param {object} config
*/
constructor : function(config)
{
config = config || {};
var self = this;
Ext.apply(config, {
xtype : 'calendarimporter.importpanel',
layout : {
type : 'form',
align : 'stretch'
},
anchor : '100%',
bodyStyle : 'background-color: inherit;',
defaults : {
border : true,
bodyStyle : 'background-color: inherit; padding: 3px 0px 3px 0px; border-style: none none solid none;'
},
items : [
this.createSelectBox(),
this.initForm()
],
buttons: [
this.createSubmitButton(),
this.createCancelButton()
]
});
Zarafa.plugins.calendarimporter.dialogs.ImportPanel.superclass.constructor.call(this, config);
},
/**
* Init embedded form, this is the form that is
* posted and contains the attachments
* @private
*/
initForm : function()
{
return {
xtype: 'form',
ref: 'addFormPanel',
layout : 'column',
fileUpload: true,
autoWidth: true,
autoHeight: true,
border: false,
bodyStyle: 'padding: 5px;',
defaults: {
anchor: '95%',
border: false,
bodyStyle: 'padding: 5px;'
},
items: [this.createUploadField()]
};
},
/**
* Init embedded form, this is the form that is
* posted and contains the attachments
* @private
*/
createGrid : function(eventdata) {
if(eventdata == null) {
var parsedData = [
];
} else {
var parsedData = new Array(eventdata.events.length);
for(var i=0; i < eventdata.events.length; i++) {
parsedData[i] = new Array(eventdata.events[i]["SUMMARY"], parseInt(eventdata.events[i]["DTSTART"]), parseInt(eventdata.events[i]["DTEND"]), eventdata.events[i]["LOCATION"], eventdata.events[i]["DESCRIPTION"]);
}
}
// create the data store
var store = new Ext.data.ArrayStore({
fields: [
{name: 'title'},
{name: 'start'},
{name: 'end'},
{name: 'location'},
{name: 'description'}
],
data: parsedData
});
return {
xtype: 'grid',
ref: 'eventgrid',
id: 'eventgrid',
columnWidth: 1.0,
store: store,
width: '100%',
height: 300,
title: 'Select events to import',
frame: true,
colModel: new Ext.grid.ColumnModel({
defaults: {
width: 300,
sortable: true
},
columns: [
{id: 'title', header: 'Title', width: 300, sortable: true, dataIndex: 'title'},
{header: 'startDate', width: 150, sortable: true, dataIndex: 'start'},
{header: 'endDate', width: 150, sortable: true, dataIndex: 'end'},
{header: 'startDate', width: 150, sortable: true, dataIndex: 'location'},
{header: 'endDate', width: 150, sortable: true, dataIndex: 'description'}
]
}),
sm: new Ext.grid.RowSelectionModel({multiSelect:true})
}
},
createSelectBox: function() {
ctx = container.getContextByName('calendar');
model = ctx.getModel();
defaultFolder = model.getDefaultFolder();
subFolders = defaultFolder.getChildren();
var myStore = new Ext.data.ArrayStore({
fields: ['calendar_id', 'calendar_displayname'],
idIndex: 0 // id for each record will be the first element
});
/* Calendar Record holds the name and real name of the calender */
var CalendarRecord = Ext.data.Record.create([
{name: 'realname', type: "string"},
{name: 'displayname', type: "string"}
]);
/* Store the default folder */
var myNewRecord = new CalendarRecord({
realname: defaultFolder.getDefaultFolderKey(),
displayname: defaultFolder.getDisplayName()
});
myStore.add(myNewRecord);
for(i=0;i<subFolders.length;i++) {
/* Store all subfolders */
myNewRecord = new CalendarRecord({
realname: subFolders[i].getDisplayName(), // TODO: get the real path...
displayname: subFolders[i].getDisplayName()
});
myStore.add(myNewRecord);
}
/* commit the changes to the store */
myStore.commitChanges();
return {
xtype: "selectbox",
editable: false,
name: "choosen_calendar",
width: 100,
fieldLabel: "Select a calender",
store: myStore,
valueField: 'realname',
displayField: 'displayname',
labelSeperator: ":",
border: false,
anchor: "100%",
scope: this,
allowBlank: false
}
},
createUploadField: function() {
return {
xtype: "fileuploadfield",
ref: 'fileuploadfield',
columnWidth: 1.0,
id: 'form-file',
name: 'icsdata',
emptyText: 'Select an .ics calendar',
border: false,
anchor: "100%",
scope: this,
allowBlank: false,
listeners: {
'fileselected': this.onFileSelected,
scope: this
}
}
},
createSubmitButton: function() {
return {
xtype: "button",
ref: "submitButton",
id: "submitButton",
disabled: true,
width: 100,
border: false,
text: _("Import"),
anchor: "100%",
handler: this.importAllEvents,
scope: this,
allowBlank: false
}
},
createCancelButton: function() {
return {
xtype: "button",
width: 100,
border: false,
text: _("Cancel"),
anchor: "100%",
handler: this.close,
scope: this,
allowBlank: false
}
},
/**
* This is called when a file has been seleceted in the file dialog
* in the {@link Ext.ux.form.FileUploadField} and the dialog is closed
* @param {Ext.ux.form.FileUploadField} uploadField being added a file to
*/
onFileSelected : function(uploadField) {
var form = this.addFormPanel.getForm();
if (form.isValid()) {
form.submit({
waitMsg: 'Uploading and parsing calendar...',
url: 'plugins/calendarimporter/php/upload.php',
failure: function(file, action) {
Ext.getCmp('submitButton').disable(); // momstly called...
Ext.MessageBox.show({
title : _('Error'),
msg : _(action.result.errors[action.result.errors.type]),
icon : Ext.MessageBox.ERROR,
buttons : Ext.MessageBox.OK
});
},
success: function(file, action){
uploadField.reset();
Ext.getCmp('submitButton').enable();
this.insert(this.items.length,this.createGrid(action.result.response));
this.doLayout();
},
scope : this
});
}
},
close: function () {
this.addFormPanel.getForm().reset();
this.getForm().reset();
this.dialog.close()
},
convertToAppointmentRecord: function (calendarFolder,entry) {
var newRecord = Zarafa.core.data.RecordFactory.createRecordObjectByMessageClass('IPM.Appointment', {
startdate: new Date(entry.start),
duedate: (entry.end) ?
new Date(entry.end) :
new Date(entry.start).add(Date.HOUR, 1),
location: entry.location,
subject: entry.title,
body: entry.description,
commonstart: new Date(entry.start),
commonend: (entry.end) ?
new Date(entry.end) :
new Date(entry.start).add(Date.HOUR, 1),
parent_entryid: calendarFolder.get('entryid'),
store_entryid: calendarFolder.get('store_entryid')
});
return newRecord;
},
importAllEvents: function () {
//receive existing calendar store
var calendarStore = new Zarafa.calendar.AppointmentStore();
var calendarFolder = container.getHierarchyStore().getDefaultFolder('calendar');
//receive Records from grid rows
var newRecords = this.eventgrid.selModel.getSelections();
Ext.each(newRecords, function(newRecord) {
var record = this.convertToAppointmentRecord(calendarFolder,newRecord.data);
calendarStore.add(record);
calendarStore.save();
}, this);
this.dialog.close();
}
});
Ext.reg('calendarimporter.importpanel', Zarafa.plugins.calendarimporter.dialogs.ImportPanel);

View File

@ -11,16 +11,16 @@ Zarafa.plugins.calendarimporter.ImportPlugin = Ext.extend(Zarafa.core.Plugin, {
Zarafa.plugins.calendarimporter.ImportPlugin.superclass.constructor.call(this, config); Zarafa.plugins.calendarimporter.ImportPlugin.superclass.constructor.call(this, config);
Zarafa.core.data.SharedComponentType.addProperty('plugins.calendarimporter.dialogs.importevents');
}, },
/** /**
* initialises insertion point for plugin * initialises insertion point for plugin
* @protected * @protected
*/ */
initPlugin : function() initPlugin : function() {
{ Zarafa.plugins.calendarimporter.ImportPlugin.superclass.initPlugin.apply(this, arguments);
Zarafa.plugins.calendarimporter.ImportPlugin.superclass.initPlugin.apply(this, arguments); Zarafa.core.data.SharedComponentType.addProperty('plugins.calendarimporter.dialogs.importevents');
/* add import button to south navigation */ /* add import button to south navigation */
this.registerInsertionPoint("navigation.south", this.createImportButton, this); this.registerInsertionPoint("navigation.south", this.createImportButton, this);
@ -32,22 +32,24 @@ Zarafa.plugins.calendarimporter.ImportPlugin = Ext.extend(Zarafa.core.Plugin, {
* @return {Object} Configuration object for a {@link Ext.Button button} * @return {Object} Configuration object for a {@link Ext.Button button}
* @private * @private
*/ */
createImportButton: function () { // eine Button definition createImportButton: function () {
return { var button=
xtype : "button", {
text : _("Import Calendar"), xtype : 'button',
iconCls : "icon_calendarimporter_button", text : _('Import Calendar'),
navigationContext : container.getContextByName("calendar"), iconCls : 'icon_calendarimporter_button',
navigationContext : container.getContextByName('calendar'),
handler : this.onImportButtonClick, handler : this.onImportButtonClick,
scope : this scope : this
} }
return button;
}, },
/** /**
* Clickhandler for the button * Clickhandler for the button
*/ */
onImportButtonClick: function () { onImportButtonClick: function () {
Zarafa.core.data.UIFactory.openCreateRecord(Zarafa.core.data.SharedComponentType['plugins.calendarimporter.dialogs.importevents'], undefined, { Zarafa.core.data.UIFactory.openLayerComponent(Zarafa.core.data.SharedComponentType['plugins.calendarimporter.dialogs.importevents'], undefined, {
manager : Ext.WindowMgr manager : Ext.WindowMgr
}); });
}, },
@ -63,8 +65,6 @@ Zarafa.plugins.calendarimporter.ImportPlugin = Ext.extend(Zarafa.core.Plugin, {
bidSharedComponent : function(type, record) bidSharedComponent : function(type, record)
{ {
var bid = -1; var bid = -1;
console.log(type);
console.log(Zarafa.core.data.SharedComponentType['plugins.calendarimporter.dialogs.importevents']);
switch(type) switch(type)
{ {
case Zarafa.core.data.SharedComponentType['plugins.calendarimporter.dialogs.importevents']: case Zarafa.core.data.SharedComponentType['plugins.calendarimporter.dialogs.importevents']:

View File

@ -21,9 +21,11 @@
<client> <client>
<clientfile load="release">js/plugin.calendarimporter.js</clientfile> <clientfile load="release">js/plugin.calendarimporter.js</clientfile>
<clientfile load="release">js/dialogs/ImportContentPanel.js</clientfile> <clientfile load="release">js/dialogs/ImportContentPanel.js</clientfile>
<clientfile load="release">js/dialogs/ImportPanel.js</clientfile>
<clientfile load="debug">js/plugin.calendarimporter.js</clientfile> <clientfile load="debug">js/plugin.calendarimporter.js</clientfile>
<clientfile load="debug">js/dialogs/ImportContentPanel.js</clientfile> <clientfile load="debug">js/dialogs/ImportContentPanel.js</clientfile>
<clientfile load="debug">js/dialogs/ImportPanel.js</clientfile>
</client> </client>
<resources> <resources>
<resourcefile load="release">resources/css/calendarimporter.css</resourcefile> <resourcefile load="release">resources/css/calendarimporter.css</resourcefile>

View File

@ -0,0 +1,216 @@
BEGIN:VCALENDAR
PRODID:-//Google Inc//Google Calendar 70.9054//EN
VERSION:2.0
CALSCALE:GREGORIAN
METHOD:PUBLISH
X-WR-CALNAME:Testkalender
X-WR-TIMEZONE:Europe/Berlin
X-WR-CALDESC:Nur zum testen vom Google Kalender
BEGIN:VEVENT
DTSTART:20121105T090000Z
DTEND:20121107T173000Z
DTSTAMP:20110121T195741Z
UID:15lc1nvupht8dtfiptenljoiv4@google.com
CREATED:20110121T195616Z
DESCRIPTION:This is a short description\nwith a new line. Some "special" 's
igns' may be interesting, too.
LAST-MODIFIED:20110121T195729Z
LOCATION:Kansas
SEQUENCE:2
STATUS:CONFIRMED
SUMMARY:My Holidays
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTART;VALUE=DATE:20110112
DTEND;VALUE=DATE:20110116
DTSTAMP:20110121T195741Z
UID:1koigufm110c5hnq6ln57murd4@google.com
CREATED:20110119T142901Z
DESCRIPTION:Project xyz Review Meeting Minutes\n
Agenda\n1. Review of project version 1.0 requirements.\n2.
Definition
of project processes.\n3. Review of project schedule.\n
Participants: John Smith, Jane Doe, Jim Dandy\n-It was
decided that the requirements need to be signed off by
product marketing.\n-Project processes were accepted.\n
-Project schedule needs to account for scheduled holidays
and employee vacation time. Check with HR for specific
dates.\n-New schedule will be distributed by Friday.\n-
Next weeks meeting is cancelled. No meeting until 3/23.
LAST-MODIFIED:20110119T152216Z
LOCATION:
SEQUENCE:2
STATUS:CONFIRMED
SUMMARY:test 11
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTART;VALUE=DATE:20110118
DTEND;VALUE=DATE:20110120
DTSTAMP:20110121T195741Z
UID:4dnsuc3nknin15kv25cn7ridss@google.com
CREATED:20110119T142059Z
DESCRIPTION:
LAST-MODIFIED:20110119T142106Z
LOCATION:
SEQUENCE:0
STATUS:CONFIRMED
SUMMARY:test 9
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTART;VALUE=DATE:20110117
DTEND;VALUE=DATE:20110122
DTSTAMP:20110121T195741Z
UID:h6f7sdjbpt47v3dkral8lnsgcc@google.com
CREATED:20110119T142040Z
DESCRIPTION:
LAST-MODIFIED:20110119T142040Z
LOCATION:
SEQUENCE:0
STATUS:CONFIRMED
SUMMARY:
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTART;VALUE=DATE:20110117
DTEND;VALUE=DATE:20110118
DTSTAMP:20110121T195741Z
UID:up56hlrtkpqdum73rk6tl10ook@google.com
CREATED:20110119T142034Z
DESCRIPTION:
LAST-MODIFIED:20110119T142034Z
LOCATION:
SEQUENCE:0
STATUS:CONFIRMED
SUMMARY:test 8
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTART;VALUE=DATE:20110118
DTEND;VALUE=DATE:20110120
DTSTAMP:20110121T195741Z
UID:8ltm205uhshsbc1huv0ooeg4nc@google.com
CREATED:20110119T142014Z
DESCRIPTION:
LAST-MODIFIED:20110119T142023Z
LOCATION:
SEQUENCE:0
STATUS:CONFIRMED
SUMMARY:test 7
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTART;VALUE=DATE:20110119
DTEND;VALUE=DATE:20110121
DTSTAMP:20110121T195741Z
UID:opklai3nm8enffdf5vpna4o5fo@google.com
CREATED:20110119T141918Z
DESCRIPTION:
LAST-MODIFIED:20110119T142005Z
LOCATION:
SEQUENCE:0
STATUS:CONFIRMED
SUMMARY:test 5
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTART;VALUE=DATE:20110119
DTEND;VALUE=DATE:20110120
DTSTAMP:20110121T195741Z
UID:kmbj764g57tcvua11hir61c4b8@google.com
CREATED:20110119T141923Z
DESCRIPTION:
LAST-MODIFIED:20110119T141923Z
LOCATION:
SEQUENCE:0
STATUS:CONFIRMED
SUMMARY:test 6
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTART;VALUE=DATE:20110119
DTEND;VALUE=DATE:20110120
DTSTAMP:20110121T195741Z
UID:shvr7hvqdag08vjqlmj5lj0i2s@google.com
CREATED:20110119T141913Z
DESCRIPTION:
LAST-MODIFIED:20110119T141913Z
LOCATION:
SEQUENCE:0
STATUS:CONFIRMED
SUMMARY:test 4
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTART;VALUE=DATE:20110119
DTEND;VALUE=DATE:20110120
DTSTAMP:20110121T195741Z
UID:77gpemlb9es0r0gtjolv3mtap0@google.com
CREATED:20110119T141909Z
DESCRIPTION:
LAST-MODIFIED:20110119T141909Z
LOCATION:
SEQUENCE:0
STATUS:CONFIRMED
SUMMARY:test 3
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTART;VALUE=DATE:20110119
DTEND;VALUE=DATE:20110120
DTSTAMP:20110121T195741Z
UID:rq8jng4jgq0m1lvpj8486fttu0@google.com
CREATED:20110119T141904Z
DESCRIPTION:
LAST-MODIFIED:20110119T141904Z
LOCATION:
SEQUENCE:0
STATUS:CONFIRMED
SUMMARY:test 2
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTART;VALUE=DATE:20110119
DTEND;VALUE=DATE:20110120
DTSTAMP:20110121T195741Z
UID:dh3fki5du0opa7cs5n5s87ca00@google.com
CREATED:20110119T141901Z
DESCRIPTION:
LAST-MODIFIED:20110119T141901Z
LOCATION:
SEQUENCE:0
STATUS:CONFIRMED
SUMMARY:test 1
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTART;VALUE=DATE:20400201
DTEND;VALUE=DATE:20400202
DTSTAMP:20400101T195741Z
UID:dh3fki5du0opa7cs5n5s87ca01@google.com
CREATED:20400101T141901Z
DESCRIPTION:
LAST-MODIFIED:20400101T141901Z
LOCATION:
SEQUENCE:0
STATUS:CONFIRMED
SUMMARY:Year 2038 problem test
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTART;VALUE=DATE:19410512
DTEND;VALUE=DATE:19410512
DTSTAMP:19410512T195741Z
UID:dh3fki5du0opa7cs5n5s87ca02@google.com
CREATED:20400101T141901Z
DESCRIPTION:
LAST-MODIFIED:20400101T141901Z
LOCATION:
SEQUENCE:0
STATUS:CONFIRMED
SUMMARY:Before 1970-Test: Konrad Zuse invents the Z3, the first digital Computer
TRANSP:TRANSPARENT
END:VEVENT
END:VCALENDAR

33
php/ical/calendar.txt Normal file
View File

@ -0,0 +1,33 @@
BEGIN:VCALENDAR
PRODID:-//Google Inc//Google Calendar 70.9054//EN
VERSION:2.0
CALSCALE:GREGORIAN
METHOD:PUBLISH
X-WR-CALNAME:Testkalender
X-WR-TIMEZONE:Europe/Berlin
X-WR-CALDESC:Nur zum testen vom Google Kalender
BEGIN:VEVENT
DTSTART;VALUE=DATE:20121112
DTEND;VALUE=DATE:20121116
DTSTAMP:20110121T195741Z
UID:1koigufm110c5hnq6ln57murd4@google.com
CREATED:20110119T142901Z
DESCRIPTION:Project xyz Review Meeting Minutes\n
Agenda\n1. Review of project version 1.0 requirements.\n2.
Definition
of project processes.\n3. Review of project schedule.\n
Participants: John Smith, Jane Doe, Jim Dandy\n-It was
decided that the requirements need to be signed off by
product marketing.\n-Project processes were accepted.\n
-Project schedule needs to account for scheduled holidays
and employee vacation time. Check with HR for specific
dates.\n-New schedule will be distributed by Friday.\n-
Next weeks meeting is cancelled. No meeting until 3/23.
LAST-MODIFIED:20110119T152216Z
LOCATION:
SEQUENCE:2
STATUS:CONFIRMED
SUMMARY:test 11
TRANSP:TRANSPARENT
END:VEVENT
END:VCALENDAR

View File

@ -0,0 +1,410 @@
<?php
/**
* Parse ics file content to array.
*
* PHP Version 5
*
* @category Parser
* @author Christoph Haas <mail@h44z.net>
* @license http://www.opensource.org/licenses/mit-license.php MIT License
* @version SVN: 13
* @example $ical = new ical('MyCal.ics');
* print_r( $ical->events() );
*/
/**
* This is the iCal-class
*
* @param {string} filename The name of the file which should be parsed
* @constructor
*/
class ICal
{
/* How many ToDos are in this ical? */
public /** @type {int} */ $todo_count = 0;
/* How many events are in this ical? */
public /** @type {int} */ $event_count = 0;
/* The parsed calendar */
public /** @type {Array} */ $cal;
/* Error message store... null default */
public /** @type {String} */ $errors;
/* Which keyword has been added to cal at last? */
private /** @type {string} */ $_lastKeyWord;
/**
* Creates the iCal-Object
*
* @param {string} $filename The path to the iCal-file
*
* @return Object The iCal-Object
*/
public function __construct($filename)
{
if (!$filename) {
$this->errors = "No filename specified";
return false;
}
$lines = file($filename, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
if (stristr($lines[0], 'BEGIN:VCALENDAR') === false) {
$this->errors = "Not a valid ical file";
return false;
} else {
// TODO: Fix multiline-description problem (see http://tools.ietf.org/html/rfc2445#section-4.8.1.5)
foreach ($lines as $line) {
$line = trim($line);
$add = $this->keyValueFromString($line);
if ($add === false) {
$this->addCalendarComponentWithKeyAndValue($type, false, $line);
continue;
}
list($keyword, $value) = $add;
switch ($line) {
// http://www.kanzaki.com/docs/ical/vtodo.html
case "BEGIN:VTODO":
$this->todo_count++;
$type = "VTODO";
break;
// http://www.kanzaki.com/docs/ical/vevent.html
case "BEGIN:VEVENT":
//echo "vevent gematcht";
$this->event_count++;
$type = "VEVENT";
break;
//all other special strings
case "BEGIN:VCALENDAR":
case "BEGIN:DAYLIGHT":
// http://www.kanzaki.com/docs/ical/vtimezone.html
case "BEGIN:VTIMEZONE":
case "BEGIN:STANDARD":
$type = $value;
break;
case "END:VTODO": // end special text - goto VCALENDAR key
case "END:VEVENT":
case "END:VCALENDAR":
case "END:DAYLIGHT":
case "END:VTIMEZONE":
case "END:STANDARD":
$type = "VCALENDAR";
break;
default:
$this->addCalendarComponentWithKeyAndValue($type,
$keyword,
$value);
break;
}
}
return $this->cal;
}
}
/**
* Creates the iCal-Object
*
* @param {string} $filecontent The content of the iCal-file
*
* @return Object The iCal-Object
*/
public function setContent($filecontent)
{
if (!$filecontent) {
$this->errors = "No filecontent";
return false;
}
$lines = explode("\n", $filecontent);
if (stristr($lines[0], 'BEGIN:VCALENDAR') === false) {
$this->errors = "Not a valid ical file";
return false;
} else {
// TODO: Fix multiline-description problem (see http://tools.ietf.org/html/rfc2445#section-4.8.1.5)
foreach ($lines as $line) {
$line = trim($line);
$add = $this->keyValueFromString($line);
if ($add === false) {
$this->addCalendarComponentWithKeyAndValue($type, false, $line);
continue;
}
list($keyword, $value) = $add;
switch ($line) {
// http://www.kanzaki.com/docs/ical/vtodo.html
case "BEGIN:VTODO":
$this->todo_count++;
$type = "VTODO";
break;
// http://www.kanzaki.com/docs/ical/vevent.html
case "BEGIN:VEVENT":
//echo "vevent gematcht";
$this->event_count++;
$type = "VEVENT";
break;
//all other special strings
case "BEGIN:VCALENDAR":
case "BEGIN:DAYLIGHT":
// http://www.kanzaki.com/docs/ical/vtimezone.html
case "BEGIN:VTIMEZONE":
case "BEGIN:STANDARD":
$type = $value;
break;
case "END:VTODO": // end special text - goto VCALENDAR key
case "END:VEVENT":
case "END:VCALENDAR":
case "END:DAYLIGHT":
case "END:VTIMEZONE":
case "END:STANDARD":
$type = "VCALENDAR";
break;
default:
$this->addCalendarComponentWithKeyAndValue($type,
$keyword,
$value);
break;
}
}
return $this->cal;
}
}
/**
* Add to $this->ical array one value and key.
*
* @param {string} $component This could be VTODO, VEVENT, VCALENDAR, ...
* @param {string} $keyword The keyword, for example DTSTART
* @param {string} $value The value, for example 20110105T090000Z
*
* @return {None}
*/
public function addCalendarComponentWithKeyAndValue($component,
$keyword,
$value)
{
if ($keyword == false) {
$keyword = $this->last_keyword;
switch ($component) {
case 'VEVENT':
if (stristr($keyword, "DTSTART") or stristr($keyword, "DTEND")) {
$ts = $this->iCalDateToUnixTimestamp($value);
$value = $ts * 1000;
}
$value = str_replace("\\n", "\n", $value);
$value = $this->cal[$component][$this->event_count - 1]
[$keyword].$value;
break;
case 'VTODO' :
$value = $this->cal[$component][$this->todo_count - 1]
[$keyword].$value;
break;
}
}
if (stristr($keyword, "DTSTART") or stristr($keyword, "DTEND")) {
$keyword = explode(";", $keyword);
$keyword = $keyword[0]; // remove additional content like VALUE=DATE
}
switch ($component) {
case "VTODO":
$this->cal[$component][$this->todo_count - 1][$keyword] = $value;
//$this->cal[$component][$this->todo_count]['Unix'] = $unixtime;
break;
case "VEVENT":
if (stristr($keyword, "DTSTART") or stristr($keyword, "DTEND")) {
$ts = $this->iCalDateToUnixTimestamp($value);
$value = $ts * 1000;
}
$value = str_replace("\\n", "\n", $value);
$this->cal[$component][$this->event_count - 1][$keyword] = $value;
break;
default:
$this->cal[$component][$keyword] = $value;
break;
}
$this->last_keyword = $keyword;
}
/**
* Get a key-value pair of a string.
*
* @param {string} $text which is like "VCALENDAR:Begin" or "LOCATION:"
*
* @return {array} array("VCALENDAR", "Begin")
*/
public function keyValueFromString($text)
{
preg_match("/(^[^a-z:]+)[:]([\w\W]*)/", $text, $matches);
error_log("Matching: " . count($matches) . " " . $text);
if (count($matches) == 0) {
return false;
}
$matches = array_splice($matches, 1, 2);
return $matches;
}
/**
* Return Unix timestamp from ical date time format
*
* @param {string} $icalDate A Date in the format YYYYMMDD[T]HHMMSS[Z] or
* YYYYMMDD[T]HHMMSS
*
* @return {int}
*/
public function iCalDateToUnixTimestamp($icalDate)
{
$icalDate = str_replace('T', '', $icalDate);
$icalDate = str_replace('Z', '', $icalDate);
$pattern = '/([0-9]{4})'; // 1: YYYY
$pattern .= '([0-9]{2})'; // 2: MM
$pattern .= '([0-9]{2})'; // 3: DD
$pattern .= '([0-9]{0,2})'; // 4: HH
$pattern .= '([0-9]{0,2})'; // 5: MM
$pattern .= '([0-9]{0,2})/'; // 6: SS
preg_match($pattern, $icalDate, $date);
// Unix timestamp can't represent dates before 1970
if ($date[1] <= 1970) {
return false;
}
// Unix timestamps after 03:14:07 UTC 2038-01-19 might cause an overflow
// if 32 bit integers are used.
$timestamp = mktime((int)$date[4],
(int)$date[5],
(int)$date[6],
(int)$date[2],
(int)$date[3],
(int)$date[1]);
return $timestamp;
}
/**
* Returns an array of arrays with all events. Every event is an associative
* array and each property is an element it.
*
* @return {array}
*/
public function events()
{
$array = $this->cal;
return $array['VEVENT'];
}
/**
* Returns an array of calendar types.
*
* @return {array}
*/
public function calendar()
{
$array = $this->cal;
return $array['VCALENDAR'];
}
/**
* Returns a boolean value whether thr current calendar has events or not
*
* @return {boolean}
*/
public function hasEvents()
{
return ( count($this->events()) > 0 ? true : false );
}
/**
* Returns false when the current calendar has no events in range, else the
* events.
*
* Note that this function makes use of a UNIX timestamp. This might be a
* problem on January the 29th, 2038.
* See http://en.wikipedia.org/wiki/Unix_time#Representing_the_number
*
* @param {boolean} $rangeStart Either true or false
* @param {boolean} $rangeEnd Either true or false
*
* @return {mixed}
*/
public function eventsFromRange($rangeStart = false, $rangeEnd = false)
{
$events = $this->sortEventsWithOrder($this->events(), SORT_ASC);
if (!$events) {
return false;
}
$extendedEvents = array();
if ($rangeStart !== false) {
$rangeStart = new DateTime();
}
if ($rangeEnd !== false or $rangeEnd <= 0) {
$rangeEnd = new DateTime('2038/01/18');
} else {
$rangeEnd = new DateTime($rangeEnd);
}
$rangeStart = $rangeStart->format('U');
$rangeEnd = $rangeEnd->format('U');
// loop through all events by adding two new elements
foreach ($events as $anEvent) {
$timestamp = $this->iCalDateToUnixTimestamp($anEvent['DTSTART']);
if ($timestamp >= $rangeStart && $timestamp <= $rangeEnd) {
$extendedEvents[] = $anEvent;
}
}
return $extendedEvents;
}
/**
* Returns sorted events
*
* @param {array} $events An array with events.
* @param {array} $sortOrder Either SORT_ASC, SORT_DESC, SORT_REGULAR,
* SORT_NUMERIC, SORT_STRING
*
* @return {array}
*/
public function sortEventsWithOrder($events, $sortOrder = SORT_ASC)
{
$extendedEvents = array();
// loop through all events by adding two new elements
foreach ($events as $anEvent) {
if (!array_key_exists('UNIX_TIMESTAMP', $anEvent)) {
$anEvent['UNIX_TIMESTAMP'] =
$this->iCalDateToUnixTimestamp($anEvent['DTSTART']);
}
if (!array_key_exists('REAL_DATETIME', $anEvent)) {
$anEvent['REAL_DATETIME'] =
date("d.m.Y", $anEvent['UNIX_TIMESTAMP']);
}
$extendedEvents[] = $anEvent;
}
foreach ($extendedEvents as $key => $value) {
$timestamp[$key] = $value['UNIX_TIMESTAMP'];
}
array_multisort($timestamp, $sortOrder, $extendedEvents);
return $extendedEvents;
}
}
?>

34
php/upload.php Normal file
View File

@ -0,0 +1,34 @@
<?php
/**
* Handle the upload request from the gui
*
* PHP Version 5
*
* @author Christoph Haas <mail@h44z.net>
* @license http://www.opensource.org/licenses/mit-license.php MIT License
* @version SVN: 13
*/
function respondJSON($arr) {
echo json_encode($arr);
}
require_once("ical/class.icalparser.php");
$filepath = $_FILES['icsdata']['tmp_name'];
if(is_readable ($filepath)) {
$ical = new ICal($filepath); // do not init with a file.. we set the content later
if(isset($ical->errors)) {
respondJSON(array ('success'=>false,'errors'=>array ('parser'=>$ical->errors, 'type'=>'parser')));
} else if(!$ical->hasEvents()) {
respondJSON(array ('success'=>false,'errors'=>array ('parser'=>"No events in ics file", 'type'=>'parser')));
} else {
respondJSON(array ('success'=>true, 'response'=>array ('tmp_file'=>$filepath, 'calendar'=>$ical->calendar(), 'events'=>$ical->events())));
}
} else {
respondJSON(array ('success'=>false,'errors'=>array ('reader'=>"File could not be read by server", 'type'=>'reader')));
}
?>