Project

General

Profile

feature request #7648

Create taxonrelation to genus or species when subordinate names are created

Added by Andreas Kohlbecker 4 months ago. Updated about 2 months ago.

Status:
Resolved
Priority:
Priority14
Category:
cdm-vaadin
Target version:
Start date:
08/13/2018
Due date:
% Done:

70%

Severity:
normal
Tags:

Description

When creating a species a new Taxon needs to be created for this species and the new taxon needs to be related to the genus via INCLUDED_IN.

Q1:

According to #6173 6) [N1T] 2. there should always only be one taxon per name for the higher classification.

Does this also account for genera? In the same ticket we wrote:

The search process primarily aims in finding genera, all species and subspecies which fall under each genus found are to be displayed independently of the higher classification information associated with the individual names. Practically this will be solved by creating for each TaxonName an includenIn relation to the Taxon for the name used in the uninomialOrGenus and in the specificEpithet field.
Ranks like e.g. variety and subgenus can not be associated automatically, so their relation to the genus or species respectively must be created manually if this information is available or if it is required in a specific case. It also should be considered to associate e.g. species to the according subgenera.


The relation needs to be updated or deleted when

  • a higher ranked name part is changed

Related issues

Related to AlgenRegistrierung - task #6173: Concept for a useful algae registry taxon classification In Progress 11/01/2016
Related to Edit - bug #7858: names used in the TaxonGraph can not be deleted if the user has no DELETE permission on Taxon New 10/24/2018
Related to Edit - task #7862: improve documentation on TaxonGraphHibernateListener and related classes New 10/24/2018
Copied to Edit - feature request #7861: TaxonGraphHibernateListener: inject the ITaxonGraphDao into TaxonGraphBeforeTransactionCompleteProcess New 10/24/2018

Associated revisions

Revision 6d636c9e (diff)
Added by Andreas Kohlbecker 3 months ago

ref #7648 generalizing bean instantiation in cdm presenters

Revision 4e19a89d (diff)
Added by Andreas Kohlbecker 3 months ago

ref #7648 using CdmEntityInstantiator to disentangle the handling the creation of new taxon names for registrations

Revision a8ef58b2 (diff)
Added by Andreas Kohlbecker 3 months ago

ref #7648 initial implementation of TaxonGraphService with tests and TaxonGraphObserver

Revision e9ca40c5 (diff)
Added by Andreas Kohlbecker 3 months ago

ref #7648 TaxonGraphService complete with full set of tests

Revision e15e805c (diff)
Added by Andreas Kohlbecker 3 months ago

ref #7648 TaxonGraphService as dao and TaxonGraphObserver as TaxonGraphHibernateListener

Revision 005f3c40 (diff)
Added by Andreas Kohlbecker 3 months ago

ref #7648 all TaxonGraph updating done in a BeforeTransactionCompletionProcess implementation - TaxonGraphHibernateListener is working now

Revision 6b3c858a (diff)
Added by Andreas Kohlbecker 3 months ago

ref #7648 cleaning up and attempting to fix TaxonGraphService test setup

Revision 335d7c23 (diff)
Added by Andreas Kohlbecker 3 months ago

ref #7648 introducing CdmPreferenceLookup for cached access to the CdmProperty with predicate 'TaxonGraphSecRefUuid' and others
- using this Preference in TaxonGraphDaoHibernateImpl and TaxonGraphBeforeTransactionCompleteProcess
- adapting tests accordingly
- deprecating secRefUUID setters
- cleaning up

Revision 38588c66 (diff)
Added by Andreas Kohlbecker 3 months ago

ref #7648 correct handling existing taxa without secReference

Revision 97312e07 (diff)
Added by Andreas Kohlbecker 3 months ago

ref #7648 registering TaxonGraphHibernateListener for RefistrationUI

Revision 196c665f (diff)
Added by Andreas Kohlbecker 2 months ago

ref #7648 fixing inconsistency in test data and improving logging

