Merge branch 'develop' of ssh://dev.e-taxonomy.eu/var/git/cdmlib into develop
[cdmlib.git] / cdmlib-services / src / main / java / eu / etaxonomy / cdm / api / service / TaxonNodeServiceImpl.java
index 00a653906e9a5e5fbf24446dc6362aaa661be00b..a5e4715f73d9a4eb1b52f6a33142af51eaa74d12 100644 (file)
@@ -9,14 +9,15 @@
 
 package eu.etaxonomy.cdm.api.service;
 
-import java.io.Serializable;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map.Entry;
 import java.util.Set;
 import java.util.UUID;
 
@@ -24,29 +25,36 @@ import org.apache.log4j.Logger;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
+import org.springframework.transaction.interceptor.TransactionAspectSupport;
 
 import eu.etaxonomy.cdm.api.service.UpdateResult.Status;
 import eu.etaxonomy.cdm.api.service.config.NodeDeletionConfigurator.ChildHandling;
+import eu.etaxonomy.cdm.api.service.config.PublishForSubtreeConfigurator;
 import eu.etaxonomy.cdm.api.service.config.SecundumForSubtreeConfigurator;
 import eu.etaxonomy.cdm.api.service.config.TaxonDeletionConfigurator;
 import eu.etaxonomy.cdm.api.service.config.TaxonNodeDeletionConfigurator;
 import eu.etaxonomy.cdm.api.service.dto.CdmEntityIdentifier;
+import eu.etaxonomy.cdm.api.service.dto.TaxonDistributionDTO;
 import eu.etaxonomy.cdm.api.service.pager.Pager;
 import eu.etaxonomy.cdm.api.service.pager.PagerUtils;
+import eu.etaxonomy.cdm.api.service.pager.impl.AbstractPagerImpl;
 import eu.etaxonomy.cdm.api.service.pager.impl.DefaultPagerImpl;
 import eu.etaxonomy.cdm.common.monitor.DefaultProgressMonitor;
 import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;
-import eu.etaxonomy.cdm.common.monitor.IRemotingProgressMonitor;
-import eu.etaxonomy.cdm.common.monitor.RemotingProgressMonitorThread;
 import eu.etaxonomy.cdm.filter.TaxonNodeFilter;
 import eu.etaxonomy.cdm.hibernate.HHH_9751_Util;
 import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
 import eu.etaxonomy.cdm.model.agent.TeamOrPersonBase;
 import eu.etaxonomy.cdm.model.common.CdmBase;
-import eu.etaxonomy.cdm.model.common.DefinedTerm;
+import eu.etaxonomy.cdm.model.common.Language;
+import eu.etaxonomy.cdm.model.common.LanguageString;
 import eu.etaxonomy.cdm.model.common.TreeIndex;
+import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
+import eu.etaxonomy.cdm.model.description.DescriptionElementSource;
+import eu.etaxonomy.cdm.model.description.DescriptiveDataSet;
 import eu.etaxonomy.cdm.model.description.TaxonDescription;
 import eu.etaxonomy.cdm.model.name.HomotypicalGroup;
+import eu.etaxonomy.cdm.model.name.HybridRelationship;
 import eu.etaxonomy.cdm.model.name.TaxonName;
 import eu.etaxonomy.cdm.model.reference.Reference;
 import eu.etaxonomy.cdm.model.taxon.Classification;
@@ -59,11 +67,15 @@ import eu.etaxonomy.cdm.model.taxon.TaxonBase;
 import eu.etaxonomy.cdm.model.taxon.TaxonNode;
 import eu.etaxonomy.cdm.model.taxon.TaxonNodeAgentRelation;
 import eu.etaxonomy.cdm.model.taxon.TaxonRelationship;
+import eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType;
+import eu.etaxonomy.cdm.model.term.DefinedTerm;
+import eu.etaxonomy.cdm.persistence.dao.common.Restriction;
 import eu.etaxonomy.cdm.persistence.dao.initializer.IBeanInitializer;
 import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonNodeDao;
 import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonNodeFilterDao;
 import eu.etaxonomy.cdm.persistence.dto.TaxonNodeDto;
 import eu.etaxonomy.cdm.persistence.dto.UuidAndTitleCache;
+import eu.etaxonomy.cdm.persistence.query.OrderHint;
 
 /**
  * @author n.hoffmann
@@ -71,7 +83,9 @@ import eu.etaxonomy.cdm.persistence.dto.UuidAndTitleCache;
  */
 @Service
 @Transactional(readOnly = true)
