Adapt after contact refactoring
[gpl/argeo-suite.git] / org.argeo.suite.workbench.rap / src / org / argeo / suite / workbench / commands / ImportEntities.java
1 package org.argeo.suite.workbench.commands;
2
3 import static org.argeo.connect.util.JxlUtils.getStringValue;
4 import static org.argeo.eclipse.ui.EclipseUiUtils.notEmpty;
5
6 import java.io.File;
7 import java.io.FileInputStream;
8 import java.io.FileOutputStream;
9 import java.io.IOException;
10 import java.io.InputStream;
11 import java.io.OutputStream;
12 import java.util.Collections;
13 import java.util.HashMap;
14 import java.util.Map;
15 import java.util.UUID;
16
17 import javax.jcr.Node;
18 import javax.jcr.Property;
19 import javax.jcr.Repository;
20 import javax.jcr.RepositoryException;
21 import javax.jcr.Session;
22
23 import org.apache.commons.io.IOUtils;
24 import org.argeo.connect.ConnectNames;
25 import org.argeo.connect.resources.ResourcesNames;
26 import org.argeo.connect.resources.ResourcesService;
27 import org.argeo.connect.util.ConnectJcrUtils;
28 import org.argeo.connect.util.JxlUtils;
29 import org.argeo.eclipse.ui.EclipseUiUtils;
30 import org.argeo.jcr.JcrUtils;
31 import org.argeo.people.ContactValueCatalogs;
32 import org.argeo.people.PeopleException;
33 import org.argeo.people.PeopleNames;
34 import org.argeo.people.PeopleService;
35 import org.argeo.people.PeopleTypes;
36 import org.argeo.people.util.PeopleJcrUtils;
37 import org.argeo.people.util.PersonJcrUtils;
38 import org.argeo.suite.SuiteException;
39 import org.argeo.suite.workbench.AsUiPlugin;
40 import org.eclipse.core.commands.AbstractHandler;
41 import org.eclipse.core.commands.ExecutionEvent;
42 import org.eclipse.core.commands.ExecutionException;
43 import org.eclipse.jface.wizard.Wizard;
44 import org.eclipse.jface.wizard.WizardDialog;
45 import org.eclipse.jface.wizard.WizardPage;
46 import org.eclipse.rap.fileupload.FileDetails;
47 import org.eclipse.rap.fileupload.FileUploadEvent;
48 import org.eclipse.rap.fileupload.FileUploadHandler;
49 import org.eclipse.rap.fileupload.FileUploadListener;
50 import org.eclipse.rap.fileupload.FileUploadReceiver;
51 import org.eclipse.rap.rwt.service.ServerPushSession;
52 import org.eclipse.rap.rwt.widgets.FileUpload;
53 import org.eclipse.swt.SWT;
54 import org.eclipse.swt.events.ModifyEvent;
55 import org.eclipse.swt.events.ModifyListener;
56 import org.eclipse.swt.events.SelectionAdapter;
57 import org.eclipse.swt.events.SelectionEvent;
58 import org.eclipse.swt.layout.GridData;
59 import org.eclipse.swt.layout.GridLayout;
60 import org.eclipse.swt.widgets.Combo;
61 import org.eclipse.swt.widgets.Composite;
62 import org.eclipse.swt.widgets.Control;
63 import org.eclipse.swt.widgets.Label;
64 import org.eclipse.swt.widgets.Shell;
65 import org.eclipse.ui.handlers.HandlerUtil;
66
67 import jxl.Sheet;
68 import jxl.Workbook;
69
70 /** Open a one page wizard to import an EXCEL 2003 legacy organisation file */
71 public class ImportEntities extends AbstractHandler implements PeopleNames {
72
73         public final static String ID = AsUiPlugin.PLUGIN_ID + ".importEntities";
74
75         // public final static String PARAM_NODE_TYPE = "param.nodeType";
76
77         private static final Map<String, String> KNOWN_TEMPLATES;
78         static {
79                 Map<String, String> tmpMap = new HashMap<String, String>();
80                 tmpMap.put("Organisations", PeopleTypes.PEOPLE_ORG);
81                 KNOWN_TEMPLATES = Collections.unmodifiableMap(tmpMap);
82         }
83
84         // TODO make this configurable
85         private final static String IMPORT_ENCODING = "ISO-8859-1";// "UTF-8";
86
87         /* DEPENDENCY INJECTION */
88         private Repository repository;
89         private ResourcesService resourcesService;
90         private PeopleService peopleService;
91
92         public Object execute(final ExecutionEvent event) throws ExecutionException {
93                 // String jcrId = event.getParameter(PARAM_NODE_TYPE);
94
95                 Wizard wizard = new ImportMappingFileWizard(HandlerUtil.getActiveShell(event),
96                                 "Upload legacy contact via Excel file import");
97                 WizardDialog dialog = new WizardDialog(HandlerUtil.getActiveShell(event), wizard);
98                 dialog.open();
99                 return null;
100         }
101
102         /** One page wizard to import a EXCEL 2003 Mapping files */
103         private class ImportMappingFileWizard extends Wizard {
104
105                 // Various UI Objects
106                 private UserInputPage userInputPage;
107                 private Combo resourceTypeCombo;
108
109                 // File upload
110                 private FileUpload fileUpload;
111                 private Label fileNameLabel;
112                 private ServerPushSession pushSession;
113                 private File file;
114
115                 public ImportMappingFileWizard(Shell parentShell, String title) {
116                         setWindowTitle(title);
117                 }
118
119                 @Override
120                 public void addPages() {
121                         try {
122                                 userInputPage = new UserInputPage("User input page");
123                                 addPage(userInputPage);
124                         } catch (Exception e) {
125                                 throw new SuiteException("Cannot add page to wizard", e);
126                         }
127                 }
128
129                 /** Performs the real import. */
130                 @Override
131                 public boolean performFinish() {
132                         // Session session = null;
133                         // String templateName =
134                         // resourceTypeCombo.getItem(resourceTypeCombo.getSelectionIndex());
135                         // String type = KNOWN_TEMPLATES.get(templateName);
136                         importDefaultOrgFile(file);
137                         return true;
138                 }
139
140                 @Override
141                 public boolean performCancel() {
142                         return true;
143                 }
144
145                 @Override
146                 public boolean canFinish() {
147                         if (resourceTypeCombo.getSelectionIndex() < 0) {
148                                 userInputPage.setErrorMessage("Please choose an entity type");
149                                 return false;
150                         } else if (file == null) {
151                                 userInputPage.setErrorMessage("Please upload a file");
152                                 return false;
153                         } else {
154                                 userInputPage.setErrorMessage(null);
155                                 return true;
156                         }
157                 }
158
159                 private class UserInputPage extends WizardPage {
160                         private static final long serialVersionUID = 1L;
161
162                         public UserInputPage(String pageName) {
163                                 super(pageName);
164                                 setTitle("Upload an Excel 2003 file (.xls)");
165                         }
166
167                         public void createControl(Composite parent) {
168                                 parent.setLayout(new GridLayout(1, false));
169                                 Composite composite = new Composite(parent, SWT.NONE);
170                                 composite.setLayout(new GridLayout(2, false));
171                                 composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
172
173                                 // Import type
174                                 resourceTypeCombo = createLC(composite, "Type");
175                                 resourceTypeCombo.addModifyListener(new ModifyListener() {
176                                         private static final long serialVersionUID = 1L;
177
178                                         @Override
179                                         public void modifyText(ModifyEvent event) {
180                                                 getWizard().getContainer().updateButtons();
181                                         }
182                                 });
183                                 resourceTypeCombo.setItems(KNOWN_TEMPLATES.keySet().toArray(new String[0]));
184                                 resourceTypeCombo.select(0);
185
186                                 // File upload
187                                 Label lbl = new Label(composite, SWT.NONE);
188                                 lbl.setText("Chosen file");
189                                 lbl.setFont(EclipseUiUtils.getBoldFont(composite));
190                                 Composite uploadCmp = new Composite(composite, SWT.NONE);
191                                 uploadCmp.setLayoutData(EclipseUiUtils.fillWidth());
192                                 createFileUploadArea(uploadCmp);
193                                 setControl(composite);
194                         }
195                 }
196
197                 private Control createFileUploadArea(Composite parent) {
198                         GridLayout gl = EclipseUiUtils.noSpaceGridLayout(new GridLayout(2, false));
199                         gl.horizontalSpacing = 5;
200                         parent.setLayout(gl);
201
202                         fileNameLabel = new Label(parent, SWT.NONE | SWT.BEGINNING);
203                         fileNameLabel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
204
205                         fileUpload = new FileUpload(parent, SWT.NONE);
206                         fileUpload.setText("Browse...");
207                         fileUpload.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));
208
209                         final String url = startUploadReceiver();
210                         pushSession = new ServerPushSession();
211
212                         fileUpload.addSelectionListener(new SelectionAdapter() {
213                                 private static final long serialVersionUID = 1L;
214
215                                 @Override
216                                 public void widgetSelected(SelectionEvent e) {
217                                         String fileName = fileUpload.getFileName();
218                                         fileNameLabel.setText(fileName == null ? "" : fileName);
219                                         pushSession.start();
220                                         fileUpload.submit(url);
221                                 }
222                         });
223                         return parent;
224                 }
225
226                 private String startUploadReceiver() {
227                         MyFileUploadReceiver receiver = new MyFileUploadReceiver();
228                         FileUploadHandler uploadHandler = new FileUploadHandler(receiver);
229                         uploadHandler.addUploadListener(new FileUploadListener() {
230
231                                 public void uploadProgress(FileUploadEvent event) {
232                                         // handle upload progress
233                                 }
234
235                                 public void uploadFailed(FileUploadEvent event) {
236                                         ImportMappingFileWizard.this.userInputPage
237                                                         .setErrorMessage("upload failed: " + event.getException());
238                                 }
239
240                                 public void uploadFinished(FileUploadEvent event) {
241                                         fileNameLabel.getDisplay().asyncExec(new Runnable() {
242                                                 public void run() {
243                                                         ImportMappingFileWizard.this.getContainer().updateButtons();
244                                                         pushSession.stop();
245                                                 }
246                                         });
247                                 }
248                         });
249                         return uploadHandler.getUploadUrl();
250                 }
251
252                 private class MyFileUploadReceiver extends FileUploadReceiver {
253
254                         private static final String TEMP_FILE_PREFIX = "fileupload_";
255
256                         @Override
257                         public void receive(InputStream dataStream, FileDetails details) throws IOException {
258                                 File result = File.createTempFile(TEMP_FILE_PREFIX, "");
259                                 FileOutputStream outputStream = new FileOutputStream(result);
260                                 try {
261                                         copy(dataStream, outputStream);
262                                 } finally {
263                                         dataStream.close();
264                                         outputStream.close();
265                                 }
266                                 if (file != null)
267                                         file.delete();
268                                 file = result;
269                         }
270                 }
271
272                 private void copy(InputStream inputStream, OutputStream outputStream) throws IOException {
273                         byte[] buffer = new byte[8192];
274                         boolean finished = false;
275                         while (!finished) {
276                                 int bytesRead = inputStream.read(buffer);
277                                 if (bytesRead != -1) {
278                                         outputStream.write(buffer, 0, bytesRead);
279                                 } else {
280                                         finished = true;
281                                 }
282                         }
283                 }
284
285                 /** Creates label and Combo. */
286                 protected Combo createLC(Composite parent, String label) {
287                         Label lbl = new Label(parent, SWT.RIGHT);
288                         lbl.setText(label);
289                         lbl.setFont(EclipseUiUtils.getBoldFont(parent));
290                         lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false));
291                         Combo combo = new Combo(parent, SWT.READ_ONLY);
292                         combo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
293                         return combo;
294                 }
295         }
296
297         // Legacy Organisations
298         private Node importDefaultOrgFile(File file) {
299                 InputStream in = null;
300                 try {
301                         in = new FileInputStream(file);
302                         return importDefaultOrgFile(in);
303                 } catch (IOException e) {
304                         throw new SuiteException("Cannot import mapping file", e);
305                 } finally {
306                         IOUtils.closeQuietly(in);
307                 }
308         }
309
310         private Node createDraftNode(Node parent, String mainMixin) throws RepositoryException {
311                 String uuid = UUID.randomUUID().toString();
312                 Node tmpNode = parent.addNode(uuid);
313                 tmpNode.addMixin(mainMixin);
314                 tmpNode.setProperty(ConnectNames.CONNECT_UID, uuid);
315                 return tmpNode;
316         }
317
318         private void importOrgEmployees(Node tmpParent, Node targetParent, Node newOrgNode, String coworkersStr)
319                         throws RepositoryException {
320                 String[] coworkers = coworkersStr.split("\\n");
321                 loop: for (String line : coworkers) {
322                         if (EclipseUiUtils.isEmpty(line))
323                                 continue loop;
324                         line = line.trim();
325                         int index = line.indexOf(' ');
326                         String firstName = null;
327                         String lastName = null;
328                         String position = null;
329                         if (index == -1)
330                                 firstName = line;
331                         else {
332                                 firstName = line.substring(0, index);
333                                 line = line.substring(index);
334
335                                 index = line.indexOf('(');
336                                 if (index == -1)
337                                         lastName = line;
338                                 else {
339                                         lastName = line.substring(0, index).trim();
340                                         position = line.substring(index + 1, line.length() - 1);
341                                 }
342                         }
343                         Node tmpPerson = createDraftNode(tmpParent, PeopleTypes.PEOPLE_PERSON);
344                         tmpPerson.setProperty(PEOPLE_FIRST_NAME, firstName);
345                         if (EclipseUiUtils.notEmpty(lastName))
346                                 tmpPerson.setProperty(PEOPLE_LAST_NAME, lastName);
347                         Node newPersonNode = peopleService.createEntity(targetParent, PeopleTypes.PEOPLE_PERSON, tmpPerson);
348                         // if (EclipseUiUtils.notEmpty(position))
349                         PersonJcrUtils.addJob(resourcesService, peopleService, newPersonNode, newOrgNode, position, true);
350                         // Save the newly created entity without creating a base version
351                         newPersonNode = peopleService.saveEntity(newPersonNode, false);
352
353                 }
354         }
355
356         private void importUrls(Node contactable, String urlStr) throws RepositoryException {
357                 String[] urls = urlStr.split("\\n");
358                 boolean hasPrimary = false;
359                 boolean hasPrimaryFacebook = false;
360
361                 loop: for (String line : urls) {
362                         if (EclipseUiUtils.isEmpty(line))
363                                 continue loop;
364                         line = line.trim();
365
366                         if (line.startsWith("https://www.facebook.com")) {
367                                 PeopleJcrUtils.createSocialMedia(resourcesService, peopleService, contactable, line,
368                                                 !hasPrimaryFacebook, ContactValueCatalogs.CONTACT_CAT_FACEBOOK, null);
369                                 hasPrimaryFacebook = true;
370                         } else {
371                                 PeopleJcrUtils.createWebsite(resourcesService, peopleService, contactable, line, !hasPrimary, null);
372                                 hasPrimary = true;
373                         }
374                 }
375         }
376
377         private void importMails(Node contactable, String mailStr) throws RepositoryException {
378                 String[] urls = mailStr.split("\\n");
379                 boolean hasPrimary = false;
380                 loop: for (String line : urls) {
381                         if (EclipseUiUtils.isEmpty(line))
382                                 continue loop;
383                         line = line.trim();
384                         PeopleJcrUtils.createEmail(resourcesService, peopleService, contactable, line, !hasPrimary, null, null);
385                         hasPrimary = true;
386                 }
387         }
388
389         // TODO make this configurable
390         int displayNameIndex = 0;
391         int legalNameIndex = 1;
392         int legalFormIndex = 2;
393         int urlsIndex = 3;
394         int streetIndex = 4;
395         int postalCodeIndex = 5;
396         int lIndex = 6;
397         int stIndex = 7;
398         int cIndex = 8;
399         int mobileIndex = 9;
400         int telephoneNumberIndex = 10;
401         int mailIndex = 11;
402         int contactsIndex = 12;
403         int descriptionIndex = 13;
404         int tagsIndex = 14;
405
406         private Node importDefaultOrgFile(InputStream in) throws IOException {
407                 Session session = null;
408                 int i = 0;
409                 try {
410                         Workbook wb = JxlUtils.toWorkbook(in, IMPORT_ENCODING);
411                         session = repository.login();
412                         String basePath = "/" + peopleService.getBaseRelPath(PeopleTypes.PEOPLE_ORG);
413                         Node targetParent = session.getNode(basePath);
414                         Sheet sheet = wb.getSheet(0);
415
416                         Node tmpParent = peopleService.getDraftParent(session);
417
418                         int rowNb = sheet.getRows();
419                         for (i = 1; i < rowNb - 1; i++) {
420
421                                 Node tmpOrg = createDraftNode(tmpParent, PeopleTypes.PEOPLE_ORG);
422
423                                 String dName = JxlUtils.getStringValue(sheet, displayNameIndex, i);
424                                 if (notEmpty(dName))
425                                         tmpOrg.setProperty(PeopleNames.PEOPLE_DISPLAY_NAME, dName);
426                                 String lName = getStringValue(sheet, legalNameIndex, i);
427                                 if (notEmpty(lName))
428                                         tmpOrg.setProperty(PeopleNames.PEOPLE_LEGAL_NAME, lName);
429                                 String lForm = getStringValue(sheet, legalFormIndex, i);
430                                 if (notEmpty(lForm))
431                                         tmpOrg.setProperty(PeopleNames.PEOPLE_LEGAL_FORM, lForm);
432                                 String urlStr = getStringValue(sheet, urlsIndex, i);
433                                 if (notEmpty(urlStr))
434                                         importUrls(tmpOrg, urlStr);
435                                 String mailStr = getStringValue(sheet, mailIndex, i);
436                                 if (notEmpty(mailStr))
437                                         importMails(tmpOrg, mailStr);
438                                 String streetStr = getStringValue(sheet, streetIndex, i);
439                                 String pcStr = getStringValue(sheet, postalCodeIndex, i);
440                                 String lStr = getStringValue(sheet, lIndex, i);
441                                 String stStr = getStringValue(sheet, stIndex, i);
442                                 String cStr = getStringValue(sheet, cIndex, i);
443                                 if (notEmpty(streetStr) || notEmpty(pcStr) || notEmpty(lStr) || notEmpty(stStr) || notEmpty(cStr))
444                                         PeopleJcrUtils.createAddress(resourcesService, peopleService, tmpOrg, streetStr, null, pcStr, lStr,
445                                                         stStr, cStr, true, ContactValueCatalogs.CONTACT_CAT_MAIN, null);
446                                 String mobileStr = getStringValue(sheet, mobileIndex, i);
447                                 if (notEmpty(mobileStr))
448                                         PeopleJcrUtils.createPhone(resourcesService, peopleService, tmpOrg, mobileStr, true, null, null);
449                                 String phoneStr = getStringValue(sheet, telephoneNumberIndex, i);
450                                 if (notEmpty(phoneStr))
451                                         PeopleJcrUtils.createPhone(resourcesService, peopleService, tmpOrg, phoneStr, true,
452                                                         ContactValueCatalogs.CONTACT_CAT_DIRECT, null);
453                                 String descStr = getStringValue(sheet, descriptionIndex, i);
454                                 if (notEmpty(descStr))
455                                         tmpOrg.setProperty(Property.JCR_DESCRIPTION, descStr);
456                                 String tagsStr = getStringValue(sheet, tagsIndex, i);
457                                 if (notEmpty(tagsStr))
458                                         tmpOrg.setProperty(ResourcesNames.CONNECT_TAGS, ConnectJcrUtils.parseAndClean(tagsStr, ",", true));
459
460                                 Node newOrgNode = peopleService.createEntity(targetParent, PeopleTypes.PEOPLE_ORG, tmpOrg);
461                                 // Save the newly created entity without creating a base version
462                                 newOrgNode = peopleService.saveEntity(newOrgNode, false);
463
464                                 String contactsStr = getStringValue(sheet, contactsIndex, i);
465                                 if (notEmpty(contactsStr))
466                                         importOrgEmployees(tmpParent, targetParent, newOrgNode, contactsStr);
467                         }
468                 } catch (PeopleException | RepositoryException e) {
469                         throw new SuiteException("Cannot import mapping file, error at line: " + (i + 1), e);
470                 } finally {
471                         JcrUtils.logoutQuietly(session);
472                 }
473
474                 return null;
475         }
476
477         /* DEPENDENCY INJECTION */
478         public void setRepository(Repository repository) {
479                 this.repository = repository;
480         }
481
482         public void setResourcesService(ResourcesService resourcesService) {
483                 this.resourcesService = resourcesService;
484         }
485
486         public void setPeopleService(PeopleService peopleService) {
487                 this.peopleService = peopleService;
488         }
489 }