Merge branch 'v2' into 'master'
V2 Merge v2 to master. Final 2.0.0 See merge request !1
This commit is contained in:
commit
c2f26697fb
18
.idea/contactimporter.iml
Normal file
18
.idea/contactimporter.iml
Normal file
@ -0,0 +1,18 @@
|
||||
<?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" />
|
||||
<orderEntry type="module-library">
|
||||
<library name="PHARS">
|
||||
<CLASSES>
|
||||
<root url="phar://$MODULE_DIR$/php/composer.phar" />
|
||||
</CLASSES>
|
||||
<SOURCES>
|
||||
<root url="phar://$MODULE_DIR$/php/composer.phar" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
</orderEntry>
|
||||
</component>
|
||||
</module>
|
6
.idea/vcs.xml
Normal file
6
.idea/vcs.xml
Normal 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>
|
148
build.xml
148
build.xml
@ -1,18 +1,15 @@
|
||||
<project default="all">
|
||||
<!--############# CONFIGURE ALL PROPERTIES FOR THE REPLACER HERE ################-->
|
||||
<property name="plugin_version" value="1.0.3"/>
|
||||
<!-- EOC -->
|
||||
|
||||
<property name="root-folder" value="${basedir}/../"/>
|
||||
<property name="tools-folder" value="${root-folder}/TOOLS/"/>
|
||||
<property name="target-folder" value="${root-folder}/DEPLOY/plugins"/>
|
||||
<property environment="env"/>
|
||||
<property name="root-folder" value="${basedir}/../../"/>
|
||||
<property name="tools-folder" value="${root-folder}/tools/"/>
|
||||
<property name="target-folder" value="${root-folder}/deploy/plugins"/>
|
||||
<property name="server-folder" value="${root-folder}/server"/>
|
||||
|
||||
<import file="${tools-folder}/antutil.xml"/>
|
||||
|
||||
<typedef file="${tools-folder}/antlib.xml">
|
||||
<classpath>
|
||||
<pathelement location="${tools-folder}/tools.jar"/>
|
||||
<pathelement location="${tools-folder}/lib/compiler.jar"/>
|
||||
</classpath>
|
||||
</typedef>
|
||||
|
||||
@ -22,29 +19,7 @@
|
||||
</classpath>
|
||||
</taskdef>
|
||||
|
||||
<!-- os checks for xmllint... -->
|
||||
<condition property="isWindows" value="true">
|
||||
<os family="windows" />
|
||||
</condition>
|
||||
|
||||
<!-- define nicknames for libraries -->
|
||||
<property name="yui-compressor" location="${tools-folder}/lib/yuicompressor-2.4.2.jar" />
|
||||
<property name="yui-compressor-ant-task" location="${tools-folder}/lib/yui-compressor-ant-task-0.5.jar" />
|
||||
|
||||
<!-- adds libraries to the classpath -->
|
||||
<path id="yui.classpath">
|
||||
<pathelement location="${yui-compressor}" />
|
||||
<pathelement location="${yui-compressor-ant-task}" />
|
||||
</path>
|
||||
|
||||
<!-- define tasks -->
|
||||
<taskdef name="yui-compressor" classname="net.noha.tools.ant.yuicompressor.tasks.YuiCompressorTask">
|
||||
<classpath refid="yui.classpath" />
|
||||
</taskdef>
|
||||
|
||||
|
||||
<!-- Determine plugin name -->
|
||||
<var name="plugin" unset="true"/>
|
||||
<basename file="${basedir}" property="plugin"/>
|
||||
|
||||
<!-- The Plugin distribution files -->
|
||||
@ -54,8 +29,7 @@
|
||||
|
||||
<!-- The Plugin CSS files -->
|
||||
<property name="plugin-css-folder" value="resources/css"/>
|
||||
<property name="plugin-css-file" value="${plugin}-min.css"/>
|
||||
<property name="plugin-css-debug-file" value="${plugin}.css"/>
|
||||
<property name="plugin-css-file" value="${plugin}.css"/>
|
||||
|
||||
<!-- Meta target -->
|
||||
<target name="all" depends="concat, compress"/>
|
||||
@ -68,16 +42,6 @@
|
||||
<include name="${plugin-file}"/>
|
||||
<include name="${plugin-debugfile}"/>
|
||||
</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>
|
||||
</target>
|
||||
|
||||
@ -89,19 +53,11 @@
|
||||
<then>
|
||||
<mkdir dir="${target-folder}/${plugin-folder}/js"/>
|
||||
<echo message="Concatenating: ${plugin-debugfile}"/>
|
||||
<!-- TODO: fix JS files for zConcat -->
|
||||
<!--zConcat outputFolder="${target-folder}/${plugin-folder}/js" outputFile="${plugin-debugfile}" prioritize="\w+">
|
||||
<zConcat outputFolder="${target-folder}/${plugin-folder}/js" outputFile="${plugin-debugfile}" prioritize="\w+">
|
||||
<concatfiles>
|
||||
<fileset dir="js" includes="**/*.js" />
|
||||
</concatfiles>
|
||||
</zConcat-->
|
||||
<concat destfile="${target-folder}/${plugin-folder}/js/${plugin-debugfile}">
|
||||
<fileset file="js/ABOUT.js" />
|
||||
<fileset file="js/plugin.contactimporter.js" />
|
||||
<fileset file="js/data/ResponseHandler.js" />
|
||||
<fileset file="js/dialogs/ImportContentPanel.js" />
|
||||
<fileset file="js/dialogs/ImportPanel.js" />
|
||||
</concat>
|
||||
</zConcat>
|
||||
</then>
|
||||
</if>
|
||||
|
||||
@ -110,8 +66,8 @@
|
||||
<available file="${plugin-css-folder}" type="dir" />
|
||||
<then>
|
||||
<mkdir dir="${target-folder}/${plugin-folder}/${plugin-css-folder}"/>
|
||||
<echo message="Concatenating: ${plugin-css-debug-file}"/>
|
||||
<zConcat outputFolder="${target-folder}/${plugin-folder}/${plugin-css-folder}" outputFile="${plugin-css-debug-file}">
|
||||
<echo message="Concatenating: ${plugin-css-file}"/>
|
||||
<zConcat outputFolder="${target-folder}/${plugin-folder}/${plugin-css-folder}" outputFile="${plugin-css-file}">
|
||||
<concatfiles>
|
||||
<fileset dir="${plugin-css-folder}" includes="**/*.css" />
|
||||
</concatfiles>
|
||||
@ -156,21 +112,15 @@
|
||||
var pgettext = function(msgctxt, msgid) {};
|
||||
</externs>
|
||||
</zCompile>
|
||||
<!--yui-compressor
|
||||
warn="false"
|
||||
munge="true"
|
||||
preserveallsemicolons="false"
|
||||
fromdir="${target-folder}/${plugin-folder}/js"
|
||||
todir="${target-folder}/${plugin-folder}/js">
|
||||
|
||||
<include name="${plugin-debugfile}" />
|
||||
</yui-compressor-->
|
||||
</then>
|
||||
</if>
|
||||
</target>
|
||||
|
||||
<!-- syntax check all PHP files -->
|
||||
<target name="validate">
|
||||
<if>
|
||||
<available file="php" filepath="${env.PATH}" />
|
||||
<then>
|
||||
<if>
|
||||
<available file="config.php" type="file" />
|
||||
<then>
|
||||
@ -185,92 +135,66 @@
|
||||
<foreach target="syntax-check" param="file">
|
||||
<path>
|
||||
<fileset dir=".">
|
||||
<exclude name="php/vendor/**" />
|
||||
<include name="**/*.php"/>
|
||||
</fileset>
|
||||
</path>
|
||||
</foreach>
|
||||
</then>
|
||||
</if>
|
||||
</then>
|
||||
<else>
|
||||
<echo message="WARNING: PHP not available, not performing syntax-check on php files"/>
|
||||
</else>
|
||||
</if>
|
||||
</target>
|
||||
|
||||
<target name="syntax-check">
|
||||
<echo message="validating ${file}"/>
|
||||
<exec executable="php" failonerror="true" failifexecutionfails="false">
|
||||
<exec executable="php" failonerror="true">
|
||||
<arg value="-l"/>
|
||||
<arg value="${file}"/>
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
<!-- on windows we do not check the xml file -->
|
||||
<target name="xml-os-sel" depends="xml-check,xml-copy">
|
||||
<echo>Processing manifest.xml</echo>
|
||||
</target>
|
||||
<!-- Install all files into the target folder -->
|
||||
<target name="deploy" depends="compress, validate">
|
||||
<mkdir dir="${target-folder}/${plugin-folder}"/>
|
||||
|
||||
<!-- check manifest.xml if we are on windows... -->
|
||||
<target name="xml-check" unless="isWindows">
|
||||
<echo message="Checking xml: manifest.xml" />
|
||||
<!-- Copy (and validate) manifest.xml -->
|
||||
<exec executable="xmllint" output="${target-folder}/${plugin-folder}/manifest.xml" failonerror="true" error="/dev/stdout" failifexecutionfails="false">
|
||||
<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="--path"/>
|
||||
<arg value="${root-folder}/server"/>
|
||||
<arg value="${server-folder}"/>
|
||||
<arg value="manifest.xml"/>
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
<!-- check manifest.xml if we are on windows... -->
|
||||
<target name="xml-copy" if="isWindows">
|
||||
<echo message="Copying xml: manifest.xml" />
|
||||
<!-- Copy manifest.xml -->
|
||||
</then>
|
||||
<else>
|
||||
<echo message="WARNING: xmllint not available, not performing syntax-check on manifest.xml"/>
|
||||
<!-- xmllint is not available, so we must copy the file manually -->
|
||||
<copy todir="${target-folder}/${plugin-folder}">
|
||||
<fileset dir=".">
|
||||
<include name="manifest.xml"/>
|
||||
</fileset>
|
||||
</copy>
|
||||
</target>
|
||||
|
||||
<!-- Install all files into the target folder -->
|
||||
<target name="deploy" depends="clean, compress, compresscss, validate, xml-os-sel">
|
||||
<mkdir dir="${target-folder}/${plugin-folder}"/>
|
||||
</else>
|
||||
</if>
|
||||
|
||||
<!-- copy files -->
|
||||
<copy todir="${target-folder}/${plugin-folder}">
|
||||
<fileset dir=".">
|
||||
<include name="resources/**/*"/>
|
||||
<include name="resources/**/*.*"/>
|
||||
<include name="external/**/*.*"/>
|
||||
<include name="php/**/*.php"/>
|
||||
<include name="config.php"/>
|
||||
<include name="changelog.txt"/>
|
||||
<!-- exclude the ant script -->
|
||||
<exclude name="build.xml"/>
|
||||
<!-- CSS is generated during build -->
|
||||
<exclude name="resources/css/*.*"/>
|
||||
</fileset>
|
||||
</copy>
|
||||
|
||||
<!-- replace all variables... -->
|
||||
<replace file="${target-folder}/${plugin-folder}/manifest.xml" token="@_@PLUGIN_VERSION@_@" value="${plugin_version}" />
|
||||
</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>
|
@ -1,11 +1,9 @@
|
||||
<?php
|
||||
/** Disable the import plugin for all clients */
|
||||
define('PLUGIN_CONTACTIMPORTER_USER_DEFAULT_ENABLE', false);
|
||||
/** Disable the export feature for all clients */
|
||||
define('PLUGIN_CONTACTIMPORTER_USER_DEFAULT_ENABLE_EXPORT', false); // currently not available
|
||||
|
||||
/** The default addressbook to import to (default: contact)*/
|
||||
define('PLUGIN_CONTACTIMPORTER_DEFAULT', "contact");
|
||||
/** The default addressbook to import to (default: Kontakte or Contacts - depending on your language)*/
|
||||
define('PLUGIN_CONTACTIMPORTER_DEFAULT', "Kontakte");
|
||||
|
||||
/** Tempory path for uploaded files... */
|
||||
define('PLUGIN_CONTACTIMPORTER_TMP_UPLOAD', "/var/lib/zarafa-webapp/tmp/");
|
||||
|
@ -2,7 +2,7 @@
|
||||
* ABOUT.js zarafa contact to vcf im/exporter
|
||||
*
|
||||
* 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
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
@ -29,7 +29,7 @@ Ext.namespace('Zarafa.plugins.contactimporter');
|
||||
* The copyright string holding the copyright notice for the Zarafa contactimporter Plugin.
|
||||
*/
|
||||
Zarafa.plugins.contactimporter.ABOUT = ""
|
||||
+ "<p>Copyright (C) 2012-2013 Christoph Haas <christoph.h@sprinternet.at></p>"
|
||||
+ "<p>Copyright (C) 2012-2016 Christoph Haas <christoph.h@sprinternet.at></p>"
|
||||
|
||||
+ "<p>This program is free software; you can redistribute it and/or "
|
||||
+ "modify it under the terms of the GNU Lesser General Public "
|
||||
@ -51,7 +51,7 @@ Zarafa.plugins.contactimporter.ABOUT = ""
|
||||
|
||||
+ "<h1>vCard-parser</h1>"
|
||||
|
||||
+ "<p>Copyright (C) 2012 Nuovo</p>"
|
||||
+ "<p>Copyright (C) 2016 Jeroen Desloovere</p>"
|
||||
|
||||
+ "<p>Licensed under the MIT License.</p>"
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
* ResponseHandler.js zarafa contact im/exporter
|
||||
*
|
||||
* 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
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
@ -56,6 +56,14 @@ Zarafa.plugins.contactimporter.data.ResponseHandler = Ext.extend(Zarafa.core.dat
|
||||
this.successCallback(response);
|
||||
},
|
||||
|
||||
/**
|
||||
* Call the successCallback callback function.
|
||||
* @param {Object} response Object contained the response data.
|
||||
*/
|
||||
doExport: function (response) {
|
||||
this.successCallback(response);
|
||||
},
|
||||
|
||||
/**
|
||||
* Call the successCallback callback function.
|
||||
* @param {Object} response Object contained the response data.
|
||||
|
@ -2,7 +2,7 @@
|
||||
* ImportContentPanel.js zarafa contact to vcf im/exporter
|
||||
*
|
||||
* 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
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
@ -43,9 +43,6 @@ Zarafa.plugins.contactimporter.dialogs.ImportContentPanel = Ext.extend(Zarafa.co
|
||||
constructor: function (config) {
|
||||
config = config || {};
|
||||
var title = _('Import Contacts');
|
||||
if(container.getSettingsModel().get("zarafa/v1/plugins/contactimporter/enable_export")){
|
||||
title = _('Import/Export Contacts');
|
||||
}
|
||||
Ext.applyIf(config, {
|
||||
layout : 'fit',
|
||||
title : title,
|
||||
@ -56,7 +53,8 @@ Zarafa.plugins.contactimporter.dialogs.ImportContentPanel = Ext.extend(Zarafa.co
|
||||
items : [
|
||||
{
|
||||
xtype : 'contactimporter.importcontactpanel',
|
||||
filename : config.filename
|
||||
filename: config.filename,
|
||||
folder : config.folder
|
||||
}
|
||||
]
|
||||
});
|
||||
|
@ -2,7 +2,7 @@
|
||||
* ImportPanel.js zarafa contact to vcf im/exporter
|
||||
*
|
||||
* 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
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
@ -29,7 +29,7 @@ Ext.namespace("Zarafa.plugins.contactimporter.dialogs");
|
||||
|
||||
/**
|
||||
* @class Zarafa.plugins.contactimporter.dialogs.ImportPanel
|
||||
* @extends Ext.form.FormPanel
|
||||
* @extends Ext.Panel
|
||||
*/
|
||||
Zarafa.plugins.contactimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
|
||||
|
||||
@ -39,6 +39,9 @@ Zarafa.plugins.contactimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
|
||||
/* The store for the selection grid */
|
||||
store : null,
|
||||
|
||||
/* selected folder */
|
||||
folder : null,
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @param {object} config
|
||||
@ -47,10 +50,14 @@ Zarafa.plugins.contactimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
|
||||
config = config || {};
|
||||
var self = this;
|
||||
|
||||
if(typeof config.filename !== "undefined") {
|
||||
if (!Ext.isEmpty(config.filename)) {
|
||||
this.vcffile = config.filename;
|
||||
}
|
||||
|
||||
if (!Ext.isEmpty(config.folder)) {
|
||||
this.folder = config.folder;
|
||||
}
|
||||
|
||||
// create the data store
|
||||
// we only display the firstname, lastname, homephone and primary email address in our grid
|
||||
this.store = new Ext.data.ArrayStore({
|
||||
@ -94,15 +101,6 @@ Zarafa.plugins.contactimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
|
||||
this.parseContacts(this.vcffile);
|
||||
}
|
||||
},
|
||||
close: function (cmp) {
|
||||
Ext.getCmp("importcontactsbutton").enable();
|
||||
},
|
||||
hide: function (cmp) {
|
||||
Ext.getCmp("importcontactsbutton").enable();
|
||||
},
|
||||
destroy: function (cmp) {
|
||||
Ext.getCmp("importcontactsbutton").enable();
|
||||
},
|
||||
scope : this
|
||||
}
|
||||
});
|
||||
@ -134,6 +132,121 @@ Zarafa.plugins.contactimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Get all contact folders.
|
||||
* @param {boolean} asDropdownStore If true, a simple array store will be returned.
|
||||
* @returns {*}
|
||||
*/
|
||||
getAllContactFolders: function (asDropdownStore) {
|
||||
asDropdownStore = Ext.isEmpty(asDropdownStore) ? false : asDropdownStore;
|
||||
|
||||
var allFolders = [];
|
||||
|
||||
var defaultContactFolder = container.getHierarchyStore().getDefaultFolder('contact');
|
||||
|
||||
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.Contact") {
|
||||
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.Contact") {
|
||||
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(this.dynamicSort(1));
|
||||
} else {
|
||||
return allFolders;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Return a contact folder element by name.
|
||||
* @param {string} name
|
||||
* @returns {*}
|
||||
*/
|
||||
getContactFolderByName: function (name) {
|
||||
var folders = this.getAllContactFolders(false);
|
||||
|
||||
for (var i = 0; i < folders.length; i++) {
|
||||
if (folders[i].display_name == name) {
|
||||
return folders[i];
|
||||
}
|
||||
}
|
||||
|
||||
return container.getHierarchyStore().getDefaultFolder('contact');
|
||||
},
|
||||
|
||||
/**
|
||||
* Return a contact folder element by entryid.
|
||||
* @param {string} entryid
|
||||
* @returns {*}
|
||||
*/
|
||||
getContactFolderByEntryid: function (entryid) {
|
||||
var folders = this.getAllContactFolders(false);
|
||||
|
||||
for (var i = 0; i < folders.length; i++) {
|
||||
if (folders[i].entryid == entryid) {
|
||||
return folders[i];
|
||||
}
|
||||
}
|
||||
|
||||
return container.getHierarchyStore().getDefaultFolder('contact');
|
||||
},
|
||||
|
||||
/**
|
||||
* Reloads the data of the grid
|
||||
* @private
|
||||
@ -146,13 +259,13 @@ Zarafa.plugins.contactimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
|
||||
var i = 0;
|
||||
for (i = 0; i < contactdata.contacts.length; i++) {
|
||||
|
||||
parsedData[i] = new Array(
|
||||
parsedData[i] = [
|
||||
contactdata.contacts[i]["display_name"],
|
||||
contactdata.contacts[i]["given_name"],
|
||||
contactdata.contacts[i]["surname"],
|
||||
contactdata.contacts[i]["company_name"],
|
||||
contactdata.contacts[i]
|
||||
);
|
||||
];
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
@ -195,55 +308,36 @@ Zarafa.plugins.contactimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Generate the UI calendar select box.
|
||||
* @returns {*}
|
||||
*/
|
||||
createSelectBox: function () {
|
||||
var defaultFolder = container.getHierarchyStore().getDefaultFolder('contact'); // @type: Zarafa.hierarchy.data.MAPIFolderRecord
|
||||
var subFolders = defaultFolder.getChildren();
|
||||
var myStore = [];
|
||||
|
||||
/* add all local contact 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 contact 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.Contact")){
|
||||
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);
|
||||
}
|
||||
}
|
||||
var myStore = this.getAllContactFolders(true);
|
||||
|
||||
return {
|
||||
xtype : "selectbox",
|
||||
ref : 'addressbookSelector',
|
||||
editable : false,
|
||||
name : "choosen_addressbook",
|
||||
value: container.getSettingsModel().get("zarafa/v1/plugins/contactimporter/default_addressbook"),
|
||||
value : Ext.isEmpty(this.folder) ? this.getContactFolderByName(container.getSettingsModel().get("zarafa/v1/plugins/contactimporter/default_addressbook")).entryid : this.folder,
|
||||
width : 100,
|
||||
fieldLabel: "Select an addressbook",
|
||||
fieldLabel : "Select folder",
|
||||
store : myStore,
|
||||
mode : 'local',
|
||||
labelSeperator: ":",
|
||||
border : false,
|
||||
anchor : "100%",
|
||||
scope : this,
|
||||
hidden : Ext.isEmpty(this.folder) ? false : true,
|
||||
allowBlank : false
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Generate the UI upload field.
|
||||
* @returns {*}
|
||||
*/
|
||||
createUploadField: function () {
|
||||
return {
|
||||
xtype : "fileuploadfield",
|
||||
@ -254,6 +348,7 @@ Zarafa.plugins.contactimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
|
||||
emptyText : 'Select an .vcf addressbook',
|
||||
border : false,
|
||||
anchor : "100%",
|
||||
height : "30",
|
||||
scope : this,
|
||||
allowBlank : false,
|
||||
listeners : {
|
||||
@ -263,6 +358,10 @@ Zarafa.plugins.contactimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Generate the UI submit button.
|
||||
* @returns {*}
|
||||
*/
|
||||
createSubmitButton: function () {
|
||||
return {
|
||||
xtype : "button",
|
||||
@ -278,6 +377,10 @@ Zarafa.plugins.contactimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Generate the UI submit all button.
|
||||
* @returns {*}
|
||||
*/
|
||||
createSubmitAllButton: function () {
|
||||
return {
|
||||
xtype : "button",
|
||||
@ -293,6 +396,10 @@ Zarafa.plugins.contactimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Generate the UI cancel button.
|
||||
* @returns {*}
|
||||
*/
|
||||
createCancelButton: function () {
|
||||
return {
|
||||
xtype : "button",
|
||||
@ -339,6 +446,10 @@ Zarafa.plugins.contactimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Start request to server to parse the given vCard file.
|
||||
* @param {string} vcfPath
|
||||
*/
|
||||
parseContacts: function (vcfPath) {
|
||||
this.loadMask.show();
|
||||
|
||||
@ -357,6 +468,10 @@ Zarafa.plugins.contactimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Callback for the parsing request.
|
||||
* @param {Object} response
|
||||
*/
|
||||
handleParsingResult: function (response) {
|
||||
this.loadMask.hide();
|
||||
|
||||
@ -377,16 +492,25 @@ Zarafa.plugins.contactimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Close the UI dialog.
|
||||
*/
|
||||
close: function () {
|
||||
this.addContactFormPanel.getForm().reset();
|
||||
this.dialog.close()
|
||||
},
|
||||
|
||||
/**
|
||||
* Create a request to import all selected contacts.
|
||||
*/
|
||||
importCheckedContacts: function () {
|
||||
var newRecords = this.contactGrid.selModel.getSelections();
|
||||
this.importContacts(newRecords);
|
||||
},
|
||||
|
||||
/**
|
||||
* Check all contacts and import them.
|
||||
*/
|
||||
importAllContacts: function () {
|
||||
//receive Records from grid rows
|
||||
this.contactGrid.selModel.selectAll(); // select all entries
|
||||
@ -395,8 +519,8 @@ Zarafa.plugins.contactimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
|
||||
},
|
||||
|
||||
/**
|
||||
* This function stores all given events to the appointmentstore
|
||||
* @param events
|
||||
* This function stores all given events to the contact store
|
||||
* @param {array} contacts
|
||||
*/
|
||||
importContacts: function (contacts) {
|
||||
//receive existing contact store
|
||||
@ -410,7 +534,6 @@ Zarafa.plugins.contactimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
|
||||
buttons: Zarafa.common.dialogs.MessageBox.OK
|
||||
});
|
||||
} else {
|
||||
var addressbookexist = true;
|
||||
if (this.contactGrid.selModel.getCount() < 1) {
|
||||
Zarafa.common.dialogs.MessageBox.show({
|
||||
title : _('Error'),
|
||||
@ -419,50 +542,15 @@ Zarafa.plugins.contactimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
|
||||
buttons: Zarafa.common.dialogs.MessageBox.OK
|
||||
});
|
||||
} else {
|
||||
var contactStore = new Zarafa.contact.ContactStore();
|
||||
var contactFolder = container.getHierarchyStore().getDefaultFolder('contact');
|
||||
var pubStore = container.getHierarchyStore().getPublicStore();
|
||||
var pubFolder = pubStore.getDefaultFolder("publicfolders");
|
||||
var pubSubFolders = pubFolder.getChildren();
|
||||
var contactFolder = this.getContactFolderByEntryid(folderValue);
|
||||
|
||||
if(folderValue != "contact") {
|
||||
var subFolders = contactFolder.getChildren();
|
||||
var i = 0;
|
||||
for(i = 0; i < pubSubFolders.length; i++) {
|
||||
if(pubSubFolders[i].isContainerClass("IPF.Contact")){
|
||||
subFolders.push(pubSubFolders[i]);
|
||||
}
|
||||
}
|
||||
for(i=0;i<subFolders.length;i++) {
|
||||
// look up right folder
|
||||
// TODO: improve!!
|
||||
if(subFolders[i].getDisplayName() == folderValue) {
|
||||
contactFolder = subFolders[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(contactFolder.isDefaultFolder()) {
|
||||
Zarafa.common.dialogs.MessageBox.show({
|
||||
title : _('Error'),
|
||||
msg : _('Selected addressbook does not exist!'),
|
||||
icon : Zarafa.common.dialogs.MessageBox.ERROR,
|
||||
buttons : Zarafa.common.dialogs.MessageBox.OK
|
||||
});
|
||||
addressbookexist = false;
|
||||
}
|
||||
}
|
||||
|
||||
if(addressbookexist) {
|
||||
this.loadMask.show();
|
||||
var uids = new Array();
|
||||
var store_entryid = "";
|
||||
var uids = [];
|
||||
|
||||
//receive Records from grid rows
|
||||
Ext.each(contacts, function (newRecord) {
|
||||
uids.push(newRecord.data.record.internal_fields.contact_uid);
|
||||
}, this);
|
||||
store_entryid = contactFolder.get('store_entryid');
|
||||
|
||||
var responseHandler = new Zarafa.plugins.contactimporter.data.ResponseHandler({
|
||||
successCallback: this.importContactsDone.createDelegate(this)
|
||||
@ -472,21 +560,22 @@ Zarafa.plugins.contactimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
|
||||
'contactmodule',
|
||||
'import',
|
||||
{
|
||||
storeid: contactFolder.get("store_entryid"),
|
||||
folderid: contactFolder.get("entryid"),
|
||||
storeid : contactFolder.store_entryid,
|
||||
folderid : contactFolder.entryid,
|
||||
uids : uids,
|
||||
vcf_filepath: this.vcffile
|
||||
},
|
||||
responseHandler
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Callback for the import request.
|
||||
* @param {Object} response
|
||||
*/
|
||||
importContactsDone: function (response) {
|
||||
console.log(response);
|
||||
this.loadMask.hide();
|
||||
this.dialog.close();
|
||||
if (response.status == true) {
|
||||
|
@ -2,7 +2,7 @@
|
||||
* plugin.contactimporter.js zarafa contactimporter
|
||||
*
|
||||
* 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
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
@ -46,33 +46,87 @@ Zarafa.plugins.contactimporter.ImportPlugin = Ext.extend(Zarafa.core.Plugin, {
|
||||
Zarafa.core.data.SharedComponentType.addProperty('plugins.contactimporter.dialogs.importcontacts');
|
||||
|
||||
/* directly import received vcfs */
|
||||
this.registerInsertionPoint('common.contextmenu.attachment.actions', this.createAttachmentImportButton);
|
||||
/* add import button to south navigation */
|
||||
this.registerInsertionPoint("navigation.south", this.createImportButton, this);
|
||||
this.registerInsertionPoint('common.contextmenu.attachment.actions', this.createAttachmentImportButton, this);
|
||||
|
||||
/* export a contact via rightclick */
|
||||
this.registerInsertionPoint('context.contact.contextmenu.actions', this.createItemExportInsertionPoint, this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates the button
|
||||
*
|
||||
* @return {Object} Configuration object for a {@link Ext.Button button}
|
||||
* This method hooks to the contact context menu and allows users to export users to vcf.
|
||||
*
|
||||
* @param include
|
||||
* @param btn
|
||||
* @returns {Object}
|
||||
*/
|
||||
createImportButton: function () {
|
||||
var button = {
|
||||
xtype : 'button',
|
||||
id : "importcontactsbutton",
|
||||
text : _('Import Contacts'),
|
||||
iconCls : 'icon_contactimporter_button',
|
||||
navigationContext : container.getContextByName('contact'),
|
||||
handler : this.onImportButtonClick,
|
||||
scope : this
|
||||
createItemExportInsertionPoint: function (include, btn) {
|
||||
return {
|
||||
text : dgettext('plugin_files', 'Export vCard'),
|
||||
handler: this.exportToVCF.createDelegate(this, [btn]),
|
||||
scope : this,
|
||||
iconCls: 'icon_contactimporter_export'
|
||||
};
|
||||
},
|
||||
|
||||
if(container.getSettingsModel().get("zarafa/v1/plugins/contactimporter/enable_export")) {
|
||||
button.text = _('Import/Export Contacts');
|
||||
/**
|
||||
* Generates a request to download the selected records as vCard.
|
||||
* @param {Ext.Button} btn
|
||||
*/
|
||||
exportToVCF: 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.contactimporter.data.ResponseHandler({
|
||||
successCallback: this.downloadVCF,
|
||||
scope : this
|
||||
});
|
||||
|
||||
// request attachment preperation
|
||||
container.getRequest().singleRequest(
|
||||
'contactmodule',
|
||||
'export',
|
||||
{
|
||||
storeid: btn.records[0].get("store_entryid"),
|
||||
records: recordIds
|
||||
},
|
||||
responseHandler
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Callback for the export request.
|
||||
* @param {Object} response
|
||||
*/
|
||||
downloadVCF: 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_vcf";
|
||||
link = Ext.urlAppend(link, "token=" + encodeURIComponent(response.download_token));
|
||||
link = Ext.urlAppend(link, "filename=" + encodeURIComponent(response.filename));
|
||||
|
||||
downloadFrame.dom.contentWindow.location = link;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@ -82,17 +136,17 @@ Zarafa.plugins.contactimporter.ImportPlugin = Ext.extend(Zarafa.core.Plugin, {
|
||||
*/
|
||||
createAttachmentImportButton: function (include, btn) {
|
||||
return {
|
||||
text : _('Import Contacts'),
|
||||
handler : this.getAttachmentFileName.createDelegate(this, [btn, this.gotAttachmentFileName]),
|
||||
text : _('Import to Contacts'),
|
||||
handler : this.getAttachmentFileName.createDelegate(this, [btn]),
|
||||
scope : this,
|
||||
iconCls : 'icon_contactimporter_button',
|
||||
beforeShow: function (item, record) {
|
||||
var extension = record.data.name.split('.').pop().toLowerCase();
|
||||
|
||||
if(record.data.filetype == "text/vcard" || extension == "vcf" || extension == "vcard") {
|
||||
item.setDisabled(false);
|
||||
if (record.data.filetype == "text/vcard" || record.data.filetype == "text/x-vcard" || extension == "vcf" || extension == "vcard") {
|
||||
item.setVisible(true);
|
||||
} else {
|
||||
item.setDisabled(true);
|
||||
item.setVisible(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -100,13 +154,11 @@ Zarafa.plugins.contactimporter.ImportPlugin = Ext.extend(Zarafa.core.Plugin, {
|
||||
|
||||
/**
|
||||
* Callback for getAttachmentFileName
|
||||
* @param {Object} response
|
||||
*/
|
||||
gotAttachmentFileName: function (response) {
|
||||
if (response.status == true) {
|
||||
Zarafa.core.data.UIFactory.openLayerComponent(Zarafa.core.data.SharedComponentType['plugins.contactimporter.dialogs.importcontacts'], undefined, {
|
||||
manager : Ext.WindowMgr,
|
||||
filename : response.tmpname
|
||||
});
|
||||
this.openImportDialog(response.tmpname);
|
||||
} else {
|
||||
Zarafa.common.dialogs.MessageBox.show({
|
||||
title : _('Error'),
|
||||
@ -119,8 +171,9 @@ Zarafa.plugins.contactimporter.ImportPlugin = Ext.extend(Zarafa.core.Plugin, {
|
||||
|
||||
/**
|
||||
* Clickhandler for the button
|
||||
* @param {Ext.Button} btn
|
||||
*/
|
||||
getAttachmentFileName: function (btn, callback) {
|
||||
getAttachmentFileName: function (btn) {
|
||||
Zarafa.common.dialogs.MessageBox.show({
|
||||
title : 'Please wait',
|
||||
msg : 'Loading attachment...',
|
||||
@ -152,15 +205,17 @@ Zarafa.plugins.contactimporter.ImportPlugin = Ext.extend(Zarafa.core.Plugin, {
|
||||
var store = attachmentStore.getParentRecord().get('store_entryid');
|
||||
var entryid = attachmentStore.getAttachmentParentRecordEntryId();
|
||||
var attachNum = new Array(1);
|
||||
if (attachmentRecord.get('attach_num') != -1)
|
||||
if (attachmentRecord.get('attach_num') != -1) {
|
||||
attachNum[0] = attachmentRecord.get('attach_num');
|
||||
else
|
||||
} else {
|
||||
attachNum[0] = attachmentRecord.get('tmpname');
|
||||
}
|
||||
var dialog_attachments = attachmentStore.getId();
|
||||
var filename = attachmentRecord.data.name;
|
||||
|
||||
var responseHandler = new Zarafa.plugins.contactimporter.data.ResponseHandler({
|
||||
successCallback: callback
|
||||
successCallback: this.gotAttachmentFileName.createDelegate(this),
|
||||
scope : this
|
||||
});
|
||||
|
||||
// request attachment preperation
|
||||
@ -179,13 +234,17 @@ Zarafa.plugins.contactimporter.ImportPlugin = Ext.extend(Zarafa.core.Plugin, {
|
||||
},
|
||||
|
||||
/**
|
||||
* Clickhandler for the button
|
||||
* Open the import dialog.
|
||||
* @param {String} filename
|
||||
*/
|
||||
onImportButtonClick: function () {
|
||||
Ext.getCmp("importcontactsbutton").disable();
|
||||
Zarafa.core.data.UIFactory.openLayerComponent(Zarafa.core.data.SharedComponentType['plugins.contactimporter.dialogs.importcontacts'], undefined, {
|
||||
manager : Ext.WindowMgr
|
||||
});
|
||||
openImportDialog: function (filename) {
|
||||
var componentType = Zarafa.core.data.SharedComponentType['plugins.contactimporter.dialogs.importcontacts'];
|
||||
var config = {
|
||||
filename: filename,
|
||||
modal : true
|
||||
};
|
||||
|
||||
Zarafa.core.data.UIFactory.openLayerComponent(componentType, undefined, config);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -200,7 +259,14 @@ Zarafa.plugins.contactimporter.ImportPlugin = Ext.extend(Zarafa.core.Plugin, {
|
||||
var bid = -1;
|
||||
switch (type) {
|
||||
case Zarafa.core.data.SharedComponentType['plugins.contactimporter.dialogs.importcontacts']:
|
||||
bid = 1;
|
||||
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.Contact") {
|
||||
bid = 2;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return bid;
|
||||
@ -219,6 +285,9 @@ Zarafa.plugins.contactimporter.ImportPlugin = Ext.extend(Zarafa.core.Plugin, {
|
||||
case Zarafa.core.data.SharedComponentType['plugins.contactimporter.dialogs.importcontacts']:
|
||||
component = Zarafa.plugins.contactimporter.dialogs.ImportContentPanel;
|
||||
break;
|
||||
case Zarafa.core.data.SharedComponentType['common.contextmenu']:
|
||||
component = Zarafa.plugins.contactimporter.ui.ContextMenu;
|
||||
break;
|
||||
}
|
||||
|
||||
return component;
|
||||
|
136
js/ui/ContextMenu.js
Normal file
136
js/ui/ContextMenu.js
Normal file
@ -0,0 +1,136 @@
|
||||
Ext.namespace('Zarafa.plugins.contactimporter.ui');
|
||||
|
||||
/**
|
||||
* @class Zarafa.plugins.contactimporter.ui.ContextMenu
|
||||
* @extends Zarafa.hierarchy.ui.ContextMenu
|
||||
* @xtype contactimporter.hierarchycontextmenu
|
||||
*/
|
||||
Zarafa.plugins.contactimporter.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.contactimporter.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.contactimporter.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.contactimporter.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.contactimporter.ui.ContextMenu HierarchyContextMenu}
|
||||
*/
|
||||
createAdditionalContextMenuItems: function (config) {
|
||||
return [{
|
||||
xtype: 'menuseparator'
|
||||
}, {
|
||||
text : _('Import vCard'),
|
||||
iconCls : 'icon_contactimporter_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 vCard'),
|
||||
iconCls : 'icon_contactimporter_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.contactimporter.ui.ContextMenu ContextMenu}
|
||||
* @private
|
||||
*/
|
||||
onContextItemExport: function () {
|
||||
var responseHandler = new Zarafa.plugins.contactimporter.data.ResponseHandler({
|
||||
successCallback: this.downloadVCF,
|
||||
scope : this
|
||||
});
|
||||
|
||||
// request attachment preperation
|
||||
container.getRequest().singleRequest(
|
||||
'contactmodule',
|
||||
'export',
|
||||
{
|
||||
storeid: this.records.get("store_entryid"),
|
||||
folder : this.records.get("entryid")
|
||||
},
|
||||
responseHandler
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Fires on selecting 'Open' menu option from {@link Zarafa.plugins.contactimporter.ui.ContextMenu ContextMenu}
|
||||
* @private
|
||||
*/
|
||||
onContextItemImport: function () {
|
||||
var componentType = Zarafa.core.data.SharedComponentType['plugins.contactimporter.dialogs.importcontacts'];
|
||||
var config = {
|
||||
modal : true,
|
||||
folder: this.records.get("entryid")
|
||||
};
|
||||
|
||||
Zarafa.core.data.UIFactory.openLayerComponent(componentType, undefined, config);
|
||||
},
|
||||
|
||||
/**
|
||||
* Callback for the export request.
|
||||
* @param {Object} response
|
||||
*/
|
||||
downloadVCF: 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_vcf";
|
||||
link = Ext.urlAppend(link, "token=" + encodeURIComponent(response.download_token));
|
||||
link = Ext.urlAppend(link, "filename=" + encodeURIComponent(response.filename));
|
||||
|
||||
downloadFrame.dom.contentWindow.location = link;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Ext.reg('contactimporter.hierarchycontextmenu', Zarafa.plugins.contactimporter.ui.ContextMenu);
|
@ -2,7 +2,7 @@
|
||||
<!DOCTYPE plugin SYSTEM "manifest.dtd">
|
||||
<plugin version="2">
|
||||
<info>
|
||||
<version>@_@PLUGIN_VERSION@_@</version>
|
||||
<version>2.0.0</version>
|
||||
<name>contactimporter</name>
|
||||
<title>VCF Contact Importer/Exporter</title>
|
||||
<author>Christoph Haas</author>
|
||||
@ -20,16 +20,18 @@
|
||||
<serverfile type="module" module="contactmodule">php/module.contact.php</serverfile>
|
||||
</server>
|
||||
<client>
|
||||
<clientfile load="release">js/contactimporter.js</clientfile>
|
||||
<clientfile load="release">js/contactimporter-debug.js</clientfile>
|
||||
<clientfile load="debug">js/contactimporter-debug.js</clientfile>
|
||||
|
||||
<clientfile load="source">js/plugin.contactimporter.js</clientfile>
|
||||
<clientfile load="source">js/ABOUT.js</clientfile>
|
||||
<clientfile load="source">js/data/ResponseHandler.js</clientfile>
|
||||
<clientfile load="source">js/dialogs/ImportContentPanel.js</clientfile>
|
||||
<clientfile load="source">js/dialogs/ImportPanel.js</clientfile>
|
||||
<clientfile load="source">js/ui/ContextMenu.js</clientfile>
|
||||
</client>
|
||||
<resources>
|
||||
<resourcefile load="release">resources/css/contactimporter-min.css</resourcefile>
|
||||
<resourcefile load="release">resources/css/contactimporter.css</resourcefile>
|
||||
<resourcefile load="debug">resources/css/contactimporter.css</resourcefile>
|
||||
<resourcefile load="source">resources/css/contactimporter-main.css</resourcefile>
|
||||
</resources>
|
||||
|
5
php/.gitignore
vendored
Normal file
5
php/.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
composer.phar
|
||||
.composer.lock
|
||||
composer.lock
|
||||
vendor
|
||||
vendor/*
|
5
php/composer.json
Normal file
5
php/composer.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"require": {
|
||||
"jeroendesloovere/vcard": "1.2.*"
|
||||
}
|
||||
}
|
72
php/download.php
Normal file
72
php/download.php
Normal file
@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* download.php, zarafa contact to vcf 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
|
||||
*
|
||||
*/
|
||||
class DownloadHandler
|
||||
{
|
||||
/**
|
||||
* Download the given vcf file.
|
||||
* @return bool
|
||||
*/
|
||||
public static function doDownload()
|
||||
{
|
||||
if (isset($_GET["token"])) {
|
||||
$token = $_GET["token"];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isset($_GET["filename"])) {
|
||||
$filename = $_GET["filename"];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
// validate token
|
||||
if (!ctype_alnum($token)) { // token is a md5 hash
|
||||
return false;
|
||||
}
|
||||
|
||||
$file = PLUGIN_CONTACTIMPORTER_TMP_UPLOAD . "vcf_" . $token . ".vcf";
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
@ -3,7 +3,7 @@
|
||||
* class.calendar.php, zarafa contact to vcf im/exporter
|
||||
*
|
||||
* 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
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
@ -21,10 +21,13 @@
|
||||
*
|
||||
*/
|
||||
|
||||
include_once('vcf/class.vCard.php');
|
||||
require_once('mapi/mapitags.php' );
|
||||
include_once('vendor/autoload.php');
|
||||
|
||||
class ContactModule extends Module {
|
||||
use JeroenDesloovere\VCard\VCard;
|
||||
use JeroenDesloovere\VCard\VCardParser;
|
||||
|
||||
class ContactModule extends Module
|
||||
{
|
||||
|
||||
private $DEBUG = false; // enable error_log debugging
|
||||
|
||||
@ -33,7 +36,8 @@ class ContactModule extends Module {
|
||||
* @param $id
|
||||
* @param $data
|
||||
*/
|
||||
public function __construct($id, $data) {
|
||||
public function __construct($id, $data)
|
||||
{
|
||||
parent::Module($id, $data);
|
||||
}
|
||||
|
||||
@ -42,7 +46,8 @@ class ContactModule extends Module {
|
||||
* Exception part is used for authentication errors also
|
||||
* @return boolean true on success or false on failure.
|
||||
*/
|
||||
public function execute() {
|
||||
public function execute()
|
||||
{
|
||||
$result = false;
|
||||
|
||||
if (!$this->DEBUG) {
|
||||
@ -63,6 +68,9 @@ class ContactModule extends Module {
|
||||
case "import":
|
||||
$result = $this->importContacts($actionType, $actionData);
|
||||
break;
|
||||
case "export":
|
||||
$result = $this->exportContacts($actionType, $actionData);
|
||||
break;
|
||||
case "importattachment":
|
||||
$result = $this->getAttachmentPath($actionType, $actionData);
|
||||
break;
|
||||
@ -90,7 +98,8 @@ class ContactModule extends Module {
|
||||
* @param $length the lenght of the generated string
|
||||
* @return string a random string
|
||||
*/
|
||||
private function randomstring($length = 6) {
|
||||
private function randomstring($length = 6)
|
||||
{
|
||||
// $chars - all allowed charakters
|
||||
$chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
|
||||
|
||||
@ -111,7 +120,8 @@ class ContactModule extends Module {
|
||||
* @param $actionType
|
||||
* @param $actionData
|
||||
*/
|
||||
private function importContacts($actionType, $actionData) {
|
||||
private function importContacts($actionType, $actionData)
|
||||
{
|
||||
|
||||
// Get uploaded vcf path
|
||||
$vcffile = false;
|
||||
@ -142,8 +152,9 @@ class ContactModule extends Module {
|
||||
$error_msg = "";
|
||||
|
||||
// parse the vcf file a last time...
|
||||
$parser = null;
|
||||
try {
|
||||
$vcard = new vCard($vcffile, false, array('Collapse' => false)); // Parse it!
|
||||
$parser = VCardParser::parseFromFile($vcffile);
|
||||
} catch (Exception $e) {
|
||||
$error = true;
|
||||
$error_msg = $e->getMessage();
|
||||
@ -151,13 +162,8 @@ class ContactModule extends Module {
|
||||
|
||||
$contacts = array();
|
||||
|
||||
if(!$error && count($vcard) > 0) {
|
||||
$vCard = $vcard;
|
||||
if (count($vCard) == 1) {
|
||||
$vCard = array($vcard);
|
||||
}
|
||||
|
||||
$contacts = $this->parseContactsToArray($vCard);
|
||||
if (!$error && iterator_count($parser) > 0) {
|
||||
$contacts = $this->parseContactsToArray($parser);
|
||||
$store = $GLOBALS["mapisession"]->openMessageStore(hex2bin($storeid));
|
||||
$folder = mapi_msgstore_openentry($store, hex2bin($folderid));
|
||||
|
||||
@ -243,7 +249,258 @@ class ContactModule extends Module {
|
||||
$GLOBALS["bus"]->addData($this->getResponseData());
|
||||
}
|
||||
|
||||
private function replaceStringPropertyTags($store, $properties) {
|
||||
/**
|
||||
* Get a property from the array.
|
||||
* @param $props
|
||||
* @param $propname
|
||||
* @return string
|
||||
*/
|
||||
private function getProp($props, $propname)
|
||||
{
|
||||
$p = $this->getProperties();
|
||||
if (isset($props["props"][$propname])) {
|
||||
return $props["props"][$propname];
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Export selected contacts to vCard.
|
||||
* @param $actionType
|
||||
* @param $actionData
|
||||
* @return bool
|
||||
*/
|
||||
private function exportContacts($actionType, $actionData)
|
||||
{
|
||||
// Get store id
|
||||
$storeid = false;
|
||||
if (isset($actionData["storeid"])) {
|
||||
$storeid = $actionData["storeid"];
|
||||
}
|
||||
|
||||
// Get records
|
||||
$records = array();
|
||||
if (isset($actionData["records"])) {
|
||||
$records = $actionData["records"];
|
||||
}
|
||||
|
||||
// Get folders
|
||||
$folder = false;
|
||||
if (isset($actionData["folder"])) {
|
||||
$folder = $actionData["folder"];
|
||||
}
|
||||
|
||||
$response = array();
|
||||
$error = false;
|
||||
$error_msg = "";
|
||||
|
||||
// write csv
|
||||
$token = $this->randomstring(16);
|
||||
$file = PLUGIN_CONTACTIMPORTER_TMP_UPLOAD . "vcf_" . $token . ".vcf";
|
||||
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]);
|
||||
}
|
||||
}
|
||||
for ($index = 0, $count = count($records); $index < $count; $index++) {
|
||||
// define vcard
|
||||
$vcard = new VCard();
|
||||
|
||||
$message = mapi_msgstore_openentry($store, hex2bin($records[$index]));
|
||||
|
||||
// get message properties.
|
||||
$properties = $GLOBALS['properties']->getContactProperties();
|
||||
$plaintext = true;
|
||||
$messageProps = $GLOBALS['operations']->getMessageProps($store, $message, $properties, $plaintext);
|
||||
|
||||
// define variables
|
||||
$firstname = $this->getProp($messageProps, "given_name");
|
||||
$lastname = $this->getProp($messageProps, "surname");
|
||||
$additional = $this->getProp($messageProps, "middle_name");
|
||||
$prefix = $this->getProp($messageProps, "display_name_prefix");
|
||||
$suffix = '';
|
||||
|
||||
// add personal data
|
||||
$vcard->addName($lastname, $firstname, $additional, $prefix, $suffix);
|
||||
|
||||
$company = $this->getProp($messageProps, "company_name");
|
||||
if (!empty($company)) {
|
||||
$vcard->addCompany($company);
|
||||
}
|
||||
|
||||
$jobtitle = $this->getProp($messageProps, "title");
|
||||
if (!empty($jobtitle)) {
|
||||
$vcard->addJobtitle($jobtitle);
|
||||
}
|
||||
|
||||
// MAIL
|
||||
$mail = $this->getProp($messageProps, "email_address_1");
|
||||
if (!empty($mail)) {
|
||||
$vcard->addEmail($mail);
|
||||
}
|
||||
$mail = $this->getProp($messageProps, "email_address_2");
|
||||
if (!empty($mail)) {
|
||||
$vcard->addEmail($mail);
|
||||
}
|
||||
$mail = $this->getProp($messageProps, "email_address_3");
|
||||
if (!empty($mail)) {
|
||||
$vcard->addEmail($mail);
|
||||
}
|
||||
|
||||
// PHONE
|
||||
$wphone = $this->getProp($messageProps, "business_telephone_number");
|
||||
if (!empty($wphone)) {
|
||||
$vcard->addPhoneNumber($wphone, 'WORK');
|
||||
}
|
||||
$wphone = $this->getProp($messageProps, "home_telephone_number");
|
||||
if (!empty($wphone)) {
|
||||
$vcard->addPhoneNumber($wphone, 'HOME');
|
||||
}
|
||||
$wphone = $this->getProp($messageProps, "cellular_telephone_number");
|
||||
if (!empty($wphone)) {
|
||||
$vcard->addPhoneNumber($wphone, 'CELL');
|
||||
}
|
||||
$wphone = $this->getProp($messageProps, "business_fax_number");
|
||||
if (!empty($wphone)) {
|
||||
$vcard->addPhoneNumber($wphone, 'FAX');
|
||||
}
|
||||
$wphone = $this->getProp($messageProps, "pager_telephone_number");
|
||||
if (!empty($wphone)) {
|
||||
$vcard->addPhoneNumber($wphone, 'PAGER');
|
||||
}
|
||||
$wphone = $this->getProp($messageProps, "car_telephone_number");
|
||||
if (!empty($wphone)) {
|
||||
$vcard->addPhoneNumber($wphone, 'CAR');
|
||||
}
|
||||
|
||||
// ADDRESS
|
||||
$addr = $this->getProp($messageProps, "business_address");
|
||||
if (!empty($addr)) {
|
||||
$vcard->addAddress(null, null, $this->getProp($messageProps, "business_address_street"), $this->getProp($messageProps, "business_address_city"), $this->getProp($messageProps, "business_address_state"), $this->getProp($messageProps, "business_address_postal_code"), $this->getProp($messageProps, "business_address_country"), "WORK");
|
||||
}
|
||||
$addr = $this->getProp($messageProps, "home_address");
|
||||
if (!empty($addr)) {
|
||||
$vcard->addAddress(null, null, $this->getProp($messageProps, "home_address_street"), $this->getProp($messageProps, "home_address_city"), $this->getProp($messageProps, "home_address_state"), $this->getProp($messageProps, "home_address_postal_code"), $this->getProp($messageProps, "home_address_country"), "HOME");
|
||||
}
|
||||
$addr = $this->getProp($messageProps, "other_address");
|
||||
if (!empty($addr)) {
|
||||
$vcard->addAddress(null, null, $this->getProp($messageProps, "other_address_street"), $this->getProp($messageProps, "other_address_city"), $this->getProp($messageProps, "other_address_state"), $this->getProp($messageProps, "other_address_postal_code"), $this->getProp($messageProps, "other_address_country"), "OTHER");
|
||||
}
|
||||
|
||||
// MISC
|
||||
$url = $this->getProp($messageProps, "webpage");
|
||||
if (!empty($url)) {
|
||||
$vcard->addURL($url);
|
||||
}
|
||||
|
||||
$bday = $this->getProp($messageProps, "birthday");
|
||||
if (!empty($bday)) {
|
||||
$vcard->addBirthday(date("Y-m-d", $bday));
|
||||
}
|
||||
|
||||
$notes = $this->getProp($messageProps, "body");
|
||||
if (!empty($notes)) {
|
||||
$vcard->addNote($notes);
|
||||
}
|
||||
|
||||
$haspicture = $this->getProp($messageProps, "has_picture");
|
||||
if (!empty($haspicture) && $haspicture === true) {
|
||||
$attachnum = -1;
|
||||
if (isset($messageProps["attachments"]) && isset($messageProps["attachments"]["item"])) {
|
||||
foreach ($messageProps["attachments"]["item"] as $attachment) {
|
||||
if ($attachment["props"]["attachment_contactphoto"] == true) {
|
||||
$attachnum = $attachment["props"]["attach_num"];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($attachnum >= 0) {
|
||||
$attachment = $this->getAttachmentByAttachNum($message, $attachnum); // get first attachment only
|
||||
$phototoken = $this->randomstring(16);
|
||||
$tmpphoto = PLUGIN_CONTACTIMPORTER_TMP_UPLOAD . "photo_" . $phototoken . ".jpg";
|
||||
$this->storeSavedAttachment($tmpphoto, $attachment);
|
||||
$vcard->addPhoto($tmpphoto, true);
|
||||
unlink($tmpphoto);
|
||||
}
|
||||
}
|
||||
// write combined vcf
|
||||
file_put_contents($file, file_get_contents($file) . $vcard->getOutput());
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (count($records) > 0) {
|
||||
$response['status'] = true;
|
||||
$response['download_token'] = $token;
|
||||
$response['filename'] = count($records) . "contacts.vcf";
|
||||
} else {
|
||||
$response['status'] = false;
|
||||
$response['message'] = "No contacts found. Export skipped!";
|
||||
}
|
||||
|
||||
$this->addActionData($actionType, $response);
|
||||
$GLOBALS["bus"]->addData($this->getResponseData());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns attachment based on specified attachNum, additionally it will also get embedded message
|
||||
* if we want to get the inline image attachment.
|
||||
* @param $message
|
||||
* @param array $attachNum
|
||||
* @return MAPIAttach embedded message attachment or attachment that is requested
|
||||
*/
|
||||
private function getAttachmentByAttachNum($message, $attachNum)
|
||||
{
|
||||
// open the attachment
|
||||
$attachment = mapi_message_openattach($message, $attachNum);
|
||||
|
||||
return $attachment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function will open passed attachment and generate response for that attachment to send it to client.
|
||||
* This should only be used to download attachment that is already saved in MAPIMessage.
|
||||
* @param MAPIAttach $attachment attachment which will be dumped to client side
|
||||
* @return Response response to sent to client including attachment data
|
||||
*/
|
||||
private function storeSavedAttachment($temppath, $attachment)
|
||||
{
|
||||
// Check if the attachment is opened
|
||||
if ($attachment) {
|
||||
// Open a stream to get the attachment data
|
||||
$stream = mapi_openproperty($attachment, PR_ATTACH_DATA_BIN, IID_IStream, 0, 0);
|
||||
$stat = mapi_stream_stat($stream);
|
||||
|
||||
// Read the attachment content from the stream
|
||||
$body = '';
|
||||
for ($i = 0; $i < $stat['cb']; $i += BLOCK_SIZE) {
|
||||
$body .= mapi_stream_read($stream, BLOCK_SIZE);
|
||||
}
|
||||
|
||||
file_put_contents($temppath, $body);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace String Property Tags
|
||||
* @param $store
|
||||
* @param $properties
|
||||
* @return array
|
||||
*/
|
||||
private function replaceStringPropertyTags($store, $properties)
|
||||
{
|
||||
$newProperties = array();
|
||||
|
||||
$ids = array("name" => array(), "id" => array(), "guid" => array(), "type" => array()); // this array stores all the information needed to retrieve a named property
|
||||
@ -307,10 +564,12 @@ class ContactModule extends Module {
|
||||
*
|
||||
* @return [array] the propertyarray
|
||||
*/
|
||||
private function getProperties() {
|
||||
private function getProperties()
|
||||
{
|
||||
$properties = array();
|
||||
|
||||
$properties["subject"] = PR_SUBJECT;
|
||||
$properties["hide_attachments"] = "PT_BOOLEAN:PSETID_Common:0x851";
|
||||
$properties["icon_index"] = PR_ICON_INDEX;
|
||||
$properties["message_class"] = PR_MESSAGE_CLASS;
|
||||
$properties["display_name"] = PR_DISPLAY_NAME;
|
||||
@ -445,13 +704,16 @@ class ContactModule extends Module {
|
||||
* @param $actionType
|
||||
* @param $actionData
|
||||
*/
|
||||
private function loadContacts($actionType, $actionData) {
|
||||
private function loadContacts($actionType, $actionData)
|
||||
{
|
||||
$error = false;
|
||||
$error_msg = "";
|
||||
|
||||
if (is_readable($actionData["vcf_filepath"])) {
|
||||
$parser = null;
|
||||
|
||||
try {
|
||||
$vcard = new vCard($actionData["vcf_filepath"], false, array('Collapse' => false)); // Parse it!
|
||||
$parser = VCardParser::parseFromFile($actionData["vcf_filepath"]);
|
||||
} catch (Exception $e) {
|
||||
$error = true;
|
||||
$error_msg = $e->getMessage();
|
||||
@ -460,19 +722,14 @@ class ContactModule extends Module {
|
||||
$response['status'] = false;
|
||||
$response['message'] = $error_msg;
|
||||
} else {
|
||||
if(count($vcard) == 0) {
|
||||
if (iterator_count($parser) == 0) {
|
||||
$response['status'] = false;
|
||||
$response['message'] = "No contacts in vcf file";
|
||||
} else {
|
||||
$vCard = $vcard;
|
||||
if (count($vCard) == 1) {
|
||||
$vCard = array($vcard);
|
||||
}
|
||||
|
||||
$response['status'] = true;
|
||||
$response['parsed_file'] = $actionData["vcf_filepath"];
|
||||
$response['parsed'] = array(
|
||||
'contacts' => $this->parseContactsToArray($vCard)
|
||||
'contacts' => $this->parseContactsToArray($parser)
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -497,148 +754,207 @@ class ContactModule extends Module {
|
||||
* @return array parsed contacts
|
||||
* @private
|
||||
*/
|
||||
private function parseContactsToArray($contacts, $csv = false) {
|
||||
private function parseContactsToArray($contacts, $csv = false)
|
||||
{
|
||||
$carr = array();
|
||||
|
||||
if (!$csv) {
|
||||
foreach ($contacts as $Index => $vCard) {
|
||||
$properties = array();
|
||||
$properties["display_name"] = $vCard -> FN[0];
|
||||
$properties["fileas"] = $vCard -> FN[0];
|
||||
if (isset($vCard->fullname)) {
|
||||
$properties["display_name"] = $vCard->fullname;
|
||||
$properties["fileas"] = $vCard->fullname;
|
||||
} elseif (!isset($vCard->organization)) {
|
||||
error_log("Skipping entry! No fullname/organization given.");
|
||||
continue;
|
||||
}
|
||||
|
||||
$properties["hide_attachments"] = true;
|
||||
|
||||
//uid - used for front/backend communication
|
||||
$properties["internal_fields"] = array();
|
||||
$properties["internal_fields"]["contact_uid"] = base64_encode($Index . $properties["fileas"]);
|
||||
|
||||
foreach ($vCard -> N as $Name) {
|
||||
$properties["given_name"] = $Name['FirstName'];
|
||||
$properties["middle_name"] = $Name['AdditionalNames'];
|
||||
$properties["surname"] = $Name['LastName'];
|
||||
$properties["display_name_prefix"] = $Name['Prefixes'];
|
||||
}
|
||||
if ($vCard -> TEL) {
|
||||
foreach ($vCard -> TEL as $Tel) {
|
||||
if(!is_scalar($Tel)) {
|
||||
if(in_array("home", $Tel['Type'])) {
|
||||
$properties["home_telephone_number"] = $Tel['Value'];
|
||||
} else if(in_array("cell", $Tel['Type'])) {
|
||||
$properties["cellular_telephone_number"] = $Tel['Value'];
|
||||
} else if(in_array("work", $Tel['Type'])) {
|
||||
$properties["business_telephone_number"] = $Tel['Value'];
|
||||
} else if(in_array("fax", $Tel['Type'])) {
|
||||
$properties["business_fax_number"] = $Tel['Value'];
|
||||
} else if(in_array("pager", $Tel['Type'])) {
|
||||
$properties["pager_telephone_number"] = $Tel['Value'];
|
||||
} else if(in_array("isdn", $Tel['Type'])) {
|
||||
$properties["isdn_number"] = $Tel['Value'];
|
||||
} else if(in_array("car", $Tel['Type'])) {
|
||||
$properties["car_telephone_number"] = $Tel['Value'];
|
||||
} else if(in_array("modem", $Tel['Type'])) {
|
||||
$properties["ttytdd_telephone_number"] = $Tel['Value'];
|
||||
$properties["given_name"] = $vCard->firstname;
|
||||
$properties["middle_name"] = $vCard->additional;
|
||||
$properties["surname"] = $vCard->lastname;
|
||||
$properties["display_name_prefix"] = $vCard->prefix;
|
||||
|
||||
if (isset($vCard->phone) && count($vCard->phone) > 0) {
|
||||
foreach ($vCard->phone as $type => $number) {
|
||||
$number = $number[0]; // we only can store one number
|
||||
if ($this->startswith(strtolower($type), "home") || strtolower($type) === "default") {
|
||||
$properties["home_telephone_number"] = $number;
|
||||
} else {
|
||||
if ($this->startswith(strtolower($type), "cell")) {
|
||||
$properties["cellular_telephone_number"] = $number;
|
||||
} else {
|
||||
if ($this->startswith(strtolower($type), "work")) {
|
||||
$properties["business_telephone_number"] = $number;
|
||||
} else {
|
||||
if ($this->startswith(strtolower($type), "fax")) {
|
||||
$properties["business_fax_number"] = $number;
|
||||
} else {
|
||||
if ($this->startswith(strtolower($type), "pager")) {
|
||||
$properties["pager_telephone_number"] = $number;
|
||||
} else {
|
||||
if ($this->startswith(strtolower($type), "isdn")) {
|
||||
$properties["isdn_number"] = $number;
|
||||
} else {
|
||||
if ($this->startswith(strtolower($type), "car")) {
|
||||
$properties["car_telephone_number"] = $number;
|
||||
} else {
|
||||
if ($this->startswith(strtolower($type), "modem")) {
|
||||
$properties["ttytdd_telephone_number"] = $number;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($vCard -> EMAIL) {
|
||||
$e=0;
|
||||
foreach ($vCard -> EMAIL as $Email) {
|
||||
$fileas = $Email['Value'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isset($vCard->email) && count($vCard->email) > 0) {
|
||||
$emailcount = 0;
|
||||
$properties["address_book_long"] = 0;
|
||||
foreach ($vCard->email as $type => $email) {
|
||||
foreach ($email as $mail) {
|
||||
$fileas = $mail;
|
||||
if (isset($properties["fileas"]) && !empty($properties["fileas"])) {
|
||||
$fileas = $properties["fileas"];
|
||||
$fileas = $properties["fileas"]; // set to real name
|
||||
}
|
||||
|
||||
if(!is_scalar($Email)) {
|
||||
switch($e) {
|
||||
// we only have storage for 3 mail addresses!
|
||||
/**
|
||||
* type of email address address_book_mv address_book_long
|
||||
* email1 0 1 (0x00000001)
|
||||
* email2 1 2 (0x00000002)
|
||||
* email3 2 4 (0x00000004)
|
||||
* fax2(business fax) 3 8 (0x00000008)
|
||||
* fax3(home fax) 4 16 (0x00000010)
|
||||
* fax1(primary fax) 5 32 (0x00000020)
|
||||
*
|
||||
* address_book_mv is a multivalued property so all the values are passed in array
|
||||
* address_book_long stores sum of the flags
|
||||
* these both properties should be in sync always
|
||||
*/
|
||||
switch ($emailcount) {
|
||||
case 0:
|
||||
$properties["email_address_1"] = $Email['Value'];
|
||||
$properties["email_address_display_name_1"] = $fileas . " (" . $Email['Value'] . ")";
|
||||
$properties["email_address_1"] = $mail;
|
||||
$properties["email_address_display_name_1"] = $fileas . " (" . $mail . ")";
|
||||
$properties["email_address_display_name_email_1"] = $mail;
|
||||
$properties["address_book_mv"][] = 0; // this is needed for adding the contact to the email address book, 0 = email 1
|
||||
$properties["address_book_long"] += 1; // this specifies the number of elements in address_book_mv
|
||||
break;
|
||||
case 1:
|
||||
$properties["email_address_2"] = $Email['Value'];
|
||||
$properties["email_address_display_name_2"] = $fileas . " (" . $Email['Value'] . ")";
|
||||
$properties["email_address_2"] = $mail;
|
||||
$properties["email_address_display_name_2"] = $fileas . " (" . $mail . ")";
|
||||
$properties["email_address_display_name_email_2"] = $mail;
|
||||
$properties["address_book_mv"][] = 1; // this is needed for adding the contact to the email address book, 1 = email 2
|
||||
$properties["address_book_long"] += 2; // this specifies the number of elements in address_book_mv
|
||||
break;
|
||||
case 2:
|
||||
$properties["email_address_3"] = $Email['Value'];
|
||||
$properties["email_address_display_name_3"] = $fileas . " (" . $Email['Value'] . ")";
|
||||
$properties["email_address_3"] = $mail;
|
||||
$properties["email_address_display_name_3"] = $fileas . " (" . $mail . ")";
|
||||
$properties["email_address_display_name_email_3"] = $mail;
|
||||
$properties["address_book_mv"][] = 2; // this is needed for adding the contact to the email address book, 2 = email 3
|
||||
$properties["address_book_long"] += 4; // this specifies the number of elements in address_book_mv
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
$e++;
|
||||
$emailcount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($vCard -> ORG) {
|
||||
foreach ($vCard -> ORG as $Organization) {
|
||||
$properties["company_name"] = $Organization['Name'];
|
||||
if (isset($vCard->organization)) {
|
||||
$properties["company_name"] = $vCard->organization;
|
||||
if (empty($properties["display_name"])) {
|
||||
$properties["display_name"] = $Organization['Name']; // if we have no displayname - use the company name as displayname
|
||||
$properties["fileas"] = $Organization['Name'];
|
||||
$properties["display_name"] = $vCard->organization; // if we have no displayname - use the company name as displayname
|
||||
$properties["fileas"] = $vCard->organization;
|
||||
}
|
||||
}
|
||||
if (isset($vCard->title)) {
|
||||
$properties["title"] = $vCard->title;
|
||||
}
|
||||
if (isset($vCard->url) && count($vCard->url) > 0) {
|
||||
foreach ($vCard->url as $type => $url) {
|
||||
$url = $url[0]; // only 1 webaddress per type
|
||||
$properties["webpage"] = $url;
|
||||
break; // we can only store on url
|
||||
}
|
||||
}
|
||||
if (isset($vCard->address) && count($vCard->address) > 0) {
|
||||
|
||||
foreach ($vCard->address as $type => $address) {
|
||||
$address = $address[0]; // we only can store one address per type
|
||||
if ($this->startswith(strtolower($type), "work")) {
|
||||
$properties["business_address_street"] = $address->street;
|
||||
if (!empty($address->extended)) {
|
||||
$properties["business_address_street"] .= "\n" . $address->extended;
|
||||
}
|
||||
$properties["business_address_city"] = $address->city;
|
||||
$properties["business_address_state"] = $address->region;
|
||||
$properties["business_address_postal_code"] = $address->zip;
|
||||
$properties["business_address_country"] = $address->country;
|
||||
$properties["business_address"] = $this->buildAddressString($properties["business_address_street"], $address->zip, $address->city, $address->region, $address->country);
|
||||
} else {
|
||||
if ($this->startswith(strtolower($type), "home")) {
|
||||
$properties["home_address_street"] = $address->street;
|
||||
if (!empty($address->extended)) {
|
||||
$properties["home_address_street"] .= "\n" . $address->extended;
|
||||
}
|
||||
$properties["home_address_city"] = $address->city;
|
||||
$properties["home_address_state"] = $address->region;
|
||||
$properties["home_address_postal_code"] = $address->zip;
|
||||
$properties["home_address_country"] = $address->country;
|
||||
$properties["home_address"] = $this->buildAddressString($properties["home_address_street"], $address->zip, $address->city, $address->region, $address->country);
|
||||
} else {
|
||||
$properties["other_address_street"] = $address->street;
|
||||
if (!empty($address->extended)) {
|
||||
$properties["other_address_street"] .= "\n" . $address->extended;
|
||||
}
|
||||
$properties["other_address_city"] = $address->city;
|
||||
$properties["other_address_state"] = $address->region;
|
||||
$properties["other_address_postal_code"] = $address->zip;
|
||||
$properties["other_address_country"] = $address->country;
|
||||
$properties["other_address"] = $this->buildAddressString($properties["other_address_street"], $address->zip, $address->city, $address->region, $address->country);
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($vCard -> TITLE) {
|
||||
$title = $vCard -> TITLE[0];
|
||||
$properties["title"] = is_array($title) ? $title["Value"] : $title;
|
||||
}
|
||||
if ($vCard -> URL) {
|
||||
$url = $vCard -> URL[0]; // only 1 webaddress
|
||||
$properties["webpage"] = is_array($url) ? $url["Value"] : $url;
|
||||
if (isset($vCard->birthday)) {
|
||||
$properties["birthday"] = $vCard->birthday->getTimestamp();
|
||||
}
|
||||
if ($vCard -> IMPP) {
|
||||
foreach ($vCard -> IMPP as $IMPP) {
|
||||
if (!is_scalar($IMPP)) {
|
||||
$properties["im"] = $IMPP['Value'];
|
||||
if (isset($vCard->note)) {
|
||||
$properties["notes"] = $vCard->note;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($vCard -> ADR) {
|
||||
foreach ($vCard -> ADR as $Address) {
|
||||
if(in_array("work", $Address['Type'])) {
|
||||
$properties["business_address_street"] = $Address['StreetAddress'];
|
||||
$properties["business_address_city"] = $Address['Locality'];
|
||||
$properties["business_address_state"] = $Address['Region'];
|
||||
$properties["business_address_postal_code"] = $Address['PostalCode'];
|
||||
$properties["business_address_country"] = $Address['Country'];
|
||||
$properties["business_address"] = $this->buildAddressString($Address['StreetAddress'], $Address['PostalCode'], $Address['Locality'], $Address['Region'], $Address['Country']);
|
||||
} else if(in_array("home", $Address['Type'])) {
|
||||
$properties["home_address_street"] = $Address['StreetAddress'];
|
||||
$properties["home_address_city"] = $Address['Locality'];
|
||||
$properties["home_address_state"] = $Address['Region'];
|
||||
$properties["home_address_postal_code"] = $Address['PostalCode'];
|
||||
$properties["home_address_country"] = $Address['Country'];
|
||||
$properties["home_address"] = $this->buildAddressString($Address['StreetAddress'], $Address['PostalCode'], $Address['Locality'], $Address['Region'], $Address['Country']);
|
||||
} else if(in_array("postal", $Address['Type'])||in_array("parcel", $Address['Type'])||in_array("intl", $Address['Type'])||in_array("dom", $Address['Type'])) {
|
||||
$properties["other_address_street"] = $Address['StreetAddress'];
|
||||
$properties["other_address_city"] = $Address['Locality'];
|
||||
$properties["other_address_state"] = $Address['Region'];
|
||||
$properties["other_address_postal_code"] = $Address['PostalCode'];
|
||||
$properties["other_address_country"] = $Address['Country'];
|
||||
$properties["other_address"] = $this->buildAddressString($Address['StreetAddress'], $Address['PostalCode'], $Address['Locality'], $Address['Region'], $Address['Country']);
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($vCard -> BDAY) {
|
||||
$properties["birthday"] = strtotime($vCard -> BDAY[0]);
|
||||
}
|
||||
if ($vCard -> NOTE) {
|
||||
$properties["notes"] = $vCard -> NOTE[0];
|
||||
}
|
||||
if ($vCard -> PHOTO) {
|
||||
if (isset($vCard->rawPhoto) || isset($vCard->photo)) {
|
||||
if (!is_writable(TMP_PATH . "/")) {
|
||||
error_log("could not write to export tmp directory!: " . $E);
|
||||
error_log("Can not write to export tmp directory!");
|
||||
} else {
|
||||
$tmppath = TMP_PATH . "/" . $this->randomstring(15);
|
||||
try {
|
||||
if($vCard -> SaveFile('photo', 0, $tmppath)) {
|
||||
if (isset($vCard->rawPhoto)) {
|
||||
if (file_put_contents($tmppath, $vCard->rawPhoto)) {
|
||||
$properties["internal_fields"]["x_photo_path"] = $tmppath;
|
||||
}
|
||||
} elseif (isset($vCard->photo)) {
|
||||
if ($this->startswith(strtolower($vCard->photo), "http://") || $this->startswith(strtolower($vCard->photo), "https://")) { // check if it starts with http
|
||||
$ctx = stream_context_create(array('http' =>
|
||||
array(
|
||||
'timeout' => 3, //3 Seconds timout
|
||||
)
|
||||
));
|
||||
|
||||
if (file_put_contents($tmppath, file_get_contents($vCard->photo, false, $ctx))) {
|
||||
$properties["internal_fields"]["x_photo_path"] = $tmppath;
|
||||
}
|
||||
} else {
|
||||
if($this->DEBUG) {
|
||||
error_log("remote imagefetching not implemented");
|
||||
error_log("Invalid photo url: " . $vCard->photo);
|
||||
}
|
||||
}
|
||||
} catch (Exception $E) {
|
||||
error_log("Image exception: " . $E);
|
||||
}
|
||||
}
|
||||
}
|
||||
array_push($carr, $properties);
|
||||
@ -661,18 +977,31 @@ class ContactModule extends Module {
|
||||
* @return string the concatinated address string
|
||||
* @private
|
||||
*/
|
||||
private function buildAddressString($street, $zip, $city, $state, $country) {
|
||||
private function buildAddressString($street, $zip, $city, $state, $country)
|
||||
{
|
||||
$out = "";
|
||||
|
||||
if (isset($country) && $street != "") $out = $country;
|
||||
if (isset($country) && $street != "") {
|
||||
$out = $country;
|
||||
}
|
||||
|
||||
$zcs = "";
|
||||
if (isset($zip) && $zip != "") $zcs = $zip;
|
||||
if (isset($city) && $city != "") $zcs .= (($zcs)?" ":"") . $city;
|
||||
if (isset($state) && $state != "") $zcs .= (($zcs)?" ":"") . $state;
|
||||
if ($zcs) $out = $zcs . "\r\n" . $out;
|
||||
if (isset($zip) && $zip != "") {
|
||||
$zcs = $zip;
|
||||
}
|
||||
if (isset($city) && $city != "") {
|
||||
$zcs .= (($zcs) ? " " : "") . $city;
|
||||
}
|
||||
if (isset($state) && $state != "") {
|
||||
$zcs .= (($zcs) ? " " : "") . $state;
|
||||
}
|
||||
if ($zcs) {
|
||||
$out = $zcs . "\n" . $out;
|
||||
}
|
||||
|
||||
if (isset($street) && $street != "") $out = $street . (($out)?"\r\n". $out: "") ;
|
||||
if (isset($street) && $street != "") {
|
||||
$out = $street . (($out) ? "\n\n" . $out : "");
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
@ -683,7 +1012,8 @@ class ContactModule extends Module {
|
||||
* @param $actionData
|
||||
* @private
|
||||
*/
|
||||
private function getAttachmentPath($actionType, $actionData) {
|
||||
private function getAttachmentPath($actionType, $actionData)
|
||||
{
|
||||
// Get store id
|
||||
$storeid = false;
|
||||
if (isset($actionData["store"])) {
|
||||
@ -720,8 +1050,7 @@ class ContactModule extends Module {
|
||||
// Check if attachNum isset
|
||||
if ($attachNum) {
|
||||
// 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
|
||||
$tempattach = mapi_message_openattach($message, (int)$attachNum[$i]);
|
||||
if ($tempattach) {
|
||||
@ -747,11 +1076,15 @@ class ContactModule extends Module {
|
||||
// Set filename
|
||||
if (isset($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];
|
||||
} else if(isset($props[PR_DISPLAY_NAME])) {
|
||||
} else {
|
||||
if (isset($props[PR_DISPLAY_NAME])) {
|
||||
$filename = $props[PR_DISPLAY_NAME];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set content type
|
||||
if (isset($props[PR_ATTACH_MIME_TAG])) {
|
||||
@ -815,6 +1148,20 @@ class ContactModule extends Module {
|
||||
$GLOBALS["bus"]->addData($this->getResponseData());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if string starts with other string.
|
||||
* @param $haystack
|
||||
* @param $needle
|
||||
* @return bool
|
||||
*/
|
||||
private function startswith($haystack, $needle)
|
||||
{
|
||||
$haystack = str_replace("type=", "", $haystack); // remove type from string
|
||||
return substr($haystack, 0, strlen($needle)) === $needle;
|
||||
}
|
||||
}
|
||||
|
||||
;
|
||||
|
||||
?>
|
||||
|
@ -3,7 +3,7 @@
|
||||
* plugin.contactimporter.php, zarafa contact to vcf im/exporter
|
||||
*
|
||||
* 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
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
@ -20,6 +20,7 @@
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
require_once __DIR__ . "/download.php";
|
||||
|
||||
/**
|
||||
* contactimporter Plugin
|
||||
@ -27,19 +28,24 @@
|
||||
* With this plugin you can import a vcf file to your zarafa addressbook
|
||||
*
|
||||
*/
|
||||
class Plugincontactimporter extends Plugin {
|
||||
class Plugincontactimporter extends Plugin
|
||||
{
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
function Plugincontactimporter() {}
|
||||
function Plugincontactimporter()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Function initializes the Plugin and registers all hooks
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function init() {
|
||||
function init()
|
||||
{
|
||||
$this->registerHook('server.core.settings.init.before');
|
||||
$this->registerHook('server.index.load.custom');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -49,11 +55,17 @@ class Plugincontactimporter extends Plugin {
|
||||
* @param mixed $data object(s) related to the hook
|
||||
* @return void
|
||||
*/
|
||||
function execute($eventID, &$data) {
|
||||
function execute($eventID, &$data)
|
||||
{
|
||||
switch ($eventID) {
|
||||
case 'server.core.settings.init.before' :
|
||||
$this->injectPluginSettings($data);
|
||||
break;
|
||||
case 'server.index.load.custom':
|
||||
if ($data['name'] == 'download_vcf') {
|
||||
DownloadHandler::doDownload();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -62,14 +74,14 @@ class Plugincontactimporter extends Plugin {
|
||||
* settings.
|
||||
* @param Array $data Reference to the data of the triggered hook
|
||||
*/
|
||||
function injectPluginSettings(&$data) {
|
||||
function injectPluginSettings(&$data)
|
||||
{
|
||||
$data['settingsObj']->addSysAdminDefaults(Array(
|
||||
'zarafa' => Array(
|
||||
'v1' => Array(
|
||||
'plugins' => Array(
|
||||
'contactimporter' => Array(
|
||||
'enable' => PLUGIN_CONTACTIMPORTER_USER_DEFAULT_ENABLE,
|
||||
'enable_export' => PLUGIN_CONTACTIMPORTER_USER_DEFAULT_ENABLE_EXPORT,
|
||||
'default_addressbook' => PLUGIN_CONTACTIMPORTER_DEFAULT
|
||||
)
|
||||
)
|
||||
@ -78,4 +90,5 @@ class Plugincontactimporter extends Plugin {
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
@ -3,7 +3,7 @@
|
||||
* upload.php, zarafa contact to vcf im/exporter
|
||||
*
|
||||
* 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
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
@ -30,7 +30,8 @@ ini_set('display_errors', '0');
|
||||
* respond/echo JSON
|
||||
* @param $arr
|
||||
*/
|
||||
function respondJSON($arr) {
|
||||
function respondJSON($arr)
|
||||
{
|
||||
echo json_encode($arr);
|
||||
}
|
||||
|
||||
@ -39,7 +40,8 @@ function respondJSON($arr) {
|
||||
* @param $length the lenght of the generated string
|
||||
* @return string a random string
|
||||
*/
|
||||
function randomstring($length = 6) {
|
||||
function randomstring($length = 6)
|
||||
{
|
||||
// $chars - all allowed charakters
|
||||
$chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
|
||||
|
||||
|
@ -1,690 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* vCard class for parsing a vCard and/or creating one
|
||||
*
|
||||
* @link https://github.com/nuovo/vCard-parser
|
||||
* @author Martins Pilsetnieks, Roberts Bruveris
|
||||
* @see RFC 2426, RFC 2425
|
||||
* @version 0.4.8
|
||||
*/
|
||||
|
||||
class vCard implements Countable, Iterator
|
||||
{
|
||||
const MODE_ERROR = 'error';
|
||||
const MODE_SINGLE = 'single';
|
||||
const MODE_MULTIPLE = 'multiple';
|
||||
|
||||
const endl = "\n";
|
||||
|
||||
/**
|
||||
* @var string Current object mode - error, single or multiple (for a single vCard within a file and multiple combined vCards)
|
||||
*/
|
||||
private $Mode; //single, multiple, error
|
||||
|
||||
private $Path = '';
|
||||
private $RawData = '';
|
||||
|
||||
/**
|
||||
* @var array Internal options container. Options:
|
||||
* bool Collapse: If true, elements that can have multiple values but have only a single value are returned as that value instead of an array
|
||||
* If false, an array is returned even if it has only one value.
|
||||
*/
|
||||
private $Options = array(
|
||||
'Collapse' => false
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array Internal data container. Contains vCard objects for multiple vCards and just the data for single vCards.
|
||||
*/
|
||||
private $Data = array();
|
||||
|
||||
/**
|
||||
* @static Parts of structured elements according to the spec.
|
||||
*/
|
||||
private static $Spec_StructuredElements = array(
|
||||
'n' => array('LastName', 'FirstName', 'AdditionalNames', 'Prefixes', 'Suffixes'),
|
||||
'adr' => array('POBox', 'ExtendedAddress', 'StreetAddress', 'Locality', 'Region', 'PostalCode', 'Country'),
|
||||
'geo' => array('Latitude', 'Longitude'),
|
||||
'org' => array('Name', 'Unit1', 'Unit2')
|
||||
);
|
||||
private static $Spec_MultipleValueElements = array('nickname', 'categories');
|
||||
|
||||
private static $Spec_ElementTypes = array(
|
||||
'email' => array('internet', 'x400', 'pref'),
|
||||
'adr' => array('dom', 'intl', 'postal', 'parcel', 'home', 'work', 'pref'),
|
||||
'label' => array('dom', 'intl', 'postal', 'parcel', 'home', 'work', 'pref'),
|
||||
'tel' => array('home', 'msg', 'work', 'pref', 'voice', 'fax', 'cell', 'video', 'pager', 'bbs', 'modem', 'car', 'isdn', 'pcs'),
|
||||
'impp' => array('personal', 'business', 'home', 'work', 'mobile', 'pref')
|
||||
);
|
||||
|
||||
private static $Spec_FileElements = array('photo', 'logo', 'sound');
|
||||
|
||||
/**
|
||||
* vCard constructor
|
||||
*
|
||||
* @param string Path to file, optional.
|
||||
* @param string Raw data, optional.
|
||||
* @param array Additional options, optional. Currently supported options:
|
||||
* bool Collapse: If true, elements that can have multiple values but have only a single value are returned as that value instead of an array
|
||||
* If false, an array is returned even if it has only one value.
|
||||
*
|
||||
* One of these parameters must be provided, otherwise an exception is thrown.
|
||||
*/
|
||||
public function __construct($Path = false, $RawData = false, array $Options = null)
|
||||
{
|
||||
// Checking preconditions for the parser.
|
||||
// If path is given, the file should be accessible.
|
||||
// If raw data is given, it is taken as it is.
|
||||
// In both cases the real content is put in $this -> RawData
|
||||
if ($Path)
|
||||
{
|
||||
if (!is_readable($Path))
|
||||
{
|
||||
throw new Exception('vCard: Path not accessible ('.$Path.')');
|
||||
}
|
||||
|
||||
$this -> Path = $Path;
|
||||
$this -> RawData = file_get_contents($this -> Path);
|
||||
}
|
||||
elseif ($RawData)
|
||||
{
|
||||
$this -> RawData = $RawData;
|
||||
}
|
||||
else
|
||||
{
|
||||
//throw new Exception('vCard: No content provided');
|
||||
// Not necessary anymore as possibility to create vCards is added
|
||||
}
|
||||
|
||||
if (!$this -> Path && !$this -> RawData)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($Options)
|
||||
{
|
||||
$this -> Options = array_merge($this -> Options, $Options);
|
||||
}
|
||||
|
||||
// Counting the begin/end separators. If there aren't any or the count doesn't match, there is a problem with the file.
|
||||
// If there is only one, this is a single vCard, if more, multiple vCards are combined.
|
||||
$Matches = array();
|
||||
$vCardBeginCount = preg_match_all('{^BEGIN\:VCARD}miS', $this -> RawData, $Matches);
|
||||
$vCardEndCount = preg_match_all('{^END\:VCARD}miS', $this -> RawData, $Matches);
|
||||
|
||||
if (($vCardBeginCount != $vCardEndCount) || !$vCardBeginCount)
|
||||
{
|
||||
$this -> Mode = vCard::MODE_ERROR;
|
||||
throw new Exception('vCard: invalid vCard');
|
||||
}
|
||||
|
||||
$this -> Mode = $vCardBeginCount == 1 ? vCard::MODE_SINGLE : vCard::MODE_MULTIPLE;
|
||||
|
||||
// Removing/changing inappropriate newlines, i.e., all CRs or multiple newlines are changed to a single newline
|
||||
$this -> RawData = str_replace("\r", "\n", $this -> RawData);
|
||||
$this -> RawData = preg_replace('{(\n+)}', "\n", $this -> RawData);
|
||||
|
||||
// In multiple card mode the raw text is split at card beginning markers and each
|
||||
// fragment is parsed in a separate vCard object.
|
||||
if ($this -> Mode == self::MODE_MULTIPLE)
|
||||
{
|
||||
$this -> RawData = explode('BEGIN:VCARD', $this -> RawData);
|
||||
$this -> RawData = array_filter($this -> RawData);
|
||||
|
||||
foreach ($this -> RawData as $SinglevCardRawData)
|
||||
{
|
||||
// Prepending "BEGIN:VCARD" to the raw string because we exploded on that one.
|
||||
// If there won't be the BEGIN marker in the new object, it will fail.
|
||||
$SinglevCardRawData = 'BEGIN:VCARD'."\n".$SinglevCardRawData;
|
||||
|
||||
$ClassName = get_class($this);
|
||||
$this -> Data[] = new $ClassName(false, $SinglevCardRawData);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Protect the BASE64 final = sign (detected by the line beginning with whitespace), otherwise the next replace will get rid of it
|
||||
$this -> RawData = preg_replace('{(\n\s.+)=(\n)}', '$1-base64=-$2', $this -> RawData);
|
||||
|
||||
// Joining multiple lines that are split with a hard wrap and indicated by an equals sign at the end of line
|
||||
// (quoted-printable-encoded values in v2.1 vCards)
|
||||
$this -> RawData = str_replace("=\n", '', $this -> RawData);
|
||||
|
||||
// Joining multiple lines that are split with a soft wrap (space or tab on the beginning of the next line
|
||||
$this -> RawData = str_replace(array("\n ", "\n\t"), '-wrap-', $this -> RawData);
|
||||
|
||||
// Restoring the BASE64 final equals sign (see a few lines above)
|
||||
$this -> RawData = str_replace("-base64=-\n", "=\n", $this -> RawData);
|
||||
|
||||
$Lines = explode("\n", $this -> RawData);
|
||||
|
||||
foreach ($Lines as $Line)
|
||||
{
|
||||
// Lines without colons are skipped because, most likely, they contain no data.
|
||||
if (strpos($Line, ':') === false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Each line is split into two parts. The key contains the element name and additional parameters, if present,
|
||||
// value is just the value
|
||||
list($Key, $Value) = explode(':', $Line, 2);
|
||||
|
||||
// Key is transformed to lowercase because, even though the element and parameter names are written in uppercase,
|
||||
// it is quite possible that they will be in lower- or mixed case.
|
||||
// The key is trimmed to allow for non-significant WSP characters as allowed by v2.1
|
||||
$Key = strtolower(trim(self::Unescape($Key)));
|
||||
|
||||
// These two lines can be skipped as they aren't necessary at all.
|
||||
if ($Key == 'begin' || $Key == 'end')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((strpos($Key, 'agent') === 0) && (stripos($Value, 'begin:vcard') !== false))
|
||||
{
|
||||
$ClassName = get_class($this);
|
||||
$Value = new $ClassName(false, str_replace('-wrap-', "\n", $Value));
|
||||
if (!isset($this -> Data[$Key]))
|
||||
{
|
||||
$this -> Data[$Key] = array();
|
||||
}
|
||||
$this -> Data[$Key][] = $Value;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
$Value = str_replace('-wrap-', '', $Value);
|
||||
}
|
||||
|
||||
$Value = trim(self::Unescape($Value));
|
||||
$Type = array();
|
||||
|
||||
// Here additional parameters are parsed
|
||||
$KeyParts = explode(';', $Key);
|
||||
$Key = $KeyParts[0];
|
||||
$Encoding = false;
|
||||
|
||||
if (strpos($Key, 'item') === 0)
|
||||
{
|
||||
$TmpKey = explode('.', $Key, 2);
|
||||
$Key = $TmpKey[1];
|
||||
$ItemIndex = (int)str_ireplace('item', '', $TmpKey[0]);
|
||||
}
|
||||
|
||||
if (count($KeyParts) > 1)
|
||||
{
|
||||
$Parameters = self::ParseParameters($Key, array_slice($KeyParts, 1));
|
||||
|
||||
foreach ($Parameters as $ParamKey => $ParamValue)
|
||||
{
|
||||
switch ($ParamKey)
|
||||
{
|
||||
case 'encoding':
|
||||
$Encoding = $ParamValue;
|
||||
if (in_array($ParamValue, array('b', 'base64')))
|
||||
{
|
||||
//$Value = base64_decode($Value);
|
||||
}
|
||||
elseif ($ParamValue == 'quoted-printable') // v2.1
|
||||
{
|
||||
$Value = quoted_printable_decode($Value);
|
||||
}
|
||||
break;
|
||||
case 'charset': // v2.1
|
||||
if ($ParamValue != 'utf-8' && $ParamValue != 'utf8')
|
||||
{
|
||||
$Value = mb_convert_encoding($Value, 'UTF-8', $ParamValue);
|
||||
}
|
||||
break;
|
||||
case 'type':
|
||||
$Type = $ParamValue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Checking files for colon-separated additional parameters (Apple's Address Book does this), for example, "X-ABCROP-RECTANGLE" for photos
|
||||
if (in_array($Key, self::$Spec_FileElements) && isset($Parameters['encoding']) && in_array($Parameters['encoding'], array('b', 'base64')))
|
||||
{
|
||||
// If colon is present in the value, it must contain Address Book parameters
|
||||
// (colon is an invalid character for base64 so it shouldn't appear in valid files)
|
||||
if (strpos($Value, ':') !== false)
|
||||
{
|
||||
$Value = explode(':', $Value);
|
||||
$Value = array_pop($Value);
|
||||
}
|
||||
}
|
||||
|
||||
// Values are parsed according to their type
|
||||
if (isset(self::$Spec_StructuredElements[$Key]))
|
||||
{
|
||||
$Value = self::ParseStructuredValue($Value, $Key);
|
||||
if ($Type)
|
||||
{
|
||||
$Value['Type'] = $Type;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (in_array($Key, self::$Spec_MultipleValueElements))
|
||||
{
|
||||
$Value = self::ParseMultipleTextValue($Value, $Key);
|
||||
}
|
||||
|
||||
if ($Type)
|
||||
{
|
||||
$Value = array(
|
||||
'Value' => $Value,
|
||||
'Type' => $Type
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_array($Value) && $Encoding)
|
||||
{
|
||||
$Value['Encoding'] = $Encoding;
|
||||
}
|
||||
|
||||
if (!isset($this -> Data[$Key]))
|
||||
{
|
||||
$this -> Data[$Key] = array();
|
||||
}
|
||||
|
||||
$this -> Data[$Key][] = $Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic method to get the various vCard values as object members, e.g.
|
||||
* a call to $vCard -> N gets the "N" value
|
||||
*
|
||||
* @param string Key
|
||||
*
|
||||
* @return mixed Value
|
||||
*/
|
||||
public function __get($Key)
|
||||
{
|
||||
$Key = strtolower($Key);
|
||||
if (isset($this -> Data[$Key]))
|
||||
{
|
||||
if ($Key == 'agent')
|
||||
{
|
||||
return $this -> Data[$Key];
|
||||
}
|
||||
elseif (in_array($Key, self::$Spec_FileElements))
|
||||
{
|
||||
$Value = $this -> Data[$Key];
|
||||
foreach ($Value as $K => $V)
|
||||
{
|
||||
if (stripos($V['Value'], 'uri:') === 0)
|
||||
{
|
||||
$Value[$K]['Value'] = substr($V, 4);
|
||||
$Value[$K]['Encoding'] = 'uri';
|
||||
}
|
||||
}
|
||||
return $Value;
|
||||
}
|
||||
|
||||
if ($this -> Options['Collapse'] && is_array($this -> Data[$Key]) && (count($this -> Data[$Key]) == 1))
|
||||
{
|
||||
return $this -> Data[$Key][0];
|
||||
}
|
||||
return $this -> Data[$Key];
|
||||
}
|
||||
elseif ($Key == 'Mode')
|
||||
{
|
||||
return $this -> Mode;
|
||||
}
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves an embedded file
|
||||
*
|
||||
* @param string Key
|
||||
* @param int Index of the file, defaults to 0
|
||||
* @param string Target path where the file should be saved, including the filename
|
||||
*
|
||||
* @return bool Operation status
|
||||
*/
|
||||
public function SaveFile($Key, $Index = 0, $TargetPath = '')
|
||||
{
|
||||
if (!isset($this -> Data[$Key]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!isset($this -> Data[$Key][$Index]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Returing false if it is an image URL
|
||||
if (stripos($this -> Data[$Key][$Index]['Value'], 'uri:') === 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_writable($TargetPath) || (!file_exists($TargetPath) && is_writable(dirname($TargetPath))))
|
||||
{
|
||||
$RawContent = $this -> Data[$Key][$Index]['Value'];
|
||||
if (isset($this -> Data[$Key][$Index]['Encoding']) && $this -> Data[$Key][$Index]['Encoding'] == 'b')
|
||||
{
|
||||
$RawContent = base64_decode($RawContent);
|
||||
}
|
||||
$Status = file_put_contents($TargetPath, $RawContent);
|
||||
return (bool)$Status;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception('vCard: Cannot save file ('.$Key.'), target path not writable ('.$TargetPath.')');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic method for adding data to the vCard
|
||||
*
|
||||
* @param string Key
|
||||
* @param string Method call arguments. First element is value.
|
||||
*
|
||||
* @return vCard Current object for method chaining
|
||||
*/
|
||||
public function __call($Key, $Arguments)
|
||||
{
|
||||
$Key = strtolower($Key);
|
||||
|
||||
if (!isset($this -> Data[$Key]))
|
||||
{
|
||||
$this -> Data[$Key] = array();
|
||||
}
|
||||
|
||||
$Value = isset($Arguments[0]) ? $Arguments[0] : false;
|
||||
|
||||
if (count($Arguments) > 1)
|
||||
{
|
||||
$Types = array_values(array_slice($Arguments, 1));
|
||||
|
||||
if (isset(self::$Spec_StructuredElements[$Key]) &&
|
||||
in_array($Arguments[1], self::$Spec_StructuredElements[$Key])
|
||||
)
|
||||
{
|
||||
$LastElementIndex = 0;
|
||||
|
||||
if (count($this -> Data[$Key]))
|
||||
{
|
||||
$LastElementIndex = count($this -> Data[$Key]) - 1;
|
||||
}
|
||||
|
||||
if (isset($this -> Data[$Key][$LastElementIndex]))
|
||||
{
|
||||
if (empty($this -> Data[$Key][$LastElementIndex][$Types[0]]))
|
||||
{
|
||||
$this -> Data[$Key][$LastElementIndex][$Types[0]] = $Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
$LastElementIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($this -> Data[$Key][$LastElementIndex]))
|
||||
{
|
||||
$this -> Data[$Key][$LastElementIndex] = array(
|
||||
$Types[0] => $Value
|
||||
);
|
||||
}
|
||||
}
|
||||
elseif (isset(self::$Spec_ElementTypes[$Key]))
|
||||
{
|
||||
$this -> Data[$Key][] = array(
|
||||
'Value' => $Value,
|
||||
'Type' => $Types
|
||||
);
|
||||
}
|
||||
}
|
||||
elseif ($Value)
|
||||
{
|
||||
$this -> Data[$Key][] = $Value;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic method for getting vCard content out
|
||||
*
|
||||
* @return string Raw vCard content
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
$Text = 'BEGIN:VCARD'.self::endl;
|
||||
$Text .= 'VERSION:3.0'.self::endl;
|
||||
|
||||
foreach ($this -> Data as $Key => $Values)
|
||||
{
|
||||
$KeyUC = strtoupper($Key);
|
||||
$Key = strtolower($Key);
|
||||
|
||||
if (in_array($KeyUC, array('PHOTO', 'VERSION')))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($Values as $Index => $Value)
|
||||
{
|
||||
$Text .= $KeyUC;
|
||||
if (is_array($Value) && isset($Value['Type']))
|
||||
{
|
||||
$Text .= ';TYPE='.self::PrepareTypeStrForOutput($Value['Type']);
|
||||
}
|
||||
$Text .= ':';
|
||||
|
||||
if (isset(self::$Spec_StructuredElements[$Key]))
|
||||
{
|
||||
$PartArray = array();
|
||||
foreach (self::$Spec_StructuredElements[$Key] as $Part)
|
||||
{
|
||||
$PartArray[] = isset($Value[$Part]) ? $Value[$Part] : '';
|
||||
}
|
||||
$Text .= implode(';', $PartArray);
|
||||
}
|
||||
elseif (is_array($Value) && isset(self::$Spec_ElementTypes[$Key]))
|
||||
{
|
||||
$Text .= $Value['Value'];
|
||||
}
|
||||
else
|
||||
{
|
||||
$Text .= $Value;
|
||||
}
|
||||
|
||||
$Text .= self::endl;
|
||||
}
|
||||
}
|
||||
|
||||
$Text .= 'END:VCARD'.self::endl;
|
||||
return $Text;
|
||||
}
|
||||
|
||||
// !Helper methods
|
||||
|
||||
private static function PrepareTypeStrForOutput($Type)
|
||||
{
|
||||
return implode(',', array_map('strtoupper', $Type));
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the escaping slashes from the text.
|
||||
*
|
||||
* @access private
|
||||
*
|
||||
* @param string Text to prepare.
|
||||
*
|
||||
* @return string Resulting text.
|
||||
*/
|
||||
private static function Unescape($Text)
|
||||
{
|
||||
return str_replace(array('\:', '\;', '\,', "\n"), array(':', ';', ',', ''), $Text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Separates the various parts of a structured value according to the spec.
|
||||
*
|
||||
* @access private
|
||||
*
|
||||
* @param string Raw text string
|
||||
* @param string Key (e.g., N, ADR, ORG, etc.)
|
||||
*
|
||||
* @return array Parts in an associative array.
|
||||
*/
|
||||
private static function ParseStructuredValue($Text, $Key)
|
||||
{
|
||||
$Text = array_map('trim', explode(';', $Text));
|
||||
|
||||
$Result = array();
|
||||
$Ctr = 0;
|
||||
|
||||
foreach (self::$Spec_StructuredElements[$Key] as $Index => $StructurePart)
|
||||
{
|
||||
$Result[$StructurePart] = isset($Text[$Index]) ? $Text[$Index] : null;
|
||||
}
|
||||
return $Result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @access private
|
||||
*/
|
||||
private static function ParseMultipleTextValue($Text)
|
||||
{
|
||||
return explode(',', $Text);
|
||||
}
|
||||
|
||||
/**
|
||||
* @access private
|
||||
*/
|
||||
private static function ParseParameters($Key, array $RawParams = null)
|
||||
{
|
||||
if (!$RawParams)
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
// Parameters are split into (key, value) pairs
|
||||
$Parameters = array();
|
||||
foreach ($RawParams as $Item)
|
||||
{
|
||||
$Parameters[] = explode('=', strtolower($Item));
|
||||
}
|
||||
|
||||
$Type = array();
|
||||
$Result = array();
|
||||
|
||||
// And each parameter is checked whether anything can/should be done because of it
|
||||
foreach ($Parameters as $Index => $Parameter)
|
||||
{
|
||||
// Skipping empty elements
|
||||
if (!$Parameter)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handling type parameters without the explicit TYPE parameter name (2.1 valid)
|
||||
if (count($Parameter) == 1)
|
||||
{
|
||||
// Checks if the type value is allowed for the specific element
|
||||
// The second part of the "if" statement means that email elements can have non-standard types (see the spec)
|
||||
if (
|
||||
(isset(self::$Spec_ElementTypes[$Key]) && in_array($Parameter[0], self::$Spec_ElementTypes[$Key])) ||
|
||||
($Key == 'email' && is_scalar($Parameter[0]))
|
||||
)
|
||||
{
|
||||
$Type[] = $Parameter[0];
|
||||
}
|
||||
}
|
||||
elseif (count($Parameter) > 2)
|
||||
{
|
||||
$TempTypeParams = self::ParseParameters($Key, explode(',', $RawParams[$Index]));
|
||||
if ($TempTypeParams['type'])
|
||||
{
|
||||
$Type = array_merge($Type, $TempTypeParams['type']);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch ($Parameter[0])
|
||||
{
|
||||
case 'encoding':
|
||||
if (in_array($Parameter[1], array('quoted-printable', 'b', 'base64')))
|
||||
{
|
||||
$Result['encoding'] = $Parameter[1] == 'base64' ? 'b' : $Parameter[1];
|
||||
}
|
||||
break;
|
||||
case 'charset':
|
||||
$Result['charset'] = $Parameter[1];
|
||||
break;
|
||||
case 'type':
|
||||
$Type = array_merge($Type, explode(',', $Parameter[1]));
|
||||
break;
|
||||
case 'value':
|
||||
if (strtolower($Parameter[1]) == 'url')
|
||||
{
|
||||
$Result['encoding'] = 'uri';
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$Result['type'] = $Type;
|
||||
|
||||
return $Result;
|
||||
}
|
||||
|
||||
// !Interface methods
|
||||
|
||||
// Countable interface
|
||||
public function count()
|
||||
{
|
||||
switch ($this -> Mode)
|
||||
{
|
||||
case self::MODE_ERROR:
|
||||
return 0;
|
||||
break;
|
||||
case self::MODE_SINGLE:
|
||||
return 1;
|
||||
break;
|
||||
case self::MODE_MULTIPLE:
|
||||
return count($this -> Data);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Iterator interface
|
||||
public function rewind()
|
||||
{
|
||||
reset($this -> Data);
|
||||
}
|
||||
|
||||
public function current()
|
||||
{
|
||||
return current($this -> Data);
|
||||
}
|
||||
|
||||
public function next()
|
||||
{
|
||||
return next($this -> Data);
|
||||
}
|
||||
|
||||
public function valid()
|
||||
{
|
||||
return ($this -> current() !== false);
|
||||
}
|
||||
|
||||
public function key()
|
||||
{
|
||||
return key($this -> Data);
|
||||
}
|
||||
}
|
||||
?>
|
@ -2,5 +2,29 @@
|
||||
background: url(../images/import_icon.png) no-repeat !important;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
background-size: 18px!important;
|
||||
}
|
||||
|
||||
.icon_contactimporter_export {
|
||||
background: url(../images/download.png) no-repeat;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.icon_contactimporter_import {
|
||||
background: url(../images/upload.png) no-repeat;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.zarafa-ciplg-container {
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.zarafa-ciplg-button .x-btn-small {
|
||||
width: 80%;
|
||||
height: 30px;
|
||||
margin-left: 10%;
|
||||
margin-right: 10%;
|
||||
margin-top: 10px;
|
||||
}
|
BIN
resources/images/download.png
Executable file
BIN
resources/images/download.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 224 B |
BIN
resources/images/download.xcf
Executable file
BIN
resources/images/download.xcf
Executable file
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 926 B After Width: | Height: | Size: 407 B |
BIN
resources/images/upload.png
Normal file
BIN
resources/images/upload.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 213 B |
BIN
resources/images/upload.xcf
Executable file
BIN
resources/images/upload.xcf
Executable file
Binary file not shown.
Loading…
Reference in New Issue
Block a user