-public class TaxonNodeServiceImpl extends AnnotatableServiceBase<TaxonNode, ITaxonNodeDao> implements ITaxonNodeService{
+public class TaxonNodeServiceImpl
+           extends AnnotatableServiceBase<TaxonNode, ITaxonNodeDao>
+           implements ITaxonNodeService{
     private static final Logger logger = Logger.getLogger(TaxonNodeServiceImpl.class);
 
     @Autowired
@@ -80,6 +94,12 @@ public class TaxonNodeServiceImpl extends AnnotatableServiceBase<TaxonNode, ITax
     @Autowired
     private ITaxonService taxonService;
 
+    @Autowired
+    private IReferenceService referenceService;
+
+    @Autowired
+    private IDescriptiveDataSetService dataSetService;
+
     @Autowired
     private IAgentService agentService;
 
@@ -98,14 +118,22 @@ public class TaxonNodeServiceImpl extends AnnotatableServiceBase<TaxonNode, ITax
 
     @Override
     public List<TaxonNode> loadChildNodesOfTaxonNode(TaxonNode taxonNode,
-            List<String> propertyPaths, boolean recursive, NodeSortMode sortMode) {
+            List<String> propertyPaths, boolean recursive,  boolean includeUnpublished,
+            NodeSortMode sortMode) {
 
         getSession().refresh(taxonNode);
         List<TaxonNode> childNodes;
         if (recursive == true){
-               childNodes  = dao.listChildrenOf(taxonNode, null, null, null, recursive);
+               childNodes  = dao.listChildrenOf(taxonNode, null, null, recursive, includeUnpublished, null);
+        }else if (includeUnpublished){
+            childNodes = new ArrayList<>(taxonNode.getChildNodes());
         }else{
-               childNodes = new ArrayList<>(taxonNode.getChildNodes());
+            childNodes = new ArrayList<>();
+            for (TaxonNode node:taxonNode.getChildNodes()){
+                if (node.getTaxon().isPublish()){
+                    childNodes.add(node);
+                }
+            }
         }
 
         HHH_9751_Util.removeAllNull(childNodes);
@@ -114,10 +142,16 @@ public class TaxonNodeServiceImpl extends AnnotatableServiceBase<TaxonNode, ITax
             Comparator<TaxonNode> comparator = sortMode.newComparator();
                Collections.sort(childNodes, comparator);
         }
-//        defaultBeanInitializer.initializeAll(childNodes, propertyPaths);
+        defaultBeanInitializer.initializeAll(childNodes, propertyPaths);
         return childNodes;
     }
 
+    @Override
+    public List<TaxonNode> listChildrenOf(TaxonNode node, Integer pageSize, Integer pageIndex,
+            boolean recursive, boolean includeUnpublished, List<String> propertyPaths){
+        return dao.listChildrenOf(node, pageSize, pageIndex, recursive, includeUnpublished, propertyPaths);
+    }
+
     /**
      * {@inheritDoc}
      */
@@ -185,7 +219,7 @@ public class TaxonNodeServiceImpl extends AnnotatableServiceBase<TaxonNode, ITax
      * {@inheritDoc}
      */
     @Override
-    public Pager<TaxonNodeDto> pageChildNodesDTOs(UUID taxonNodeUuid, boolean recursive,
+    public Pager<TaxonNodeDto> pageChildNodesDTOs(UUID taxonNodeUuid, boolean recursive,  boolean includeUnpublished,
             boolean doSynonyms, NodeSortMode sortMode,
             Integer pageSize, Integer pageIndex) {
 
@@ -194,7 +228,7 @@ public class TaxonNodeServiceImpl extends AnnotatableServiceBase<TaxonNode, ITax
         List<CdmBase> allRecords = new ArrayList<>();
 
         //acceptedTaxa
-        List<TaxonNode> childNodes = loadChildNodesOfTaxonNode(parentNode, null, recursive, sortMode);
+        List<TaxonNode> childNodes = loadChildNodesOfTaxonNode(parentNode, null, recursive, includeUnpublished, sortMode);
         allRecords.addAll(childNodes);
 
         //add synonyms if pager is not yet full synonyms
@@ -207,7 +241,7 @@ public class TaxonNodeServiceImpl extends AnnotatableServiceBase<TaxonNode, ITax
         }
 
         List<TaxonNodeDto> dtos = new ArrayList<>(pageSize==null?25:pageSize);
-        Long totalCount = Long.valueOf(allRecords.size());
+        long totalCount = Long.valueOf(allRecords.size());
 
         TaxonName parentName = null;
 
@@ -221,7 +255,7 @@ public class TaxonNodeServiceImpl extends AnnotatableServiceBase<TaxonNode, ITax
                 dtos.add(new TaxonNodeDto(synonym, isHomotypic));
             }
         }
