changeset 180:0d31c8be7c31

new MissingRelations feature and UI. including searchByRelations() with Filter in FullEntityRepositoryBean.
author Robert Casties <casties@mpiwg-berlin.mpg.de>
date Wed, 13 Jun 2018 14:57:13 +0200
parents c9dec00f0f17
children 52aa06772336
files src/main/java/de/mpiwg/itgroup/ismi/browse/FullEntityRepositoryBean.java src/main/java/de/mpiwg/itgroup/ismi/entry/beans/SessionBean.java src/main/java/de/mpiwg/itgroup/ismi/merge/MissingRelationsBean.java src/main/java/de/mpiwg/itgroup/ismi/merge/PublicByAuthorBean.java src/main/webapp/browse/entityDetails.xhtml src/main/webapp/clean/components/missingAuthor.xhtml src/main/webapp/clean/components/missingCodex.xhtml src/main/webapp/clean/components/missingText.xhtml src/main/webapp/clean/missingRelations.xhtml src/main/webapp/templates/main_template.xhtml
diffstat 10 files changed, 770 insertions(+), 7 deletions(-) [+]
line wrap: on
line diff
--- a/src/main/java/de/mpiwg/itgroup/ismi/browse/FullEntityRepositoryBean.java	Fri Jun 08 18:59:49 2018 +0200
+++ b/src/main/java/de/mpiwg/itgroup/ismi/browse/FullEntityRepositoryBean.java	Wed Jun 13 14:57:13 2018 +0200
@@ -9,6 +9,8 @@
 import org.mpi.openmind.cache.WrapperService;
 import org.mpi.openmind.repository.bo.Attribute;
 import org.mpi.openmind.repository.bo.Entity;
+import org.mpi.openmind.repository.bo.Relation;
+import org.mpi.openmind.repository.services.utils.RelationFilter;
 
 /**
  * EntityRepositoryBean for full Entities with Attributes and Relations loaded.
@@ -24,8 +26,9 @@
 
     protected boolean sortAttributeNumerically = false;
 
-    /*
-     * (non-Javadoc)
+    /**
+     * updateAdvancedEntities() method that makes sure that the current Entities
+     * are not lightweight.
      * 
      * @see de.mpiwg.itgroup.ismi.browse.AbstractEntityRepositoryBean#
      * updateAdvancedEntities()
@@ -71,7 +74,15 @@
         return GOTO_ENTITY_REPOSITORY;
     }
 
+    /**
+     * Loads all entities of this.objectClass and sorts by this.sortAttributeName.
+     * 
+     * Sort attributes as integer if this.sortAttributeNumerically.
+     *  
+     * @throws Exception
+     */
     public void sortByAttributes() throws Exception {
+        logger.debug("Start sortByAttributes...");
         this.resultMode = MODE_ADVANCED;
         this.setPage("");
         this.entities = new ArrayList<Entity>();
@@ -83,7 +94,7 @@
          * run search and sort result (by attribute)
          */
         List<Entity> resultList = getWrapper().getEntitiesByDef(this.objectClass);
-        // sort List (by ownvalue)
+        // sort List (by attribute)
         Collections.sort(resultList,
                 getEntityAttributeComparator(this.sortAttributeName, this.sortAttributeNumerically));
         this.entities = resultList;
@@ -97,8 +108,18 @@
         } else {
             this.resultSummaryMsg = "No items were found!";
         }
+        logger.debug("Done sortByAttributes.");
     }
 
+    /**
+     * Returns Comparator for Entities that uses the Attribute attname.
+     * 
+     * Sorts as integer if numerically.
+     * 
+     * @param attName
+     * @param numerically
+     * @return
+     */
     public Comparator<Entity> getEntityAttributeComparator(final String attName, final boolean numerically) {
         return new Comparator<Entity>() {
             @Override
@@ -143,6 +164,84 @@
             }
         };
     }
