calendarimporter 1.2 pre:

- New timezone management
 - more imported fields (Busystatus, importance, label, class, organizer, reminder)
 - smaller improvements
 - deploy/build script
This commit is contained in:
Christoph Haas 2012-12-30 17:18:39 +00:00
parent b4b396ade2
commit 4795a0002a
10 changed files with 565 additions and 180 deletions

263
build.xml Normal file
View File

@ -0,0 +1,263 @@
<project default="all">
<property name="root-folder" value="${basedir}/../"/>
<property name="tools-folder" value="${root-folder}/TOOLS/"/>
<property name="target-folder" value="${root-folder}/DEPLOY/plugins"/>
<import file="${tools-folder}/antutil.xml"/>
<typedef file="${tools-folder}/antlib.xml">
<classpath>
<pathelement location="${tools-folder}/tools.jar"/>
<pathelement location="${tools-folder}/lib/compiler.jar"/>
</classpath>
</typedef>
<taskdef resource="net/sf/antcontrib/antcontrib.properties">
<classpath>
<pathelement location="${tools-folder}/lib/ant-contrib-1.0b3.jar"/>
</classpath>
</taskdef>
<!-- os checks for xmllint... -->
<condition property="isWindows" value="true">
<os family="windows" />
</condition>
<!-- define nicknames for libraries -->
<property name="yui-compressor" location="${tools-folder}/lib/yuicompressor-2.4.2.jar" />
<property name="yui-compressor-ant-task" location="${tools-folder}/lib/yui-compressor-ant-task-0.5.jar" />
<!-- adds libraries to the classpath -->
<path id="yui.classpath">
<pathelement location="${yui-compressor}" />
<pathelement location="${yui-compressor-ant-task}" />
</path>
<!-- define tasks -->
<taskdef name="yui-compressor" classname="net.noha.tools.ant.yuicompressor.tasks.YuiCompressorTask">
<classpath refid="yui.classpath" />
</taskdef>
<!-- Determine plugin name -->
<var name="plugin" unset="true"/>
<basename file="${basedir}" property="plugin"/>
<!-- The Plugin distribution files -->
<property name="plugin-folder" value="${plugin}"/>
<property name="plugin-debugfile" value="${plugin}-debug.js"/>
<property name="plugin-file" value="${plugin}.js"/>
<!-- The Plugin CSS files -->
<property name="plugin-css-folder" value="resources/css"/>
<property name="plugin-css-file" value="${plugin}-min.css"/>
<property name="plugin-css-debug-file" value="${plugin}.css"/>
<!-- Meta target -->
<target name="all" depends="concat, compress"/>
<!-- Clean -->
<target name="clean">
<delete includeemptydirs="true" failonerror="false">
<!-- Delete the Plugin files -->
<fileset dir="${target-folder}/${plugin-folder}/js">
<include name="${plugin-file}"/>
<include name="${plugin-debugfile}"/>
</fileset>
<fileset dir="${target-folder}/${plugin-folder}/${plugin-css-folder}">
<include name="${plugin-css-debug-file}"/>
<include name="${plugin-css-file}"/>
</fileset>
</delete>
</target>
<!-- Concatenates JavaScript files with automatic dependency generation -->
<target name="concat">
<!-- Concatenate plugin JS file -->
<if>
<available file="js" type="dir" />
<then>
<mkdir dir="${target-folder}/${plugin-folder}/js"/>
<echo message="Concatenating: ${plugin-debugfile}"/>
<!-- TODO: fix JS files for zConcat -->
<!--zConcat outputFolder="${target-folder}/${plugin-folder}/js" outputFile="${plugin-debugfile}" prioritize="\w+">
<concatfiles>
<fileset dir="js" includes="**/*.js" />
</concatfiles>
</zConcat-->
<concat destfile="${target-folder}/${plugin-folder}/js/${plugin-debugfile}">
<fileset file="js/data/timezones.js" />
<fileset file="js/plugin.calendarimporter.js" />
<fileset file="js/data/ResponseHandler.js" />
<fileset file="js/dialogs/ImportContentPanel.js" />
<fileset file="js/dialogs/ImportPanel.js" />
</concat>
</then>
</if>
<!-- Concatenate plugin CSS files -->
<if>
<available file="${plugin-css-folder}" type="dir" />
<then>
<mkdir dir="${target-folder}/${plugin-folder}/${plugin-css-folder}"/>
<echo message="Concatenating: ${plugin-css-debug-file}"/>
<zConcat outputFolder="${target-folder}/${plugin-folder}/${plugin-css-folder}" outputFile="${plugin-css-debug-file}">
<concatfiles>
<fileset dir="${plugin-css-folder}" includes="**/*.css" />
</concatfiles>
</zConcat>
</then>
</if>
</target>
<!-- Preformat the Concatenated Javascript files to improve compilation -->
<target name="preformat" depends="concat">
<if>
<available file="${target-folder}/${plugin-folder}/js/${plugin-debugfile}" type="file" />
<then>
<echo message="Preformatting: ${plugin-debugfile}"/>
<replaceregexp byline="true">
<regexp pattern="(^[ ,\t]*\*[ ,\t]@.*)\{(.*)\[\]\}"/>
<substitution expression="\1{\2\|Array}"/>
<fileset dir="${target-folder}/${plugin-folder}/js" includes="${plugin-debugfile}"/>
</replaceregexp>
</then>
</if>
</target>
<!-- Compress JavaScript -->
<target name="compress" depends="preformat">
<if>
<available file="${target-folder}/${plugin-folder}/js/${plugin-debugfile}" type="file" />
<then>
<echo message="Compiling: ${plugin-debugfile}" />
<zCompile inputFolder="${target-folder}/${plugin-folder}/js" inputFile="${plugin-debugfile}" outputFolder="${target-folder}/${plugin-folder}/js" outputFile="${plugin-file}">
<externs>
var Ext = {};
var Zarafa = {};
var container = {};
var _ = function(key, domain) {};
var dgettext = function(domain, msgid) {};
var dngettext = function(domain, msgid, msgid_plural, count) {};
var dnpgettext = function(domain, msgctxt, msgid, msgid_plural, count) {};
var dpgettext = function(domain, msgctxt, msgid) {};
var ngettext = function(msgid, msgid_plural, count) {};
var npgettext = function(msgctxt, msgid, msgid_plural, count) {};
var pgettext = function(msgctxt, msgid) {};
</externs>
</zCompile>
<!--yui-compressor
warn="false"
munge="true"
preserveallsemicolons="false"
fromdir="${target-folder}/${plugin-folder}/js"
todir="${target-folder}/${plugin-folder}/js">
<include name="${plugin-debugfile}" />
</yui-compressor-->
</then>
</if>
</target>
<!-- syntax check all PHP files -->
<target name="validate">
<if>
<available file="config.php" type="file" />
<then>
<antcall target="syntax-check">
<param name="file" value="config.php"/>
</antcall>
</then>
</if>
<if>
<available file="php" type="dir" />
<then>
<foreach target="syntax-check" param="file">
<path>
<fileset dir=".">
<include name="**/*.php"/>
</fileset>
</path>
</foreach>
</then>
</if>
</target>
<target name="syntax-check">
<echo message="validating ${file}"/>
<exec executable="php" failonerror="true" failifexecutionfails="false">
<arg value="-l"/>
<arg value="${file}"/>
</exec>
</target>
<!-- on windows we do not check the xml file -->
<target name="xml-os-sel" depends="xml-check,xml-copy">
<echo>Processing manifest.xml</echo>
</target>
<!-- check manifest.xml if we are on windows... -->
<target name="xml-check" unless="isWindows">
<echo message="Checking xml: manifest.xml" />
<!-- Copy (and validate) manifest.xml -->
<exec executable="xmllint" output="${target-folder}/${plugin-folder}/manifest.xml" failonerror="true" error="/dev/stdout" failifexecutionfails="false">
<arg value="--valid"/>
<arg value="--path"/>
<arg value="${root-folder}/server"/>
<arg value="manifest.xml"/>
</exec>
</target>
<!-- check manifest.xml if we are on windows... -->
<target name="xml-copy" if="isWindows">
<echo message="Copying xml: manifest.xml" />
<!-- Copy manifest.xml -->
<copy todir="${target-folder}/${plugin-folder}">
<fileset dir=".">
<include name="manifest.xml"/>
</fileset>
</copy>
</target>
<!-- Install all files into the target folder -->
<target name="deploy" depends="clean, compress, compresscss, validate, xml-os-sel">
<mkdir dir="${target-folder}/${plugin-folder}"/>
<!-- copy files -->
<copy todir="${target-folder}/${plugin-folder}">
<fileset dir=".">
<include name="resources/**/*.*"/>
<include name="php/**/*.php"/>
<include name="config.php"/>
<include name="changelog.txt"/>
<!-- exclude the ant script -->
<exclude name="build.xml"/>
<!-- CSS is generated during build -->
<exclude name="resources/css/*.*"/>
</fileset>
</copy>
</target>
<!-- compresses each CSS file -->
<target name="compresscss" depends="concat">
<available file="${tools-folder}/lib/yui-compressor-ant-task-0.5.jar" property="YUIANT_AVAILABLE" />
<fail unless="YUIANT_AVAILABLE" message="yui-compressor-ant-task-0.5.jar not found" />
<if>
<available file="${target-folder}/${plugin-folder}/${plugin-css-folder}/${plugin-css-debug-file}" type="file" />
<then>
<yui-compressor
warn="false"
munge="true"
preserveallsemicolons="false"
fromdir="${target-folder}/${plugin-folder}/${plugin-css-folder}"
todir="${target-folder}/${plugin-folder}/${plugin-css-folder}">
<include name="${plugin-css-debug-file}" />
</yui-compressor>
</then>
</if>
</target>
</project>