-        return new DefaultPagerImpl<TaxonNodeDto>(pageIndex, totalCount, pageSize , dtos);
+        return new DefaultPagerImpl<>(pageIndex, totalCount, pageSize , dtos);
     }
 
     @Override
@@ -233,6 +267,15 @@ public class TaxonNodeServiceImpl extends AnnotatableServiceBase<TaxonNode, ITax
         return null;
     }
 
+    @Override
+    public TaxonNodeDto dto(UUID taxonNodeUuid) {
+        TaxonNode taxonNode = dao.load(taxonNodeUuid);
+        if(taxonNode.getParent() != null) {
+            return new TaxonNodeDto(taxonNode);
+        }
+        return null;
+    }
+
     @Override
     @Autowired
     protected void setDao(ITaxonNodeDao dao) {
@@ -242,7 +285,7 @@ public class TaxonNodeServiceImpl extends AnnotatableServiceBase<TaxonNode, ITax
     @Override
     @Transactional(readOnly = false)
     public DeleteResult makeTaxonNodeASynonymOfAnotherTaxonNode(TaxonNode oldTaxonNode, TaxonNode newAcceptedTaxonNode,
-            SynonymType synonymType, Reference citation, String citationMicroReference)  {
+            SynonymType synonymType, Reference citation, String citationMicroReference, boolean setNameInSource)  {
 
         // TODO at the moment this method only moves synonym-, concept relations and descriptions to the new accepted taxon
         // in a future version we also want to move cdm data like annotations, marker, so., but we will need a policy for that
@@ -350,13 +393,21 @@ public class TaxonNodeServiceImpl extends AnnotatableServiceBase<TaxonNode, ITax
             taxonRelationship.setFromTaxon(null);
         }
 
-
         //Move descriptions to new taxon
         List<TaxonDescription> descriptions = new ArrayList<TaxonDescription>( oldTaxon.getDescriptions()); //to avoid concurrent modification errors (newAcceptedTaxon.addDescription() modifies also oldtaxon.descritpions())
         for(TaxonDescription description : descriptions){
             String message = "Description copied from former accepted taxon: %s (Old title: %s)";
             message = String.format(message, oldTaxon.getTitleCache(), description.getTitleCache());
             description.setTitleCache(message, true);
+            if (setNameInSource) {
+                for (DescriptionElementBase element: description.getElements()){
+                    for (DescriptionElementSource source: element.getSources()){
+                        if (source.getNameUsedInSource() == null){
+                            source.setNameUsedInSource(newSynonymName);
+                        }
+                    }
+                }
+            }
             //oldTaxon.removeDescription(description, false);
             newAcceptedTaxon.addDescription(description);
         }
@@ -396,10 +447,11 @@ public class TaxonNodeServiceImpl extends AnnotatableServiceBase<TaxonNode, ITax
             UUID newAcceptedTaxonNodeUUIDs,
             SynonymType synonymType,
             Reference citation,
-            String citationMicroReference) {
+            String citationMicroReference,
+            boolean setNameInSource) {
        UpdateResult result = new UpdateResult();
        for (UUID nodeUuid: oldTaxonNodeUuids) {
-               result.includeResult(makeTaxonNodeASynonymOfAnotherTaxonNode(nodeUuid, newAcceptedTaxonNodeUUIDs, synonymType, citation, citationMicroReference));
+               result.includeResult(makeTaxonNodeASynonymOfAnotherTaxonNode(nodeUuid, newAcceptedTaxonNodeUUIDs, synonymType, citation, citationMicroReference, setNameInSource));
        }
        return result;
     }
@@ -410,7 +462,8 @@ public class TaxonNodeServiceImpl extends AnnotatableServiceBase<TaxonNode, ITax
             UUID newAcceptedTaxonNodeUUID,
             SynonymType synonymType,
             Reference citation,
