Merge branch 'webapp22' into 'master'

Version 2.2.0 RC1

Release Candidate 1 of 2.2.0 version.

See changelog for changes...

See merge request !1
This commit is contained in:
Christoph Haas 2016-11-01 01:01:03 +01:00
commit 187b2a60f2
41 changed files with 2857 additions and 13066 deletions

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@ -0,0 +1,3 @@
<component name="CopyrightManager">
<settings default="" />
</component>

4
.idea/encodings.xml Normal file
View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false" />
</project>

8
.idea/modules.xml Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/calendarimporter.iml" filepath="$PROJECT_DIR$/.idea/calendarimporter.iml" />
</modules>
</component>
</project>

4
.idea/php.xml Normal file
View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="PhpProjectSharedConfiguration" php_language_level="5.5.0" />
</project>

View File

@ -0,0 +1,5 @@
<component name="DependencyValidationManager">
<state>
<option name="SKIP_IMPORT_STATEMENTS" value="false" />
</state>
</component>

6
.idea/vcs.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

677
.idea/workspace.xml Normal file
View File

@ -0,0 +1,677 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ChangeListManager">
<list default="true" id="e7c9c2bb-66f1-4e37-904c-d33d8c113bde" name="Default" comment="">
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/changelog.txt" afterPath="$PROJECT_DIR$/changelog.txt" />
</list>
<ignored path="calendarimporter.iws" />
<ignored path=".idea/workspace.xml" />
<ignored path=".idea/dataSources.local.xml" />
<option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
<option name="TRACKING_ENABLED" value="true" />
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="CreatePatchCommitExecutor">
<option name="PATCH_PATH" value="" />
</component>
<component name="ExecutionTargetManager" SELECTED_TARGET="default_target" />
<component name="FavoritesManager">
<favorites_list name="calendarimporter" />
</component>
<component name="FileEditorManager">
<leaf>
<file leaf-file-name="CalSyncEditPanel.js" pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/js/settings/dialogs/CalSyncEditPanel.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="1480">
<caret line="101" column="30" selection-start-line="101" selection-start-column="30" selection-end-line="101" selection-end-column="30" />
<folding />
</state>
</provider>
</entry>
</file>
<file leaf-file-name="CalSyncGrid.js" pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/js/settings/ui/CalSyncGrid.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="504">
<caret line="85" column="59" selection-start-line="85" selection-start-column="59" selection-end-line="85" selection-end-column="59" />
<folding />
</state>
</provider>
</entry>
</file>
<file leaf-file-name="SettingsCalSyncWidget.js" pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/js/settings/SettingsCalSyncWidget.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="729">
<caret line="104" column="16" selection-start-line="104" selection-start-column="16" selection-end-line="104" selection-end-column="142" />
<folding />
</state>
</provider>
</entry>
</file>
<file leaf-file-name="sync.php" pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/backend/sync.php">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="360">
<caret line="24" column="47" selection-start-line="24" selection-start-column="47" selection-end-line="24" selection-end-column="47" />
<folding>
<element signature="e#21#926#0#PHP" expanded="true" />
</folding>
</state>
</provider>
</entry>
</file>
<file leaf-file-name="changelog.txt" pinned="false" current-in-tab="true">
<entry file="file://$PROJECT_DIR$/changelog.txt">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="75">
<caret line="5" column="19" selection-start-line="5" selection-start-column="19" selection-end-line="5" selection-end-column="19" />
<folding />
</state>
</provider>
</entry>
</file>
<file leaf-file-name="manifest.xml" pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/manifest.xml">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="0">
<caret line="0" column="0" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
<folding />
</state>
</provider>
</entry>
</file>
<file leaf-file-name="functions.php" pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/backend/functions.php">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="75">
<caret line="5" column="26" selection-start-line="5" selection-start-column="26" selection-end-line="5" selection-end-column="26" />
<folding />
</state>
</provider>
</entry>
</file>
<file leaf-file-name="README.txt" pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/backend/README.txt">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="0">
<caret line="0" column="0" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
<folding />
</state>
</provider>
</entry>
</file>
<file leaf-file-name="config.php" pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/backend/config.php">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="0">
<caret line="0" column="0" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
<folding />
</state>
</provider>
</entry>
</file>
<file leaf-file-name="CalSyncEditContentPanel.js" pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/js/settings/dialogs/CalSyncEditContentPanel.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="0">
<caret line="0" column="63" selection-start-line="0" selection-start-column="63" selection-end-line="0" selection-end-column="63" />
<folding />
</state>
</provider>
</entry>
</file>
</leaf>
</component>
<component name="FileTemplateManagerImpl">
<option name="RECENT_TEMPLATES">
<list>
<option value="JavaScript File" />
</list>
</option>
</component>
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="IdeDocumentHistory">
<option name="CHANGED_PATHS">
<list>
<option value="$PROJECT_DIR$/php/plugin.calendarimporter.php" />
<option value="$PROJECT_DIR$/config.php" />
<option value="$PROJECT_DIR$/js/plugin.calendarimporter.js" />
<option value="$PROJECT_DIR$/php/module.calendar.php" />
<option value="$PROJECT_DIR$/js/data/CalendarHelper.js" />
<option value="$PROJECT_DIR$/js/data/Actions.js" />
<option value="$PROJECT_DIR$/js/dialogs/ImportPanel.js" />
<option value="$PROJECT_DIR$/js/settings/dialogs/CalSyncEditContentPanel.js" />
<option value="$PROJECT_DIR$/js/settings/SettingsWidget.js" />
<option value="$PROJECT_DIR$/js/settings/SettingsCalSyncWidget.js" />
<option value="$PROJECT_DIR$/js/settings/ui/CalSyncGrid.js" />
<option value="$PROJECT_DIR$/js/settings/dialogs/CalSyncEditPanel.js" />
<option value="$PROJECT_DIR$/backend/sync.php" />
<option value="$PROJECT_DIR$/backend/functions.php" />
<option value="$PROJECT_DIR$/changelog.txt" />
</list>
</option>
</component>
<component name="JsBuildToolGruntFileManager" detection-done="true" sorting="DEFINITION_ORDER" />
<component name="JsBuildToolPackageJson" detection-done="true" sorting="DEFINITION_ORDER" />
<component name="JsGulpfileManager">
<detection-done>true</detection-done>
<sorting>DEFINITION_ORDER</sorting>
</component>
<component name="PhpWorkspaceProjectConfiguration" backward_compatibility_performed="true" />
<component name="ProjectFrameBounds">
<option name="x" value="65" />
<option name="y" value="24" />
<option name="width" value="1792" />
<option name="height" value="999" />
</component>
<component name="ProjectLevelVcsManager" settingsEditedManually="false">
<OptionsSetting value="true" id="Add" />
<OptionsSetting value="true" id="Remove" />
<OptionsSetting value="true" id="Checkout" />
<OptionsSetting value="true" id="Update" />
<OptionsSetting value="true" id="Status" />
<OptionsSetting value="true" id="Edit" />
<ConfirmationsSetting value="0" id="Add" />
<ConfirmationsSetting value="0" id="Remove" />
</component>
<component name="ProjectView">
<navigator currentView="ProjectPane" proportions="" version="1">
<flattenPackages />
<showMembers />
<showModules />
<showLibraryContents />
<hideEmptyPackages />
<abbreviatePackageNames />
<autoscrollToSource />
<autoscrollFromSource />
<sortByType />
<manualOrder />
<foldersAlwaysOnTop value="true" />
</navigator>
<panes>
<pane id="Scratches" />
<pane id="ProjectPane">
<subPane>
<PATH>
<PATH_ELEMENT>
<option name="myItemId" value="calendarimporter" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
</PATH_ELEMENT>
</PATH>
<PATH>
<PATH_ELEMENT>
<option name="myItemId" value="calendarimporter" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="calendarimporter" />
<option name="myItemType" value="com.jetbrains.php.projectView.PhpTreeStructureProvider$1" />
</PATH_ELEMENT>
</PATH>
<PATH>
<PATH_ELEMENT>
<option name="myItemId" value="calendarimporter" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="calendarimporter" />
<option name="myItemType" value="com.jetbrains.php.projectView.PhpTreeStructureProvider$1" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="js" />
<option name="myItemType" value="com.jetbrains.php.projectView.PhpTreeStructureProvider$1" />
</PATH_ELEMENT>
</PATH>
<PATH>
<PATH_ELEMENT>
<option name="myItemId" value="calendarimporter" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="calendarimporter" />
<option name="myItemType" value="com.jetbrains.php.projectView.PhpTreeStructureProvider$1" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="js" />
<option name="myItemType" value="com.jetbrains.php.projectView.PhpTreeStructureProvider$1" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="ui" />
<option name="myItemType" value="com.jetbrains.php.projectView.PhpTreeStructureProvider$1" />
</PATH_ELEMENT>
</PATH>
<PATH>
<PATH_ELEMENT>
<option name="myItemId" value="calendarimporter" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="calendarimporter" />
<option name="myItemType" value="com.jetbrains.php.projectView.PhpTreeStructureProvider$1" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="js" />
<option name="myItemType" value="com.jetbrains.php.projectView.PhpTreeStructureProvider$1" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="settings" />
<option name="myItemType" value="com.jetbrains.php.projectView.PhpTreeStructureProvider$1" />
</PATH_ELEMENT>
</PATH>
<PATH>
<PATH_ELEMENT>
<option name="myItemId" value="calendarimporter" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="calendarimporter" />
<option name="myItemType" value="com.jetbrains.php.projectView.PhpTreeStructureProvider$1" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="js" />
<option name="myItemType" value="com.jetbrains.php.projectView.PhpTreeStructureProvider$1" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="settings" />
<option name="myItemType" value="com.jetbrains.php.projectView.PhpTreeStructureProvider$1" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="ui" />
<option name="myItemType" value="com.jetbrains.php.projectView.PhpTreeStructureProvider$1" />
</PATH_ELEMENT>
</PATH>
<PATH>
<PATH_ELEMENT>
<option name="myItemId" value="calendarimporter" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="calendarimporter" />
<option name="myItemType" value="com.jetbrains.php.projectView.PhpTreeStructureProvider$1" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="js" />
<option name="myItemType" value="com.jetbrains.php.projectView.PhpTreeStructureProvider$1" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="settings" />
<option name="myItemType" value="com.jetbrains.php.projectView.PhpTreeStructureProvider$1" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="dialogs" />
<option name="myItemType" value="com.jetbrains.php.projectView.PhpTreeStructureProvider$1" />
</PATH_ELEMENT>
</PATH>
<PATH>
<PATH_ELEMENT>
<option name="myItemId" value="calendarimporter" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="calendarimporter" />
<option name="myItemType" value="com.jetbrains.php.projectView.PhpTreeStructureProvider$1" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="js" />
<option name="myItemType" value="com.jetbrains.php.projectView.PhpTreeStructureProvider$1" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="dialogs" />
<option name="myItemType" value="com.jetbrains.php.projectView.PhpTreeStructureProvider$1" />
</PATH_ELEMENT>
</PATH>
<PATH>
<PATH_ELEMENT>
<option name="myItemId" value="calendarimporter" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="calendarimporter" />
<option name="myItemType" value="com.jetbrains.php.projectView.PhpTreeStructureProvider$1" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="js" />
<option name="myItemType" value="com.jetbrains.php.projectView.PhpTreeStructureProvider$1" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="data" />
<option name="myItemType" value="com.jetbrains.php.projectView.PhpTreeStructureProvider$1" />
</PATH_ELEMENT>
</PATH>
<PATH>
<PATH_ELEMENT>
<option name="myItemId" value="calendarimporter" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="calendarimporter" />
<option name="myItemType" value="com.jetbrains.php.projectView.PhpTreeStructureProvider$1" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="backend" />
<option name="myItemType" value="com.jetbrains.php.projectView.PhpTreeStructureProvider$1" />
</PATH_ELEMENT>
</PATH>
</subPane>
</pane>
<pane id="Scope" />
</panes>
</component>
<component name="PropertiesComponent">
<property name="last_opened_file_path" value="$PROJECT_DIR$" />
<property name="WebServerToolWindowFactoryState" value="false" />
<property name="js-jscs-nodeInterpreter" value="/usr/bin/node" />
</component>
<component name="RunManager">
<configuration default="true" type="JavascriptDebugType" factoryName="JavaScript Debug">
<method />
</configuration>
<configuration default="true" type="NodeJSConfigurationType" factoryName="Node.js" path-to-node="project" working-dir="">
<method />
</configuration>
<configuration default="true" type="PHPUnitRunConfigurationType" factoryName="PHPUnit">
<TestRunner />
<method />
</configuration>
<configuration default="true" type="PhpBehatConfigurationType" factoryName="Behat">
<BehatRunner />
<method />
</configuration>
<configuration default="true" type="PhpLocalRunConfigurationType" factoryName="PHP Console">
<method />
</configuration>
<configuration default="true" type="js.build_tools.gulp" factoryName="Gulp.js">
<method />
</configuration>
<configuration default="true" type="js.build_tools.npm" factoryName="npm">
<command value="run-script" />
<scripts />
<node-interpreter value="project" />
<envs />
<method />
</configuration>
<configuration default="true" type="mocha-javascript-test-runner" factoryName="Mocha">
<node-interpreter>project</node-interpreter>
<node-options />
<working-directory />
<pass-parent-env>true</pass-parent-env>
<envs />
<ui />
<extra-mocha-options />
<test-kind>DIRECTORY</test-kind>
<test-directory />
<recursive>false</recursive>
<method />
</configuration>
</component>
<component name="ShelveChangesManager" show_recycled="false">
<option name="remove_strategy" value="false" />
</component>
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="e7c9c2bb-66f1-4e37-904c-d33d8c113bde" name="Default" comment="" />
<created>1477949602474</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1477949602474</updated>
<workItem from="1477949603566" duration="8606000" />
</task>
<servers />
</component>
<component name="TimeTrackingManager">
<option name="totallyTimeSpent" value="8606000" />
</component>
<component name="ToolWindowManager">
<frame x="65" y="24" width="1792" height="999" extended-state="6" />
<editor active="true" />
<layout>
<window_info id="Project" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="0" side_tool="false" content_ui="combo" />
<window_info id="TODO" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" />
<window_info id="Event Log" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="-1" side_tool="true" content_ui="tabs" />
<window_info id="Database" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" />
<window_info id="Find" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.32900432" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
<window_info id="Version Control" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.32900432" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" />
<window_info id="Structure" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
<window_info id="Terminal" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" />
<window_info id="Favorites" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="-1" side_tool="true" content_ui="tabs" />
<window_info id="Cvs" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" />
<window_info id="Hierarchy" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="2" side_tool="false" content_ui="combo" />
<window_info id="Message" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
<window_info id="Commander" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.4" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
<window_info id="Inspection" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.4" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" />
<window_info id="Run" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" />
<window_info id="Ant Build" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
<window_info id="Debug" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.4" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" />
</layout>
</component>
<component name="Vcs.Log.UiProperties">
<option name="RECENTLY_FILTERED_USER_GROUPS">
<collection />
</option>
<option name="RECENTLY_FILTERED_BRANCH_GROUPS">
<collection />
</option>
</component>
<component name="VcsContentAnnotationSettings">
<option name="myLimit" value="2678400000" />
</component>
<component name="XDebuggerManager">
<breakpoint-manager>
<option name="time" value="1" />
</breakpoint-manager>
<watches-manager />
</component>
<component name="editorHistoryManager">
<entry file="file://$PROJECT_DIR$/Makefile">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="0">
<caret line="0" column="0" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/php/composer.json">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="0">
<caret line="0" column="0" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/php/upload.php">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="0">
<caret line="0" column="0" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/js/data/ResponseHandler.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-456">
<caret line="0" column="0" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
<folding>
<element signature="n#!!doc" expanded="false" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/php/module.calendar.php">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="1057">
<caret line="467" column="64" selection-start-line="467" selection-start-column="64" selection-end-line="467" selection-end-column="64" />
<folding>
<element signature="n#__construct#0;n#CalendarModule#0;n#!!top" expanded="false" />
<element signature="n#execute#0;n#CalendarModule#0;n#!!top" expanded="false" />
<element signature="n#randomstring#0;n#CalendarModule#0;n#!!top" expanded="false" />
<element signature="n#getDurationStringFromMintues#0;n#CalendarModule#0;n#!!top" expanded="false" />
<element signature="n#exportCalendar#0;n#CalendarModule#0;n#!!top" expanded="false" />
<element signature="n#importCalendar#0;n#CalendarModule#0;n#!!top" expanded="false" />
<element signature="n#getAttachmentPath#0;n#CalendarModule#0;n#!!top" expanded="false" />
<element signature="n#loadCalendar#0;n#CalendarModule#0;n#!!top" expanded="false" />
<element signature="n#parseCalendarToArray#0;n#CalendarModule#0;n#!!top" expanded="false" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/config.php">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="60">
<caret line="4" column="60" selection-start-line="4" selection-start-column="12" selection-end-line="4" selection-end-column="60" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/php/plugin.calendarimporter.php">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="569">
<caret line="82" column="40" selection-start-line="82" selection-start-column="29" selection-end-line="82" selection-end-column="40" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/js/dialogs/ImportContentPanel.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-171">
<caret line="0" column="0" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
<folding>
<element signature="n#!!doc" expanded="false" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/js/data/CalendarHelper.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="0">
<caret line="0" column="0" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/js/dialogs/ImportPanel.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="332">
<caret line="245" column="12" selection-start-line="245" selection-start-column="12" selection-end-line="245" selection-end-column="226" />
<folding>
<element signature="n#!!doc" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/js/data/Actions.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="144">
<caret line="133" column="67" selection-start-line="133" selection-start-column="22" selection-end-line="133" selection-end-column="67" />
<folding>
<element signature="n#!!doc" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/js/settings/SettingsWidget.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="340">
<caret line="83" column="82" selection-start-line="83" selection-start-column="82" selection-end-line="83" selection-end-column="82" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/js/plugin.calendarimporter.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-51">
<caret line="262" column="12" selection-start-line="262" selection-start-column="12" selection-end-line="262" selection-end-column="12" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/js/settings/ui/CalSyncGrid.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="504">
<caret line="85" column="59" selection-start-line="85" selection-start-column="59" selection-end-line="85" selection-end-column="59" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/js/settings/SettingsCalSyncWidget.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="729">
<caret line="104" column="16" selection-start-line="104" selection-start-column="16" selection-end-line="104" selection-end-column="142" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/js/settings/dialogs/CalSyncEditContentPanel.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="0">
<caret line="0" column="63" selection-start-line="0" selection-start-column="63" selection-end-line="0" selection-end-column="63" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/js/settings/dialogs/CalSyncEditPanel.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="1480">
<caret line="101" column="30" selection-start-line="101" selection-start-column="30" selection-end-line="101" selection-end-column="30" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/backend/functions.php">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="75">
<caret line="5" column="26" selection-start-line="5" selection-start-column="26" selection-end-line="5" selection-end-column="26" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/backend/config.php">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="0">
<caret line="0" column="0" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/backend/README.txt">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="0">
<caret line="0" column="0" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/backend/sync.php">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="360">
<caret line="24" column="47" selection-start-line="24" selection-start-column="47" selection-end-line="24" selection-end-column="47" />
<folding>
<element signature="e#21#926#0#PHP" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/manifest.xml">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="0">
<caret line="0" column="0" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/changelog.txt">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="75">
<caret line="5" column="19" selection-start-line="5" selection-start-column="19" selection-end-line="5" selection-end-column="19" />
<folding />
</state>
</provider>
</entry>
</component>
</project>

2
Makefile Normal file
View File

@ -0,0 +1,2 @@
default:
ant deploy; cp -r /home/osboxes/Documents/kopano-webapp-3.2.0.285/deploy/plugins/calendarimporter /usr/share/kopano-webapp/plugins/

View File

@ -3,7 +3,7 @@
* functions.php, zarafa calender to ics im/exporter backend * functions.php, zarafa calender to ics im/exporter backend
* *
* Author: Christoph Haas <christoph.h@sprinternet.at> * Author: Christoph Haas <christoph.h@sprinternet.at>
* Copyright (C) 2012-2014 Christoph Haas * Copyright (C) 2012-2016 Christoph Haas
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public

View File

