svn commit: r1756949 - in /ofbiz/trunk: ./ applications/marketing/config/ applications/marketing/data/ applications/marketing/servicedef/ applications/marketing/src/main/java/org/apache/ofbiz/sfa/vcard/ applications/marketing/webapp/sfa/WEB-INF/ applic...

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

svn commit: r1756949 - in /ofbiz/trunk: ./ applications/marketing/config/ applications/marketing/data/ applications/marketing/servicedef/ applications/marketing/src/main/java/org/apache/ofbiz/sfa/vcard/ applications/marketing/webapp/sfa/WEB-INF/ applic...

nmalin
Author: nmalin
Date: Fri Aug 19 17:33:15 2016
New Revision: 1756949

URL: http://svn.apache.org/viewvc?rev=1756949&view=rev
Log:
 

With the suppression of all jar to download them form jcenter, the jpim.jar isn't present.

To resolve this issue we can convert the current use for import/export vcard by an other api ez-vcard (https://github.com/mangstadt/ez-vcard) under bsd licence and present on jcenter.

The refactoring raise some problem on existant code, we will try with Gil to improve the import/export to grow data conversion to functional framework.
Some improve on import :
 * check if more than one email is present before check the prefered else take the one
 * check if more than one phone is present before check the prefered else take the one
 * check if more than one address is present before check the prefered else take the one
 * When you load a party by vcard, initialize a reference by fn attribute on PartyIndentification to escape the second loading
Will need continue improve the import like correct Organization loading, update process, add service to detect already party present
I also improve the screen result to display all parties loaded by the import and all already present.
The solution isn't perfect but it's work and compile, first purpose to delete jpim.jar is done :)
Related issue OFBIZ-7961

Added:
    ofbiz/trunk/applications/marketing/widget/sfa/forms/CommonForms.xml   (with props)
Removed:
    ofbiz/trunk/framework/base/lib/jpim-0.1.jar
Modified:
    ofbiz/trunk/applications/marketing/config/MarketingUiLabels.xml
    ofbiz/trunk/applications/marketing/data/MarketingTypeData.xml
    ofbiz/trunk/applications/marketing/servicedef/services.xml
    ofbiz/trunk/applications/marketing/src/main/java/org/apache/ofbiz/sfa/vcard/VCard.java
    ofbiz/trunk/applications/marketing/webapp/sfa/WEB-INF/controller.xml
    ofbiz/trunk/applications/marketing/widget/sfa/CommonScreens.xml
    ofbiz/trunk/build.gradle

Modified: ofbiz/trunk/applications/marketing/config/MarketingUiLabels.xml
URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/marketing/config/MarketingUiLabels.xml?rev=1756949&r1=1756948&r2=1756949&view=diff
==============================================================================
--- ofbiz/trunk/applications/marketing/config/MarketingUiLabels.xml (original)
+++ ofbiz/trunk/applications/marketing/config/MarketingUiLabels.xml Fri Aug 19 17:33:15 2016
@@ -951,6 +951,10 @@
         <value xml:lang="zh">雇员数量</value>
         <value xml:lang="zh-TW">員工數量</value>
     </property>
+    <property key="MarketingNoPartyLoad">
+        <value xml:lang="en">No party load</value>
+        <value xml:lang="fr">Aucun acteur chargé</value>
+    </property>
     <property key="MarketingOrderAmount">
         <value xml:lang="de">Auftragssumme</value>
         <value xml:lang="en">Amount of Orders</value>
@@ -995,6 +999,14 @@
         <value xml:lang="zh">上级攻势标识</value>
         <value xml:lang="zh-TW">上級活動識別</value>
     </property>
+    <property key="MarketingPartiesLoaded">
+        <value xml:lang="en">Parties Loaded</value>
+        <value xml:lang="fr">Acteurs chargés</value>
+    </property>
+    <property key="MarketingPartiesNoLoadedAlreadyExist">
+        <value xml:lang="en">Parties no loaded because they already exist on the system</value>
+        <value xml:lang="fr">Acteurs non chargés car déjà présent dans le système</value>
+    </property>
     <property key="MarketingPartyStatusReport">
         <value xml:lang="de">Statusbericht Akteur</value>
         <value xml:lang="en">Party Status Report</value>

Modified: ofbiz/trunk/applications/marketing/data/MarketingTypeData.xml
URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/marketing/data/MarketingTypeData.xml?rev=1756949&r1=1756948&r2=1756949&view=diff
==============================================================================
--- ofbiz/trunk/applications/marketing/data/MarketingTypeData.xml (original)
+++ ofbiz/trunk/applications/marketing/data/MarketingTypeData.xml Fri Aug 19 17:33:15 2016
@@ -99,5 +99,6 @@ under the License.
     <StatusValidChange statusId="LEAD_ASSIGNED" statusIdTo="PARTY_DISABLED" transitionName="Disabled"/>
 
     <EnumerationType enumTypeId="SLSOPP_TYP_ENUM" description="Sales Opportunity Type"/>
-    
+
+    <PartyIdentificationType description="VCard reference origin" partyIdentificationTypeId="VCARD_FN_ORIGIN" parentTypeId=""/>
 </entity-engine-xml>