Revision 5e4cc7ea (diff)
Added by Andreas Kohlbecker 2 months ago

ref #7648 solving session corruption problems in test suite for TaxonGraphHibernateListenerTest

Revision 15d2f2bf (diff)
Added by Andreas Kohlbecker 2 months ago

ref #7648 allowing to set the TaxonGraphHibernateListener inactive - only for test environments

Revision 9a2d0b71 (diff)
Added by Andreas Kohlbecker 2 months ago

ref #7648 allowing to set the TaxonGraphHibernateListener inactive - only for test environments

Revision 75338ad3 (diff)
Added by Andreas Kohlbecker 2 months ago

ref #7648 common base class AbstractHibernateTaxonGraphProcessor to deduplicate TaxonGraph methods

Revision 81c01935 (diff)
Added by Andreas Kohlbecker 2 months ago

ref #7648 more pulling up into AbstractHibernateTaxonGraphProcessor

Revision 397b3cd7 (diff)
Added by Andreas Kohlbecker 2 months ago

ref #7648 startup option for registrations to create missing taxon graph edges

Revision 4c5284df (diff)
Added by Andreas Kohlbecker 2 months ago

ref #7648 moving taxongraph methods from taxonDao to taxonGraphDao

Revision 19628819 (diff)
Added by Andreas Kohlbecker 2 months ago

ref #7648 fixing problems in TaxonGraphTest

Revision 11ab88e8 (diff)
Added by Andreas Kohlbecker 2 months ago

ref #7648 automatic management of lower ranked names in the taxon graph

Revision f7df0b93 (diff)
Added by Andreas Kohlbecker 2 months ago

ref #7648 TaxonGraphHibernateListener as Spring bean configured via the CdmHibernateListenerConfiguration

Revision 48e27c57 (diff)
Added by Andreas Kohlbecker 2 months ago

ref #7648 moving TaxonGraphHibernateListener and related classes into cdmlib-service

Revision f528c666 (diff)
Added by Andreas Kohlbecker 2 months ago

ref #7648 free configurable TaxonGraphHibernateListener ready

Revision 6ec42fab (diff)
Added by Andreas Kohlbecker 2 months ago

ref #7648 adapting CdmVaadinConfiguration to changes in the TaxonGraphHibernateListener

Revision 6623206c (diff)
Added by Andreas Kohlbecker 2 months ago

ref #7709 adding missing @CdmHibernateListener config annotation to allow activating the TaxonGraphHibernateListener bean

Revision ee7aab02 (diff)
Added by Andreas Kohlbecker 2 months ago

ref #7709 fixing missing runAsAuthenticator for RunAsAdmin, TaxonGraphHibernateListener and test

Revision c621e94f (diff)
Added by Andreas Kohlbecker 2 months ago

ref #7648 using runAsAuthenticator for RunAsAdmin to configure TaxonGraphHibernateListener for vaadin

Revision 644bd633 (diff)
Added by Andreas Kohlbecker 2 months ago

ref #7648 adding links to the ticket as preliminary rudimentary documentation

Revision 886e3f27 (diff)
Added by Andreas Kohlbecker 2 months ago

ref #7648 avoiding NPE during equality check in TaxonGraphBeforeTransactionCompleteProcess

Revision 388fc07c (diff)
Added by Andreas Kohlbecker 2 months ago

ref #7648 fixing bugs related to single taxa per graph and updating rels from lower ranks, with testcase

History

#1 Updated by Andreas Kohlbecker 4 months ago

  • Description updated (diff)

#2 Updated by Andreas Kohlbecker 4 months ago

  • Related to task #6173: Concept for a useful algae registry taxon classification added

#3 Updated by Andreas Müller 4 months ago

Andreas Kohlbecker wrote:

When creating a species a new Taxon needs to be created for this species and the new taxon needs to be related to the genus via INCLUDED_IN.

Q1:

According to #6173 6) [N1T] 2. there should always only be one taxon per name for the higher classification.

