From 2fc452d1b72dffbd5183c29adceb91ec3a9e1c1b Mon Sep 17 00:00:00 2001 From: miciele Ghiorghis Date: Tue, 19 Mar 2013 17:33:34 +0100 Subject: [PATCH] jira and tim -connector: Refactoring OrderCRUDController and other improvements - OrderCRUDController refactored - Save or update for OrderSyncInfo instead of creating new instance for each sync - Name change for TimImpExpInfo to SynchronizationInfo - JiraSyncInfo is removed instead SynchronizationInfo is used - All files which are affected by name changes are modified - Translations added where applicable - clean up unused variables etc --- .../orders/daos/IOrderSyncInfoDAO.java | 20 + .../orders/daos/OrderSyncInfoDAO.java | 15 + .../orders/entities/OrderSyncInfo.java | 13 +- .../src/main/resources/db.changelog-1.3.xml | 6 +- .../importers/ExportTimesheetToTimJob.java | 2 +- .../importers/ExportTimesheetsToTim.java | 79 ++-- .../importers/IExportTimesheetsToTim.java | 4 +- .../importers/IImportRosterFromTim.java | 4 +- .../IJiraOrderElementSynchronizer.java | 6 +- .../importers/IJiraTimesheetSynchronizer.java | 2 +- .../importers/ImportRosterFromTim.java | 34 +- .../importers/ImportRosterFromTimJob.java | 2 +- .../JiraOrderElementSynchronizer.java | 48 ++- .../org/libreplan/importers/JiraSyncInfo.java | 61 --- .../importers/JiraTimesheetSynchronizer.java | 40 +- ...pExpInfo.java => SynchronizationInfo.java} | 15 +- .../web/common/IJobSchedulerModel.java | 6 +- .../web/common/JobSchedulerController.java | 10 +- .../web/common/JobSchedulerModel.java | 12 +- .../orders/JiraSynchronizationController.java | 359 ++++++++++++++++++ .../web/orders/OrderCRUDController.java | 220 +---------- .../orders/TimSynchronizationController.java | 99 +++-- .../src/main/webapp/orders/_edition.zul | 42 +- .../src/main/webapp/orders/_jiraSyncInfo.zul | 30 +- .../components/_jiraOrderElementSync.zul | 58 +++ .../components/_timOrderTimesheetSync.zul | 3 +- .../importers/ExportTimesheetsToTimTest.java | 6 +- 27 files changed, 688 insertions(+), 508 deletions(-) delete mode 100644 libreplan-webapp/src/main/java/org/libreplan/importers/JiraSyncInfo.java rename libreplan-webapp/src/main/java/org/libreplan/importers/{TimImpExpInfo.java => SynchronizationInfo.java} (82%) create mode 100644 libreplan-webapp/src/main/java/org/libreplan/web/orders/JiraSynchronizationController.java create mode 100644 libreplan-webapp/src/main/webapp/orders/components/_jiraOrderElementSync.zul diff --git a/libreplan-business/src/main/java/org/libreplan/business/orders/daos/IOrderSyncInfoDAO.java b/libreplan-business/src/main/java/org/libreplan/business/orders/daos/IOrderSyncInfoDAO.java index 0bf385a6e..839cee517 100644 --- a/libreplan-business/src/main/java/org/libreplan/business/orders/daos/IOrderSyncInfoDAO.java +++ b/libreplan-business/src/main/java/org/libreplan/business/orders/daos/IOrderSyncInfoDAO.java @@ -59,4 +59,24 @@ public interface IOrderSyncInfoDAO extends IGenericDAO { List findLastSynchronizedInfosByOrderAndConnectorId( Order order, String connectorId); + /** + * Searches and returns {@link OrderSyncInfo} for the specified + * key and connectorId + * + * @param key + * the unique key with in connector id + * @param connectorId + * the connector id + */ + OrderSyncInfo findByKeyAndConnectorId(String key, String connectorId); + + /** + * Finds the {@link OrderSyncInfo}s for the specified + * connectorId + * + * @param connectorId + * the connectorId + * @return a list of OrderSyncInfo if found and null if not + */ + List findByConnectorId(String connectorId); } diff --git a/libreplan-business/src/main/java/org/libreplan/business/orders/daos/OrderSyncInfoDAO.java b/libreplan-business/src/main/java/org/libreplan/business/orders/daos/OrderSyncInfoDAO.java index 6b0838328..c1150f315 100644 --- a/libreplan-business/src/main/java/org/libreplan/business/orders/daos/OrderSyncInfoDAO.java +++ b/libreplan-business/src/main/java/org/libreplan/business/orders/daos/OrderSyncInfoDAO.java @@ -62,4 +62,19 @@ public class OrderSyncInfoDAO extends GenericDAOHibernate return criteria.list(); } + @Override + public OrderSyncInfo findByKeyAndConnectorId(String key, String connectorId) { + Criteria criteria = getSession().createCriteria(OrderSyncInfo.class); + criteria.add(Restrictions.eq("key", key)); + criteria.add(Restrictions.eq("connectorId", connectorId)); + return (OrderSyncInfo) criteria.uniqueResult(); + } + + @Override + public List findByConnectorId(String connectorId) { + Criteria criteria = getSession().createCriteria(OrderSyncInfo.class); + criteria.add(Restrictions.eq("connectorId", connectorId)); + return criteria.list(); + } + } diff --git a/libreplan-business/src/main/java/org/libreplan/business/orders/entities/OrderSyncInfo.java b/libreplan-business/src/main/java/org/libreplan/business/orders/entities/OrderSyncInfo.java index 404b7d7f1..f100274b1 100644 --- a/libreplan-business/src/main/java/org/libreplan/business/orders/entities/OrderSyncInfo.java +++ b/libreplan-business/src/main/java/org/libreplan/business/orders/entities/OrderSyncInfo.java @@ -28,8 +28,8 @@ import org.libreplan.business.common.BaseEntity; /** * OrderSyncInfo entity. This entity holds order synchronization info. Each time * that order synchronization is successfully completed, an instance of this - * entity is created and saved to DB to hold the synchronized info. This info is - * then displayed in UI. + * entity is created or updated and saved to DB to hold the synchronized info. + * This info is then displayed in UI. * * This entity contains the following fields: *
    @@ -49,10 +49,12 @@ public class OrderSyncInfo extends BaseEntity { private String connectorId; private Order order; - public static OrderSyncInfo create(Order order, String connectorId) { + public static OrderSyncInfo create(String key, Order order, + String connectorId) { + Validate.notEmpty(key); Validate.notNull(order); Validate.notEmpty(connectorId); - return create(new OrderSyncInfo(order, connectorId)); + return create(new OrderSyncInfo(key, order, connectorId)); } /** @@ -61,8 +63,9 @@ public class OrderSyncInfo extends BaseEntity { protected OrderSyncInfo() { } - private OrderSyncInfo(Order order, String connectorId) { + private OrderSyncInfo(String key, Order order, String connectorId) { this.lastSyncDate = new Date(); + this.key = key; this.order = order; this.connectorId = connectorId; } diff --git a/libreplan-business/src/main/resources/db.changelog-1.3.xml b/libreplan-business/src/main/resources/db.changelog-1.3.xml index 16b5f4667..d4ad46eb4 100644 --- a/libreplan-business/src/main/resources/db.changelog-1.3.xml +++ b/libreplan-business/src/main/resources/db.changelog-1.3.xml @@ -213,7 +213,7 @@ Create new table order_sync_info - + @@ -288,10 +288,10 @@ - + - + diff --git a/libreplan-webapp/src/main/java/org/libreplan/importers/ExportTimesheetToTimJob.java b/libreplan-webapp/src/main/java/org/libreplan/importers/ExportTimesheetToTimJob.java index 31c8b1cf6..5c8575edc 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/importers/ExportTimesheetToTimJob.java +++ b/libreplan-webapp/src/main/java/org/libreplan/importers/ExportTimesheetToTimJob.java @@ -48,7 +48,7 @@ public class ExportTimesheetToTimJob extends QuartzJobBean { try { exportTimesheetsToTim.exportTimesheets(); LOG.info("Export scuccessful: " - + exportTimesheetsToTim.getExportProcessInfo() + + exportTimesheetsToTim.getSynchronizationInfo() .isSuccessful()); } catch (ConnectorException e) { LOG.error("Export timesheet to Tim failed", e); diff --git a/libreplan-webapp/src/main/java/org/libreplan/importers/ExportTimesheetsToTim.java b/libreplan-webapp/src/main/java/org/libreplan/importers/ExportTimesheetsToTim.java index f77d32731..14f1f3d75 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/importers/ExportTimesheetsToTim.java +++ b/libreplan-webapp/src/main/java/org/libreplan/importers/ExportTimesheetsToTim.java @@ -32,20 +32,17 @@ import org.apache.commons.logging.LogFactory; import org.joda.time.LocalDate; import org.libreplan.business.common.IAdHocTransactionService; import org.libreplan.business.common.IOnTransaction; -import org.libreplan.business.common.daos.IConfigurationDAO; import org.libreplan.business.common.daos.IConnectorDAO; import org.libreplan.business.common.entities.Connector; import org.libreplan.business.common.entities.ConnectorException; import org.libreplan.business.common.entities.PredefinedConnectorProperties; import org.libreplan.business.common.entities.PredefinedConnectors; import org.libreplan.business.common.exceptions.InstanceNotFoundException; -import org.libreplan.business.orders.daos.IOrderDAO; import org.libreplan.business.orders.daos.IOrderSyncInfoDAO; import org.libreplan.business.orders.entities.Order; import org.libreplan.business.orders.entities.OrderSyncInfo; import org.libreplan.business.resources.daos.IWorkerDAO; import org.libreplan.business.resources.entities.Worker; -import org.libreplan.business.workreports.daos.IWorkReportLineDAO; import org.libreplan.business.workreports.entities.WorkReportLine; import org.libreplan.importers.tim.DurationDTO; import org.libreplan.importers.tim.PersonDTO; @@ -76,12 +73,6 @@ public class ExportTimesheetsToTim implements IExportTimesheetsToTim { @Autowired private IWorkerDAO workerDAO; - @Autowired - private IWorkReportLineDAO workReportLineDAO; - - @Autowired - private IConfigurationDAO configurationDAO; - @Autowired IOrderSyncInfoDAO orderSyncInfoDAO; @@ -91,10 +82,7 @@ public class ExportTimesheetsToTim implements IExportTimesheetsToTim { @Autowired private IConnectorDAO connectorDAO; - @Autowired - private IOrderDAO orderDAO; - - private TimImpExpInfo timImpExpInfo; + private SynchronizationInfo synchronizationInfo; @Override @Transactional(readOnly = true) @@ -108,23 +96,19 @@ public class ExportTimesheetsToTim implements IExportTimesheetsToTim { _("Connection values of Tim connector are invalid")); } - timImpExpInfo = new TimImpExpInfo(_("Export")); + synchronizationInfo = new SynchronizationInfo(_("Export")); - List orders = orderDAO.getOrders(); - for (Order order : orders) { - OrderSyncInfo orderSyncInfo = getOrderLastSyncInfo(order); - if (orderSyncInfo == null) { - LOG.warn("Order '" + order.getName() - + "' is not yet synchronized"); - timImpExpInfo - .addFailedReason(_( - "Order '{0}' is not yet synchronized", - order.getName())); - } else { - LOG.info("Exporting '" + order.getName() + "'"); - exportTimesheets(orderSyncInfo.getKey(), - orderSyncInfo.getOrder(), connector); - } + List orderSyncInfos = orderSyncInfoDAO.findByConnectorId(PredefinedConnectors.TIM.getName()); + if (orderSyncInfos == null || orderSyncInfos.isEmpty()) { + LOG.warn("No items found in 'OrderSyncInfo' to export to Tim"); + synchronizationInfo.addFailedReason(_("No items found in 'OrderSyncInfo' to export to Tim")); + return; + } + + for (OrderSyncInfo orderSyncInfo : orderSyncInfos) { + LOG.info("Exporting '" + orderSyncInfo.getOrder().getName() + "'"); + exportTimesheets(orderSyncInfo.getKey(), orderSyncInfo.getOrder(), + connector); } } @@ -133,10 +117,10 @@ public class ExportTimesheetsToTim implements IExportTimesheetsToTim { public void exportTimesheets(String productCode, Order order) throws ConnectorException { if (productCode == null || productCode.isEmpty()) { - throw new RuntimeException("Product code should not be empty"); + throw new ConnectorException(_("Product code should not be empty")); } if (order == null) { - throw new RuntimeException("Order should not be empty"); + throw new ConnectorException(_("Order should not be empty")); } Connector connector = getTimConnector(); @@ -149,7 +133,7 @@ public class ExportTimesheetsToTim implements IExportTimesheetsToTim { _("Connection values of Tim connector are invalid")); } - timImpExpInfo = new TimImpExpInfo(_("Export")); + synchronizationInfo = new SynchronizationInfo(_("Export")); exportTimesheets(productCode, order, connector); } @@ -187,8 +171,8 @@ public class ExportTimesheetsToTim implements IExportTimesheetsToTim { if (workReportLines == null || workReportLines.isEmpty()) { LOG.warn("No work reportlines are found for order: '" + order.getName() + "'"); - timImpExpInfo.addFailedReason(_( - "No work reportlines are found for order: '{0}'", + synchronizationInfo.addFailedReason(_( + "No work reportlines are found for order: \"{0}\"", order.getName())); return; } @@ -205,7 +189,7 @@ public class ExportTimesheetsToTim implements IExportTimesheetsToTim { if (timeRegistrationDTOs.isEmpty()) { LOG.warn("Unable to crate timeregistration for request"); - timImpExpInfo + synchronizationInfo .addFailedReason(_("Unable to crate time registration for request")); return; } @@ -219,15 +203,15 @@ public class ExportTimesheetsToTim implements IExportTimesheetsToTim { if (timeRegistrationResponseDTO == null) { LOG.error("No response or exception in response"); - timImpExpInfo - .addFailedReason("No response or exception in response"); + synchronizationInfo + .addFailedReason(_("No response or exception in response")); return; } if (isRefsListEmpty(timeRegistrationResponseDTO.getRefs())) { LOG.warn("Registration response with empty refs"); - timImpExpInfo - .addFailedReason("Registration response with empty refs"); + synchronizationInfo + .addFailedReason(_("Registration response with empty refs")); return; } saveSyncInfoOnAnotherTransaction(productCode, order); @@ -262,9 +246,14 @@ public class ExportTimesheetsToTim implements IExportTimesheetsToTim { .runOnAnotherTransaction(new IOnTransaction() { @Override public Void execute() { - OrderSyncInfo orderSyncInfo = OrderSyncInfo.create( - order, PredefinedConnectors.TIM.getName()); - orderSyncInfo.setKey(productCode); + OrderSyncInfo orderSyncInfo = orderSyncInfoDAO + .findByKeyAndConnectorId(productCode, + PredefinedConnectors.TIM.getName()); + if (orderSyncInfo == null) { + orderSyncInfo = OrderSyncInfo.create(productCode, + order, PredefinedConnectors.TIM.getName()); + } + orderSyncInfo.setLastSyncDate(new Date()); orderSyncInfoDAO.save(orderSyncInfo); return null; } @@ -288,7 +277,7 @@ public class ExportTimesheetsToTim implements IExportTimesheetsToTim { worker = workerDAO.findByCode(workerCode); } catch (InstanceNotFoundException e) { LOG.warn("Worker '" + workerCode + "' not found"); - timImpExpInfo.addFailedReason(_("Worker '{0}' not found", + synchronizationInfo.addFailedReason(_("Worker \"{0}\" not found", workerCode)); return null; } @@ -334,8 +323,8 @@ public class ExportTimesheetsToTim implements IExportTimesheetsToTim { } @Override - public TimImpExpInfo getExportProcessInfo() { - return timImpExpInfo; + public SynchronizationInfo getSynchronizationInfo() { + return synchronizationInfo; } } diff --git a/libreplan-webapp/src/main/java/org/libreplan/importers/IExportTimesheetsToTim.java b/libreplan-webapp/src/main/java/org/libreplan/importers/IExportTimesheetsToTim.java index fd3df810e..75730a689 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/importers/IExportTimesheetsToTim.java +++ b/libreplan-webapp/src/main/java/org/libreplan/importers/IExportTimesheetsToTim.java @@ -69,8 +69,8 @@ public interface IExportTimesheetsToTim { OrderSyncInfo getOrderLastSyncInfo(Order order); /** - * Returns export process info, success of fail info + * Returns synchronization info, success of fail info */ - TimImpExpInfo getExportProcessInfo(); + SynchronizationInfo getSynchronizationInfo(); } diff --git a/libreplan-webapp/src/main/java/org/libreplan/importers/IImportRosterFromTim.java b/libreplan-webapp/src/main/java/org/libreplan/importers/IImportRosterFromTim.java index 20db1f873..2d421e38e 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/importers/IImportRosterFromTim.java +++ b/libreplan-webapp/src/main/java/org/libreplan/importers/IImportRosterFromTim.java @@ -46,7 +46,7 @@ public interface IImportRosterFromTim { void importRosters() throws ConnectorException; /** - * Returns import process info, success of fail info + * Returns synchronization info, success of fail info */ - TimImpExpInfo getImportProcessInfo(); + SynchronizationInfo getSynchronizationInfo(); } diff --git a/libreplan-webapp/src/main/java/org/libreplan/importers/IJiraOrderElementSynchronizer.java b/libreplan-webapp/src/main/java/org/libreplan/importers/IJiraOrderElementSynchronizer.java index f0d2b243b..2fbdff736 100755 --- a/libreplan-webapp/src/main/java/org/libreplan/importers/IJiraOrderElementSynchronizer.java +++ b/libreplan-webapp/src/main/java/org/libreplan/importers/IJiraOrderElementSynchronizer.java @@ -50,8 +50,10 @@ public interface IJiraOrderElementSynchronizer { * https://jira.atlassian.com/browse/JRA-29409 * * @return A list of labels + * @throws ConnectorException + * if connector not found */ - List getAllJiraLabels(); + List getAllJiraLabels() throws ConnectorException; /** * Get all jira issues based on the specified label parameter @@ -108,6 +110,6 @@ public interface IJiraOrderElementSynchronizer { /** * returns synchronization info, success or fail info */ - JiraSyncInfo getJiraSyncInfo(); + SynchronizationInfo getSynchronizationInfo(); } diff --git a/libreplan-webapp/src/main/java/org/libreplan/importers/IJiraTimesheetSynchronizer.java b/libreplan-webapp/src/main/java/org/libreplan/importers/IJiraTimesheetSynchronizer.java index f7fb4c920..7611a75f3 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/importers/IJiraTimesheetSynchronizer.java +++ b/libreplan-webapp/src/main/java/org/libreplan/importers/IJiraTimesheetSynchronizer.java @@ -59,6 +59,6 @@ public interface IJiraTimesheetSynchronizer { /** * returns synchronization info, success or fail info */ - JiraSyncInfo getJiraSyncInfo(); + SynchronizationInfo getSynchronizationInfo(); } diff --git a/libreplan-webapp/src/main/java/org/libreplan/importers/ImportRosterFromTim.java b/libreplan-webapp/src/main/java/org/libreplan/importers/ImportRosterFromTim.java index b9570234b..b44bb0f45 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/importers/ImportRosterFromTim.java +++ b/libreplan-webapp/src/main/java/org/libreplan/importers/ImportRosterFromTim.java @@ -40,14 +40,12 @@ import org.libreplan.business.calendars.entities.PredefinedCalendarExceptionType import org.libreplan.business.calendars.entities.ResourceCalendar; import org.libreplan.business.common.IAdHocTransactionService; import org.libreplan.business.common.IOnTransaction; -import org.libreplan.business.common.daos.IConfigurationDAO; import org.libreplan.business.common.daos.IConnectorDAO; import org.libreplan.business.common.entities.Connector; import org.libreplan.business.common.entities.ConnectorException; import org.libreplan.business.common.entities.PredefinedConnectorProperties; import org.libreplan.business.common.entities.PredefinedConnectors; import org.libreplan.business.common.exceptions.InstanceNotFoundException; -import org.libreplan.business.resources.daos.IResourceDAO; import org.libreplan.business.resources.daos.IWorkerDAO; import org.libreplan.business.resources.entities.Worker; import org.libreplan.business.workingday.EffortDuration; @@ -62,7 +60,6 @@ import org.libreplan.importers.tim.RosterDTO; import org.libreplan.importers.tim.RosterRequestDTO; import org.libreplan.importers.tim.RosterResponseDTO; import org.libreplan.web.calendars.IBaseCalendarModel; -import org.libreplan.web.resources.worker.IWorkerModel; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.config.BeanDefinition; @@ -81,18 +78,9 @@ public class ImportRosterFromTim implements IImportRosterFromTim { private static final Log LOG = LogFactory.getLog(ImportRosterFromTim.class); - @Autowired - private IConfigurationDAO configurationDAO; - @Autowired private IWorkerDAO workerDAO; - @Autowired - private IResourceDAO resourceDAO; - - @Autowired - private IWorkerModel workerModel; - @Autowired private IConnectorDAO connectorDAO; @@ -106,7 +94,7 @@ public class ImportRosterFromTim implements IImportRosterFromTim { @Qualifier("subclass") private IBaseCalendarModel baseCalendarModel; - private TimImpExpInfo timImpExpInfo; + private SynchronizationInfo synchronizationInfo; /** * Search criteria for roster exception days in RESPONSE message @@ -168,7 +156,7 @@ public class ImportRosterFromTim implements IImportRosterFromTim { String[] departmentIdsArray = StringUtils.stripAll(StringUtils.split( departmentIds, ",")); - timImpExpInfo = new TimImpExpInfo(_("Import")); + synchronizationInfo = new SynchronizationInfo(_("Import")); for (String department : departmentIdsArray) { LOG.info("Department: " + department); @@ -183,8 +171,9 @@ public class ImportRosterFromTim implements IImportRosterFromTim { productivityFactor); } else { LOG.error("No valid response for department " + department); - timImpExpInfo.addFailedReason(_( - "No valid response for department '{0}'", department)); + synchronizationInfo.addFailedReason(_( + "No valid response for department \"{0}\"", + department)); } } } @@ -208,7 +197,7 @@ public class ImportRosterFromTim implements IImportRosterFromTim { updateCalendarException(rosterExceptions); } else { LOG.info("No roster-exceptions found in the response"); - timImpExpInfo + synchronizationInfo .addFailedReason(_("No roster-exceptions found in the response")); } return null; @@ -237,7 +226,8 @@ public class ImportRosterFromTim implements IImportRosterFromTim { worker = workerDAO.findUniqueByNif(workerCode); } catch (InstanceNotFoundException e) { LOG.warn("Worker '" + workerCode + "' not found"); - timImpExpInfo.addFailedReason(_("Worker '{0}' not found", + synchronizationInfo.addFailedReason(_( + "Worker \"{0}\" not found", workerCode)); } if (worker != null) { @@ -353,7 +343,7 @@ public class ImportRosterFromTim implements IImportRosterFromTim { private CalendarExceptionType getCalendarExceptionType(String name) { if (name == null || name.isEmpty()) { LOG.error("Exception name should not be empty"); - timImpExpInfo + synchronizationInfo .addFailedReason(_("Exception name should not be empty")); return null; } @@ -369,7 +359,7 @@ public class ImportRosterFromTim implements IImportRosterFromTim { return calendarExceptionTypeDAO.findUniqueByName(nameToSearch); } catch (InstanceNotFoundException e) { LOG.error("Calendar exceptionType not found", e); - timImpExpInfo + synchronizationInfo .addFailedReason(_("Calendar exception day not found")); } return null; @@ -465,7 +455,7 @@ public class ImportRosterFromTim implements IImportRosterFromTim { } @Override - public TimImpExpInfo getImportProcessInfo() { - return timImpExpInfo; + public SynchronizationInfo getSynchronizationInfo() { + return synchronizationInfo; } } diff --git a/libreplan-webapp/src/main/java/org/libreplan/importers/ImportRosterFromTimJob.java b/libreplan-webapp/src/main/java/org/libreplan/importers/ImportRosterFromTimJob.java index cc296ad7f..22e0a2dc3 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/importers/ImportRosterFromTimJob.java +++ b/libreplan-webapp/src/main/java/org/libreplan/importers/ImportRosterFromTimJob.java @@ -53,7 +53,7 @@ public class ImportRosterFromTimJob extends QuartzJobBean { try { importRosterFromTim.importRosters(); LOG.info("Import scuccessful: " - + importRosterFromTim.getImportProcessInfo().isSuccessful()); + + importRosterFromTim.getSynchronizationInfo().isSuccessful()); } catch (ConnectorException e) { LOG.error("Import roster from Tim failed", e); } diff --git a/libreplan-webapp/src/main/java/org/libreplan/importers/JiraOrderElementSynchronizer.java b/libreplan-webapp/src/main/java/org/libreplan/importers/JiraOrderElementSynchronizer.java index 327379469..90163f02a 100755 --- a/libreplan-webapp/src/main/java/org/libreplan/importers/JiraOrderElementSynchronizer.java +++ b/libreplan-webapp/src/main/java/org/libreplan/importers/JiraOrderElementSynchronizer.java @@ -72,7 +72,7 @@ import org.springframework.transaction.annotation.Transactional; @Scope(BeanDefinition.SCOPE_PROTOTYPE) public class JiraOrderElementSynchronizer implements IJiraOrderElementSynchronizer { - private JiraSyncInfo jiraSyncInfo; + private SynchronizationInfo synchronizationInfo; @Autowired private IConnectorDAO connectorDAO; @@ -82,10 +82,10 @@ public class JiraOrderElementSynchronizer implements IJiraOrderElementSynchroniz @Override @Transactional(readOnly = true) - public List getAllJiraLabels() { + public List getAllJiraLabels() throws ConnectorException { Connector connector = getJiraConnector(); if (connector == null) { - return null; + throw new ConnectorException(_("JIRA connector not found")); } String jiraLabels = connector.getPropertiesAsMap().get( @@ -137,7 +137,7 @@ public class JiraOrderElementSynchronizer implements IJiraOrderElementSynchroniz @Transactional(readOnly = true) public void syncOrderElementsWithJiraIssues(List issues, Order order) { - jiraSyncInfo = new JiraSyncInfo(); + synchronizationInfo = new SynchronizationInfo(_("Synchronization")); for (IssueDTO issue : issues) { String code = PredefinedConnectorProperties.JIRA_CODE_PREFIX @@ -147,8 +147,9 @@ public class JiraOrderElementSynchronizer implements IJiraOrderElementSynchroniz OrderLine orderLine = syncOrderLine(order, code, name); if (orderLine == null) { - jiraSyncInfo.addSyncFailedReason("Order-element for '" - + issue.getKey() + "' issue not found"); + synchronizationInfo.addFailedReason(_( + "Order-element for \"{0}\" issue not found", + issue.getKey())); continue; } @@ -158,8 +159,9 @@ public class JiraOrderElementSynchronizer implements IJiraOrderElementSynchroniz .getTimetracking(), loggedHours); if (estimatedHours.isZero()) { - jiraSyncInfo.addSyncFailedReason("Estimated time for '" - + issue.getKey() + "' issue is 0"); + synchronizationInfo.addFailedReason(_( + "Estimated time for \"{0}\" issue is 0", + issue.getKey())); continue; } @@ -246,15 +248,16 @@ public class JiraOrderElementSynchronizer implements IJiraOrderElementSynchroniz WorkLogDTO workLog = issue.getFields().getWorklog(); if (workLog == null) { - jiraSyncInfo.addSyncFailedReason("No worklogs found for '" - + issue.getKey() + "' issue"); + synchronizationInfo.addFailedReason(_( + "No worklogs found for \"{0}\" issue", issue.getKey())); return; } List workLogItems = workLog.getWorklogs(); if (workLogItems.isEmpty()) { - jiraSyncInfo.addSyncFailedReason("No worklog items found for '" - + issue.getKey() + "' issue"); + synchronizationInfo.addFailedReason(_( + "No worklog items found for \"{0}\" issue", + issue.getKey())); return; } @@ -363,9 +366,10 @@ public class JiraOrderElementSynchronizer implements IJiraOrderElementSynchroniz } catch (DuplicateAdvanceAssignmentForOrderElementException e) { // This could happen if a parent or child of the current // OrderElement has an advance of type PERCENTAGE - jiraSyncInfo - .addSyncFailedReason("Duplicate value AdvanceAssignment for order element of '" - + orderElement.getCode() + "'"); + synchronizationInfo + .addFailedReason(_( + "Duplicate value AdvanceAssignment for order element of \"{0}\"", + orderElement.getCode())); return; } } @@ -422,8 +426,8 @@ public class JiraOrderElementSynchronizer implements IJiraOrderElementSynchroniz } @Override - public JiraSyncInfo getJiraSyncInfo() { - return jiraSyncInfo; + public SynchronizationInfo getSynchronizationInfo() { + return synchronizationInfo; } /** @@ -437,9 +441,13 @@ public class JiraOrderElementSynchronizer implements IJiraOrderElementSynchroniz @Override @Transactional public void saveSyncInfo(String key, Order order) { - OrderSyncInfo orderSyncInfo = OrderSyncInfo.create(order, - PredefinedConnectors.JIRA.getName()); - orderSyncInfo.setKey(key); + OrderSyncInfo orderSyncInfo = orderSyncInfoDAO.findByKeyAndConnectorId( + key, PredefinedConnectors.JIRA.getName()); + if (orderSyncInfo == null) { + orderSyncInfo = OrderSyncInfo.create(key, order, + PredefinedConnectors.JIRA.getName()); + } + orderSyncInfo.setLastSyncDate(new Date()); orderSyncInfoDAO.save(orderSyncInfo); } diff --git a/libreplan-webapp/src/main/java/org/libreplan/importers/JiraSyncInfo.java b/libreplan-webapp/src/main/java/org/libreplan/importers/JiraSyncInfo.java deleted file mode 100644 index fecf80d95..000000000 --- a/libreplan-webapp/src/main/java/org/libreplan/importers/JiraSyncInfo.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * This file is part of LibrePlan - * - * Copyright (C) 2013 St. Antoniusziekenhuis - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package org.libreplan.importers; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * Keeps track the synchronization info. - * - * @author Miciele Ghiorghis - */ -public class JiraSyncInfo { - - private List syncFailedReasons = new ArrayList(); - - /** - * Add the specified reason to syncFailedReasons list - * - * @param reason - * reason why synchronizition failed - */ - public void addSyncFailedReason(String reason) { - syncFailedReasons.add(reason); - } - - /** - * Is synchronization successful - * - * @return - */ - public boolean isSyncSuccessful() { - return syncFailedReasons.isEmpty(); - } - - /** - * returns reasons why synchronization is failed - */ - public List getSyncFailedReasons() { - return Collections.unmodifiableList(syncFailedReasons); - } - -} diff --git a/libreplan-webapp/src/main/java/org/libreplan/importers/JiraTimesheetSynchronizer.java b/libreplan-webapp/src/main/java/org/libreplan/importers/JiraTimesheetSynchronizer.java index f3e95f2f3..991421353 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/importers/JiraTimesheetSynchronizer.java +++ b/libreplan-webapp/src/main/java/org/libreplan/importers/JiraTimesheetSynchronizer.java @@ -44,7 +44,6 @@ import org.libreplan.business.resources.entities.Resource; import org.libreplan.business.resources.entities.Worker; import org.libreplan.business.workingday.EffortDuration; import org.libreplan.business.workreports.daos.IWorkReportDAO; -import org.libreplan.business.workreports.daos.IWorkReportLineDAO; import org.libreplan.business.workreports.daos.IWorkReportTypeDAO; import org.libreplan.business.workreports.entities.PredefinedWorkReportTypes; import org.libreplan.business.workreports.entities.WorkReport; @@ -71,7 +70,7 @@ import org.springframework.transaction.annotation.Transactional; @Scope(BeanDefinition.SCOPE_PROTOTYPE) public class JiraTimesheetSynchronizer implements IJiraTimesheetSynchronizer { - private JiraSyncInfo jiraSyncInfo; + private SynchronizationInfo synchronizationInfo; private List workers; @@ -88,9 +87,6 @@ public class JiraTimesheetSynchronizer implements IJiraTimesheetSynchronizer { @Autowired private IWorkReportDAO workReportDAO; - @Autowired - private IWorkReportLineDAO workReportLineDAO; - @Autowired private IWorkReportModel workReportModel; @@ -109,14 +105,14 @@ public class JiraTimesheetSynchronizer implements IJiraTimesheetSynchronizer { @Override @Transactional public void syncJiraTimesheetWithJiraIssues(List issues, Order order) throws ConnectorException { - jiraSyncInfo = new JiraSyncInfo(); + synchronizationInfo = new SynchronizationInfo(_("Synchronization")); workReportType = getJiraTimesheetsWorkReportType(); typeOfWorkHours = getTypeOfWorkHours(); workers = getWorkers(); if (workers == null && workers.isEmpty()) { - jiraSyncInfo.addSyncFailedReason("No workers found"); + synchronizationInfo.addFailedReason(_("No workers found")); return; } @@ -124,13 +120,15 @@ public class JiraTimesheetSynchronizer implements IJiraTimesheetSynchronizer { .findLastSynchronizedInfoByOrderAndConnectorId(order, PredefinedConnectors.JIRA.getName()); if (orderSyncInfo == null) { - jiraSyncInfo.addSyncFailedReason("Order '" + order.getName() - + "' not found. Order probalbly not synchronized"); + synchronizationInfo.addFailedReason(_( + "Order \"{0}\" not found. Order probalbly not synchronized", + order.getName())); return; } if (StringUtils.isBlank(orderSyncInfo.getKey())) { - jiraSyncInfo.addSyncFailedReason("Key for Order '" - + order.getName() + "' is empty"); + synchronizationInfo.addFailedReason(_( + "Key for Order \"{0}\" is empty", + order.getName())); return; } @@ -141,14 +139,14 @@ public class JiraTimesheetSynchronizer implements IJiraTimesheetSynchronizer { for (IssueDTO issue : issues) { WorkLogDTO worklog = issue.getFields().getWorklog(); if (worklog == null) { - jiraSyncInfo.addSyncFailedReason("No worklogs found for '" - + issue.getKey() + "'"); + synchronizationInfo.addFailedReason(_( + "No worklogs found for \"{0}\" key", issue.getKey())); } else { List workLogItems = worklog.getWorklogs(); if (workLogItems == null || workLogItems.isEmpty()) { - jiraSyncInfo - .addSyncFailedReason("No worklog items found for '" - + issue.getKey() + "' issue"); + synchronizationInfo.addFailedReason(_( + "No worklog items found for \"{0}\" issue", + issue.getKey())); } else { String codeOrderElement = PredefinedConnectorProperties.JIRA_CODE_PREFIX @@ -157,8 +155,8 @@ public class JiraTimesheetSynchronizer implements IJiraTimesheetSynchronizer { OrderElement orderElement = order.getOrderElement(codeOrderElement); if (orderElement == null) { - jiraSyncInfo.addSyncFailedReason("Order element(" - + code + ") not found"); + synchronizationInfo.addFailedReason(_( + "Order element \"{0}\" not found", code)); } else { updateOrCreateWorkReportLineAndAddToWorkReport(workReport, orderElement, workLogItems); @@ -382,13 +380,13 @@ public class JiraTimesheetSynchronizer implements IJiraTimesheetSynchronizer { return worker; } } - jiraSyncInfo.addSyncFailedReason("Worker('" + nif + "') not found"); + synchronizationInfo.addFailedReason(_("Worker \"{0}\" not found", nif)); return null; } @Override - public JiraSyncInfo getJiraSyncInfo() { - return jiraSyncInfo; + public SynchronizationInfo getSynchronizationInfo() { + return synchronizationInfo; } } diff --git a/libreplan-webapp/src/main/java/org/libreplan/importers/TimImpExpInfo.java b/libreplan-webapp/src/main/java/org/libreplan/importers/SynchronizationInfo.java similarity index 82% rename from libreplan-webapp/src/main/java/org/libreplan/importers/TimImpExpInfo.java rename to libreplan-webapp/src/main/java/org/libreplan/importers/SynchronizationInfo.java index 777922f58..192710eb3 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/importers/TimImpExpInfo.java +++ b/libreplan-webapp/src/main/java/org/libreplan/importers/SynchronizationInfo.java @@ -24,14 +24,15 @@ import java.util.Collections; import java.util.List; /** - * Keeps track the success/failure of Tim's import and/or export process + * Keeps track the success/failure of synchronization process * * @author Miciele Ghiorghis */ -public class TimImpExpInfo { +public class SynchronizationInfo { /** - * action, import or export process + * The action, a unique key for example synchronization, import or export + * etc action */ private String action; @@ -40,7 +41,7 @@ public class TimImpExpInfo { */ private List failedReasons = new ArrayList(); - public TimImpExpInfo(String action) { + public SynchronizationInfo(String action) { this.action = action; } @@ -55,14 +56,14 @@ public class TimImpExpInfo { * Adds the specified reason to failedReasons list * * @param reason - * reason why import/export failed + * reason why synchronization is failed */ public void addFailedReason(String reason) { failedReasons.add(reason); } /** - * Is import or export succeeded + * Is synchronization succeeded * * @return true if failedReasons is empty */ @@ -71,7 +72,7 @@ public class TimImpExpInfo { } /** - * returns reasons why import or export failed + * returns reasons why synchronization is failed */ public List getFailedReasons() { return Collections.unmodifiableList(failedReasons); diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/common/IJobSchedulerModel.java b/libreplan-webapp/src/main/java/org/libreplan/web/common/IJobSchedulerModel.java index 071edc0d3..fd7327275 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/common/IJobSchedulerModel.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/common/IJobSchedulerModel.java @@ -26,7 +26,7 @@ import org.libreplan.business.common.entities.ConnectorException; import org.libreplan.business.common.entities.JobSchedulerConfiguration; import org.libreplan.business.common.entities.PredefinedConnectorProperties; import org.libreplan.business.common.exceptions.ValidationException; -import org.libreplan.importers.TimImpExpInfo; +import org.libreplan.importers.SynchronizationInfo; /** * Contract for {@link JobSchedulerModel}. @@ -64,9 +64,9 @@ public interface IJobSchedulerModel { throws ConnectorException; /** - * Returns import/export info. Failure or success info + * Returns synchronization info. Failure or success info */ - TimImpExpInfo getImportExportInfo(); + SynchronizationInfo getSynchronizationInfo(); /** * Prepares for create a new {@link JobSchedulerConfiguration}. diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/common/JobSchedulerController.java b/libreplan-webapp/src/main/java/org/libreplan/web/common/JobSchedulerController.java index bf5c06f62..38efce86c 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/common/JobSchedulerController.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/common/JobSchedulerController.java @@ -36,7 +36,7 @@ import org.libreplan.business.common.entities.JobClassNameEnum; import org.libreplan.business.common.entities.JobSchedulerConfiguration; import org.libreplan.business.common.exceptions.InstanceNotFoundException; import org.libreplan.business.common.exceptions.ValidationException; -import org.libreplan.importers.TimImpExpInfo; +import org.libreplan.importers.SynchronizationInfo; import org.quartz.CronExpression; import org.zkoss.zk.ui.Component; import org.zkoss.zk.ui.Executions; @@ -212,11 +212,11 @@ public class JobSchedulerController extends private void shwoImpExpInfo() { Map args = new HashMap(); - TimImpExpInfo timImpExpInfo = jobSchedulerModel.getImportExportInfo(); - args.put("action", timImpExpInfo.getAction()); - args.put("showSuccess", timImpExpInfo.isSuccessful()); + SynchronizationInfo synchronizationInfo = jobSchedulerModel.getSynchronizationInfo(); + args.put("action", synchronizationInfo.getAction()); + args.put("showSuccess", synchronizationInfo.isSuccessful()); args.put("failedReasons", - new SimpleListModel(timImpExpInfo.getFailedReasons())); + new SimpleListModel(synchronizationInfo.getFailedReasons())); Window timImpExpInfoWindow = (Window) Executions.createComponents( "/orders/_timImpExpInfo.zul", null, args); diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/common/JobSchedulerModel.java b/libreplan-webapp/src/main/java/org/libreplan/web/common/JobSchedulerModel.java index 06838bf81..32f2536cc 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/common/JobSchedulerModel.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/common/JobSchedulerModel.java @@ -32,7 +32,7 @@ import org.libreplan.business.common.exceptions.ValidationException; import org.libreplan.importers.IExportTimesheetsToTim; import org.libreplan.importers.IImportRosterFromTim; import org.libreplan.importers.ISchedulerManager; -import org.libreplan.importers.TimImpExpInfo; +import org.libreplan.importers.SynchronizationInfo; import org.libreplan.web.common.concurrentdetection.OnConcurrentModification; import org.quartz.SchedulerException; import org.springframework.beans.factory.annotation.Autowired; @@ -69,7 +69,7 @@ public class JobSchedulerModel implements IJobSchedulerModel { @Autowired private IExportTimesheetsToTim exportTimesheetsToTim; - private TimImpExpInfo timImpExpInfo; + private SynchronizationInfo synchronizationInfo; @Override @Transactional(readOnly = true) @@ -89,19 +89,19 @@ public class JobSchedulerModel implements IJobSchedulerModel { String name = jobSchedulerConfiguration.getJobClassName().getName(); if (name.equals(JobClassNameEnum.IMPORT_ROSTER_FROM_TIM_JOB.getName())) { importRosterFromTim.importRosters(); - timImpExpInfo = importRosterFromTim.getImportProcessInfo(); + synchronizationInfo = importRosterFromTim.getSynchronizationInfo(); return; } if (name.equals(JobClassNameEnum.EXPORT_TIMESHEET_TO_TIM_JOB.getName())) { exportTimesheetsToTim.exportTimesheets(); - timImpExpInfo = exportTimesheetsToTim.getExportProcessInfo(); + synchronizationInfo = exportTimesheetsToTim.getSynchronizationInfo(); return; } } @Override - public TimImpExpInfo getImportExportInfo() { - return timImpExpInfo; + public SynchronizationInfo getSynchronizationInfo() { + return synchronizationInfo; } @Override diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/orders/JiraSynchronizationController.java b/libreplan-webapp/src/main/java/org/libreplan/web/orders/JiraSynchronizationController.java new file mode 100644 index 000000000..19e1a06e2 --- /dev/null +++ b/libreplan-webapp/src/main/java/org/libreplan/web/orders/JiraSynchronizationController.java @@ -0,0 +1,359 @@ +/* + * This file is part of LibrePlan + * + * Copyright (C) 2013 St. Antoniusziekenhuis + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.libreplan.web.orders; + +import static org.libreplan.web.I18nHelper._; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import javax.ws.rs.WebApplicationException; + +import org.apache.commons.logging.LogFactory; +import org.libreplan.business.common.daos.IConnectorDAO; +import org.libreplan.business.common.entities.Connector; +import org.libreplan.business.common.entities.ConnectorException; +import org.libreplan.business.common.entities.PredefinedConnectors; +import org.libreplan.business.orders.entities.Order; +import org.libreplan.business.orders.entities.OrderSyncInfo; +import org.libreplan.importers.IJiraOrderElementSynchronizer; +import org.libreplan.importers.IJiraTimesheetSynchronizer; +import org.libreplan.importers.SynchronizationInfo; +import org.libreplan.importers.jira.IssueDTO; +import org.libreplan.web.common.IMessagesForUser; +import org.libreplan.web.common.Level; +import org.libreplan.web.common.MessagesForUser; +import org.libreplan.web.common.Util; +import org.springframework.beans.factory.annotation.Autowired; +import org.zkoss.zk.ui.Component; +import org.zkoss.zk.ui.Executions; +import org.zkoss.zk.ui.SuspendNotAllowedException; +import org.zkoss.zk.ui.event.Event; +import org.zkoss.zk.ui.event.EventListener; +import org.zkoss.zk.ui.event.Events; +import org.zkoss.zk.ui.util.GenericForwardComposer; +import org.zkoss.zul.Button; +import org.zkoss.zul.Combobox; +import org.zkoss.zul.ListModel; +import org.zkoss.zul.Popup; +import org.zkoss.zul.SimpleListModel; +import org.zkoss.zul.Tab; +import org.zkoss.zul.Textbox; +import org.zkoss.zul.api.Groupbox; +import org.zkoss.zul.api.Window; + +/** + * Controller for JIRA synchronization + * + * @author Miciele Ghiorghis + */ +public class JiraSynchronizationController extends GenericForwardComposer { + + private static final org.apache.commons.logging.Log LOG = LogFactory + .getLog(JiraSynchronizationController.class); + + private OrderCRUDController orderController; + + private Window editWindow; + + private Groupbox jiraGroupBox; + + private Popup jirasyncPopup; + + private Button startJiraSyncButton, cancelJiraSyncButton, + syncWithJiraButton; + + private Textbox txtImportedLabel, txtLastSyncDate; + + private Combobox comboJiraLabel; + + private IMessagesForUser messagesForUser; + + private Component messagesContainer; + + @Autowired + private IJiraOrderElementSynchronizer jiraOrderElementSynchronizer; + + @Autowired + private IJiraTimesheetSynchronizer jiraTimesheetSynchronizer; + + @Autowired + private IConnectorDAO connectorDAO; + + @Override + public void doAfterCompose(Component comp) throws Exception { + super.doAfterCompose(comp); + comp.setVariable("jiraSynchroniaztionController", this, true); + loadComponentsEditWindow(); + showOrHideJiraEditWindow(); + updateOrderLastSyncInfoScreen(); + } + + /** + * Returns current {@link Order} + */ + private Order getOrder() { + return orderController.getOrder(); + } + + private void loadComponentsEditWindow() { + txtLastSyncDate = (Textbox) editWindow + .getFellowIfAny("txtLastSyncDate"); + txtImportedLabel = (Textbox) editWindow + .getFellowIfAny("txtImportedLabel"); + jiraGroupBox = (Groupbox) editWindow.getFellowIfAny("jiraGroupBox"); + syncWithJiraButton = (Button) editWindow + .getFellow("syncWithJiraButton"); + messagesForUser = new MessagesForUser(messagesContainer); + } + + /** + * Show or hide JiraEditWindow based on JIRA + * {@link Connector#isActivated()} + */ + private void showOrHideJiraEditWindow() { + jiraGroupBox.setVisible(isJiraActivated()); + } + + /** + * Updates the UI text last synchronized date and the text imported label + */ + private void updateOrderLastSyncInfoScreen() { + OrderSyncInfo orderSyncInfo = jiraOrderElementSynchronizer + .getOrderLastSyncInfo(getOrder()); + + if (orderSyncInfo != null) { + txtLastSyncDate.setValue(Util.formatDateTime(orderSyncInfo + .getLastSyncDate())); + txtImportedLabel.setValue(orderSyncInfo.getKey()); + } + } + + /** + * Returns true if jira is Activated. Used to show/hide Jira edit window + */ + public boolean isJiraActivated() { + Connector connector = connectorDAO + .findUniqueByName(PredefinedConnectors.JIRA.getName()); + if (connector == null) { + return false; + } + return connector.isActivated(); + } + + /** + * Synchronize with Jira + */ + public void syncWithJira() { + try { + List items = jiraOrderElementSynchronizer + .getAllJiraLabels(); + + if (!(txtImportedLabel.getText()).isEmpty()) { + startSyncWithJira(txtImportedLabel.getText()); + return; + } + + setupJiraSyncPopup(editWindow, new SimpleListModelExt(items)); + + jirasyncPopup.open(syncWithJiraButton, "before_start"); + + } catch (ConnectorException e) { + messagesForUser.showMessage(Level.ERROR, + _("Failed: {0}", e.getMessage())); + } catch (WebApplicationException e) { + LOG.info(e); + messagesForUser.showMessage(Level.ERROR, + _("Cannot connect to JIRA server")); + } + } + + /** + * Start synchronize with jira for the specified label + * + * @param label + * the jira label + */ + public void startSyncWithJira(String label) { + try { + Order order = getOrder(); + + List issues = jiraOrderElementSynchronizer + .getJiraIssues(label); + + if (issues == null || issues.isEmpty()) { + messagesForUser.showMessage(Level.ERROR, + _("No JIRA issues to import")); + return; + } + + order.setCodeAutogenerated(false); + + jiraOrderElementSynchronizer.syncOrderElementsWithJiraIssues( + issues, order); + + orderController.saveAndContinue(false); + + jiraOrderElementSynchronizer.saveSyncInfo(label, order); + + if (jirasyncPopup != null) { + jirasyncPopup.close(); + } + + jiraTimesheetSynchronizer.syncJiraTimesheetWithJiraIssues(issues, + order); + + showSyncInfo(); + + // Reload order info in all tabs + Tab previousTab = orderController.getCurrentTab(); + orderController.initEdit(order); + orderController.selectTab(previousTab.getId()); + } catch (ConnectorException e) { + messagesForUser.showMessage(Level.ERROR, + _("Failed: {0}", e.getMessage())); + } catch (WebApplicationException e) { + LOG.info(e); + messagesForUser.showMessage(Level.ERROR, + _("Cannot connect to JIRA server")); + } + } + + /** + * Shows the success or failure info of synchronization + */ + private void showSyncInfo() { + Map args = new HashMap(); + + SynchronizationInfo syncOrderElementInfo = jiraOrderElementSynchronizer + .getSynchronizationInfo(); + + boolean succeeded = isSyncSucceeded(syncOrderElementInfo); + + args.put("syncOrderElementSuccess", succeeded); + if (syncOrderElementInfo != null) { + args.put("syncOrderElementFailedReasons", new SimpleListModel( + syncOrderElementInfo.getFailedReasons())); + } + + SynchronizationInfo jiraSyncInfoTimesheet = jiraTimesheetSynchronizer + .getSynchronizationInfo(); + + succeeded = isSyncSucceeded(jiraSyncInfoTimesheet); + + args.put("syncTimesheetSuccess", succeeded); + if (jiraSyncInfoTimesheet != null) { + args.put("syncTimesheetFailedReasons", new SimpleListModel( + jiraSyncInfoTimesheet.getFailedReasons())); + } + + Window jiraSyncInfoWindow = (Window) Executions.createComponents( + "/orders/_jiraSyncInfo.zul", null, args); + + try { + jiraSyncInfoWindow.doModal(); + } catch (SuspendNotAllowedException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + private boolean isSyncSucceeded(SynchronizationInfo syncInfo) { + if (syncInfo == null) { + return false; + } + return syncInfo.isSuccessful(); + } + + /** + * Setups the pop-up components + * + * @param comp + * the compenent(editWidnow) + * @param model + * labels list model to render the combobox + * comboJiraLabel + */ + private void setupJiraSyncPopup(Component comp, ListModel model) { + + startJiraSyncButton = (Button) comp.getFellow("startJiraSyncButton"); + startJiraSyncButton.setLabel(_("Start sync")); + + startJiraSyncButton.addEventListener(Events.ON_CLICK, + new EventListener() { + + @Override + public void onEvent(Event event) { + startSyncWithJira(comboJiraLabel.getValue()); + } + }); + cancelJiraSyncButton = (Button) comp.getFellow("cancelJiraSyncButton"); + cancelJiraSyncButton.setLabel(_("Cancel")); + cancelJiraSyncButton.addEventListener(Events.ON_CLICK, + new EventListener() { + + @Override + public void onEvent(Event event) { + jirasyncPopup.close(); + } + }); + comboJiraLabel = (Combobox) comp.getFellowIfAny("comboJiraLabel"); + comboJiraLabel.setModel(model); + + jirasyncPopup = (Popup) comp.getFellow("jirasyncPopup"); + + } + + /** + * This class provides case insensitive search for the {@link Combobox}. + */ + private class SimpleListModelExt extends SimpleListModel { + + public SimpleListModelExt(List data) { + super(data); + } + + public ListModel getSubModel(Object value, int nRows) { + final String idx = value == null ? "" : objectToString(value); + if (nRows < 0) { + nRows = 10; + } + final LinkedList data = new LinkedList(); + for (int i = 0; i < getSize(); i++) { + if (idx.equals("") + || entryMatchesText(getElementAt(i).toString(), idx)) { + data.add(getElementAt(i)); + if (--nRows <= 0) { + break; + } + } + } + return new SimpleListModelExt(data); + } + + public boolean entryMatchesText(String entry, String text) { + return entry.toLowerCase().contains(text.toLowerCase()); + } + } + +} diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/orders/OrderCRUDController.java b/libreplan-webapp/src/main/java/org/libreplan/web/orders/OrderCRUDController.java index 86703c101..82ade693b 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/orders/OrderCRUDController.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/orders/OrderCRUDController.java @@ -27,22 +27,16 @@ import java.util.ConcurrentModificationException; import java.util.Date; import java.util.HashMap; import java.util.Iterator; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.SortedSet; import java.util.TreeSet; import javax.annotation.Resource; -import javax.ws.rs.WebApplicationException; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.LogFactory; import org.libreplan.business.calendars.entities.BaseCalendar; -import org.libreplan.business.common.daos.IConnectorDAO; -import org.libreplan.business.common.entities.Connector; -import org.libreplan.business.common.entities.ConnectorException; -import org.libreplan.business.common.entities.PredefinedConnectors; import org.libreplan.business.common.exceptions.InstanceNotFoundException; import org.libreplan.business.externalcompanies.entities.DeadlineCommunication; import org.libreplan.business.externalcompanies.entities.DeliverDateComparator; @@ -53,14 +47,9 @@ import org.libreplan.business.orders.entities.Order; import org.libreplan.business.orders.entities.Order.SchedulingMode; import org.libreplan.business.orders.entities.OrderElement; import org.libreplan.business.orders.entities.OrderStatusEnum; -import org.libreplan.business.orders.entities.OrderSyncInfo; import org.libreplan.business.planner.entities.PositionConstraintType; import org.libreplan.business.templates.entities.OrderTemplate; import org.libreplan.business.users.entities.UserRole; -import org.libreplan.importers.IJiraOrderElementSynchronizer; -import org.libreplan.importers.IJiraTimesheetSynchronizer; -import org.libreplan.importers.JiraSyncInfo; -import org.libreplan.importers.jira.IssueDTO; import org.libreplan.web.common.ConfirmCloseUtil; import org.libreplan.web.common.IMessagesForUser; import org.libreplan.web.common.Level; @@ -88,7 +77,6 @@ import org.zkoss.ganttz.util.LongOperationFeedback; import org.zkoss.zk.ui.Component; import org.zkoss.zk.ui.Desktop; import org.zkoss.zk.ui.Executions; -import org.zkoss.zk.ui.SuspendNotAllowedException; import org.zkoss.zk.ui.WrongValueException; import org.zkoss.zk.ui.event.Event; import org.zkoss.zk.ui.event.EventListener; @@ -106,9 +94,7 @@ import org.zkoss.zul.Datebox; import org.zkoss.zul.Grid; import org.zkoss.zul.Hbox; import org.zkoss.zul.Label; -import org.zkoss.zul.ListModel; import org.zkoss.zul.Messagebox; -import org.zkoss.zul.Popup; import org.zkoss.zul.Row; import org.zkoss.zul.RowRenderer; import org.zkoss.zul.Rows; @@ -198,6 +184,8 @@ public class OrderCRUDController extends GenericForwardComposer { private ProjectDetailsController projectDetailsController; + private JiraSynchronizationController jiraSynchronizationController; + private TimSynchronizationController timSynchronizationController; @Autowired @@ -207,16 +195,6 @@ public class OrderCRUDController extends GenericForwardComposer { private EndDatesRenderer endDatesRenderer = new EndDatesRenderer(); - @Autowired - private IJiraOrderElementSynchronizer jiraOrderElementSynchronizer; - - @Autowired - private IJiraTimesheetSynchronizer jiraTimesheetSynchronizer; - - @Autowired - private IConnectorDAO connectorDAO; - - @Override public void doAfterCompose(Component comp) throws Exception { super.doAfterCompose(comp); @@ -693,7 +671,7 @@ public class OrderCRUDController extends GenericForwardComposer { saveAndContinue(true); } - private void saveAndContinue(boolean showSaveMessage) { + protected void saveAndContinue(boolean showSaveMessage) { Order order = orderModel.getOrder(); final boolean isNewObject = order.isNewObject(); @@ -802,11 +780,11 @@ public class OrderCRUDController extends GenericForwardComposer { } } - private Tab getCurrentTab() { + protected Tab getCurrentTab() { return selectedTab; } - private void selectTab(String str) { + protected void selectTab(String str) { Tab tab = (Tab) editWindow.getFellowIfAny(str); if (tab != null) { tab.setSelected(true); @@ -1042,6 +1020,7 @@ public class OrderCRUDController extends GenericForwardComposer { initializeCustomerComponent(); reloadOrderDetailsTab(); orderDatesHandler.chooseCurrentSchedulingMode(); + setupJiraSynchronizationController(); setupTimSynchronizationController(); } @@ -1706,196 +1685,29 @@ public class OrderCRUDController extends GenericForwardComposer { return Util.getCurrencySymbol(); } - private Popup jirasyncPopup; - private Button startJiraSyncButton, cancelJiraSyncButton, syncWithJiraButton; - private Combobox comboJiraLabel; - - public boolean isJiraActivated() { - Connector connector = connectorDAO - .findUniqueByName(PredefinedConnectors.JIRA.getName()); - if (connector == null) { - return false; + /** + * Setup the connector, JiraSynchronization controller + */ + public void setupJiraSynchronizationController() { + if (jiraSynchronizationController == null) { + jiraSynchronizationController = new JiraSynchronizationController(); } - return connector.isActivated(); - } - - public boolean isJiraDeactivated() { - return !isJiraActivated(); - - } - - public void syncWithJira() { try { - List items = jiraOrderElementSynchronizer.getAllJiraLabels(); - - Textbox txtImportedLabel = (Textbox) editWindow - .getFellowIfAny("txtImportedLabel"); - - if (!(txtImportedLabel.getText()).isEmpty()) { - startSyncWithJira(txtImportedLabel.getText()); - return; - } - - setupJiraSyncPopup(editWindow, new SimpleListModelExt(items)); - - syncWithJiraButton = (Button) getCurrentTab().getFellow( - "syncWithJiraButton"); - - jirasyncPopup.open(syncWithJiraButton, "before_start"); - - } catch (WebApplicationException e) { - LOG.info(e); - messagesForUser.showMessage(Level.ERROR, - _("Cannot connect to JIRA server")); - } - } - - - public void startSyncWithJira(String label) { - try { - Order order = getOrder(); - - List issues = jiraOrderElementSynchronizer - .getJiraIssues(label); - - if (issues == null || issues.isEmpty()) { - messagesForUser.showMessage(Level.ERROR, - _("No JIRA issues to import")); - return; - } - - order.setCodeAutogenerated(false); - - jiraOrderElementSynchronizer.syncOrderElementsWithJiraIssues( - issues, order); - - saveAndContinue(false); - - jiraOrderElementSynchronizer.saveSyncInfo(label, order); - - if (jirasyncPopup != null) { - jirasyncPopup.close(); - } - - jiraTimesheetSynchronizer.syncJiraTimesheetWithJiraIssues(issues, - order); - - showSyncInfo(); - - // Reload order info in all tabs - Tab previousTab = getCurrentTab(); - initEdit(order); - selectTab(previousTab.getId()); - } catch (WebApplicationException e) { - LOG.info(e); - messagesForUser.showMessage(Level.ERROR, - _("Cannot connect to JIRA server")); - } catch (ConnectorException e) { - messagesForUser.showMessage(Level.ERROR, - _("Failed: {0}", e.getMessage())); - } - } - - public OrderSyncInfo getOrderLastSyncInfo() { - return jiraOrderElementSynchronizer.getOrderLastSyncInfo(getOrder()); - } - - private void showSyncInfo() { - Map args = new HashMap(); - - JiraSyncInfo jiraSyncInfoProgress = jiraOrderElementSynchronizer - .getJiraSyncInfo(); - args.put("showSyncProgressSuccess", - jiraSyncInfoProgress.isSyncSuccessful()); - args.put("jiraSyncProgressFailedReasons", new SimpleListModel( - jiraSyncInfoProgress.getSyncFailedReasons())); - - JiraSyncInfo jiraSyncInfoTimesheet = jiraTimesheetSynchronizer - .getJiraSyncInfo(); - args.put("showSyncTimesheetSuccess", - jiraSyncInfoTimesheet.isSyncSuccessful()); - args.put("jiraSyncTimesheetFailedReasons", new SimpleListModel( - jiraSyncInfoTimesheet.getSyncFailedReasons())); - - Window jiraSyncInfoWindow = (Window) Executions.createComponents( - "/orders/_jiraSyncInfo.zul", null, args); - - try { - jiraSyncInfoWindow.doModal(); - } catch (SuspendNotAllowedException e) { - throw new RuntimeException(e); - } catch (InterruptedException e) { + jiraSynchronizationController.doAfterCompose(editWindow); + } catch (Exception e) { throw new RuntimeException(e); } } - private void setupJiraSyncPopup(Component comp, ListModel model) { - - startJiraSyncButton = (Button) comp.getFellow("startJiraSyncButton"); - startJiraSyncButton.setLabel(_("Start sync")); - - startJiraSyncButton.addEventListener(Events.ON_CLICK, new EventListener() { - - @Override - public void onEvent(Event event) { - startSyncWithJira(comboJiraLabel.getValue()); - } - }); - cancelJiraSyncButton = (Button) comp.getFellow("cancelJiraSyncButton"); - cancelJiraSyncButton.setLabel(_("Cancel")); - cancelJiraSyncButton.addEventListener(Events.ON_CLICK, new EventListener() { - - @Override - public void onEvent(Event event) { - jirasyncPopup.close(); - } - }); - comboJiraLabel = (Combobox) comp.getFellowIfAny("comboJiraLabel"); - comboJiraLabel.setModel(model); - - jirasyncPopup = (Popup) comp.getFellow("jirasyncPopup"); - - } - /** - * This class provides case insensitive search for the {@link Combobox}. + * Setup the connector, TimSynchronization controller */ - private class SimpleListModelExt extends SimpleListModel { - - public SimpleListModelExt(List data) { - super(data); - } - - public ListModel getSubModel(Object value, int nRows) { - final String idx = value == null ? "" : objectToString(value); - if (nRows < 0) { - nRows = 10; - } - final LinkedList data = new LinkedList(); - for (int i = 0; i < getSize(); i++) { - if (idx.equals("") - || entryMatchesText(getElementAt(i).toString(), idx)) { - data.add(getElementAt(i)); - if (--nRows <= 0) { - break; - } - } - } - return new SimpleListModelExt(data); - } - - public boolean entryMatchesText(String entry, String text) { - return entry.toLowerCase().contains(text.toLowerCase()); - } - } - public void setupTimSynchronizationController() { if (timSynchronizationController == null) { timSynchronizationController = new TimSynchronizationController(); } try { - timSynchronizationController.doAfterCompose(self - .getFellow("editOrderElement")); + timSynchronizationController.doAfterCompose(editWindow); } catch (Exception e) { throw new RuntimeException(e); } diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/orders/TimSynchronizationController.java b/libreplan-webapp/src/main/java/org/libreplan/web/orders/TimSynchronizationController.java index e54bf7846..e74897469 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/orders/TimSynchronizationController.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/orders/TimSynchronizationController.java @@ -29,9 +29,10 @@ import org.libreplan.business.common.daos.IConnectorDAO; import org.libreplan.business.common.entities.Connector; import org.libreplan.business.common.entities.ConnectorException; import org.libreplan.business.common.entities.PredefinedConnectors; +import org.libreplan.business.orders.entities.Order; import org.libreplan.business.orders.entities.OrderSyncInfo; import org.libreplan.importers.IExportTimesheetsToTim; -import org.libreplan.importers.TimImpExpInfo; +import org.libreplan.importers.SynchronizationInfo; import org.libreplan.web.common.IMessagesForUser; import org.libreplan.web.common.Level; import org.libreplan.web.common.MessagesForUser; @@ -44,6 +45,7 @@ import org.zkoss.zk.ui.util.GenericForwardComposer; import org.zkoss.zul.Label; import org.zkoss.zul.SimpleListModel; import org.zkoss.zul.Textbox; +import org.zkoss.zul.api.Groupbox; import org.zkoss.zul.api.Window; /** @@ -58,7 +60,12 @@ public class TimSynchronizationController extends GenericForwardComposer { private OrderCRUDController orderController; + private Window editWindow; + + private Groupbox timGroupBox; + private Textbox txtProductCode; + private Label labelProductCode, labelLastSyncDate; @Autowired @@ -75,19 +82,67 @@ public class TimSynchronizationController extends GenericForwardComposer { public void doAfterCompose(Component comp) throws Exception { super.doAfterCompose(comp); comp.setVariable("timSynchronizationController", this, true); - txtProductCode = (Textbox) comp.getFellowIfAny("txtProductCode"); - labelLastSyncDate = (Label) comp.getFellowIfAny("labelLastSyncDate"); - labelProductCode = (Label) comp.getFellowIfAny("labelProductCode"); - messagesForUser = new MessagesForUser(messagesContainer); + loadComponentsEditWindow(comp); + showOrHideTimEditWindow(); updateOrderLastSyncInfoScreen(); } + /** + * Returns current {@link Order} + */ + private Order getOrder() { + return orderController.getOrder(); + } + + private void loadComponentsEditWindow(Component comp) { + txtProductCode = (Textbox) comp.getFellowIfAny("txtProductCode"); + labelLastSyncDate = (Label) comp + .getFellowIfAny("labelLastSyncDate"); + labelProductCode = (Label) comp + .getFellowIfAny("labelProductCode"); + timGroupBox = (Groupbox) comp.getFellowIfAny("timGroupBox"); + + messagesForUser = new MessagesForUser(messagesContainer); + } + + /** + * Show or hide TimEditWindow based on Tim + * {@link Connector#isActivated()} + */ + private void showOrHideTimEditWindow() { + timGroupBox.setVisible(isTimActivated()); + } + + /** + * Updates the UI text last synchronized date and the text product code + */ + private void updateOrderLastSyncInfoScreen() { + OrderSyncInfo orderSyncInfo = exportTimesheetsToTim + .getOrderLastSyncInfo(getOrder()); + if (orderSyncInfo != null) { + labelLastSyncDate.setValue(Util.formatDateTime(orderSyncInfo + .getLastSyncDate())); + labelProductCode.setValue("(" + orderSyncInfo.getKey() + ")"); + } + } + + /** + * Returns true if Tim is Activated. Used to show/hide Tim edit window + */ + public boolean isTimActivated() { + Connector connector = connectorDAO + .findUniqueByName(PredefinedConnectors.TIM.getName()); + if (connector == null) { + return false; + } + return connector.isActivated(); + } + public void startExportToTim() { - LOG.info("startExportToTim(): " + orderController.getOrder().getName()); txtProductCode.setConstraint("no empty:" + _("cannot be empty")); try { exportTimesheetsToTim.exportTimesheets(txtProductCode.getValue(), - orderController.getOrder()); + getOrder()); updateOrderLastSyncInfoScreen(); @@ -99,37 +154,15 @@ public class TimSynchronizationController extends GenericForwardComposer { } } - private void updateOrderLastSyncInfoScreen() { - OrderSyncInfo orderSyncInfo = exportTimesheetsToTim - .getOrderLastSyncInfo(orderController.getOrder()); - if (orderSyncInfo != null) { - if (labelLastSyncDate != null) { - labelLastSyncDate.setValue(Util.formatDateTime(orderSyncInfo - .getLastSyncDate())); - } - if (labelProductCode != null) { - labelProductCode.setValue("(" + orderSyncInfo.getKey() + ")"); - } - } - } - - public boolean isTimActivated() { - Connector connector = connectorDAO - .findUniqueByName(PredefinedConnectors.TIM.getName()); - if (connector == null) { - return false; - } - return connector.isActivated(); - } private void shwoImpExpInfo() { Map args = new HashMap(); - TimImpExpInfo timImpExpInfo = exportTimesheetsToTim.getExportProcessInfo(); - args.put("action", timImpExpInfo.getAction()); - args.put("showSuccess", timImpExpInfo.isSuccessful()); + SynchronizationInfo synchronizationInfo = exportTimesheetsToTim.getSynchronizationInfo(); + args.put("action", synchronizationInfo.getAction()); + args.put("showSuccess", synchronizationInfo.isSuccessful()); args.put("failedReasons", - new SimpleListModel(timImpExpInfo.getFailedReasons())); + new SimpleListModel(synchronizationInfo.getFailedReasons())); Window timImpExpInfoWindow = (Window) Executions.createComponents( "/orders/_timImpExpInfo.zul", null, args); diff --git a/libreplan-webapp/src/main/webapp/orders/_edition.zul b/libreplan-webapp/src/main/webapp/orders/_edition.zul index e5658d9d0..0602b6c0d 100644 --- a/libreplan-webapp/src/main/webapp/orders/_edition.zul +++ b/libreplan-webapp/src/main/webapp/orders/_edition.zul @@ -32,6 +32,7 @@ macroURI="/orders/components/_listOrderElementMaterials.zul"?> + @@ -282,34 +283,8 @@ - - - - - - - - - - - - - -