+    
+    /**
+     * Loads all entities of this.objectClass matching the given RelationFilters.
+     * 
+     * Filters Relations by relObjectClass and either srcObjectClass or tarObjectClass.
+     * 
+     * Requires all RelationFilters to match.
+     *  
+     * @throws Exception
+     */
+    public void searchByRelations(List<RelationFilter> relationFilters) {
+        logger.debug("Start searchByRelations...");
+        this.resultMode = MODE_ADVANCED;
+        this.setPage("");
+        this.entities = new ArrayList<Entity>();
+        this.currentEntities = new ArrayList<Entity>();
+        this.resultSummaryMsg = "";        
+        
+        /*
+         * get all entities and filter result (by relation)
+         */
+        List<Entity> resultList = getWrapper().getEntitiesByDef(this.objectClass);
+        for (Entity entity : resultList) {
+        	if (entity.isLightweight()) {
+        		entity = getWrapper().getEntityContent(entity);
+        	}
+        	boolean condFailed = false;
+        	for (RelationFilter filter :relationFilters) {
+        		if (filter.tarObjectClass != null) {
+            		List<Relation> rels = entity.getSourceRelations(filter.relObjectClass, filter.tarObjectClass);
+            		if (filter.relationMissing) {
+            			// is the relation missing?
+            			if (!rels.isEmpty()) {
+            				condFailed = true;
+            				break;
+            			}
+            		} else {
+            			if (rels.isEmpty()) {
+            				condFailed = true;
+            				break;
+            			}
+            		}
+        		} else if (filter.srcObjectClass != null) {
+            		List<Relation> rels = entity.getTargetRelations(filter.relObjectClass, filter.srcObjectClass);
+            		if (filter.relationMissing) {
+            			// is the relation missing?
+            			if (!rels.isEmpty()) {
+            				condFailed = true;
+            				break;
+            			}
+            		} else {
+            			if (rels.isEmpty()) {
+            				condFailed = true;
+            				break;
+            			}
+            		}
+        		}
+        	}
+        	if (!condFailed) {
+        		// all conditions matched
+        		entities.add(entity);
+        	}
+        }
+        
+        // sort List (by ownValue)
+        Collections.sort(entities);
+
+        if (entities.size() > 0) {
+            int entitiesCount = entities.size();
+            this.resultSummaryMsg = entitiesCount + " items were found!";
+            this.advancedPaginator.setCurrentPage(0);
+            this.advancedPaginator.resetNumberOfPages(entitiesCount);
+            this.updateAdvancedEntities();
+        } else {
+            this.resultSummaryMsg = "No items were found!";
+        }
+        logger.debug("Done searchByRelations.");
+    }
 
     /**
      * @return the sortAttributeName
--- a/src/main/java/de/mpiwg/itgroup/ismi/entry/beans/SessionBean.java	Fri Jun 08 18:59:49 2018 +0200
+++ b/src/main/java/de/mpiwg/itgroup/ismi/entry/beans/SessionBean.java	Wed Jun 13 14:57:13 2018 +0200
@@ -27,6 +27,7 @@
 import de.mpiwg.itgroup.ismi.event.beans.TransferEvent;
 import de.mpiwg.itgroup.ismi.merge.GeneralMerge;
 import de.mpiwg.itgroup.ismi.merge.ImportMerge;
+import de.mpiwg.itgroup.ismi.merge.MissingRelationsBean;
 import de.mpiwg.itgroup.ismi.merge.PublicByAuthorBean;
 import de.mpiwg.itgroup.ismi.merge.ReferenceMerge;
 import de.mpiwg.itgroup.ismi.publicView.DynamicPageEditor;
@@ -49,6 +50,7 @@
 	private TransferEvent transferEventForm = new TransferEvent();
 	private transient AdvancedSearchBean advancedSearch = new AdvancedSearchBean();
 	private transient PublicByAuthorBean publicByAuthor = new PublicByAuthorBean();
+	private transient MissingRelationsBean missingRelations = new MissingRelationsBean();
 	
 	private EntityDetailsBean entDetailsForm = new EntityDetailsBean();
 	
@@ -140,7 +142,7 @@
 	private String selectedEventTab = WITNESS_STUDY_EVENT_TAB;
 	private String selectedAdvancedSearchTab = "01";
 	private String selectedPublicByAuthorTab = "aut";
-	
+	private String selectedMissingRelationsTab = "aut";
 
 	private Long editFormCurrentEntId;
 
@@ -663,6 +665,11 @@
 		this.redirect(null, url);
 	}
 	
+	public void listenerMissingRelationsTabChange(ItemChangeEvent event) {
+		String url = "?formIndex=" + this.selectedMissingRelationsTab;
+		this.redirect(null, url);
+	}
+	
 	protected void redirect(String redirectPath, String parameters){
 		FacesContext fc = FacesContext.getCurrentInstance();
 		ExternalContext ec = fc.getExternalContext();
@@ -1135,6 +1142,22 @@
 	public void setSelectedPublicByAuthorTab(String selectedPublicByAuthorTab) {
 		this.selectedPublicByAuthorTab = selectedPublicByAuthorTab;
 	}
+
+	public MissingRelationsBean getMissingRelations() {
+		return missingRelations;
+	}
+
+	public void setMissingRelations(MissingRelationsBean missingRelations) {
+		this.missingRelations = missingRelations;
+	}
+
+	public String getSelectedMissingRelationsTab() {
+		return selectedMissingRelationsTab;
+	}
+
+	public void setSelectedMissingRelationsTab(String selectedMissingRelationsTab) {
+		this.selectedMissingRelationsTab = selectedMissingRelationsTab;
+	}
 	
 	
 	
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/java/de/mpiwg/itgroup/ismi/merge/MissingRelationsBean.java	Wed Jun 13 14:57:13 2018 +0200
@@ -0,0 +1,110 @@
+package de.mpiwg.itgroup.ismi.merge;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+import org.mpi.openmind.repository.services.utils.RelationFilter;
+
+import de.mpiwg.itgroup.ismi.browse.EntityRepositoryBean;
+import de.mpiwg.itgroup.ismi.browse.FullEntityRepositoryBean;
+import de.mpiwg.itgroup.ismi.entry.beans.AbstractISMIBean;
+
+public class MissingRelationsBean extends AbstractISMIBean implements Serializable{
+	
+	private static Logger logger = Logger.getLogger(MissingRelationsBean.class);
+	
+	private static final long serialVersionUID = 1L;
+		
+	private FullEntityRepositoryBean browseBean;
+	
+	
+	public MissingRelationsBean(){
+		//logger.info("AdvancedSearchBean");
+		this.reset();
+	}
+	
+	public synchronized void reset(){
+		logger.info("MissingRelationsBean.reset()");
+	    browseBean = new FullEntityRepositoryBean();
+	    browseBean.setObjectClass(TEXT);
+	    
+        logger.info("MissingRelationsBean.reset() Done.");
+	}
+	
+	
+	public void actionReset() {
+	    reset();
+	}
+	
+
+	public void actionFindMissingAuthors() {
+		logger.debug("Start findMissingAuthors...");
+		List<RelationFilter> relFilters = new ArrayList<RelationFilter>();
+		/*
+		 * find TEXT without was_created_by PERSON relation
+		 */
+	    browseBean.setObjectClass(TEXT);
+	    RelationFilter relFilter = new RelationFilter();
+	    relFilter.relObjectClass = "was_created_by";
+	    relFilter.tarObjectClass = "PERSON";
+	    relFilter.relationMissing = true;
+	    relFilters.add(relFilter);
+	    try {
+            browseBean.searchByRelations(relFilters);
+        } catch (Exception e) {
+            logger.error(e);
+        }
+		logger.debug("Done findMissingAuthors.");
+	}
+	
+	public void actionFindMissingTexts() {
+		logger.debug("Start findMissingTexts...");
+		List<RelationFilter> relFilters = new ArrayList<RelationFilter>();
+		/*
+		 * find WITNESS without is_exemplar_of TEXT relation
+		 */
+	    browseBean.setObjectClass(WITNESS);
+	    RelationFilter relFilter = new RelationFilter();
+	    relFilter.relObjectClass = "is_exemplar_of";
+	    relFilter.tarObjectClass = "TEXT";
+	    relFilter.relationMissing = true;
+	    relFilters.add(relFilter);
+	    try {
+            browseBean.searchByRelations(relFilters);
+        } catch (Exception e) {
+            logger.error(e);
+        }
+		logger.debug("Done findMissingTexts.");
+	}
+	
+	public void actionFindMissingCodices() {
+		logger.debug("Start findMissingCodices...");
+		List<RelationFilter> relFilters = new ArrayList<RelationFilter>();
+		/*
+		 * find WITNESS without is_part_of CODEX relation
+		 */
+	    browseBean.setObjectClass(WITNESS);
+	    RelationFilter relFilter = new RelationFilter();
+	    relFilter.relObjectClass = "is_part_of";
+	    relFilter.tarObjectClass = "CODEX";
+	    relFilter.relationMissing = true;
+	    relFilters.add(relFilter);
+	    try {
+            browseBean.searchByRelations(relFilters);
+        } catch (Exception e) {
+            logger.error(e);
+        }
+		logger.debug("Done findMissingCodices.");
+	}
+	
+    /**
+     * @return the browseBean
+     */
+    public EntityRepositoryBean getBrowseBean() {
+        return browseBean;
+    }
+
+
+}
--- a/src/main/java/de/mpiwg/itgroup/ismi/merge/PublicByAuthorBean.java	Fri Jun 08 18:59:49 2018 +0200
+++ b/src/main/java/de/mpiwg/itgroup/ismi/merge/PublicByAuthorBean.java	Wed Jun 13 14:57:13 2018 +0200
@@ -46,7 +46,11 @@
 	
 	public PublicByAuthorBean(){
 		//logger.info("AdvancedSearchBean");
-		this.reset();
+	    browseBean = new FullEntityRepositoryBean();
+	    browseBean.setObjectClass(PERSON);
+	    
+	    selectedPersonTexts = new ArrayList<Entity>();
+	    selectedPersonSubjectMap = new HashMap<String,List<Entity>>();
 	}
 	
 	public synchronized void reset(){
@@ -69,6 +73,7 @@
 	 * Also creates the subjectList List of subject names.
 	 */
 	protected void makeSubjectTree() {
+        logger.debug("Start makeSubjectTree...");
 	    subjectList = new ArrayList<String>();
 	    subjectEntity = new HashMap<String,Entity>();
 	    subjectParents = new HashMap<String,List<String>>();
@@ -107,6 +112,7 @@
 	        } while (++cnt < 5);
 	        subjectParents.put(subjectName, parents);
 	    }