-            String citationMicroReference) {
+            String citationMicroReference,
+            boolean setNameInSource) {
 
         TaxonNode oldTaxonNode = dao.load(oldTaxonNodeUuid);
         TaxonNode oldTaxonParentNode = oldTaxonNode.getParent();
@@ -420,7 +473,7 @@ public class TaxonNodeServiceImpl extends AnnotatableServiceBase<TaxonNode, ITax
                 newTaxonNode,
                 synonymType,
                 citation,
-                citationMicroReference);
+                citationMicroReference, setNameInSource);
         result.addUpdatedCdmId(new CdmEntityIdentifier(oldTaxonParentNode.getId(), TaxonNode.class));
         result.addUpdatedCdmId(new CdmEntityIdentifier(newTaxonNode.getId(), TaxonNode.class));
         result.setCdmEntity(oldTaxonParentNode);
@@ -604,11 +657,20 @@ public class TaxonNodeServiceImpl extends AnnotatableServiceBase<TaxonNode, ITax
           for (Object child: children){
               childNode = (TaxonNode) child;
               parent.addChildNode(childNode, childNode.getReference(), childNode.getMicroReference());
+
           }
        }else{
            result.includeResult(deleteTaxonNodes(node.getChildNodes(), config));
        }
 
+       //remove node from DescriptiveDataSet
+        commonService.getReferencingObjects(node).stream()
+        .filter(obj->obj instanceof DescriptiveDataSet)
+        .forEach(dataSet->{
+            ((DescriptiveDataSet)dataSet).removeTaxonSubtree(node);
+            dataSetService.saveOrUpdate((DescriptiveDataSet) dataSet);
+        });
+
        if (taxon != null){
                if (config.getTaxonNodeConfig().isDeleteTaxon() && (config.isDeleteInAllClassifications() || taxon.getTaxonNodes().size() == 1)){
                        result = taxonService.deleteTaxon(taxon.getUuid(), config, node.getClassification().getUuid());
@@ -672,7 +734,8 @@ public class TaxonNodeServiceImpl extends AnnotatableServiceBase<TaxonNode, ITax
     public UpdateResult moveTaxonNode(UUID taxonNodeUuid, UUID targetNodeUuid, int movingType){
         TaxonNode taxonNode = HibernateProxyHelper.deproxy(dao.load(taxonNodeUuid), TaxonNode.class);
        TaxonNode targetNode = HibernateProxyHelper.deproxy(dao.load(targetNodeUuid), TaxonNode.class);
-       return moveTaxonNode(taxonNode, targetNode, movingType);
+       UpdateResult result = moveTaxonNode(taxonNode, targetNode, movingType);
+       return result;
     }
 
     @Override
@@ -696,14 +759,8 @@ public class TaxonNodeServiceImpl extends AnnotatableServiceBase<TaxonNode, ITax
             result.addException(new Exception("The moving type "+ movingType +" is not supported."));
         }
 
-
         taxonNode = newParent.addChildNode(taxonNode, sortIndex, taxonNode.getReference(),  taxonNode.getMicroReference());
-        result.addUpdatedObject(newParent);
-        result.addUpdatedObject(oldParent);
-        result.setCdmEntity(taxonNode);
-
-        dao.saveOrUpdate(taxonNode);
-        dao.saveOrUpdate(oldParent);
+        result.addUpdatedObject(taxonNode);
 
         return result;
     }
@@ -712,13 +769,39 @@ public class TaxonNodeServiceImpl extends AnnotatableServiceBase<TaxonNode, ITax
 
     @Override
     @Transactional