The final decision on N1T was to create multiple taxa for higher rank taxon names, 1 taxon per classification. This will also hold for taxa of rank genus. Therefore we need to create multiple INCLUDED_IN relationships depending on the number of taxa we have.

#4 Updated by Andreas Müller 4 months ago

Also after import of new higher classifications we need to attache subgeneric taxa to their according taxa in bulk.

So maybe we should implement an algorithm that creates these relationships for all relevant data additionally to the algorithm which attaches a single new taxon to its parents.

#5 Updated by Andreas Kohlbecker 3 months ago

  • Target version changed from Release 5.3 to Release 5.4

#6 Updated by Andreas Kohlbecker 3 months ago

Andreas Müller wrote:

Andreas Kohlbecker wrote:

When creating a species a new Taxon needs to be created for this species and the new taxon needs to be related to the genus via INCLUDED_IN.

Q1:

According to #6173 6) [N1T] 2. there should always only be one taxon per name for the higher classification.

The final decision on N1T was to create multiple taxa for higher rank taxon names, 1 taxon per classification. This will also hold for taxa of rank genus. Therefore we need to create multiple INCLUDED_IN relationships depending on the number of taxa we have.

this is a misinterpretation of the N1T schema!

#7 Updated by Andreas Kohlbecker 3 months ago

  • Description updated (diff)
  • Priority changed from New to Highest

#8 Updated by Andreas Kohlbecker 3 months ago

  • Status changed from New to Resolved
  • Assignee changed from Andreas Kohlbecker to Andreas Müller
  • % Done changed from 0 to 50

please review 335d7c23

especially

  • PreferencePredicate.TaxonGraphSecRefUuid predicate ok?
  • CdmPreferenceLookup where to move this to?

#9 Updated by Andreas Kohlbecker 2 months ago

There is a problem when running the test in the maven test suite. The TaxonGraphHibernateListenerTest fails when it is run after specific combinations of other tests:

AnnotationDaoTest,CdmEntityDaoBaseTest,TaxonGraphHibernateListenerTest ==> FAILS
CdmEntityDaoBaseTest,AnnotationDaoTest,TaxonGraphHibernateListenerTest ==> FAILS
AnnotationDaoTest,TaxonGraphHibernateListenerTest ==> OK
CdmEntityDaoBaseTest,TaxonGraphHibernateListenerTest ==> OK

The problem caused by the test combination is:

org.hibernate.SessionException: Session is closed!
        at org.hibernate.internal.AbstractSessionImpl.errorIfClosed(AbstractSessionImpl.java:132)
        at org.hibernate.internal.SessionImpl.clear(SessionImpl.java:384)
        at org.springframework.orm.hibernate5.SpringSessionSynchronization.afterCompletion(SpringSessionSynchronization.java:143)
        at org.springframework.transaction.support.TransactionSynchronizationUtils.invokeAfterCompletion(TransactionSynchronizationUtils.java:168)
        at org.springframework.transaction.support.AbstractPlatformTransactionManager.invokeAfterCompletion(AbstractPlatformTransactionManager.java:1001)
        at org.springframework.transaction.support.AbstractPlatformTransactionManager.triggerAfterCompletion(AbstractPlatformTransactionManager.java:976)
        at org.springframework.transaction.support.AbstractPlatformTransactionManager.processRollback(AbstractPlatformTransactionManager.java:880)
        at org.springframework.transaction.support.AbstractPlatformTransactionManager.rollback(AbstractPlatformTransactionManager.java:830)
        at eu.etaxonomy.cdm.test.integration.CdmTransactionalIntegrationTest.endTransaction(CdmTransactionalIntegrationTest.java:306)
        at eu.etaxonomy.cdm.test.integration.CdmTransactionalIntegrationTest.onTearDown(CdmTransactionalIntegrationTest.java:232)

#10 Updated by Andreas Kohlbecker 2 months ago

"Session is closed!"-problem is solved now.

#11 Updated by Andreas Kohlbecker 2 months ago

  • % Done changed from 50 to 70

implementation complete!