Modified: ofbiz/trunk/applications/marketing/servicedef/services.xml
URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/marketing/servicedef/services.xml?rev=1756949&r1=1756948&r2=1756949&view=diff
==============================================================================
--- ofbiz/trunk/applications/marketing/servicedef/services.xml (original)
+++ ofbiz/trunk/applications/marketing/servicedef/services.xml Fri Aug 19 17:33:15 2016
@@ -517,10 +517,11 @@ under the License.
     <!-- VCard services -->
     <service name="importVCard" engine="java" location="org.apache.ofbiz.sfa.vcard.VCard" invoke="importVCard" transaction-timeout="7200">
         <attribute name="infile" type="java.nio.ByteBuffer" mode="IN" optional="false"/>
-        <attribute name="partyId" type="String" mode="OUT" optional="false"/>
         <attribute name="partyType" type="String" mode="IN" optional="true"/>
         <attribute name="serviceContext" type="Map" mode="IN" optional="true"/>
         <attribute name="serviceName" type="String" mode="IN" optional="false"/>
+        <attribute name="partiesCreated" type="List" mode="OUT" optional="false"/>
+        <attribute name="partiesExist" type="List" mode="OUT" optional="true"/>
     </service>
     <service name="exportVCard" engine="java" location="org.apache.ofbiz.sfa.vcard.VCard" invoke="exportVCard">
         <attribute name="partyId" type="String" mode="IN" optional="false"/>

Modified: ofbiz/trunk/applications/marketing/src/main/java/org/apache/ofbiz/sfa/vcard/VCard.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/marketing/src/main/java/org/apache/ofbiz/sfa/vcard/VCard.java?rev=1756949&r1=1756948&r2=1756949&view=diff
==============================================================================
--- ofbiz/trunk/applications/marketing/src/main/java/org/apache/ofbiz/sfa/vcard/VCard.java (original)
+++ ofbiz/trunk/applications/marketing/src/main/java/org/apache/ofbiz/sfa/vcard/VCard.java Fri Aug 19 17:33:15 2016
@@ -22,21 +22,28 @@ package org.apache.ofbiz.sfa.vcard;
 import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.nio.ByteBuffer;
+import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 
-import javax.mail.internet.AddressException;
-import javax.mail.internet.InternetAddress;
-
+import ezvcard.Ezvcard;
+import ezvcard.io.text.VCardReader;
+import ezvcard.parameter.AddressType;
+import ezvcard.parameter.TelephoneType;
+import ezvcard.parameter.EmailType;
+import ezvcard.property.Address;
+import ezvcard.property.Email;
+import ezvcard.property.FormattedName;
+import ezvcard.property.StructuredName;
+import ezvcard.property.Telephone;
 import org.apache.ofbiz.base.util.Debug;
 import org.apache.ofbiz.base.util.FileUtil;
+import org.apache.ofbiz.base.util.StringUtil;
 import org.apache.ofbiz.base.util.UtilGenerics;
 import org.apache.ofbiz.base.util.UtilMisc;
 import org.apache.ofbiz.base.util.UtilProperties;
@@ -55,142 +62,141 @@ import org.apache.ofbiz.service.GenericS
 import org.apache.ofbiz.service.LocalDispatcher;
 import org.apache.ofbiz.service.ServiceUtil;
 