-    public UpdateResult moveTaxonNodes(Set<UUID> taxonNodeUuids, UUID newParentNodeUuid, int movingType){
+    public UpdateResult moveTaxonNodes(Set<UUID> taxonNodeUuids, UUID newParentNodeUuid, int movingType, IProgressMonitor monitor){
+
+        if (monitor == null){
+            monitor = DefaultProgressMonitor.NewInstance();
+        }
         UpdateResult result = new UpdateResult();
+
         TaxonNode targetNode = dao.load(newParentNodeUuid);
-        for (UUID taxonNodeUuid: taxonNodeUuids){
-            TaxonNode taxonNode = dao.load(taxonNodeUuid);
-            result.includeResult(moveTaxonNode(taxonNode,targetNode, movingType));
+        List<TaxonNode> nodes = dao.list(taxonNodeUuids, null, null, null, null);
+        boolean hasPermission = true;
+
+        monitor.beginTask("Move Taxonnodes", nodes.size()*2);
+        monitor.subTask("move taxon nodes");
+        for (TaxonNode node: nodes){
+            if (!monitor.isCanceled()){
+                if (!nodes.contains(node.getParent())){
+                    result.includeResult(moveTaxonNode(node,targetNode, movingType));
+                }
+                monitor.worked(1);
+            }else{
+                monitor.done();
+                result.setAbort();
+                break;
+            }
         }
+        if (!monitor.isCanceled()){
+            monitor.subTask("saving and reindex");
+            dao.saveOrUpdateAll(nodes);
+        }else{
+            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
+        }
+
+        monitor.done();
         return result;
     }
 
@@ -746,6 +829,15 @@ public class TaxonNodeServiceImpl extends AnnotatableServiceBase<TaxonNode, ITax
         if (newTaxon.getName().getId() != 0){
             TaxonName name = nameService.load(newTaxon.getName().getUuid());
             newTaxon.setName(name);
+        }else{
+            for (HybridRelationship rel : newTaxon.getName().getHybridChildRelations()){
+                if (!rel.getHybridName().isPersited()) {
+                    nameService.save(rel.getHybridName());
+                }
+                if (!rel.getParentName().isPersited()) {
+                    nameService.save(rel.getParentName());
+                }
+            }
         }
         UUID taxonUUID = taxonService.saveOrUpdate(newTaxon);
         newTaxon = (Taxon) taxonService.load(taxonUUID);
@@ -753,15 +845,14 @@ public class TaxonNodeServiceImpl extends AnnotatableServiceBase<TaxonNode, ITax
         TaxonNode parent = dao.load(parentNodeUuid);
         TaxonNode child = null;
         try{
-            child = parent.addChildTaxon(newTaxon, parent.getReference(), parent.getMicroReference());
+            child = parent.addChildTaxon(newTaxon,ref, microref);
         }catch(Exception e){
             result.addException(e);
             result.setError();
             return result;
         }
-//        child = dao.save(child);
+        child = dao.save(child);
 
-        dao.saveOrUpdate(parent);
         result.addUpdatedObject(parent);
         if (child != null){
             result.setCdmEntity(child);
@@ -769,6 +860,83 @@ public class TaxonNodeServiceImpl extends AnnotatableServiceBase<TaxonNode, ITax
         return result;
 
     }
+
+
+    @Override
+    @Transactional
+    public UpdateResult saveNewTaxonNode(TaxonNode newTaxonNode){
+        UpdateResult result = new UpdateResult();
+        UUID parentUuid = newTaxonNode.getParent().getUuid();
+        Taxon taxon = null;
+
+        if (newTaxonNode.getTaxon().getId() != 0){
+            taxon = (Taxon)taxonService.load(newTaxonNode.getTaxon().getUuid());
+            //newTaxonNode.setTaxon(taxon);
+        }else if (newTaxonNode.getTaxon().getName().getId() != 0){
+            TaxonName name = nameService.load(newTaxonNode.getTaxon().getName().getUuid());
+            taxon = newTaxonNode.getTaxon();
+            taxon.setName(name);
+        }else{
+            for (HybridRelationship rel : newTaxonNode.getTaxon().getName().getHybridChildRelations()){
+                if (!rel.getHybridName().isPersited()) {
+                    nameService.save(rel.getHybridName());
+                }
+                if (!rel.getParentName().isPersited()) {
+                    nameService.save(rel.getParentName());
+                }
+            }
+        }
+        if (taxon == null){
+            taxon = newTaxonNode.getTaxon();
+        }
+        taxon.removeTaxonNode(newTaxonNode);
+
+        if (taxon.getSec() != null && taxon.getSec().isPersited()){
+            Reference sec = referenceService.load(taxon.getSec().getUuid());
+            taxon.setSec(sec);
+        }
+        if (taxon.getId() == 0){
+            UUID taxonUUID = taxonService.saveOrUpdate(taxon);
+            taxon = (Taxon) taxonService.load(taxonUUID);
+
+        }
+
+
+        TaxonNode parent = dao.load(parentUuid);
+        TaxonNode child = null;
+        try{
+            child = parent.addChildTaxon(taxon, newTaxonNode.getReference(), newTaxonNode.getMicroReference());
+
+        }catch(Exception e){
+            result.addException(e);
+            result.setError();
+            return result;
+        }
+
+        child.setUnplaced(newTaxonNode.isUnplaced());
+        child.setExcluded(newTaxonNode.isExcluded());
+        child.setDoubtful(newTaxonNode.isDoubtful());
+        for (TaxonNodeAgentRelation agentRel :newTaxonNode.getAgentRelations()){
+            child.addAgentRelation(agentRel.getType(), agentRel.getAgent());
+        }
+        for (Entry<Language, LanguageString> entry: newTaxonNode.getExcludedNote().entrySet()){
+            child.putExcludedNote(entry.getKey(), entry.getValue().getText());
+        }
+
+        newTaxonNode = null;
+        dao.saveOrUpdate(child);
+
+        result.addUpdatedObject(child.getParent());
+        if (child != null){
+            result.setCdmEntity(child);
+        }
+        return result;
+
+
+    }
+
+
+
     @Override
     @Transactional
     public UpdateResult createNewTaxonNode(UUID parentNodeUuid, UUID taxonUuid, Reference ref, String microref){
@@ -812,10 +980,11 @@ public class TaxonNodeServiceImpl extends AnnotatableServiceBase<TaxonNode, ITax
     }
 
     @Override
-    @Transactional
+    @Transactional(readOnly=false)
     public UpdateResult setSecundumForSubtree(SecundumForSubtreeConfigurator config) {
         UpdateResult result = new UpdateResult();
         IProgressMonitor monitor = config.getMonitor();
+
         if (monitor == null){
             monitor = DefaultProgressMonitor.NewInstance();
         }
@@ -825,13 +994,6 @@ public class TaxonNodeServiceImpl extends AnnotatableServiceBase<TaxonNode, ITax
         if (config.getNewSecundum() != null){
             newSec = refService.load(config.getNewSecundum().getUuid());
         }
-        if (subTree != null){
-            subTreeIndex = TreeIndex.NewInstance(subTree.treeIndex());
-            Long count = dao.countChildrenOf(subTree, subTree.getClassification(), true);
-            int intCount = count.intValue();
-            monitor.beginTask("Update Secundum Reference", intCount);
-        }
-
 
         if (config.getSubtreeUuid() == null){
             result.setError();
@@ -845,19 +1007,23 @@ public class TaxonNodeServiceImpl extends AnnotatableServiceBase<TaxonNode, ITax
             result.addException(new NullPointerException("Subtree does not exist"));
             monitor.done();
             return result;
+        }else{
+            subTreeIndex = TreeIndex.NewInstance(subTree.treeIndex());
+            int count = config.isIncludeAcceptedTaxa() ? dao.countSecundumForSubtreeAcceptedTaxa(subTreeIndex, newSec, config.isOverwriteExistingAccepted(), config.isIncludeSharedTaxa(), config.isEmptySecundumDetail()):0;
+            count += config.isIncludeSynonyms() ? dao.countSecundumForSubtreeSynonyms(subTreeIndex, newSec, config.isOverwriteExistingSynonyms(), config.isIncludeSharedTaxa() , config.isEmptySecundumDetail()) :0;
+            monitor.beginTask("Update Secundum Reference", count);
         }
 
-
         //Reference ref = config.getNewSecundum();
         if (config.isIncludeAcceptedTaxa()){
             monitor.subTask("Update Accepted Taxa");
 
-            Set<TaxonBase> updatedTaxa = dao.setSecundumForSubtreeAcceptedTaxa(subTreeIndex, newSec, config.isOverwriteExistingAccepted(), config.isIncludeSharedTaxa(), config.isEmptySecundumDetail());
+            Set<TaxonBase> updatedTaxa = dao.setSecundumForSubtreeAcceptedTaxa(subTreeIndex, newSec, config.isOverwriteExistingAccepted(), config.isIncludeSharedTaxa(), config.isEmptySecundumDetail(), monitor);
             result.addUpdatedObjects(updatedTaxa);
         }
         if (config.isIncludeSynonyms()){
            monitor.subTask("Update Synonyms");
-           Set<TaxonBase> updatedSynonyms = dao.setSecundumForSubtreeSynonyms(subTreeIndex, newSec, config.isOverwriteExistingSynonyms(), config.isIncludeSharedTaxa() , config.isEmptySecundumDetail());
+           Set<TaxonBase> updatedSynonyms = dao.setSecundumForSubtreeSynonyms(subTreeIndex, newSec, config.isOverwriteExistingSynonyms(), config.isIncludeSharedTaxa() , config.isEmptySecundumDetail(), monitor);
            result.addUpdatedObjects(updatedSynonyms);
         }
 
@@ -866,52 +1032,71 @@ public class TaxonNodeServiceImpl extends AnnotatableServiceBase<TaxonNode, ITax
     }
 
 
-    /**
-     * {@inheritDoc}
-     */
     @Override
-    @Transactional
-    public UpdateResult setPublishForSubtree(UUID subtreeUuid, boolean publish, boolean includeAcceptedTaxa,
-            boolean includeSynonyms, boolean includeSharedTaxa, IProgressMonitor monitor) {
+    @Transactional(readOnly=false)
+    public UpdateResult setPublishForSubtree(PublishForSubtreeConfigurator config){
         UpdateResult result = new UpdateResult();
-       // IProgressMonitor monitor = config.getMonitor();
+        IProgressMonitor monitor = config.getMonitor();
         if (monitor == null){
             monitor = DefaultProgressMonitor.NewInstance();
         }
-        monitor.beginTask("Update publish flag", 100);
-        if (subtreeUuid == null){
+        TreeIndex subTreeIndex = null;
+
+        if (config.getSubtreeUuid() == null){
             result.setError();
             result.addException(new NullPointerException("No subtree given"));
             monitor.done();
             return result;
         }
-        TaxonNode subTree = find(subtreeUuid);
+        TaxonNode subTree = find(config.getSubtreeUuid());
+        boolean includeAcceptedTaxa = config.isIncludeAcceptedTaxa();
+        boolean publish = config.isPublish();
+        boolean includeSynonyms = config.isIncludeSynonyms();
+        boolean includeSharedTaxa = config.isIncludeSharedTaxa();
+        boolean includeHybrids = config.isIncludeHybrids();
+        boolean includeRelatedTaxa = config.isIncludeProParteSynonyms() || config.isIncludeMisapplications();
         if (subTree == null){
             result.setError();
             result.addException(new NullPointerException("Subtree does not exist"));
             monitor.done();
             return result;
+        }else{
+            subTreeIndex = TreeIndex.NewInstance(subTree.treeIndex());
+            int count = includeAcceptedTaxa ? dao.countPublishForSubtreeAcceptedTaxa(subTreeIndex, publish, includeSharedTaxa, includeHybrids):0;
+            count += includeSynonyms ? dao.countPublishForSubtreeSynonyms(subTreeIndex, publish, includeSharedTaxa, includeHybrids):0;
+            count += includeRelatedTaxa ? dao.countPublishForSubtreeRelatedTaxa(subTreeIndex, publish, includeSharedTaxa, includeHybrids):0;
+            monitor.beginTask("Update publish flag", count);
         }
-        TreeIndex subTreeIndex = TreeIndex.NewInstance(subTree.treeIndex());
 
 
         if (includeAcceptedTaxa){
             monitor.subTask("Update Accepted Taxa");
-            Set<TaxonBase> updatedTaxa = dao.setPublishForSubtreeAcceptedTaxa(subTreeIndex, publish, includeSharedTaxa);
-//            taxonService.saveOrUpdate(updatedTaxa);
+            Set<TaxonBase> updatedTaxa = dao.setPublishForSubtreeAcceptedTaxa(subTreeIndex, publish, includeSharedTaxa, includeHybrids, monitor);
             result.addUpdatedObjects(updatedTaxa);
         }
         if (includeSynonyms){
             monitor.subTask("Update Synonyms");
-            Set<TaxonBase> updatedSynonyms = dao.setPublishForSubtreeSynonyms(subTreeIndex, publish, includeSharedTaxa);
-//            taxonService.saveOrUpdate(updatedSynonyms);
+            Set<TaxonBase> updatedSynonyms = dao.setPublishForSubtreeSynonyms(subTreeIndex, publish, includeSharedTaxa, includeHybrids, monitor);
             result.addUpdatedObjects(updatedSynonyms);
         }
+        if (includeRelatedTaxa){
+            monitor.subTask("Update Related Taxa");
+            Set<UUID> relationTypes = new HashSet<>();
+            if (config.isIncludeMisapplications()){
+                relationTypes.addAll(TaxonRelationshipType.misappliedNameUuids());
+            }
+            if (config.isIncludeProParteSynonyms()){
+                relationTypes.addAll(TaxonRelationshipType.proParteOrPartialSynonymUuids());
+            }
+            Set<TaxonBase> updatedTaxa = dao.setPublishForSubtreeRelatedTaxa(subTreeIndex, publish,
+                    relationTypes, includeSharedTaxa, includeHybrids, monitor);
+            result.addUpdatedObjects(updatedTaxa);
+        }
+
         monitor.done();
         return result;
     }
 
-
     @Override
     public long count(TaxonNodeFilter filter){
         return nodeFilterDao.count(filter);
@@ -928,20 +1113,90 @@ public class TaxonNodeServiceImpl extends AnnotatableServiceBase<TaxonNode, ITax
     }
 
     @Override
-    @Transactional
-    public UUID monitSetSecundum(final SecundumForSubtreeConfigurator configurator) {
-        RemotingProgressMonitorThread monitorThread = new RemotingProgressMonitorThread() {
-            @Override
-            public Serializable doRun(IRemotingProgressMonitor monitor) {
-                configurator.setMonitor(monitor);
-                UpdateResult result = setSecundumForSubtree(configurator);
-                return result;
+    public TaxonNodeDto findCommonParentDto(Collection<TaxonNodeDto> nodes) {
+        TaxonNodeDto commonParent = null;
+        List<String> treePath = null;
+        for (TaxonNodeDto nodeDto : nodes) {
+            String nodeTreeIndex = nodeDto.getTreeIndex();
+            nodeTreeIndex = nodeTreeIndex.replaceFirst("#", "");
+            String[] split = nodeTreeIndex.split("#");
+            if(treePath == null){
+                treePath = Arrays.asList(split);
             }
-        };
-        UUID uuid = progressMonitorService.registerNewRemotingMonitor(monitorThread);
-        monitorThread.setPriority(3);
-        monitorThread.start();
-        return uuid;
+            else{
+                List<String> match = new ArrayList<>();
+                for(int i=0;i<treePath.size();i++){
+                    if(i>=split.length){
+                        //current tree index is shorter so break
+                        break;
+                    }
+                    else if(split[i].equals(treePath.get(i))){
+                        //match found
+                        match.add(treePath.get(i));
+                    }
+                    else{
+                        //first mismatch found
+                        break;
+                    }
+                }
+                treePath = match;
+                if(treePath.isEmpty()){
+                    //no common parent found for at least two nodes
+                    //-> they belong to a different classification
+                    break;
+                }
+            }
+        }
+        if(treePath!=null && !treePath.isEmpty()) {
+            //get last index
+            int nodeId = Integer.parseInt(treePath.get(treePath.size()-1));
+            TaxonNode taxonNode = dao.load(nodeId, null);
+            commonParent = new TaxonNodeDto(taxonNode);
+        }
+        return commonParent;
     }
 
+    @Override
+    public List<TaxonDistributionDTO> getTaxonDistributionDTOForSubtree(UUID parentNodeUuid, List<String> propertyPaths){
+        List<TaxonNode> nodes = listChildrenOf(load(parentNodeUuid), null, null,
+               true, true, propertyPaths);
+        List<TaxonDistributionDTO> result = new ArrayList<>();
+        for(TaxonNode node:nodes){
+            if (node.getTaxon() != null){
+                try{
+                    TaxonDistributionDTO dto = new TaxonDistributionDTO(node.getTaxon());
+                    result.add(dto);
+                }catch(Exception e){
+                    System.err.println(node.getTaxon().getTitleCache());
+                }
+
+            }
+
+        }
+
+        return result;
+    }
+
+    @Override
+    public <S extends TaxonNode> Pager<S> page(Class<S> clazz, List<Restriction<?>> restrictions, Integer pageSize,
+            Integer pageIndex, List<OrderHint> orderHints, List<String> propertyPaths) {
+        return page(clazz, restrictions, pageSize, pageIndex, orderHints, propertyPaths, INCLUDE_UNPUBLISHED);
+    }
+
+    @Override
+    public <S extends TaxonNode> Pager<S> page(Class<S> clazz, List<Restriction<?>> restrictions, Integer pageSize,
+            Integer pageIndex, List<OrderHint> orderHints, List<String> propertyPaths, boolean includeUnpublished) {
+
+        List<S> records;
+        long resultSize = dao.count(clazz, restrictions);
+        if(AbstractPagerImpl.hasResultsInRange(resultSize, pageIndex, pageSize)){
+            records = dao.list(clazz, restrictions, pageSize, pageIndex, orderHints, propertyPaths, includeUnpublished);
+        } else {
+            records = new ArrayList<>();
+        }
+        Pager<S> pager = new DefaultPagerImpl<>(pageIndex, resultSize, pageSize, records);
+        return pager;
+    }
+
+
 }