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