@ -4,7 +4,7 @@
* sync.php, zarafa calender to ics im/exporter backend * sync.php, zarafa calender to ics im/exporter backend
* *
* Author: Christoph Haas <christoph.h@sprinternet.at> * Author: Christoph Haas <christoph.h@sprinternet.at>
* Copyright (C) 2012-2014 Christoph Haas * Copyright (C) 2012-2016 Christoph Haas
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
@ -129,7 +129,7 @@ foreach($userList as $userName => $userData) {
if($icsData != NULL) { if($icsData != NULL) {
file_put_contents($tmpFilename, $icsData); file_put_contents($tmpFilename, $icsData);
echo "Got valid data for " . $syncItem["icsurl"] . " stored in " . $tmpFilename . "\n"; echo "Got valid data for " . $syncItem["icsurl"] . " stored in " . $tmpFilename . "\n";
$result = upload_ics_to_caldav($tmpFilename, $CALDAVURL, $userName, $syncItem["calendar"], $ADMINUSERNAME, $ADMINPASSWORD); $result = upload_ics_to_caldav($tmpFilename, $CALDAVURL, $userName, $syncItem["calendarname"], $ADMINUSERNAME, $ADMINPASSWORD);
if(intval($result) == 200) { if(intval($result) == 200) {
echo "Import completed: $result\n"; echo "Import completed: $result\n";
$result = update_last_sync_date($userStore, $syncItemName); $result = update_last_sync_date($userStore, $syncItemName);

156
build.xml
View File

@ -1,18 +1,15 @@
<project default="all"> <project default="all">
<!--############# CONFIGURE ALL PROPERTIES FOR THE REPLACER HERE ################--> <property environment="env"/>
<property name="plugin_version" value="2.1.0"/> <property name="root-folder" value="${basedir}/../../"/>
<!-- EOC --> <property name="tools-folder" value="${root-folder}/tools/"/>
<property name="target-folder" value="${root-folder}/deploy/plugins"/>
<property name="root-folder" value="${basedir}/../"/> <property name="server-folder" value="${root-folder}/server"/>
<property name="tools-folder" value="${root-folder}/TOOLS/"/>
<property name="target-folder" value="${root-folder}/DEPLOY/plugins"/>
<import file="${tools-folder}/antutil.xml"/> <import file="${tools-folder}/antutil.xml"/>
<typedef file="${tools-folder}/antlib.xml"> <typedef file="${tools-folder}/antlib.xml">
<classpath> <classpath>
<pathelement location="${tools-folder}/tools.jar"/> <pathelement location="${tools-folder}/tools.jar"/>
<pathelement location="${tools-folder}/lib/compiler.jar"/>
</classpath> </classpath>
</typedef> </typedef>
@ -22,29 +19,7 @@
</classpath> </classpath>
</taskdef> </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 --> <!-- Determine plugin name -->
<var name="plugin" unset="true"/>
<basename file="${basedir}" property="plugin"/> <basename file="${basedir}" property="plugin"/>
<!-- The Plugin distribution files --> <!-- The Plugin distribution files -->
@ -54,8 +29,7 @@
<!-- The Plugin CSS files --> <!-- The Plugin CSS files -->
<property name="plugin-css-folder" value="resources/css"/> <property name="plugin-css-folder" value="resources/css"/>
<property name="plugin-css-file" value="${plugin}-min.css"/> <property name="plugin-css-file" value="${plugin}.css"/>
<property name="plugin-css-debug-file" value="${plugin}.css"/>
<!-- Meta target --> <!-- Meta target -->
<target name="all" depends="concat, compress"/> <target name="all" depends="concat, compress"/>
@ -68,16 +42,6 @@
<include name="${plugin-file}"/> <include name="${plugin-file}"/>
<include name="${plugin-debugfile}"/> <include name="${plugin-debugfile}"/>
</fileset> </fileset>
<fileset dir="${target-folder}/${plugin-folder}/php">
<include name="**/*.php"/>
</fileset>
<fileset dir="${target-folder}/${plugin-folder}/resources">
<include name="**/*"/>
</fileset>
<fileset dir="${target-folder}/${plugin-folder}/${plugin-css-folder}">
<include name="${plugin-css-debug-file}"/>
<include name="${plugin-css-file}"/>
</fileset>
</delete> </delete>
</target> </target>
@ -89,27 +53,11 @@
<then> <then>
<mkdir dir="${target-folder}/${plugin-folder}/js"/> <mkdir dir="${target-folder}/${plugin-folder}/js"/>
<echo message="Concatenating: ${plugin-debugfile}"/> <echo message="Concatenating: ${plugin-debugfile}"/>
<!-- TODO: fix JS files for zConcat --> <zConcat outputFolder="${target-folder}/${plugin-folder}/js" outputFile="${plugin-debugfile}" prioritize="\w+">
<!--zConcat outputFolder="${target-folder}/${plugin-folder}/js" outputFile="${plugin-debugfile}" prioritize="\w+">
<concatfiles> <concatfiles>
<fileset dir="js" includes="**/*.js" /> <fileset dir="js" includes="**/*.js" />
</concatfiles> </concatfiles>
</zConcat--> </zConcat>
<concat destfile="${target-folder}/${plugin-folder}/js/${plugin-debugfile}">
<fileset file="js/ABOUT.js" />
<fileset file="js/external/Ext.util.base64.js" />
<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" />
<fileset file="js/settings/SettingsWidget.js" />
<fileset file="js/settings/SettingsCalSyncWidget.js" />
<fileset file="js/settings/ui/CalSyncPanel.js" />
<fileset file="js/settings/ui/CalSyncGrid.js" />
<fileset file="js/settings/dialogs/CalSyncEditContentPanel.js" />
<fileset file="js/settings/dialogs/CalSyncEditPanel.js" />
</concat>
</then> </then>
</if> </if>
@ -118,8 +66,8 @@
<available file="${plugin-css-folder}" type="dir" /> <available file="${plugin-css-folder}" type="dir" />
<then> <then>
<mkdir dir="${target-folder}/${plugin-folder}/${plugin-css-folder}"/> <mkdir dir="${target-folder}/${plugin-folder}/${plugin-css-folder}"/>
<echo message="Concatenating: ${plugin-css-debug-file}"/> <echo message="Concatenating: ${plugin-css-file}"/>
<zConcat outputFolder="${target-folder}/${plugin-folder}/${plugin-css-folder}" outputFile="${plugin-css-debug-file}"> <zConcat outputFolder="${target-folder}/${plugin-folder}/${plugin-css-folder}" outputFile="${plugin-css-file}">
<concatfiles> <concatfiles>
<fileset dir="${plugin-css-folder}" includes="**/*.css" /> <fileset dir="${plugin-css-folder}" includes="**/*.css" />
</concatfiles> </concatfiles>
@ -164,21 +112,15 @@
var pgettext = function(msgctxt, msgid) {}; var pgettext = function(msgctxt, msgid) {};
</externs> </externs>
</zCompile> </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> </then>
</if> </if>
</target> </target>
<!-- syntax check all PHP files --> <!-- syntax check all PHP files -->
<target name="validate"> <target name="validate">
<if>
<available file="php" filepath="${env.PATH}" />
<then>
<if> <if>
<available file="config.php" type="file" /> <available file="config.php" type="file" />
<then> <then>
@ -193,92 +135,66 @@
<foreach target="syntax-check" param="file"> <foreach target="syntax-check" param="file">
<path> <path>
<fileset dir="."> <fileset dir=".">
<exclude name="php/vendor/**" />
<include name="**/*.php"/> <include name="**/*.php"/>
</fileset> </fileset>
</path> </path>
</foreach> </foreach>
</then> </then>
</if> </if>
</then>
<else>
<echo message="WARNING: PHP not available, not performing syntax-check on php files"/>
</else>
</if>
</target> </target>
<target name="syntax-check"> <target name="syntax-check">
<echo message="validating ${file}"/> <echo message="validating ${file}"/>
<exec executable="php" failonerror="true" failifexecutionfails="false"> <exec executable="php" failonerror="true">
<arg value="-l"/> <arg value="-l"/>
<arg value="${file}"/> <arg value="${file}"/>
</exec> </exec>
</target> </target>
<!-- on windows we do not check the xml file --> <!-- Install all files into the target folder -->
<target name="xml-os-sel" depends="xml-check,xml-copy"> <target name="deploy" depends="compress, validate">
<echo>Processing manifest.xml</echo> <mkdir dir="${target-folder}/${plugin-folder}"/>
</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 --> <!-- Copy (and validate) manifest.xml -->
<exec executable="xmllint" output="${target-folder}/${plugin-folder}/manifest.xml" failonerror="true" error="/dev/stdout" failifexecutionfails="false"> <if>
<available file="xmllint" filepath="${env.PATH}" />
<then>
<exec executable="xmllint" output="${target-folder}/${plugin-folder}/manifest.xml" failonerror="true">
<arg value="--valid"/> <arg value="--valid"/>
<arg value="--path"/> <arg value="--path"/>
<arg value="${root-folder}/server"/> <arg value="${server-folder}"/>
<arg value="manifest.xml"/> <arg value="manifest.xml"/>
</exec> </exec>
</target> </then>
<else>
<!-- check manifest.xml if we are on windows... --> <echo message="WARNING: xmllint not available, not performing syntax-check on manifest.xml"/>
<target name="xml-copy" if="isWindows"> <!-- xmllint is not available, so we must copy the file manually -->
<echo message="Copying xml: manifest.xml" />
<!-- Copy manifest.xml -->
<copy todir="${target-folder}/${plugin-folder}"> <copy todir="${target-folder}/${plugin-folder}">
<fileset dir="."> <fileset dir=".">
<include name="manifest.xml"/> <include name="manifest.xml"/>
</fileset> </fileset>
</copy> </copy>
</target> </else>
</if>
<!-- 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 files -->
<copy todir="${target-folder}/${plugin-folder}"> <copy todir="${target-folder}/${plugin-folder}">
<fileset dir="."> <fileset dir=".">
<include name="resources/**/*"/> <include name="resources/**/*.*"/>
<include name="external/**/*.*"/>
<include name="php/**/*.php"/> <include name="php/**/*.php"/>
<include name="config.php"/> <include name="config.php"/>
<include name="changelog.txt"/>
<!-- exclude the ant script --> <!-- exclude the ant script -->
<exclude name="build.xml"/> <exclude name="build.xml"/>
<!-- CSS is generated during build --> <!-- CSS is generated during build -->
<exclude name="resources/css/*.*"/> <exclude name="resources/css/*.*"/>
</fileset> </fileset>
</copy> </copy>
<!-- replace all variables... -->
<replace file="${target-folder}/${plugin-folder}/manifest.xml" token="@_@PLUGIN_VERSION@_@" value="${plugin_version}" />
</target> </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> </project>

View File

@ -1,3 +1,10 @@
calendarimporter 2.2.0:
- support for Kopano Webapp 3.1.1
- Code rework
- Calendar export improved
- Calendar import improved
- GUI improvements
calendarimporter 2.1.0: calendarimporter 2.1.0:
- ics sync is now implemented - ics sync is now implemented

View File

@ -1,15 +1,13 @@
<?php <?php
/** Disable the import plugin for all clients */ /** Disable the import plugin for all clients */
define('PLUGIN_CALENDARIMPORTER_USER_DEFAULT_ENABLE', false); define('PLUGIN_CALENDARIMPORTER_USER_DEFAULT_ENABLE', true);
/** Disable the export feature for all clients */
define('PLUGIN_CALENDARIMPORTER_USER_DEFAULT_ENABLE_EXPORT', false);
/** Disable the sync feature for all clients */ /** Disable the sync feature for all clients */
define('PLUGIN_CALENDARIMPORTER_USER_DEFAULT_ENABLE_SYNC', true); // not yet implemented define('PLUGIN_CALENDARIMPORTER_USER_DEFAULT_ENABLE_SYNC', true); // not yet implemented
/** The default calendar to import to*/ /** The default calendar to import to*/
define('PLUGIN_CALENDARIMPORTER_DEFAULT', "calendar"); define('PLUGIN_CALENDARIMPORTER_DEFAULT', "Kalender");
define('PLUGIN_CALENDARIMPORTER_DEFAULT_TIMEZONE', "Europe/Vienna"); define('PLUGIN_CALENDARIMPORTER_DEFAULT_TIMEZONE', "Europe/Vienna");
/** Tempory path for uploaded files... */ /** Tempory path for uploaded files... */
define('PLUGIN_CALENDARIMPORTER_TMP_UPLOAD', "/var/lib/zarafa-webapp/tmp/"); define('PLUGIN_CALENDARIMPORTER_TMP_UPLOAD', "/var/lib/kopano-webapp/tmp/");
?> ?>

View File

@ -1,87 +0,0 @@
/**
* A small tool to create our timezone mappings list =)
*/
import java.util.TimeZone;
public class Main {
static String map[] = new String[51]; // one field = 30 minutes step
private static void initMap() {
map[0] = "Pacific/Midway";
map[1] = "";
map[2] = "Pacific/Fakaofo";
map[3] = "Pacific/Marquesas";
map[4] = "America/Anchorage";
map[5] = "";
map[6] = "America/Dawson";
map[7] = "";
map[8] = "America/Dawson_Creek";
map[9] = "";
map[10] = "America/Chicago";
map[11] = "America/Caracas";
map[12] = "America/Detroit";
map[13] = "America/Caracas";
map[14] = "America/Santiago";
map[15] = "America/St_Johns";
map[16] = "America/Sao_Paulo";
map[17] = "";
map[18] = "America/Noronha";
map[19] = "";
map[20] = "Atlantic/Cape_Verde";
map[21] = "";
map[22] = "Africa/Abidjan";
map[23] = "";
map[24] = "Europe/Vienna";
map[25] = "";
map[26] = "Asia/Jerusalem";
map[27] = "";
map[28] = "Africa/Addis_Ababa";
map[29] = "Asia/Tehran";
map[30] = "Asia/Dubai";
map[31] = "Asia/Kabul";
map[32] = "Antarctica/Mawson";
map[33] = "Asia/Colombo";
map[34] = "Antarctica/Vostok";
map[35] = "Asia/Rangoon";
map[36] = "Antarctica/Davis";
map[37] = "";
map[38] = "Antarctica/Casey";
map[39] = "";
map[40] = "Asia/Dili";
map[41] = "Australia/Darwin";
map[42] = "Australia/Currie";
map[43] = "Australia/Lord_Howe";
map[44] = "Antarctica/Macquarie";
map[45] = "Pacific/Norfolk";
map[46] = "Antarctica/McMurdo";
map[47] = "";
map[48] = "Pacific/Enderbury";
map[49] = "";
map[50] = "Pacific/Kiritimati";
}
/**
* @param args
*/
public static void main(String[] args) {
initMap();
int i = 0;
for(int time = -660; time < 900; time += 30) {
int hours = time / 60;
int minutes = Math.abs(time) % 60;
String[] avaiId = TimeZone.getAvailableIDs(time*60*1000);
if(avaiId.length > 0) {
System.out.printf("\t\t/*%+d:%02d*/\n", hours, minutes);
for (String string : avaiId) {
System.out.println("\t\t'" + string + "' : '" + map[i] + "',");
}
}
i++;
}
}
}

View File

@ -2,7 +2,7 @@
* ABOUT.js zarafa calender to ics im/exporter * ABOUT.js zarafa calender to ics im/exporter
* *
* Author: Christoph Haas <christoph.h@sprinternet.at> * Author: Christoph Haas <christoph.h@sprinternet.at>
* Copyright (C) 2012-2013 Christoph Haas * Copyright (C) 2012-2016 Christoph Haas
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
@ -29,7 +29,7 @@ Ext.namespace('Zarafa.plugins.calendarimporter');
* The copyright string holding the copyright notice for the Zarafa calendarimporter Plugin. * The copyright string holding the copyright notice for the Zarafa calendarimporter Plugin.
*/ */
Zarafa.plugins.calendarimporter.ABOUT = "" Zarafa.plugins.calendarimporter.ABOUT = ""
+ "<p>Copyright (C) 2012-2013 Christoph Haas &lt;christoph.h@sprinternet.at&gt;</p>" + "<p>Copyright (C) 2012-2016 Christoph Haas &lt;christoph.h@sprinternet.at&gt;</p>"
+ "<p>This program is free software; you can redistribute it and/or " + "<p>This program is free software; you can redistribute it and/or "
+ "modify it under the terms of the GNU Lesser General Public " + "modify it under the terms of the GNU Lesser General Public "

178
js/data/Actions.js Normal file
View File

@ -0,0 +1,178 @@
/**
* Actions.js zarafa calender to ics im/exporter
*
* Author: Christoph Haas <christoph.h@sprinternet.at>
* Copyright (C) 2012-2016 Christoph Haas
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
/**
* ResponseHandler
*
* This class handles all responses from the php backend
*/
Ext.namespace('Zarafa.plugins.calendarimporter.data');
/**
* @class Zarafa.plugins.calendarimporter.data.Actions
* Common actions which can be used within {@link Ext.Button buttons}
* or other {@link Ext.Component components} with action handlers.
* @singleton
*/
Zarafa.plugins.calendarimporter.data.Actions = {
/**
* Callback for the export request.
* @param {Object} response
*/
downloadICS: function (response) {
if (response.status == false) {
Zarafa.common.dialogs.MessageBox.show({
title : dgettext('plugin_files', 'Warning'),
msg : dgettext('plugin_files', response.message),
icon : Zarafa.common.dialogs.MessageBox.WARNING,
buttons: Zarafa.common.dialogs.MessageBox.OK
});
} else {
var downloadFrame = Ext.getBody().createChild({
tag: 'iframe',
cls: 'x-hidden'
});
var url = document.URL;
var link = url.substring(0, url.lastIndexOf('/') + 1);
link += "index.php?sessionid=" + container.getUser().getSessionId() + "&load=custom&name=download_ics";
link = Ext.urlAppend(link, "token=" + encodeURIComponent(response.download_token));
link = Ext.urlAppend(link, "filename=" + encodeURIComponent(response.filename));
downloadFrame.dom.contentWindow.location = link;
}
},
/**
* Get all calendar folders.
* @param {boolean} asDropdownStore If true, a simple array store will be returned.
* @returns {*}
*/
getAllCalendarFolders: function (asDropdownStore) {
asDropdownStore = Ext.isEmpty(asDropdownStore) ? false : asDropdownStore;
var allFolders = [];
var inbox = container.getHierarchyStore().getDefaultStore();
var pub = container.getHierarchyStore().getPublicStore();
if (!Ext.isEmpty(inbox.subStores) && inbox.subStores.folders.totalLength > 0) {
for (var i = 0; i < inbox.subStores.folders.totalLength; i++) {
var folder = inbox.subStores.folders.getAt(i);
if (folder.get("container_class") == "IPF.Appointment") {
if (asDropdownStore) {
allFolders.push([
folder.get("entryid"),
folder.get("display_name")
]);
} else {
allFolders.push({
display_name : folder.get("display_name"),
entryid : folder.get("entryid"),
store_entryid: folder.get("store_entryid"),
is_public : false
});
}
}
}
}
if (!Ext.isEmpty(pub.subStores) && pub.subStores.folders.totalLength > 0) {
for (var j = 0; j < pub.subStores.folders.totalLength; j++) {
var folder = pub.subStores.folders.getAt(j);
if (folder.get("container_class") == "IPF.Appointment") {
if (asDropdownStore) {
allFolders.push([
folder.get("entryid"),
folder.get("display_name") + " (Public)"
]);
} else {
allFolders.push({
display_name : folder.get("display_name"),
entryid : folder.get("entryid"),
store_entryid: folder.get("store_entryid"),
is_public : true
});
}
}
}
}
if (asDropdownStore) {
return allFolders.sort(Zarafa.plugins.calendarimporter.data.Actions.dynamicSort(1));
} else {
return allFolders;
}
},
/**
* Return a calendar folder element by name.
* @param {string} name
* @returns {*}
*/
getCalendarFolderByName: function (name) {
var folders = Zarafa.plugins.calendarimporter.data.Actions.getAllCalendarFolders(false);
for (var i = 0; i < folders.length; i++) {
if (folders[i].display_name == name) {
return folders[i];
}
}
return container.getHierarchyStore().getDefaultFolder('calendar');
},
/**
* Return a calendar folder element by entryid.
* @param {string} entryid
* @returns {*}
*/
getCalendarFolderByEntryid: function (entryid) {
var folders = Zarafa.plugins.calendarimporter.data.Actions.getAllCalendarFolders(false);
for (var i = 0; i < folders.length; i++) {
if (folders[i].entryid == entryid) {
return folders[i];
}
}
return container.getHierarchyStore().getDefaultFolder('calendar');
},
/**
* Dynamic sort function, sorts by property name.
* @param {string|int} property
* @returns {Function}
*/
dynamicSort: function (property) {
var sortOrder = 1;
if (property[0] === "-") {
sortOrder = -1;
property = property.substr(1);
}
return function (a, b) {
var result = (a[property].toLowerCase() < b[property].toLowerCase()) ? -1 : (a[property].toLowerCase() > b[property].toLowerCase()) ? 1 : 0;
return result * sortOrder;
}
}
};

View File

@ -2,7 +2,7 @@
* ResponseHandler.js zarafa calender to ics im/exporter * ResponseHandler.js zarafa calender to ics im/exporter
* *
* Author: Christoph Haas <christoph.h@sprinternet.at> * Author: Christoph Haas <christoph.h@sprinternet.at>
* Copyright (C) 2012-2013 Christoph Haas * Copyright (C) 2012-2016 Christoph Haas
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
@ -38,13 +38,13 @@ Zarafa.plugins.calendarimporter.data.ResponseHandler = Ext.extend(Zarafa.core.da
* @cfg {Function} successCallback The function which * @cfg {Function} successCallback The function which
* will be called after success request. * will be called after success request.
*/ */
successCallback : null, successCallback: null,
/** /**
* Call the successCallback callback function. * Call the successCallback callback function.
* @param {Object} response Object contained the response data. * @param {Object} response Object contained the response data.
*/ */
doExport : function(response) { doExport: function (response) {
this.successCallback(response); this.successCallback(response);
}, },
@ -52,7 +52,7 @@ Zarafa.plugins.calendarimporter.data.ResponseHandler = Ext.extend(Zarafa.core.da
* Call the successCallback callback function. * Call the successCallback callback function.
* @param {Object} response Object contained the response data. * @param {Object} response Object contained the response data.
*/ */
doList : function(response) { doLoad: function (response) {
this.successCallback(response); this.successCallback(response);
}, },
@ -60,7 +60,7 @@ Zarafa.plugins.calendarimporter.data.ResponseHandler = Ext.extend(Zarafa.core.da
* Call the successCallback callback function. * Call the successCallback callback function.
* @param {Object} response Object contained the response data. * @param {Object} response Object contained the response data.
*/ */
doImport : function(response) { doImport: function (response) {
this.successCallback(response); this.successCallback(response);
}, },
@ -68,7 +68,7 @@ Zarafa.plugins.calendarimporter.data.ResponseHandler = Ext.extend(Zarafa.core.da
* Call the successCallback callback function. * Call the successCallback callback function.
* @param {Object} response Object contained the response data. * @param {Object} response Object contained the response data.
*/ */
doAttachmentpath : function(response) { doImportattachment: function (response) {
this.successCallback(response); this.successCallback(response);
}, },
@ -77,7 +77,7 @@ Zarafa.plugins.calendarimporter.data.ResponseHandler = Ext.extend(Zarafa.core.da
* exception response with the code of exception. * exception response with the code of exception.
* @param {Object} response Object contained the response data. * @param {Object} response Object contained the response data.
*/ */
doError: function(response) { doError: function (response) {
alert("error response code: " + response.error.info.code); alert("error response code: " + response.error.info.code);
} }
}); });

View File

@ -2,7 +2,7 @@
* timezones.js zarafa calender to ics im/exporter * timezones.js zarafa calender to ics im/exporter
* *
* Author: Christoph Haas <christoph.h@sprinternet.at> * Author: Christoph Haas <christoph.h@sprinternet.at>
* Copyright (C) 2012-2013 Christoph Haas * Copyright (C) 2012-2016 Christoph Haas
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
@ -29,76 +29,76 @@ Ext.namespace("Zarafa.plugins.calendarimporter.data");
Zarafa.plugins.calendarimporter.data.Timezones = Ext.extend(Object, { Zarafa.plugins.calendarimporter.data.Timezones = Ext.extend(Object, {
store : [ store: [
['Pacific/Midway','(UTC -11:00) Midway, Niue, Pago Pago', -660], ['Pacific/Midway', '(UTC -11:00) Midway, Niue, Pago Pago', -660],
['Pacific/Fakaofo','(UTC -10:00) Adak, Fakaofo, Honolulu, Johnston, Rarotonga, Tahiti', -600], ['Pacific/Fakaofo', '(UTC -10:00) Adak, Fakaofo, Honolulu, Johnston, Rarotonga, Tahiti', -600],
['Pacific/Marquesas','(UTC -09:30) Marquesas', -570], ['Pacific/Marquesas', '(UTC -09:30) Marquesas', -570],
['America/Anchorage','(UTC -09:00) Gambier, Anchorage, Juneau, Nome, Sitka, Yakutat', -540], ['America/Anchorage', '(UTC -09:00) Gambier, Anchorage, Juneau, Nome, Sitka, Yakutat', -540],
['America/Dawson','(UTC -08:00) Dawson, Los Angeles, Tijuana, Vancouver, Whitehorse, Santa Isabel, Metlakatla, Pitcairn', -480], ['America/Dawson', '(UTC -08:00) Dawson, Los Angeles, Tijuana, Vancouver, Whitehorse, Santa Isabel, Metlakatla, Pitcairn', -480],
['America/Dawson_Creek','(UTC -07:00) Dawson Creek, Hermosillo, Phoenix, Chihuahua, Mazatlan, Boise, Cambridge Bay, Denver, Edmonton, Inuvik, Ojinaga, Shiprock, Yellowknife', -420], ['America/Dawson_Creek', '(UTC -07:00) Dawson Creek, Hermosillo, Phoenix, Chihuahua, Mazatlan, Boise, Cambridge Bay, Denver, Edmonton, Inuvik, Ojinaga, Shiprock, Yellowknife', -420],
['America/Chicago','(UTC -06:00) Beulah, Center, Chicago, Knox, Matamoros, Menominee, New Salem, Rainy River, Rankin Inlet, Resolute, Tell City, Winnipeg', -360], ['America/Chicago', '(UTC -06:00) Beulah, Center, Chicago, Knox, Matamoros, Menominee, New Salem, Rainy River, Rankin Inlet, Resolute, Tell City, Winnipeg', -360],
['America/Belize','(UTC -06:00) Belize, Costa Rica, El Salvador, Galapagos, Guatemala, Managua, Regina, Swift Current, Tegucigalpa', -360], ['America/Belize', '(UTC -06:00) Belize, Costa Rica, El Salvador, Galapagos, Guatemala, Managua, Regina, Swift Current, Tegucigalpa', -360],
['Pacific/Easter','(UTC -06:00) Easter', -360], ['Pacific/Easter', '(UTC -06:00) Easter', -360],
['America/Bahia_Banderas','(UTC -06:00) Bahia Banderas, Cancun, Merida, Mexico City, Monterrey', -360], ['America/Bahia_Banderas', '(UTC -06:00) Bahia Banderas, Cancun, Merida, Mexico City, Monterrey', -360],
['America/Detroit','(UTC -05:00) Detroit, Grand Turk, Indianapolis, Iqaluit, Louisville, Marengo, Monticello, Montreal, Nassau, New York, Nipigon, Pangnirtung, Petersburg, Thunder Bay, Toronto, Vevay, Vincennes, Winamac', -300], ['America/Detroit', '(UTC -05:00) Detroit, Grand Turk, Indianapolis, Iqaluit, Louisville, Marengo, Monticello, Montreal, Nassau, New York, Nipigon, Pangnirtung, Petersburg, Thunder Bay, Toronto, Vevay, Vincennes, Winamac', -300],
['America/Atikokan','(UTC -05:00) Atikokan, Bogota, Cayman, Guayaquil, Jamaica, Lima, Panama, Port-au-Prince', -300], ['America/Atikokan', '(UTC -05:00) Atikokan, Bogota, Cayman, Guayaquil, Jamaica, Lima, Panama, Port-au-Prince', -300],
['America/Havana','(UTC -05:00) Havana', -300], ['America/Havana', '(UTC -05:00) Havana', -300],
['America/Caracas','(UTC -04:30) Caracas', -270], ['America/Caracas', '(UTC -04:30) Caracas', -270],
['America/Glace_Bay','(UTC -04:00) Bermuda, Glace Bay, Goose Bay, Halifax, Moncton, Thule', -240], ['America/Glace_Bay', '(UTC -04:00) Bermuda, Glace Bay, Goose Bay, Halifax, Moncton, Thule', -240],
['Atlantic/Stanley','(UTC -04:00) Stanley', -240], ['Atlantic/Stanley', '(UTC -04:00) Stanley', -240],
['America/Santiago','(UTC -04:00) Palmer, Santiago', -240], ['America/Santiago', '(UTC -04:00) Palmer, Santiago', -240],
['America/Anguilla','(UTC -04:00) Anguilla, Antigua, Aruba, Barbados, Blanc-Sablon, Boa Vista, Curacao, Dominica, Eirunepe, Grenada, Guadeloupe, Guyana, Kralendijk, La Paz, Lower Princes, Manaus, Marigot, Martinique, Montserrat, Port of Spain, Porto Velho, Puerto Rico, Rio Branco, Santo Domingo, St Barthelemy, St Kitts, St Lucia, St Thomas, St Vincent, Tortola', -240], ['America/Anguilla', '(UTC -04:00) Anguilla, Antigua, Aruba, Barbados, Blanc-Sablon, Boa Vista, Curacao, Dominica, Eirunepe, Grenada, Guadeloupe, Guyana, Kralendijk, La Paz, Lower Princes, Manaus, Marigot, Martinique, Montserrat, Port of Spain, Porto Velho, Puerto Rico, Rio Branco, Santo Domingo, St Barthelemy, St Kitts, St Lucia, St Thomas, St Vincent, Tortola', -240],
['America/Campo_Grande','(UTC -04:00) Campo Grande, Cuiaba', -240], ['America/Campo_Grande', '(UTC -04:00) Campo Grande, Cuiaba', -240],
['America/Asuncion','(UTC -04:00) Asuncion', -240], ['America/Asuncion', '(UTC -04:00) Asuncion', -240],
['America/St_Johns','(UTC -03:30) St Johns', -210], ['America/St_Johns', '(UTC -03:30) St Johns', -210],
['America/Sao_Paulo','(UTC -03:00) Sao Paulo', -180], ['America/Sao_Paulo', '(UTC -03:00) Sao Paulo', -180],
['America/Araguaina','(UTC -03:00) Araguaina, Bahia, Belem, Buenos Aires, Catamarca, Cayenne, Cordoba, Fortaleza, Jujuy, La Rioja, Maceio, Mendoza, Paramaribo, Recife, Rio Gallegos, Rothera, Salta, San Juan, Santarem, Tucuman, Ushuaia', -180], ['America/Araguaina', '(UTC -03:00) Araguaina, Bahia, Belem, Buenos Aires, Catamarca, Cayenne, Cordoba, Fortaleza, Jujuy, La Rioja, Maceio, Mendoza, Paramaribo, Recife, Rio Gallegos, Rothera, Salta, San Juan, Santarem, Tucuman, Ushuaia', -180],
['America/Montevideo','(UTC -03:00) Montevideo', -180], ['America/Montevideo', '(UTC -03:00) Montevideo', -180],
['America/Godthab','(UTC -03:00) Godthab', -180], ['America/Godthab', '(UTC -03:00) Godthab', -180],
['America/Argentina/San_Luis','(UTC -03:00) San Luis', -180], ['America/Argentina/San_Luis', '(UTC -03:00) San Luis', -180],
['America/Miquelon','(UTC -03:00) Miquelon', -180], ['America/Miquelon', '(UTC -03:00) Miquelon', -180],
['America/Noronha','(UTC -02:00) Noronha, South Georgia', -120], ['America/Noronha', '(UTC -02:00) Noronha, South Georgia', -120],
['Atlantic/Cape_Verde','(UTC -01:00) Cape Verde', -60], ['Atlantic/Cape_Verde', '(UTC -01:00) Cape Verde', -60],
['America/Scoresbysund','(UTC -01:00) Azores, Scoresbysund', -60], ['America/Scoresbysund', '(UTC -01:00) Azores, Scoresbysund', -60],
['Atlantic/Canary','(UTC) Canary, Dublin, Faroe, Guernsey, Isle of Man, Jersey, Lisbon, London, Madeira', 0], ['Atlantic/Canary', '(UTC) Canary, Dublin, Faroe, Guernsey, Isle of Man, Jersey, Lisbon, London, Madeira', 0],
['Africa/Abidjan','(UTC) Abidjan, Accra, Bamako, Banjul, Bissau, Casablanca, Conakry, Dakar, Danmarkshavn, El Aaiun, Freetown, Lome, Monrovia, Nouakchott, Ouagadougou, Reykjavik, Sao Tome, St Helena', 0], ['Africa/Abidjan', '(UTC) Abidjan, Accra, Bamako, Banjul, Bissau, Casablanca, Conakry, Dakar, Danmarkshavn, El Aaiun, Freetown, Lome, Monrovia, Nouakchott, Ouagadougou, Reykjavik, Sao Tome, St Helena', 0],
['Africa/Algiers','(UTC +01:00) Algiers, Bangui, Brazzaville, Douala, Kinshasa, Lagos, Libreville, Luanda, Malabo, Ndjamena, Niamey, Porto-Novo, Tunis', 60], ['Africa/Algiers', '(UTC +01:00) Algiers, Bangui, Brazzaville, Douala, Kinshasa, Lagos, Libreville, Luanda, Malabo, Ndjamena, Niamey, Porto-Novo, Tunis', 60],
['Europe/Vienna','(UTC +01:00) Amsterdam, Andorra, Belgrade, Berlin, Bratislava, Brussels, Budapest, Ceuta, Copenhagen, Gibraltar, Ljubljana, Longyearbyen, Luxembourg, Madrid, Malta, Monaco, Oslo, Paris, Podgorica, Prague, Rome, San Marino, Sarajevo, Skopje, Stockholm, Tirane, Vaduz, Vatican, Vienna, Warsaw, Zagreb, Zurich', 60], ['Europe/Vienna', '(UTC +01:00) Amsterdam, Andorra, Belgrade, Berlin, Bratislava, Brussels, Budapest, Ceuta, Copenhagen, Gibraltar, Ljubljana, Longyearbyen, Luxembourg, Madrid, Malta, Monaco, Oslo, Paris, Podgorica, Prague, Rome, San Marino, Sarajevo, Skopje, Stockholm, Tirane, Vaduz, Vatican, Vienna, Warsaw, Zagreb, Zurich', 60],
['Africa/Windhoek','(UTC +01:00) Windhoek', 60], ['Africa/Windhoek', '(UTC +01:00) Windhoek', 60],
['Asia/Damascus','(UTC +02:00) Damascus', 120], ['Asia/Damascus', '(UTC +02:00) Damascus', 120],
['Asia/Beirut','(UTC +02:00) Beirut', 120], ['Asia/Beirut', '(UTC +02:00) Beirut', 120],
['Asia/Jerusalem','(UTC +02:00) Jerusalem', 120], ['Asia/Jerusalem', '(UTC +02:00) Jerusalem', 120],
['Asia/Nicosia','(UTC +02:00) Athens, Bucharest, Chisinau, Helsinki, Istanbul, Mariehamn, Nicosia, Riga, Sofia, Tallinn, Vilnius', 120], ['Asia/Nicosia', '(UTC +02:00) Athens, Bucharest, Chisinau, Helsinki, Istanbul, Mariehamn, Nicosia, Riga, Sofia, Tallinn, Vilnius', 120],
['Africa/Blantyre','(UTC +02:00) Blantyre, Bujumbura, Cairo, Gaborone, Gaza, Harare, Hebron, Johannesburg, Kigali, Lubumbashi, Lusaka, Maputo, Maseru, Mbabane, Tripoli', 120], ['Africa/Blantyre', '(UTC +02:00) Blantyre, Bujumbura, Cairo, Gaborone, Gaza, Harare, Hebron, Johannesburg, Kigali, Lubumbashi, Lusaka, Maputo, Maseru, Mbabane, Tripoli', 120],
['Asia/Amman','(UTC +02:00) Amman', 120], ['Asia/Amman', '(UTC +02:00) Amman', 120],
['Africa/Addis_Ababa','(UTC +03:00) Addis Ababa, Aden, Antananarivo, Asmara, Baghdad, Bahrain, Comoro, Dar es Salaam, Djibouti, Juba, Kaliningrad, Kampala, Khartoum, Kiev, Kuwait, Mayotte, Minsk, Mogadishu, Nairobi, Qatar, Riyadh, Simferopol, Syowa, Uzhgorod, Zaporozhye', 180], ['Africa/Addis_Ababa', '(UTC +03:00) Addis Ababa, Aden, Antananarivo, Asmara, Baghdad, Bahrain, Comoro, Dar es Salaam, Djibouti, Juba, Kaliningrad, Kampala, Khartoum, Kiev, Kuwait, Mayotte, Minsk, Mogadishu, Nairobi, Qatar, Riyadh, Simferopol, Syowa, Uzhgorod, Zaporozhye', 180],
['Asia/Tehran','(UTC +03:30) Tehran', 210], ['Asia/Tehran', '(UTC +03:30) Tehran', 210],
['Asia/Yerevan','(UTC +04:00) Yerevan', 240], ['Asia/Yerevan', '(UTC +04:00) Yerevan', 240],
['Asia/Dubai','(UTC +04:00) Dubai, Mahe, Mauritius, Moscow, Muscat, Reunion, Samara, Tbilisi, Volgograd', 240], ['Asia/Dubai', '(UTC +04:00) Dubai, Mahe, Mauritius, Moscow, Muscat, Reunion, Samara, Tbilisi, Volgograd', 240],
['Asia/Baku','(UTC +04:00) Baku', 240], ['Asia/Baku', '(UTC +04:00) Baku', 240],
['Asia/Kabul','(UTC +04:30) Kabul', 270], ['Asia/Kabul', '(UTC +04:30) Kabul', 270],
['Antarctica/Mawson','(UTC +05:00) Aqtau, Aqtobe, Ashgabat, Dushanbe, Karachi, Kerguelen, Maldives, Mawson, Oral, Samarkand, Tashkent', 300], ['Antarctica/Mawson', '(UTC +05:00) Aqtau, Aqtobe, Ashgabat, Dushanbe, Karachi, Kerguelen, Maldives, Mawson, Oral, Samarkand, Tashkent', 300],
['Asia/Colombo','(UTC +05:30) Colombo, Kolkata', 330], ['Asia/Colombo', '(UTC +05:30) Colombo, Kolkata', 330],
['Asia/Kathmandu','(UTC +05:45) Kathmandu', 345], ['Asia/Kathmandu', '(UTC +05:45) Kathmandu', 345],
['Antarctica/Vostok','(UTC +06:00) Almaty, Bishkek, Chagos, Dhaka, Qyzylorda, Thimphu, Vostok, Yekaterinburg', 360], ['Antarctica/Vostok', '(UTC +06:00) Almaty, Bishkek, Chagos, Dhaka, Qyzylorda, Thimphu, Vostok, Yekaterinburg', 360],
['Asia/Rangoon','(UTC +06:30) Cocos, Rangoon', 390], ['Asia/Rangoon', '(UTC +06:30) Cocos, Rangoon', 390],
['Antarctica/Davis','(UTC +07:00) Bangkok, Christmas, Davis, Ho Chi Minh, Hovd, Jakarta, Novokuznetsk, Novosibirsk, Omsk, Phnom Penh, Pontianak, Vientiane', 420], ['Antarctica/Davis', '(UTC +07:00) Bangkok, Christmas, Davis, Ho Chi Minh, Hovd, Jakarta, Novokuznetsk, Novosibirsk, Omsk, Phnom Penh, Pontianak, Vientiane', 420],
['Antarctica/Casey','(UTC +08:00) Brunei, Casey, Choibalsan, Chongqing, Harbin, Hong Kong, Kashgar, Krasnoyarsk, Kuala Lumpur, Kuching, Macau, Makassar, Manila, Perth, Shanghai, Singapore, Taipei, Ulaanbaatar, Urumqi', 480], ['Antarctica/Casey', '(UTC +08:00) Brunei, Casey, Choibalsan, Chongqing, Harbin, Hong Kong, Kashgar, Krasnoyarsk, Kuala Lumpur, Kuching, Macau, Makassar, Manila, Perth, Shanghai, Singapore, Taipei, Ulaanbaatar, Urumqi', 480],
['Australia/Eucla','(UTC +08:45) Eucla', 525], ['Australia/Eucla', '(UTC +08:45) Eucla', 525],
['Asia/Dili','(UTC +09:00) Dili, Irkutsk, Jayapura, Palau, Pyongyang, Seoul, Tokyo', 540], ['Asia/Dili', '(UTC +09:00) Dili, Irkutsk, Jayapura, Palau, Pyongyang, Seoul, Tokyo', 540],
['Australia/Adelaide','(UTC +09:30) Adelaide, Broken Hill', 570], ['Australia/Adelaide', '(UTC +09:30) Adelaide, Broken Hill', 570],
['Australia/Darwin','(UTC +09:30) Darwin', 570], ['Australia/Darwin', '(UTC +09:30) Darwin', 570],
['Antarctica/DumontDUrville','(UTC +10:00) Brisbane, Chuuk, DumontDUrville, Guam, Lindeman, Port Moresby, Saipan, Yakutsk', 600], ['Antarctica/DumontDUrville', '(UTC +10:00) Brisbane, Chuuk, DumontDUrville, Guam, Lindeman, Port Moresby, Saipan, Yakutsk', 600],
['Australia/Currie','(UTC +10:00) Currie, Hobart, Melbourne, Sydney', 600], ['Australia/Currie', '(UTC +10:00) Currie, Hobart, Melbourne, Sydney', 600],
['Australia/Lord_Howe','(UTC +10:30) Lord Howe', 630], ['Australia/Lord_Howe', '(UTC +10:30) Lord Howe', 630],
['Antarctica/Macquarie','(UTC +11:00) Efate, Guadalcanal, Kosrae, Macquarie, Noumea, Pohnpei, Sakhalin, Vladivostok', 660], ['Antarctica/Macquarie', '(UTC +11:00) Efate, Guadalcanal, Kosrae, Macquarie, Noumea, Pohnpei, Sakhalin, Vladivostok', 660],
['Pacific/Norfolk','(UTC +11:30) Norfolk', 690], ['Pacific/Norfolk', '(UTC +11:30) Norfolk', 690],
['Antarctica/McMurdo','(UTC +12:00) Auckland, McMurdo, South Pole', 720], ['Antarctica/McMurdo', '(UTC +12:00) Auckland, McMurdo, South Pole', 720],
['Asia/Anadyr','(UTC +12:00) Anadyr, Fiji, Funafuti, Kamchatka, Kwajalein, Magadan, Majuro, Nauru, Tarawa, Wake, Wallis', 720], ['Asia/Anadyr', '(UTC +12:00) Anadyr, Fiji, Funafuti, Kamchatka, Kwajalein, Magadan, Majuro, Nauru, Tarawa, Wake, Wallis', 720],
['Pacific/Chatham','(UTC +12:45) Chatham', 765], ['Pacific/Chatham', '(UTC +12:45) Chatham', 765],
['Pacific/Enderbury','(UTC +13:00) Enderbury, Tongatapu', 780], ['Pacific/Enderbury', '(UTC +13:00) Enderbury, Tongatapu', 780],
['Pacific/Apia','(UTC +13:00) Apia', 780], ['Pacific/Apia', '(UTC +13:00) Apia', 780],
['Pacific/Kiritimati','(UTC +14:00) Kiritimati', 840] ['Pacific/Kiritimati', '(UTC +14:00) Kiritimati', 840]
], ],
/* map all citys to the above timezones */ /* map all citys to the above timezones */
@ -331,7 +331,7 @@ Zarafa.plugins.calendarimporter.data.Timezones = Ext.extend(Object, {
'America/Araguaina' : 'America/Sao_Paulo', 'America/Araguaina' : 'America/Sao_Paulo',
'America/Argentina/Buenos_Aires' : 'America/Sao_Paulo', 'America/Argentina/Buenos_Aires' : 'America/Sao_Paulo',
'America/Argentina/Catamarca' : 'America/Sao_Paulo', 'America/Argentina/Catamarca' : 'America/Sao_Paulo',
'America/Argentina/ComodRivadavia' : 'America/Sao_Paulo', 'America/Argentina/ComodRivadavia': 'America/Sao_Paulo',
'America/Argentina/Cordoba' : 'America/Sao_Paulo', 'America/Argentina/Cordoba' : 'America/Sao_Paulo',
'America/Argentina/Jujuy' : 'America/Sao_Paulo', 'America/Argentina/Jujuy' : 'America/Sao_Paulo',
'America/Argentina/La_Rioja' : 'America/Sao_Paulo', 'America/Argentina/La_Rioja' : 'America/Sao_Paulo',
@ -746,16 +746,16 @@ Zarafa.plugins.calendarimporter.data.Timezones = Ext.extend(Object, {
}, },
/* return unmapped timezone... */ /* return unmapped timezone... */
unMap: function(timezone) { unMap: function (timezone) {
return this.map[timezone] !== undefined ? this.map[timezone] : timezone; return this.map[timezone] !== undefined ? this.map[timezone] : timezone;
}, },
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; 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

@ -2,7 +2,7 @@
* ImportContentPanel.js zarafa calender to ics im/exporter * ImportContentPanel.js zarafa calender to ics im/exporter
* *
* Author: Christoph Haas <christoph.h@sprinternet.at> * Author: Christoph Haas <christoph.h@sprinternet.at>
* Copyright (C) 2012-2013 Christoph Haas * Copyright (C) 2012-2016 Christoph Haas
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
@ -42,13 +42,9 @@ Zarafa.plugins.calendarimporter.dialogs.ImportContentPanel = Ext.extend(Zarafa.c
*/ */
constructor : function(config) { constructor : function(config) {
config = config || {}; config = config || {};
var title = _('Import Calendar File');
if(container.getSettingsModel().get("zarafa/v1/plugins/calendarimporter/enable_export")){
title = _('Import/Export Calendar File');
}
Ext.applyIf(config, { Ext.applyIf(config, {
layout : 'fit', layout : 'fit',
title : title, title : _('Import Calendar File'),
closeOnSave : true, closeOnSave : true,
width : 800, width : 800,
height : 700, height : 700,
@ -56,7 +52,8 @@ Zarafa.plugins.calendarimporter.dialogs.ImportContentPanel = Ext.extend(Zarafa.c
items : [ items : [
{ {
xtype : 'calendarimporter.importpanel', xtype : 'calendarimporter.importpanel',
filename : config.filename filename : config.filename,
folder : config.folder
} }
] ]
}); });

View File

@ -2,7 +2,7 @@
* ImportPanel.js zarafa calender to ics im/exporter * ImportPanel.js zarafa calender to ics im/exporter
* *
* Author: Christoph Haas <christoph.h@sprinternet.at> * Author: Christoph Haas <christoph.h@sprinternet.at>
* Copyright (C) 2012-2013 Christoph Haas * Copyright (C) 2012-2016 Christoph Haas
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
@ -29,7 +29,7 @@ Ext.namespace("Zarafa.plugins.calendarimporter.dialogs");
/** /**
* @class Zarafa.plugins.calendarimporter.dialogs.ImportPanel * @class Zarafa.plugins.calendarimporter.dialogs.ImportPanel
* @extends Ext.form.FormPanel * @extends Ext.Panel
*/ */
Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, { Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
@ -46,7 +46,7 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
loadMask: null, loadMask: null,
/* export event buffer */ /* export event buffer */
exportResponse: new Array(), exportResponse: [],
/* how many requests are still running? */ /* how many requests are still running? */
runningRequests: null, runningRequests: null,
@ -54,13 +54,8 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
/* The store for the selection grid */ /* The store for the selection grid */
store: null, store: null,
/** /* selected folder */
* The internal 'iframe' which is hidden from the user, which is used for downloading folder : null,
* attachments. See {@link #doOpen}.
* @property
* @type Ext.Element
*/
downloadFrame : undefined,
/** /**
* @constructor * @constructor
@ -71,24 +66,30 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
var self = this; var self = this;
this.timezone = container.getSettingsModel().get("zarafa/v1/plugins/calendarimporter/default_timezone"); this.timezone = container.getSettingsModel().get("zarafa/v1/plugins/calendarimporter/default_timezone");
if(typeof config.filename !== "undefined") { if (!Ext.isEmpty(config.filename)) {
this.icsfile = config.filename; this.icsfile = config.filename;
} }
if (!Ext.isEmpty(config.folder)) {
this.folder = config.folder;
}
// create the data store // create the data store
this.store = new Ext.data.ArrayStore({ this.store = new Ext.data.ArrayStore({
fields: [ fields: [
{name: 'title'}, {name: 'subject'},
{name: 'start'}, {name: 'startdate'},
{name: 'end'}, {name: 'enddate'},
{name: 'location'}, {name: 'location'},
{name: 'description'}, {name: 'body'},
{name: 'priority'}, {name: 'priority'},
{name: 'label'}, {name: 'label'},
{name: 'busy'}, {name: 'busy'},
{name: 'privatestate'}, {name: 'class'},
{name: 'organizer'}, {name: 'organizer'},
{name: 'trigger'} {name: 'alarms'},
{name: 'timezone'},
{name: 'record'}
] ]
}); });
@ -114,29 +115,19 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
this.createGrid() this.createGrid()
], ],
buttons: [ buttons: [
this.createExportAllButton(),
this.createSubmitAllButton(), this.createSubmitAllButton(),
this.createSubmitButton(), this.createSubmitButton(),
this.createCancelButton() this.createCancelButton()
], ],
listeners: { listeners: {
afterrender: function (cmp) { afterrender: function (cmp) {
Ext.getCmp('importbutton').disable(); this.loadMask = new Ext.LoadMask(this.getEl(), {msg:'Loading...'});
this.loadMask = new Ext.LoadMask(Ext.getCmp("importpanel").getEl(), {msg:'Loading...'});
if(this.icsfile != null) { // if we have got the filename from an attachment if(this.icsfile != null) { // if we have got the filename from an attachment
this.parseCalendar(this.icsfile, this.timezone, this.ignoredst); this.parseCalendar(this.icsfile, this.timezone, this.ignoredst);
} }
}, },
close: function (cmp) { scope: this
Ext.getCmp('importbutton').enable();
},
hide: function (cmp) {
Ext.getCmp('importbutton').enable();
},
destroy: function (cmp) {
Ext.getCmp('importbutton').enable();
}
} }
}); });
@ -174,22 +165,25 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
reloadGridStore: function(eventdata) { reloadGridStore: function(eventdata) {
var parsedData = []; var parsedData = [];
// this is done to get rid of the local browser timezone....
// because all timezone specific stuff is done via php
var local_tz_offset = new Date().getTimezoneOffset() * 60000; // getTimezoneOffset returns minutes... we need milliseconds
if(eventdata !== null) { if(eventdata !== null) {
parsedData = new Array(eventdata.events.length); parsedData = new Array(eventdata.events.length);
var i = 0; var i = 0;
for(i = 0; i < eventdata.events.length; i++) { for(i = 0; i < eventdata.events.length; i++) {
var trigger = null; parsedData[i] = [
eventdata.events[i]["subject"],
if(eventdata.events[i]["VALARM"]) { new Date(parseInt(eventdata.events[i]["startdate"]) * 1000),
trigger = eventdata.events[i]["VALARM"]["TRIGGER"]; new Date(parseInt(eventdata.events[i]["duedate"]) * 1000),
trigger = new Date(parseInt(trigger) + local_tz_offset); eventdata.events[i]["location"],
} eventdata.events[i]["body"],
parsedData[i] = new Array(eventdata.events[i]["SUMMARY"], new Date(parseInt(eventdata.events[i]["DTSTART"]) + local_tz_offset), new Date(parseInt(eventdata.events[i]["DTEND"]) + local_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); eventdata.events[i]["priority"],
eventdata.events[i]["label"],
eventdata.events[i]["busystatus"],
eventdata.events[i]["private"],
eventdata.events[i]["organizer"],
eventdata.events[i]["alarms"],
eventdata.events[i]["timezone"],
eventdata.events[i]
];
} }
} else { } else {
return null; return null;
@ -223,17 +217,18 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
sortable: true sortable: true
}, },
columns: [ columns: [
{id: 'Summary', header: 'Title', width: 200, sortable: true, dataIndex: 'title'}, {id: 'Summary', header: 'Title', width: 200, sortable: true, dataIndex: 'subject'},
{header: 'Start', width: 200, sortable: true, dataIndex: 'start', renderer : Zarafa.common.ui.grid.Renderers.datetime}, {header: 'Start', width: 200, sortable: true, dataIndex: 'startdate', renderer : Zarafa.common.ui.grid.Renderers.datetime},
{header: 'End', width: 200, sortable: true, dataIndex: 'end', renderer : Zarafa.common.ui.grid.Renderers.datetime}, {header: 'End', width: 200, sortable: true, dataIndex: 'enddate', 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', sortable: true, dataIndex: 'description'}, {header: 'Description', sortable: true, dataIndex: 'body'},
{header: "Priority", dataIndex: 'priority', hidden: true}, {header: "Priority", dataIndex: 'priority', hidden: true},
{header: "Label", dataIndex: 'label', hidden: true}, {header: "Label", dataIndex: 'label', hidden: true},
{header: "Busystatus", dataIndex: 'busy', hidden: true}, {header: "Busystatus", dataIndex: 'busy', hidden: true},
{header: "Privacystatus", dataIndex: 'privatestate', hidden: true}, {header: "Privacystatus", dataIndex: 'class', hidden: true},
{header: "Organizer", dataIndex: 'organizer', hidden: true}, {header: "Organizer", dataIndex: 'organizer', hidden: true},
{header: "Alarm", dataIndex: 'trigger', hidden: true, renderer : Zarafa.common.ui.grid.Renderers.datetime} {header: "Alarm", dataIndex: 'alarms', hidden: true, renderer : Zarafa.common.ui.grid.Renderers.datetime},
{header: "Timezone", dataIndex: 'timezone', hidden: true}
] ]
}), }),
sm: new Ext.grid.RowSelectionModel({multiSelect:true}) sm: new Ext.grid.RowSelectionModel({multiSelect:true})
@ -241,52 +236,23 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
}, },
createSelectBox: function() { createSelectBox: function() {
var defaultFolder = container.getHierarchyStore().getDefaultFolder('calendar'); // @type: Zarafa.hierarchy.data.MAPIFolderRecord var myStore = Zarafa.plugins.calendarimporter.data.Actions.getAllCalendarFolders(true);
var subFolders = defaultFolder.getChildren();
var myStore = [];
/* add all local calendar folders */
var i = 0;
myStore.push(new Array(defaultFolder.getDefaultFolderKey(), defaultFolder.getDisplayName()));
for(i = 0; i < subFolders.length; i++) {
/* Store all subfolders */
myStore.push(new Array(subFolders[i].getDisplayName(), subFolders[i].getDisplayName(), false)); // 3rd field = isPublicfolder
}
/* add all shared calendar folders */
var pubStore = container.getHierarchyStore().getPublicStore();
if(typeof pubStore !== "undefined") {
try {
var pubFolder = pubStore.getDefaultFolder("publicfolders");
var pubSubFolders = pubFolder.getChildren();
for(i = 0; i < pubSubFolders.length; i++) {
if(pubSubFolders[i].isContainerClass("IPF.Appointment")){
myStore.push(new Array(pubSubFolders[i].getDisplayName(), pubSubFolders[i].getDisplayName() + " [Shared]", true)); // 3rd field = isPublicfolder
}
}
} catch (e) {
console.log("Error opening the shared folder...");
console.log(e);
}
}
return { return {
xtype: "selectbox", xtype: "selectbox",
ref: 'calendarselector', ref: 'calendarselector',
id: 'calendarselector',
editable: false, editable: false,
name: "choosen_calendar", name: "choosen_calendar",
value: container.getSettingsModel().get("zarafa/v1/plugins/calendarimporter/default_calendar"), value: Ext.isEmpty(this.folder) ? Zarafa.plugins.calendarimporter.data.Actions.getCalendarFolderByName(container.getSettingsModel().get("zarafa/v1/plugins/calendarimporter/default_calendar")).entryid : this.folder,
width: 100, width: 100,
fieldLabel: "Select a calender", fieldLabel: "Select folder",
store: myStore, store: myStore,
mode: 'local', mode: 'local',
labelSeperator: ":", labelSeperator: ":",
border: false, border: false,
anchor: "100%", anchor: "100%",
scope: this, scope: this,
hidden : Ext.isEmpty(this.folder) ? false : true,
allowBlank: false allowBlank: false
} }
}, },
@ -295,12 +261,11 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
return { return {
xtype: "selectbox", xtype: "selectbox",
ref: 'timezoneselector', ref: 'timezoneselector',
id: 'timezoneselector',
editable: false, editable: false,
name: "choosen_timezone", name: "choosen_timezone",
value: Zarafa.plugins.calendarimporter.data.Timezones.unMap(container.getSettingsModel().get("zarafa/v1/plugins/calendarimporter/default_timezone")), value: Zarafa.plugins.calendarimporter.data.Timezones.unMap(container.getSettingsModel().get("zarafa/v1/plugins/calendarimporter/default_timezone")),
width: 100, width: 100,
fieldLabel: "Select a timezone (optional)", fieldLabel: "Timezone",
store: Zarafa.plugins.calendarimporter.data.Timezones.store, store: Zarafa.plugins.calendarimporter.data.Timezones.store,
labelSeperator: ":", labelSeperator: ":",
mode: 'local', mode: 'local',
@ -309,7 +274,7 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
scope: this, scope: this,
allowBlank: true, allowBlank: true,
listeners: { listeners: {
'select': this.onTimezoneSelected, select: this.onTimezoneSelected,
scope: this scope: this
} }
} }
@ -319,10 +284,9 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
return { return {
xtype: "checkbox", xtype: "checkbox",
ref: 'dstcheck', ref: 'dstcheck',
id: 'dstcheck',
name: "dst_check", name: "dst_check",
width: 100, width: 100,
fieldLabel: "Ignore DST (optional)", fieldLabel: "Ignore DST",
boxLabel: 'This will ignore "Daylight saving time" offsets.', boxLabel: 'This will ignore "Daylight saving time" offsets.',
labelSeperator: ":", labelSeperator: ":",
border: false, border: false,
@ -330,7 +294,7 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
scope: this, scope: this,
allowBlank: true, allowBlank: true,
listeners: { listeners: {
'check': this.onDstChecked, check: this.onDstChecked,
scope: this scope: this
} }
} }
@ -346,10 +310,11 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
emptyText: 'Select an .ics calendar', emptyText: 'Select an .ics calendar',
border: false, border: false,
anchor: "100%", anchor: "100%",
height : "30",
scope: this, scope: this,
allowBlank: false, allowBlank: false,
listeners: { listeners: {
'fileselected': this.onFileSelected, fileselected: this.onFileSelected,
scope: this scope: this
} }
} }
@ -358,48 +323,28 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
createSubmitButton: function() { createSubmitButton: function() {
return { return {
xtype: "button", xtype: "button",
ref: "submitButton", ref: "../submitButton",
id: "submitButton",
disabled: true, disabled: true,
width: 100, width: 100,
border: false, border: false,
text: _("Import"), text: _("Import"),
anchor: "100%", anchor: "100%",
handler: this.importCheckedEvents, handler: this.importCheckedEvents,
scope: this, scope: this
allowBlank: false
} }
}, },
createSubmitAllButton: function() { createSubmitAllButton: function() {
return { return {
xtype: "button", xtype: "button",
ref: "submitAllButton", ref: "../submitAllButton",
id: "submitAllButton",
disabled: true, disabled: true,
width: 100, width: 100,
border: false, border: false,
text: _("Import All"), text: _("Import All"),
anchor: "100%", anchor: "100%",
handler: this.importAllEvents, handler: this.importAllEvents,
scope: this, scope: this
allowBlank: false
}
},
createExportAllButton: function() {
return {
xtype: "button",
ref: "exportAllButton",
id: "exportAllButton",
hidden: !container.getSettingsModel().get("zarafa/v1/plugins/calendarimporter/enable_export"),
width: 100,
border: false,
text: _("Export All"),
anchor: "100%",
handler: this.exportAllEvents,
scope: this,
allowBlank: false
} }
}, },
@ -411,8 +356,7 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
text: _("Cancel"), text: _("Cancel"),
anchor: "100%", anchor: "100%",
handler: this.close, handler: this.close,
scope: this, scope: this
allowBlank: false
} }
}, },
@ -432,7 +376,7 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
/** /**
* This is called when the dst checkbox has been selected * This is called when the dst checkbox has been selected
* @param {Ext.form.CheckBox} combo * @param {Ext.form.CheckBox} checkbox
* @param {boolean} checked * @param {boolean} checked
*/ */
onDstChecked : function(checkbox, checked) { onDstChecked : function(checkbox, checked) {
@ -456,8 +400,8 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
waitMsg: 'Uploading and parsing calendar...', waitMsg: 'Uploading and parsing calendar...',
url: 'plugins/calendarimporter/php/upload.php', url: 'plugins/calendarimporter/php/upload.php',
failure: function(file, action) { failure: function(file, action) {
Ext.getCmp('submitButton').disable(); this.submitButton.disable();
Ext.getCmp('submitAllButton').disable(); this.submitAllButton.disable();
Zarafa.common.dialogs.MessageBox.show({ Zarafa.common.dialogs.MessageBox.show({
title : _('Error'), title : _('Error'),
msg : _(action.result.error), msg : _(action.result.error),
@ -478,14 +422,15 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
parseCalendar: function (icsPath, timezone, ignoredst) { parseCalendar: function (icsPath, timezone, ignoredst) {
this.loadMask.show(); this.loadMask.show();
// call export function here!
var responseHandler = new Zarafa.plugins.calendarimporter.data.ResponseHandler({ var responseHandler = new Zarafa.plugins.calendarimporter.data.ResponseHandler({
successCallback: this.handleParsingResult.createDelegate(this) successCallback: this.handleParsingResult,
scope: this
}); });
container.getRequest().singleRequest( container.getRequest().singleRequest(
'calendarmodule', 'calendarmodule',
'import', 'load',
{ {
ics_filepath: icsPath, ics_filepath: icsPath,
timezone: timezone, timezone: timezone,
@ -496,20 +441,22 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
}, },
handleParsingResult: function(response) { handleParsingResult: function(response) {
this.loadMask.hide(); var self = this.scope;
self.loadMask.hide();
if(response["status"] == true) { if(response["status"] == true) {
Ext.getCmp('submitButton').enable(); self.submitButton.enable();
Ext.getCmp('submitAllButton').enable(); self.submitAllButton.enable();
if(typeof response.parsed.calendar["X-WR-TIMEZONE"] !== "undefined") {; if(typeof response.parsed.calendar["X-WR-TIMEZONE"] !== "undefined") {
this.timezone = response.parsed.calendar["X-WR-TIMEZONE"]; self.timezone = response.parsed.calendar["X-WR-TIMEZONE"];
this.timezoneselector.setValue(Zarafa.plugins.calendarimporter.data.Timezones.unMap(this.timezone)); self.timezoneselector.setValue(Zarafa.plugins.calendarimporter.data.Timezones.unMap(this.timezone));
} }
this.reloadGridStore(response.parsed); self.reloadGridStore(response.parsed);
} else { } else {
Ext.getCmp('submitButton').disable(); self.submitButton.disable();
Ext.getCmp('submitAllButton').disable(); self.submitAllButton.disable();
Zarafa.common.dialogs.MessageBox.show({ Zarafa.common.dialogs.MessageBox.show({
title : _('Parser Error'), title : _('Parser Error'),
msg : _(response["message"]), msg : _(response["message"]),
@ -524,54 +471,6 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
this.dialog.close() this.dialog.close()
}, },
convertToAppointmentRecord: function (calendarFolder,entry) {
var newRecord = Zarafa.core.data.RecordFactory.createRecordObjectByMessageClass('IPM.Appointment', {
startdate: new Date(entry.start),
duedate: (entry.end != null) ?
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 != null) ?
new Date(entry.end) :
new Date(entry.start).add(Date.HOUR, 1),
timezone: this.timezone,
parent_entryid: calendarFolder.get('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;
},
importCheckedEvents: function () { importCheckedEvents: function () {
var newRecords = this.eventgrid.selModel.getSelections(); var newRecords = this.eventgrid.selModel.getSelections();
this.importEvents(newRecords); this.importEvents(newRecords);
@ -584,295 +483,15 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
this.importEvents(newRecords); this.importEvents(newRecords);
}, },
exportAllEvents: function () {
//receive existing calendar store
var calValue = this.calendarselector.value;
if(calValue == undefined) { // no calendar choosen
Zarafa.common.dialogs.MessageBox.show({
title : _('Error'),
msg : _('You have to choose a calendar!'),
icon : Zarafa.common.dialogs.MessageBox.ERROR,
buttons : Zarafa.common.dialogs.MessageBox.OK
});
} else {
var calexist = true;
var calendarFolder = container.getHierarchyStore().getDefaultFolder('calendar');
var pubStore = container.getHierarchyStore().getPublicStore();
var pubSubFolders = [];
var pubFolder;
if(typeof pubStore !== "undefined") {
try {
pubFolder = pubStore.getDefaultFolder("publicfolders");
pubSubFolders = pubFolder.getChildren();
} catch (e) {
console.log("Error opening the shared folder...");
console.log(e);
}
}
if(calValue != "calendar") {
var subFolders = calendarFolder.getChildren();
var i = 0;
/* add public folders if any exist */
for(i = 0; i < pubSubFolders.length; i++) {
if(pubSubFolders[i].isContainerClass("IPF.Appointment")){
subFolders.push(pubSubFolders[i]);
}
}
for(i=0;i<subFolders.length;i++) {
// loo up right folder
// TODO: improve!!
if(subFolders[i].getDisplayName() == calValue) {
calendarFolder = subFolders[i];
break;
}
}
if(calendarFolder.isDefaultFolder()) {
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;
}
}
if(calexist) {
Zarafa.common.dialogs.MessageBox.show({
title: 'Please wait',
msg: 'Generating ical file...',
progressText: 'Exporting...',
width:300,
progress:true,
closable:false
});
// progress bar... ;)
var updateProgressBar = function(v){
return function(){
if(v == 100){
if(Zarafa.common.dialogs.MessageBox.isVisible()) {
updateTimer();
}
}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();
// call export function here!
var responseHandler = new Zarafa.plugins.calendarimporter.data.ResponseHandler({
successCallback: this.exportPagedEvents.createDelegate(this)
});
container.getRequest().singleRequest(
'appointmentlistmodule',
'list',
{
groupDir: "ASC",
restriction: {
//start: 0,
//limit: 500, // limit to 500 events.... not working because of hardcoded limit in listmodule
//startdate: 0,
//duedate: 2145826800 // 2037... nearly highest unix timestamp
},
sort: [{
"field": "startdate",
"direction": "DESC"
}],
store_entryid : calendarFolder.data.store_entryid,
entryid : calendarFolder.data.entryid
},
responseHandler
);
}
}
},
/**
* Calculate needed Requests for all events
* Needed because the listmodule has hardcoded pageing setting -.-
* @param {Object} response
*/
exportPagedEvents:function(response) {
if(response.page.start = 0 && response.item.length <= 0) {
container.getNotifier().notify('info', 'Export Failed', 'There were no items to export!');
Zarafa.common.dialogs.MessageBox.hide();
} else {
this.exportResponse = response.item;
}
if(this.totalExportCount == null) {
this.totalExportCount = response.page.totalrowcount;
}
var requests = Math.ceil(response.page.totalrowcount / response.page.rowcount);
this.runningRequests = requests;
var i = 0;
//receive existing calendar store
var calValue = this.calendarselector.value;
var calendarFolder = container.getHierarchyStore().getDefaultFolder('calendar');
var pubStore = container.getHierarchyStore().getPublicStore();
var pubSubFolders = [];
var pubFolder;
if(typeof pubStore !== "undefined") {
try {
pubFolder = pubStore.getDefaultFolder("publicfolders");
pubSubFolders = pubFolder.getChildren();
} catch (e) {
console.log("Error opening the shared folder...");
console.log(e);
}
}
if(calValue != "calendar") {
var subFolders = calendarFolder.getChildren();
i = 0;
/* add public folders if any exist */
for(i = 0; i < pubSubFolders.length; i++) {
if(pubSubFolders[i].isContainerClass("IPF.Appointment")){
subFolders.push(pubSubFolders[i]);
}
}
for(i=0;i<subFolders.length;i++) {
// loo up right folder
// TODO: improve!!
if(subFolders[i].getDisplayName() == calValue) {
calendarFolder = subFolders[i];
break;
}
}
if(calendarFolder.isDefaultFolder()) {
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
});
}
}
for(i = 0; i < requests; i++) {
var responseHandler = new Zarafa.plugins.calendarimporter.data.ResponseHandler({
successCallback: this.storeResult.createDelegate(this)
});
this.requestNext(calendarFolder, responseHandler, response.page.rowcount *(i+1), response.page.rowcount);
}
},
/**
* Responsehandler for the single requests... will merge all events into one array
* @param {Object} response
*/
storeResult: function(response) {
var tmp = this.exportResponse;
this.exportResponse = tmp.concat(response.item);
if(this.runningRequests <= 1) {
// final request =)
var responseHandler = new Zarafa.plugins.calendarimporter.data.ResponseHandler({
successCallback: this.downLoadICS.createDelegate(this)
});
container.getRequest().singleRequest(
'calendarmodule',
'export',
{
data: this.exportResponse,
calendar: this.calendarselector.value
},
responseHandler
);
container.getNotifier().notify('info', 'Exported', 'Found ' + this.exportResponse.length + ' entries to export. Preparing download...');
this.dialog.close();
}
this.runningRequests--;
},
/**
* build a new event request for the listmodule
* @param {Object} calendarFolder the calendarFolder to export
* @param {Object} responseHandler (should be storeResult)
* @param {int} start
* @param {int} limit
*/
requestNext: function(calendarFolder, responseHandler, start, limit) {
container.getRequest().singleRequest(
'appointmentlistmodule',
'list',
{
groupDir: "ASC",
restriction: {
start: start,
limit: limit
//startdate: 0,
//duedate: 2145826800 // 2037... nearly highest unix timestamp
},
sort: [{
"field": "startdate",
"direction": "DESC"
}],
store_entryid : calendarFolder.data.store_entryid,
entryid : calendarFolder.data.entryid
},
responseHandler
);
},
/**
* download ics file =)
* @param {Object} response
* @private
*/
downLoadICS : function(response) {
Zarafa.common.dialogs.MessageBox.hide();
if(response.status === true) {
if(!this.downloadFrame){
this.downloadFrame = Ext.getBody().createChild({
tag: 'iframe',
cls: 'x-hidden'
});
}
var url = 'plugins/calendarimporter/php/download.php?fileid='+response.fileid+'&basedir='+response.basedir+'&secid='+response.secid+'&realname='+response.realname;
this.downloadFrame.dom.contentWindow.location = url;
} else {
container.getNotifier().notify('error', 'Export Failed', 'ICal File creation failed!');
}
},
/** /**
* This function stores all given events to the appointmentstore * This function stores all given events to the appointmentstore
* @param events * @param events
*/ */
importEvents: function (events) { importEvents: function (events) {
//receive existing calendar store //receive existing calendar store
var calValue = this.calendarselector.value; var calValue = this.calendarselector.getValue();
if(calValue == undefined) { // no calendar choosen if(Ext.isEmpty(calValue)) { // no calendar choosen
Zarafa.common.dialogs.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!'),
@ -880,7 +499,6 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
buttons : Zarafa.common.dialogs.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) {
Zarafa.common.dialogs.MessageBox.show({ Zarafa.common.dialogs.MessageBox.show({
title : _('Error'), title : _('Error'),
@ -889,53 +507,54 @@ Zarafa.plugins.calendarimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
buttons : Zarafa.common.dialogs.MessageBox.OK buttons : Zarafa.common.dialogs.MessageBox.OK
}); });
} else { } else {
var calendarStore = new Zarafa.calendar.AppointmentStore(); var calendarFolder = Zarafa.plugins.calendarimporter.data.Actions.getCalendarFolderByEntryid(calValue);
var calendarFolder = container.getHierarchyStore().getDefaultFolder('calendar');
var pubStore = container.getHierarchyStore().getPublicStore();
var pubFolder = pubStore.getDefaultFolder("publicfolders");
var pubSubFolders = pubFolder.getChildren();
if(calValue != "calendar") {
var subFolders = calendarFolder.getChildren();
var i = 0;
for(i = 0; i < pubSubFolders.length; i++) {
if(pubSubFolders[i].isContainerClass("IPF.Appointment")){
subFolders.push(pubSubFolders[i]);
}
}
for(i=0;i<subFolders.length;i++) {
// look up right folder
// TODO: improve!!
if(subFolders[i].getDisplayName() == calValue) {
calendarFolder = subFolders[i];
break;
}
}
if(calendarFolder.isDefaultFolder()) {
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;
}
}
if(calexist) {
this.loadMask.show(); this.loadMask.show();
var uids = [];
//receive Records from grid rows //receive Records from grid rows
Ext.each(events, function(newRecord) { Ext.each(events, function(newRecord) {
var record = this.convertToAppointmentRecord(calendarFolder,newRecord.data); uids.push(newRecord.data.record.internal_fields.event_uid);
calendarStore.add(record);
}, this); }, this);
calendarStore.save();
this.loadMask.hide(); var responseHandler = new Zarafa.plugins.calendarimporter.data.ResponseHandler({
this.dialog.close(); successCallback: this.importEventsDone,
container.getNotifier().notify('info', 'Imported', 'Imported ' + events.length + ' events. Please reload your calendar!'); scope: this
});
container.getRequest().singleRequest(
'calendarmodule',
'import',
{
storeid : calendarFolder.store_entryid,
folderid : calendarFolder.entryid,
uids : uids,
ics_filepath: this.icsfile
},
responseHandler
);
} }
} }
},
/**
* Callback for the import request.
* @param {Object} response
*/
importEventsDone: function (response) {
var self = this.scope;
self.loadMask.hide();
self.dialog.close();
if (response.status == true) {
container.getNotifier().notify('info', 'Imported', 'Imported ' + response.count + ' events. Please reload your calendar!');
} else {
Zarafa.common.dialogs.MessageBox.show({
title : _('Error'),
msg : _('Import failed: ') + response.message,
icon : Zarafa.common.dialogs.MessageBox.ERROR,
buttons: Zarafa.common.dialogs.MessageBox.OK
});
} }
} }
}); });

View File

@ -2,7 +2,7 @@
* plugin.calendarimporter.js zarafa calender to ics im/exporter * plugin.calendarimporter.js zarafa calender to ics im/exporter
* *
* Author: Christoph Haas <christoph.h@sprinternet.at> * Author: Christoph Haas <christoph.h@sprinternet.at>
* Copyright (C) 2012-2013 Christoph Haas * Copyright (C) 2012-2016 Christoph Haas
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
@ -39,7 +39,7 @@ Zarafa.plugins.calendarimporter.ImportPlugin = Ext.extend(Zarafa.core.Plugin, {
* 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);
/* our panel */ /* our panel */
@ -47,44 +47,69 @@ Zarafa.plugins.calendarimporter.ImportPlugin = Ext.extend(Zarafa.core.Plugin, {
/* directly import received icals */ /* directly import received icals */
this.registerInsertionPoint('common.contextmenu.attachment.actions', this.createAttachmentImportButton); this.registerInsertionPoint('common.contextmenu.attachment.actions', this.createAttachmentImportButton);
/* add import button to south navigation */
this.registerInsertionPoint("navigation.south", this.createImportButton, this);
/* add settings widget */ /* add settings widget */
this.registerInsertionPoint('context.settings.category.calendar', this.createSettingsWidget); this.registerInsertionPoint('context.settings.category.calendar', this.createSettingsWidget);
/* export a calendar entry via rightclick */
this.registerInsertionPoint('context.calendar.contextmenu.actions', this.createItemExportInsertionPoint, this);
/* ical sync stuff */ /* ical sync stuff */
if(container.getSettingsModel().get("zarafa/v1/plugins/calendarimporter/enable_sync") === true) { if (container.getSettingsModel().get("zarafa/v1/plugins/calendarimporter/enable_sync") === true) {
/* edit panel */ /* edit panel */
Zarafa.core.data.SharedComponentType.addProperty('plugins.calendarimporter.settings.dialogs.calsyncedit'); Zarafa.core.data.SharedComponentType.addProperty('plugins.calendarimporter.settings.dialogs.calsyncedit');
/* enable the settings widget */ /* enable the settings widget */
this.registerInsertionPoint('context.settings.category.calendar', this.createSettingsCalSyncWidget); this.registerInsertionPoint('context.settings.category.calendar', this.createSettingsCalSyncWidget);
} }
}, },
/** /**
* Creates the button * This method hooks to the contact context menu and allows users to export users to vcf.
*
* @return {Object} Configuration object for a {@link Ext.Button button}
* *
* @param include
* @param btn
* @returns {Object}
*/ */
createImportButton: function () { createItemExportInsertionPoint: function (include, btn) {
var button = { return {
xtype : 'button', text : dgettext('plugin_files', 'Export Event'),
ref : "importbutton", handler: this.exportToICS.createDelegate(this, [btn]),
id : "importbutton", scope : this,
text : _('Import Calendar'), iconCls: 'icon_calendarimporter_export'
iconCls : 'icon_calendarimporter_button',
navigationContext : container.getContextByName('calendar'),
handler : this.onImportButtonClick,
scope : this
}; };
},
if(container.getSettingsModel().get("zarafa/v1/plugins/calendarimporter/enable_export")) { /**
button.text = _('Import/Export Calendar'); * Generates a request to download the selected records as vCard.
* @param {Ext.Button} btn
*/
exportToICS: function (btn) {
if (btn.records.length == 0) {
return; // skip if no records where given!
} }
return button; var recordIds = [];
for (var i = 0; i < btn.records.length; i++) {
recordIds.push(btn.records[i].get("entryid"));
}
var responseHandler = new Zarafa.plugins.calendarimporter.data.ResponseHandler({
successCallback: Zarafa.plugins.calendarimporter.data.Actions.downloadICS,
scope : this
});
// request attachment preperation
container.getRequest().singleRequest(
'calendarmodule',
'export',
{
storeid: btn.records[0].get("store_entryid"),
records: recordIds
},
responseHandler
);
}, },
/** /**
@ -95,7 +120,7 @@ Zarafa.plugins.calendarimporter.ImportPlugin = Ext.extend(Zarafa.core.Plugin, {
*/ */
createSettingsWidget: function () { createSettingsWidget: function () {
return [{ return [{
xtype : 'calendarimporter.settingswidget' xtype: 'calendarimporter.settingswidget'
}]; }];
}, },
@ -107,7 +132,7 @@ Zarafa.plugins.calendarimporter.ImportPlugin = Ext.extend(Zarafa.core.Plugin, {
*/ */
createSettingsCalSyncWidget: function () { createSettingsCalSyncWidget: function () {
return [{ return [{
xtype : 'calendarimporter.settingscalsyncwidget' xtype: 'calendarimporter.settingscalsyncwidget'
}]; }];
}, },
@ -116,19 +141,19 @@ 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}
*/ */
createAttachmentImportButton : function(include, btn) { createAttachmentImportButton: function (include, btn) {
return { return {
text : _('Import Calendar'), text : _('Import to Calendar'),
handler : this.getAttachmentFileName.createDelegate(this, [btn, this.gotAttachmentFileName]), handler : this.getAttachmentFileName.createDelegate(this, [btn]),
scope : this, scope : this,
iconCls : 'icon_calendarimporter_button', iconCls : 'icon_calendarimporter_button',
beforeShow : function(item, record) { beforeShow: function (item, record) {
var extension = record.data.name.split('.').pop().toLowerCase(); var extension = record.data.name.split('.').pop().toLowerCase();
if(record.data.filetype == "text/calendar" || extension == "ics" || extension == "ifb" || extension == "ical" || extension == "ifbf") { if (record.data.filetype == "text/calendar" || extension == "ics" || extension == "ifb" || extension == "ical" || extension == "ifbf") {
item.setDisabled(false); item.setVisible(true);
} else { } else {
item.setDisabled(true); item.setVisible(false);
} }
} }
}; };
@ -137,18 +162,15 @@ Zarafa.plugins.calendarimporter.ImportPlugin = Ext.extend(Zarafa.core.Plugin, {
/** /**
* Callback for getAttachmentFileName * Callback for getAttachmentFileName
*/ */
gotAttachmentFileName: function(response) { gotAttachmentFileName: function (response) {
if(response.status == true) { if (response.status == true) {
Zarafa.core.data.UIFactory.openLayerComponent(Zarafa.core.data.SharedComponentType['plugins.calendarimporter.dialogs.importevents'], undefined, { this.scope.openImportDialog(response.tmpname);
manager : Ext.WindowMgr,
filename : response.tmpname
});
} else { } else {
Zarafa.common.dialogs.MessageBox.show({ Zarafa.common.dialogs.MessageBox.show({
title : _('Error'), title : _('Error'),
msg : _(response["message"]), msg : _(response["message"]),
icon : Zarafa.common.dialogs.MessageBox.ERROR, icon : Zarafa.common.dialogs.MessageBox.ERROR,
buttons : Zarafa.common.dialogs.MessageBox.OK buttons: Zarafa.common.dialogs.MessageBox.OK
}); });
} }
}, },
@ -158,27 +180,27 @@ Zarafa.plugins.calendarimporter.ImportPlugin = Ext.extend(Zarafa.core.Plugin, {
*/ */
getAttachmentFileName: function (btn, callback) { getAttachmentFileName: function (btn, callback) {
Zarafa.common.dialogs.MessageBox.show({ Zarafa.common.dialogs.MessageBox.show({
title: 'Please wait', title : 'Please wait',
msg: 'Loading attachment...', msg : 'Loading attachment...',
progressText: 'Initializing...', progressText: 'Initializing...',
width:300, width : 300,
progress:true, progress : true,
closable:false closable : false
}); });
// progress bar... ;) // progress bar... ;)
var f = function(v){ var f = function (v) {
return function(){ return function () {
if(v == 100){ if (v == 100) {
Zarafa.common.dialogs.MessageBox.hide(); Zarafa.common.dialogs.MessageBox.hide();
}else{ } else {
Zarafa.common.dialogs.MessageBox.updateProgress(v/100, Math.round(v)+'% loaded'); Zarafa.common.dialogs.MessageBox.updateProgress(v / 100, Math.round(v) + '% loaded');
} }
}; };
}; };
for(var i = 1; i < 101; i++){ for (var i = 1; i < 101; i++) {
setTimeout(f(i), 20*i); setTimeout(f(i), 20 * i);
} }
/* store the attachment to a temporary folder and prepare it for uploading */ /* store the attachment to a temporary folder and prepare it for uploading */
@ -188,39 +210,46 @@ Zarafa.plugins.calendarimporter.ImportPlugin = Ext.extend(Zarafa.core.Plugin, {
var store = attachmentStore.getParentRecord().get('store_entryid'); var store = attachmentStore.getParentRecord().get('store_entryid');
var entryid = attachmentStore.getAttachmentParentRecordEntryId(); var entryid = attachmentStore.getAttachmentParentRecordEntryId();
var attachNum = new Array(1); var attachNum = new Array(1);
if (attachmentRecord.get('attach_num') != -1) if (attachmentRecord.get('attach_num') != -1) {
attachNum[0] = attachmentRecord.get('attach_num'); attachNum[0] = attachmentRecord.get('attach_num');
else } else {
attachNum[0] = attachmentRecord.get('tmpname'); attachNum[0] = attachmentRecord.get('tmpname');
}
var dialog_attachments = attachmentStore.getId(); var dialog_attachments = attachmentStore.getId();
var filename = attachmentRecord.data.name; var filename = attachmentRecord.data.name;
var responseHandler = new Zarafa.plugins.calendarimporter.data.ResponseHandler({ var responseHandler = new Zarafa.plugins.calendarimporter.data.ResponseHandler({
successCallback: callback successCallback: this.gotAttachmentFileName,
scope : this
}); });
// request attachment preperation // request attachment preperation
container.getRequest().singleRequest( container.getRequest().singleRequest(
'calendarmodule', 'calendarmodule',
'attachmentpath', 'importattachment',
{ {
entryid : entryid, entryid : entryid,
store: store, store : store,
attachNum: attachNum, attachNum : attachNum,
dialog_attachments: dialog_attachments, dialog_attachments: dialog_attachments,
filename: filename filename : filename
}, },
responseHandler responseHandler
); );
}, },
/** /**
* Clickhandler for the button * Open the import dialog.
* @param {String} filename
*/ */
onImportButtonClick: function () { openImportDialog: function (filename) {
Zarafa.core.data.UIFactory.openLayerComponent(Zarafa.core.data.SharedComponentType['plugins.calendarimporter.dialogs.importevents'], undefined, { var componentType = Zarafa.core.data.SharedComponentType['plugins.calendarimporter.dialogs.importevents'];
manager : Ext.WindowMgr var config = {
}); filename: filename,
modal : true
};
Zarafa.core.data.UIFactory.openLayerComponent(componentType, undefined, config);
}, },
/** /**
@ -231,15 +260,22 @@ Zarafa.plugins.calendarimporter.ImportPlugin = Ext.extend(Zarafa.core.Plugin, {
* @param {Ext.data.Record} record Optionally passed record. * @param {Ext.data.Record} record Optionally passed record.
* @return {Number} The bid for the shared component * @return {Number} The bid for the shared component
*/ */
bidSharedComponent : function(type, record) { bidSharedComponent: function (type, record) {
var bid = -1; var bid = -1;
switch(type) { switch (type) {
case Zarafa.core.data.SharedComponentType['plugins.calendarimporter.dialogs.importevents']: case Zarafa.core.data.SharedComponentType['plugins.calendarimporter.dialogs.importevents']:
bid = 2; bid = 2;
break; break;
case Zarafa.core.data.SharedComponentType['plugins.calendarimporter.settings.dialogs.calsyncedit']: case Zarafa.core.data.SharedComponentType['plugins.calendarimporter.settings.dialogs.calsyncedit']:
bid = 2; bid = 2;
break; break;
case Zarafa.core.data.SharedComponentType['common.contextmenu']:
if (record instanceof Zarafa.core.data.MAPIRecord) {
if (record.get('object_type') == Zarafa.core.mapi.ObjectType.MAPI_FOLDER && record.get('container_class') == "IPF.Appointment") {
bid = 2;
}
}
break;
} }
return bid; return bid;
}, },
@ -251,15 +287,18 @@ Zarafa.plugins.calendarimporter.ImportPlugin = Ext.extend(Zarafa.core.Plugin, {
* @param {Ext.data.Record} record Optionally passed record. * @param {Ext.data.Record} record Optionally passed record.
* @return {Ext.Component} Component * @return {Ext.Component} Component
*/ */
getSharedComponent : function(type, record) { getSharedComponent: function (type, record) {
var component; var component;
switch(type) { switch (type) {
case Zarafa.core.data.SharedComponentType['plugins.calendarimporter.dialogs.importevents']: case Zarafa.core.data.SharedComponentType['plugins.calendarimporter.dialogs.importevents']:
component = Zarafa.plugins.calendarimporter.dialogs.ImportContentPanel; component = Zarafa.plugins.calendarimporter.dialogs.ImportContentPanel;
break; break;
case Zarafa.core.data.SharedComponentType['plugins.calendarimporter.settings.dialogs.calsyncedit']: case Zarafa.core.data.SharedComponentType['plugins.calendarimporter.settings.dialogs.calsyncedit']:
component = Zarafa.plugins.calendarimporter.settings.dialogs.CalSyncEditContentPanel; component = Zarafa.plugins.calendarimporter.settings.dialogs.CalSyncEditContentPanel;
break; break;
case Zarafa.core.data.SharedComponentType['common.contextmenu']:
component = Zarafa.plugins.calendarimporter.ui.ContextMenu;
break;
} }
return component; return component;
@ -270,11 +309,11 @@ Zarafa.plugins.calendarimporter.ImportPlugin = Ext.extend(Zarafa.core.Plugin, {
/*############################################################################################################################* /*############################################################################################################################*
* STARTUP * STARTUP
*############################################################################################################################*/ *############################################################################################################################*/
Zarafa.onReady(function() { Zarafa.onReady(function () {
container.registerPlugin(new Zarafa.core.PluginMetaData({ container.registerPlugin(new Zarafa.core.PluginMetaData({
name : 'calendarimporter', name : 'calendarimporter',
displayName : _('Calendarimporter Plugin'), displayName : _('Calendarimporter Plugin'),
about : Zarafa.plugins.calendarimporter.ABOUT, about : Zarafa.plugins.calendarimporter.ABOUT,
pluginConstructor : Zarafa.plugins.calendarimporter.ImportPlugin pluginConstructor: Zarafa.plugins.calendarimporter.ImportPlugin
})); }));
}); });

View File

@ -27,6 +27,7 @@ Zarafa.plugins.calendarimporter.settings.SettingsCalSyncWidget = Ext.extend(Zara
{ name : 'pass' }, { name : 'pass' },
{ name : 'intervall', type : 'int' }, { name : 'intervall', type : 'int' },
{ name : 'calendar' }, { name : 'calendar' },
{ name : 'calendarname' },
{ name : 'lastsync' } { name : 'lastsync' }
], ],
sortInfo : { sortInfo : {
@ -68,7 +69,9 @@ Zarafa.plugins.calendarimporter.settings.SettingsCalSyncWidget = Ext.extend(Zara
var icslinks = settingsModel.get('zarafa/v1/contexts/calendar/icssync', true); var icslinks = settingsModel.get('zarafa/v1/contexts/calendar/icssync', true);
var syncArray = []; var syncArray = [];
for (var key in icslinks) { for (var key in icslinks) {
syncArray.push(Ext.apply({}, icslinks[key], { id : key })); if(icslinks.hasOwnProperty(key)) { // skip inherited props
syncArray.push(Ext.apply({}, icslinks[key], {id: key}));
}
} }
// Load all icslinks into the GridPanel // Load all icslinks into the GridPanel
@ -98,7 +101,8 @@ Zarafa.plugins.calendarimporter.settings.SettingsCalSyncWidget = Ext.extend(Zara
'user' : icslink.get('user'), 'user' : icslink.get('user'),
'pass' : icslink.get('pass'), 'pass' : icslink.get('pass'),
'lastsync' : icslink.get('lastsync'), 'lastsync' : icslink.get('lastsync'),
'calendar' : icslink.get('calendar') 'calendar' : icslink.get('calendar'),
'calendarname' : Zarafa.plugins.calendarimporter.data.Actions.getCalendarFolderByEntryid(icslink.get('calendar')).display_name
}; };
} }
settingsModel.set('zarafa/v1/contexts/calendar/icssync', icslinkData); settingsModel.set('zarafa/v1/contexts/calendar/icssync', icslinkData);

View File

@ -24,13 +24,6 @@ Zarafa.plugins.calendarimporter.settings.SettingsWidget = Ext.extend(Zarafa.sett
title : _('Calendar Import/Export plugin settings'), title : _('Calendar Import/Export plugin settings'),
xtype : 'calendarimporter.settingswidget', xtype : 'calendarimporter.settingswidget',
items : [ items : [
{
xtype : 'checkbox',
name : 'zarafa/v1/plugins/calendarimporter/enable_export',
ref : 'enableExport',
fieldLabel : 'Enable exporter',
lazyInit : false
},
{ {
xtype : 'checkbox', xtype : 'checkbox',
name : 'zarafa/v1/plugins/calendarimporter/enable_sync', name : 'zarafa/v1/plugins/calendarimporter/enable_sync',
@ -47,43 +40,14 @@ Zarafa.plugins.calendarimporter.settings.SettingsWidget = Ext.extend(Zarafa.sett
}, },
createSelectBox: function() { createSelectBox: function() {
var defaultFolder = container.getHierarchyStore().getDefaultFolder('calendar'); // @type: Zarafa.hierarchy.data.MAPIFolderRecord var myStore = Zarafa.plugins.calendarimporter.data.Actions.getAllCalendarFolders(true);
var subFolders = defaultFolder.getChildren();
var myStore = [];
/* add all local calendar folders */
var i = 0;
myStore.push(new Array(defaultFolder.getDefaultFolderKey(), defaultFolder.getDisplayName()));
for(i = 0; i < subFolders.length; i++) {
/* Store all subfolders */
myStore.push(new Array(subFolders[i].getDisplayName(), subFolders[i].getDisplayName(), false)); // 3rd field = isPublicfolder
}
/* add all shared calendar folders */
var pubStore = container.getHierarchyStore().getPublicStore();
if(typeof pubStore !== "undefined") {
try {
var pubFolder = pubStore.getDefaultFolder("publicfolders");
var pubSubFolders = pubFolder.getChildren();
for(i = 0; i < pubSubFolders.length; i++) {
if(pubSubFolders[i].isContainerClass("IPF.Appointment")){
myStore.push(new Array(pubSubFolders[i].getDisplayName(), pubSubFolders[i].getDisplayName() + " [Shared]", true)); // 3rd field = isPublicfolder
}
}
} catch (e) {
console.log("Error opening the shared folder...");
console.log(e);
}
}
return { return {
xtype: "selectbox", xtype: "selectbox",
ref : 'defaultCalendar', ref : 'defaultCalendar',
editable: false, editable: false,
name: "zarafa/v1/plugins/calendarimporter/default_calendar", name: "zarafa/v1/plugins/calendarimporter/default_calendar",
value: container.getSettingsModel().get("zarafa/v1/plugins/calendarimporter/default_calendar"), value: Zarafa.plugins.calendarimporter.data.Actions.getCalendarFolderByName(container.getSettingsModel().get("zarafa/v1/plugins/calendarimporter/default_calendar")).entryid,
width: 100, width: 100,
fieldLabel: "Default calender", fieldLabel: "Default calender",
store: myStore, store: myStore,
@ -123,9 +87,8 @@ Zarafa.plugins.calendarimporter.settings.SettingsWidget = Ext.extend(Zarafa.sett
* @param {Zarafa.settings.SettingsModel} settingsModel The settings to load * @param {Zarafa.settings.SettingsModel} settingsModel The settings to load
*/ */
update : function(settingsModel) { update : function(settingsModel) {
this.enableExport.setValue(settingsModel.get(this.enableExport.name));
this.enableSync.setValue(settingsModel.get(this.enableSync.name)); this.enableSync.setValue(settingsModel.get(this.enableSync.name));
this.defaultCalendar.setValue(settingsModel.get(this.defaultCalendar.name)); this.defaultCalendar.setValue(Zarafa.plugins.calendarimporter.data.Actions.getCalendarFolderByName(settingsModel.get(this.defaultCalendar.name)).entryid);
this.defaultTimezone.setValue(settingsModel.get(this.defaultTimezone.name)); this.defaultTimezone.setValue(settingsModel.get(this.defaultTimezone.name));
}, },
@ -136,10 +99,114 @@ Zarafa.plugins.calendarimporter.settings.SettingsWidget = Ext.extend(Zarafa.sett
* @param {Zarafa.settings.SettingsModel} settingsModel The settings to update * @param {Zarafa.settings.SettingsModel} settingsModel The settings to update
*/ */
updateSettings : function(settingsModel) { updateSettings : function(settingsModel) {
settingsModel.set(this.enableExport.name, this.enableExport.getValue()); // check if the user changed a value
var changed = false;
if(settingsModel.get(this.enableSync.name) != this.enableSync.getValue()) {
changed = true;
} else if(settingsModel.get(this.defaultCalendar.name) != Zarafa.plugins.calendarimporter.data.Actions.getCalendarFolderByEntryid(this.defaultCalendar.getValue()).display_name) {
changed = true;
} else if(settingsModel.get(this.defaultTimezone.name) != this.defaultTimezone.getValue()) {
changed = true;
}
if(changed) {
// Really save changes
settingsModel.set(this.enableSync.name, this.enableSync.getValue()); settingsModel.set(this.enableSync.name, this.enableSync.getValue());
settingsModel.set(this.defaultCalendar.name, this.defaultCalendar.getValue()); settingsModel.set(this.defaultCalendar.name, Zarafa.plugins.calendarimporter.data.Actions.getCalendarFolderByEntryid(this.defaultCalendar.getValue()).display_name); // store name
settingsModel.set(this.defaultTimezone.name, this.defaultTimezone.getValue()); settingsModel.set(this.defaultTimezone.name, this.defaultTimezone.getValue());
this.onUpdateSettings();
}
},
/**
* Called after the {@link Zarafa.settings.SettingsModel} fires the {@link Zarafa.settings.SettingsModel#save save}
* event to indicate the settings were successfully saved and it will forcefully realod the webapp.
* settings which were saved to the server.
* @private
*/
onUpdateSettings : function()
{
var message = _('Your WebApp needs to be reloaded to make the changes visible!');
message += '<br/><br/>';
message += _('WebApp will automatically restart in order for these changes to take effect');
message += '<br/>';
Zarafa.common.dialogs.MessageBox.addCustomButtons({
title: _('Restart WebApp'),
msg : message,
icon: Ext.MessageBox.QUESTION,
fn : this.restartWebapp,
customButton : [{
text : _('Restart'),
name : 'restart'
}, {
text : _('Cancel'),
name : 'cancel'
}],
scope : this
});
},
/**
* Event handler for {@link #onResetSettings}. This will check if the user
* wishes to reset the default settings or not.
* @param {String} button The button which user pressed.
* @private
*/
restartWebapp : function(button)
{
if (button === 'restart') {
var contextModel = this.ownerCt.settingsContext.getModel();
var realModel = contextModel.getRealSettingsModel();
realModel.save();
this.loadMask = new Zarafa.common.ui.LoadMask(Ext.getBody(), {
msg : '<b>' + _('Webapp is reloading, Please wait.') + '</b>'
});
this.loadMask.show();
this.mon(realModel, 'save', this.onSettingsSave, this);
this.mon(realModel, 'exception', this.onSettingsException, this);
}
},
/**
* Called when the {@link Zarafa.settings.} fires the {@link Zarafa.settings.SettingsModel#save save}
* event to indicate the settings were successfully saved and it will forcefully realod the webapp.
* @param {Zarafa.settings.SettingsModel} model The model which fired the event.
* @param {Object} parameters The key-value object containing the action and the corresponding
* settings which were saved to the server.
* @private
*/
onSettingsSave : function(model, parameters)
{
this.mun(model, 'save', this.onSettingsSave, this);
Zarafa.core.Util.reloadWebapp();
},
/**
* Called when the {@link Zarafa.settings.SettingsModel} fires the {@link Zarafa.settings.SettingsModel#exception exception}
* event to indicate the settings were not successfully saved.
* @param {Zarafa.settings.SettingsModel} model The settings model which fired the event
* @param {String} type The value of this parameter will be either 'response' or 'remote'.
* @param {String} action Name of the action (see {@link Ext.data.Api#actions}).
* @param {Object} options The object containing a 'path' and 'value' field indicating
* respectively the Setting and corresponding value for the setting which was being saved.
* @param {Object} response The response object as received from the PHP-side
* @private
*/
onSettingsException : function(model, type, action, options, response)
{
this.loadMask.hide();
// Remove event handlers
this.mun(model, 'save', this.onSettingsSave, this);
this.mun(model, 'exception', this.onSettingsException, this);
} }
}); });

View File

@ -23,7 +23,7 @@ Zarafa.plugins.calendarimporter.settings.dialogs.CalSyncEditContentPanel = Ext.e
model : true, model : true,
autoSave : false, autoSave : false,
width : 400, width : 400,
height : 350, height : 400,
title : _('ICAL Sync'), title : _('ICAL Sync'),
items : [{ items : [{
xtype : 'calendarimporter.calsynceditpanel', xtype : 'calendarimporter.calsynceditpanel',

View File

@ -60,7 +60,6 @@ Zarafa.plugins.calendarimporter.settings.dialogs.CalSyncEditPanel = Ext.extend(E
var id = 0; var id = 0;
var record = undefined; var record = undefined;
console.log(this);
if(!this.currentItem) { if(!this.currentItem) {
record = new store.recordType({ record = new store.recordType({
id: this.hashCode(this.icsurl.getValue()), id: this.hashCode(this.icsurl.getValue()),
@ -69,6 +68,7 @@ Zarafa.plugins.calendarimporter.settings.dialogs.CalSyncEditPanel = Ext.extend(E
user: this.user.getValue(), user: this.user.getValue(),
pass: Ext.util.base64.encode(this.pass.getValue()), pass: Ext.util.base64.encode(this.pass.getValue()),
calendar: this.calendar.getValue(), calendar: this.calendar.getValue(),
calendarname : Zarafa.plugins.calendarimporter.data.Actions.getCalendarFolderByEntryid(this.calendar.getValue()).display_name,
lastsync: "never" lastsync: "never"
}); });
} }
@ -82,6 +82,7 @@ Zarafa.plugins.calendarimporter.settings.dialogs.CalSyncEditPanel = Ext.extend(E
this.currentItem.set('user', this.user.getValue()); this.currentItem.set('user', this.user.getValue());
this.currentItem.set('pass', Ext.util.base64.encode(this.pass.getValue())); this.currentItem.set('pass', Ext.util.base64.encode(this.pass.getValue()));
this.currentItem.set('calendar', this.calendar.getValue()); this.currentItem.set('calendar', this.calendar.getValue());
this.currentItem.set('calendarname', Zarafa.plugins.calendarimporter.data.Actions.getCalendarFolderByEntryid(this.calendar.getValue()).display_name);
} }
this.dialog.close(); this.dialog.close();
} }
@ -95,14 +96,12 @@ Zarafa.plugins.calendarimporter.settings.dialogs.CalSyncEditPanel = Ext.extend(E
createPanelItems : function(config) createPanelItems : function(config)
{ {
var icsurl = ""; var icsurl = "";
var intervall = ""; var intervall = "15";
var user = ""; var user = "";
var pass = ""; var pass = "";
var calendar = ""; var calendarname = "";
var calendar = Zarafa.plugins.calendarimporter.data.Actions.getCalendarFolderByName(container.getSettingsModel().get("zarafa/v1/plugins/calendarimporter/default_calendar")).entryid;
var defaultFolder = container.getHierarchyStore().getDefaultFolder('calendar'); // @type: Zarafa.hierarchy.data.MAPIFolderRecord var myStore = Zarafa.plugins.calendarimporter.data.Actions.getAllCalendarFolders(true);
var subFolders = defaultFolder.getChildren();
var myStore = [];
if(config.item){ if(config.item){
icsurl = config.item.get('icsurl'); icsurl = config.item.get('icsurl');
@ -110,33 +109,7 @@ Zarafa.plugins.calendarimporter.settings.dialogs.CalSyncEditPanel = Ext.extend(E
user = config.item.get('user'); user = config.item.get('user');
pass = Ext.util.base64.decode(config.item.get('pass')); pass = Ext.util.base64.decode(config.item.get('pass'));
calendar = config.item.get('calendar'); calendar = config.item.get('calendar');
} calendarname = config.item.get('calendarname');
/* add all local calendar folders */
var i = 0;
myStore.push(new Array(defaultFolder.getDefaultFolderKey(), defaultFolder.getDisplayName()));
for(i = 0; i < subFolders.length; i++) {
/* Store all subfolders */
myStore.push(new Array(subFolders[i].getDisplayName(), subFolders[i].getDisplayName(), false)); // 3rd field = isPublicfolder
}
/* add all shared calendar folders */
var pubStore = container.getHierarchyStore().getPublicStore();
if(typeof pubStore !== "undefined") {
try {
var pubFolder = pubStore.getDefaultFolder("publicfolders");
var pubSubFolders = pubFolder.getChildren();
for(i = 0; i < pubSubFolders.length; i++) {
if(pubSubFolders[i].isContainerClass("IPF.Appointment")){
myStore.push(new Array(pubSubFolders[i].getDisplayName(), pubSubFolders[i].getDisplayName() + " [Shared]", true)); // 3rd field = isPublicfolder
}
}
} catch (e) {
console.log("Error opening the shared folder...");
console.log(e);
}
} }

View File

@ -58,6 +58,16 @@ Zarafa.plugins.calendarimporter.settings.ui.CalSyncGrid = Ext.extend(Ext.grid.Gr
return value ? "true" : "false"; return value ? "true" : "false";
}, },
/**
* Render function
* @return {String}
* @private
*/
renderCalendarColumn : function(value, p, record)
{
return Zarafa.plugins.calendarimporter.data.Actions.getCalendarFolderByEntryid(value).display_name;
},
/** /**
* Creates a column model object, used in {@link #colModel} config * Creates a column model object, used in {@link #colModel} config
* @return {Ext.grid.ColumnModel} column model object * @return {Ext.grid.ColumnModel} column model object
@ -71,7 +81,7 @@ Zarafa.plugins.calendarimporter.settings.ui.CalSyncGrid = Ext.extend(Ext.grid.Gr
renderer : Zarafa.common.ui.grid.Renderers.text renderer : Zarafa.common.ui.grid.Renderers.text
}, },
{ {
dataIndex : 'calendar', dataIndex : 'calendarname',
header : _('Destination Calender'), header : _('Destination Calender'),
renderer : Zarafa.common.ui.grid.Renderers.text renderer : Zarafa.common.ui.grid.Renderers.text
}, },

129
js/ui/ContextMenu.js Normal file
View File

@ -0,0 +1,129 @@
/**
* ContectMenu.js zarafa calender to ics im/exporter
*
* Author: Christoph Haas <christoph.h@sprinternet.at>
* Copyright (C) 2012-2016 Christoph Haas
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
Ext.namespace('Zarafa.plugins.calendarimporter.ui');
/**
* @class Zarafa.plugins.calendarimporter.ui.ContextMenu
* @extends Zarafa.hierarchy.ui.ContextMenu
* @xtype calendarimporter.hierarchycontextmenu
*/
Zarafa.plugins.calendarimporter.ui.ContextMenu = Ext.extend(Zarafa.hierarchy.ui.ContextMenu, {
/**
* @constructor
* @param {Object} config Configuration object
*/
constructor: function (config) {
config = config || {};
if (config.contextNode) {
config.contextTree = config.contextNode.getOwnerTree();
}
Zarafa.plugins.calendarimporter.ui.ContextMenu.superclass.constructor.call(this, config);
// add item to menu
var additionalItems = this.createAdditionalContextMenuItems(config);
for (var i = 0; i < additionalItems.length; i++) {
config.items[0].push(additionalItems[i]);
}
Zarafa.plugins.calendarimporter.ui.ContextMenu.superclass.constructor.call(this, config); // redo ... otherwise menu does not get published
},
/**
* Create the Action context menu items.
* @param {Object} config Configuration object for the {@link Zarafa.plugins.calendarimporter.ui.ContextMenu ContextMenu}
* @return {Zarafa.core.ui.menu.ConditionalItem[]} The list of Action context menu items
* @private
*
* Note: All handlers are called within the scope of {@link Zarafa.plugins.calendarimporter.ui.ContextMenu HierarchyContextMenu}
*/
createAdditionalContextMenuItems: function (config) {
return [{
xtype: 'menuseparator'
}, {
text : _('Import Calendar'),
iconCls : 'icon_calendarimporter_import',
handler : this.onContextItemImport,
beforeShow: function (item, record) {
var access = record.get('access') & Zarafa.core.mapi.Access.ACCESS_MODIFY;
if (!access || (record.isIPMSubTree() && !record.getMAPIStore().isDefaultStore())) {
item.setDisabled(true);
} else {
item.setDisabled(false);
}
}
}, {
text : _('Export Calendar'),
iconCls : 'icon_calendarimporter_export',
handler : this.onContextItemExport,
beforeShow: function (item, record) {
var access = record.get('access') & Zarafa.core.mapi.Access.ACCESS_READ;
if (!access || (record.isIPMSubTree() && !record.getMAPIStore().isDefaultStore())) {
item.setDisabled(true);
} else {
item.setDisabled(false);
}
}
}];
},
/**
* Fires on selecting 'Open' menu option from {@link Zarafa.plugins.calendarimporter.ui.ContextMenu ContextMenu}
* @private
*/
onContextItemExport: function () {
var responseHandler = new Zarafa.plugins.calendarimporter.data.ResponseHandler({
successCallback: Zarafa.plugins.calendarimporter.data.Actions.downloadICS,
scope : this
});
// request attachment preperation
container.getRequest().singleRequest(
'calendarmodule',
'export',
{
storeid: this.records.get("store_entryid"),
folder : this.records.get("entryid")
},
responseHandler
);
},
/**
* Fires on selecting 'Open' menu option from {@link Zarafa.plugins.calendarimporter.ui.ContextMenu ContextMenu}
* @private
*/
onContextItemImport: function () {
var componentType = Zarafa.core.data.SharedComponentType['plugins.calendarimporter.dialogs.importevents'];
var config = {
modal : true,
folder: this.records.get("entryid")
};
Zarafa.core.data.UIFactory.openLayerComponent(componentType, undefined, config);
}
});
Ext.reg('calendarimporter.hierarchycontextmenu', Zarafa.plugins.calendarimporter.ui.ContextMenu);

View File

@ -2,7 +2,7 @@
<!DOCTYPE plugin SYSTEM "manifest.dtd"> <!DOCTYPE plugin SYSTEM "manifest.dtd">
<plugin version="2"> <plugin version="2">
<info> <info>
<version>@_@PLUGIN_VERSION@_@</version> <version>2.2.0</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>
@ -20,13 +20,14 @@
<serverfile type="module" module="calendarmodule">php/module.calendar.php</serverfile> <serverfile type="module" module="calendarmodule">php/module.calendar.php</serverfile>
</server> </server>
<client> <client>
<clientfile load="release">js/calendarimporter.js</clientfile> <clientfile load="release">js/calendarimporter-debug.js</clientfile>
<clientfile load="debug">js/calendarimporter-debug.js</clientfile> <clientfile load="debug">js/calendarimporter-debug.js</clientfile>
<clientfile load="source">js/data/timezones.js</clientfile> <clientfile load="source">js/data/timezones.js</clientfile>
<clientfile load="source">js/external/Ext.util.base64.js</clientfile> <clientfile load="source">js/data/Actions.js</clientfile>
<clientfile load="source">js/plugin.calendarimporter.js</clientfile>
<clientfile load="source">js/data/ResponseHandler.js</clientfile> <clientfile load="source">js/data/ResponseHandler.js</clientfile>
<clientfile load="source">js/external/Ext.util.base64.js</clientfile>
<clientfile load="source">js/ui/ContextMenu.js</clientfile>
<clientfile load="source">js/dialogs/ImportContentPanel.js</clientfile> <clientfile load="source">js/dialogs/ImportContentPanel.js</clientfile>
<clientfile load="source">js/dialogs/ImportPanel.js</clientfile> <clientfile load="source">js/dialogs/ImportPanel.js</clientfile>
<clientfile load="source">js/dialogs/settings/SettingsWidget.js</clientfile> <clientfile load="source">js/dialogs/settings/SettingsWidget.js</clientfile>
@ -35,9 +36,10 @@
<clientfile load="source">js/dialogs/settings/ui/CalSyncPanel.js</clientfile> <clientfile load="source">js/dialogs/settings/ui/CalSyncPanel.js</clientfile>
<clientfile load="source">js/dialogs/settings/dialogs/CalSyncEditContentPanel.js</clientfile> <clientfile load="source">js/dialogs/settings/dialogs/CalSyncEditContentPanel.js</clientfile>
<clientfile load="source">js/dialogs/settings/dialogs/CalSyncEditPanel.js</clientfile> <clientfile load="source">js/dialogs/settings/dialogs/CalSyncEditPanel.js</clientfile>
<clientfile load="source">js/plugin.calendarimporter.js</clientfile>
</client> </client>
<resources> <resources>
<resourcefile load="release">resources/css/calendarimporter-min.css</resourcefile> <resourcefile load="release">resources/css/calendarimporter.css</resourcefile>
<resourcefile load="debug">resources/css/calendarimporter.css</resourcefile> <resourcefile load="debug">resources/css/calendarimporter.css</resourcefile>
<resourcefile load="source">resources/css/calendarimporter-main.css</resourcefile> <resourcefile load="source">resources/css/calendarimporter-main.css</resourcefile>
</resources> </resources>

5
php/.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
composer.phar
.composer.lock
composer.lock
vendor
vendor/*

5
php/composer.json Normal file
View File

@ -0,0 +1,5 @@
{
"require": {
"sabre/vobject": "4.1"
}
}

View File

@ -1,9 +1,10 @@
<?php <?php
/** /**
* download.php, zarafa calender to ics im/exporter * download.php, zarafa calendar to ics im/exporter
* *
* Author: Christoph Haas <christoph.h@sprinternet.at> * Author: Christoph Haas <christoph.h@sprinternet.at>
* Copyright (C) 2012-2014 Christoph Haas * Copyright (C) 2012-2016 Christoph Haas
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
@ -20,25 +21,54 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* *
*/ */
$basedir = $_GET["basedir"]; namespace calendarimporter;
$secid = $_GET["secid"];
$fileid = $_GET["fileid"];
$realname = $_GET["realname"];
$secfile = $basedir . "/secid." . $secid; class DownloadHandler
$icsfile = $basedir . "/" . $fileid . "." . $secid; {
/**
* Download the given vcf file.
* @return bool
*/
public static function doDownload()
{
if (isset($_GET["token"])) {
$token = $_GET["token"];
} else {
return false;
}
// if the secid file exists -> download! if (isset($_GET["filename"])) {
if(file_exists($secfile)) { $filename = $_GET["filename"];
@header("Last-Modified: " . @gmdate("D, d M Y H:i:s",time()) . " GMT"); } else {
@header("Content-type: text/calendar"); return false;
header("Content-Length: " . filesize($icsfile)); }
header("Content-Disposition: attachment; filename=" . $realname . ".ics");
//write ics // validate token
readfile($icsfile); if (!ctype_alnum($token)) { // token is a md5 hash
unlink($secfile); return false;
unlink($icsfile); }
$file = PLUGIN_CALENDARIMPORTER_TMP_UPLOAD . "ics_" . $token . ".ics";
if (!file_exists($file)) { // invalid token
return false;
}
// set headers here
header('Content-Disposition: attachment; filename="' . $filename . '"');
// no caching
header('Expires: 0'); // set expiration time
header('Content-Description: File Transfer');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Content-Length: ' . filesize($file));
header('Content-Type: application/octet-stream');
header('Pragma: public');
flush();
// print the downloaded file
readfile($file);
ignore_user_abort(true);
unlink($file);
}
} }
?>

File diff suppressed because it is too large Load Diff

View File

@ -1,552 +0,0 @@
<?php
/**
* class.icalparser.php zarafa calender to ics im/exporter
* http://code.google.com/p/ics-parser/
*
* Author: Martin Thoma , Christoph Haas <christoph.h@sprinternet.at>
* Copyright (C) 2012-2014 Christoph Haas
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
/**
* This is the iCal-class
* Parse ics file content to array.
*
* @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;
/* Currently editing an alarm? */
private /** @type {boolean} */ $isalarm = false;
/* 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;
/* The default timezone, used to convert UTC Time */
private /** @type {string} */ $default_timezone = "Europe/Vienna";
/* The default timezone, used to convert UTC Time */
private /** @type {boolean} */ $timezone_set = false;
/* Ignore Daylight Saving Time */
private /** @type {boolean} */ $ignore_dst = false;
/**
* Creates the iCal-Object
*
* @param {string} $filename The path to the iCal-file
*
* @return Object The iCal-Object
*/
public function __construct($filename, $default_timezone, $timezone = false, $igndst = false) {
if (!$filename) {
$this->errors = "No filename specified";
return false;
}
$this->default_timezone = $default_timezone;
if(isset($timezone) && $timezone != false) {
$this->default_timezone = $timezone;
$this->timezone_set = true;
}
if(isset($igndst) && $igndst != false) {
$this->ignore_dst = true;
}
$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 {
foreach ($lines as $line) {
$line = trim($line);
$add = $this->keyValueFromString($line);
if ($add === false) {
$this->addCalendarComponentWithKeyAndValue($type, false, $line);
continue;
}
list($keyword, $dummy, $prop, $propvalue, $value) = $add;
switch ($line) {
// http://www.kanzaki.com/docs/ical/vtodo.html
case "BEGIN:VTODO":
$this->todo_count++;
$type = "VTODO";
break;
case "BEGIN:VALARM":
//echo "vevent gematcht";
$this->isalarm=true;
$type = "VEVENT";
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;
case "END:VALARM":
$this->isalarm=false;
$type = "VEVENT";
break;
default:
$this->addCalendarComponentWithKeyAndValue($type, $keyword, $value, $prop, $propvalue);
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, $prop = false, $propvalue = false) {
if ($keyword == false) { // multiline value
$keyword = $this->last_keyword;
switch ($component) {
case 'VEVENT':
if (stristr($keyword, "DTSTART") or stristr($keyword, "DTEND") or stristr($keyword, "TRIGGER")) {
$ts = $this->iCalDateToUnixTimestamp($value, $prop, $propvalue);
$value = $ts * 1000;
}
$value = str_replace("\\n", "\n", $value);
$value = $this->customFilters($keyword, $value);
if(!$this->isalarm) {
$value = $this->cal[$component][$this->event_count - 1][$keyword].$value;
} else {
$value = $this->cal[$component][$this->event_count - 1]["VALARM"][$keyword].$value;
}
break;
case 'VTODO' :
$value = $this->cal[$component][$this->todo_count - 1]
[$keyword].$value;
break;
}
}
/* This should not be neccesary anymore*/
//always strip additional content....
//if (stristr($keyword, "DTSTART") or stristr($keyword, "DTEND")) {
//$keyword = explode(";", $keyword);
//$keyword = $keyword[0]; // remove additional content like VALUE=DATE
//}
if ((stristr($keyword, "TIMEZONE") || stristr($keyword, "TZID")) && !$this->timezone_set) { // check if timezone already set...
$this->default_timezone = $this->trimTimeZone($value); // store the calendertimezone
}
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") or stristr($keyword, "TRIGGER")) {
$ts = $this->iCalDateToUnixTimestamp($value, $prop, $propvalue);
$value = $ts * 1000;
}
$value = str_replace("\\n", "\n", $value);
$value = $this->customFilters($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;
default:
$this->cal[$component][$keyword] = $value;
break;
}
$this->last_keyword = $keyword;
}
/**
* Filter some chars out of the value.
*
* @param {string} $keyword keyword to which the filter is applied
* @param {string} $value to filter
* @return {string} filtered value
*/
private function customFilters($keyword, $value) {
if (stristr($keyword, "SUMMARY")) {
$value = str_replace("\n", " ", $value); // we don't need linebreaks in the summary...
}
if (stristr($keyword, "SUMMARY")) {
$value = str_replace("\,", ",", $value); // strange escaped comma
}
return $value;
}
/**
* Trim a Timezone String
*
* @param {string} $timezone timezone string which should be trimmed
* @return {string} trimmed value
*/
private function trimTimeZone($timezone) {
if(preg_match('~([?<=/]*)([^/]*[/|-][^/]*$)~', $timezone, $matches)) { // detects tzurls in tzids
if ($matches[2] != "") {
return $matches[2]; // 2 = extracted timezone
} else {
return $timezone;
}
}
return $timezone;
}
/**
* Get a key-value pair of a string.
*
* @param {string} $text which is like "VCALENDAR:Begin" or "LOCATION:"
*
* @return {array} array("Argument", "Optional Arg/Val", "Optional Arg", "Optional Value", "Value")
*/
public function keyValueFromString($text) {
preg_match('/(^[^a-z:;]+)([;]+([a-zA-Z]*)[=]*([^:"]*|"[\w\W]*"))?[:]([\w\W]*)/', $text, $matches);
// this regex has problems with multiple attributes... ATTENDEE;RSVP=TRUE;ROLE=REQ-PARTICIPANT:mailto:jsmith@example.com
// TODO: fix this
if (count($matches) == 0) {
return false;
}
$matches = array_splice($matches, 1, 5); // 0 = Arg, 1 = Complete Optional Arg/Val, 2 = Optional Arg, 3 = Optional Val, 4 = Value
return $matches;
}
/**
* Return UTC 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}
*/
private function iCalDateToUTCUnixTimestamp($icalDate, $prop, $propvalue) {
$timezone = false;
$allday = false;
if($prop) {
$pos = strpos("TZIDtzid", $prop);
if($pos !== false && $propvalue != false) {
$timezone = str_replace('"', '', $propvalue);
$timezone = str_replace('\'', '', $timezone);
$timezone = $this->trimTimeZone($timezone);
}
}
/* timestring format */
$utc = strpos("zZ",substr($icalDate, -1)) === false ? false : true;
$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;
}
// check if we have a allday event
if((!$date[6] || $date[6] === "") || (!$date[5] || $date[5] === "") || (!$date[4] || $date[4] === "")) {
$date[6] = 0;
$date[5] = 0;
$date[4] = 0;
$allday = true;
$dtz = date_default_timezone_get();
date_default_timezone_set('UTC');
}
// 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]);
if($allday) {
date_default_timezone_set($dtz);
}
if(!$utc && !$allday) {
$tz = $this->default_timezone;
if($timezone != false) {
$tz = $timezone;
}
$error = false;
$this_tz = false;
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 {
$timestamp_utc = $timestamp;
}
return array($timestamp_utc,$allday);
}
/**
* Return a timezone specific timestamp
* @param {int} $timestamp_utc UTC Timestamp to convert
* @param {string} $timezone Timezone
* @return {int}
*/
private function UTCTimestampToTZTimestamp($timestamp_utc, $timezone, $ignore_dst = false) {
$this_tz = false;
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) {
$transition = $this_tz->getTransitions($timestamp_utc,$timestamp_utc);
$trans_offset = $transition[0]['offset'];
$isdst = $transition[0]['isdst'];
$tz_now = new DateTime("now", $this_tz);
$tz_offset = $this_tz->getOffset($tz_now);
if(!$ignore_dst) {
$tz_offset = $trans_offset; // normaly use dst
}
return $timestamp_utc + $tz_offset;
}
return $timestamp_utc; // maybe timezone conversion will fail...
}
/**
* Return Timezone specific 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, $prop, $propvalue) {
list($timestamp, $allday) = $this->iCalDateToUTCUnixTimestamp($icalDate, $prop, $propvalue);
if(!$allday) {
$timestamp = $this->UTCTimestampToTZTimestamp($timestamp, $this->default_timezone, $this->ignore_dst, $allday);
}
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 the default or set timezone
*
* @return {string}
*/
public function timezone() {
return $this->default_timezone;
}
/**
* 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;
}
}
?>

View File

@ -21,21 +21,61 @@
* *
*/ */
include_once('mapi/class.recurrence.php'); include_once('vendor/autoload.php');
include_once('plugins/calendarimporter/php/ical/class.icalcreator.php');
include_once('plugins/calendarimporter/php/ical/class.icalparser.php');
class CalendarModule extends Module { use Sabre\VObject;
private $DEBUG = false; // enable error_log debugging class CalendarModule extends Module
{
private $DEBUG = true; // enable error_log debugging
private $busystates = null;
private $labels = null;
private $attendeetype = null;
/** /**
* @constructor * @constructor
* @param $id * @param $id
* @param $data * @param $data
*/ */
public function __construct($id, $data) { public function __construct($id, $data)
parent::Module($id, $data); {
parent::__construct($id, $data);
// init default timezone
date_default_timezone_set(PLUGIN_CALENDARIMPORTER_DEFAULT_TIMEZONE);
// init mappings
$this->busystates = array(
"FREE",
"TENTATIVE",
"BUSY",
"OOF"
);
$this->labels = array(
"NONE",
"IMPORTANT",
"WORK",
"PERSONAL",
"HOLIDAY",
"REQUIRED",
"TRAVEL REQUIRED",
"PREPARATION REQUIERED",
"BIRTHDAY",
"SPECIAL DATE",
"PHONE INTERVIEW"
);
$this->attendeetype = array(
"NON-PARTICIPANT", // needed as zarafa starts counting at 1
"REQ-PARTICIPANT",
"OPT-PARTICIPANT",
"NON-PARTICIPANT"
);
} }
/** /**
@ -43,28 +83,32 @@ class CalendarModule extends Module {
* Exception part is used for authentication errors also * Exception part is used for authentication errors also
* @return boolean true on success or false on failure. * @return boolean true on success or false on failure.
*/ */
public function execute() { public function execute()
{
$result = false; $result = false;
if(!$this->DEBUG) { if (!$this->DEBUG) {
/* disable error printing - otherwise json communication might break... */ /* disable error printing - otherwise json communication might break... */
ini_set('display_errors', '0'); ini_set('display_errors', '0');
} }
foreach($this->data as $actionType => $actionData) { foreach ($this->data as $actionType => $actionData) {
if(isset($actionType)) { if (isset($actionType)) {
try { try {
if($this->DEBUG) { if ($this->DEBUG) {
error_log("exec: " . $actionType); error_log("exec: " . $actionType);
} }
switch($actionType) { switch ($actionType) {
case "load":
$result = $this->loadCalendar($actionType, $actionData);
break;
case "export": case "export":
$result = $this->exportCalendar($actionType, $actionData); $result = $this->exportCalendar($actionType, $actionData);
break; break;
case "import": case "import":
$result = $this->importCalendar($actionType, $actionData); $result = $this->importCalendar($actionType, $actionData);
break; break;
case "attachmentpath": case "importattachment":
$result = $this->getAttachmentPath($actionType, $actionData); $result = $this->getAttachmentPath($actionType, $actionData);
break; break;
default: default:
@ -72,11 +116,11 @@ class CalendarModule extends Module {
} }
} catch (MAPIException $e) { } catch (MAPIException $e) {
if($this->DEBUG) { if ($this->DEBUG) {
error_log("mapi exception: " . $e->getMessage()); error_log("mapi exception: " . $e->getMessage());
} }
} catch (Exception $e) { } catch (Exception $e) {
if($this->DEBUG) { if ($this->DEBUG) {
error_log("exception: " . $e->getMessage()); error_log("exception: " . $e->getMessage());
} }
} }
@ -91,11 +135,12 @@ class CalendarModule extends Module {
* @param $length the lenght of the generated string * @param $length the lenght of the generated string
* @return string a random string * @return string a random string
*/ */
private function randomstring($length = 6) { private function randomstring($length = 6)
{
// $chars - all allowed charakters // $chars - all allowed charakters
$chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
srand((double)microtime()*1000000); srand((double)microtime() * 1000000);
$i = 0; $i = 0;
$pass = ""; $pass = "";
while ($i < $length) { while ($i < $length) {
@ -108,224 +153,60 @@ class CalendarModule extends Module {
} }
/** /**
* Generates the secid file (used to verify the download path) * Get a property from the array.
* @param $secid the secid, a random security token * @param $props
* @param $propname
* @return string
*/ */
private function createSecIDFile($secid) { private function getProp($props, $propname)
$lockFile = TMP_PATH . "/secid." . $secid; {
$fh = fopen($lockFile, 'w') or die("can't open secid file"); if (isset($props["props"][$propname])) {
$stringData = date(DATE_RFC822); return $props["props"][$propname];
fwrite($fh, $stringData); }
fclose($fh); return "";
} }
/** private function getDurationStringFromMintues($minutes, $pos = false) {
* Generates the secid file (used to verify the download path) $pos = $pos === true ? "+" : "-";
* @param $time a timestamp $str = $pos . "P";
* @param $incl_time true if date should include time
* @ return date object
*/
private function getIcalDate($time, $incl_time = true) { // variables for holding values
return $incl_time ? date('Ymd\THis', $time) : date('Ymd', $time); $mins = intval($minutes);
$hours = 0;
$days = 0;
$weeks = 0;
// calculations
if ( $mins >= 60 ) {
$hours = (int)($mins / 60);
$mins = $mins % 60;
}
if ( $hours >= 24 ) {
$days = (int)($hours / 24);
$hours = $hours % 60;
}
if ( $days >= 7 ) {
$weeks = (int)($days / 7);
$days = $days % 7;
} }
/** // format result
* adds an event to the exported calendar) if ( $weeks ) {
* @param $vevent pointer to the eventstore $str .= "{$weeks}W";
* @param $event the event to add
*/
private function addEvent(&$vevent, $event) {
$busystate = array("FREE", "TENTATIVE", "BUSY", "OOF");
$zlabel = array("NONE", "IMPORTANT", "WORK", "PERSONAL", "HOLIDAY", "REQUIRED", "TRAVEL REQUIRED", "PREPARATION REQUIERED", "BIRTHDAY", "SPECIAL DATE", "PHONE INTERVIEW");
$vevent->setProperty("LOCATION", $event["location"]); // property name - case independent
$vevent->setProperty("SUMMARY", $event["subject"]);
$vevent->setProperty("DESCRIPTION", str_replace("\n", "\\n",$event["description"]));
$vevent->setProperty("COMMENT", "Exported from Zarafa" );
$vevent->setProperty("ORGANIZER", $event["sent_representing_email_address"]);
$vevent->setProperty("DTSTART", $this->getIcalDate($event["commonstart"]) . "Z");
$vevent->setProperty("DTEND", $this->getIcalDate($event["commonend"]) . "Z");
$vevent->setProperty("DTSTAMP", $this->getIcalDate($event["creation_time"]) . "Z");
$vevent->setProperty("CREATED", $this->getIcalDate($event["creation_time"]) . "Z");
$vevent->setProperty("LAST-MODIFIED", $this->getIcalDate($event["last_modification_time"]) . "Z");
$vevent->setProperty("X-MICROSOFT-CDO-BUSYSTATUS", $busystate[$event["busystatus"]]);
$vevent->setProperty("X-ZARAFA-LABEL", $zlabel[$event["label"]]);
$vevent->setProperty("PRIORITY", $event["importance"]);
$vevent->setProperty("CLASS", $event["private"] ? "PRIVATE" : "PUBLIC");
// ATTENDEES
if(count($event["attendees"]) > 0) {
foreach($event["attendees"] as $attendee) {
$vevent->setProperty("ATTENDEE", $attendee["props"]["smtp_address"]);
} }
if ( $days ) {
$str .= "{$days}D";
}
if ( $hours ) {
$str .= "{$hours}H";
}
if ( $mins ) {
$str .= "{$mins}M";
} }
// REMINDERS return $str;
if($event["reminder"]) {
$valarm = & $vevent->newComponent("valarm"); // create an event alarm
$valarm->setProperty("action", "DISPLAY" );
$valarm->setProperty("description", $vevent->getProperty("SUMMARY")); // reuse the event summary
$valarm->setProperty("trigger", $this->getIcalDate($event["reminder_time"]) . "Z"); // create alarm trigger (in UTC datetime)
}
}
/**
* Loads the descriptiontext of an event
* @param $event
* @return array with event description/body
*/
private function loadEventDescription($event) {
$entryid = $this->getActionEntryID($event);
$store = $this->getActionStore($event);
$basedate = null;
$properties = $GLOBALS['properties']->getAppointmentProperties();
$plaintext = true;
$data = array();
if($store && $entryid) {
$message = $GLOBALS['operations']->openMessage($store, $entryid);
// add all standard properties from the series/normal message
$data['item'] = $GLOBALS['operations']->getMessageProps($store, $message, $properties, (isset($plaintext) && $plaintext));
// if appointment is recurring then only we should get properties of occurence if basedate is supplied
if($data['item']['props']['recurring'] === true) {
if(isset($basedate) && $basedate) {
$recur = new Recurrence($store, $message);
$exceptionatt = $recur->getExceptionAttachment($basedate);
// Single occurences are never recurring
$data['item']['props']['recurring'] = false;
if($exceptionatt) {
// Existing exception (open existing item, which includes basedate)
$exceptionattProps = mapi_getprops($exceptionatt, array(PR_ATTACH_NUM));
$exception = mapi_attach_openobj($exceptionatt, 0);
// overwrite properties with the ones from the exception
$exceptionProps = $GLOBALS['operations']->getMessageProps($store, $exception, $properties, (isset($plaintext) && $plaintext));
/**
* If recurring item has set reminder to true then
* all occurrences before the 'flagdueby' value(of recurring item)
* should not show that reminder is set.
*/
if (isset($exceptionProps['props']['reminder']) && $data['item']['props']['reminder'] == true) {
$flagDueByDay = $recur->dayStartOf($data['item']['props']['flagdueby']);
if ($flagDueByDay > $basedate) {
$exceptionProps['props']['reminder'] = false;
}
}
// The properties must be merged, if the recipients or attachments are present in the exception
// then that list should be used. Otherwise the list from the series must be applied (this
// corresponds with OL2007).
// @FIXME getMessageProps should not return empty string if exception doesn't contain body
// by this change we can handle a situation where user has set empty string in the body explicitly
if (!empty($exceptionProps['props']['body']) || !empty($exceptionProps['props']['html_body'])) {
if(!empty($exceptionProps['props']['body'])) {
$data['item']['props']['body'] = $exceptionProps['props']['body'];
}
if(!empty($exceptionProps['props']['html_body'])) {
$data['item']['props']['html_body'] = $exceptionProps['props']['html_body'];
}
$data['item']['props']['isHTML'] = $exceptionProps['props']['isHTML'];
}
// remove properties from $exceptionProps so array_merge will not overwrite it
unset($exceptionProps['props']['html_body']);
unset($exceptionProps['props']['body']);
unset($exceptionProps['props']['isHTML']);
$data['item']['props'] = array_merge($data['item']['props'], $exceptionProps['props']);
if (isset($exceptionProps['recipients'])) {
$data['item']['recipients'] = $exceptionProps['recipients'];
}
if (isset($exceptionProps['attachments'])) {
$data['item']['attachments'] = $exceptionProps['attachments'];
}
// Make sure we are using the passed basedate and not something wrong in the opened item
$data['item']['props']['basedate'] = $basedate;
} else {
// opening an occurence of a recurring series (same as normal open, but add basedate, startdate and enddate)
$data['item']['props']['basedate'] = $basedate;
$data['item']['props']['startdate'] = $recur->getOccurrenceStart($basedate);
$data['item']['props']['duedate'] = $recur->getOccurrenceEnd($basedate);
$data['item']['props']['commonstart'] = $data['item']['props']['startdate'];
$data['item']['props']['commonend'] = $data['item']['props']['duedate'];
unset($data['item']['props']['reminder_time']);
/**
* If recurring item has set reminder to true then
* all occurrences before the 'flagdueby' value(of recurring item)
* should not show that reminder is set.
*/
if (isset($exceptionProps['props']['reminder']) && $data['item']['props']['reminder'] == true) {
$flagDueByDay = $recur->dayStartOf($data['item']['props']['flagdueby']);
if ($flagDueByDay > $basedate) {
$exceptionProps['props']['reminder'] = false;
}
}
}
} else {
// Opening a recurring series, get the recurrence information
$recur = new Recurrence($store, $message);
$recurpattern = $recur->getRecurrence();
$tz = $recur->tz; // no function to do this at the moment
// Add the recurrence pattern to the data
if(isset($recurpattern) && is_array($recurpattern)) {
$data['item']['props'] += $recurpattern;
}
// Add the timezone information to the data
if(isset($tz) && is_array($tz)) {
$data['item']['props'] += $tz;
}
}
}
}
return $data['item']['props']['body'];
}
/**
* Loads the attendees of an event
* @param $event
* @return array with event attendees
*/
private function loadAttendees($event) {
$entryid = $this->getActionEntryID($event);
$store = $this->getActionStore($event);
$basedate = null;
$properties = $GLOBALS['properties']->getAppointmentProperties();
$plaintext = true;
$data = array();
if($store && $entryid) {
$message = $GLOBALS['operations']->openMessage($store, $entryid);
// add all standard properties from the series/normal message
$data['item'] = $GLOBALS['operations']->getMessageProps($store, $message, $properties, (isset($plaintext) && $plaintext));
}
return $data['item']['recipients']['item'];
} }
/** /**
@ -333,61 +214,137 @@ class CalendarModule extends Module {
* @param $actionType * @param $actionType
* @param $actionData * @param $actionData
*/ */
private function exportCalendar($actionType, $actionData) { private function exportCalendar($actionType, $actionData)
$secid = $this->randomstring(); {
$this->createSecIDFile($secid); // Get store id
$tmpname = stripslashes($actionData["calendar"] . ".ics." . $this->randomstring(8)); $storeid = false;
$filename = TMP_PATH . "/" . $tmpname . "." . $secid; if (isset($actionData["storeid"])) {
$storeid = $actionData["storeid"];
if(!is_writable(TMP_PATH . "/")) {
error_log("could not write to export tmp directory!");
} }
$tz = date("e"); // use php timezone (maybe set up in php.ini, date.timezone) // Get records
$records = array();
if($this->DEBUG) { if (isset($actionData["records"])) {
error_log("PHP Timezone: " . $tz); $records = $actionData["records"];
} }
$config = array( // Get folders
"language" => substr($GLOBALS["settings"]->get("zarafa/v1/main/language"),0,2), $folder = false;
"directory" => TMP_PATH . "/", if (isset($actionData["folder"])) {
"filename" => $tmpname . "." . $secid, $folder = $actionData["folder"];
"unique_id" => "zarafa-export-plugin",
"TZID" => $tz
);
$v = new vcalendar($config);
$v->setProperty("method", "PUBLISH"); // required of some calendar software
$v->setProperty("x-wr-calname", $actionData["calendar"]); // required of some calendar software
$v->setProperty("X-WR-CALDESC", "Exported Zarafa Calendar"); // required of some calendar software
$v->setProperty("X-WR-TIMEZONE", $tz);
$xprops = array("X-LIC-LOCATION" => $tz); // required of some calendar software
iCalUtilityFunctions::createTimezone($v, $tz, $xprops); // create timezone object in calendar
foreach($actionData["data"] as $event) {
$event["props"]["description"] = $this->loadEventDescription($event);
$event["props"]["attendees"] = $this->loadAttendees($event);
$vevent = & $v->newComponent("vevent"); // create a new event object
$this->addEvent($vevent, $event["props"]);
} }
$v->saveCalendar(); $response = array();
$error = false;
$error_msg = "";
// write csv
$token = $this->randomstring(16);
$file = PLUGIN_CALENDARIMPORTER_TMP_UPLOAD . "ics_" . $token . ".ics";
file_put_contents($file, "");
$store = $GLOBALS["mapisession"]->openMessageStore(hex2bin($storeid));
if ($store) {
// load folder first
if ($folder !== false) {
$mapifolder = mapi_msgstore_openentry($store, hex2bin($folder));
$table = mapi_folder_getcontentstable($mapifolder);
$list = mapi_table_queryallrows($table, array(PR_ENTRYID));
foreach ($list as $item) {
$records[] = bin2hex($item[PR_ENTRYID]);
}
}
$vcalendar = new VObject\Component\VCalendar();
// Add static stuff to vcalendar
$vcalendar->add('METHOD', 'PUBLISH');
$vcalendar->add('X-WR-CALDESC', 'Exported Zarafa Calendar');
$vcalendar->add('X-WR-TIMEZONE', date_default_timezone_get());
// TODO: add VTIMEZONE object to ical.
for ($index = 0, $count = count($records); $index < $count; $index++) {
$message = mapi_msgstore_openentry($store, hex2bin($records[$index]));
// get message properties.
$properties = $GLOBALS['properties']->getAppointmentProperties();
$plaintext = true;
$messageProps = $GLOBALS['operations']->getMessageProps($store, $message, $properties, $plaintext);
$vevent = $vcalendar->add('VEVENT', [
'SUMMARY' => $this->getProp($messageProps, "subject"),
'DTSTART' => date_timestamp_set(new DateTime(), $this->getProp($messageProps, "startdate")),
'DTEND' => date_timestamp_set(new DateTime(), $this->getProp($messageProps, "duedate")),
'CREATED' => date_timestamp_set(new DateTime(), $this->getProp($messageProps, "creation_time")),
'LAST-MODIFIED' => date_timestamp_set(new DateTime(), $this->getProp($messageProps, "last_modification_time")),
'PRIORITY' => $this->getProp($messageProps, "importance"),
'X-MICROSOFT-CDO-INTENDEDSTATUS' => $this->busystates[intval($this->getProp($messageProps, "busystatus"))], // both seem to be valid...
'X-MICROSOFT-CDO-BUSYSTATUS' => $this->busystates[intval($this->getProp($messageProps, "busystatus"))], // both seem to be valid...
'X-ZARAFA-LABEL' => $this->labels[intval($this->getProp($messageProps, "label"))],
'CLASS' => $this->getProp($messageProps, "private") ? "PRIVATE" : "PUBLIC",
'COMMENT' => "eid:" . $records[$index]
]);
// Add organizer
$vevent->add('ORGANIZER','mailto:' . $this->getProp($messageProps, "sender_email_address"));
$vevent->ORGANIZER['CN'] = $this->getProp($messageProps, "sender_name");
// Add Attendees
if(isset($messageProps["recipients"]) && count($messageProps["recipients"]["item"]) > 0) {
foreach($messageProps["recipients"]["item"] as $attendee) {
$att = $vevent->add('ATTENDEE', "mailto:" . $this->getProp($attendee, "email_address"));
$att["CN"] = $this->getProp($attendee, "display_name");
$att["ROLE"] = $this->attendeetype[intval($this->getProp($attendee, "recipient_type"))];
}
}
// Add alarms
if(!empty($this->getProp($messageProps, "reminder")) && $this->getProp($messageProps, "reminder") == 1) {
$valarm = $vevent->add('VALARM', [
'ACTION' => 'DISPLAY',
'DESCRIPTION' => $this->getProp($messageProps, "subject") // reuse the event summary
]);
// Add trigger
$durationValue = $this->getDurationStringFromMintues($this->getProp($messageProps, "reminder_minutes"), false);
$valarm->add('TRIGGER', $durationValue); // default trigger type is duration (see 4.8.6.3)
/*
$valarm->add('TRIGGER', date_timestamp_set(new DateTime(), $this->getProp($messageProps, "reminder_time"))); // trigger type "DATE-TIME"
$valarm->TRIGGER['VALUE'] = 'DATE-TIME';
*/
}
// Add location
if(!empty($this->getProp($messageProps, "location"))) {
$vevent->add('LOCATION',$this->getProp($messageProps, "location"));
}
// Add description
$body = $this->getProp($messageProps, "isHTML") ? $this->getProp($messageProps, "html_body") : $this->getProp($messageProps, "body");
if(!empty($body)) {
$vevent->add('DESCRIPTION',$body);
}
}
// write combined ics file
file_put_contents($file, file_get_contents($file) . $vcalendar->serialize());
}
if (count($records) > 0) {
$response['status'] = true; $response['status'] = true;
$response['fileid'] = $tmpname; // number of entries that will be exported $response['download_token'] = $token;
$response['basedir'] = TMP_PATH; $response['filename'] = count($records) . "events.ics";
$response['secid'] = $secid; } else {
$response['realname'] = $actionData["calendar"]; $response['status'] = false;
$response['message'] = "No events found. Export skipped!";
}
$this->addActionData($actionType, $response); $this->addActionData($actionType, $response);
$GLOBALS["bus"]->addData($this->getResponseData()); $GLOBALS["bus"]->addData($this->getResponseData());
if($this->DEBUG) {
error_log("export done, bus data written!");
}
} }
/** /**
@ -395,40 +352,111 @@ class CalendarModule extends Module {
* @param $actionType * @param $actionType
* @param $actionData * @param $actionData
*/ */
private function importCalendar($actionType, $actionData) { private function importCalendar($actionType, $actionData)
if($this->DEBUG) { {
error_log("PHP Timezone: " . $tz); // Get uploaded vcf path
$icsfile = false;
if (isset($actionData["ics_filepath"])) {
$icsfile = $actionData["ics_filepath"];
} }
if(is_readable ($actionData["ics_filepath"])) { // Get store id
$ical = new ICal($actionData["ics_filepath"], $GLOBALS["settings"]->get("zarafa/v1/plugins/calendarimporter/default_timezone"), $actionData["timezone"], $actionData["ignore_dst"]); // Parse it! $storeid = false;
if (isset($actionData["storeid"])) {
$storeid = $actionData["storeid"];
}
if(isset($ical->errors)) { // Get folder entryid
$response['status'] = false; $folderid = false;
$response['message']= $ical->errors; if (isset($actionData["folderid"])) {
} else if(!$ical->hasEvents()) { $folderid = $actionData["folderid"];
$response['status'] = false; }
$response['message']= "No events in ics file";
} else { // Get uids
$response['status'] = true; $uids = array();
$response['parsed_file']= $actionData["ics_filepath"]; if (isset($actionData["uids"])) {
$response['parsed'] = array ( $uids = $actionData["uids"];
'timezone' => $ical->timezone(), }
'calendar' => $ical->calendar(),
'events' => $ical->events() $response = array();
$error = false;
$error_msg = "";
// parse the ics file a last time...
$parser = null;
try {
$parser = VObject\Reader::read(
fopen($icsfile,'r')
); );
} catch (Exception $e) {
$error = true;
$error_msg = $e->getMessage();
} }
$events = array();
if (count($parser->VEVENT) > 0) {
$events = $this->parseCalendarToArray($parser);
$store = $GLOBALS["mapisession"]->openMessageStore(hex2bin($storeid));
$folder = mapi_msgstore_openentry($store, hex2bin($folderid));
$importall = false;
if (count($uids) == count($events)) {
$importall = true;
}
$propValuesMAPI = array();
$properties = $GLOBALS['properties']->getAppointmentProperties();
// extend properties...
$properties["body"] = PR_BODY;
$count = 0;
// iterate through all events and import them :)
foreach ($events as $event) {
if (isset($event["startdate"]) && ($importall || in_array($event["internal_fields"]["event_uid"], $uids))) {
$message = mapi_folder_createmessage($folder);
// parse the arraykeys
foreach ($event as $key => $value) {
if ($key !== "internal_fields") {
if(isset($properties[$key])) {
$propValuesMAPI[$properties[$key]] = $value;
}
}
}
$propValuesMAPI[$properties["commonstart"]] = $propValuesMAPI[$properties["startdate"]];
$propValuesMAPI[$properties["commonend"]] = $propValuesMAPI[$properties["duedate"]];
$propValuesMAPI[$properties["duration"]] = ($propValuesMAPI[$properties["duedate"]] - $propValuesMAPI[$properties["startdate"]]) / 60; // Minutes needed
$propValuesMAPI[$properties["reminder"]] = false; // needed, overwritten if there is a timer
$propValuesMAPI[$properties["message_class"]] = "IPM.Appointment";
$propValuesMAPI[$properties["icon_index"]] = "1024";
// TODO: set attendees and alarms
mapi_setprops($message, $propValuesMAPI);
mapi_savechanges($message);
if ($this->DEBUG) {
error_log("New event added: \"" . $event["subject"] . "\".\n");
}
$count++;
}
}
$response['status'] = true;
$response['count'] = $count;
$response['message'] = "";
} else { } else {
$response['status'] = false; $response['status'] = false;
$response['message']= "File could not be read by server"; $response['count'] = 0;
$response['message'] = $error ? $error_msg : "ICS file empty!";
} }
$this->addActionData($actionType, $response); $this->addActionData($actionType, $response);
$GLOBALS["bus"]->addData($this->getResponseData()); $GLOBALS["bus"]->addData($this->getResponseData());
if($this->DEBUG) {
error_log("parsing done, bus data written!");
}
} }
/** /**
@ -437,16 +465,17 @@ class CalendarModule extends Module {
* @param $actionData * @param $actionData
* @private * @private
*/ */
private function getAttachmentPath($actionType, $actionData) { private function getAttachmentPath($actionType, $actionData)
{
// Get store id // Get store id
$storeid = false; $storeid = false;
if(isset($actionData["store"])) { if (isset($actionData["store"])) {
$storeid = $actionData["store"]; $storeid = $actionData["store"];
} }
// Get message entryid // Get message entryid
$entryid = false; $entryid = false;
if(isset($actionData["entryid"])) { if (isset($actionData["entryid"])) {
$entryid = $actionData["entryid"]; $entryid = $actionData["entryid"];
} }
@ -455,41 +484,40 @@ class CalendarModule extends Module {
// Get number of attachment which should be opened. // Get number of attachment which should be opened.
$attachNum = false; $attachNum = false;
if(isset($actionData["attachNum"])) { if (isset($actionData["attachNum"])) {
$attachNum = $actionData["attachNum"]; $attachNum = $actionData["attachNum"];
} }
// Check if storeid and entryid isset // Check if storeid and entryid isset
if($storeid && $entryid) { if ($storeid && $entryid) {
// Open the store // Open the store
$store = $GLOBALS["mapisession"]->openMessageStore(hex2bin($storeid)); $store = $GLOBALS["mapisession"]->openMessageStore(hex2bin($storeid));
if($store) { if ($store) {
// Open the message // Open the message
$message = mapi_msgstore_openentry($store, hex2bin($entryid)); $message = mapi_msgstore_openentry($store, hex2bin($entryid));
if($message) { if ($message) {
$attachment = false; $attachment = false;
// Check if attachNum isset // Check if attachNum isset
if($attachNum) { if ($attachNum) {
// Loop through the attachNums, message in message in message ... // Loop through the attachNums, message in message in message ...
for($i = 0; $i < (count($attachNum) - 1); $i++) for ($i = 0; $i < (count($attachNum) - 1); $i++) {
{
// Open the attachment // Open the attachment
$tempattach = mapi_message_openattach($message, (int) $attachNum[$i]); $tempattach = mapi_message_openattach($message, (int)$attachNum[$i]);
if($tempattach) { if ($tempattach) {
// Open the object in the attachment // Open the object in the attachment
$message = mapi_attach_openobj($tempattach); $message = mapi_attach_openobj($tempattach);
} }
} }
// Open the attachment // Open the attachment
$attachment = mapi_message_openattach($message, (int) $attachNum[(count($attachNum) - 1)]); $attachment = mapi_message_openattach($message, (int)$attachNum[(count($attachNum) - 1)]);
} }
// Check if the attachment is opened // Check if the attachment is opened
if($attachment) { if ($attachment) {
// Get the props of the attachment // Get the props of the attachment
$props = mapi_attach_getprops($attachment, array(PR_ATTACH_LONG_FILENAME, PR_ATTACH_MIME_TAG, PR_DISPLAY_NAME, PR_ATTACH_METHOD)); $props = mapi_attach_getprops($attachment, array(PR_ATTACH_LONG_FILENAME, PR_ATTACH_MIME_TAG, PR_DISPLAY_NAME, PR_ATTACH_METHOD));
@ -499,29 +527,33 @@ class CalendarModule extends Module {
$filename = "ERROR"; $filename = "ERROR";
// Set filename // Set filename
if(isset($props[PR_ATTACH_LONG_FILENAME])) { if (isset($props[PR_ATTACH_LONG_FILENAME])) {
$filename = $props[PR_ATTACH_LONG_FILENAME]; $filename = $props[PR_ATTACH_LONG_FILENAME];
} else if(isset($props[PR_ATTACH_FILENAME])) { } else {
if (isset($props[PR_ATTACH_FILENAME])) {
$filename = $props[PR_ATTACH_FILENAME]; $filename = $props[PR_ATTACH_FILENAME];
} else if(isset($props[PR_DISPLAY_NAME])) { } else {
if (isset($props[PR_DISPLAY_NAME])) {
$filename = $props[PR_DISPLAY_NAME]; $filename = $props[PR_DISPLAY_NAME];
} }
}
}
// Set content type // Set content type
if(isset($props[PR_ATTACH_MIME_TAG])) { if (isset($props[PR_ATTACH_MIME_TAG])) {
$contentType = $props[PR_ATTACH_MIME_TAG]; $contentType = $props[PR_ATTACH_MIME_TAG];
} else { } else {
// Parse the extension of the filename to get the content type // Parse the extension of the filename to get the content type
if(strrpos($filename, ".") !== false) { if (strrpos($filename, ".") !== false) {
$extension = strtolower(substr($filename, strrpos($filename, "."))); $extension = strtolower(substr($filename, strrpos($filename, ".")));
$contentType = "application/octet-stream"; $contentType = "application/octet-stream";
if (is_readable("mimetypes.dat")){ if (is_readable("mimetypes.dat")) {
$fh = fopen("mimetypes.dat","r"); $fh = fopen("mimetypes.dat", "r");
$ext_found = false; $ext_found = false;
while (!feof($fh) && !$ext_found){ while (!feof($fh) && !$ext_found) {
$line = fgets($fh); $line = fgets($fh);
preg_match("/(\.[a-z0-9]+)[ \t]+([^ \t\n\r]*)/i", $line, $result); preg_match("/(\.[a-z0-9]+)[ \t]+([^ \t\n\r]*)/i", $line, $result);
if ($extension == $result[1]){ if ($extension == $result[1]) {
$ext_found = true; $ext_found = true;
$contentType = $result[2]; $contentType = $result[2];
} }
@ -539,12 +571,12 @@ class CalendarModule extends Module {
$stat = mapi_stream_stat($stream); $stat = mapi_stream_stat($stream);
// File length = $stat["cb"] // File length = $stat["cb"]
$fhandle = fopen($tmpname,'w'); $fhandle = fopen($tmpname, 'w');
$buffer = null; $buffer = null;
for($i = 0; $i < $stat["cb"]; $i += BLOCK_SIZE) { for ($i = 0; $i < $stat["cb"]; $i += BLOCK_SIZE) {
// Write stream // Write stream
$buffer = mapi_stream_read($stream, BLOCK_SIZE); $buffer = mapi_stream_read($stream, BLOCK_SIZE);
fwrite($fhandle,$buffer,strlen($buffer)); fwrite($fhandle, $buffer, strlen($buffer));
} }
fclose($fhandle); fclose($fhandle);
@ -569,6 +601,129 @@ class CalendarModule extends Module {
$GLOBALS["bus"]->addData($this->getResponseData()); $GLOBALS["bus"]->addData($this->getResponseData());
} }
} }
};
/**
* Function that parses the uploaded ics file and posts it via json
* @param $actionType
* @param $actionData
*/
private function loadCalendar($actionType, $actionData)
{
$error = false;
$error_msg = "";
if (is_readable($actionData["ics_filepath"])) {
$parser = null;
try {
$parser = VObject\Reader::read(
fopen($actionData["ics_filepath"],'r')
);
//error_log(print_r($parser->VTIMEZONE, true));
} catch (Exception $e) {
$error = true;
$error_msg = $e->getMessage();
}
if ($error) {
$response['status'] = false;
$response['message'] = $error_msg;
} else {
if (count($parser->VEVENT) == 0) {
$response['status'] = false;
$response['message'] = "No event in ics file";
} else {
$response['status'] = true;
$response['parsed_file'] = $actionData["ics_filepath"];
$response['parsed'] = array(
'events' => $this->parseCalendarToArray($parser),
'timezone' => isset($parser->VTIMEZONE->TZID) ? (string)$parser->VTIMEZONE->TZID : (string)$parser->{'X-WR-TIMEZONE'},
'calendar' => (string)$parser->PRODID
);
}
}
} else {
$response['status'] = false;
$response['message'] = "File could not be read by server";
}
$this->addActionData($actionType, $response);
$GLOBALS["bus"]->addData($this->getResponseData());
if ($this->DEBUG) {
error_log("parsing done, bus data written!");
}
}
/**
* Create a array with contacts
*
* @param {VObject} $calendar ics parser object
* @return array parsed events
* @private
*/
private function parseCalendarToArray($calendar)
{
$events = array();
foreach ($calendar->VEVENT as $Index => $vEvent) {
// Sabre\VObject\Parser\XML\Element\VEvent
$properties = array();
//uid - used for front/backend communication
$properties["internal_fields"] = array();
$properties["internal_fields"]["event_uid"] = base64_encode($Index . $vEvent->UID);
$properties["startdate"] = (string)$vEvent->DTSTART->getDateTime()->getTimestamp();
$properties["duedate"] = (string)$vEvent->DTEND->getDateTime()->getTimestamp();
$properties["location"] = (string)$vEvent->LOCATION;
$properties["subject"] = (string)$vEvent->SUMMARY;
$properties["body"] = (string)$vEvent->DESCRIPTION;
$properties["comment"] = (string)$vEvent->COMMENT;
$properties["timezone"] = (string)$vEvent->DTSTART["TZID"];
$properties["organizer"] = (string)$vEvent->ORGANIZER;
$properties["busystatus"] = array_search((string)$vEvent->{'X-MICROSOFT-CDO-INTENDEDSTATUS'}, $this->busystates); // X-MICROSOFT-CDO-BUSYSTATUS
$properties["transp"] = (string)$vEvent->TRANSP;
//$properties["trigger"] = (string)$vEvent->COMMENT;
$properties["priority"] = (string)$vEvent->PRIORITY;
$properties["private"] = ((string)$vEvent->CLASS) == "PRIVATE" ? true : false;
if(!empty((string)$vEvent->{'X-ZARAFA-LABEL'})) {
$properties["label"] = array_search((string)$vEvent->{'X-ZARAFA-LABEL'}, $this->labels);
}
$properties["last_modification_time"] = (string)$vEvent->{'LAST-MODIFIED'}->getDateTime()->getTimestamp();
$properties["creation_time"] = (string)$vEvent->CREATED->getDateTime()->getTimestamp();
$properties["rrule"] = (string)$vEvent->RRULE;
// Attendees
$properties["attendees"] = array();
if(isset($vEvent->ATTENDEE) && count($vEvent->ATTENDEE) > 0) {
foreach($vEvent->ATTENDEE as $attendee) {
$properties["attendees"][] = array(
"name" => (string)$attendee["CN"],
"mail" => (string)$attendee,
"status" => (string)$attendee["PARTSTAT"],
"role" => (string)$attendee["ROLE"]
);
}
}
// Alarms
$properties["alarms"] = array();
if(isset($vEvent->VALARM) && count($vEvent->VALARM) > 0) {
foreach($vEvent->VALARM as $alarm) {
$properties["alarms"][] = array(
"description" => (string)$alarm->DESCRIPTION,
"trigger" => (string)$alarm->TRIGGER,
"type" => (string)$alarm->TRIGGER["VALUE"]
);
}
}
array_push($events, $properties);
}
return $events;
}
}
;
?> ?>

View File

@ -19,25 +19,31 @@
* License along with this library; if not, write to the Free Software * License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* *
* */
require_once __DIR__ . "/download.php";
/**
* calendarimporter Plugin * calendarimporter Plugin
* *
* With this plugin you can import a ics file to your zarafa calendar * With this plugin you can import a ics file to your zarafa calendar
* *
*/ */
class Plugincalendarimporter extends Plugin { class Plugincalendarimporter extends Plugin
{
/** /**
* Constructor * Constructor
*/ */
function Plugincalendarimporter() {} function __construct() {}
/** /**
* Function initializes the Plugin and registers all hooks * Function initializes the Plugin and registers all hooks
* *
* @return void * @return void
*/ */
function init() { function init()
{
$this->registerHook('server.core.settings.init.before'); $this->registerHook('server.core.settings.init.before');
$this->registerHook('server.index.load.custom');
} }
/** /**
@ -47,11 +53,17 @@ class Plugincalendarimporter extends Plugin {
* @param mixed $data object(s) related to the hook * @param mixed $data object(s) related to the hook
* @return void * @return void
*/ */
function execute($eventID, &$data) { function execute($eventID, &$data)
switch($eventID) { {
switch ($eventID) {
case 'server.core.settings.init.before' : case 'server.core.settings.init.before' :
$this->injectPluginSettings($data); $this->injectPluginSettings($data);
break; break;
case 'server.index.load.custom':
if ($data['name'] == 'download_ics') {
calendarimporter\DownloadHandler::doDownload();
}
break;
} }
} }
@ -60,14 +72,14 @@ class Plugincalendarimporter extends Plugin {
* settings. * settings.
* @param Array $data Reference to the data of the triggered hook * @param Array $data Reference to the data of the triggered hook
*/ */
function injectPluginSettings(&$data) { function injectPluginSettings(&$data)
{
$data['settingsObj']->addSysAdminDefaults(Array( $data['settingsObj']->addSysAdminDefaults(Array(
'zarafa' => Array( 'zarafa' => Array(
'v1' => Array( 'v1' => Array(
'plugins' => Array( 'plugins' => Array(
'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_sync' => PLUGIN_CALENDARIMPORTER_USER_DEFAULT_ENABLE_SYNC, 'enable_sync' => PLUGIN_CALENDARIMPORTER_USER_DEFAULT_ENABLE_SYNC,
'default_calendar' => PLUGIN_CALENDARIMPORTER_DEFAULT, 'default_calendar' => PLUGIN_CALENDARIMPORTER_DEFAULT,
'default_timezone' => PLUGIN_CALENDARIMPORTER_DEFAULT_TIMEZONE 'default_timezone' => PLUGIN_CALENDARIMPORTER_DEFAULT_TIMEZONE
@ -78,4 +90,5 @@ class Plugincalendarimporter extends Plugin {
)); ));
} }
} }
?> ?>

View File

@ -2,5 +2,29 @@
background: url(../images/import_icon.png) no-repeat !important; background: url(../images/import_icon.png) no-repeat !important;
background-repeat: no-repeat; background-repeat: no-repeat;
background-position: center; background-position: center;
background-size: 18px!important; }
.icon_calendarimporter_export {
background: url(../images/download.png) no-repeat;
background-repeat: no-repeat;
background-position: center;
}
.icon_calendarimporter_import {
background: url(../images/upload.png) no-repeat;
background-repeat: no-repeat;
background-position: center;
}
.zarafa-caiplg-container {
width: 100%;
height: 50px;
}
.zarafa-caiplg-button .x-btn-small {
width: 80%;
height: 30px;
margin-left: 10%;
margin-right: 10%;
margin-top: 10px;
} }

BIN
resources/images/download.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 224 B

BIN
resources/images/download.xcf Executable file

Binary file not shown.

BIN
resources/images/upload.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 B

BIN
resources/images/upload.xcf Executable file

Binary file not shown.