001/* 002 * (C) Copyright 2012-2014 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * Thomas Roger 018 * Florent Guillaume 019 * Julien Carsique 020 */ 021package org.nuxeo.ecm.csv; 022 023import static org.nuxeo.ecm.csv.CSVImportLog.Status.ERROR; 024import static org.nuxeo.ecm.csv.Constants.CSV_NAME_COL; 025import static org.nuxeo.ecm.csv.Constants.CSV_TYPE_COL; 026 027import java.io.BufferedReader; 028import java.io.File; 029import java.io.FileInputStream; 030import java.io.FileNotFoundException; 031import java.io.IOException; 032import java.io.InputStream; 033import java.io.InputStreamReader; 034import java.io.Reader; 035import java.io.Serializable; 036import java.text.DateFormat; 037import java.text.ParseException; 038import java.text.SimpleDateFormat; 039import java.util.ArrayList; 040import java.util.Arrays; 041import java.util.Date; 042import java.util.HashMap; 043import java.util.List; 044import java.util.Map; 045 046import org.apache.commons.csv.CSVFormat; 047import org.apache.commons.csv.CSVParser; 048import org.apache.commons.csv.CSVRecord; 049import org.apache.commons.io.Charsets; 050import org.apache.commons.io.FilenameUtils; 051import org.apache.commons.io.IOUtils; 052import org.apache.commons.io.input.BOMInputStream; 053import org.apache.commons.lang.StringUtils; 054import org.apache.commons.logging.Log; 055import org.apache.commons.logging.LogFactory; 056import org.nuxeo.common.utils.ExceptionUtils; 057import org.nuxeo.common.utils.Path; 058import org.nuxeo.ecm.automation.AutomationService; 059import org.nuxeo.ecm.automation.OperationChain; 060import org.nuxeo.ecm.automation.OperationContext; 061import org.nuxeo.ecm.automation.core.operations.notification.MailTemplateHelper; 062import org.nuxeo.ecm.automation.core.operations.notification.SendMail; 063import org.nuxeo.ecm.automation.core.scripting.Expression; 064import org.nuxeo.ecm.automation.core.scripting.Scripting; 065import org.nuxeo.ecm.automation.core.util.ComplexTypeJSONDecoder; 066import org.nuxeo.ecm.automation.core.util.StringList; 067import org.nuxeo.ecm.core.api.Blobs; 068import org.nuxeo.ecm.core.api.DocumentModel; 069import org.nuxeo.ecm.core.api.DocumentRef; 070import org.nuxeo.ecm.core.api.NuxeoException; 071import org.nuxeo.ecm.core.api.NuxeoPrincipal; 072import org.nuxeo.ecm.core.api.PathRef; 073import org.nuxeo.ecm.core.query.sql.NXQL; 074import org.nuxeo.ecm.core.schema.DocumentType; 075import org.nuxeo.ecm.core.schema.SchemaManager; 076import org.nuxeo.ecm.core.schema.types.ComplexType; 077import org.nuxeo.ecm.core.schema.types.Field; 078import org.nuxeo.ecm.core.schema.types.ListType; 079import org.nuxeo.ecm.core.schema.types.SimpleTypeImpl; 080import org.nuxeo.ecm.core.schema.types.Type; 081import org.nuxeo.ecm.core.schema.types.primitives.BooleanType; 082import org.nuxeo.ecm.core.schema.types.primitives.DateType; 083import org.nuxeo.ecm.core.schema.types.primitives.DoubleType; 084import org.nuxeo.ecm.core.schema.types.primitives.IntegerType; 085import org.nuxeo.ecm.core.schema.types.primitives.LongType; 086import org.nuxeo.ecm.core.schema.types.primitives.StringType; 087import org.nuxeo.ecm.core.work.AbstractWork; 088import org.nuxeo.ecm.csv.CSVImportLog.Status; 089import org.nuxeo.ecm.platform.ec.notification.service.NotificationService; 090import org.nuxeo.ecm.platform.ec.notification.service.NotificationServiceHelper; 091import org.nuxeo.ecm.platform.types.TypeManager; 092import org.nuxeo.ecm.platform.ui.web.rest.api.URLPolicyService; 093import org.nuxeo.ecm.platform.url.DocumentViewImpl; 094import org.nuxeo.ecm.platform.url.api.DocumentView; 095import org.nuxeo.ecm.platform.usermanager.UserManager; 096import org.nuxeo.runtime.api.Framework; 097 098/** 099 * Work task to import form a CSV file. Because the file is read from the local filesystem, this must be executed in a 100 * local queue. Since NXP-15252 the CSV reader manages "records", not "lines". 101 * 102 * @since 5.7 103 */ 104public class CSVImporterWork extends AbstractWork { 105 106 public static final String NUXEO_CSV_MAIL_TO = "nuxeo.csv.mail.to"; 107 108 public static final String LABEL_CSV_IMPORTER_NOT_EXISTING_FIELD = "label.csv.importer.notExistingField"; 109 110 public static final String LABEL_CSV_IMPORTER_CANNOT_CONVERT_FIELD_VALUE = "label.csv.importer.cannotConvertFieldValue"; 111 112 public static final String LABEL_CSV_IMPORTER_NOT_EXISTING_FILE = "label.csv.importer.notExistingFile"; 113 114 public static final String NUXEO_CSV_BLOBS_FOLDER = "nuxeo.csv.blobs.folder"; 115 116 public static final String LABEL_CSV_IMPORTER_DOCUMENT_ALREADY_EXISTS = "label.csv.importer.documentAlreadyExists"; 117 118 public static final String LABEL_CSV_IMPORTER_UNABLE_TO_UPDATE = "label.csv.importer.unableToUpdate"; 119 120 public static final String LABEL_CSV_IMPORTER_DOCUMENT_UPDATED = "label.csv.importer.documentUpdated"; 121 122 public static final String LABEL_CSV_IMPORTER_UNABLE_TO_CREATE = "label.csv.importer.unableToCreate"; 123 124 public static final String LABEL_CSV_IMPORTER_PARENT_DOES_NOT_EXIST = "label.csv.importer.parentDoesNotExist"; 125 126 public static final String LABEL_CSV_IMPORTER_DOCUMENT_CREATED = "label.csv.importer.documentCreated"; 127 128 public static final String LABEL_CSV_IMPORTER_NOT_ALLOWED_SUB_TYPE = "label.csv.importer.notAllowedSubType"; 129 130 public static final String LABEL_CSV_IMPORTER_UNABLE_TO_SAVE = "label.csv.importer.unableToSave"; 131 132 public static final String LABEL_CSV_IMPORTER_ERROR_IMPORTING_LINE = "label.csv.importer.errorImportingLine"; 133 134 public static final String LABEL_CSV_IMPORTER_NOT_EXISTING_TYPE = "label.csv.importer.notExistingType"; 135 136 public static final String LABEL_CSV_IMPORTER_MISSING_TYPE_VALUE = "label.csv.importer.missingTypeValue"; 137 138 public static final String LABEL_CSV_IMPORTER_MISSING_NAME_VALUE = "label.csv.importer.missingNameValue"; 139 140 public static final String LABEL_CSV_IMPORTER_MISSING_NAME_OR_TYPE_COLUMN = "label.csv.importer.missingNameOrTypeColumn"; 141 142 public static final String LABEL_CSV_IMPORTER_EMPTY_FILE = "label.csv.importer.emptyFile"; 143 144 public static final String LABEL_CSV_IMPORTER_ERROR_DURING_IMPORT = "label.csv.importer.errorDuringImport"; 145 146 public static final String LABEL_CSV_IMPORTER_EMPTY_LINE = "label.csv.importer.emptyLine"; 147 148 private static final long serialVersionUID = 1L; 149 150 private static final Log log = LogFactory.getLog(CSVImporterWork.class); 151 152 private static final String TEMPLATE_IMPORT_RESULT = "templates/csvImportResult.ftl"; 153 154 public static final String CATEGORY_CSV_IMPORTER = "csvImporter"; 155 156 public static final String CONTENT_FILED_TYPE_NAME = "content"; 157 158 /** 159 * CSV headers that won't be checked if the field exists on the document type. 160 * 161 * @since 7.3 162 */ 163 public static List<String> AUTHORIZED_HEADERS = Arrays.asList(NXQL.ECM_LIFECYCLESTATE); 164 165 protected String parentPath; 166 167 protected String username; 168 169 protected File csvFile; 170 171 protected String csvFileName; 172 173 protected CSVImporterOptions options; 174 175 protected transient DateFormat dateformat; 176 177 protected Date startDate; 178 179 protected List<CSVImportLog> importLogs = new ArrayList<>(); 180 181 public CSVImporterWork(String id) { 182 super(id); 183 } 184 185 public CSVImporterWork(String repositoryName, String parentPath, String username, File csvFile, String csvFileName, 186 CSVImporterOptions options) { 187 super(CSVImportId.create(repositoryName, parentPath, csvFile)); 188 setDocument(repositoryName, null); 189 setOriginatingUsername(username); 190 this.parentPath = parentPath; 191 this.username = username; 192 this.csvFile = csvFile; 193 this.csvFileName = csvFileName; 194 this.options = options; 195 startDate = new Date(); 196 } 197 198 @Override 199 public String getCategory() { 200 return CATEGORY_CSV_IMPORTER; 201 } 202 203 @Override 204 public String getTitle() { 205 return String.format("CSV import in '%s'", parentPath); 206 } 207 208 public List<CSVImportLog> getImportLogs() { 209 return new ArrayList<>(importLogs); 210 } 211 212 @Override 213 public void work() { 214 setStatus("Importing"); 215 openUserSession(); 216 try (Reader in = newReader(csvFile); 217 CSVParser parser = CSVFormat.DEFAULT.withEscape(options.getEscapeCharacter()).withHeader().parse(in)) { 218 doImport(parser); 219 } catch (IOException e) { 220 logError(0, "Error while doing the import: %s", LABEL_CSV_IMPORTER_ERROR_DURING_IMPORT, e.getMessage()); 221 log.debug(e, e); 222 } 223 if (options.sendEmail()) { 224 setStatus("Sending email"); 225 sendMail(); 226 } 227 setStatus(null); 228 } 229 230 /** 231 * @since 7.3 232 */ 233 protected BufferedReader newReader(File file) throws FileNotFoundException { 234 return new BufferedReader(new InputStreamReader(new BOMInputStream(new FileInputStream(file)))); 235 } 236 237 protected void doImport(CSVParser parser) { 238 log.info(String.format("Importing CSV file: %s", csvFileName)); 239 Map<String, Integer> header = parser.getHeaderMap(); 240 if (header == null) { 241 logError(0, "No header line, empty file?", LABEL_CSV_IMPORTER_EMPTY_FILE); 242 return; 243 } 244 if (!header.containsKey(CSV_NAME_COL) || !header.containsKey(CSV_TYPE_COL)) { 245 logError(0, "Missing 'name' or 'type' column", LABEL_CSV_IMPORTER_MISSING_NAME_OR_TYPE_COLUMN); 246 return; 247 } 248 249 try { 250 int batchSize = options.getBatchSize(); 251 long docsCreatedCount = 0; 252 for (CSVRecord record : parser) { 253 if (record.size() == 0) { 254 // empty record 255 importLogs.add(new CSVImportLog(record.getRecordNumber(), Status.SKIPPED, "Empty record", 256 LABEL_CSV_IMPORTER_EMPTY_LINE)); 257 continue; 258 } 259 try { 260 if (importRecord(record, header)) { 261 docsCreatedCount++; 262 if (docsCreatedCount % batchSize == 0) { 263 commitOrRollbackTransaction(); 264 startTransaction(); 265 } 266 } 267 } catch (NuxeoException e) { 268 // try next line 269 Throwable unwrappedException = unwrapException(e); 270 logError(parser.getRecordNumber(), "Error while importing line: %s", 271 LABEL_CSV_IMPORTER_ERROR_IMPORTING_LINE, unwrappedException.getMessage()); 272 log.debug(unwrappedException, unwrappedException); 273 } 274 } 275 276 try { 277 session.save(); 278 } catch (NuxeoException e) { 279 Throwable ue = unwrapException(e); 280 logError(parser.getRecordNumber(), "Unable to save: %s", LABEL_CSV_IMPORTER_UNABLE_TO_SAVE, 281 ue.getMessage()); 282 log.debug(ue, ue); 283 } 284 } finally { 285 commitOrRollbackTransaction(); 286 startTransaction(); 287 } 288 log.info(String.format("Done importing CSV file: %s", csvFileName)); 289 } 290 291 /** 292 * Import a line from the CSV file. 293 * 294 * @return {@code true} if a document has been created or updated, {@code false} otherwise. 295 * @since 6.0 296 */ 297 protected boolean importRecord(CSVRecord record, Map<String, Integer> header) { 298 final String name = record.get(CSV_NAME_COL); 299 final String type = record.get(CSV_TYPE_COL); 300 if (StringUtils.isBlank(name)) { 301 log.debug("record.isSet=" + record.isSet(CSV_NAME_COL)); 302 logError(record.getRecordNumber(), "Missing 'name' value", LABEL_CSV_IMPORTER_MISSING_NAME_VALUE); 303 return false; 304 } 305 if (StringUtils.isBlank(type)) { 306 log.debug("record.isSet=" + record.isSet(CSV_TYPE_COL)); 307 logError(record.getRecordNumber(), "Missing 'type' value", LABEL_CSV_IMPORTER_MISSING_TYPE_VALUE); 308 return false; 309 } 310 DocumentType docType = Framework.getLocalService(SchemaManager.class).getDocumentType(type); 311 if (docType == null) { 312 logError(record.getRecordNumber(), "The type '%s' does not exist", LABEL_CSV_IMPORTER_NOT_EXISTING_TYPE, 313 type); 314 return false; 315 } 316 Map<String, Serializable> values = computePropertiesMap(record, docType, header); 317 if (values == null) { 318 // skip this line 319 return false; 320 } 321 return createOrUpdateDocument(record.getRecordNumber(), name, type, values); 322 } 323 324 /** 325 * @since 6.0 326 */ 327 protected Map<String, Serializable> computePropertiesMap(CSVRecord record, DocumentType docType, 328 Map<String, Integer> header) { 329 Map<String, Serializable> values = new HashMap<>(); 330 for (String headerValue : header.keySet()) { 331 String lineValue = record.get(headerValue); 332 lineValue = lineValue.trim(); 333 String fieldName = headerValue; 334 if (!CSV_NAME_COL.equals(headerValue) && !CSV_TYPE_COL.equals(headerValue)) { 335 if (AUTHORIZED_HEADERS.contains(headerValue) && !StringUtils.isBlank(lineValue)) { 336 values.put(headerValue, lineValue); 337 } else { 338 if (!docType.hasField(fieldName)) { 339 fieldName = fieldName.split(":")[1]; 340 } 341 if (docType.hasField(fieldName) && !StringUtils.isBlank(lineValue)) { 342 Serializable convertedValue = convertValue(docType, fieldName, headerValue, lineValue, 343 record.getRecordNumber()); 344 if (convertedValue == null) { 345 return null; 346 } 347 values.put(headerValue, convertedValue); 348 } 349 } 350 } 351 } 352 return values; 353 } 354 355 protected Serializable convertValue(DocumentType docType, String fieldName, String headerValue, String stringValue, 356 long lineNumber) { 357 if (docType.hasField(fieldName)) { 358 Field field = docType.getField(fieldName); 359 if (field != null) { 360 try { 361 Serializable fieldValue = null; 362 Type fieldType = field.getType(); 363 if (fieldType.isComplexType()) { 364 if (fieldType.getName().equals(CONTENT_FILED_TYPE_NAME)) { 365 String blobsFolderPath = Framework.getProperty(NUXEO_CSV_BLOBS_FOLDER); 366 String path = FilenameUtils.normalize(blobsFolderPath + "/" + stringValue); 367 File file = new File(path); 368 if (file.exists()) { 369 fieldValue = (Serializable) Blobs.createBlob(file); 370 } else { 371 logError(lineNumber, "The file '%s' does not exist", 372 LABEL_CSV_IMPORTER_NOT_EXISTING_FILE, stringValue); 373 return null; 374 } 375 } else { 376 fieldValue = (Serializable) ComplexTypeJSONDecoder.decode((ComplexType) fieldType, 377 stringValue); 378 } 379 } else { 380 if (fieldType.isListType()) { 381 Type listFieldType = ((ListType) fieldType).getFieldType(); 382 if (listFieldType.isSimpleType()) { 383 /* 384 * Array. 385 */ 386 fieldValue = stringValue.split(options.getListSeparatorRegex()); 387 } else { 388 /* 389 * Complex list. 390 */ 391 fieldValue = (Serializable) ComplexTypeJSONDecoder.decodeList((ListType) fieldType, 392 stringValue); 393 } 394 } else { 395 /* 396 * Primitive type. 397 */ 398 Type type = field.getType(); 399 if (type instanceof SimpleTypeImpl) { 400 type = type.getSuperType(); 401 } 402 if (type.isSimpleType()) { 403 if (type instanceof StringType) { 404 fieldValue = stringValue; 405 } else if (type instanceof IntegerType) { 406 fieldValue = Integer.valueOf(stringValue); 407 } else if (type instanceof LongType) { 408 fieldValue = Long.valueOf(stringValue); 409 } else if (type instanceof DoubleType) { 410 fieldValue = Double.valueOf(stringValue); 411 } else if (type instanceof BooleanType) { 412 fieldValue = Boolean.valueOf(stringValue); 413 } else if (type instanceof DateType) { 414 fieldValue = getDateFormat().parse(stringValue); 415 } 416 } 417 } 418 } 419 return fieldValue; 420 } catch (ParseException | NumberFormatException | IOException e) { 421 logError(lineNumber, "Unable to convert field '%s' with value '%s'", 422 LABEL_CSV_IMPORTER_CANNOT_CONVERT_FIELD_VALUE, headerValue, stringValue); 423 log.debug(e, e); 424 } 425 } 426 } else { 427 logError(lineNumber, "Field '%s' does not exist on type '%s'", LABEL_CSV_IMPORTER_NOT_EXISTING_FIELD, 428 headerValue, docType.getName()); 429 } 430 return null; 431 } 432 433 protected DateFormat getDateFormat() { 434 // transient field so may become null 435 if (dateformat == null) { 436 dateformat = new SimpleDateFormat(options.getDateFormat()); 437 } 438 return dateformat; 439 } 440 441 protected boolean createOrUpdateDocument(long lineNumber, String name, String type, 442 Map<String, Serializable> properties) { 443 Path targetPath = new Path(parentPath).append(name); 444 name = targetPath.lastSegment(); 445 String newParentPath = targetPath.removeLastSegments(1).toString(); 446 DocumentRef docRef = new PathRef(targetPath.toString()); 447 if (options.getCSVImporterDocumentFactory().exists(session, newParentPath, name, type, properties)) { 448 return updateDocument(lineNumber, docRef, properties); 449 } else { 450 return createDocument(lineNumber, newParentPath, name, type, properties); 451 } 452 } 453 454 protected boolean createDocument(long lineNumber, String newParentPath, String name, String type, 455 Map<String, Serializable> properties) { 456 try { 457 DocumentRef parentRef = new PathRef(newParentPath); 458 if (session.exists(parentRef)) { 459 DocumentModel parent = session.getDocument(parentRef); 460 461 TypeManager typeManager = Framework.getLocalService(TypeManager.class); 462 if (options.checkAllowedSubTypes() && !typeManager.isAllowedSubType(type, parent.getType())) { 463 logError(lineNumber, "'%s' type is not allowed in '%s'", LABEL_CSV_IMPORTER_NOT_ALLOWED_SUB_TYPE, 464 type, parent.getType()); 465 } else { 466 options.getCSVImporterDocumentFactory().createDocument(session, newParentPath, name, type, 467 properties); 468 importLogs.add(new CSVImportLog(lineNumber, Status.SUCCESS, "Document created", 469 LABEL_CSV_IMPORTER_DOCUMENT_CREATED)); 470 return true; 471 } 472 } else { 473 logError(lineNumber, "Parent document '%s' does not exist", LABEL_CSV_IMPORTER_PARENT_DOES_NOT_EXIST, 474 newParentPath); 475 } 476 } catch (RuntimeException e) { 477 Throwable unwrappedException = unwrapException(e); 478 logError(lineNumber, "Unable to create document: %s", LABEL_CSV_IMPORTER_UNABLE_TO_CREATE, 479 unwrappedException.getMessage()); 480 log.debug(unwrappedException, unwrappedException); 481 } 482 return false; 483 } 484 485 protected boolean updateDocument(long lineNumber, DocumentRef docRef, Map<String, Serializable> properties) { 486 if (options.updateExisting()) { 487 try { 488 options.getCSVImporterDocumentFactory().updateDocument(session, docRef, properties); 489 importLogs.add(new CSVImportLog(lineNumber, Status.SUCCESS, "Document updated", 490 LABEL_CSV_IMPORTER_DOCUMENT_UPDATED)); 491 return true; 492 } catch (RuntimeException e) { 493 Throwable unwrappedException = unwrapException(e); 494 logError(lineNumber, "Unable to update document: %s", LABEL_CSV_IMPORTER_UNABLE_TO_UPDATE, 495 unwrappedException.getMessage()); 496 log.debug(unwrappedException, unwrappedException); 497 } 498 } else { 499 importLogs.add(new CSVImportLog(lineNumber, Status.SKIPPED, "Document already exists", 500 LABEL_CSV_IMPORTER_DOCUMENT_ALREADY_EXISTS)); 501 } 502 return false; 503 } 504 505 protected void logError(long lineNumber, String message, String localizedMessage, String... params) { 506 importLogs.add(new CSVImportLog(lineNumber, ERROR, String.format(message, (Object[]) params), localizedMessage, 507 params)); 508 String lineMessage = String.format("Line %d", lineNumber); 509 String errorMessage = String.format(message, (Object[]) params); 510 log.error(String.format("%s: %s", lineMessage, errorMessage)); 511 } 512 513 protected void sendMail() { 514 UserManager userManager = Framework.getLocalService(UserManager.class); 515 NuxeoPrincipal principal = userManager.getPrincipal(username); 516 String email = principal.getEmail(); 517 if (email == null) { 518 log.info(String.format("Not sending import result email to '%s', no email configured", username)); 519 return; 520 } 521 522 OperationContext ctx = new OperationContext(session); 523 ctx.setInput(session.getRootDocument()); 524 525 CSVImporter csvImporter = Framework.getLocalService(CSVImporter.class); 526 List<CSVImportLog> importerLogs = csvImporter.getImportLogs(getId()); 527 CSVImportResult importResult = CSVImportResult.fromImportLogs(importerLogs); 528 List<CSVImportLog> skippedAndErrorImportLogs = csvImporter.getImportLogs(getId(), Status.SKIPPED, Status.ERROR); 529 ctx.put("importResult", importResult); 530 ctx.put("skippedAndErrorImportLogs", skippedAndErrorImportLogs); 531 ctx.put("csvFilename", csvFileName); 532 ctx.put("startDate", DateFormat.getInstance().format(startDate)); 533 ctx.put("username", username); 534 535 DocumentModel importFolder = session.getDocument(new PathRef(parentPath)); 536 String importFolderUrl = getDocumentUrl(importFolder); 537 ctx.put("importFolderTitle", importFolder.getTitle()); 538 ctx.put("importFolderUrl", importFolderUrl); 539 ctx.put("userUrl", getUserUrl()); 540 541 StringList to = buildRecipientsList(email); 542 Expression from = Scripting.newExpression("Env[\"mail.from\"]"); 543 String subject = "CSV Import result of " + csvFileName; 544 String message = loadTemplate(TEMPLATE_IMPORT_RESULT); 545 546 try { 547 OperationChain chain = new OperationChain("SendMail"); 548 chain.add(SendMail.ID) 549 .set("from", from) 550 .set("to", to) 551 .set("HTML", true) 552 .set("subject", subject) 553 .set("message", message); 554 Framework.getLocalService(AutomationService.class).run(ctx, chain); 555 } catch (Exception e) { 556 ExceptionUtils.checkInterrupt(e); 557 log.error(String.format("Unable to notify user '%s' for import result of '%s': %s", username, csvFileName, 558 e.getMessage())); 559 log.debug(e, e); 560 throw ExceptionUtils.runtimeException(e); 561 } 562 } 563 564 protected String getDocumentUrl(DocumentModel doc) { 565 return MailTemplateHelper.getDocumentUrl(doc, null); 566 } 567 568 protected String getUserUrl() { 569 NotificationService notificationService = NotificationServiceHelper.getNotificationService(); 570 Map<String, String> params = new HashMap<>(); 571 params.put("username", username); 572 DocumentView docView = new DocumentViewImpl(null, null, params); 573 URLPolicyService urlPolicyService = Framework.getLocalService(URLPolicyService.class); 574 return urlPolicyService.getUrlFromDocumentView("user", docView, notificationService.getServerUrlPrefix()); 575 } 576 577 protected StringList buildRecipientsList(String userEmail) { 578 String csvMailTo = Framework.getProperty(NUXEO_CSV_MAIL_TO); 579 if (StringUtils.isBlank(csvMailTo)) { 580 return new StringList(new String[] { userEmail }); 581 } else { 582 return new StringList(new String[] { userEmail, csvMailTo }); 583 } 584 } 585 586 private static String loadTemplate(String key) { 587 InputStream io = CSVImporterWork.class.getClassLoader().getResourceAsStream(key); 588 if (io != null) { 589 try { 590 return IOUtils.toString(io, Charsets.UTF_8); 591 } catch (IOException e) { 592 // cannot happen 593 throw new NuxeoException(e); 594 } finally { 595 try { 596 io.close(); 597 } catch (IOException e) { 598 // nothing to do 599 } 600 } 601 } 602 return null; 603 } 604 605 public static Throwable unwrapException(Throwable t) { 606 Throwable cause = null; 607 if (t != null) { 608 cause = t.getCause(); 609 } 610 if (cause == null) { 611 return t; 612 } else { 613 return unwrapException(cause); 614 } 615 } 616 617}