#12 Updated by Andreas Kohlbecker 2 months ago

  • Status changed from Resolved to In Progress

The taxon graph management processes are currently run in with the current authentication, this causes problems if a user has no grants to create or modify taxa as demonstrated by the below stacktrace snippet:

Caused by: eu.etaxonomy.cdm.database.PermissionDeniedException: [CREATE] not permitted for 'andreas' on Taxon[uuid:2abdadac-6725-493b-9ccc-70b071f773ce', toString:'Achnanthes kohlii Koh, AK & Pakla, MP sec. PhycoBank']
    at eu.etaxonomy.cdm.persistence.hibernate.CdmSecurityHibernateInterceptor.checkPermissions(CdmSecurityHibernateInterceptor.java:207)
    at eu.etaxonomy.cdm.persistence.hibernate.CdmSecurityHibernateInterceptor.onSave(CdmSecurityHibernateInterceptor.java:106)
    at org.hibernate.event.internal.AbstractSaveEventListener.substituteValuesIfNecessary(AbstractSaveEventListener.java:388)
    at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:254)
    at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:178)
    at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:121)
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:192)
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:177)
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:97)
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:73)
    at org.hibernate.internal.SessionImpl.fireSaveOrUpdate(SessionImpl.java:648)
    at org.hibernate.internal.SessionImpl.saveOrUpdate(SessionImpl.java:640)
    at org.hibernate.internal.SessionImpl.saveOrUpdate(SessionImpl.java:635)
    at eu.etaxonomy.cdm.persistence.dao.hibernate.taxonGraph.AbstractHibernateTaxonGraphProcessor.assureSingleTaxon(AbstractHibernateTaxonGraphProcessor.java:215)
    at eu.etaxonomy.cdm.persistence.dao.hibernate.taxonGraph.TaxonGraphBeforeTransactionCompleteProcess.onNameOrRankChange(TaxonGraphBeforeTransactionCompleteProcess.java:157)
    at eu.etaxonomy.cdm.persistence.dao.hibernate.taxonGraph.TaxonGraphBeforeTransactionCompleteProcess.onNewTaxonName(TaxonGraphBeforeTransactionCompleteProcess.java:153)
    at eu.etaxonomy.cdm.persistence.dao.hibernate.taxonGraph.TaxonGraphBeforeTransactionCompleteProcess.doBeforeTransactionCompletion(TaxonGraphBeforeTransactionCompleteProcess.java:80)
    at org.hibernate.engine.spi.ActionQueue$BeforeTransactionCompletionProcessQueue.beforeTransactionCompletion(ActionQueue.java:899)
    at org.hibernate.engine.spi.ActionQueue.beforeTransactionCompletion(ActionQueue.java:481)
    at org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:2340)
    at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.beforeTransactionCompletion(JdbcCoordinatorImpl.java:485)
    at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.beforeCompletionCallback(JdbcResourceLocalTransactionCoordinatorImpl.java:147)
    at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.access$100(JdbcResourceLocalTransactionCoordinatorImpl.java:38)
    at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.commit(JdbcResourceLocalTransactionCoordinatorImpl.java:231)
    at org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:65)
    at org.springframework.orm.hibernate5.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:581)
    ... 106 more

The operations in the TaxonGraphBeforeTransactionCompleteProcess which modify the persisted data must be run as admin, see RunAsAuthenticator

#13 Updated by Andreas Kohlbecker 2 months ago

  • Status changed from In Progress to Feedback

The RunAsAuthenticator is in cdmlib-service and TaxonGraphBeforeTransactionCompleteProcess in cdmlib-persistence.
We need to move on of these classes into the other project. Move TaxonGraphBeforeTransactionCompleteProcess, TaxonGraphHibernateListener into cdmlib-service or rater move a authentication related class into the persistence. I think the first option is more logical.

#14 Updated by Andreas Kohlbecker 2 months ago

After moving the TaxonGraphHibernateListenerTest into cdmlib-services we are again facing problems with test being not independent.

Running