+        logger.debug("Done makeSubjectTree.");
 	}
 
     /**
@@ -114,6 +120,7 @@
      * 
      */
     private void updateSubjectTexts() {
+        logger.debug("Start updateSubjectTexts...");
         subjectTexts = new HashMap<String,List<Entity>>();
         subjectPublicTexts = new HashMap<String,List<Entity>>();
         subjectPrivateTexts = new HashMap<String,List<Entity>>();
@@ -140,6 +147,7 @@
             subjectPublicTexts.put(subjectName, pubTextList);
             subjectPrivateTexts.put(subjectName, privTextList);
         }
+        logger.debug("Done updateSubjectTexts...");
     }	
 	
 	public void actionReset() {
@@ -418,6 +426,10 @@
      * @return the subjectParents
      */
     public Map<String, List<String>> getSubjectParents() {
+    	if (subjectParents == null) {
+    	    makeSubjectTree();
+    	    updateSubjectTexts();
+    	}
         return subjectParents;
     }
 
@@ -425,6 +437,10 @@
      * @return the subjectList
      */
     public List<String> getSubjectList() {
+    	if (subjectList == null) {
+    	    makeSubjectTree();
+    	    updateSubjectTexts();
+    	}
         return subjectList;
     }
 
@@ -446,6 +462,10 @@
      * @return the subjectTexts
      */
     public Map<String, List<Entity>> getSubjectTexts() {
+    	if (subjectTexts == null) {
+    	    makeSubjectTree();
+    	    updateSubjectTexts();
+    	}
         return subjectTexts;
     }
 
@@ -453,6 +473,10 @@
      * @return the subjectPublicTexts
      */
     public Map<String, List<Entity>> getSubjectPublicTexts() {
+    	if (subjectPublicTexts == null) {
+    	    makeSubjectTree();
+    	    updateSubjectTexts();
+    	}
         return subjectPublicTexts;
     }
 
@@ -460,6 +484,10 @@
      * @return the subjectPrivateTexts
      */
     public Map<String, List<Entity>> getSubjectPrivateTexts() {
+    	if (subjectPrivateTexts == null) {
+    	    makeSubjectTree();
+    	    updateSubjectTexts();
+    	}
         return subjectPrivateTexts;
     }
 
@@ -467,6 +495,10 @@
      * @return the subjectEntity
      */
     public Map<String, Entity> getSubjectEntity() {
+    	if (subjectEntity == null) {
+    	    makeSubjectTree();
+    	    updateSubjectTexts();
+    	}
         return subjectEntity;
     }
 
--- a/src/main/webapp/browse/entityDetails.xhtml	Fri Jun 08 18:59:49 2018 +0200
+++ b/src/main/webapp/browse/entityDetails.xhtml	Wed Jun 13 14:57:13 2018 +0200
@@ -82,13 +82,13 @@
                                 onclick="if(!confirm('Do you really want to make this entity private?')){ return; };"
                                 action="#{Session.entDetailsForm.actionChangeEntityPrivacity}"/>
                             
-                            <a4j:commandButton value="Make related entities public" 
+                            <a4j:commandButton value="Make all directly related entities public" 
                                 title="Change publication state of directly related entities"
                                 rendered="#{Session.entDetailsForm.entity.isPublic}"
                                 style="margin-right:10px;"
                                 onclick="if(!confirm('Do you really want to make all related entities public?')){ return; };"
                                 action="#{Session.entDetailsForm.actionChangeRelatedEntitiesPrivacity}"/>
-                            <a4j:commandButton value="Make related entities private" 
+                            <a4j:commandButton value="Make all directly related entities private" 
                                 title="Change publication state of directly related entities"
                                 rendered="#{!Session.entDetailsForm.entity.isPublic}"
                                 style="margin-right:10px;"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/webapp/clean/components/missingAuthor.xhtml	Wed Jun 13 14:57:13 2018 +0200
@@ -0,0 +1,148 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+	xmlns:h="http://java.sun.com/jsf/html"
+	xmlns:f="http://java.sun.com/jsf/core"
+	xmlns:ui="http://java.sun.com/jsf/facelets"
+	xmlns:a4j="http://richfaces.org/a4j"
+	xmlns:rich="http://richfaces.org/rich">
+
+<body>
+	<ui:composition>
+
+		<h:panelGrid columns="3" styleClass="displayPanel"
+			columnClasses="displayPanelColumn01,displayPanelColumn02">
+
+            <a4j:commandButton value="show texts without author"
+                actionListener="#{Session.missingRelations.actionFindMissingAuthors}"
+                render="showTextPanel" />
+            <h:outputText/>
+            <h:outputText/>
+
+		</h:panelGrid>
+
+		<h:panelGrid id="showTextPanel" columns="1"
+			style="margin-left: auto; margin-right: auto; text-align: center; width: 100%;">
+
+			<h:outputText
+				value="#{Session.missingRelations.browseBean.resultSummaryMsg}"
+				rendered="#{!empty Session.missingRelations.browseBean.resultSummaryMsg}"
+				styleClass="sumaryMsg" />
+
+			<h:column
+				rendered="#{!empty Session.missingRelations.browseBean.entities}">
+				<h:outputText value="go to page" styleClass="sumaryMsg" />
+				<h:inputText value="#{Session.missingRelations.browseBean.page}"
+					size="4" />
+				<h:commandButton value="submit"
+					actionListener="#{Session.missingRelations.browseBean.actionGoToPageAdvancedResult}" />
+				<h:outputText value=" #{Session.missingRelations.browseBean.pageMsg}"
+					rendered="#{!empty Session.missingRelations.browseBean.pageMsg}"
+					style="color:red" />
+			</h:column>
+
+
+			<h:column
+				rendered="#{!empty Session.missingRelations.browseBean.entities}">
+				<h:panelGroup>
+					<h:commandButton
+						image="/resources/css/xp/css-images/arrow-first.gif"
+						style="border:none;" title="First Page"
+						action="#{Session.missingRelations.browseBean.advancedFirst}" />
+
+					<h:commandButton image="/resources/css/xp/css-images/arrow-fr.gif"
+						style="border:none;" title="Fast Backwards"
+						action="#{Session.missingRelations.browseBean.advancedFastRewind}" />
+
+					<h:commandButton
+						image="/resources/css/xp/css-images/arrow-previous.gif"
+						style="border:none;" title="Previous Page"
+						action="#{Session.missingRelations.browseBean.advancedPrevious}" />
+
+					<h:outputText
+						value="#{Session.missingRelations.browseBean.advancedPaginator.recordStatus}"
+						styleClass="sumaryMsg" />
+
+					<h:commandButton
+						image="/resources/css/xp/css-images/arrow-next.gif"
+						style="border:none;" title="Next Page"
+						action="#{Session.missingRelations.browseBean.advancedNext}" />
+
+					<h:commandButton image="/resources/css/xp/css-images/arrow-ff.gif"
+						style="border:none;" title="Fast Forward"
+						action="#{Session.missingRelations.browseBean.advancedFastForward}" />
+
+					<h:commandButton
+						image="/resources/css/xp/css-images/arrow-last.gif"
+						style="border:none;" title="Last Page"
+						action="#{Session.missingRelations.browseBean.advancedLast}" />
+				</h:panelGroup>
+			</h:column>
+
+			<rich:dataTable id="textDataTable" rows="30"
+				value="#{Session.missingRelations.browseBean.currentEntities}"
+				rendered="#{!empty Session.missingRelations.browseBean.currentEntities}"
+				var="entity" styleClass="rsPanel" columnClasses="rsPanelColumn">
+				<h:column style="text-align:left">
+                    <f:facet name="header">Text</f:facet>
+                    <h:outputLink target="_blank"
+                        value="#{ApplicationBean1.root}/browse/entityDetails.xhtml?eid=#{entity.id}">
+                        <h:outputText
+                            value="#{entity.ownValue} [#{entity.id}]" />
+                    </h:outputLink>
+					<h:commandButton
+						action="#{Session.missingRelations.browseBean.actionEdit}"
+						rendered="#{Session.canEdit}"
+						image="/resources/css/xp/css-images/edit16.gif"
+						title="Edit this entity" />
+				</h:column>
+                <h:column style="text-align:left">
+                    <f:facet name="header">State</f:facet>
+                    <h:outputText value="#{entity.privacity}" />
+                </h:column>
+			</rich:dataTable>
+
+			<h:column
+				rendered="#{!empty Session.missingRelations.browseBean.entities}">
+				<h:panelGroup>
+					<h:commandButton
+						image="/resources/css/xp/css-images/arrow-first.gif"
+						style="border:none;" title="First Page"
+						action="#{Session.missingRelations.browseBean.advancedFirst}" />
+
+					<h:commandButton image="/resources/css/xp/css-images/arrow-fr.gif"
+						style="border:none;" title="Fast Backwards"
+						action="#{Session.missingRelations.browseBean.advancedFastRewind}" />
+
+					<h:commandButton
+						image="/resources/css/xp/css-images/arrow-previous.gif"
+						style="border:none;" title="Previous Page"
+						action="#{Session.missingRelations.browseBean.advancedPrevious}" />
+
+					<h:outputText
+						value="#{Session.missingRelations.browseBean.advancedPaginator.recordStatus}"
+						styleClass="sumaryMsg" />
+
+					<h:commandButton
+						image="/resources/css/xp/css-images/arrow-next.gif"
+						style="border:none;" title="Next Page"
+						action="#{Session.missingRelations.browseBean.advancedNext}" />
+
+					<h:commandButton image="/resources/css/xp/css-images/arrow-ff.gif"
+						style="border:none;" title="Fast Forward"
+						action="#{Session.missingRelations.browseBean.advancedFastForward}" />
+
+					<h:commandButton
+						image="/resources/css/xp/css-images/arrow-last.gif"
+						style="border:none;" title="Last Page"
+						action="#{Session.missingRelations.browseBean.advancedLast}" />
+				</h:panelGroup>
+			</h:column>
+
+
+		</h:panelGrid>
+
+
+
+	</ui:composition>
+</body>
+</html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/webapp/clean/components/missingCodex.xhtml	Wed Jun 13 14:57:13 2018 +0200
@@ -0,0 +1,148 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+	xmlns:h="http://java.sun.com/jsf/html"
+	xmlns:f="http://java.sun.com/jsf/core"
+	xmlns:ui="http://java.sun.com/jsf/facelets"
+	xmlns:a4j="http://richfaces.org/a4j"
+	xmlns:rich="http://richfaces.org/rich">
+
+<body>
+	<ui:composition>
+
+		<h:panelGrid columns="3" styleClass="displayPanel"
+			columnClasses="displayPanelColumn01,displayPanelColumn02">
+
+            <a4j:commandButton value="show witnesses without codex"
+                actionListener="#{Session.missingRelations.actionFindMissingTexts}"
+                render="showWitnessPanel2" />
+            <h:outputText/>
+            <h:outputText/>
+
+		</h:panelGrid>
+
+		<h:panelGrid id="showWitnessPanel2" columns="1"
+			style="margin-left: auto; margin-right: auto; text-align: center; width: 100%;">
+
+			<h:outputText
+				value="#{Session.missingRelations.browseBean.resultSummaryMsg}"
+				rendered="#{!empty Session.missingRelations.browseBean.resultSummaryMsg}"
+				styleClass="sumaryMsg" />
+
+			<h:column
+				rendered="#{!empty Session.missingRelations.browseBean.entities}">
+				<h:outputText value="go to page" styleClass="sumaryMsg" />
+				<h:inputText value="#{Session.missingRelations.browseBean.page}"
+					size="4" />
+				<h:commandButton value="submit"
+					actionListener="#{Session.missingRelations.browseBean.actionGoToPageAdvancedResult}" />
+				<h:outputText value=" #{Session.missingRelations.browseBean.pageMsg}"
+					rendered="#{!empty Session.missingRelations.browseBean.pageMsg}"
+					style="color:red" />
+			</h:column>
+
+
+			<h:column
+				rendered="#{!empty Session.missingRelations.browseBean.entities}">
+				<h:panelGroup>
+					<h:commandButton
+						image="/resources/css/xp/css-images/arrow-first.gif"
+						style="border:none;" title="First Page"
+						action="#{Session.missingRelations.browseBean.advancedFirst}" />
+
+					<h:commandButton image="/resources/css/xp/css-images/arrow-fr.gif"
+						style="border:none;" title="Fast Backwards"
+						action="#{Session.missingRelations.browseBean.advancedFastRewind}" />
+
+					<h:commandButton
+						image="/resources/css/xp/css-images/arrow-previous.gif"
+						style="border:none;" title="Previous Page"
+						action="#{Session.missingRelations.browseBean.advancedPrevious}" />
+
+					<h:outputText
+						value="#{Session.missingRelations.browseBean.advancedPaginator.recordStatus}"
+						styleClass="sumaryMsg" />
+
+					<h:commandButton
+						image="/resources/css/xp/css-images/arrow-next.gif"
+						style="border:none;" title="Next Page"
+						action="#{Session.missingRelations.browseBean.advancedNext}" />
+
+					<h:commandButton image="/resources/css/xp/css-images/arrow-ff.gif"
+						style="border:none;" title="Fast Forward"
+						action="#{Session.missingRelations.browseBean.advancedFastForward}" />
+
+					<h:commandButton
+						image="/resources/css/xp/css-images/arrow-last.gif"
+						style="border:none;" title="Last Page"
+						action="#{Session.missingRelations.browseBean.advancedLast}" />
+				</h:panelGroup>
+			</h:column>
+
+			<rich:dataTable id="witnessDataTable2" rows="30"
+				value="#{Session.missingRelations.browseBean.currentEntities}"
+				rendered="#{!empty Session.missingRelations.browseBean.currentEntities}"
+				var="entity" styleClass="rsPanel" columnClasses="rsPanelColumn">
+				<h:column style="text-align:left">
+                    <f:facet name="header">Witness</f:facet>
+                    <h:outputLink target="_blank"
+                        value="#{ApplicationBean1.root}/browse/entityDetails.xhtml?eid=#{entity.id}">
+                        <h:outputText
+                            value="#{entity.ownValue} [#{entity.id}]" />
+                    </h:outputLink>
+					<h:commandButton
+						action="#{Session.missingRelations.browseBean.actionEdit}"
+						rendered="#{Session.canEdit}"
+						image="/resources/css/xp/css-images/edit16.gif"
+						title="Edit this entity" />
+				</h:column>
+                <h:column style="text-align:left">
+                    <f:facet name="header">State</f:facet>
+                    <h:outputText value="#{entity.privacity}" />
+                </h:column>
+			</rich:dataTable>
+
+			<h:column
+				rendered="#{!empty Session.missingRelations.browseBean.entities}">
+				<h:panelGroup>
+					<h:commandButton
+						image="/resources/css/xp/css-images/arrow-first.gif"
+						style="border:none;" title="First Page"
+						action="#{Session.missingRelations.browseBean.advancedFirst}" />
+
+					<h:commandButton image="/resources/css/xp/css-images/arrow-fr.gif"
+						style="border:none;" title="Fast Backwards"
+						action="#{Session.missingRelations.browseBean.advancedFastRewind}" />
+
+					<h:commandButton
+						image="/resources/css/xp/css-images/arrow-previous.gif"
+						style="border:none;" title="Previous Page"
+						action="#{Session.missingRelations.browseBean.advancedPrevious}" />
+
+					<h:outputText
+						value="#{Session.missingRelations.browseBean.advancedPaginator.recordStatus}"
+						styleClass="sumaryMsg" />
+
+					<h:commandButton
+						image="/resources/css/xp/css-images/arrow-next.gif"
+						style="border:none;" title="Next Page"
+						action="#{Session.missingRelations.browseBean.advancedNext}" />
+
+					<h:commandButton image="/resources/css/xp/css-images/arrow-ff.gif"
+						style="border:none;" title="Fast Forward"
+						action="#{Session.missingRelations.browseBean.advancedFastForward}" />
+
+					<h:commandButton
+						image="/resources/css/xp/css-images/arrow-last.gif"
+						style="border:none;" title="Last Page"
+						action="#{Session.missingRelations.browseBean.advancedLast}" />
+				</h:panelGroup>
+			</h:column>
+
+
+		</h:panelGrid>
+
+
+
+	</ui:composition>
+</body>
+</html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/webapp/clean/components/missingText.xhtml	Wed Jun 13 14:57:13 2018 +0200
@@ -0,0 +1,148 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+	xmlns:h="http://java.sun.com/jsf/html"
+	xmlns:f="http://java.sun.com/jsf/core"
+	xmlns:ui="http://java.sun.com/jsf/facelets"
+	xmlns:a4j="http://richfaces.org/a4j"
+	xmlns:rich="http://richfaces.org/rich">
+
+<body>
+	<ui:composition>
+
+		<h:panelGrid columns="3" styleClass="displayPanel"
+			columnClasses="displayPanelColumn01,displayPanelColumn02">
+
+            <a4j:commandButton value="show witnesses without text"
+                actionListener="#{Session.missingRelations.actionFindMissingTexts}"
+                render="showWitnessPanel" />
+            <h:outputText/>
+            <h:outputText/>
+
+		</h:panelGrid>
+
+		<h:panelGrid id="showWitnessPanel" columns="1"
+			style="margin-left: auto; margin-right: auto; text-align: center; width: 100%;">
+
+			<h:outputText
+				value="#{Session.missingRelations.browseBean.resultSummaryMsg}"
+				rendered="#{!empty Session.missingRelations.browseBean.resultSummaryMsg}"
+				styleClass="sumaryMsg" />
+
+			<h:column
+				rendered="#{!empty Session.missingRelations.browseBean.entities}">
+				<h:outputText value="go to page" styleClass="sumaryMsg" />
+				<h:inputText value="#{Session.missingRelations.browseBean.page}"
+					size="4" />
+				<h:commandButton value="submit"
+					actionListener="#{Session.missingRelations.browseBean.actionGoToPageAdvancedResult}" />
+				<h:outputText value=" #{Session.missingRelations.browseBean.pageMsg}"
+					rendered="#{!empty Session.missingRelations.browseBean.pageMsg}"
+					style="color:red" />
+			</h:column>
+
+
+			<h:column
+				rendered="#{!empty Session.missingRelations.browseBean.entities}">
+				<h:panelGroup>
+					<h:commandButton
+						image="/resources/css/xp/css-images/arrow-first.gif"
+						style="border:none;" title="First Page"
+						action="#{Session.missingRelations.browseBean.advancedFirst}" />
+
+					<h:commandButton image="/resources/css/xp/css-images/arrow-fr.gif"
+						style="border:none;" title="Fast Backwards"
+						action="#{Session.missingRelations.browseBean.advancedFastRewind}" />
+
+					<h:commandButton
+						image="/resources/css/xp/css-images/arrow-previous.gif"
+						style="border:none;" title="Previous Page"
+						action="#{Session.missingRelations.browseBean.advancedPrevious}" />
+
+					<h:outputText
+						value="#{Session.missingRelations.browseBean.advancedPaginator.recordStatus}"
+						styleClass="sumaryMsg" />
+
+					<h:commandButton
+						image="/resources/css/xp/css-images/arrow-next.gif"
+						style="border:none;" title="Next Page"
+						action="#{Session.missingRelations.browseBean.advancedNext}" />
+
+					<h:commandButton image="/resources/css/xp/css-images/arrow-ff.gif"
+						style="border:none;" title="Fast Forward"
+						action="#{Session.missingRelations.browseBean.advancedFastForward}" />
+
+					<h:commandButton
+						image="/resources/css/xp/css-images/arrow-last.gif"
+						style="border:none;" title="Last Page"
+						action="#{Session.missingRelations.browseBean.advancedLast}" />
+				</h:panelGroup>
+			</h:column>
+
+			<rich:dataTable id="witnessDataTable" rows="30"
+				value="#{Session.missingRelations.browseBean.currentEntities}"
+				rendered="#{!empty Session.missingRelations.browseBean.currentEntities}"
+				var="entity" styleClass="rsPanel" columnClasses="rsPanelColumn">
+				<h:column style="text-align:left">
+                    <f:facet name="header">Witness</f:facet>
+                    <h:outputLink target="_blank"
+                        value="#{ApplicationBean1.root}/browse/entityDetails.xhtml?eid=#{entity.id}">
+                        <h:outputText
+                            value="#{entity.ownValue} [#{entity.id}]" />
+                    </h:outputLink>
+					<h:commandButton
+						action="#{Session.missingRelations.browseBean.actionEdit}"
+						rendered="#{Session.canEdit}"
+						image="/resources/css/xp/css-images/edit16.gif"
+						title="Edit this entity" />
+				</h:column>
+                <h:column style="text-align:left">
+                    <f:facet name="header">State</f:facet>
+                    <h:outputText value="#{entity.privacity}" />
+                </h:column>
+			</rich:dataTable>
+
+			<h:column
+				rendered="#{!empty Session.missingRelations.browseBean.entities}">
+				<h:panelGroup>
+					<h:commandButton
+						image="/resources/css/xp/css-images/arrow-first.gif"
+						style="border:none;" title="First Page"
+						action="#{Session.missingRelations.browseBean.advancedFirst}" />
+
+					<h:commandButton image="/resources/css/xp/css-images/arrow-fr.gif"
+						style="border:none;" title="Fast Backwards"
+						action="#{Session.missingRelations.browseBean.advancedFastRewind}" />
+
+					<h:commandButton
+						image="/resources/css/xp/css-images/arrow-previous.gif"
+						style="border:none;" title="Previous Page"
+						action="#{Session.missingRelations.browseBean.advancedPrevious}" />
+
+					<h:outputText
+						value="#{Session.missingRelations.browseBean.advancedPaginator.recordStatus}"
+						styleClass="sumaryMsg" />
+
+					<h:commandButton
+						image="/resources/css/xp/css-images/arrow-next.gif"
+						style="border:none;" title="Next Page"
+						action="#{Session.missingRelations.browseBean.advancedNext}" />
+
+					<h:commandButton image="/resources/css/xp/css-images/arrow-ff.gif"
+						style="border:none;" title="Fast Forward"
+						action="#{Session.missingRelations.browseBean.advancedFastForward}" />
+
+					<h:commandButton
+						image="/resources/css/xp/css-images/arrow-last.gif"
+						style="border:none;" title="Last Page"
+						action="#{Session.missingRelations.browseBean.advancedLast}" />
+				</h:panelGroup>
+			</h:column>
+
+
+		</h:panelGrid>
+
+
+
+	</ui:composition>
+</body>
+</html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/webapp/clean/missingRelations.xhtml	Wed Jun 13 14:57:13 2018 +0200
@@ -0,0 +1,51 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+	xmlns:h="http://java.sun.com/jsf/html"
+	xmlns:f="http://java.sun.com/jsf/core"
+	xmlns:ui="http://java.sun.com/jsf/facelets"
+	xmlns:a4j="http://richfaces.org/a4j"
+	xmlns:rich="http://richfaces.org/rich">
+
+<body>
+	<ui:composition template="/templates/privateTemplate.xhtml">
+
+		<h:outputStylesheet name="/css/ismi-db/repository.css" />
+
+		<ui:define name="privateContent">
+
+			<div id="pageTitle">
+				<h:outputText value="Find Missing Relations" />
+			</div>
+
+			<h:panelGrid columns="1" styleClass="mainPanel">
+
+				<rich:tabPanel id="tabPanel"
+					itemChangeListener="#{Session.listenerMissingRelationsTabChange}"
+					activeItem="#{Session.selectedMissingRelationsTab}">
+
+					<rich:tab header="Missing Author" name="aut">
+						<ui:include src="components/missingAuthor.xhtml" />
+					</rich:tab>
+
+					<rich:tab header="Missing Text" name="txt">
+						<ui:include src="components/missingText.xhtml" />
+					</rich:tab>
+
+					<rich:tab header="Missing Codex" name="cod">
+						<ui:include src="components/missingCodex.xhtml" />
+					</rich:tab>
+				</rich:tabPanel>
+
+				<a4j:commandButton value="reload all texts"
+					actionListener="#{Session.missingRelations.actionReset}"
+					render="mainPanel" />
+
+
+			</h:panelGrid>
+
+
+		</ui:define>
+	</ui:composition>
+
+</body>
+</html>
\ No newline at end of file
--- a/src/main/webapp/templates/main_template.xhtml	Fri Jun 08 18:59:49 2018 +0200
+++ b/src/main/webapp/templates/main_template.xhtml	Wed Jun 13 14:57:13 2018 +0200
@@ -177,6 +177,10 @@
                 <rich:menuItem label="Make Texts Public"
                     onclick="document.location.href='#{ApplicationBean1.root}/clean/makeTextsPublic.xhtml'">
                 </rich:menuItem>
+
+                <rich:menuItem label="Find Missing Relations"
+                    onclick="document.location.href='#{ApplicationBean1.root}/clean/missingRelations.xhtml'">
+                </rich:menuItem>
 			</rich:dropDownMenu>
 
 			<h:outputLink  rendered="#{Session.canCreate}"