-import net.wimpi.pim.Pim;
-import net.wimpi.pim.contact.basicimpl.AddressImpl;
-import net.wimpi.pim.contact.basicimpl.EmailAddressImpl;
-import net.wimpi.pim.contact.basicimpl.PhoneNumberImpl;
-import net.wimpi.pim.contact.io.ContactMarshaller;
-import net.wimpi.pim.contact.io.ContactUnmarshaller;
-import net.wimpi.pim.contact.model.Address;
-import net.wimpi.pim.contact.model.Communications;
-import net.wimpi.pim.contact.model.Contact;
-import net.wimpi.pim.contact.model.EmailAddress;
-import net.wimpi.pim.contact.model.Organization;
-import net.wimpi.pim.contact.model.OrganizationalIdentity;
-import net.wimpi.pim.contact.model.PersonalIdentity;
-import net.wimpi.pim.contact.model.PhoneNumber;
-import net.wimpi.pim.factory.ContactIOFactory;
-import net.wimpi.pim.factory.ContactModelFactory;
-
 public class VCard {
     public static final String module = VCard.class.getName();
     public static final String resourceError = "MarketingUiLabels";
 
+    /**
+     * import a vcard from byteBuffer. the reader use is ez-vcard, see official site https://github.com/mangstadt/ez-vcard/
+     * @param dctx
+     * @param context
+     * @return
+     */
     public static Map<String, Object> importVCard(DispatchContext dctx, Map<String, ? extends Object> context) {
         LocalDispatcher dispatcher = dctx.getDispatcher();
         Delegator delegator = dctx.getDelegator();
         Locale locale = (Locale) context.get("locale");
         Map<String, Object> result = ServiceUtil.returnSuccess();
-        Address workAddress = null;
-        String email = null;
-        String phone = null;
         ByteBuffer byteBuffer = (ByteBuffer) context.get("infile");
         byte[] inputByteArray = byteBuffer.array();
         InputStream in = new ByteArrayInputStream(inputByteArray);
-        String partyType = (String) context.get("partyType");
-        Boolean isGroup =  "PartyGroup".equals(partyType); // By default we import a Person.
         Map<String, Object> serviceCtx = new HashMap<String, Object>();
+        boolean isGroup = false;
+        List<Map<String, String>> partiesCreated = new ArrayList<Map<String,String>>();
+        List<Map<String, String>> partiesExist = new ArrayList<Map<String,String>>();
+        String partyName = "";
 
         try {
-            ContactIOFactory ciof = Pim.getContactIOFactory();
-            ContactUnmarshaller unmarshaller = ciof.createContactUnmarshaller();
-            unmarshaller.setStrict(false);
-            Contact[] contacts = unmarshaller.unmarshallContacts(in);
-
-            int contactsCount = 1;
-            for (Contact contact: contacts) {
-                PersonalIdentity pid = contact.getPersonalIdentity();
-                if (!isGroup) {
-                    serviceCtx.put("firstName", pid.getFirstname());
-                    serviceCtx.put("lastName", pid.getLastname());
-                }
-                for (Iterator<?> iter = contact.getAddresses(); iter.hasNext();) {
-                    Address address = (AddressImpl) iter.next();
-                    if (contact.isPreferredAddress(address)) {
-                        workAddress = address;
-                        break;
-                    } else if (address.isWork()) {
-                        workAddress = address;
-                        break;
-                    } else { // for now use preferred/work address only
+            VCardReader vCardReader = new VCardReader(in);
+            ezvcard.VCard vcard = null;
+            while ((vcard = vCardReader.readNext()) != null) {
+
+                //Todo create a generic service to resolve duplicate party
+                FormattedName formattedName = vcard.getFormattedName();
+                if (formattedName != null) {
+                    String refCardId = formattedName.getValue();
+                    GenericValue partyIdentification = EntityQuery.use(delegator).from("PartyIdentification").where("partyIdentificationTypeId", "VCARD_FN_ORIGIN", "idValue", refCardId).queryFirst();
+                    if (partyIdentification != null) {
+                        partiesExist.add(UtilMisc.toMap("partyId", (String)partyIdentification.get("partyId")));
                         continue;
                     }
+                    //TODO manage update
                 }
-                if (UtilValidate.isNotEmpty(workAddress)) {
-                    serviceCtx.put("address1", workAddress.getStreet());
-                    serviceCtx.put("city", workAddress.getCity());
-                    serviceCtx.put("postalCode", workAddress.getPostalCode());
+                //check if it's already load
+                isGroup = false;
+                if (vcard.getKind() != null) isGroup = vcard.getKind().isGroup();
 
+                StructuredName structuredName = vcard.getStructuredName();
+                if (UtilValidate.isEmpty(structuredName)) continue;
+                if (!isGroup) {
+                    serviceCtx.put("firstName", structuredName.getGiven());
+                    serviceCtx.put("lastName", structuredName.getFamily());
+                    partyName = structuredName.getGiven() + " " + structuredName.getFamily();
+                }
+
+                // Resolve all postal Address
+                for (Address address : vcard.getAddresses()) {
+                    boolean workAddress = false;
+                    for (AddressType addressType : address.getTypes()) {
+                        if (AddressType.PREF.equals(addressType) || AddressType.WORK.equals(addressType)) {
+                            workAddress = true;
+                            break;
+                        }
+                    }
+                    if (! workAddress) continue;
+
+                    serviceCtx.put("address1", address.getStreetAddressFull());
+                    serviceCtx.put("city", address.getLocality());
+                    serviceCtx.put("postalCode", address.getPostalCode());
                     GenericValue countryGeo = EntityQuery.use(delegator).from("Geo")
                             .where(EntityCondition.makeCondition("geoTypeId", EntityOperator.EQUALS, "COUNTRY"),
-                                    EntityCondition.makeCondition("geoName", EntityOperator.LIKE, workAddress.getCountry()))
+                                    EntityCondition.makeCondition("geoName", EntityOperator.LIKE, address.getCountry()))
                             .cache().queryFirst();
                     if (countryGeo != null) {
                         serviceCtx.put("countryGeoId", countryGeo.get("geoId"));
                     }
-
                     GenericValue stateGeo = EntityQuery.use(delegator).from("Geo")
                             .where(EntityCondition.makeCondition("geoTypeId", EntityOperator.EQUALS, "STATE"),
-                            EntityCondition.makeCondition("geoName", EntityOperator.LIKE, workAddress.getRegion()))
+                                    EntityCondition.makeCondition("geoName", EntityOperator.LIKE, address.getRegion()))
                             .cache().queryFirst();
                     if (stateGeo != null) {
                         serviceCtx.put("stateProvinceGeoId", stateGeo.get("geoId"));
                     }
                 }
 
-                if (!isGroup) {
-                    Communications communications = contact.getCommunications();
-                    if (UtilValidate.isNotEmpty(communications)) {
-                        for (Iterator<?> iter = communications.getEmailAddresses(); iter.hasNext();) {
-                            EmailAddress emailAddress = (EmailAddressImpl) iter.next();
-                            if (communications.isPreferredEmailAddress(emailAddress)) {
-                                email = emailAddress.getAddress();
-                                break;
-                            } else {
-                                email = emailAddress.getAddress();
+                int nbEmailAddr = (vcard.getEmails() != null) ? vcard.getEmails().size() : 0;
+                for (Email email : vcard.getEmails()) {
+                    if (nbEmailAddr > 1) {
+                        boolean workEmail = false;
+                        for (EmailType emailType : email.getTypes()) {
+                            if (EmailType.PREF.equals(emailType) || EmailType.WORK.equals(emailType)) {
+                                workEmail = true;
                                 break;
                             }
                         }
-                        if (UtilValidate.isNotEmpty(email)) {
-                                  InternetAddress emailAddr;
-                                try {
-                                    emailAddr = new InternetAddress(email);
-                                    emailAddr.validate();
-                                } catch (AddressException e) {
-                                    String emailFOrmatErrMsg = UtilProperties.getMessage(resourceError, "SfaImportVCardEmailFormatError", locale);
-                                    return ServiceUtil.returnError(pid.getFirstname() + " " + pid.getLastname() + " has " + emailFOrmatErrMsg);
-                                }
-                            serviceCtx.put("emailAddress", email);
-                        }
-                        for (Iterator<?> iter = communications.getPhoneNumbers(); iter.hasNext();) {
-                            PhoneNumber phoneNumber = (PhoneNumberImpl) iter.next();
-                            if (phoneNumber.isPreferred()) {
-                                phone = phoneNumber.getNumber();
-                                break;
-                            } else if (phoneNumber.isWork()) {
-                                phone = phoneNumber.getNumber();
+                        if (! workEmail) continue;
+                    }
+                    String emailAddr = email.getValue();
+                    if (UtilValidate.isEmail(emailAddr)) {
+                        serviceCtx.put("emailAddress", emailAddr);
+                    } else {
+                        //TODO change uncorrect labellisation
+                        String emailFormatErrMsg = UtilProperties.getMessage(resourceError, "SfaImportVCardEmailFormatError", locale);
+                        return ServiceUtil.returnError(structuredName.getGiven() + " " + structuredName.getFamily() + " has " + emailFormatErrMsg);
+                    }
+                }
+
+                int nbPhone = (vcard.getTelephoneNumbers() != null) ? vcard.getTelephoneNumbers().size() : 0;
+                for (Telephone phone : vcard.getTelephoneNumbers()) {
+                    if (nbPhone > 1) {
+                        boolean workPhone = false;
+                        for (TelephoneType phoneType : phone.getTypes()) {
+                            if (TelephoneType.PREF.equals(phoneType) || TelephoneType.WORK.equals(phoneType)) {
+                                workPhone = true;
                                 break;
-                            } else { // for now use only preferred/work phone numbers
-                                continue;
                             }
                         }
-                        if (UtilValidate.isNotEmpty(phone)) {
-                            String[] numberParts = phone.split("\\D");
-                            StringBuilder telNumber = new StringBuilder("");
-                            for (String number: numberParts) {
-                                if (number != "") {
-                                    telNumber.append(number);
-                                }
-                            }
-                            serviceCtx.put("areaCode", telNumber.substring(0, 3));
-                            serviceCtx.put("contactNumber", telNumber.substring(3));
+                        if (! workPhone) continue;
+                    }
+                    String phoneAddr = phone.getText();
+                    boolean internationalPhone = phoneAddr.startsWith("+") || phoneAddr.startsWith("00");
+                    phoneAddr = StringUtil.removeNonNumeric(phoneAddr);
+                    int indexLocal = 0;
+                    if (internationalPhone) {
+                        indexLocal = 4;
+                        if (!phoneAddr.startsWith("00")) {
+                            phoneAddr = phoneAddr.concat("00");
                         }
+                        serviceCtx.put("areaCode", phoneAddr.substring(0, indexLocal));
                     }
+                    serviceCtx.put("contactNumber", phoneAddr.substring(indexLocal));
                 }
-                OrganizationalIdentity  oid = contact.getOrganizationalIdentity();
+
+                /* TODO improve this part to manage party organization
+                Organization organization = vcard.getOrganization();
                 // Useful when creating a contact with more than OOTB
-                if (!isGroup && oid != null && oid.getTitle() != null) {
-                    String personalTitle = oid.getTitle().replace("\\","").replaceAll("\uFFFD", " ");
+                if (!isGroup && organization != null && oid.getTitle() != null) {
+                    String personalTitle = organization..getTitle().replace("\\","").replaceAll("\uFFFD", " ");
                     if (personalTitle.length() > 100) {
                         personalTitle = oid.getTitle().replace("\\", "").replaceAll("\uFFFD", " ").substring(0, 100);
                     }
@@ -204,55 +210,37 @@ public class VCard {
                         Organization org = oid.getOrganization();
                         serviceCtx.put("groupName", org.getName().replace("\\", ""));
                     }
-                }
+                }*/
 
                 GenericValue userLogin = (GenericValue) context.get("userLogin");
                 serviceCtx.put("userLogin", userLogin);
                 String serviceName = (String) context.get("serviceName");
                 Map<String, Object> serviceContext = UtilGenerics.cast(context.get("serviceContext"));
-                if(UtilValidate.isNotEmpty(serviceContext)) {
+                if (UtilValidate.isNotEmpty(serviceContext)) {
                     for (Map.Entry<String, Object> entry : serviceContext.entrySet()) {
                         serviceCtx.put(entry.getKey(), entry.getValue());
                     }
                 }
-                List<GenericValue> persons = EntityQuery.use(delegator).from("Person").where(
-                        "firstName", serviceCtx.get("firstName"),
-                        "lastName", serviceCtx.get("lastName")
-                        ).queryList();
-                boolean blockPerson = false;
-                for (GenericValue person: persons) {
-                    GenericValue partyStatus = EntityQuery.use(delegator).from("PartyStatus").where(
-                            "partyId", person.get("partyId")).orderBy("-statusDate").queryFirst();
-                    if (!partyStatus.get("statusId").equals("PARTY_DISABLED")) {
-                        blockPerson = true;
-                    }
-                }
-                if (!blockPerson) {
-                    String nameMissingErrMsg = UtilProperties.getMessage(resourceError, "SfaImportVcardNameMissingError", locale);
-                    if (!isGroup && serviceCtx.get("lastName") == null) {
-                        return ServiceUtil.returnError(serviceCtx.get("firstName") + " " + nameMissingErrMsg);
-                    }
-                    if (!isGroup && serviceCtx.get("firstName") == null) {
-                        return ServiceUtil.returnError(serviceCtx.get("lastName") + " " + nameMissingErrMsg);
-                    }
-                    Map<String, Object> resp = dispatcher.runSync(serviceName, serviceCtx);
-                    result.put("partyId", resp.get("partyId"));
-                }
-                if (result.get("partyId") == null && contactsCount == contacts.length) {
-                    String duplicatedErrMsg = UtilProperties.getMessage(resourceError, "SfaImportVcardDuplicatedVcardError", locale);
-                    return ServiceUtil.returnError(duplicatedErrMsg);
+                Map<String, Object> resp = dispatcher.runSync(serviceName, serviceCtx);
+                partiesCreated.add(UtilMisc.toMap("partyId", (String) resp.get("partyId")));
+
+                if (formattedName != null) {
+                    //store the origin creation
+                    Map<String, Object> createPartyIdentificationMap = dctx.makeValidContext("createPartyIdentification", "IN", context);
+                    createPartyIdentificationMap.put("partyId", resp.get("partyId"));
+                    createPartyIdentificationMap.put("partyIdentificationTypeId", "VCARD_FN_ORIGIN");
+                    createPartyIdentificationMap.put("idValue", formattedName.getValue());
+                    resp = dispatcher.runSync("createPartyIdentification", createPartyIdentificationMap);
                 }
-                contactsCount++;
             }
-        } catch (GenericEntityException e) {
-            Debug.logError(e, module);
-            return ServiceUtil.returnError(UtilProperties.getMessage(resourceError,
-                    "SfaImportVCardError", UtilMisc.toMap("errorString", e.getMessage()), locale));
-        } catch (GenericServiceException e) {
+            vCardReader.close();
+        } catch (IOException | GenericEntityException | GenericServiceException e) {
             Debug.logError(e, module);
-            return ServiceUtil.returnError(UtilProperties.getMessage(resourceError,
+            return ServiceUtil.returnError(UtilProperties.getMessage(resourceError,
                     "SfaImportVCardError", UtilMisc.toMap("errorString", e.getMessage()), locale));
         }
+        result.put("partiesCreated", partiesCreated);
+        result.put("partiesExist", partiesExist);
         return result;
     }
 
@@ -262,61 +250,60 @@ public class VCard {
         Locale locale = (Locale) context.get("locale");
         File file = null;
         try {
-            ContactModelFactory cmf = Pim.getContactModelFactory();
-            Contact contact = cmf.createContact();
-
-            PersonalIdentity pid = cmf.createPersonalIdentity();
+            ezvcard.VCard vcard = new ezvcard.VCard();
+            StructuredName structuredName = new StructuredName();
+            GenericValue person = EntityQuery.use(delegator).from("Person").where("partyId", partyId).queryOne();
+            if (person != null) {
+                if (UtilValidate.isNotEmpty(person.getString("firstName")))
+                    structuredName.setGiven(person.getString("firstName"));
+                if (UtilValidate.isNotEmpty(person.getString("lastName")))
+                    structuredName.setFamily(person.getString("lastName"));
+                vcard.setStructuredName(structuredName);
+            }
             String fullName = PartyHelper.getPartyName(delegator, partyId, false);
-            String[] name = fullName.split("\\s");
-            pid.setFirstname(name[0]);
-            pid.setLastname(name[1]);
-            contact.setPersonalIdentity(pid);
+            vcard.setFormattedName(fullName);
 
             GenericValue postalAddress = PartyWorker.findPartyLatestPostalAddress(partyId, delegator);
-            Address address = cmf.createAddress();
-            address.setStreet(postalAddress.getString("address1"));
-            address.setCity(postalAddress.getString("city"));
-
-            address.setPostalCode(postalAddress.getString("postalCode"));
-            GenericValue state = postalAddress.getRelatedOne("StateProvinceGeo", false);
-            if (UtilValidate.isNotEmpty(state)) {
-                address.setRegion(state.getString("geoName"));
-            }
-            GenericValue countryGeo = postalAddress.getRelatedOne("CountryGeo", false);
-            if (UtilValidate.isNotEmpty(countryGeo)) {
-                String country = postalAddress.getRelatedOne("CountryGeo", false).getString("geoName");
-                address.setCountry(country);
-                address.setWork(true); // this can be better set by checking contactMechPurposeTypeId
+            if (postalAddress != null) {
+                Address address =  new Address();
+                address.setStreetAddress(postalAddress.getString("address1"));
+                address.setLocality(postalAddress.getString("city"));
+                address.setPostalCode(postalAddress.getString("postalCode"));
+                GenericValue state = postalAddress.getRelatedOne("StateProvinceGeo", false);
+                if (UtilValidate.isNotEmpty(state)) {
+                    address.setRegion(state.getString("geoName"));
+                }
+                GenericValue countryGeo = postalAddress.getRelatedOne("CountryGeo", false);
+                if (UtilValidate.isNotEmpty(countryGeo)) {
+                    String country = postalAddress.getRelatedOne("CountryGeo", false).getString("geoName");
+                    address.setCountry(country);
+                    address.getTypes().add(AddressType.WORK);;
+                    //TODO : this can be better set by checking contactMechPurposeTypeId
+                }
+                vcard.addAddress(address);
             }
-            contact.addAddress(address);
-
-            Communications communication = cmf.createCommunications();
-            contact.setCommunications(communication);
 
-            PhoneNumber number = cmf.createPhoneNumber();
             GenericValue telecomNumber = PartyWorker.findPartyLatestTelecomNumber(partyId, delegator);
             if (UtilValidate.isNotEmpty(telecomNumber)) {
-                number.setNumber(telecomNumber.getString("areaCode") + telecomNumber.getString("contactNumber"));
-                number.setWork(true); // this can be better set by checking contactMechPurposeTypeId
-                communication.addPhoneNumber(number);
+                Telephone tel = new Telephone(telecomNumber.getString("areaCode") + telecomNumber.getString("contactNumber"));
+                tel.getTypes().add(TelephoneType.WORK);
+                vcard.addTelephoneNumber(tel);
+                //TODO : this can be better set by checking contactMechPurposeTypeId
             }
-            EmailAddress email = cmf.createEmailAddress();
+
             GenericValue emailAddress = PartyWorker.findPartyLatestContactMech(partyId, "EMAIL_ADDRESS", delegator);
-            if (UtilValidate.isNotEmpty(emailAddress.getString("infoString"))) {
-                email.setAddress(emailAddress.getString("infoString"));
-                communication.addEmailAddress(email);
+            if (emailAddress != null && UtilValidate.isNotEmpty(emailAddress.getString("infoString"))) {
+                vcard.addEmail(new Email(emailAddress.getString("infoString")));
             }
-            ContactIOFactory ciof = Pim.getContactIOFactory();
-            ContactMarshaller marshaller = ciof.createContactMarshaller();
+
+            //TODO : convert to directdownload of a vcf file
             String saveToDirectory = EntityUtilProperties.getPropertyValue("sfa", "save.outgoing.directory", "", delegator);
             if (UtilValidate.isEmpty(saveToDirectory)) {
                 saveToDirectory = System.getProperty("ofbiz.home");
             }
             String saveToFilename = fullName + ".vcf";
             file = FileUtil.getFile(saveToDirectory + "/" + saveToFilename);
-            FileOutputStream outputStream = new FileOutputStream(file);
-            marshaller.marshallContact(outputStream, contact);
-            outputStream.close();
+            Ezvcard.write(vcard).go(file);
         } catch (FileNotFoundException e) {
             Debug.logError(e, module);
             return ServiceUtil.returnError(UtilProperties.getMessage(resourceError,

Modified: ofbiz/trunk/applications/marketing/webapp/sfa/WEB-INF/controller.xml
URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/marketing/webapp/sfa/WEB-INF/controller.xml?rev=1756949&r1=1756948&r2=1756949&view=diff
==============================================================================
--- ofbiz/trunk/applications/marketing/webapp/sfa/WEB-INF/controller.xml (original)
+++ ofbiz/trunk/applications/marketing/webapp/sfa/WEB-INF/controller.xml Fri Aug 19 17:33:15 2016
@@ -131,7 +131,7 @@ under the License.
     <request-map uri="createLeadFromVCard">
         <security https="true" auth="true"/>
         <event type="service" invoke="importVCard"/>
-        <response name="success" type="request" value="viewprofile"/><!-- What would be the view in case a vCard has more than one contact -->
+        <response name="success" type="view" value="ViewPartiesCreatedByVCard"/>
         <response name="error" type="view" value="NewLeadFromVCard"/>
     </request-map>
     <request-map uri="quickAddLead">
@@ -180,7 +180,7 @@ under the License.
     <request-map uri="createContactFromVCard">
         <security https="true" auth="true"/>
         <event type="service" invoke="importVCard"/>
-        <response name="success" type="request" value="viewprofile"/><!-- What would be the view in case a vCard contains more than one contact -->
+        <response name="success" type="view" value="ViewPartiesCreatedByVCard"/>
         <response name="error" type="view" value="NewContactFromVCard"/>
     </request-map>
     <request-map uri="createVCardFromContact">
@@ -276,6 +276,7 @@ under the License.
     <!-- View Mappings -->
     <view-map name="main" type="screen" page="component://marketing/widget/sfa/CommonScreens.xml#main"/>
     <view-map name="viewprofile" type="screen" page="component://marketing/widget/sfa/CommonScreens.xml#ViewProfile"/>
+    <view-map name="ViewPartiesCreatedByVCard" type="screen" page="component://marketing/widget/sfa/CommonScreens.xml#ViewPartiesCreatedByVCard"/>
 
     <view-map name="FindSalesOpportunity" type="screen" page="component://marketing/widget/sfa/OpportunityScreens.xml#FindSalesOpportunity"/>
     <view-map name="ViewSalesOpportunity" type="screen" page="component://marketing/widget/sfa/OpportunityScreens.xml#ViewSalesOpportunity"/>

Modified: ofbiz/trunk/applications/marketing/widget/sfa/CommonScreens.xml
URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/marketing/widget/sfa/CommonScreens.xml?rev=1756949&r1=1756948&r2=1756949&view=diff
==============================================================================
--- ofbiz/trunk/applications/marketing/widget/sfa/CommonScreens.xml (original)
+++ ofbiz/trunk/applications/marketing/widget/sfa/CommonScreens.xml Fri Aug 19 17:33:15 2016
@@ -60,7 +60,7 @@ under the License.
                 <decorator-screen name="main-decorator" location="${parameters.mainDecoratorLocation}">
                     <decorator-section name="left-column">
                         <include-screen name="leftbar"/>
-                    </decorator-section>                    
+                    </decorator-section>
                     <decorator-section name="body">
                         <include-portal-page id="${parameters.portalPageId}"/>
                     </decorator-section>
@@ -96,7 +96,7 @@ under the License.
                     </decorator-section>
                     <decorator-section name="left-column">
                         <include-screen name="leftbar"/>
-                    </decorator-section>                    
+                    </decorator-section>
                     <decorator-section name="body">
                         <decorator-section-include name="body"/>
                     </decorator-section>
@@ -344,6 +344,41 @@ under the License.
             </widgets>
         </section>
     </screen>
+    <screen name="ViewPartiesCreatedByVCard">
+        <section>
+            <widgets>
+                <decorator-screen name="CommonPartyDecorator" location="${parameters.mainDecoratorLocation}">
+                    <decorator-section name="body">
+                        <section>
+                            <condition>
+                                <or>
+                                    <not><if-empty field="parameters.partiesCreated"/></not>
+                                    <not><if-empty field="parameters.partiesExist"/></not>
+                                </or>
+                            </condition>
+                            <actions>
+                                <set field="partiesCreated" from-field="parameters.partiesCreated"/>
+                                <set field="partiesExist" from-field="parameters.partiesExist"/>
+                            </actions>
+                            <widgets>
+                                <screenlet title="${uiLabelMap.MarketingPartiesLoaded}">
+                                    <include-grid name="ViewPartiesCreatedByVCard" location="component://marketing/widget/sfa/forms/CommonForms.xml"/>
+                                </screenlet>
+                                <screenlet title="${uiLabelMap.MarketingPartiesNoLoadedAlreadyExist}">
+                                    <include-grid name="ViewPartiesExistInVCard" location="component://marketing/widget/sfa/forms/CommonForms.xml"/>
+                                </screenlet>
+                            </widgets>
+                            <fail-widgets>
+                                <container>
+                                    <label style="h3">${uiLabelMap.MarketingNoPartyLoad}</label>
+                                </container>
+                            </fail-widgets>
+                        </section>
+                    </decorator-section>
+                </decorator-screen>
+            </widgets>
+        </section>
+    </screen>
     <screen name="CommonCommunicationEventDecorator">
         <section>
             <actions>
@@ -381,7 +416,7 @@ under the License.
                             </decorator-section>
                             <decorator-section name="left-column">
                                 <include-screen name="leftbar"/>
-                            </decorator-section>                    
+                            </decorator-section>
                             <decorator-section name="body">
                                 <include-menu name="OpportunitySubTabBar" location="component://marketing/widget/sfa/SfaMenus.xml"/>
                                 <decorator-section-include name="body"/>
@@ -401,7 +436,7 @@ under the License.
                     </decorator-section>
                     <decorator-section name="left-column">
                         <include-screen name="leftbar"/>
-                    </decorator-section>                    
+                    </decorator-section>
                     <decorator-section name="body">
                         <include-menu name="AccountSubTabBar" location="component://marketing/widget/sfa/SfaMenus.xml"/>
                         <decorator-section-include name="body"/>
@@ -419,7 +454,7 @@ under the License.
                     </decorator-section>
                     <decorator-section name="left-column">
                         <include-screen name="leftbar"/>
-                    </decorator-section>                    
+                    </decorator-section>
                     <decorator-section name="body">
                         <include-menu name="ContactSubTabBar" location="component://marketing/widget/sfa/SfaMenus.xml"/>
                         <decorator-section-include name="body"/>
@@ -463,7 +498,7 @@ under the License.
                     </decorator-section>
                     <decorator-section name="left-column">
                         <include-screen name="leftbar"/>
-                    </decorator-section>                    
+                    </decorator-section>
                     <decorator-section name="body">
                         <include-menu name="EventSubTabBar" location="component://marketing/widget/sfa/SfaMenus.xml"/>
                         <decorator-section-include name="body"/>

Added: ofbiz/trunk/applications/marketing/widget/sfa/forms/CommonForms.xml
URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/marketing/widget/sfa/forms/CommonForms.xml?rev=1756949&view=auto
==============================================================================
--- ofbiz/trunk/applications/marketing/widget/sfa/forms/CommonForms.xml (added)
+++ ofbiz/trunk/applications/marketing/widget/sfa/forms/CommonForms.xml Fri Aug 19 17:33:15 2016
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+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.
+-->
+
+<forms xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xmlns="http://ofbiz.apache.org/Widget-Form" xsi:schemaLocation="http://ofbiz.apache.org/Widget-Form http://ofbiz.apache.org/dtds/widget-form.xsd">
+
+    <grid name="ViewPartiesCreatedByVCard" list-name="partiesCreated" header-row-style="header-row" default-table-style="basic-table">
+        <row-actions>
+            <entity-one entity-name="Party" value-field="party" use-cache="true"/>
+            <set field="partyName" value="${groovy:org.apache.ofbiz.party.party.PartyHelper.getPartyName(party, true)}"/>
+            <entity-and entity-name="PartyAndContactMech" list="emailAddresses">
+                <field-map field-name="partyId"/>
+                <field-map field-name="contactMechTypeId" value="EMAIL_ADDRESS"/>
+            </entity-and>
+            <entity-and entity-name="PartyAndContactMech" list="telecomNumbers">
+                <field-map field-name="partyId"/>
+                <field-map field-name="contactMechTypeId" value="TELECOM_NUMBER"/>
+            </entity-and>
+            <set field="telecomNumber" from-field="telecomNumbers[0]" type="Object"/>
+            <entity-and entity-name="PartyAndContactMech" list="postalAddresses">
+                <field-map field-name="partyId"/>
+                <field-map field-name="contactMechTypeId" value="POSTAL_ADDRESS"/>
+            </entity-and>
+            <set field="postalAddress" from-field="postalAddresses[0]" type="Object"/>
+        </row-actions>
+        <field name="partyId" title="${uiLabelMap.PartyPartyId}">
+            <hyperlink target="viewprofile" description="${partyName} [${partyId}]" target-window="_blank">
+                <parameter param-name="partyId"/>
+                <parameter param-name="roleTypeId"/>
+            </hyperlink>
+        </field>
+        <field name="emailAddress"><display description="${emailAddresses[0].infoString}"/></field>
+        <field name="telecomNumber" title="${uiLabelMap.PartyPhoneNumber}">
+            <display description="${telecomNumber.tnCountryCode} ${telecomNumber.tnAreaCode} ${telecomNumber.tnContactNumber} ${telecomNumber.tnAskForName}"/>
+        </field>
+        <field name="city"><display description="${postalAddress.paCity}"/></field>
+    </grid>
+    <grid name="ViewPartiesExistInVCard" list-name="partiesExist" extends="ViewPartiesCreatedByVCard"/>
+
+</forms>

Propchange: ofbiz/trunk/applications/marketing/widget/sfa/forms/CommonForms.xml
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: ofbiz/trunk/applications/marketing/widget/sfa/forms/CommonForms.xml
------------------------------------------------------------------------------
    svn:keywords = Date Rev Author URL Id

Propchange: ofbiz/trunk/applications/marketing/widget/sfa/forms/CommonForms.xml
------------------------------------------------------------------------------
    svn:mime-type = text/xml

Modified: ofbiz/trunk/build.gradle
URL: http://svn.apache.org/viewvc/ofbiz/trunk/build.gradle?rev=1756949&r1=1756948&r2=1756949&view=diff
==============================================================================
--- ofbiz/trunk/build.gradle (original)
+++ ofbiz/trunk/build.gradle Fri Aug 19 17:33:15 2016
@@ -66,6 +66,7 @@ configurations {
 dependencies {
     // general framework compile libs
     compile 'apache-xerces:xercesImpl:2.9.1'
+    compile 'com.googlecode.ez-vcard:ez-vcard:0.9.10'
     compile 'com.google.zxing:core:3.2.1'
     compile 'com.googlecode.concurrentlinkedhashmap:concurrentlinkedhashmap-lru:1.0'
     compile 'com.googlecode.owasp-java-html-sanitizer:owasp-java-html-sanitizer:20160628.1'
@@ -770,6 +771,7 @@ def createOfbizCommandTask(taskName, arg
         arguments.each { argument ->
             args argument
         }
+
     }
 }