]> git.argeo.org Git - gpl/argeo-suite.git/blob - src/org/argeo/suite/workbench/commands/ImportEntities.java
Prepare next development cycle
[gpl/argeo-suite.git] / 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 }