mvn  -Dtest=eu.etaxonomy.cdm.api.service.dto.TaxonRelationshipsDTOTest,eu.etaxonomy.cdm.api.service.taxonGraph.TaxonGraphHibernateListenerTest test

causes all tests in TaxonGraphHibernateListenerTest to fail due to polluted Session.

Tests in error: 
  testChangeSpecificEpithet_of_InfraSpecific(eu.etaxonomy.cdm.api.service.taxonGraph.TaxonGraphHibernateListenerTest): object references an unsaved transient instance - save the transient instance before flushing: eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType
  testChangeNomRef(eu.etaxonomy.cdm.api.service.taxonGraph.TaxonGraphHibernateListenerTest): object references an unsaved transient instance - save the transient instance before flushing: eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType
  testChangeSpecificEpithet_of_Species(eu.etaxonomy.cdm.api.service.taxonGraph.TaxonGraphHibernateListenerTest): object references an unsaved transient instance - save the transient instance before flushing: eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType
  testChangeRank(eu.etaxonomy.cdm.api.service.taxonGraph.TaxonGraphHibernateListenerTest): object references an unsaved transient instance - save the transient instance before flushing: eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType
  testNewTaxonName(eu.etaxonomy.cdm.api.service.taxonGraph.TaxonGraphHibernateListenerTest): object references an unsaved transient instance - save the transient instance before flushing: eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType
  testChangeGenus(eu.etaxonomy.cdm.api.service.taxonGraph.TaxonGraphHibernateListenerTest): object references an unsaved transient instance - save the transient instance before flushing: eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType
  testNewGenusName(eu.etaxonomy.cdm.api.service.taxonGraph.TaxonGraphHibernateListenerTest): object references an unsaved transient instance - save the transient instance before flushing: eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType

When running all test which precedeTaxonGraphHibernateListenerTest in the suite

mvn -Dtest=eu.etaxonomy.cdm.api.service.SecurityTest,eu.etaxonomy.cdm.api.service.StatisticsServiceImplTest,eu.etaxonomy.cdm.api.service.TaxonNodeDtoByNameComparatorTest,eu.etaxonomy.cdm.api.service.TaxonNodeServiceImplTest,eu.etaxonomy.cdm.api.service.TaxonServiceImplBusinessTest,eu.etaxonomy.cdm.api.service.TaxonServiceImplTest,eu.etaxonomy.cdm.api.service.TaxonServiceSearchTaxaAndNamesTest,eu.etaxonomy.cdm.api.service.TaxonServiceSearchTest,eu.etaxonomy.cdm.api.service.TermServiceImplTest,eu.etaxonomy.cdm.api.service.TransmissionEngineDistributionTest,eu.etaxonomy.cdm.api.service.TreeIndexComparatorTest,eu.etaxonomy.cdm.api.service.TypeDesignationSetManagerTest,eu.etaxonomy.cdm.api.service.UserAndGroupServiceImplTest,eu.etaxonomy.cdm.api.service.VocabularyServiceImplTest,eu.etaxonomy.cdm.api.service.dto.TaxonRelationshipsDTOTest,eu.etaxonomy.cdm.api.service.idminter.RegistrationIdentifierMinterTest,eu.etaxonomy.cdm.api.service.lsid.LSIDAuthorityServiceTest,eu.etaxonomy.cdm.api.service.lsid.LSIDDataServiceTest,eu.etaxonomy.cdm.api.service.lsid.LSIDMetadataServiceTest,eu.etaxonomy.cdm.api.service.lsid.LSIDRegistryTest,eu.etaxonomy.cdm.api.service.molecular.AmplificationServiceTest,eu.etaxonomy.cdm.api.service.molecular.PrimerServiceTest,eu.etaxonomy.cdm.api.service.molecular.SequenceServiceTest,eu.etaxonomy.cdm.api.service.pager.PagerTest,eu.etaxonomy.cdm.api.service.search.LuceneIndexToolProviderTest,eu.etaxonomy.cdm.api.service.search.QueryFactoryTest,eu.etaxonomy.cdm.api.service.taxonGraph.TaxonGraphHibernateListenerTest test