View File

@ -1,10 +1,14 @@
calendarimporter 1.2:
- New timezone management
- more imported fields (Busystatus, importance, label, class, organizer, reminder)
- smaller improvements
- deploy/build script
calendarimporter 1.1 final: calendarimporter 1.1 final:
- ics exporter - ics exporter
- improved ics fileparser - improved ics fileparser
- fixed ExtJS Problem in chrome - fixed ExtJS Problem in chrome
KNOWN PROBLEMS: KNOWN PROBLEMS:
- importer ignores some fields (priority, status...)
- attechments are ignored - attechments are ignored
- timezone handling is not perfect
- recurrent events are not handled properly (im/export) - recurrent events are not handled properly (im/export)

View File

@ -5,5 +5,5 @@
define('PLUGIN_CALENDARIMPORTER_USER_DEFAULT_ENABLE_EXPORT', false); define('PLUGIN_CALENDARIMPORTER_USER_DEFAULT_ENABLE_EXPORT', false);
/** The default calendar to import to*/ /** The default calendar to import to*/
define('PLUGIN_CALENDARIMPORTER_DEFAULT', "Default"); define('PLUGIN_CALENDARIMPORTER_DEFAULT', "calendar");
?> ?>

View File

@ -736,7 +736,7 @@ Zarafa.plugins.calendarimporter.data.Timezones = Ext.extend(Object, {
getOffset: function(timezone) { getOffset: function(timezone) {
/* find timezone, this needs to be optimized ;) */ /* find timezone, this needs to be optimized ;) */
timezone = this.unMap(timezone); timezone = this.unMap(timezone);
var i = 0;
for(i = 0; i < this.store.length; i++) { for(i = 0; i < this.store.length; i++) {
if(this.store[i][0] == timezone) { if(this.store[i][0] == timezone) {
return (this.store[i][2] * 60000); return (this.store[i][2] * 60000);

View File

@ -101,10 +101,16 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
} }
if(eventdata !== null) { if(eventdata !== null) {
var parsedData = new Array(eventdata.events.length); parsedData = new Array(eventdata.events.length);
var i = 0;
for(var i=0; i < eventdata.events.length; i++) { for(i = 0; i < eventdata.events.length; i++) {
parsedData[i] = new Array(eventdata.events[i]["SUMMARY"], new Date(parseInt(eventdata.events[i]["DTSTART"]) + local_tz_offset + tz_offset), new Date(parseInt(eventdata.events[i]["DTEND"]) + local_tz_offset + tz_offset), eventdata.events[i]["LOCATION"], eventdata.events[i]["DESCRIPTION"]); var trigger = null;
if(eventdata.events[i]["VALARM"]) {
trigger = eventdata.events[i]["VALARM"]["TRIGGER"];
trigger = new Date(parseInt(trigger) + local_tz_offset + tz_offset);
}
parsedData[i] = new Array(eventdata.events[i]["SUMMARY"], new Date(parseInt(eventdata.events[i]["DTSTART"]) + local_tz_offset + tz_offset), new Date(parseInt(eventdata.events[i]["DTEND"]) + local_tz_offset + tz_offset), eventdata.events[i]["LOCATION"], eventdata.events[i]["DESCRIPTION"],eventdata.events[i]["PRIORITY"],eventdata.events[i]["X-ZARAFA-LABEL"],eventdata.events[i]["X-MICROSOFT-CDO-BUSYSTATUS"],eventdata.events[i]["CLASS"],eventdata.events[i]["ORGANIZER"],trigger);
} }
} else { } else {
return null; return null;
@ -113,11 +119,17 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
// create the data store // create the data store
var store = new Ext.data.ArrayStore({ var store = new Ext.data.ArrayStore({
fields: [ fields: [
{name: 'title'}, {name: 'title'},
{name: 'start'}, {name: 'start'},
{name: 'end'}, {name: 'end'},
{name: 'location'}, {name: 'location'},
{name: 'description'} {name: 'description'},
{name: 'priority'},
{name: 'label'},
{name: 'busy'},
{name: 'privatestate'},
{name: 'organizer'},
{name: 'trigger'}
], ],
data: parsedData data: parsedData
}); });
@ -142,7 +154,13 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
{header: 'Start', width: 150, sortable: true, dataIndex: 'start', renderer : Zarafa.common.ui.grid.Renderers.datetime}, {header: 'Start', width: 150, sortable: true, dataIndex: 'start', renderer : Zarafa.common.ui.grid.Renderers.datetime},
{header: 'End', width: 150, sortable: true, dataIndex: 'end', renderer : Zarafa.common.ui.grid.Renderers.datetime}, {header: 'End', width: 150, sortable: true, dataIndex: 'end', renderer : Zarafa.common.ui.grid.Renderers.datetime},
{header: 'Location', width: 150, sortable: true, dataIndex: 'location'}, {header: 'Location', width: 150, sortable: true, dataIndex: 'location'},
{header: 'Description', width: 150, sortable: true, dataIndex: 'description'} {header: 'Description', width: 150, sortable: true, dataIndex: 'description'},
{header: "Priority", dataIndex: 'priority', hidden: true},
{header: "Label", dataIndex: 'label', hidden: true},
{header: "Busystatus", dataIndex: 'busy', hidden: true},
{header: "Privacystatus", dataIndex: 'privatestate', hidden: true},
{header: "Organizer", dataIndex: 'organizer', hidden: true},
{header: "Alarm", dataIndex: 'trigger', hidden: true, renderer : Zarafa.common.ui.grid.Renderers.datetime}
] ]
}), }),
sm: new Ext.grid.RowSelectionModel({multiSelect:true}) sm: new Ext.grid.RowSelectionModel({multiSelect:true})
@ -150,10 +168,10 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
}, },
createSelectBox: function() { createSelectBox: function() {
ctx = container.getContextByName('calendar'); var ctx = container.getContextByName('calendar');
model = ctx.getModel(); var model = ctx.getModel();
defaultFolder = model.getDefaultFolder(); // @type: Zarafa.hierarchy.data.MAPIFolderRecord var defaultFolder = model.getDefaultFolder(); // @type: Zarafa.hierarchy.data.MAPIFolderRecord
subFolders = defaultFolder.getChildren(); var subFolders = defaultFolder.getChildren();
var myStore = new Ext.data.ArrayStore({ var myStore = new Ext.data.ArrayStore({
fields: ['calendar_id', 'calendar_displayname'], fields: ['calendar_id', 'calendar_displayname'],
@ -172,12 +190,12 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
displayname: defaultFolder.getDisplayName() displayname: defaultFolder.getDisplayName()
}); });
myStore.add(myNewRecord); myStore.add(myNewRecord);
var i = 0;
for(i=0;i<subFolders.length;i++) { for(i = 0; i < subFolders.length; i++) {
/* Store all subfolders */ /* Store all subfolders */
myNewRecord = new CalendarRecord({ myNewRecord = new CalendarRecord({
realname: subFolders[i].getDisplayName(), // TODO: get the real path... realname: subFolders[i].getDisplayName(), // TODO: get the real path...
displayname: subFolders[i].getDisplayName() displayname: subFolders[i].getDisplayName()
}); });
myStore.add(myNewRecord); myStore.add(myNewRecord);
} }
@ -191,6 +209,7 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
id: 'calendarselector', id: 'calendarselector',
editable: false, editable: false,
name: "choosen_calendar", name: "choosen_calendar",
value: container.getSettingsModel().get("zarafa/v1/plugins/calendarimporter/default_calendar"),
width: 100, width: 100,
fieldLabel: "Select a calender", fieldLabel: "Select a calender",
store: myStore, store: myStore,
@ -337,11 +356,11 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
failure: function(file, action) { failure: function(file, action) {
Ext.getCmp('submitButton').disable(); Ext.getCmp('submitButton').disable();
Ext.getCmp('submitAllButton').disable(); Ext.getCmp('submitAllButton').disable();
Ext.MessageBox.show({ Zarafa.common.dialogs.MessageBox.show({
title : _('Error'), title : _('Error'),
msg : _(action.result.errors[action.result.errors.type]), msg : _(action.result.errors[action.result.errors.type]),
icon : Ext.MessageBox.ERROR, icon : Zarafa.common.dialogs.MessageBox.ERROR,
buttons : Ext.MessageBox.OK buttons : Zarafa.common.dialogs.MessageBox.OK
}); });
}, },
success: function(file, action){ success: function(file, action){
@ -372,81 +391,120 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
convertToAppointmentRecord: function (calendarFolder,entry) { convertToAppointmentRecord: function (calendarFolder,entry) {
var newRecord = Zarafa.core.data.RecordFactory.createRecordObjectByMessageClass('IPM.Appointment', { var newRecord = Zarafa.core.data.RecordFactory.createRecordObjectByMessageClass('IPM.Appointment', {
startdate: new Date(entry.start), startdate: new Date(entry.start),
duedate: (entry.end) ? duedate: (entry.end != null) ?
new Date(entry.end) : new Date(entry.end) :
new Date(entry.start).add(Date.HOUR, 1), new Date(entry.start).add(Date.HOUR, 1),
location: entry.location, location: entry.location,
subject: entry.title, subject: entry.title,
body: entry.description, body: entry.description,
commonstart: new Date(entry.start), commonstart: new Date(entry.start),
commonend: (entry.end) ? commonend: (entry.end != null) ?
new Date(entry.end) : new Date(entry.end) :
new Date(entry.start).add(Date.HOUR, 1), new Date(entry.start).add(Date.HOUR, 1),
timezone: this.timezone, timezone: this.timezone,
parent_entryid: calendarFolder.get('entryid'), parent_entryid: calendarFolder.get('entryid'),
store_entryid: calendarFolder.get('store_entryid') store_entryid: calendarFolder.get('store_entryid')
}); });
var busystate = new Array("FREE", "TENTATIVE", "BUSY", "OOF");
var zlabel = new Array("NONE", "IMPORTANT", "WORK", "PERSONAL", "HOLIDAY", "REQUIRED", "TRAVEL REQUIRED", "PREPARATION REQUIERED", "BIRTHDAY", "SPECIAL DATE", "PHONE INTERVIEW");
/* optional fields */
if(entry.priority !== "") {
newRecord.data.importance = entry.priority;
}
if(entry.label !== "") {
newRecord.data.label = zlabel.indexOf(entry.label);
}
if(entry.busy !== "") {
newRecord.data.busystatus = busystate.indexOf(entry.busy);
}
if(entry.privatestate !== "") {
newRecord.data["private"] = entry.privatestate == "PUBLIC" ? false : true;
}
if(entry.organizer !== "") {
newRecord.data.sent_representing_email_address = entry.organizer;
}
if(entry.trigger != null) {
newRecord.data.reminder = true;
newRecord.data.reminder_minutes = new Date((entry.start - entry.trigger)/60);
newRecord.data.reminder_time = new Date(entry.trigger);
} else {
newRecord.data.reminder = false;
}
return newRecord; return newRecord;
}, },
importAllEvents: function () { importAllEvents: function () {
//receive existing calendar store //receive existing calendar store
var selIndex = this.calendarselector.selectedIndex;
var calValue = this.calendarselector.value; var calValue = this.calendarselector.value;
if(selIndex == -1) { // no calendar choosen if(calValue == undefined) { // no calendar choosen
Ext.MessageBox.show({ Zarafa.common.dialogs.MessageBox.show({
title : _('Error'), title : _('Error'),
msg : _('You have to choose a calendar!'), msg : _('You have to choose a calendar!'),
icon : Ext.MessageBox.ERROR, icon : Zarafa.common.dialogs.MessageBox.ERROR,
buttons : Ext.MessageBox.OK buttons : Zarafa.common.dialogs.MessageBox.OK
}); });
} else { } else {
var calexist = true;
var calendarStore = new Zarafa.calendar.AppointmentStore(); var calendarStore = new Zarafa.calendar.AppointmentStore();
var calendarFolder = container.getHierarchyStore().getDefaultFolder('calendar'); var calendarFolder = container.getHierarchyStore().getDefaultFolder('calendar');
if(calValue != "calendar") { if(calValue != "calendar") {
var subFolders = calendarFolder.getChildren(); var subFolders = calendarFolder.getChildren();
var i = 0;
for(i=0;i<subFolders.length;i++) { for(i=0;i<subFolders.length;i++) {
// loo up right folder // look up right folder
// TODO: improve!! // TODO: improve!!
if(subFolders[i].getDisplayName() == calValue) { if(subFolders[i].getDisplayName() == calValue) {
calendarFolder = subFolders[i]; calendarFolder = subFolders[i];
break; break;
} }
} }
if(calendarFolder.getDefaultFolderKey() != undefined) {
Zarafa.common.dialogs.MessageBox.show({
title : _('Error'),
msg : _('Selected calendar does not exist!'),
icon : Zarafa.common.dialogs.MessageBox.ERROR,
buttons : Zarafa.common.dialogs.MessageBox.OK
});
calexist = false;
}
} }
//receive Records from grid rows if(calexist) {
this.eventgrid.selModel.selectAll(); // select all entries //receive Records from grid rows
var newRecords = this.eventgrid.selModel.getSelections(); this.eventgrid.selModel.selectAll(); // select all entries
Ext.each(newRecords, function(newRecord) { var newRecords = this.eventgrid.selModel.getSelections();
var record = this.convertToAppointmentRecord(calendarFolder,newRecord.data); Ext.each(newRecords, function(newRecord) {
calendarStore.add(record); var record = this.convertToAppointmentRecord(calendarFolder,newRecord.data);
}, this); calendarStore.add(record);
calendarStore.save(); }, this);
this.dialog.close(); calendarStore.save();
this.dialog.close();
}
} }
}, },
exportAllEvents: function () { exportAllEvents: function () {
//receive existing calendar store //receive existing calendar store
var selIndex = this.calendarselector.selectedIndex;
var calValue = this.calendarselector.value; var calValue = this.calendarselector.value;
if(selIndex == -1 || calValue == "") { // no calendar choosen if(calValue == undefined) { // no calendar choosen
Ext.MessageBox.show({ Zarafa.common.dialogs.MessageBox.show({
title : _('Error'), title : _('Error'),
msg : _('You have to choose a calendar!'), msg : _('You have to choose a calendar!'),
icon : Ext.MessageBox.ERROR, icon : Zarafa.common.dialogs.MessageBox.ERROR,
buttons : Ext.MessageBox.OK buttons : Zarafa.common.dialogs.MessageBox.OK
}); });
} else { } else {
var calexist = true;
var calendarFolder = container.getHierarchyStore().getDefaultFolder('calendar'); var calendarFolder = container.getHierarchyStore().getDefaultFolder('calendar');
if(calValue != "calendar") { if(calValue != "calendar") {
var subFolders = calendarFolder.getChildren(); var subFolders = calendarFolder.getChildren();
var i = 0;
for(i=0;i<subFolders.length;i++) { for(i=0;i<subFolders.length;i++) {
// loo up right folder // loo up right folder
// TODO: improve!! // TODO: improve!!
@ -455,61 +513,73 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
break; break;
} }
} }
if(calendarFolder.getDefaultFolderKey() != undefined) {
Zarafa.common.dialogs.MessageBox.show({
title : _('Error'),
msg : _('Selected calendar does not exist!'),
icon : Zarafa.common.dialogs.MessageBox.ERROR,
buttons : Zarafa.common.dialogs.MessageBox.OK
});
calexist = false;
}
} }
Zarafa.common.dialogs.MessageBox.show({ if(calexist) {
title: 'Please wait', Zarafa.common.dialogs.MessageBox.show({
msg: 'Generating ical file...', title: 'Please wait',
progressText: 'Exporting...', msg: 'Generating ical file...',
width:300, progressText: 'Exporting...',
progress:true, width:300,
closable:false progress:true,
}); closable:false
});
// progress bar... ;)
var updateProgressBar = function(v){ // progress bar... ;)
return function(){ var updateProgressBar = function(v){
if(v == 100){ return function(){
if(Zarafa.common.dialogs.MessageBox.isVisible()) { if(v == 100){
updateTimer(); if(Zarafa.common.dialogs.MessageBox.isVisible()) {
updateTimer();
}
}else{
Zarafa.common.dialogs.MessageBox.updateProgress(v/100, 'Exporting...');
} }
}else{ };
Zarafa.common.dialogs.MessageBox.updateProgress(v/100, 'Exporting...'); };
var updateTimer = function() {
for(var i = 1; i < 101; i++){
setTimeout(updateProgressBar(i), 20*i);
} }
}; };
};
updateTimer();
var updateTimer = function() { // call export function here!
for(var i = 1; i < 101; i++){ var responseHandler = new Zarafa.plugins.calendarimporter.data.ResponseHandler({
setTimeout(updateProgressBar(i), 20*i); successCallback: this.exportDone.createDelegate(this)
} });
};
container.getRequest().singleRequest(
updateTimer(); 'appointmentlistmodule',
'list',
// call export function here! {
var responseHandler = new Zarafa.plugins.calendarimporter.data.ResponseHandler({ groupDir: "ASC",
successCallback: this.exportDone.createDelegate(this) restriction: {
}); startdate: 0,
duedate: 2145826800 // 2037... nearly highest unix timestamp
container.getRequest().singleRequest( },
'appointmentlistmodule', sort: [{
'list', "field": "startdate",
{ "direction": "DESC"
groupDir: "ASC", }],
restriction: { store_entryid : calendarFolder.data.store_entryid,
startdate: 0, entryid : calendarFolder.data.entryid
duedate: 2145826800 // 2037... nearly highest unix timestamp
}, },
sort: [{ responseHandler
"field": "startdate", );
"direction": "DESC" }
}],
store_entryid : calendarFolder.data.store_entryid,
entryid : calendarFolder.data.entryid
},
responseHandler
);
} }
}, },
@ -518,8 +588,7 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
* @param {Object} response * @param {Object} response
* @private * @private
*/ */
exportDone : function(response) exportDone : function(response) {
{
if(response.item.length > 0) { if(response.item.length > 0) {
// call export function here! // call export function here!
var responseHandler = new Zarafa.plugins.calendarimporter.data.ResponseHandler({ var responseHandler = new Zarafa.plugins.calendarimporter.data.ResponseHandler({
@ -548,8 +617,7 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
* @param {Object} response * @param {Object} response
* @private * @private
*/ */
downLoadICS : function(response) downLoadICS : function(response) {
{
Zarafa.common.dialogs.MessageBox.hide(); Zarafa.common.dialogs.MessageBox.hide();
if(response.status === true) { if(response.status === true) {
// needs to be window.open, document.location.href kills the extjs response handler... // needs to be window.open, document.location.href kills the extjs response handler...
@ -561,48 +629,60 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
importCheckedEvents: function () { importCheckedEvents: function () {
//receive existing calendar store //receive existing calendar store
var selIndex = this.calendarselector.selectedIndex;
var calValue = this.calendarselector.value; var calValue = this.calendarselector.value;
if(selIndex == -1) { // no calendar choosen if(calValue == undefined) { // no calendar choosen
Ext.MessageBox.show({ Zarafa.common.dialogs.MessageBox.show({
title : _('Error'), title : _('Error'),
msg : _('You have to choose a calendar!'), msg : _('You have to choose a calendar!'),
icon : Ext.MessageBox.ERROR, icon : Zarafa.common.dialogs.MessageBox.ERROR,
buttons : Ext.MessageBox.OK buttons : Zarafa.common.dialogs.MessageBox.OK
}); });
} else { } else {
var calexist = true;
if(this.eventgrid.selModel.getCount() < 1) { if(this.eventgrid.selModel.getCount() < 1) {
Ext.MessageBox.show({ Zarafa.common.dialogs.MessageBox.show({
title : _('Error'), title : _('Error'),
msg : _('You have to choose at least one event to import!'), msg : _('You have to choose at least one event to import!'),
icon : Ext.MessageBox.ERROR, icon : Zarafa.common.dialogs.MessageBox.ERROR,
buttons : Ext.MessageBox.OK buttons : Zarafa.common.dialogs.MessageBox.OK
}); });
} else { } else {
var calendarStore = new Zarafa.calendar.AppointmentStore(); var calendarStore = new Zarafa.calendar.AppointmentStore();
var calendarFolder = container.getHierarchyStore().getDefaultFolder('calendar'); var calendarFolder = container.getHierarchyStore().getDefaultFolder('calendar');
if(calValue != "calendar") { if(calValue != "calendar") {
var subFolders = calendarFolder.getChildren(); var subFolders = calendarFolder.getChildren();
var i = 0;
for(i=0;i<subFolders.length;i++) { for(i=0;i<subFolders.length;i++) {
// loo up right folder // look up right folder
// TODO: improve!! // TODO: improve!!
if(subFolders[i].getDisplayName() == calValue) { if(subFolders[i].getDisplayName() == calValue) {
calendarFolder = subFolders[i]; calendarFolder = subFolders[i];
break; break;
} }
} }
if(calendarFolder.getDefaultFolderKey() != undefined) {
Zarafa.common.dialogs.MessageBox.show({
title : _('Error'),
msg : _('Selected calendar does not exist!'),
icon : Zarafa.common.dialogs.MessageBox.ERROR,
buttons : Zarafa.common.dialogs.MessageBox.OK
});
calexist = false;
}
} }
//receive Records from grid rows if(calexist) {
var newRecords = this.eventgrid.selModel.getSelections(); //receive Records from grid rows
Ext.each(newRecords, function(newRecord) { var newRecords = this.eventgrid.selModel.getSelections();
var record = this.convertToAppointmentRecord(calendarFolder,newRecord.data); Ext.each(newRecords, function(newRecord) {
calendarStore.add(record); var record = this.convertToAppointmentRecord(calendarFolder,newRecord.data);
}, this); calendarStore.add(record);
calendarStore.save(); }, this);
this.dialog.close(); calendarStore.save();
this.dialog.close();
}
} }
} }
} }

View File

@ -1,42 +1,40 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<!DOCTYPE plugin SYSTEM "manifest.dtd"> <!DOCTYPE plugin SYSTEM "manifest.dtd">
<plugin version="2"> <plugin version="2">
<info> <info>
<version>1.1</version> <version>1.2</version>
<name>calendarimporter</name> <name>calendarimporter</name>
<title>ICS Calendar Importer/Exporter</title> <title>ICS Calendar Importer/Exporter</title>
<author>Christoph Haas</author> <author>Christoph Haas</author>
<authorURL>http://www.sprinternet.at</authorURL> <authorURL>http://www.sprinternet.at</authorURL>
<description>Import a ICS file to the zarafa calendar</description> <description>Import or Export a ICS file to/from the zarafa calendar</description>
</info> </info>
<config> <config>
<configfile>config.php</configfile> <configfile>config.php</configfile>
</config> </config>
<components> <components>
<component> <component>
<files> <files>
<server> <server>
<serverfile>php/plugin.calendarimporter.php</serverfile> <serverfile>php/plugin.calendarimporter.php</serverfile>
<serverfile type="module" module="calendarexportermodule">php/module.calendarexporter.php</serverfile> <serverfile type="module" module="calendarexportermodule">php/module.calendarexporter.php</serverfile>
</server> </server>
<client> <client>
<clientfile load="release">js/data/timezones.js</clientfile> <clientfile load="release">js/calendarimporter.js</clientfile>
<clientfile load="release">js/plugin.calendarimporter.js</clientfile> <clientfile load="debug">js/calendarimporter-debug.js</clientfile>
<clientfile load="release">js/data/ResponseHandler.js</clientfile>
<clientfile load="release">js/dialogs/ImportContentPanel.js</clientfile> <clientfile load="source">js/data/timezones.js</clientfile>
<clientfile load="release">js/dialogs/ImportPanel.js</clientfile> <clientfile load="source">js/plugin.calendarimporter.js</clientfile>
<clientfile load="source">js/data/ResponseHandler.js</clientfile>
<clientfile load="debug">js/data/timezones.js</clientfile> <clientfile load="source">js/dialogs/ImportContentPanel.js</clientfile>
<clientfile load="debug">js/plugin.calendarimporter.js</clientfile> <clientfile load="source">js/dialogs/ImportPanel.js</clientfile>
<clientfile load="debug">js/data/ResponseHandler.js</clientfile> </client>
<clientfile load="debug">js/dialogs/ImportContentPanel.js</clientfile> <resources>
<clientfile load="debug">js/dialogs/ImportPanel.js</clientfile> <resourcefile load="release">resources/css/calendarimporter-min.css</resourcefile>
</client> <resourcefile load="debug">resources/css/calendarimporter.css</resourcefile>
<resources> <resourcefile load="source">resources/css/calendarimporter-main.css</resourcefile>
<resourcefile load="release">resources/css/calendarimporter.css</resourcefile> </resources>
<resourcefile load="debug">resources/css/calendarimporter.css</resourcefile> </files>
</resources> </component>
</files> </components>
</component>
</components>
</plugin> </plugin>

View File

@ -4,11 +4,11 @@ VERSION:2.0
CALSCALE:GREGORIAN CALSCALE:GREGORIAN
METHOD:PUBLISH METHOD:PUBLISH
X-WR-CALNAME:Testkalender X-WR-CALNAME:Testkalender
X-WR-TIMEZONE:America/Detroit X-WR-TIMEZONE:Europe/Berlin
X-WR-CALDESC:Nur zum testen vom Google Kalender X-WR-CALDESC:Nur zum testen vom Google Kalender
BEGIN:VEVENT BEGIN:VEVENT
DTSTART:20121227T150000Z DTSTART;TZID="W. Europe":20121227T100000
DTEND:20121227T160000Z DTEND;TZID="W. Europe":20121227T120000
DTSTAMP:20110121T195741Z DTSTAMP:20110121T195741Z
UID:1koigufm110c5hnq6ln57murd4@google.com UID:1koigufm110c5hnq6ln57murd4@google.com
CREATED:20110119T142901Z CREATED:20110119T142901Z

View File

@ -9,7 +9,7 @@
* @author Christoph Haas <mail@h44z.net> * @author Christoph Haas <mail@h44z.net>
* @modified 17.11.2012 by Christoph Haas (original at http://code.google.com/p/ics-parser/) * @modified 17.11.2012 by Christoph Haas (original at http://code.google.com/p/ics-parser/)
* @license http://www.opensource.org/licenses/mit-license.php MIT License * @license http://www.opensource.org/licenses/mit-license.php MIT License
* @version SVN: 16 * @version SVN: 62
* @example $ical = new ical('calendar.ics'); * @example $ical = new ical('calendar.ics');
* print_r( $ical->events() ); * print_r( $ical->events() );
*/ */
@ -26,6 +26,9 @@ class ICal {
/* How many events are in this ical? */ /* How many events are in this ical? */
public /** @type {int} */ $event_count = 0; public /** @type {int} */ $event_count = 0;
/* Currently editing an alarm? */
private /** @type {int} */ $isalarm = false;
/* The parsed calendar */ /* The parsed calendar */
public /** @type {Array} */ $cal; public /** @type {Array} */ $cal;
@ -74,7 +77,12 @@ class ICal {
$this->todo_count++; $this->todo_count++;
$type = "VTODO"; $type = "VTODO";
break; break;
case "BEGIN:VALARM":
//echo "vevent gematcht";
$this->isalarm=true;
$type = "VEVENT";
break;
// http://www.kanzaki.com/docs/ical/vevent.html // http://www.kanzaki.com/docs/ical/vevent.html
case "BEGIN:VEVENT": case "BEGIN:VEVENT":
//echo "vevent gematcht"; //echo "vevent gematcht";
@ -91,13 +99,17 @@ class ICal {
$type = $value; $type = $value;
break; break;
case "END:VTODO": // end special text - goto VCALENDAR key case "END:VTODO": // end special text - goto VCALENDAR key
case "END:VEVENT": case "END:VEVENT":
case "END:VCALENDAR": case "END:VCALENDAR":
case "END:DAYLIGHT": case "END:DAYLIGHT":
case "END:VTIMEZONE": case "END:VTIMEZONE":
case "END:STANDARD": case "END:STANDARD":
$type = "VCALENDAR"; $type = "VCALENDAR";
break; break;
case "END:VALARM":
$this->isalarm=false;
$type = "VEVENT";
break;
default: default:
$this->addCalendarComponentWithKeyAndValue($type, $keyword, $value, $prop, $propvalue); $this->addCalendarComponentWithKeyAndValue($type, $keyword, $value, $prop, $propvalue);
break; break;
@ -122,13 +134,16 @@ class ICal {
switch ($component) { switch ($component) {
case 'VEVENT': case 'VEVENT':
if (stristr($keyword, "DTSTART") or stristr($keyword, "DTEND")) { if (stristr($keyword, "DTSTART") or stristr($keyword, "DTEND") or stristr($keyword, "TRIGGER")) {
$ts = $this->iCalDateToUnixTimestamp($value, $prop, $propvalue); $ts = $this->iCalDateToUnixTimestamp($value, $prop, $propvalue);
$value = $ts * 1000; $value = $ts * 1000;
} }
$value = str_replace("\\n", "\n", $value); $value = str_replace("\\n", "\n", $value);
$value = $this->cal[$component][$this->event_count - 1] if(!$this->isalarm) {
[$keyword].$value; $value = $this->cal[$component][$this->event_count - 1][$keyword].$value;
} else {
$value = $this->cal[$component][$this->event_count - 1]["VALARM"][$keyword].$value;
}
break; break;
case 'VTODO' : case 'VTODO' :
$value = $this->cal[$component][$this->todo_count - 1] $value = $this->cal[$component][$this->todo_count - 1]
@ -154,14 +169,19 @@ class ICal {
//$this->cal[$component][$this->todo_count]['Unix'] = $unixtime; //$this->cal[$component][$this->todo_count]['Unix'] = $unixtime;
break; break;
case "VEVENT": case "VEVENT":
if (stristr($keyword, "DTSTART") or stristr($keyword, "DTEND")) { if (stristr($keyword, "DTSTART") or stristr($keyword, "DTEND") or stristr($keyword, "TRIGGER")) {
$ts = $this->iCalDateToUnixTimestamp($value, $prop, $propvalue); $ts = $this->iCalDateToUnixTimestamp($value, $prop, $propvalue);
$value = $ts * 1000; $value = $ts * 1000;
} }
$value = str_replace("\\n", "\n", $value); $value = str_replace("\\n", "\n", $value);
$this->cal[$component][$this->event_count - 1][$keyword] = $value;
if(!$this->isalarm) {
$this->cal[$component][$this->event_count - 1][$keyword] = $value;
} else {
$this->cal[$component][$this->event_count - 1]["VALARM"][$keyword] = $value;
}
break; break;
default: default:
$this->cal[$component][$keyword] = $value; $this->cal[$component][$keyword] = $value;
break; break;
} }
@ -176,7 +196,7 @@ class ICal {
* @return {array} array("VCALENDAR", "Begin", "Optional Props") * @return {array} array("VCALENDAR", "Begin", "Optional Props")
*/ */
public function keyValueFromString($text) { public function keyValueFromString($text) {
preg_match("/(^[^a-z:;]+)([;a-zA-Z]*)[=]*([a-zA-Z\/\"\'\.\s]*)[:]([\w\W]*)/", $text, $matches); preg_match("/(^[^a-z:;]+)[;]*([a-zA-Z]*)[=]*(.*)[:]([\w\W]*)/", $text, $matches);
if (count($matches) == 0) { if (count($matches) == 0) {
return false; return false;
@ -200,7 +220,7 @@ class ICal {
if($prop) { if($prop) {
$pos = strpos("TZIDtzid", $prop); $pos = strpos("TZIDtzid", $prop);
if($pos !== false && $propvalue) { if($pos !== false && $propvalue != false) {
$timezone = str_replace('"', '', $propvalue); $timezone = str_replace('"', '', $propvalue);
$timezone = str_replace('\'', '', $timezone); $timezone = str_replace('\'', '', $timezone);
} }
@ -239,14 +259,34 @@ class ICal {
if(!$utc) { if(!$utc) {
$tz = $this->default_timezone; $tz = $this->default_timezone;
if($timezone) { if($timezone != false) {
$tz = $timezone; $tz = $timezone;
} }
$this_tz = new DateTimeZone($tz); $error = false;
$tz_now = new DateTime("now", $this_tz); $this_tz = false;
$tz_offset = $this_tz->getOffset($tz_now);
$timestamp_utc = $timestamp - $tz_offset; try {
$this_tz = new DateTimeZone($tz);
} catch(Exception $e) {
error_log($e->getMessage());
$error = true;
}
if($error) {
try { // Try using the default calendar timezone
$this_tz = new DateTimeZone($this->default_timezone);
} catch(Exception $e) {
error_log($e->getMessage());
$timestamp_utc = $timestamp; // if that fails, we cannot do anymore
}
}
if($this_tz != false) {
$tz_now = new DateTime("now", $this_tz);
$tz_offset = $this_tz->getOffset($tz_now);
$timestamp_utc = $timestamp - $tz_offset;
}
} else { } else {
$timestamp_utc = $timestamp; $timestamp_utc = $timestamp;
} }

View File

@ -48,7 +48,7 @@ class Plugincalendarimporter extends Plugin {
'calendarimporter' => Array( 'calendarimporter' => Array(
'enable' => PLUGIN_CALENDARIMPORTER_USER_DEFAULT_ENABLE, 'enable' => PLUGIN_CALENDARIMPORTER_USER_DEFAULT_ENABLE,
'enable_export' => PLUGIN_CALENDARIMPORTER_USER_DEFAULT_ENABLE_EXPORT, 'enable_export' => PLUGIN_CALENDARIMPORTER_USER_DEFAULT_ENABLE_EXPORT,
'default_calendar' => PLUGIN_CALENDARIMPORTER_DEFAULT // currently not used, maybe in next release 'default_calendar' => PLUGIN_CALENDARIMPORTER_DEFAULT
) )
) )
) )