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>
|
202
build.xml
202
build.xml
@ -1,18 +1,15 @@
|
|||||||
<project default="all">
|
<project default="all">
|
||||||
<!--############# CONFIGURE ALL PROPERTIES FOR THE REPLACER HERE ################-->
|
<property environment="env"/>
|
||||||
<property name="plugin_version" value="1.0.3"/>
|
<property name="root-folder" value="${basedir}/../../"/>
|
||||||
<!-- EOC -->
|
<property name="tools-folder" value="${root-folder}/tools/"/>
|
||||||
|
<property name="target-folder" value="${root-folder}/deploy/plugins"/>
|
||||||
<property name="root-folder" value="${basedir}/../"/>
|
<property name="server-folder" value="${root-folder}/server"/>
|
||||||
<property name="tools-folder" value="${root-folder}/TOOLS/"/>
|
|
||||||
<property name="target-folder" value="${root-folder}/DEPLOY/plugins"/>
|
|
||||||
|
|
||||||
<import file="${tools-folder}/antutil.xml"/>
|
<import file="${tools-folder}/antutil.xml"/>
|
||||||
|
|
||||||
<typedef file="${tools-folder}/antlib.xml">
|
<typedef file="${tools-folder}/antlib.xml">
|
||||||
<classpath>
|
<classpath>
|
||||||
<pathelement location="${tools-folder}/tools.jar"/>
|
<pathelement location="${tools-folder}/tools.jar"/>
|
||||||
<pathelement location="${tools-folder}/lib/compiler.jar"/>
|
|
||||||
</classpath>
|
</classpath>
|
||||||
</typedef>
|
</typedef>
|
||||||
|
|
||||||
@ -21,30 +18,8 @@
|
|||||||
<pathelement location="${tools-folder}/lib/ant-contrib-1.0b3.jar"/>
|
<pathelement location="${tools-folder}/lib/ant-contrib-1.0b3.jar"/>
|
||||||
</classpath>
|
</classpath>
|
||||||
</taskdef>
|
</taskdef>
|
||||||
|
|
||||||
<!-- os checks for xmllint... -->
|
|
||||||
<condition property="isWindows" value="true">
|
|
||||||
<os family="windows" />
|
|
||||||
</condition>
|
|
||||||
|
|
||||||
<!-- define nicknames for libraries -->
|
|
||||||
<property name="yui-compressor" location="${tools-folder}/lib/yuicompressor-2.4.2.jar" />
|
|
||||||
<property name="yui-compressor-ant-task" location="${tools-folder}/lib/yui-compressor-ant-task-0.5.jar" />
|
|
||||||
|
|
||||||
<!-- adds libraries to the classpath -->
|
|
||||||
<path id="yui.classpath">
|
|
||||||
<pathelement location="${yui-compressor}" />
|
|
||||||
<pathelement location="${yui-compressor-ant-task}" />
|
|
||||||
</path>
|
|
||||||
|
|
||||||
<!-- define tasks -->
|
|
||||||
<taskdef name="yui-compressor" classname="net.noha.tools.ant.yuicompressor.tasks.YuiCompressorTask">
|
|
||||||
<classpath refid="yui.classpath" />
|
|
||||||
</taskdef>
|
|
||||||
|
|
||||||
|
|
||||||
<!-- Determine plugin name -->
|
<!-- Determine plugin name -->
|
||||||
<var name="plugin" unset="true"/>
|
|
||||||
<basename file="${basedir}" property="plugin"/>
|
<basename file="${basedir}" property="plugin"/>
|
||||||
|
|
||||||
<!-- The Plugin distribution files -->
|
<!-- The Plugin distribution files -->
|
||||||
@ -54,8 +29,7 @@
|
|||||||
|
|
||||||
<!-- The Plugin CSS files -->
|
<!-- The Plugin CSS files -->
|
||||||
<property name="plugin-css-folder" value="resources/css"/>
|
<property name="plugin-css-folder" value="resources/css"/>
|
||||||
<property name="plugin-css-file" value="${plugin}-min.css"/>
|
<property name="plugin-css-file" value="${plugin}.css"/>
|
||||||
<property name="plugin-css-debug-file" value="${plugin}.css"/>
|
|
||||||
|
|
||||||
<!-- Meta target -->
|
<!-- Meta target -->
|
||||||
<target name="all" depends="concat, compress"/>
|
<target name="all" depends="concat, compress"/>
|
||||||
@ -68,16 +42,6 @@
|
|||||||
<include name="${plugin-file}"/>
|
<include name="${plugin-file}"/>
|
||||||
<include name="${plugin-debugfile}"/>
|
<include name="${plugin-debugfile}"/>
|
||||||
</fileset>
|
</fileset>
|
||||||
<fileset dir="${target-folder}/${plugin-folder}/php">
|
|
||||||
<include name="**/*.php"/>
|
|
||||||
</fileset>
|
|
||||||
<fileset dir="${target-folder}/${plugin-folder}/resources">
|
|
||||||
<include name="**/*"/>
|
|
||||||
</fileset>
|
|
||||||
<fileset dir="${target-folder}/${plugin-folder}/${plugin-css-folder}">
|
|
||||||
<include name="${plugin-css-debug-file}"/>
|
|
||||||
<include name="${plugin-css-file}"/>
|
|
||||||
</fileset>
|
|
||||||
</delete>
|
</delete>
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
@ -89,19 +53,11 @@
|
|||||||
<then>
|
<then>
|
||||||
<mkdir dir="${target-folder}/${plugin-folder}/js"/>
|
<mkdir dir="${target-folder}/${plugin-folder}/js"/>
|
||||||
<echo message="Concatenating: ${plugin-debugfile}"/>
|
<echo message="Concatenating: ${plugin-debugfile}"/>
|
||||||
<!-- TODO: fix JS files for zConcat -->
|
<zConcat outputFolder="${target-folder}/${plugin-folder}/js" outputFile="${plugin-debugfile}" prioritize="\w+">
|
||||||
<!--zConcat outputFolder="${target-folder}/${plugin-folder}/js" outputFile="${plugin-debugfile}" prioritize="\w+">
|
|
||||||
<concatfiles>
|
<concatfiles>
|
||||||
<fileset dir="js" includes="**/*.js" />
|
<fileset dir="js" includes="**/*.js" />
|
||||||
</concatfiles>
|
</concatfiles>
|
||||||
</zConcat-->
|
</zConcat>
|
||||||
<concat destfile="${target-folder}/${plugin-folder}/js/${plugin-debugfile}">
|
|
||||||
<fileset file="js/ABOUT.js" />
|
|
||||||
<fileset file="js/plugin.contactimporter.js" />
|
|
||||||
<fileset file="js/data/ResponseHandler.js" />
|
|
||||||
<fileset file="js/dialogs/ImportContentPanel.js" />
|
|
||||||
<fileset file="js/dialogs/ImportPanel.js" />
|
|
||||||
</concat>
|
|
||||||
</then>
|
</then>
|
||||||
</if>
|
</if>
|
||||||
|
|
||||||
@ -110,8 +66,8 @@
|
|||||||
<available file="${plugin-css-folder}" type="dir" />
|
<available file="${plugin-css-folder}" type="dir" />
|
||||||
<then>
|
<then>
|
||||||
<mkdir dir="${target-folder}/${plugin-folder}/${plugin-css-folder}"/>
|
<mkdir dir="${target-folder}/${plugin-folder}/${plugin-css-folder}"/>
|
||||||
<echo message="Concatenating: ${plugin-css-debug-file}"/>
|
<echo message="Concatenating: ${plugin-css-file}"/>
|
||||||
<zConcat outputFolder="${target-folder}/${plugin-folder}/${plugin-css-folder}" outputFile="${plugin-css-debug-file}">
|
<zConcat outputFolder="${target-folder}/${plugin-folder}/${plugin-css-folder}" outputFile="${plugin-css-file}">
|
||||||
<concatfiles>
|
<concatfiles>
|
||||||
<fileset dir="${plugin-css-folder}" includes="**/*.css" />
|
<fileset dir="${plugin-css-folder}" includes="**/*.css" />
|
||||||
</concatfiles>
|
</concatfiles>
|
||||||
@ -155,16 +111,7 @@
|
|||||||
var npgettext = function(msgctxt, msgid, msgid_plural, count) {};
|
var npgettext = function(msgctxt, msgid, msgid_plural, count) {};
|
||||||
var pgettext = function(msgctxt, msgid) {};
|
var pgettext = function(msgctxt, msgid) {};
|
||||||
</externs>
|
</externs>
|
||||||
</zCompile>
|
</zCompile>
|
||||||
<!--yui-compressor
|
|
||||||
warn="false"
|
|
||||||
munge="true"
|
|
||||||
preserveallsemicolons="false"
|
|
||||||
fromdir="${target-folder}/${plugin-folder}/js"
|
|
||||||
todir="${target-folder}/${plugin-folder}/js">
|
|
||||||
|
|
||||||
<include name="${plugin-debugfile}" />
|
|
||||||
</yui-compressor-->
|
|
||||||
</then>
|
</then>
|
||||||
</if>
|
</if>
|
||||||
</target>
|
</target>
|
||||||
@ -172,105 +119,82 @@
|
|||||||
<!-- syntax check all PHP files -->
|
<!-- syntax check all PHP files -->
|
||||||
<target name="validate">
|
<target name="validate">
|
||||||
<if>
|
<if>
|
||||||
<available file="config.php" type="file" />
|
<available file="php" filepath="${env.PATH}" />
|
||||||
<then>
|
<then>
|
||||||
<antcall target="syntax-check">
|
<if>
|
||||||
<param name="file" value="config.php"/>
|
<available file="config.php" type="file" />
|
||||||
</antcall>
|
<then>
|
||||||
</then>
|
<antcall target="syntax-check">
|
||||||
</if>
|
<param name="file" value="config.php"/>
|
||||||
<if>
|
</antcall>
|
||||||
<available file="php" type="dir" />
|
</then>
|
||||||
<then>
|
</if>
|
||||||
<foreach target="syntax-check" param="file">
|
<if>
|
||||||
<path>
|
<available file="php" type="dir" />
|
||||||
<fileset dir=".">
|
<then>
|
||||||
<include name="**/*.php"/>
|
<foreach target="syntax-check" param="file">
|
||||||
</fileset>
|
<path>
|
||||||
</path>
|
<fileset dir=".">
|
||||||
</foreach>
|
<exclude name="php/vendor/**" />
|
||||||
|
<include name="**/*.php"/>
|
||||||
|
</fileset>
|
||||||
|
</path>
|
||||||
|
</foreach>
|
||||||
|
</then>
|
||||||
|
</if>
|
||||||
</then>
|
</then>
|
||||||
|
<else>
|
||||||
|
<echo message="WARNING: PHP not available, not performing syntax-check on php files"/>
|
||||||
|
</else>
|
||||||
</if>
|
</if>
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
<target name="syntax-check">
|
<target name="syntax-check">
|
||||||
<echo message="validating ${file}"/>
|
<echo message="validating ${file}"/>
|
||||||
<exec executable="php" failonerror="true" failifexecutionfails="false">
|
<exec executable="php" failonerror="true">
|
||||||
<arg value="-l"/>
|
<arg value="-l"/>
|
||||||
<arg value="${file}"/>
|
<arg value="${file}"/>
|
||||||
</exec>
|
</exec>
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
<!-- on windows we do not check the xml file -->
|
|
||||||
<target name="xml-os-sel" depends="xml-check,xml-copy">
|
|
||||||
<echo>Processing manifest.xml</echo>
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<!-- check manifest.xml if we are on windows... -->
|
|
||||||
<target name="xml-check" unless="isWindows">
|
|
||||||
<echo message="Checking xml: manifest.xml" />
|
|
||||||
<!-- Copy (and validate) manifest.xml -->
|
|
||||||
<exec executable="xmllint" output="${target-folder}/${plugin-folder}/manifest.xml" failonerror="true" error="/dev/stdout" failifexecutionfails="false">
|
|
||||||
<arg value="--valid"/>
|
|
||||||
<arg value="--path"/>
|
|
||||||
<arg value="${root-folder}/server"/>
|
|
||||||
<arg value="manifest.xml"/>
|
|
||||||
</exec>
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<!-- check manifest.xml if we are on windows... -->
|
|
||||||
<target name="xml-copy" if="isWindows">
|
|
||||||
<echo message="Copying xml: manifest.xml" />
|
|
||||||
<!-- Copy manifest.xml -->
|
|
||||||
<copy todir="${target-folder}/${plugin-folder}">
|
|
||||||
<fileset dir=".">
|
|
||||||
<include name="manifest.xml"/>
|
|
||||||
</fileset>
|
|
||||||
</copy>
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<!-- Install all files into the target folder -->
|
<!-- Install all files into the target folder -->
|
||||||
<target name="deploy" depends="clean, compress, compresscss, validate, xml-os-sel">
|
<target name="deploy" depends="compress, validate">
|
||||||
<mkdir dir="${target-folder}/${plugin-folder}"/>
|
<mkdir dir="${target-folder}/${plugin-folder}"/>
|
||||||
|
|
||||||
|
<!-- Copy (and validate) manifest.xml -->
|
||||||
|
<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="${server-folder}"/>
|
||||||
|
<arg value="manifest.xml"/>
|
||||||
|
</exec>
|
||||||
|
</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>
|
||||||
|
</else>
|
||||||
|
</if>
|
||||||
|
|
||||||
<!-- copy files -->
|
<!-- copy files -->
|
||||||
<copy todir="${target-folder}/${plugin-folder}">
|
<copy todir="${target-folder}/${plugin-folder}">
|
||||||
<fileset dir=".">
|
<fileset dir=".">
|
||||||
<include name="resources/**/*"/>
|
<include name="resources/**/*.*"/>
|
||||||
|
<include name="external/**/*.*"/>
|
||||||
<include name="php/**/*.php"/>
|
<include name="php/**/*.php"/>
|
||||||
<include name="config.php"/>
|
<include name="config.php"/>
|
||||||
<include name="changelog.txt"/>
|
|
||||||
<!-- exclude the ant script -->
|
<!-- exclude the ant script -->
|
||||||
<exclude name="build.xml"/>
|
<exclude name="build.xml"/>
|
||||||
<!-- CSS is generated during build -->
|
<!-- CSS is generated during build -->
|
||||||
<exclude name="resources/css/*.*"/>
|
<exclude name="resources/css/*.*"/>
|
||||||
</fileset>
|
</fileset>
|
||||||
</copy>
|
</copy>
|
||||||
|
|
||||||
<!-- replace all variables... -->
|
|
||||||
<replace file="${target-folder}/${plugin-folder}/manifest.xml" token="@_@PLUGIN_VERSION@_@" value="${plugin_version}" />
|
|
||||||
</target>
|
</target>
|
||||||
|
</project>
|
||||||
<!-- 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>
|
|
18
config.php
18
config.php
@ -1,12 +1,10 @@
|
|||||||
<?php
|
<?php
|
||||||
/** Disable the import plugin for all clients */
|
/** Disable the import plugin for all clients */
|
||||||
define('PLUGIN_CONTACTIMPORTER_USER_DEFAULT_ENABLE', false);
|
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: Kontakte or Contacts - depending on your language)*/
|
||||||
|
define('PLUGIN_CONTACTIMPORTER_DEFAULT', "Kontakte");
|
||||||
/** The default addressbook to import to (default: contact)*/
|
|
||||||
define('PLUGIN_CONTACTIMPORTER_DEFAULT', "contact");
|
/** Tempory path for uploaded files... */
|
||||||
|
define('PLUGIN_CONTACTIMPORTER_TMP_UPLOAD', "/var/lib/zarafa-webapp/tmp/");
|
||||||
/** Tempory path for uploaded files... */
|
|
||||||
define('PLUGIN_CONTACTIMPORTER_TMP_UPLOAD', "/var/lib/zarafa-webapp/tmp/");
|
|
||||||
?>
|
?>
|
||||||
|
44
js/ABOUT.js
44
js/ABOUT.js
@ -2,7 +2,7 @@
|
|||||||
* ABOUT.js zarafa contact to vcf im/exporter
|
* ABOUT.js zarafa contact to vcf im/exporter
|
||||||
*
|
*
|
||||||
* Author: Christoph Haas <christoph.h@sprinternet.at>
|
* Author: Christoph Haas <christoph.h@sprinternet.at>
|
||||||
* Copyright (C) 2012-2013 Christoph Haas
|
* Copyright (C) 2012-2016 Christoph Haas
|
||||||
*
|
*
|
||||||
* This library is free software; you can redistribute it and/or
|
* This library is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
@ -19,7 +19,7 @@
|
|||||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Ext.namespace('Zarafa.plugins.contactimporter');
|
Ext.namespace('Zarafa.plugins.contactimporter');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -29,30 +29,30 @@ Ext.namespace('Zarafa.plugins.contactimporter');
|
|||||||
* The copyright string holding the copyright notice for the Zarafa contactimporter Plugin.
|
* The copyright string holding the copyright notice for the Zarafa contactimporter Plugin.
|
||||||
*/
|
*/
|
||||||
Zarafa.plugins.contactimporter.ABOUT = ""
|
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 "
|
+ "<p>This program is free software; you can redistribute it and/or "
|
||||||
+ "modify it under the terms of the GNU Lesser General Public "
|
+ "modify it under the terms of the GNU Lesser General Public "
|
||||||
+ "License as published by the Free Software Foundation; either "
|
+ "License as published by the Free Software Foundation; either "
|
||||||
+ "version 2.1 of the License, or (at your option) any later version.</p>"
|
+ "version 2.1 of the License, or (at your option) any later version.</p>"
|
||||||
|
|
||||||
+ "<p>This program is distributed in the hope that it will be useful, "
|
+ "<p>This program is distributed in the hope that it will be useful, "
|
||||||
+ "but WITHOUT ANY WARRANTY; without even the implied warranty of "
|
+ "but WITHOUT ANY WARRANTY; without even the implied warranty of "
|
||||||
+ "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU "
|
+ "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU "
|
||||||
+ "Lesser General Public License for more details.</p>"
|
+ "Lesser General Public License for more details.</p>"
|
||||||
|
|
||||||
+ "<p>You should have received a copy of the GNU Lesser General Public "
|
+ "<p>You should have received a copy of the GNU Lesser General Public "
|
||||||
+ "License along with this program; if not, write to the Free Software "
|
+ "License along with this program; if not, write to the Free Software "
|
||||||
+ "Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA</p>"
|
+ "Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA</p>"
|
||||||
|
|
||||||
+ "<hr />"
|
+ "<hr />"
|
||||||
|
|
||||||
+ "<p>The contactimporter plugin contains the following third-party components:</p>"
|
+ "<p>The contactimporter plugin contains the following third-party components:</p>"
|
||||||
|
|
||||||
+ "<h1>vCard-parser</h1>"
|
|
||||||
|
|
||||||
+ "<p>Copyright (C) 2012 Nuovo</p>"
|
+ "<h1>vCard-parser</h1>"
|
||||||
|
|
||||||
+ "<p>Licensed under the MIT License.</p>"
|
+ "<p>Copyright (C) 2016 Jeroen Desloovere</p>"
|
||||||
|
|
||||||
+ "<p>Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.</p>"
|
+ "<p>Licensed under the MIT License.</p>"
|
||||||
|
|
||||||
|
+ "<p>Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.</p>"
|
@ -2,7 +2,7 @@
|
|||||||
* ResponseHandler.js zarafa contact im/exporter
|
* ResponseHandler.js zarafa contact im/exporter
|
||||||
*
|
*
|
||||||
* Author: Christoph Haas <christoph.h@sprinternet.at>
|
* Author: Christoph Haas <christoph.h@sprinternet.at>
|
||||||
* Copyright (C) 2012-2013 Christoph Haas
|
* Copyright (C) 2012-2016 Christoph Haas
|
||||||
*
|
*
|
||||||
* This library is free software; you can redistribute it and/or
|
* This library is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
@ -19,7 +19,7 @@
|
|||||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ResponseHandler
|
* ResponseHandler
|
||||||
*
|
*
|
||||||
@ -38,38 +38,46 @@ Zarafa.plugins.contactimporter.data.ResponseHandler = Ext.extend(Zarafa.core.dat
|
|||||||
* @cfg {Function} successCallback The function which
|
* @cfg {Function} successCallback The function which
|
||||||
* will be called after success request.
|
* will be called after success request.
|
||||||
*/
|
*/
|
||||||
successCallback : null,
|
successCallback: null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Call the successCallback callback function.
|
* Call the successCallback callback function.
|
||||||
* @param {Object} response Object contained the response data.
|
* @param {Object} response Object contained the response data.
|
||||||
*/
|
*/
|
||||||
doLoad : function(response) {
|
doLoad: function (response) {
|
||||||
this.successCallback(response);
|
this.successCallback(response);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Call the successCallback callback function.
|
* Call the successCallback callback function.
|
||||||
* @param {Object} response Object contained the response data.
|
* @param {Object} response Object contained the response data.
|
||||||
*/
|
*/
|
||||||
doImport : function(response) {
|
doImport: function (response) {
|
||||||
this.successCallback(response);
|
this.successCallback(response);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Call the successCallback callback function.
|
* Call the successCallback callback function.
|
||||||
* @param {Object} response Object contained the response data.
|
* @param {Object} response Object contained the response data.
|
||||||
*/
|
*/
|
||||||
doImportattachment : function(response) {
|
doExport: function (response) {
|
||||||
this.successCallback(response);
|
this.successCallback(response);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call the successCallback callback function.
|
||||||
|
* @param {Object} response Object contained the response data.
|
||||||
|
*/
|
||||||
|
doImportattachment: function (response) {
|
||||||
|
this.successCallback(response);
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* In case exception happened on server, server will return
|
* In case exception happened on server, server will return
|
||||||
* exception response with the code of exception.
|
* exception response with the code of exception.
|
||||||
* @param {Object} response Object contained the response data.
|
* @param {Object} response Object contained the response data.
|
||||||
*/
|
*/
|
||||||
doError: function(response) {
|
doError: function (response) {
|
||||||
alert("error response code: " + response.error.info.code);
|
alert("error response code: " + response.error.info.code);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
* ImportContentPanel.js zarafa contact to vcf im/exporter
|
* ImportContentPanel.js zarafa contact to vcf im/exporter
|
||||||
*
|
*
|
||||||
* Author: Christoph Haas <christoph.h@sprinternet.at>
|
* Author: Christoph Haas <christoph.h@sprinternet.at>
|
||||||
* Copyright (C) 2012-2013 Christoph Haas
|
* Copyright (C) 2012-2016 Christoph Haas
|
||||||
*
|
*
|
||||||
* This library is free software; you can redistribute it and/or
|
* This library is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
@ -19,13 +19,13 @@
|
|||||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ImportContentPanel
|
* ImportContentPanel
|
||||||
*
|
*
|
||||||
* Container for the importpanel.
|
* Container for the importpanel.
|
||||||
*/
|
*/
|
||||||
Ext.namespace("Zarafa.plugins.contactimporter.dialogs");
|
Ext.namespace("Zarafa.plugins.contactimporter.dialogs");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class Zarafa.plugins.contactimporter.dialogs.ImportContentPanel
|
* @class Zarafa.plugins.contactimporter.dialogs.ImportContentPanel
|
||||||
@ -40,23 +40,21 @@ Zarafa.plugins.contactimporter.dialogs.ImportContentPanel = Ext.extend(Zarafa.co
|
|||||||
* @constructor
|
* @constructor
|
||||||
* @param config Configuration structure
|
* @param config Configuration structure
|
||||||
*/
|
*/
|
||||||
constructor : function(config) {
|
constructor: function (config) {
|
||||||
config = config || {};
|
config = config || {};
|
||||||
var title = _('Import Contacts');
|
var title = _('Import Contacts');
|
||||||
if(container.getSettingsModel().get("zarafa/v1/plugins/contactimporter/enable_export")){
|
|
||||||
title = _('Import/Export Contacts');
|
|
||||||
}
|
|
||||||
Ext.applyIf(config, {
|
Ext.applyIf(config, {
|
||||||
layout : 'fit',
|
layout : 'fit',
|
||||||
title : title,
|
title : title,
|
||||||
closeOnSave : true,
|
closeOnSave: true,
|
||||||
width : 620,
|
width : 620,
|
||||||
height : 465,
|
height : 465,
|
||||||
//Add panel
|
//Add panel
|
||||||
items : [
|
items : [
|
||||||
{
|
{
|
||||||
xtype : 'contactimporter.importcontactpanel',
|
xtype : 'contactimporter.importcontactpanel',
|
||||||
filename : config.filename
|
filename: config.filename,
|
||||||
|
folder : config.folder
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
@ -66,4 +64,4 @@ Zarafa.plugins.contactimporter.dialogs.ImportContentPanel = Ext.extend(Zarafa.co
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Ext.reg('contactimporter.contentpanel' ,Zarafa.plugins.contactimporter.dialogs.ImportContentPanel);
|
Ext.reg('contactimporter.contentpanel', Zarafa.plugins.contactimporter.dialogs.ImportContentPanel);
|
@ -2,7 +2,7 @@
|
|||||||
* ImportPanel.js zarafa contact to vcf im/exporter
|
* ImportPanel.js zarafa contact to vcf im/exporter
|
||||||
*
|
*
|
||||||
* Author: Christoph Haas <christoph.h@sprinternet.at>
|
* Author: Christoph Haas <christoph.h@sprinternet.at>
|
||||||
* Copyright (C) 2012-2013 Christoph Haas
|
* Copyright (C) 2012-2016 Christoph Haas
|
||||||
*
|
*
|
||||||
* This library is free software; you can redistribute it and/or
|
* This library is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
@ -13,7 +13,7 @@
|
|||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
* Lesser General Public License for more details.
|
* Lesser General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU Lesser General Public
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
* License along with this library; if not, write to the Free Software
|
* License along with this library; if not, write to the Free Software
|
||||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
@ -25,32 +25,39 @@
|
|||||||
*
|
*
|
||||||
* The main Panel of the contactimporter plugin.
|
* The main Panel of the contactimporter plugin.
|
||||||
*/
|
*/
|
||||||
Ext.namespace("Zarafa.plugins.contactimporter.dialogs");
|
Ext.namespace("Zarafa.plugins.contactimporter.dialogs");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class Zarafa.plugins.contactimporter.dialogs.ImportPanel
|
* @class Zarafa.plugins.contactimporter.dialogs.ImportPanel
|
||||||
* @extends Ext.form.FormPanel
|
* @extends Ext.Panel
|
||||||
*/
|
*/
|
||||||
Zarafa.plugins.contactimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
|
Zarafa.plugins.contactimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
|
||||||
|
|
||||||
/* path to vcf file on server... */
|
/* path to vcf file on server... */
|
||||||
vcffile: null,
|
vcffile: null,
|
||||||
|
|
||||||
/* The store for the selection grid */
|
/* The store for the selection grid */
|
||||||
store: null,
|
store : null,
|
||||||
|
|
||||||
|
/* selected folder */
|
||||||
|
folder : null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @constructor
|
* @constructor
|
||||||
* @param {object} config
|
* @param {object} config
|
||||||
*/
|
*/
|
||||||
constructor : function (config) {
|
constructor: function (config) {
|
||||||
config = config || {};
|
config = config || {};
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
if(typeof config.filename !== "undefined") {
|
if (!Ext.isEmpty(config.filename)) {
|
||||||
this.vcffile = config.filename;
|
this.vcffile = config.filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!Ext.isEmpty(config.folder)) {
|
||||||
|
this.folder = config.folder;
|
||||||
|
}
|
||||||
|
|
||||||
// create the data store
|
// create the data store
|
||||||
// we only display the firstname, lastname, homephone and primary email address in our grid
|
// we only display the firstname, lastname, homephone and primary email address in our grid
|
||||||
this.store = new Ext.data.ArrayStore({
|
this.store = new Ext.data.ArrayStore({
|
||||||
@ -62,51 +69,42 @@ Zarafa.plugins.contactimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
|
|||||||
{name: 'record'}
|
{name: 'record'}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
Ext.apply(config, {
|
Ext.apply(config, {
|
||||||
xtype : 'contactimporter.importpanel',
|
xtype : 'contactimporter.importpanel',
|
||||||
ref : "importcontactpanel",
|
ref : "importcontactpanel",
|
||||||
layout : {
|
layout : {
|
||||||
type : 'form',
|
type : 'form',
|
||||||
align : 'stretch'
|
align: 'stretch'
|
||||||
},
|
},
|
||||||
anchor : '100%',
|
anchor : '100%',
|
||||||
bodyStyle : 'background-color: inherit;',
|
bodyStyle: 'background-color: inherit;',
|
||||||
defaults : {
|
defaults : {
|
||||||
border : true,
|
border : true,
|
||||||
bodyStyle : 'background-color: inherit; padding: 3px 0px 3px 0px; border-style: none none solid none;'
|
bodyStyle: 'background-color: inherit; padding: 3px 0px 3px 0px; border-style: none none solid none;'
|
||||||
},
|
},
|
||||||
items : [
|
items : [
|
||||||
this.createSelectBox(),
|
this.createSelectBox(),
|
||||||
this.initForm(),
|
this.initForm(),
|
||||||
this.createGrid()
|
this.createGrid()
|
||||||
],
|
],
|
||||||
buttons: [
|
buttons : [
|
||||||
this.createSubmitAllButton(),
|
this.createSubmitAllButton(),
|
||||||
this.createSubmitButton(),
|
this.createSubmitButton(),
|
||||||
this.createCancelButton()
|
this.createCancelButton()
|
||||||
],
|
],
|
||||||
listeners: {
|
listeners: {
|
||||||
afterrender: function (cmp) {
|
afterrender: function (cmp) {
|
||||||
this.loadMask = new Ext.LoadMask(this.getEl(), {msg:'Loading...'});
|
this.loadMask = new Ext.LoadMask(this.getEl(), {msg: 'Loading...'});
|
||||||
|
|
||||||
if(this.vcffile != null) { // if we have got the filename from an attachment
|
if (this.vcffile != null) { // if we have got the filename from an attachment
|
||||||
this.parseContacts(this.vcffile);
|
this.parseContacts(this.vcffile);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
close: function (cmp) {
|
scope : this
|
||||||
Ext.getCmp("importcontactsbutton").enable();
|
|
||||||
},
|
|
||||||
hide: function (cmp) {
|
|
||||||
Ext.getCmp("importcontactsbutton").enable();
|
|
||||||
},
|
|
||||||
destroy: function (cmp) {
|
|
||||||
Ext.getCmp("importcontactsbutton").enable();
|
|
||||||
},
|
|
||||||
scope: this
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Zarafa.plugins.contactimporter.dialogs.ImportPanel.superclass.constructor.call(this, config);
|
Zarafa.plugins.contactimporter.dialogs.ImportPanel.superclass.constructor.call(this, config);
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -115,44 +113,159 @@ Zarafa.plugins.contactimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
|
|||||||
* posted and contains the attachments
|
* posted and contains the attachments
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
initForm : function () {
|
initForm: function () {
|
||||||
return {
|
return {
|
||||||
xtype: 'form',
|
xtype : 'form',
|
||||||
ref: 'addContactFormPanel',
|
ref : 'addContactFormPanel',
|
||||||
layout : 'column',
|
layout : 'column',
|
||||||
fileUpload: true,
|
fileUpload: true,
|
||||||
autoWidth: true,
|
autoWidth : true,
|
||||||
autoHeight: true,
|
autoHeight: true,
|
||||||
border: false,
|
border : false,
|
||||||
bodyStyle: 'padding: 5px;',
|
bodyStyle : 'padding: 5px;',
|
||||||
defaults: {
|
defaults : {
|
||||||
anchor: '95%',
|
anchor : '95%',
|
||||||
border: false,
|
border : false,
|
||||||
bodyStyle: 'padding: 5px;'
|
bodyStyle: 'padding: 5px;'
|
||||||
},
|
},
|
||||||
items: [this.createUploadField()]
|
items : [this.createUploadField()]
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
* Reloads the data of the grid
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
reloadGridStore: function(contactdata) {
|
reloadGridStore: function (contactdata) {
|
||||||
var parsedData = [];
|
var parsedData = [];
|
||||||
|
|
||||||
if(contactdata) {
|
if (contactdata) {
|
||||||
parsedData = new Array(contactdata.contacts.length);
|
parsedData = new Array(contactdata.contacts.length);
|
||||||
var i = 0;
|
var i = 0;
|
||||||
for(i = 0; i < contactdata.contacts.length; i++) {
|
for (i = 0; i < contactdata.contacts.length; i++) {
|
||||||
|
|
||||||
parsedData[i] = new Array(
|
parsedData[i] = [
|
||||||
contactdata.contacts[i]["display_name"],
|
contactdata.contacts[i]["display_name"],
|
||||||
contactdata.contacts[i]["given_name"],
|
contactdata.contacts[i]["given_name"],
|
||||||
contactdata.contacts[i]["surname"],
|
contactdata.contacts[i]["surname"],
|
||||||
contactdata.contacts[i]["company_name"],
|
contactdata.contacts[i]["company_name"],
|
||||||
contactdata.contacts[i]
|
contactdata.contacts[i]
|
||||||
);
|
];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
@ -160,193 +273,191 @@ Zarafa.plugins.contactimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
|
|||||||
|
|
||||||
this.store.loadData(parsedData, false);
|
this.store.loadData(parsedData, false);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Init embedded form, this is the form that is
|
* Init embedded form, this is the form that is
|
||||||
* posted and contains the attachments
|
* posted and contains the attachments
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
createGrid : function() {
|
createGrid: function () {
|
||||||
return {
|
return {
|
||||||
xtype: 'grid',
|
xtype : 'grid',
|
||||||
ref: 'contactGrid',
|
ref : 'contactGrid',
|
||||||
columnWidth: 1.0,
|
columnWidth: 1.0,
|
||||||
store: this.store,
|
store : this.store,
|
||||||
width: '100%',
|
width : '100%',
|
||||||
height: 300,
|
height : 300,
|
||||||
title: 'Select contacts to import',
|
title : 'Select contacts to import',
|
||||||
frame: false,
|
frame : false,
|
||||||
viewConfig:{
|
viewConfig : {
|
||||||
forceFit:true
|
forceFit: true
|
||||||
},
|
},
|
||||||
colModel: new Ext.grid.ColumnModel({
|
colModel : new Ext.grid.ColumnModel({
|
||||||
defaults: {
|
defaults: {
|
||||||
width: 300,
|
width : 300,
|
||||||
sortable: true
|
sortable: true
|
||||||
},
|
},
|
||||||
columns: [
|
columns : [
|
||||||
{id: 'Displayname', header: 'Displayname', width: 350, sortable: true, dataIndex: 'display_name'},
|
{id: 'Displayname', header: 'Displayname', width: 350, sortable: true, dataIndex: 'display_name'},
|
||||||
{header: 'Firstname', width: 200, sortable: true, dataIndex: 'given_name'},
|
{header: 'Firstname', width: 200, sortable: true, dataIndex: 'given_name'},
|
||||||
{header: 'Lastname', width: 200, sortable: true, dataIndex: 'surname'},
|
{header: 'Lastname', width: 200, sortable: true, dataIndex: 'surname'},
|
||||||
{header: 'Company', sortable: true, dataIndex: 'company_name'}
|
{header: 'Company', sortable: true, dataIndex: 'company_name'}
|
||||||
]
|
]
|
||||||
}),
|
}),
|
||||||
sm: new Ext.grid.RowSelectionModel({multiSelect:true})
|
sm : new Ext.grid.RowSelectionModel({multiSelect: true})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
createSelectBox: function() {
|
/**
|
||||||
var defaultFolder = container.getHierarchyStore().getDefaultFolder('contact'); // @type: Zarafa.hierarchy.data.MAPIFolderRecord
|
* Generate the UI calendar select box.
|
||||||
var subFolders = defaultFolder.getChildren();
|
* @returns {*}
|
||||||
var myStore = [];
|
*/
|
||||||
|
createSelectBox: function () {
|
||||||
/* add all local contact folders */
|
var myStore = this.getAllContactFolders(true);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
xtype: "selectbox",
|
xtype : "selectbox",
|
||||||
ref: 'addressbookSelector',
|
ref : 'addressbookSelector',
|
||||||
editable: false,
|
editable : false,
|
||||||
name: "choosen_addressbook",
|
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,
|
width : 100,
|
||||||
fieldLabel: "Select an addressbook",
|
fieldLabel : "Select folder",
|
||||||
store: myStore,
|
store : myStore,
|
||||||
mode: 'local',
|
mode : 'local',
|
||||||
labelSeperator: ":",
|
labelSeperator: ":",
|
||||||
border: false,
|
border : false,
|
||||||
anchor: "100%",
|
anchor : "100%",
|
||||||
scope: this,
|
scope : this,
|
||||||
allowBlank: false
|
hidden : Ext.isEmpty(this.folder) ? false : true,
|
||||||
|
allowBlank : false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
createUploadField: function() {
|
/**
|
||||||
|
* Generate the UI upload field.
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
|
createUploadField: function () {
|
||||||
return {
|
return {
|
||||||
xtype: "fileuploadfield",
|
xtype : "fileuploadfield",
|
||||||
ref: 'contactfileuploadfield',
|
ref : 'contactfileuploadfield',
|
||||||
columnWidth: 1.0,
|
columnWidth: 1.0,
|
||||||
id: 'form-file',
|
id : 'form-file',
|
||||||
name: 'vcfdata',
|
name : 'vcfdata',
|
||||||
emptyText: 'Select an .vcf addressbook',
|
emptyText : 'Select an .vcf addressbook',
|
||||||
border: false,
|
border : false,
|
||||||
anchor: "100%",
|
anchor : "100%",
|
||||||
scope: this,
|
height : "30",
|
||||||
allowBlank: false,
|
scope : this,
|
||||||
listeners: {
|
allowBlank : false,
|
||||||
|
listeners : {
|
||||||
'fileselected': this.onFileSelected,
|
'fileselected': this.onFileSelected,
|
||||||
scope: this
|
scope : this
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
createSubmitButton: function() {
|
/**
|
||||||
|
* Generate the UI submit button.
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
|
createSubmitButton: function () {
|
||||||
return {
|
return {
|
||||||
xtype: "button",
|
xtype : "button",
|
||||||
ref: "../submitButton",
|
ref : "../submitButton",
|
||||||
disabled: true,
|
disabled : true,
|
||||||
width: 100,
|
width : 100,
|
||||||
border: false,
|
border : false,
|
||||||
text: _("Import"),
|
text : _("Import"),
|
||||||
anchor: "100%",
|
anchor : "100%",
|
||||||
handler: this.importCheckedContacts,
|
handler : this.importCheckedContacts,
|
||||||
scope: this,
|
scope : this,
|
||||||
allowBlank: false
|
allowBlank: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
createSubmitAllButton: function() {
|
/**
|
||||||
|
* Generate the UI submit all button.
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
|
createSubmitAllButton: function () {
|
||||||
return {
|
return {
|
||||||
xtype: "button",
|
xtype : "button",
|
||||||
ref: "../submitAllButton",
|
ref : "../submitAllButton",
|
||||||
disabled: true,
|
disabled : true,
|
||||||
width: 100,
|
width : 100,
|
||||||
border: false,
|
border : false,
|
||||||
text: _("Import All"),
|
text : _("Import All"),
|
||||||
anchor: "100%",
|
anchor : "100%",
|
||||||
handler: this.importAllContacts,
|
handler : this.importAllContacts,
|
||||||
scope: this,
|
scope : this,
|
||||||
allowBlank: false
|
allowBlank: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
createCancelButton: function() {
|
/**
|
||||||
|
* Generate the UI cancel button.
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
|
createCancelButton: function () {
|
||||||
return {
|
return {
|
||||||
xtype: "button",
|
xtype : "button",
|
||||||
width: 100,
|
width : 100,
|
||||||
border: false,
|
border : false,
|
||||||
text: _("Cancel"),
|
text : _("Cancel"),
|
||||||
anchor: "100%",
|
anchor : "100%",
|
||||||
handler: this.close,
|
handler : this.close,
|
||||||
scope: this,
|
scope : this,
|
||||||
allowBlank: false
|
allowBlank: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is called when a file has been seleceted in the file dialog
|
* This is called when a file has been seleceted in the file dialog
|
||||||
* in the {@link Ext.ux.form.FileUploadField} and the dialog is closed
|
* in the {@link Ext.ux.form.FileUploadField} and the dialog is closed
|
||||||
* @param {Ext.ux.form.FileUploadField} uploadField being added a file to
|
* @param {Ext.ux.form.FileUploadField} uploadField being added a file to
|
||||||
*/
|
*/
|
||||||
onFileSelected : function(uploadField) {
|
onFileSelected: function (uploadField) {
|
||||||
var form = this.addContactFormPanel.getForm();
|
var form = this.addContactFormPanel.getForm();
|
||||||
|
|
||||||
if (form.isValid()) {
|
if (form.isValid()) {
|
||||||
form.submit({
|
form.submit({
|
||||||
waitMsg: 'Uploading and parsing contacts...',
|
waitMsg: 'Uploading and parsing contacts...',
|
||||||
url: 'plugins/contactimporter/php/upload.php',
|
url : 'plugins/contactimporter/php/upload.php',
|
||||||
failure: function(file, action) {
|
failure: function (file, action) {
|
||||||
this.submitButton.disable();
|
this.submitButton.disable();
|
||||||
this.submitAllButton.disable();
|
this.submitAllButton.disable();
|
||||||
Zarafa.common.dialogs.MessageBox.show({
|
Zarafa.common.dialogs.MessageBox.show({
|
||||||
title : _('Error'),
|
title : _('Error'),
|
||||||
msg : _(action.result.error),
|
msg : _(action.result.error),
|
||||||
icon : Zarafa.common.dialogs.MessageBox.ERROR,
|
icon : Zarafa.common.dialogs.MessageBox.ERROR,
|
||||||
buttons : Zarafa.common.dialogs.MessageBox.OK
|
buttons: Zarafa.common.dialogs.MessageBox.OK
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
success: function(file, action){
|
success: function (file, action) {
|
||||||
uploadField.reset();
|
uploadField.reset();
|
||||||
this.vcffile = action.result.vcf_file;
|
this.vcffile = action.result.vcf_file;
|
||||||
|
|
||||||
this.parseContacts(this.vcffile);
|
this.parseContacts(this.vcffile);
|
||||||
},
|
},
|
||||||
scope : this
|
scope : this
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start request to server to parse the given vCard file.
|
||||||
|
* @param {string} vcfPath
|
||||||
|
*/
|
||||||
parseContacts: function (vcfPath) {
|
parseContacts: function (vcfPath) {
|
||||||
this.loadMask.show();
|
this.loadMask.show();
|
||||||
|
|
||||||
// call export function here!
|
// call export function here!
|
||||||
var responseHandler = new Zarafa.plugins.contactimporter.data.ResponseHandler({
|
var responseHandler = new Zarafa.plugins.contactimporter.data.ResponseHandler({
|
||||||
successCallback: this.handleParsingResult.createDelegate(this)
|
successCallback: this.handleParsingResult.createDelegate(this)
|
||||||
});
|
});
|
||||||
|
|
||||||
container.getRequest().singleRequest(
|
container.getRequest().singleRequest(
|
||||||
'contactmodule',
|
'contactmodule',
|
||||||
'load',
|
'load',
|
||||||
@ -356,147 +467,125 @@ Zarafa.plugins.contactimporter.dialogs.ImportPanel = Ext.extend(Ext.Panel, {
|
|||||||
responseHandler
|
responseHandler
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
handleParsingResult: function(response) {
|
/**
|
||||||
|
* Callback for the parsing request.
|
||||||
|
* @param {Object} response
|
||||||
|
*/
|
||||||
|
handleParsingResult: function (response) {
|
||||||
this.loadMask.hide();
|
this.loadMask.hide();
|
||||||
|
|
||||||
if(response["status"] == true) {
|
if (response["status"] == true) {
|
||||||
this.submitButton.enable();
|
this.submitButton.enable();
|
||||||
this.submitAllButton.enable();
|
this.submitAllButton.enable();
|
||||||
|
|
||||||
this.reloadGridStore(response.parsed);
|
this.reloadGridStore(response.parsed);
|
||||||
} else {
|
} else {
|
||||||
this.submitButton.disable();
|
this.submitButton.disable();
|
||||||
this.submitAllButton.disable();
|
this.submitAllButton.disable();
|
||||||
Zarafa.common.dialogs.MessageBox.show({
|
Zarafa.common.dialogs.MessageBox.show({
|
||||||
title : _('Parser Error'),
|
title : _('Parser Error'),
|
||||||
msg : _(response["message"]),
|
msg : _(response["message"]),
|
||||||
icon : Zarafa.common.dialogs.MessageBox.ERROR,
|
icon : Zarafa.common.dialogs.MessageBox.ERROR,
|
||||||
buttons : Zarafa.common.dialogs.MessageBox.OK
|
buttons: Zarafa.common.dialogs.MessageBox.OK
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close the UI dialog.
|
||||||
|
*/
|
||||||
close: function () {
|
close: function () {
|
||||||
this.addContactFormPanel.getForm().reset();
|
this.addContactFormPanel.getForm().reset();
|
||||||
this.dialog.close()
|
this.dialog.close()
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a request to import all selected contacts.
|
||||||
|
*/
|
||||||
importCheckedContacts: function () {
|
importCheckedContacts: function () {
|
||||||
var newRecords = this.contactGrid.selModel.getSelections();
|
var newRecords = this.contactGrid.selModel.getSelections();
|
||||||
this.importContacts(newRecords);
|
this.importContacts(newRecords);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check all contacts and import them.
|
||||||
|
*/
|
||||||
importAllContacts: function () {
|
importAllContacts: function () {
|
||||||
//receive Records from grid rows
|
//receive Records from grid rows
|
||||||
this.contactGrid.selModel.selectAll(); // select all entries
|
this.contactGrid.selModel.selectAll(); // select all entries
|
||||||
var newRecords = this.contactGrid.selModel.getSelections();
|
var newRecords = this.contactGrid.selModel.getSelections();
|
||||||
this.importContacts(newRecords);
|
this.importContacts(newRecords);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function stores all given events to the appointmentstore
|
* This function stores all given events to the contact store
|
||||||
* @param events
|
* @param {array} contacts
|
||||||
*/
|
*/
|
||||||
importContacts: function (contacts) {
|
importContacts: function (contacts) {
|
||||||
//receive existing contact store
|
//receive existing contact store
|
||||||
var folderValue = this.addressbookSelector.getValue();
|
var folderValue = this.addressbookSelector.getValue();
|
||||||
|
|
||||||
if(folderValue == undefined) { // no addressbook choosen
|
if (folderValue == undefined) { // no addressbook choosen
|
||||||
Zarafa.common.dialogs.MessageBox.show({
|
Zarafa.common.dialogs.MessageBox.show({
|
||||||
title : _('Error'),
|
title : _('Error'),
|
||||||
msg : _('You have to choose an addressbook!'),
|
msg : _('You have to choose an addressbook!'),
|
||||||
icon : Zarafa.common.dialogs.MessageBox.ERROR,
|
icon : Zarafa.common.dialogs.MessageBox.ERROR,
|
||||||
buttons : Zarafa.common.dialogs.MessageBox.OK
|
buttons: Zarafa.common.dialogs.MessageBox.OK
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
var addressbookexist = true;
|
if (this.contactGrid.selModel.getCount() < 1) {
|
||||||
if(this.contactGrid.selModel.getCount() < 1) {
|
|
||||||
Zarafa.common.dialogs.MessageBox.show({
|
Zarafa.common.dialogs.MessageBox.show({
|
||||||
title : _('Error'),
|
title : _('Error'),
|
||||||
msg : _('You have to choose at least one contact to import!'),
|
msg : _('You have to choose at least one contact to import!'),
|
||||||
icon : Zarafa.common.dialogs.MessageBox.ERROR,
|
icon : Zarafa.common.dialogs.MessageBox.ERROR,
|
||||||
buttons : Zarafa.common.dialogs.MessageBox.OK
|
buttons: Zarafa.common.dialogs.MessageBox.OK
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
var contactStore = new Zarafa.contact.ContactStore();
|
var contactFolder = this.getContactFolderByEntryid(folderValue);
|
||||||
var contactFolder = container.getHierarchyStore().getDefaultFolder('contact');
|
|
||||||
var pubStore = container.getHierarchyStore().getPublicStore();
|
|
||||||
var pubFolder = pubStore.getDefaultFolder("publicfolders");
|
|
||||||
var pubSubFolders = pubFolder.getChildren();
|
|
||||||
|
|
||||||
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();
|
||||||
this.loadMask.show();
|
var uids = [];
|
||||||
var uids = new Array();
|
|
||||||
var store_entryid = "";
|
//receive Records from grid rows
|
||||||
|
Ext.each(contacts, function (newRecord) {
|
||||||
//receive Records from grid rows
|
uids.push(newRecord.data.record.internal_fields.contact_uid);
|
||||||
Ext.each(contacts, function(newRecord) {
|
}, this);
|
||||||
uids.push(newRecord.data.record.internal_fields.contact_uid);
|
|
||||||
}, this);
|
var responseHandler = new Zarafa.plugins.contactimporter.data.ResponseHandler({
|
||||||
store_entryid = contactFolder.get('store_entryid');
|
successCallback: this.importContactsDone.createDelegate(this)
|
||||||
|
});
|
||||||
var responseHandler = new Zarafa.plugins.contactimporter.data.ResponseHandler({
|
|
||||||
successCallback: this.importContactsDone.createDelegate(this)
|
container.getRequest().singleRequest(
|
||||||
});
|
'contactmodule',
|
||||||
|
'import',
|
||||||
container.getRequest().singleRequest(
|
{
|
||||||
'contactmodule',
|
storeid : contactFolder.store_entryid,
|
||||||
'import',
|
folderid : contactFolder.entryid,
|
||||||
{
|
uids : uids,
|
||||||
storeid: contactFolder.get("store_entryid"),
|
vcf_filepath: this.vcffile
|
||||||
folderid: contactFolder.get("entryid"),
|
},
|
||||||
uids: uids,
|
responseHandler
|
||||||
vcf_filepath: this.vcffile
|
);
|
||||||
},
|
|
||||||
responseHandler
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
importContactsDone : function (response) {
|
/**
|
||||||
console.log(response);
|
* Callback for the import request.
|
||||||
|
* @param {Object} response
|
||||||
|
*/
|
||||||
|
importContactsDone: function (response) {
|
||||||
this.loadMask.hide();
|
this.loadMask.hide();
|
||||||
this.dialog.close();
|
this.dialog.close();
|
||||||
if(response.status == true) {
|
if (response.status == true) {
|
||||||
container.getNotifier().notify('info', 'Imported', 'Imported ' + response.count + ' contacts. Please reload your addressbook!');
|
container.getNotifier().notify('info', 'Imported', 'Imported ' + response.count + ' contacts. Please reload your addressbook!');
|
||||||
} else {
|
} else {
|
||||||
Zarafa.common.dialogs.MessageBox.show({
|
Zarafa.common.dialogs.MessageBox.show({
|
||||||
title : _('Error'),
|
title : _('Error'),
|
||||||
msg : _('Import failed: ') + response.message,
|
msg : _('Import failed: ') + response.message,
|
||||||
icon : Zarafa.common.dialogs.MessageBox.ERROR,
|
icon : Zarafa.common.dialogs.MessageBox.ERROR,
|
||||||
buttons : Zarafa.common.dialogs.MessageBox.OK
|
buttons: Zarafa.common.dialogs.MessageBox.OK
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
* plugin.contactimporter.js zarafa contactimporter
|
* plugin.contactimporter.js zarafa contactimporter
|
||||||
*
|
*
|
||||||
* Author: Christoph Haas <christoph.h@sprinternet.at>
|
* Author: Christoph Haas <christoph.h@sprinternet.at>
|
||||||
* Copyright (C) 2012-2013 Christoph Haas
|
* Copyright (C) 2012-2016 Christoph Haas
|
||||||
*
|
*
|
||||||
* This library is free software; you can redistribute it and/or
|
* This library is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
@ -19,175 +19,234 @@
|
|||||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Ext.namespace("Zarafa.plugins.contactimporter"); // Assign the right namespace
|
Ext.namespace("Zarafa.plugins.contactimporter"); // Assign the right namespace
|
||||||
|
|
||||||
Zarafa.plugins.contactimporter.ImportPlugin = Ext.extend(Zarafa.core.Plugin, { // create new import plugin
|
Zarafa.plugins.contactimporter.ImportPlugin = Ext.extend(Zarafa.core.Plugin, { // create new import plugin
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @constructor
|
* @constructor
|
||||||
* @param {Object} config Configuration object
|
* @param {Object} config Configuration object
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
constructor: function (config) {
|
constructor: function (config) {
|
||||||
config = config || {};
|
config = config || {};
|
||||||
|
|
||||||
Zarafa.plugins.contactimporter.ImportPlugin.superclass.constructor.call(this, config);
|
Zarafa.plugins.contactimporter.ImportPlugin.superclass.constructor.call(this, config);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* initialises insertion point for plugin
|
* initialises insertion point for plugin
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
initPlugin : function() {
|
initPlugin: function () {
|
||||||
Zarafa.plugins.contactimporter.ImportPlugin.superclass.initPlugin.apply(this, arguments);
|
Zarafa.plugins.contactimporter.ImportPlugin.superclass.initPlugin.apply(this, arguments);
|
||||||
|
|
||||||
/* our panel */
|
/* our panel */
|
||||||
Zarafa.core.data.SharedComponentType.addProperty('plugins.contactimporter.dialogs.importcontacts');
|
Zarafa.core.data.SharedComponentType.addProperty('plugins.contactimporter.dialogs.importcontacts');
|
||||||
|
|
||||||
/* directly import received vcfs */
|
/* directly import received vcfs */
|
||||||
this.registerInsertionPoint('common.contextmenu.attachment.actions', this.createAttachmentImportButton);
|
this.registerInsertionPoint('common.contextmenu.attachment.actions', this.createAttachmentImportButton, this);
|
||||||
/* add import button to south navigation */
|
|
||||||
this.registerInsertionPoint("navigation.south", this.createImportButton, this);
|
/* export a contact via rightclick */
|
||||||
|
this.registerInsertionPoint('context.contact.contextmenu.actions', this.createItemExportInsertionPoint, this);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates the button
|
* This method hooks to the contact context menu and allows users to export users to vcf.
|
||||||
*
|
*
|
||||||
* @return {Object} Configuration object for a {@link Ext.Button button}
|
* @param include
|
||||||
*
|
* @param btn
|
||||||
*/
|
* @returns {Object}
|
||||||
createImportButton: function () {
|
*/
|
||||||
var button = {
|
createItemExportInsertionPoint: function (include, btn) {
|
||||||
xtype : 'button',
|
return {
|
||||||
id : "importcontactsbutton",
|
text : dgettext('plugin_files', 'Export vCard'),
|
||||||
text : _('Import Contacts'),
|
handler: this.exportToVCF.createDelegate(this, [btn]),
|
||||||
iconCls : 'icon_contactimporter_button',
|
scope : this,
|
||||||
navigationContext : container.getContextByName('contact'),
|
iconCls: 'icon_contactimporter_export'
|
||||||
handler : this.onImportButtonClick,
|
|
||||||
scope : this
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if(container.getSettingsModel().get("zarafa/v1/plugins/contactimporter/enable_export")) {
|
|
||||||
button.text = _('Import/Export Contacts');
|
|
||||||
}
|
|
||||||
|
|
||||||
return button;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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!
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Insert import button in all attachment suggestions
|
* Insert import button in all attachment suggestions
|
||||||
|
|
||||||
* @return {Object} Configuration object for a {@link Ext.Button button}
|
* @return {Object} Configuration object for a {@link Ext.Button button}
|
||||||
*/
|
*/
|
||||||
createAttachmentImportButton : function(include, btn) {
|
createAttachmentImportButton: function (include, btn) {
|
||||||
return {
|
return {
|
||||||
text : _('Import Contacts'),
|
text : _('Import to Contacts'),
|
||||||
handler : this.getAttachmentFileName.createDelegate(this, [btn, this.gotAttachmentFileName]),
|
handler : this.getAttachmentFileName.createDelegate(this, [btn]),
|
||||||
scope : this,
|
scope : this,
|
||||||
iconCls : 'icon_contactimporter_button',
|
iconCls : 'icon_contactimporter_button',
|
||||||
beforeShow : function(item, record) {
|
beforeShow: function (item, record) {
|
||||||
var extension = record.data.name.split('.').pop().toLowerCase();
|
var extension = record.data.name.split('.').pop().toLowerCase();
|
||||||
|
|
||||||
if(record.data.filetype == "text/vcard" || extension == "vcf" || extension == "vcard") {
|
if (record.data.filetype == "text/vcard" || record.data.filetype == "text/x-vcard" || extension == "vcf" || extension == "vcard") {
|
||||||
item.setDisabled(false);
|
item.setVisible(true);
|
||||||
} else {
|
} else {
|
||||||
item.setDisabled(true);
|
item.setVisible(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback for getAttachmentFileName
|
* Callback for getAttachmentFileName
|
||||||
|
* @param {Object} response
|
||||||
*/
|
*/
|
||||||
gotAttachmentFileName: function(response) {
|
gotAttachmentFileName: function (response) {
|
||||||
if(response.status == true) {
|
if (response.status == true) {
|
||||||
Zarafa.core.data.UIFactory.openLayerComponent(Zarafa.core.data.SharedComponentType['plugins.contactimporter.dialogs.importcontacts'], undefined, {
|
this.openImportDialog(response.tmpname);
|
||||||
manager : Ext.WindowMgr,
|
|
||||||
filename : response.tmpname
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
Zarafa.common.dialogs.MessageBox.show({
|
Zarafa.common.dialogs.MessageBox.show({
|
||||||
title : _('Error'),
|
title : _('Error'),
|
||||||
msg : _(response["message"]),
|
msg : _(response["message"]),
|
||||||
icon : Zarafa.common.dialogs.MessageBox.ERROR,
|
icon : Zarafa.common.dialogs.MessageBox.ERROR,
|
||||||
buttons : Zarafa.common.dialogs.MessageBox.OK
|
buttons: Zarafa.common.dialogs.MessageBox.OK
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clickhandler for the button
|
* Clickhandler for the button
|
||||||
|
* @param {Ext.Button} btn
|
||||||
*/
|
*/
|
||||||
getAttachmentFileName: function (btn, callback) {
|
getAttachmentFileName: function (btn) {
|
||||||
Zarafa.common.dialogs.MessageBox.show({
|
Zarafa.common.dialogs.MessageBox.show({
|
||||||
title: 'Please wait',
|
title : 'Please wait',
|
||||||
msg: 'Loading attachment...',
|
msg : 'Loading attachment...',
|
||||||
progressText: 'Initializing...',
|
progressText: 'Initializing...',
|
||||||
width:300,
|
width : 300,
|
||||||
progress:true,
|
progress : true,
|
||||||
closable:false
|
closable : false
|
||||||
});
|
});
|
||||||
|
|
||||||
// progress bar... ;)
|
// progress bar... ;)
|
||||||
var f = function(v){
|
var f = function (v) {
|
||||||
return function(){
|
return function () {
|
||||||
if(v == 100){
|
if (v == 100) {
|
||||||
Zarafa.common.dialogs.MessageBox.hide();
|
Zarafa.common.dialogs.MessageBox.hide();
|
||||||
}else{
|
} else {
|
||||||
Zarafa.common.dialogs.MessageBox.updateProgress(v/100, Math.round(v)+'% loaded');
|
Zarafa.common.dialogs.MessageBox.updateProgress(v / 100, Math.round(v) + '% loaded');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
for(var i = 1; i < 101; i++){
|
for (var i = 1; i < 101; i++) {
|
||||||
setTimeout(f(i), 20*i);
|
setTimeout(f(i), 20 * i);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* store the attachment to a temporary folder and prepare it for uploading */
|
/* store the attachment to a temporary folder and prepare it for uploading */
|
||||||
var attachmentRecord = btn.records;
|
var attachmentRecord = btn.records;
|
||||||
var attachmentStore = attachmentRecord.store;
|
var attachmentStore = attachmentRecord.store;
|
||||||
|
|
||||||
var store = attachmentStore.getParentRecord().get('store_entryid');
|
var store = attachmentStore.getParentRecord().get('store_entryid');
|
||||||
var entryid = attachmentStore.getAttachmentParentRecordEntryId();
|
var entryid = attachmentStore.getAttachmentParentRecordEntryId();
|
||||||
var attachNum = new Array(1);
|
var attachNum = new Array(1);
|
||||||
if (attachmentRecord.get('attach_num') != -1)
|
if (attachmentRecord.get('attach_num') != -1) {
|
||||||
attachNum[0] = attachmentRecord.get('attach_num');
|
attachNum[0] = attachmentRecord.get('attach_num');
|
||||||
else
|
} else {
|
||||||
attachNum[0] = attachmentRecord.get('tmpname');
|
attachNum[0] = attachmentRecord.get('tmpname');
|
||||||
|
}
|
||||||
var dialog_attachments = attachmentStore.getId();
|
var dialog_attachments = attachmentStore.getId();
|
||||||
var filename = attachmentRecord.data.name;
|
var filename = attachmentRecord.data.name;
|
||||||
|
|
||||||
var responseHandler = new Zarafa.plugins.contactimporter.data.ResponseHandler({
|
var responseHandler = new Zarafa.plugins.contactimporter.data.ResponseHandler({
|
||||||
successCallback: callback
|
successCallback: this.gotAttachmentFileName.createDelegate(this),
|
||||||
|
scope : this
|
||||||
});
|
});
|
||||||
|
|
||||||
// request attachment preperation
|
// request attachment preperation
|
||||||
container.getRequest().singleRequest(
|
container.getRequest().singleRequest(
|
||||||
'contactmodule',
|
'contactmodule',
|
||||||
'importattachment',
|
'importattachment',
|
||||||
{
|
{
|
||||||
entryid : entryid,
|
entryid : entryid,
|
||||||
store: store,
|
store : store,
|
||||||
attachNum: attachNum,
|
attachNum : attachNum,
|
||||||
dialog_attachments: dialog_attachments,
|
dialog_attachments: dialog_attachments,
|
||||||
filename: filename
|
filename : filename
|
||||||
},
|
},
|
||||||
responseHandler
|
responseHandler
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clickhandler for the button
|
* Open the import dialog.
|
||||||
|
* @param {String} filename
|
||||||
*/
|
*/
|
||||||
onImportButtonClick: function () {
|
openImportDialog: function (filename) {
|
||||||
Ext.getCmp("importcontactsbutton").disable();
|
var componentType = Zarafa.core.data.SharedComponentType['plugins.contactimporter.dialogs.importcontacts'];
|
||||||
Zarafa.core.data.UIFactory.openLayerComponent(Zarafa.core.data.SharedComponentType['plugins.contactimporter.dialogs.importcontacts'], undefined, {
|
var config = {
|
||||||
manager : Ext.WindowMgr
|
filename: filename,
|
||||||
});
|
modal : true
|
||||||
|
};
|
||||||
|
|
||||||
|
Zarafa.core.data.UIFactory.openLayerComponent(componentType, undefined, config);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bid for the type of shared component
|
* Bid for the type of shared component
|
||||||
* and the given record.
|
* and the given record.
|
||||||
@ -196,11 +255,18 @@ Zarafa.plugins.contactimporter.ImportPlugin = Ext.extend(Zarafa.core.Plugin, {
|
|||||||
* @param {Ext.data.Record} record Optionally passed record.
|
* @param {Ext.data.Record} record Optionally passed record.
|
||||||
* @return {Number} The bid for the shared component
|
* @return {Number} The bid for the shared component
|
||||||
*/
|
*/
|
||||||
bidSharedComponent : function(type, record) {
|
bidSharedComponent: function (type, record) {
|
||||||
var bid = -1;
|
var bid = -1;
|
||||||
switch(type) {
|
switch (type) {
|
||||||
case Zarafa.core.data.SharedComponentType['plugins.contactimporter.dialogs.importcontacts']:
|
case Zarafa.core.data.SharedComponentType['plugins.contactimporter.dialogs.importcontacts']:
|
||||||
bid = 2;
|
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;
|
break;
|
||||||
}
|
}
|
||||||
return bid;
|
return bid;
|
||||||
@ -213,12 +279,15 @@ Zarafa.plugins.contactimporter.ImportPlugin = Ext.extend(Zarafa.core.Plugin, {
|
|||||||
* @param {Ext.data.Record} record Optionally passed record.
|
* @param {Ext.data.Record} record Optionally passed record.
|
||||||
* @return {Ext.Component} Component
|
* @return {Ext.Component} Component
|
||||||
*/
|
*/
|
||||||
getSharedComponent : function(type, record) {
|
getSharedComponent: function (type, record) {
|
||||||
var component;
|
var component;
|
||||||
switch(type) {
|
switch (type) {
|
||||||
case Zarafa.core.data.SharedComponentType['plugins.contactimporter.dialogs.importcontacts']:
|
case Zarafa.core.data.SharedComponentType['plugins.contactimporter.dialogs.importcontacts']:
|
||||||
component = Zarafa.plugins.contactimporter.dialogs.ImportContentPanel;
|
component = Zarafa.plugins.contactimporter.dialogs.ImportContentPanel;
|
||||||
break;
|
break;
|
||||||
|
case Zarafa.core.data.SharedComponentType['common.contextmenu']:
|
||||||
|
component = Zarafa.plugins.contactimporter.ui.ContextMenu;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return component;
|
return component;
|
||||||
@ -229,11 +298,11 @@ Zarafa.plugins.contactimporter.ImportPlugin = Ext.extend(Zarafa.core.Plugin, {
|
|||||||
/*############################################################################################################################
|
/*############################################################################################################################
|
||||||
* STARTUP
|
* STARTUP
|
||||||
*############################################################################################################################*/
|
*############################################################################################################################*/
|
||||||
Zarafa.onReady(function() {
|
Zarafa.onReady(function () {
|
||||||
container.registerPlugin(new Zarafa.core.PluginMetaData({
|
container.registerPlugin(new Zarafa.core.PluginMetaData({
|
||||||
name : 'contactimporter',
|
name : 'contactimporter',
|
||||||
displayName : _('Contactimporter Plugin'),
|
displayName : _('Contactimporter Plugin'),
|
||||||
about : Zarafa.plugins.contactimporter.ABOUT,
|
about : Zarafa.plugins.contactimporter.ABOUT,
|
||||||
pluginConstructor : Zarafa.plugins.contactimporter.ImportPlugin
|
pluginConstructor: Zarafa.plugins.contactimporter.ImportPlugin
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
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);
|
10
manifest.xml
10
manifest.xml
@ -2,7 +2,7 @@
|
|||||||
<!DOCTYPE plugin SYSTEM "manifest.dtd">
|
<!DOCTYPE plugin SYSTEM "manifest.dtd">
|
||||||
<plugin version="2">
|
<plugin version="2">
|
||||||
<info>
|
<info>
|
||||||
<version>@_@PLUGIN_VERSION@_@</version>
|
<version>2.0.0</version>
|
||||||
<name>contactimporter</name>
|
<name>contactimporter</name>
|
||||||
<title>VCF Contact Importer/Exporter</title>
|
<title>VCF Contact Importer/Exporter</title>
|
||||||
<author>Christoph Haas</author>
|
<author>Christoph Haas</author>
|
||||||
@ -20,16 +20,18 @@
|
|||||||
<serverfile type="module" module="contactmodule">php/module.contact.php</serverfile>
|
<serverfile type="module" module="contactmodule">php/module.contact.php</serverfile>
|
||||||
</server>
|
</server>
|
||||||
<client>
|
<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="debug">js/contactimporter-debug.js</clientfile>
|
||||||
|
|
||||||
<clientfile load="source">js/plugin.contactimporter.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/data/ResponseHandler.js</clientfile>
|
||||||
<clientfile load="source">js/dialogs/ImportContentPanel.js</clientfile>
|
<clientfile load="source">js/dialogs/ImportContentPanel.js</clientfile>
|
||||||
<clientfile load="source">js/dialogs/ImportPanel.js</clientfile>
|
<clientfile load="source">js/dialogs/ImportPanel.js</clientfile>
|
||||||
|
<clientfile load="source">js/ui/ContextMenu.js</clientfile>
|
||||||
</client>
|
</client>
|
||||||
<resources>
|
<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="debug">resources/css/contactimporter.css</resourcefile>
|
||||||
<resourcefile load="source">resources/css/contactimporter-main.css</resourcefile>
|
<resourcefile load="source">resources/css/contactimporter-main.css</resourcefile>
|
||||||
</resources>
|
</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);
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@ -3,7 +3,7 @@
|
|||||||
* plugin.contactimporter.php, zarafa contact to vcf im/exporter
|
* plugin.contactimporter.php, zarafa contact to vcf im/exporter
|
||||||
*
|
*
|
||||||
* Author: Christoph Haas <christoph.h@sprinternet.at>
|
* Author: Christoph Haas <christoph.h@sprinternet.at>
|
||||||
* Copyright (C) 2012-2013 Christoph Haas
|
* Copyright (C) 2012-2016 Christoph Haas
|
||||||
*
|
*
|
||||||
* This library is free software; you can redistribute it and/or
|
* This library is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
@ -20,26 +20,32 @@
|
|||||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
require_once __DIR__ . "/download.php";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* contactimporter Plugin
|
* contactimporter Plugin
|
||||||
*
|
*
|
||||||
* With this plugin you can import a vcf file to your zarafa addressbook
|
* With this plugin you can import a vcf file to your zarafa addressbook
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
class Plugincontactimporter extends Plugin {
|
class Plugincontactimporter extends Plugin
|
||||||
|
{
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
*/
|
*/
|
||||||
function Plugincontactimporter() {}
|
function Plugincontactimporter()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function initializes the Plugin and registers all hooks
|
* Function initializes the Plugin and registers all hooks
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
function init() {
|
function init()
|
||||||
|
{
|
||||||
$this->registerHook('server.core.settings.init.before');
|
$this->registerHook('server.core.settings.init.before');
|
||||||
|
$this->registerHook('server.index.load.custom');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -49,11 +55,17 @@ class Plugincontactimporter extends Plugin {
|
|||||||
* @param mixed $data object(s) related to the hook
|
* @param mixed $data object(s) related to the hook
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
function execute($eventID, &$data) {
|
function execute($eventID, &$data)
|
||||||
switch($eventID) {
|
{
|
||||||
|
switch ($eventID) {
|
||||||
case 'server.core.settings.init.before' :
|
case 'server.core.settings.init.before' :
|
||||||
$this->injectPluginSettings($data);
|
$this->injectPluginSettings($data);
|
||||||
break;
|
break;
|
||||||
|
case 'server.index.load.custom':
|
||||||
|
if ($data['name'] == 'download_vcf') {
|
||||||
|
DownloadHandler::doDownload();
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,15 +74,15 @@ class Plugincontactimporter extends Plugin {
|
|||||||
* settings.
|
* settings.
|
||||||
* @param Array $data Reference to the data of the triggered hook
|
* @param Array $data Reference to the data of the triggered hook
|
||||||
*/
|
*/
|
||||||
function injectPluginSettings(&$data) {
|
function injectPluginSettings(&$data)
|
||||||
|
{
|
||||||
$data['settingsObj']->addSysAdminDefaults(Array(
|
$data['settingsObj']->addSysAdminDefaults(Array(
|
||||||
'zarafa' => Array(
|
'zarafa' => Array(
|
||||||
'v1' => Array(
|
'v1' => Array(
|
||||||
'plugins' => Array(
|
'plugins' => Array(
|
||||||
'contactimporter' => Array(
|
'contactimporter' => Array(
|
||||||
'enable' => PLUGIN_CONTACTIMPORTER_USER_DEFAULT_ENABLE,
|
'enable' => PLUGIN_CONTACTIMPORTER_USER_DEFAULT_ENABLE,
|
||||||
'enable_export' => PLUGIN_CONTACTIMPORTER_USER_DEFAULT_ENABLE_EXPORT,
|
'default_addressbook' => PLUGIN_CONTACTIMPORTER_DEFAULT
|
||||||
'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
|
* upload.php, zarafa contact to vcf im/exporter
|
||||||
*
|
*
|
||||||
* Author: Christoph Haas <christoph.h@sprinternet.at>
|
* Author: Christoph Haas <christoph.h@sprinternet.at>
|
||||||
* Copyright (C) 2012-2013 Christoph Haas
|
* Copyright (C) 2012-2016 Christoph Haas
|
||||||
*
|
*
|
||||||
* This library is free software; you can redistribute it and/or
|
* This library is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
@ -20,17 +20,18 @@
|
|||||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
require_once("../config.php");
|
require_once("../config.php");
|
||||||
|
|
||||||
/* disable error printing - otherwise json communication might break... */
|
/* disable error printing - otherwise json communication might break... */
|
||||||
ini_set('display_errors', '0');
|
ini_set('display_errors', '0');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* respond/echo JSON
|
* respond/echo JSON
|
||||||
* @param $arr
|
* @param $arr
|
||||||
*/
|
*/
|
||||||
function respondJSON($arr) {
|
function respondJSON($arr)
|
||||||
|
{
|
||||||
echo json_encode($arr);
|
echo json_encode($arr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,11 +40,12 @@ function respondJSON($arr) {
|
|||||||
* @param $length the lenght of the generated string
|
* @param $length the lenght of the generated string
|
||||||
* @return string a random string
|
* @return string a random string
|
||||||
*/
|
*/
|
||||||
function randomstring($length = 6) {
|
function randomstring($length = 6)
|
||||||
|
{
|
||||||
// $chars - all allowed charakters
|
// $chars - all allowed charakters
|
||||||
$chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
|
$chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
|
||||||
|
|
||||||
srand((double)microtime()*1000000);
|
srand((double)microtime() * 1000000);
|
||||||
$i = 0;
|
$i = 0;
|
||||||
$pass = "";
|
$pass = "";
|
||||||
while ($i < $length) {
|
while ($i < $length) {
|
||||||
@ -58,15 +60,15 @@ function randomstring($length = 6) {
|
|||||||
$destpath = PLUGIN_CONTACTIMPORTER_TMP_UPLOAD;
|
$destpath = PLUGIN_CONTACTIMPORTER_TMP_UPLOAD;
|
||||||
$destpath .= $_FILES['vcfdata']['name'] . randomstring();
|
$destpath .= $_FILES['vcfdata']['name'] . randomstring();
|
||||||
|
|
||||||
if(is_readable ($_FILES['vcfdata']['tmp_name'])) {
|
if (is_readable($_FILES['vcfdata']['tmp_name'])) {
|
||||||
$result = move_uploaded_file($_FILES['vcfdata']['tmp_name'],$destpath);
|
$result = move_uploaded_file($_FILES['vcfdata']['tmp_name'], $destpath);
|
||||||
|
|
||||||
if($result) {
|
if ($result) {
|
||||||
respondJSON(array ('success'=>true, 'vcf_file'=>$destpath));
|
respondJSON(array('success' => true, 'vcf_file' => $destpath));
|
||||||
} else {
|
} else {
|
||||||
respondJSON(array ('success'=>false,'error'=>"File could not be moved to TMP path! Check plugin config and folder permissions!"));
|
respondJSON(array('success' => false, 'error' => "File could not be moved to TMP path! Check plugin config and folder permissions!"));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
respondJSON(array ('success'=>false,'error'=>"File could not be read by server, upload error!"));
|
respondJSON(array('success' => false, 'error' => "File could not be read by server, upload error!"));
|
||||||
}
|
}
|
||||||
?>
|
?>
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
?>
|
|
@ -1,6 +1,30 @@
|
|||||||
.icon_contactimporter_button {
|
.icon_contactimporter_button {
|
||||||
background: url(../images/import_icon.png) no-repeat !important;
|
background: url(../images/import_icon.png) no-repeat !important;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
background-size: 18px!important;
|
}
|
||||||
}
|
|
||||||
|
.icon_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