only testNewGenusName() fails

This is just like playing lotto!

#15 Updated by Andreas Kohlbecker 2 months ago

Since this is not a problem with the TaxonGraphHibernateListenerTest I am creating a new issue: #7818

#16 Updated by Andreas Kohlbecker 2 months ago

The problem described in #7818 was not the actual cause for the test failing in the full suite, another candidate is the RegistrationIdentifierMinterTest:

mvn -Dtest=eu.etaxonomy.cdm.api.service.idminter.RegistrationIdentifierMinterTest,eu.etaxonomy.cdm.api.service.taxonGraph.TaxonGraphHibernateListenerTest test

#17 Updated by Andreas Kohlbecker 2 months ago

now the expected functionality I implemented, but we could think about refactoring the TaxonGraphHibernateListener a bit:

Andreas Müller suggested to inject the ITaxonGraphDao into TaxonGraphBeforeTransactionCompleteProcess which could be done by passing it as cunstrucor parameter of the TaxonGraphBeforeTransactionCompleteProcess. Here some example code snippets of a spring configuration class setting up the TaxonGraphHibernateListener:

@Configuration
@CdmHibernateListener // enable the configuration which activates the TaxonGraphHibernateListener bean
public class MyConfiguration {

    @Autowired
    @Qualifier("runAsAuthenticationProvider")
    private AuthenticationProvider runAsAuthenticationProvider;

    @Autowired
    private ITaxonGraphHibernateListener taxonGraphHibernateListener;


    @Autowired
    private ITaxonGraphDao taxonGraphDao;

    @Bean
    public MyBean myBean() {
     taxonGraphHibernateListener.registerProcessClass(
        TaxonGraphBeforeTransactionCompleteProcess.class, 
        new Object[]{new RunAsAdmin(runAsAuthenticationProvider), taxonGraphDao}, 
        new Class[]{IRunAs.class, ITaxonGraphDao.class});
    }

}

The constructor of the TaxonGraphBeforeTransactionCompleteProcess would need to be adapted for this to work.
The main benefit of this excercise is that we could get rid of the AbstractHibernateTaxonGraphProcessor which currently is the common base class for the dao and the TaxonGraphBeforeTransactionCompleteProcess.

And also some documentation which goes beyond linking this ticket would be nice.

#18 Updated by Andreas Müller about 2 months ago

  • Assignee changed from Andreas Müller to Andreas Kohlbecker

Can we create a new ticket for the remaining issues and close this one?

Does this ticket need further review from my side? I think functionality testing can and will be done by phycobank anyway.

#19 Updated by Andreas Kohlbecker about 2 months ago

  • Related to bug #7858: names used in the TaxonGraph can not be deleted if the user has no DELETE permission on Taxon added

#20 Updated by Andreas Kohlbecker about 2 months ago

  • Copied to feature request #7861: TaxonGraphHibernateListener: inject the ITaxonGraphDao into TaxonGraphBeforeTransactionCompleteProcess added

#21 Updated by Andreas Kohlbecker about 2 months ago

  • Related to task #7862: improve documentation on TaxonGraphHibernateListener and related classes added

#22 Updated by Andreas Kohlbecker about 2 months ago

  • Status changed from Feedback to Resolved
  • Assignee changed from Andreas Kohlbecker to Andreas Müller

ok, all open tasks are copied to new issues: #7860, #7861

the reason why I passed this to you is written in comment 8 which is meanwhile hard to find in the long thread, here is it again:

please review 335d7c23

especially

  • PreferencePredicate.TaxonGraphSecRefUuid predicate ok?
  • CdmPreferenceLookup where to move this to?

#23 Updated by Andreas Müller about 2 months ago

  • Priority changed from Highest to Priority14

Also available in: Atom PDF

Add picture from clipboard (Maximum size: 40 MB)