diff --git a/src/main/java/no/bioforsk/vips/logic/controller/servlet/ForecastConfigurationController.java b/src/main/java/no/bioforsk/vips/logic/controller/servlet/ForecastConfigurationController.java
index e1139b0a3a55ce7bd66ff4896969cff828f4f09b..8f287be2f1bd618599a874d1866dbd3cc6403c1b 100644
--- a/src/main/java/no/bioforsk/vips/logic/controller/servlet/ForecastConfigurationController.java
+++ b/src/main/java/no/bioforsk/vips/logic/controller/servlet/ForecastConfigurationController.java
@@ -18,6 +18,7 @@ import no.bioforsk.vips.logic.util.SessionControllerGetter;
 import no.bioforsk.vips.util.ServletUtil;
 import no.bioforsk.web.forms.FormField;
 import no.bioforsk.web.forms.FormValidation;
+import no.bioforsk.web.forms.FormValidationException;
 import no.bioforsk.web.forms.FormValidator;
 
 /**
@@ -182,9 +183,16 @@ public class ForecastConfigurationController extends HttpServlet {
                         }
                     }
                 }
-                catch(NullPointerException | NumberFormatException ex)
+                catch(NullPointerException | NumberFormatException | FormValidationException ex)
                 {
-                    response.sendError(500, "Invalid forecast configurationId " + request.getParameter("forecastConfigurationId"));
+                    if(ex instanceof NumberFormatException)
+                    {
+                        response.sendError(500, "Invalid forecast configurationId " + request.getParameter("forecastConfigurationId"));
+                    }
+                    else
+                    {
+                        response.sendError(500, ex.getMessage());
+                    }
                 }
             }
             else
diff --git a/src/main/java/no/bioforsk/vips/logic/controller/servlet/OrganismController.java b/src/main/java/no/bioforsk/vips/logic/controller/servlet/OrganismController.java
new file mode 100644
index 0000000000000000000000000000000000000000..9c2a1b1bb1665a67f7b1ddb403fa5007803d604b
--- /dev/null
+++ b/src/main/java/no/bioforsk/vips/logic/controller/servlet/OrganismController.java
@@ -0,0 +1,321 @@
+/*
+ * Copyright (c) 2014 Bioforsk <http://www.bioforsk.no/>. 
+ * 
+ * This file is part of VIPSLogic.
+ * VIPSLogic is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * VIPSLogic is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with VIPSLogic.  If not, see <http://www.gnu.org/licenses/>.
+ * 
+ */
+
+package no.bioforsk.vips.logic.controller.servlet;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import no.bioforsk.vips.logic.controller.session.UserBean;
+import no.bioforsk.vips.logic.entity.Organism;
+import no.bioforsk.vips.logic.entity.OrganismExternalResource;
+import no.bioforsk.vips.logic.entity.OrganismExternalResourcePK;
+import no.bioforsk.vips.logic.entity.VipsLogicUser;
+import no.bioforsk.vips.logic.i18n.SessionLocaleUtil;
+import no.bioforsk.vips.logic.util.SessionControllerGetter;
+import no.bioforsk.vips.util.ServletUtil;
+import no.bioforsk.web.forms.FormField;
+import no.bioforsk.web.forms.FormValidation;
+import no.bioforsk.web.forms.FormValidationException;
+import no.bioforsk.web.forms.FormValidator;
+
+/**
+ * @copyright 2014 <a href="http://www.bioforsk.no/">Bioforsk</a>
+ * @author Tor-Einar Skog <tor-einar.skog@bioforsk.no>
+ */
+public class OrganismController extends HttpServlet {
+   
+    @PersistenceContext(unitName="VIPSLogic-PU")
+    EntityManager em;
+
+    /** 
+     * Processes requests for both HTTP <code>GET</code> and <code>POST</code> methods.
+     * @param request servlet request
+     * @param response servlet response
+     * @throws ServletException if a servlet-specific error occurs
+     * @throws IOException if an I/O error occurs
+     */
+    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
+    throws ServletException, IOException {
+        response.setContentType("text/html;charset=UTF-8");
+        String action = request.getParameter("action");
+        VipsLogicUser user = (VipsLogicUser) request.getSession().getAttribute("user");
+        UserBean userBean = SessionControllerGetter.getUserBean();
+        
+        // Default: View top organisms
+        // for everyone
+        if(action == null || action.equals("listChildOrganisms"))
+        {
+            try
+            {
+                Organism organism;
+                if(     action == null 
+                        || (request.getParameter("organismId") != null && (request.getParameter("organismId").isEmpty() || request.getParameter("organismId").equals("null")))
+                )
+                {
+                    organism = new Organism();
+                    organism.setChildOrganisms(SessionControllerGetter.getOrganismBean().getTopLevelOrganisms());
+                }
+                else
+                {
+                    Integer organismId = Integer.valueOf(request.getParameter("organismId"));
+                    organism = SessionControllerGetter.getOrganismBean().getOrganismAndChildrenTwoLevels(organismId);
+                }
+                request.setAttribute("organism", organism);
+                // Check if any of the child organisms has trade name
+                Boolean tradeNamePresent = Boolean.FALSE;
+                for(Organism child:organism.getChildOrganisms())
+                {
+                    if(child.getTradeName() != null && !child.getTradeName().isEmpty())
+                    {
+                        tradeNamePresent = Boolean.TRUE;
+                    }
+                }
+                request.setAttribute("tradeNamePresent", tradeNamePresent);
+                // Check if message has been passed on
+                request.setAttribute("messageKey", request.getParameter("messageKey"));
+                // Delegate to template
+                request.getRequestDispatcher("/organismList.ftl").forward(request, response);
+            }
+            catch(NullPointerException | NumberFormatException ex)
+            {
+                if(ex instanceof NumberFormatException)
+                {
+                    response.sendError(500, "Invalid organism id " + request.getParameter("organismId"));
+                }
+                else
+                {
+                    response.sendError(500, ex.getMessage());
+                }
+            }
+        }
+        else if(action.equals("viewOrganism"))
+        {
+            try
+            {
+                
+                Integer organismId = Integer.valueOf(request.getParameter("organismId"));
+                Organism organism = em.find(Organism.class, organismId);
+                request.setAttribute("organism", organism);
+                // Hierarchy categories
+                request.setAttribute("hierarchyCategories", SessionControllerGetter.getOrganismBean().getHierarchyCategoryNames(SessionLocaleUtil.getCurrentLocale(request)));
+                request.setAttribute("parentOrganism", organism.getParentOrganismId() != null ? em.find(Organism.class, organism.getParentOrganismId()) : null);
+                request.getRequestDispatcher("/organismDetails.ftl").forward(request, response);
+            }
+            catch(NullPointerException | NumberFormatException ex)
+            {
+                response.sendError(500, "Invalid organism id " + request.getParameter("organismId"));
+            }
+        }
+        else if(action.equals("editOrganismForm"))
+        {
+            try
+            {
+                
+                Integer organismId = Integer.valueOf(request.getParameter("organismId"));
+                Organism organism = em.find(Organism.class, organismId);
+                request.setAttribute("organism", organism);
+                // All organisms used for parent organism list
+                List<Organism> allOrganisms = em.createNamedQuery("Organism.findAll").getResultList();
+                request.setAttribute("allOrganisms", allOrganisms);
+                // Hierarchy categories
+                request.setAttribute("hierarchyCategories", SessionControllerGetter.getOrganismBean().getHierarchyCategoryNames(SessionLocaleUtil.getCurrentLocale(request)));
+                // Finding all external resources where entry is missing
+                request.setAttribute("unreferencedExternalResources", SessionControllerGetter.getOrganismBean().getUnusedExternalResourcesForOrganism(organism));
+                request.getRequestDispatcher("/organismForm.ftl").forward(request, response);
+            }
+            catch(NullPointerException | NumberFormatException ex)
+            {
+                response.sendError(500, "Invalid organism id " + request.getParameter("organismId"));
+            }
+        }
+        else if(action.equals("newOrganismForm"))
+        {
+            try
+            {
+                Integer parentOrganismId = null;
+                if(request.getParameter("parentOrganismId") != null && !request.getParameter("parentOrganismId").equals("null") && ! request.getParameter("parentOrganismId").isEmpty())
+                {
+                    parentOrganismId = Integer.valueOf(request.getParameter("parentOrganismId"));
+                }
+                request.setAttribute("parentOrganismId", parentOrganismId);
+                Organism organism = new Organism();
+                request.setAttribute("organism", organism);
+                request.setAttribute("allOrganisms", em.createNamedQuery("Organism.findAll").getResultList());
+                // Hierarchy categories
+                request.setAttribute("hierarchyCategories", SessionControllerGetter.getOrganismBean().getHierarchyCategoryNames(SessionLocaleUtil.getCurrentLocale(request)));
+                
+                // Finding all external resources where entry is missing
+                request.setAttribute("unreferencedExternalResources", SessionControllerGetter.getOrganismBean().getUnusedExternalResourcesForOrganism(organism));
+
+                request.getRequestDispatcher("/organismForm.ftl").forward(request, response);
+            }
+            catch(NullPointerException | NumberFormatException ex)
+            {
+                response.sendError(500, "Invalid organism id " + request.getParameter("organismId"));
+            }
+        }
+        else if(action.equals("organismFormSubmit"))
+        {
+            try
+            {
+                Integer organismId = Integer.valueOf(request.getParameter("organismId"));
+                Organism organism = organismId > 0 ? em.find(Organism.class, organismId) : new Organism();
+                FormValidation formValidation = FormValidator.validateForm("organismForm",request,getServletContext());
+                if(formValidation.isValid())
+                {
+                    organism.setLatinName(formValidation.getFormField("latinName").getWebValue());
+                    organism.setTradeName(formValidation.getFormField("tradeName").getWebValue());
+                    organism.setHierarchyCategoryId(formValidation.getFormField("hierarchyCategoryId").getValueAsInteger());
+                    organism.setParentOrganismId(formValidation.getFormField("parentOrganismId").getValueAsInteger());
+                    organism = SessionControllerGetter.getOrganismBean().storeOrganism(organism);
+                    
+                    // Adding local name
+                    if(!formValidation.getFormField("localName").getWebValue().isEmpty())
+                    {
+                        SessionControllerGetter.getOrganismBean().storeOrganismLocalName(organism, formValidation.getFormField("localName").getWebValue(), SessionLocaleUtil.getCurrentLocale(request));
+                    }
+                    //System.out.println(formValidation.getFormFields().toString());
+                    Map<String, FormField> externalResourceIdentifiers = formValidation.getMultipleMapFormFields().get("externalResourceIdentifier");
+                    for(String key:externalResourceIdentifiers.keySet())
+                    {
+                        FormField identifierField = externalResourceIdentifiers.get(key);
+                        if(identifierField.getWebValue() == null || identifierField.getWebValue().isEmpty())
+                        {
+                            continue;
+                        }
+                        Integer externalResourceId = Integer.valueOf(key);
+                        OrganismExternalResource organismExternalResource = new OrganismExternalResource();
+                        OrganismExternalResourcePK pk = new OrganismExternalResourcePK(organism.getOrganismId(), externalResourceId);
+                        organismExternalResource.setOrganismExternalResourcePK(pk);
+                        organismExternalResource.setResourceIdentifier(identifierField.getWebValue());
+                        SessionControllerGetter.getOrganismBean().storeOrganismExternalResource(organismExternalResource);
+                    }
+                    // Need to refresh organism after storing the external resources
+                    Organism refreshedOrganism = em.find(Organism.class, organism.getOrganismId());
+                    request.setAttribute("organism", refreshedOrganism);
+                    request.setAttribute("allOrganisms", em.createNamedQuery("Organism.findAll").getResultList());
+                    // Hierarchy categories
+                    request.setAttribute("hierarchyCategories", SessionControllerGetter.getOrganismBean().getHierarchyCategoryNames(SessionLocaleUtil.getCurrentLocale(request)));
+                    // Finding all external resources where entry is missing
+                    request.setAttribute("unreferencedExternalResources", SessionControllerGetter.getOrganismBean().getUnusedExternalResourcesForOrganism(refreshedOrganism));
+                    request.setAttribute("messageKey", organismId > 0 ? "organismUpdated" : "organismRegistered");
+                    request.getRequestDispatcher("/organismForm.ftl").forward(request, response);
+                }
+                else
+                {
+                    request.setAttribute("formValidation", formValidation);
+                    request.setAttribute("organism", organism);
+                    // Finding all external resources where entry is missing
+                    request.setAttribute("unreferencedExternalResources", SessionControllerGetter.getOrganismBean().getUnusedExternalResourcesForOrganism(organism));
+                    request.getRequestDispatcher("/organismForm.ftl").forward(request, response);
+                }
+            }
+            catch(NullPointerException | NumberFormatException | FormValidationException ex)
+            {
+                if(ex instanceof NumberFormatException)
+                {
+                    response.sendError(500, "Invalid organism id " + request.getParameter("organismId"));
+                }
+                else if(ex instanceof FormValidationException)
+                {
+                    response.sendError(500, "Form validation exception: " + ex.getMessage());
+                }
+                else
+                {
+                    ex.printStackTrace();
+                    response.sendError(500, ex.getMessage());
+                }
+            }
+        }
+        else if(action.equals("deleteOrganism"))
+        {
+            try
+            {
+                Integer organismId = Integer.valueOf(request.getParameter("organismId"));
+                Organism organism = em.find(Organism.class, organismId);
+                String parentOrganismId = organism.getParentOrganismId() != null ? String.valueOf(organism.getParentOrganismId()) : "null";
+                if(SessionControllerGetter.getOrganismBean().deleteOrganism(organismId))
+                {
+                    // Route back to where we were with confirmation message
+                    response.sendRedirect(new StringBuilder("http://").append(ServletUtil.getServerName(request)).append("/organism?action=listChildOrganisms&organismId=").append(parentOrganismId).append("&messageKey=organismDeleted").toString());
+                }
+                else
+                {
+                    // Route back to where we were with error message
+                    response.sendRedirect("/organism?action=listChildOrganisms&organismId=" + parentOrganismId + "&errorMessageKey=organismNotDeleted");
+                }
+            }
+            catch(NullPointerException | NumberFormatException ex)
+            {
+                if(ex instanceof NumberFormatException)
+                {
+                    response.sendError(500, "Invalid organism id " + request.getParameter("organismId"));
+                }
+                else
+                {
+                    response.sendError(500, ex.getMessage());
+                }
+            }
+        }
+    } 
+
+    // <editor-fold defaultstate="collapsed" desc="HttpServlet methods. Click on the + sign on the left to edit the code.">
+    /** 
+     * Handles the HTTP <code>GET</code> method.
+     * @param request servlet request
+     * @param response servlet response
+     * @throws ServletException if a servlet-specific error occurs
+     * @throws IOException if an I/O error occurs
+     */
+    @Override
+    protected void doGet(HttpServletRequest request, HttpServletResponse response)
+    throws ServletException, IOException {
+        processRequest(request, response);
+    } 
+
+    /** 
+     * Handles the HTTP <code>POST</code> method.
+     * @param request servlet request
+     * @param response servlet response
+     * @throws ServletException if a servlet-specific error occurs
+     * @throws IOException if an I/O error occurs
+     */
+    @Override
+    protected void doPost(HttpServletRequest request, HttpServletResponse response)
+    throws ServletException, IOException {
+        processRequest(request, response);
+    }
+
+    /** 
+     * Returns a short description of the servlet.
+     * @return a String containing servlet description
+     */
+    @Override
+    public String getServletInfo() {
+        return "Short description";
+    }// </editor-fold>
+
+}
diff --git a/src/main/java/no/bioforsk/vips/logic/controller/servlet/SchedulingController.java b/src/main/java/no/bioforsk/vips/logic/controller/servlet/SchedulingController.java
index faa1b0fe0bc01d94782bd087635932a141a741b2..b26e8741bef98a72345b0cf9e785b00d7a36eb7a 100644
--- a/src/main/java/no/bioforsk/vips/logic/controller/servlet/SchedulingController.java
+++ b/src/main/java/no/bioforsk/vips/logic/controller/servlet/SchedulingController.java
@@ -26,6 +26,7 @@ import no.bioforsk.vips.logic.scheduling.VipsLogicTaskFactory;
 import no.bioforsk.vips.logic.util.SessionControllerGetter;
 import no.bioforsk.vips.util.ServletUtil;
 import no.bioforsk.web.forms.FormValidation;
+import no.bioforsk.web.forms.FormValidationException;
 import no.bioforsk.web.forms.FormValidator;
 import no.bioforsk.web.forms.HTMLFormGenerator;
 
@@ -63,32 +64,39 @@ public class SchedulingController extends HttpServlet {
         {
             if(userBean.authorizeUser(user, VipsLogicRole.ORGANIZATION_ADMINISTRATOR, VipsLogicRole.SUPERUSER))
             {
-                FormValidation formValidation = FormValidator.validateForm("taskHistoryDateForm", request, getServletContext());
-                Date taskHistoryDate = new Date();
-                if(formValidation.isValid())
+                try
                 {
-                    if(! formValidation.getFormField("taskHistoryDate").isEmpty())
+                    FormValidation formValidation = FormValidator.validateForm("taskHistoryDateForm", request, getServletContext());
+                    Date taskHistoryDate = new Date();
+                    if(formValidation.isValid())
+                    {
+                        if(! formValidation.getFormField("taskHistoryDate").isEmpty())
+                        {
+                            taskHistoryDate = formValidation.getFormField("taskHistoryDate").getValueAsDate();
+                        }
+                    }
+                    else
                     {
-                        taskHistoryDate = formValidation.getFormField("taskHistoryDate").getValueAsDate();
+                        request.setAttribute("formValidation", formValidation);
                     }
+
+
+                    TaskExecutor[] taskExecutors = schedulingBean.getRunningTasks();
+                    request.setAttribute("taskExecutors", taskExecutors);
+                    request.setAttribute("schedulingStarted", Boolean.valueOf(schedulingBean.getSystemScheduler().isStarted()));
+                    request.setAttribute("orderedScheduledTasks", schedulingBean.getOrderedScheduledTasks());
+                    request.setAttribute("taskHistoryDate",taskHistoryDate);
+                    request.setAttribute("taskHistory",schedulingBean.getTaskHistory(taskHistoryDate));
+                    request.setAttribute("allTasksMap", VipsLogicTaskFactory.getAllVipsLogicTasksMap());
+                    // If this is a redirect from a controller, with a message to be passed on
+                    request.setAttribute("messageKey", request.getParameter("messageKey"));
+                    request.setAttribute("message", request.getParameter("message"));
+                    request.getRequestDispatcher("/schedulingOverview.ftl").forward(request, response);
                 }
-                else
+                catch(FormValidationException ex)
                 {
-                    request.setAttribute("formValidation", formValidation);
+                    response.sendError(500, ex.getMessage());
                 }
-                
-                
-                TaskExecutor[] taskExecutors = schedulingBean.getRunningTasks();
-                request.setAttribute("taskExecutors", taskExecutors);
-                request.setAttribute("schedulingStarted", Boolean.valueOf(schedulingBean.getSystemScheduler().isStarted()));
-                request.setAttribute("orderedScheduledTasks", schedulingBean.getOrderedScheduledTasks());
-                request.setAttribute("taskHistoryDate",taskHistoryDate);
-                request.setAttribute("taskHistory",schedulingBean.getTaskHistory(taskHistoryDate));
-                request.setAttribute("allTasksMap", VipsLogicTaskFactory.getAllVipsLogicTasksMap());
-                // If this is a redirect from a controller, with a message to be passed on
-                request.setAttribute("messageKey", request.getParameter("messageKey"));
-                request.setAttribute("message", request.getParameter("message"));
-                request.getRequestDispatcher("/schedulingOverview.ftl").forward(request, response);
             }
             else
             {
@@ -192,7 +200,7 @@ public class SchedulingController extends HttpServlet {
                         request.getRequestDispatcher("/runTaskManuallyForm.ftl").forward(request, response);
                     }
                 }
-                catch(NullPointerException | NumberFormatException ex)
+                catch(NullPointerException | NumberFormatException | FormValidationException ex)
                 {
                     response.sendError(500, "ERROR: " + ex.getMessage());
                 }
diff --git a/src/main/java/no/bioforsk/vips/logic/controller/servlet/UserController.java b/src/main/java/no/bioforsk/vips/logic/controller/servlet/UserController.java
index 6910a8e0b02d633e307396daf6d24fdf57bc0d12..1837943f41a7edfa9b4e67d8f739256c1c64719b 100644
--- a/src/main/java/no/bioforsk/vips/logic/controller/servlet/UserController.java
+++ b/src/main/java/no/bioforsk/vips/logic/controller/servlet/UserController.java
@@ -44,6 +44,7 @@ import no.bioforsk.vips.logic.util.SessionControllerGetter;
 import no.bioforsk.vips.util.ServletUtil;
 import no.bioforsk.web.forms.FormField;
 import no.bioforsk.web.forms.FormValidation;
+import no.bioforsk.web.forms.FormValidationException;
 import no.bioforsk.web.forms.FormValidator;
 
 /**
@@ -195,9 +196,8 @@ public class UserController extends HttpServlet {
                         
                     }
                 }
-                catch(NullPointerException | NumberFormatException ex)
+                catch(NullPointerException | NumberFormatException | FormValidationException ex)
                 {
-                    ex.printStackTrace();
                     response.sendError(500, ex.getMessage());
                 }
             }
@@ -289,55 +289,62 @@ public class UserController extends HttpServlet {
             }
 
             // Standard form validation
-            FormValidation formValidation = FormValidator.validateForm(
-                                                "userRegistrationFormType" + userAuthenticationType.getUserAuthenticationTypeId(),
-                                                request,getServletContext()
-                                            );
-            // Extra check: That user's email is not already in use
-            FormField emailField = formValidation.getFormField("email");
-            if(userBean.getUserByEmail(emailField.getWebValue()) != null)
+            try
             {
-                emailField.setValid(false);
-                emailField.setValidationMessage(SessionLocaleUtil.getI18nText(request, "emailAddressIsAlreadyInUse"));
-            }
-
-            if(formValidation.isValid())
-            {
-
-                // Setting authentication info first
-                auth.setUserAuthenticationType(userAuthenticationType);
-                if(userAuthenticationType.getUserAuthenticationTypeId().equals(UserAuthenticationType.TYPE_PASSWORD))
+                FormValidation formValidation = FormValidator.validateForm(
+                                                    "userRegistrationFormType" + userAuthenticationType.getUserAuthenticationTypeId(),
+                                                    request,getServletContext()
+                                                );
+                // Extra check: That user's email is not already in use
+                FormField emailField = formValidation.getFormField("email");
+                if(userBean.getUserByEmail(emailField.getWebValue()) != null)
                 {
-                    auth.setUsername(formValidation.getFormField("username").getWebValue());
-                    auth.setPassword(userBean.getMD5EncryptedString(formValidation.getFormField("password").getWebValue()));
+                    emailField.setValid(false);
+                    emailField.setValidationMessage(SessionLocaleUtil.getI18nText(request, "emailAddressIsAlreadyInUse"));
                 }
-                // For OpenId and GoogleId
-                else
+
+                if(formValidation.isValid())
                 {
-                    auth.setUsername((String)request.getSession().getAttribute("openId"));
+
+                    // Setting authentication info first
+                    auth.setUserAuthenticationType(userAuthenticationType);
+                    if(userAuthenticationType.getUserAuthenticationTypeId().equals(UserAuthenticationType.TYPE_PASSWORD))
+                    {
+                        auth.setUsername(formValidation.getFormField("username").getWebValue());
+                        auth.setPassword(userBean.getMD5EncryptedString(formValidation.getFormField("password").getWebValue()));
+                    }
+                    // For OpenId and GoogleId
+                    else
+                    {
+                        auth.setUsername((String)request.getSession().getAttribute("openId"));
+                    }
+                    // Adding user info
+                    newUser.setEmail(formValidation.getFormField("email").getWebValue());
+                    newUser.setFirstName(formValidation.getFormField("firstName").getWebValue());
+                    newUser.setLastName(formValidation.getFormField("lastName").getWebValue());
+                    Organization organization = em.find(Organization.class, formValidation.getFormField("organizationId").getValueAsInteger());
+                    newUser.setOrganizationId(organization);
+                    newUser.setApprovalApplication(formValidation.getFormField("approvalApplication").getWebValue());
+                    newUser.setUserStatusId(Globals.USER_STATUS_AWAITING_EMAIL_VERIFICATION);
+                    userBean.storeUserFirstTime(newUser, auth);
+                    // Send email about user verification
+                    if(newUser.getUserStatusId().equals(Globals.USER_STATUS_AWAITING_EMAIL_VERIFICATION))
+                    {
+                        userBean.sendUserEmailVerification(newUser, SessionLocaleUtil.getI18nBundle(request), ServletUtil.getServerName(request));
+                    }
+                    response.sendRedirect(new StringBuilder("http://").append(ServletUtil.getServerName(request)).append("/user?action=registerNewUserFormReceipt").toString());
                 }
-                // Adding user info
-                newUser.setEmail(formValidation.getFormField("email").getWebValue());
-                newUser.setFirstName(formValidation.getFormField("firstName").getWebValue());
-                newUser.setLastName(formValidation.getFormField("lastName").getWebValue());
-                Organization organization = em.find(Organization.class, formValidation.getFormField("organizationId").getValueAsInteger());
-                newUser.setOrganizationId(organization);
-                newUser.setApprovalApplication(formValidation.getFormField("approvalApplication").getWebValue());
-                newUser.setUserStatusId(Globals.USER_STATUS_AWAITING_EMAIL_VERIFICATION);
-                userBean.storeUserFirstTime(newUser, auth);
-                // Send email about user verification
-                if(newUser.getUserStatusId().equals(Globals.USER_STATUS_AWAITING_EMAIL_VERIFICATION))
+                else
                 {
-                    userBean.sendUserEmailVerification(newUser, SessionLocaleUtil.getI18nBundle(request), ServletUtil.getServerName(request));
+                    request.setAttribute("formValidation", formValidation);
+                    request.setAttribute("organizations", em.createNamedQuery("Organization.findAll").getResultList());
+                    request.setAttribute("userAuthenticationTypeId", userAuthenticationType.getUserAuthenticationTypeId());
+                    request.getRequestDispatcher("/userRegistrationForm.ftl").forward(request, response);
                 }
-                response.sendRedirect(new StringBuilder("http://").append(ServletUtil.getServerName(request)).append("/user?action=registerNewUserFormReceipt").toString());
             }
-            else
+            catch(FormValidationException ex)
             {
-                request.setAttribute("formValidation", formValidation);
-                request.setAttribute("organizations", em.createNamedQuery("Organization.findAll").getResultList());
-                request.setAttribute("userAuthenticationTypeId", userAuthenticationType.getUserAuthenticationTypeId());
-                request.getRequestDispatcher("/userRegistrationForm.ftl").forward(request, response);
+                response.sendError(500, ex.getMessage());
             }
         }
         else if(action.equals("registerNewUserFormReceipt"))
diff --git a/src/main/java/no/bioforsk/vips/logic/controller/session/OrganismBean.java b/src/main/java/no/bioforsk/vips/logic/controller/session/OrganismBean.java
new file mode 100644
index 0000000000000000000000000000000000000000..69bc10d427be8659645ba0235f3b89078f78221f
--- /dev/null
+++ b/src/main/java/no/bioforsk/vips/logic/controller/session/OrganismBean.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2014 Bioforsk <http://www.bioforsk.no/>. 
+ * 
+ * This file is part of VIPSLogic.
+ * VIPSLogic is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * VIPSLogic is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with VIPSLogic.  If not, see <http://www.gnu.org/licenses/>.
+ * 
+ */
+
+package no.bioforsk.vips.logic.controller.session;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import javax.ejb.Stateless;
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+import javax.persistence.Query;
+import no.bioforsk.vips.logic.entity.ExternalResource;
+import no.bioforsk.vips.logic.entity.ExternalResourceType;
+import no.bioforsk.vips.logic.entity.HierarchyCategory;
+import no.bioforsk.vips.logic.entity.Organism;
+import no.bioforsk.vips.logic.entity.OrganismExternalResource;
+import no.bioforsk.vips.logic.entity.OrganismLocale;
+import no.bioforsk.vips.logic.entity.OrganismLocalePK;
+import no.bioforsk.vips.logic.util.HierarchyCategoryLocaleNames;
+
+/**
+ * @copyright 2014 <a href="http://www.bioforsk.no/">Bioforsk</a>
+ * @author Tor-Einar Skog <tor-einar.skog@bioforsk.no>
+ */
+@Stateless
+public class OrganismBean {
+    @PersistenceContext(unitName="VIPSLogic-PU")
+    EntityManager em;
+
+   
+    /**
+     * 
+     * @param parentOrganismId Id of the parent organism you start the tree from. If NULL, all top nodes are selected
+     * @return All child nodes of given organism, organized in tree
+     */
+    public List<Organism> getOrganismSubTree(Integer parentOrganismId){
+        List<Organism> retVal;
+        if(parentOrganismId == null)
+        {
+            retVal = em.createNativeQuery("SELECT * FROM Organism o WHERE o.parent_organism_id IS NULL", Organism.class).getResultList();
+        }
+        else
+        {
+            retVal = em.createNamedQuery("Organism.findByParentOrganismId").setParameter("parentOrganismId", parentOrganismId).getResultList();
+        }
+         
+        for(Organism organism : retVal)
+        {
+            organism.setChildOrganisms(this.getOrganismSubTree(organism.getOrganismId()));
+        }
+        return retVal;
+        
+    }
+    
+    /**
+     * 
+     * @param organismId
+     * @return the requested organism and its children AND their children again
+     */
+    public Organism getOrganismAndChildrenTwoLevels(Integer organismId)
+    {
+        Organism retVal = em.find(Organism.class, organismId);
+        retVal.setChildOrganisms(em.createNamedQuery("Organism.findByParentOrganismId").setParameter("parentOrganismId", organismId).getResultList());
+        // Iterate and set children of next level (to decide if the child is deletable)
+        for(Organism child: retVal.getChildOrganisms())
+        {
+            List<Organism> grandChildren = em.createNamedQuery("Organism.findByParentOrganismId").setParameter("parentOrganismId", child.getOrganismId()).getResultList();
+            child.setChildOrganisms(grandChildren);
+        }
+        return retVal;
+    }
+    
+    /**
+     * 
+     * @return top level organisms and direct children
+     */
+    public List<Organism> getTopLevelOrganisms()
+    {
+        List<Organism> retVal = em.createNativeQuery("SELECT * FROM Organism o WHERE o.parent_organism_id IS NULL", Organism.class).getResultList();
+        for(Organism organism: retVal)
+        {
+            List<Organism> children = em.createNamedQuery("Organism.findByParentOrganismId").setParameter("parentOrganismId", organism.getOrganismId()).getResultList();
+            organism.setChildOrganisms(children);
+        }
+        return retVal;
+    }
+
+    public List<ExternalResource> getUnusedExternalResourcesForOrganism(Organism organism) {
+        StringBuilder sql = new StringBuilder()
+                .append("SELECT * FROM external_resource ")
+                .append("WHERE external_resource_type_id=")
+                .append(ExternalResourceType.ORGANISM_DATABASE).append(" \n");
+        if(organism.getOrganismExternalResourceSet() == null || organism.getOrganismExternalResourceSet().isEmpty())
+        {
+            return em.createNativeQuery(sql.toString(), ExternalResource.class).getResultList();
+        }
+        else
+        {
+            sql.append("AND external_resource_id NOT IN (:externalResourceIds)");
+            List<Integer> ids = new ArrayList<>();
+            for(OrganismExternalResource ore : organism.getOrganismExternalResourceSet())
+            {
+                ids.add(ore.getExternalResource().getExternalResourceId());
+            }
+            
+            Query q = em.createNativeQuery(sql.toString(), ExternalResource.class);
+            q.setParameter("externalResourceIds", ids);
+            return q.getResultList();
+        }
+    }
+
+    public Organism storeOrganism(Organism organism) {
+        // Ensure that parentOrganismId = -1 => null
+        if(organism.getParentOrganismId().equals(-1))
+        {
+            organism.setParentOrganismId(null);
+        }
+        return em.merge(organism);
+    }
+
+    public void storeOrganismExternalResource(OrganismExternalResource organismExternalResource) {
+        em.merge(organismExternalResource);
+    }
+
+    public HierarchyCategoryLocaleNames getHierarchyCategoryNames(Locale locale) {
+        Map<Integer, String> map = new HashMap<>();
+        for(HierarchyCategory cat:em.createNamedQuery("HierarchyCategory.findAll", HierarchyCategory.class).getResultList())
+        {
+            map.put(cat.getHierarchyCategoryId(), cat.getLocalName(locale.getLanguage()));
+        }
+        
+        HierarchyCategoryLocaleNames retVal = new HierarchyCategoryLocaleNames();
+        retVal.setNameMap(map);
+        return retVal;
+    }
+
+    /**
+     * Removes an organism from database. Does this only if it has no children
+     * @param organismId 
+     */
+    public boolean deleteOrganism(Integer organismId) {
+        List<Organism> children = em.createNamedQuery("Organism.findByParentOrganismId").setParameter("parentOrganismId", organismId).getResultList();
+        if(children == null || children.isEmpty())
+        {
+            Organism organism = em.find(Organism.class, organismId);
+            em.remove(organism);
+            return true;
+        }
+        else
+        {
+            return false;
+        }
+    }
+
+    /**
+     * Adds local name in given locale to organism
+     * @param organism
+     * @param localName
+     * @param currentLocale 
+     */
+    public void storeOrganismLocalName(Organism organism, String localName, Locale currentLocale) {
+        
+        if(organism.getOrganismLocale(currentLocale.getLanguage()) == null)
+        {
+            
+            OrganismLocalePK pk = new OrganismLocalePK(organism.getOrganismId(), currentLocale.getLanguage());
+            OrganismLocale oLocale = new OrganismLocale(pk);
+            oLocale.setLocalName(localName);
+            em.persist(oLocale);
+        }
+        else
+        {
+            OrganismLocale oLocale = organism.getOrganismLocale(currentLocale.getLanguage());
+            oLocale.setLocalName(localName);
+        }
+    }
+    
+}
diff --git a/src/main/java/no/bioforsk/vips/logic/entity/ExternalResource.java b/src/main/java/no/bioforsk/vips/logic/entity/ExternalResource.java
index 52430f0302f51fad52b2aee9362eedd726b4e988..d766d6cee5376287af7b14080969a5751cdee7cc 100644
--- a/src/main/java/no/bioforsk/vips/logic/entity/ExternalResource.java
+++ b/src/main/java/no/bioforsk/vips/logic/entity/ExternalResource.java
@@ -1,6 +1,20 @@
 /*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
+ * Copyright (c) 2013-2014 Bioforsk <http://www.bioforsk.no/>. 
+ * 
+ * This file is part of VIPSLogic.
+ * VIPSLogic is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * VIPSLogic is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with VIPSLogic.  If not, see <http://www.gnu.org/licenses/>.
+ * 
  */
 
 package no.bioforsk.vips.logic.entity;
diff --git a/src/main/java/no/bioforsk/vips/logic/entity/ExternalResourceType.java b/src/main/java/no/bioforsk/vips/logic/entity/ExternalResourceType.java
index ff296be51349d501b632d1c9cc6e3e5f25596321..fbe7aed129ae63b3081a976f90706e6e79419740 100644
--- a/src/main/java/no/bioforsk/vips/logic/entity/ExternalResourceType.java
+++ b/src/main/java/no/bioforsk/vips/logic/entity/ExternalResourceType.java
@@ -1,8 +1,21 @@
 /*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
+ * Copyright (c) 2013-2014 Bioforsk <http://www.bioforsk.no/>. 
+ * 
+ * This file is part of VIPSLogic.
+ * VIPSLogic is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * VIPSLogic is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with VIPSLogic.  If not, see <http://www.gnu.org/licenses/>.
+ * 
  */
-
 package no.bioforsk.vips.logic.entity;
 
 import java.io.Serializable;
@@ -33,6 +46,9 @@ import org.codehaus.jackson.annotate.JsonIgnore;
     @NamedQuery(name = "ExternalResourceType.findByExternalResourceTypeId", query = "SELECT e FROM ExternalResourceType e WHERE e.externalResourceTypeId = :externalResourceTypeId"),
     @NamedQuery(name = "ExternalResourceType.findByDefaultName", query = "SELECT e FROM ExternalResourceType e WHERE e.defaultName = :defaultName")})
 public class ExternalResourceType implements Serializable {
+    
+    public static final int ORGANISM_DATABASE = 2;
+    
     private static final long serialVersionUID = 1L;
     @Id
     @Basic(optional = false)
diff --git a/src/main/java/no/bioforsk/vips/logic/entity/HierarchyCategory.java b/src/main/java/no/bioforsk/vips/logic/entity/HierarchyCategory.java
index ec1d5d8e3a22b362776d1d4324d86510d0af3b84..537c3ada7ee08b21089cb97d545a9b203b801dad 100644
--- a/src/main/java/no/bioforsk/vips/logic/entity/HierarchyCategory.java
+++ b/src/main/java/no/bioforsk/vips/logic/entity/HierarchyCategory.java
@@ -8,6 +8,7 @@ package no.bioforsk.vips.logic.entity;
 import java.io.Serializable;
 import java.util.Set;
 import javax.persistence.Basic;
+import javax.persistence.CascadeType;
 import javax.persistence.Column;
 import javax.persistence.Entity;
 import javax.persistence.Id;
@@ -15,6 +16,7 @@ import javax.persistence.NamedQueries;
 import javax.persistence.NamedQuery;
 import javax.persistence.OneToMany;
 import javax.persistence.Table;
+import javax.persistence.Transient;
 import javax.validation.constraints.NotNull;
 import javax.validation.constraints.Size;
 import javax.xml.bind.annotation.XmlRootElement;
@@ -34,6 +36,8 @@ import org.codehaus.jackson.annotate.JsonIgnore;
     @NamedQuery(name = "HierarchyCategory.findByDefaultName", query = "SELECT h FROM HierarchyCategory h WHERE h.defaultName = :defaultName"),
     @NamedQuery(name = "HierarchyCategory.findByLogicallyDeleted", query = "SELECT h FROM HierarchyCategory h WHERE h.logicallyDeleted = :logicallyDeleted")})
 public class HierarchyCategory implements Serializable {
+    @OneToMany(cascade = CascadeType.ALL, mappedBy = "hierarchyCategoryId")
+    private Set<HierarchyCategoryLocale> hierarchyCategoryLocaleSet;
     private static final long serialVersionUID = 1L;
     @Id
     @Basic(optional = false)
@@ -121,4 +125,27 @@ public class HierarchyCategory implements Serializable {
         return "no.bioforsk.vips.logic.entity.HierarchyCategory[ hierarchyCategoryId=" + hierarchyCategoryId + " ]";
     }
 
+    @XmlTransient
+    @JsonIgnore
+    public Set<HierarchyCategoryLocale> getHierarchyCategoryLocaleSet() {
+        return hierarchyCategoryLocaleSet;
+    }
+
+    public void setHierarchyCategoryLocaleSet(Set<HierarchyCategoryLocale> hierarchyCategoryLocaleSet) {
+        this.hierarchyCategoryLocaleSet = hierarchyCategoryLocaleSet;
+    }
+
+    @Transient
+    public String getLocalName(String locale)
+    {
+        for(HierarchyCategoryLocale hLocale: this.getHierarchyCategoryLocaleSet())
+        {
+            if(hLocale.getHierarchyCategoryLocalePK().getLocale().equals(locale))
+            {
+                return hLocale.getLocalName();
+            }
+        }
+        
+        return "";
+    }
 }
diff --git a/src/main/java/no/bioforsk/vips/logic/entity/HierarchyCategoryLocale.java b/src/main/java/no/bioforsk/vips/logic/entity/HierarchyCategoryLocale.java
new file mode 100644
index 0000000000000000000000000000000000000000..84942351fa4d17761afae713282b013cb74e1ddd
--- /dev/null
+++ b/src/main/java/no/bioforsk/vips/logic/entity/HierarchyCategoryLocale.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2014 Bioforsk <http://www.bioforsk.no/>. 
+ * 
+ * This file is part of VIPSLogic.
+ * VIPSLogic is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * VIPSLogic is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with VIPSLogic.  If not, see <http://www.gnu.org/licenses/>.
+ * 
+ */
+
+package no.bioforsk.vips.logic.entity;
+
+import java.io.Serializable;
+import javax.persistence.Column;
+import javax.persistence.EmbeddedId;
+import javax.persistence.Entity;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.Table;
+import javax.validation.constraints.Size;
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * @copyright 2014 <a href="http://www.bioforsk.no/">Bioforsk</a>
+ * @author Tor-Einar Skog <tor-einar.skog@bioforsk.no>
+ */
+@Entity
+@Table(name = "hierarchy_category_locale")
+@XmlRootElement
+@NamedQueries({
+    @NamedQuery(name = "HierarchyCategoryLocale.findAll", query = "SELECT h FROM HierarchyCategoryLocale h"),
+    @NamedQuery(name = "HierarchyCategoryLocale.findByHierarchyCategoryId", query = "SELECT h FROM HierarchyCategoryLocale h WHERE h.hierarchyCategoryLocalePK.hierarchyCategoryId = :hierarchyCategoryId"),
+    @NamedQuery(name = "HierarchyCategoryLocale.findByLocale", query = "SELECT h FROM HierarchyCategoryLocale h WHERE h.hierarchyCategoryLocalePK.locale = :locale"),
+    @NamedQuery(name = "HierarchyCategoryLocale.findByLocalName", query = "SELECT h FROM HierarchyCategoryLocale h WHERE h.localName = :localName")})
+public class HierarchyCategoryLocale implements Serializable {
+    private static final long serialVersionUID = 1L;
+    @EmbeddedId
+    protected HierarchyCategoryLocalePK hierarchyCategoryLocalePK;
+    @Size(max = 255)
+    @Column(name = "local_name")
+    private String localName;
+    @Column(name = "hierarchy_category_id", insertable = false, updatable = false)
+    private Integer hierarchyCategoryId;
+
+    public HierarchyCategoryLocale() {
+    }
+
+    public HierarchyCategoryLocale(HierarchyCategoryLocalePK hierarchyCategoryLocalePK) {
+        this.hierarchyCategoryLocalePK = hierarchyCategoryLocalePK;
+    }
+
+    public HierarchyCategoryLocale(int hierarchyCategoryId, String locale) {
+        this.hierarchyCategoryLocalePK = new HierarchyCategoryLocalePK(hierarchyCategoryId, locale);
+    }
+
+    public HierarchyCategoryLocalePK getHierarchyCategoryLocalePK() {
+        return hierarchyCategoryLocalePK;
+    }
+
+    public void setHierarchyCategoryLocalePK(HierarchyCategoryLocalePK hierarchyCategoryLocalePK) {
+        this.hierarchyCategoryLocalePK = hierarchyCategoryLocalePK;
+    }
+
+    public String getLocalName() {
+        return localName;
+    }
+
+    public void setLocalName(String localName) {
+        this.localName = localName;
+    }
+
+
+    @Override
+    public int hashCode() {
+        int hash = 0;
+        hash += (hierarchyCategoryLocalePK != null ? hierarchyCategoryLocalePK.hashCode() : 0);
+        return hash;
+    }
+
+    @Override
+    public boolean equals(Object object) {
+        // TODO: Warning - this method won't work in the case the id fields are not set
+        if (!(object instanceof HierarchyCategoryLocale)) {
+            return false;
+        }
+        HierarchyCategoryLocale other = (HierarchyCategoryLocale) object;
+        if ((this.hierarchyCategoryLocalePK == null && other.hierarchyCategoryLocalePK != null) || (this.hierarchyCategoryLocalePK != null && !this.hierarchyCategoryLocalePK.equals(other.hierarchyCategoryLocalePK))) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return "no.bioforsk.vips.logic.entity.HierarchyCategoryLocale[ hierarchyCategoryLocalePK=" + hierarchyCategoryLocalePK + " ]";
+    }
+
+}
diff --git a/src/main/java/no/bioforsk/vips/logic/entity/HierarchyCategoryLocalePK.java b/src/main/java/no/bioforsk/vips/logic/entity/HierarchyCategoryLocalePK.java
new file mode 100644
index 0000000000000000000000000000000000000000..8566fa1665ae993d68f81c56cadb4871a1b94a00
--- /dev/null
+++ b/src/main/java/no/bioforsk/vips/logic/entity/HierarchyCategoryLocalePK.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2014 Bioforsk <http://www.bioforsk.no/>. 
+ * 
+ * This file is part of VIPSLogic.
+ * VIPSLogic is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * VIPSLogic is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with VIPSLogic.  If not, see <http://www.gnu.org/licenses/>.
+ * 
+ */
+
+package no.bioforsk.vips.logic.entity;
+
+import java.io.Serializable;
+import javax.persistence.Basic;
+import javax.persistence.Column;
+import javax.persistence.Embeddable;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+
+/**
+ * @copyright 2014 <a href="http://www.bioforsk.no/">Bioforsk</a>
+ * @author Tor-Einar Skog <tor-einar.skog@bioforsk.no>
+ */
+@Embeddable
+public class HierarchyCategoryLocalePK implements Serializable {
+    @Basic(optional = false)
+    @NotNull
+    @Column(name = "hierarchy_category_id")
+    private int hierarchyCategoryId;
+    @Basic(optional = false)
+    @NotNull
+    @Size(min = 1, max = 10)
+    @Column(name = "locale")
+    private String locale;
+
+    public HierarchyCategoryLocalePK() {
+    }
+
+    public HierarchyCategoryLocalePK(int hierarchyCategoryId, String locale) {
+        this.hierarchyCategoryId = hierarchyCategoryId;
+        this.locale = locale;
+    }
+
+    public int getHierarchyCategoryId() {
+        return hierarchyCategoryId;
+    }
+
+    public void setHierarchyCategoryId(int hierarchyCategoryId) {
+        this.hierarchyCategoryId = hierarchyCategoryId;
+    }
+
+    public String getLocale() {
+        return locale;
+    }
+
+    public void setLocale(String locale) {
+        this.locale = locale;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 0;
+        hash += (int) hierarchyCategoryId;
+        hash += (locale != null ? locale.hashCode() : 0);
+        return hash;
+    }
+
+    @Override
+    public boolean equals(Object object) {
+        // TODO: Warning - this method won't work in the case the id fields are not set
+        if (!(object instanceof HierarchyCategoryLocalePK)) {
+            return false;
+        }
+        HierarchyCategoryLocalePK other = (HierarchyCategoryLocalePK) object;
+        if (this.hierarchyCategoryId != other.hierarchyCategoryId) {
+            return false;
+        }
+        if ((this.locale == null && other.locale != null) || (this.locale != null && !this.locale.equals(other.locale))) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return "no.bioforsk.vips.logic.entity.HierarchyCategoryLocalePK[ hierarchyCategoryId=" + hierarchyCategoryId + ", locale=" + locale + " ]";
+    }
+
+}
diff --git a/src/main/java/no/bioforsk/vips/logic/entity/Organism.java b/src/main/java/no/bioforsk/vips/logic/entity/Organism.java
index edd0dc7d727566f5abfa72464686127d0e7f803c..3ea50f35754926139ec6d55f1d102367148ca50a 100644
--- a/src/main/java/no/bioforsk/vips/logic/entity/Organism.java
+++ b/src/main/java/no/bioforsk/vips/logic/entity/Organism.java
@@ -1,33 +1,46 @@
 /*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
+ * Copyright (c) 2013-2014 Bioforsk <http://www.bioforsk.no/>. 
+ * 
+ * This file is part of VIPSLogic.
+ * VIPSLogic is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * VIPSLogic is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with VIPSLogic.  If not, see <http://www.gnu.org/licenses/>.
+ * 
  */
 
 package no.bioforsk.vips.logic.entity;
 
 import java.io.Serializable;
+import java.util.List;
 import java.util.Set;
 import javax.persistence.Basic;
 import javax.persistence.CascadeType;
 import javax.persistence.Column;
 import javax.persistence.Entity;
+import javax.persistence.FetchType;
 import javax.persistence.GeneratedValue;
 import javax.persistence.GenerationType;
 import javax.persistence.Id;
-import javax.persistence.JoinColumn;
-import javax.persistence.ManyToOne;
 import javax.persistence.NamedQueries;
 import javax.persistence.NamedQuery;
 import javax.persistence.OneToMany;
 import javax.persistence.Table;
+import javax.persistence.Transient;
 import javax.validation.constraints.NotNull;
 import javax.validation.constraints.Size;
 import javax.xml.bind.annotation.XmlRootElement;
-import javax.xml.bind.annotation.XmlTransient;
-import org.codehaus.jackson.annotate.JsonIgnore;
 
 /**
- * @copyright 2013 <a href="http://www.bioforsk.no/">Bioforsk</a>
+ * @copyright 2013-2014 <a href="http://www.bioforsk.no/">Bioforsk</a>
  * @author Tor-Einar Skog <tor-einar.skog@bioforsk.no>
  */
 @Entity
@@ -36,6 +49,7 @@ import org.codehaus.jackson.annotate.JsonIgnore;
 @NamedQueries({
     @NamedQuery(name = "Organism.findAll", query = "SELECT o FROM Organism o"),
     @NamedQuery(name = "Organism.findByOrganismId", query = "SELECT o FROM Organism o WHERE o.organismId = :organismId"),
+    @NamedQuery(name = "Organism.findByParentOrganismId", query = "SELECT o FROM Organism o WHERE o.parentOrganismId = :parentOrganismId"),
     @NamedQuery(name = "Organism.findByLatinName", query = "SELECT o FROM Organism o WHERE o.latinName = :latinName"),
     @NamedQuery(name = "Organism.findByTradeName", query = "SELECT o FROM Organism o WHERE o.tradeName = :tradeName"),
     @NamedQuery(name = "Organism.findByLogicallyDeleted", query = "SELECT o FROM Organism o WHERE o.logicallyDeleted = :logicallyDeleted")})
@@ -56,18 +70,33 @@ public class Organism implements Serializable {
     @NotNull
     @Column(name = "logically_deleted")
     private boolean logicallyDeleted;
-    @OneToMany(cascade = CascadeType.ALL, mappedBy = "organism")
+    
+    // We simplify this to avoid very large queries
+    @Column(name = "parent_organism_id")
+    private Integer parentOrganismId;
+    @Column(name = "hierarchy_category_id")
+    private Integer hierarchyCategoryId;
+    @OneToMany(cascade = CascadeType.ALL, mappedBy = "organismId", fetch = FetchType.EAGER)
+    private Set<OrganismLocale> organismLocaleSet;
+    @OneToMany(cascade = CascadeType.ALL, mappedBy = "organism", fetch = FetchType.EAGER)
+    private Set<OrganismExternalResource> organismExternalResourceSet;
+    
+    @Transient
+    private List<Organism> childOrganisms;
+    
+    /*@OneToMany(cascade = CascadeType.ALL, mappedBy = "organism")
     private Set<OrganismLocale> organismLocaleSet;
     @OneToMany(cascade = CascadeType.ALL, mappedBy = "organism")
     private Set<OrganismExternalResource> organismExternalResourceSet;
-    @OneToMany(mappedBy = "parentOrganismId")
+    @OneToMany(mappedBy = "parentOrganismId", fetch = FetchType.EAGER)
     private Set<Organism> organismSet;
     @JoinColumn(name = "parent_organism_id", referencedColumnName = "organism_id")
-    @ManyToOne
+    @ManyToOne(fetch = FetchType.LAZY)
     private Organism parentOrganismId;
     @JoinColumn(name = "hierarchy_category_id", referencedColumnName = "hierarchy_category_id")
     @ManyToOne
     private HierarchyCategory hierarchyCategoryId;
+    */
 
     public Organism() {
     }
@@ -113,8 +142,7 @@ public class Organism implements Serializable {
         this.logicallyDeleted = logicallyDeleted;
     }
 
-    @XmlTransient
-    @JsonIgnore
+    
     public Set<OrganismLocale> getOrganismLocaleSet() {
         return organismLocaleSet;
     }
@@ -123,8 +151,6 @@ public class Organism implements Serializable {
         this.organismLocaleSet = organismLocaleSet;
     }
 
-    @XmlTransient
-    @JsonIgnore
     public Set<OrganismExternalResource> getOrganismExternalResourceSet() {
         return organismExternalResourceSet;
     }
@@ -133,8 +159,7 @@ public class Organism implements Serializable {
         this.organismExternalResourceSet = organismExternalResourceSet;
     }
 
-    @XmlTransient
-    @JsonIgnore
+    /*
     public Set<Organism> getOrganismSet() {
         return organismSet;
     }
@@ -143,6 +168,7 @@ public class Organism implements Serializable {
         this.organismSet = organismSet;
     }
 
+    @JsonIgnore
     public Organism getParentOrganismId() {
         return parentOrganismId;
     }
@@ -158,6 +184,7 @@ public class Organism implements Serializable {
     public void setHierarchyCategoryId(HierarchyCategory hierarchyCategoryId) {
         this.hierarchyCategoryId = hierarchyCategoryId;
     }
+    */
 
     @Override
     public int hashCode() {
@@ -184,4 +211,75 @@ public class Organism implements Serializable {
         return "no.bioforsk.vips.logic.entity.Organism[ organismId=" + organismId + " ]";
     }
 
+    /**
+     * @return the parentOrganismId
+     */
+    public Integer getParentOrganismId() {
+        return parentOrganismId;
+    }
+
+    /**
+     * @param parentOrganismId the parentOrganismId to set
+     */
+    public void setParentOrganismId(Integer parentOrganismId) {
+        this.parentOrganismId = parentOrganismId;
+    }
+
+    /**
+     * @return the hierarchyCategoryId
+     */
+    public Integer getHierarchyCategoryId() {
+        return hierarchyCategoryId;
+    }
+
+    /**
+     * @param hierarchyCategoryId the hierarchyCategoryId to set
+     */
+    public void setHierarchyCategoryId(Integer hierarchyCategoryId) {
+        this.hierarchyCategoryId = hierarchyCategoryId;
+    }
+
+    @Transient
+    public List<Organism> getChildOrganisms()
+    {
+        return this.childOrganisms;
+    }
+
+    /**
+     * @param childOrganisms the childOrganisms to set
+     */
+    @Transient
+    public void setChildOrganisms(List<Organism> childOrganisms) {
+        this.childOrganisms = childOrganisms;
+    }
+    
+    @Transient
+    public String getLocalName(String locale)
+    {
+        for(OrganismLocale oLocale: this.getOrganismLocaleSet())
+        {
+            if(oLocale.getOrganismLocalePK().getLocale().equals(locale))
+            {
+                return oLocale.getLocalName();
+            }
+        }
+        
+        return "";
+    }
+
+    @Transient
+    public OrganismLocale getOrganismLocale(String language) {
+        if(this.getOrganismLocaleSet() == null)
+        {
+            return null;
+        }
+        for(OrganismLocale oLocale:this.getOrganismLocaleSet())
+        {
+            if(oLocale.getOrganismLocalePK().getLocale().equals(language))
+            {
+                return oLocale;
+            }
+        }
+        return null;
+    }
 }
diff --git a/src/main/java/no/bioforsk/vips/logic/entity/OrganismExternalResource.java b/src/main/java/no/bioforsk/vips/logic/entity/OrganismExternalResource.java
index 6b3858656c76b4d289db52e076674cae97246648..5426d7bd3a0138b11d20c290f1322220a2a613ee 100644
--- a/src/main/java/no/bioforsk/vips/logic/entity/OrganismExternalResource.java
+++ b/src/main/java/no/bioforsk/vips/logic/entity/OrganismExternalResource.java
@@ -14,6 +14,7 @@ import javax.persistence.ManyToOne;
 import javax.persistence.NamedQueries;
 import javax.persistence.NamedQuery;
 import javax.persistence.Table;
+import javax.persistence.Transient;
 import javax.validation.constraints.Size;
 import javax.xml.bind.annotation.XmlRootElement;
 
@@ -111,4 +112,12 @@ public class OrganismExternalResource implements Serializable {
         return "no.bioforsk.vips.logic.entity.OrganismExternalResource[ organismExternalResourcePK=" + organismExternalResourcePK + " ]";
     }
 
+    @Transient
+    public String getResourceUrl()
+    {
+        StringBuilder retVal = new StringBuilder()
+                .append(this.getExternalResource().getUri())
+                .append(this.getExternalResource().getIdentifierTemplate());
+        return String.format(retVal.toString(), this.getResourceIdentifier());
+    }
 }
diff --git a/src/main/java/no/bioforsk/vips/logic/entity/OrganismLocale.java b/src/main/java/no/bioforsk/vips/logic/entity/OrganismLocale.java
index 2ba06c284b66a38417d313bc4229cf2a15bf8801..69cd61975edd7a7061e1ddfc59343532e5162a26 100644
--- a/src/main/java/no/bioforsk/vips/logic/entity/OrganismLocale.java
+++ b/src/main/java/no/bioforsk/vips/logic/entity/OrganismLocale.java
@@ -1,6 +1,20 @@
 /*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
+ * Copyright (c) 2013-2014 Bioforsk <http://www.bioforsk.no/>. 
+ * 
+ * This file is part of VIPSLogic.
+ * VIPSLogic is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * VIPSLogic is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with VIPSLogic.  If not, see <http://www.gnu.org/licenses/>.
+ * 
  */
 
 package no.bioforsk.vips.logic.entity;
@@ -9,8 +23,6 @@ import java.io.Serializable;
 import javax.persistence.Column;
 import javax.persistence.EmbeddedId;
 import javax.persistence.Entity;
-import javax.persistence.JoinColumn;
-import javax.persistence.ManyToOne;
 import javax.persistence.NamedQueries;
 import javax.persistence.NamedQuery;
 import javax.persistence.Table;
@@ -18,7 +30,7 @@ import javax.validation.constraints.Size;
 import javax.xml.bind.annotation.XmlRootElement;
 
 /**
- * @copyright 2013 <a href="http://www.bioforsk.no/">Bioforsk</a>
+ * @copyright 2013-2014 <a href="http://www.bioforsk.no/">Bioforsk</a>
  * @author Tor-Einar Skog <tor-einar.skog@bioforsk.no>
  */
 @Entity
@@ -36,10 +48,9 @@ public class OrganismLocale implements Serializable {
     @Size(max = 255)
     @Column(name = "local_name")
     private String localName;
-    @JoinColumn(name = "organism_id", referencedColumnName = "organism_id", insertable = false, updatable = false)
-    @ManyToOne(optional = false)
-    private Organism organism;
-
+    @Column(name = "organism_id", insertable = false, updatable = false)
+    private Integer organismId;
+    
     public OrganismLocale() {
     }
 
@@ -67,13 +78,7 @@ public class OrganismLocale implements Serializable {
         this.localName = localName;
     }
 
-    public Organism getOrganism() {
-        return organism;
-    }
-
-    public void setOrganism(Organism organism) {
-        this.organism = organism;
-    }
+    
 
     @Override
     public int hashCode() {
diff --git a/src/main/java/no/bioforsk/vips/logic/entity/OrganismLocalePK.java b/src/main/java/no/bioforsk/vips/logic/entity/OrganismLocalePK.java
index bd89b4d8231f5d71843268b0d2262cd097d26d0b..5b290fc2621ef58e2007db055d6a0c8de683af92 100644
--- a/src/main/java/no/bioforsk/vips/logic/entity/OrganismLocalePK.java
+++ b/src/main/java/no/bioforsk/vips/logic/entity/OrganismLocalePK.java
@@ -1,6 +1,20 @@
 /*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
+ * Copyright (c) 2013-2014 Bioforsk <http://www.bioforsk.no/>. 
+ * 
+ * This file is part of VIPSLogic.
+ * VIPSLogic is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * VIPSLogic is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with VIPSLogic.  If not, see <http://www.gnu.org/licenses/>.
+ * 
  */
 
 package no.bioforsk.vips.logic.entity;
diff --git a/src/main/java/no/bioforsk/vips/logic/i18n/LocalizationFilter.java b/src/main/java/no/bioforsk/vips/logic/i18n/LocalizationFilter.java
index 133de6dad04bf560ccba6ea45d7a1046f751a36e..5eb93874d92b72620831ad4c07eae2fca6c2783c 100644
--- a/src/main/java/no/bioforsk/vips/logic/i18n/LocalizationFilter.java
+++ b/src/main/java/no/bioforsk/vips/logic/i18n/LocalizationFilter.java
@@ -1,3 +1,22 @@
+/*
+ * Copyright (c) 2013-2014 Bioforsk <http://www.bioforsk.no/>. 
+ * 
+ * This file is part of VIPSLogic.
+ * VIPSLogic is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * VIPSLogic is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with VIPSLogic.  If not, see <http://www.gnu.org/licenses/>.
+ * 
+ */
+
 package no.bioforsk.vips.logic.i18n;
 
 import java.io.IOException;
diff --git a/src/main/java/no/bioforsk/vips/logic/i18n/SessionLocaleUtil.java b/src/main/java/no/bioforsk/vips/logic/i18n/SessionLocaleUtil.java
index e4fe4430cf37f65de5de0d2e660019c1e6379e22..318048abd7cb355e6ec5db600ab7f3cc0afbb6eb 100644
--- a/src/main/java/no/bioforsk/vips/logic/i18n/SessionLocaleUtil.java
+++ b/src/main/java/no/bioforsk/vips/logic/i18n/SessionLocaleUtil.java
@@ -1,3 +1,22 @@
+/*
+ * Copyright (c) 2013-2014 Bioforsk <http://www.bioforsk.no/>. 
+ * 
+ * This file is part of VIPSLogic.
+ * VIPSLogic is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * VIPSLogic is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with VIPSLogic.  If not, see <http://www.gnu.org/licenses/>.
+ * 
+ */
+
 package no.bioforsk.vips.logic.i18n;
 
 import java.util.Locale;
diff --git a/src/main/java/no/bioforsk/vips/logic/util/HierarchyCategoryLocaleNames.java b/src/main/java/no/bioforsk/vips/logic/util/HierarchyCategoryLocaleNames.java
new file mode 100644
index 0000000000000000000000000000000000000000..6905fc2bb60a24a0a180dadd58f6f38c0c19cc7f
--- /dev/null
+++ b/src/main/java/no/bioforsk/vips/logic/util/HierarchyCategoryLocaleNames.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2014 Bioforsk <http://www.bioforsk.no/>. 
+ * 
+ * This file is part of VIPSLogic.
+ * VIPSLogic is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * VIPSLogic is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with VIPSLogic.  If not, see <http://www.gnu.org/licenses/>.
+ * 
+ */
+
+package no.bioforsk.vips.logic.util;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @copyright 2014 <a href="http://www.bioforsk.no/">Bioforsk</a>
+ * @author Tor-Einar Skog <tor-einar.skog@bioforsk.no>
+ */
+public class HierarchyCategoryLocaleNames {
+
+    private Map<Integer, String> nameMap;
+    
+    public String getName(Integer hierarchyCategoryId)
+    {
+        return this.nameMap.get(hierarchyCategoryId);
+    }
+    
+    public List<Integer> getHierarchyCategoryIds()
+    {
+        List<Integer> retVal = new ArrayList(this.getNameMap().keySet());
+        Collections.sort(retVal);
+        return retVal;
+    }
+    
+    /**
+     * @return the nameMap
+     */
+    public Map<Integer, String> getNameMap() {
+        return nameMap;
+    }
+
+    /**
+     * @param nameMap the nameMap to set
+     */
+    public void setNameMap(Map<Integer, String> nameMap) {
+        this.nameMap = nameMap;
+    }
+    
+    
+}
diff --git a/src/main/java/no/bioforsk/web/forms/FormField.java b/src/main/java/no/bioforsk/web/forms/FormField.java
index b4ac5068f935c209e785910ce59047805602fc3e..1b07115f8ebcd8d0658f05cc25dc4cb9458467c1 100644
--- a/src/main/java/no/bioforsk/web/forms/FormField.java
+++ b/src/main/java/no/bioforsk/web/forms/FormField.java
@@ -22,7 +22,9 @@ package no.bioforsk.web.forms;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.util.Date;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import org.codehaus.jackson.annotate.JsonIgnore;
 
 /**
@@ -49,6 +51,8 @@ public class FormField {
     public final static String FIELD_TYPE_HIDDEN = "HIDDEN";
     public final static String FIELD_TYPE_SELECT_SINGLE = "SELECT_SINGLE";
     public final static String FIELD_TYPE_SELECT_MULTIPLE = "SELECT_MULTIPLE";
+    // Multiple inputs for map. (e.g. <input type="text" name="relatedItem_25" value="Foo"/>,<input type="text" name="relatedItem_3" value="Bar"/>
+    public final static String FIELD_TYPE_MULTIPLE_MAP = "MULTIPLE_MAP";
 
     // Standard format for dates is the ISO-8861-format
     public final static String DATE_DEFAULT_FORMAT = "yyyy-MM-dd";
@@ -369,5 +373,5 @@ public class FormField {
     {
         return this.getWebValue() == null || this.getWebValue().trim().isEmpty();
     }
-    
+
 }
diff --git a/src/main/java/no/bioforsk/web/forms/FormValidation.java b/src/main/java/no/bioforsk/web/forms/FormValidation.java
index aa4bcd10f45dead2af73184a383c2690c50ee4b1..e740d2fa9d76a172181ebd63c6b4fe813106b3c1 100644
--- a/src/main/java/no/bioforsk/web/forms/FormValidation.java
+++ b/src/main/java/no/bioforsk/web/forms/FormValidation.java
@@ -12,6 +12,7 @@ public class FormValidation {
 
     private Map<String,FormField> formFields;
     
+    private Map<String,Map<String,FormField>> multipleMapFormFields;
 
     /**
      * @return the valid If at least one field is not valid
@@ -67,5 +68,37 @@ public class FormValidation {
     public void setFormFields(Map<String,FormField> formFields) {
         this.formFields = formFields;
     }
+
+    /**
+     * @return the multipleMapFormFields
+     */
+    public Map<String,Map<String,FormField>> getMultipleMapFormFields() {
+        if(this.multipleMapFormFields == null)
+        {
+            this.multipleMapFormFields = new HashMap<>();
+        }
+        return multipleMapFormFields;
+    }
+
+    /**
+     * @param multipleMapFormFields the multipleMapFormFields to set
+     */
+    public void setMultipleMapFormFields(Map<String,Map<String,FormField>> multipleMapFormFields) {
+        this.multipleMapFormFields = multipleMapFormFields;
+    }
     
+    public void addMultipleMapFormField(String fieldName, String key, FormField formField)
+    {
+        if(this.multipleMapFormFields == null)
+        {
+            this.multipleMapFormFields = new HashMap<>();
+        }
+        Map<String, FormField> fieldMap = this.multipleMapFormFields.get(fieldName);
+        if(fieldMap == null)
+        {
+            fieldMap = new HashMap<>();
+            this.multipleMapFormFields.put(fieldName, fieldMap);
+        }
+        fieldMap.put(key, formField);
+    }
 }
diff --git a/src/main/java/no/bioforsk/web/forms/FormValidationException.java b/src/main/java/no/bioforsk/web/forms/FormValidationException.java
new file mode 100644
index 0000000000000000000000000000000000000000..16289cbe2ac7abf7e595e716d4467b4e86db221a
--- /dev/null
+++ b/src/main/java/no/bioforsk/web/forms/FormValidationException.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2014 Bioforsk <http://www.bioforsk.no/>. 
+ * 
+ * This file is part of VIPSLogic.
+ * VIPSLogic is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * VIPSLogic is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with VIPSLogic.  If not, see <http://www.gnu.org/licenses/>.
+ * 
+ */
+
+package no.bioforsk.web.forms;
+
+/**
+ * @copyright 2014 <a href="http://www.bioforsk.no/">Bioforsk</a>
+ * @author Tor-Einar Skog <tor-einar.skog@bioforsk.no>
+ */
+public class FormValidationException extends Exception {
+
+    /**
+     * Creates a new instance of <code>FormValidationException</code> without detail message.
+     */
+    public FormValidationException() {
+    }
+
+
+    /**
+     * Constructs an instance of <code>FormValidationException</code> with the specified detail message.
+     * @param msg the detail message.
+     */
+    public FormValidationException(String msg) {
+        super(msg);
+    }
+}
diff --git a/src/main/java/no/bioforsk/web/forms/FormValidator.java b/src/main/java/no/bioforsk/web/forms/FormValidator.java
index d9162601be4b33f2ae6852952654eed868372b90..8d1b01174df84afdcf5e1a07cbd2ba5cabfc466c 100644
--- a/src/main/java/no/bioforsk/web/forms/FormValidator.java
+++ b/src/main/java/no/bioforsk/web/forms/FormValidator.java
@@ -1,3 +1,22 @@
+/*
+ * Copyright (c) 2013-2014 Bioforsk <http://www.bioforsk.no/>. 
+ * 
+ * This file is part of VIPSLogic.
+ * VIPSLogic is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * VIPSLogic is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with VIPSLogic.  If not, see <http://www.gnu.org/licenses/>.
+ * 
+ */
+
 package no.bioforsk.web.forms;
 
 import java.io.IOException;
@@ -5,11 +24,14 @@ import java.io.InputStream;
 import java.text.MessageFormat;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
+import java.util.ArrayList;
 import java.util.Date;
+import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.MissingResourceException;
 import javax.servlet.ServletContext;
 import javax.servlet.http.HttpServletRequest;
 import no.bioforsk.vips.logic.authenticate.PasswordValidationException;
@@ -38,27 +60,77 @@ public class FormValidator {
     public static String RELATION_TYPE_EQUALS = "EQUALS";
     public static String RELATION_TYPE_AFTER = "AFTER";
 
-    public static FormValidation validateForm(String formName, HttpServletRequest request, ServletContext servletContext) throws IOException {
+    public static FormValidation validateForm(String formName, HttpServletRequest request, ServletContext servletContext) throws IOException, FormValidationException {
         JsonNode formDefinition = getFormDefinition(formName, servletContext);
         return validateForm(formDefinition, request);
     }
     
-    public static FormValidation validateForm(String formDefinitionStr, HttpServletRequest request) throws IOException
+    public static FormValidation validateForm(String formDefinitionStr, HttpServletRequest request) throws IOException, FormValidationException
     {
         JsonNode formDefinition = getFormDefinition(formDefinitionStr);
         return validateForm(formDefinition, request);
     }
     
-    public static FormValidation validateForm(JsonNode formDefinition, HttpServletRequest request)   
+    public static FormValidation validateForm(JsonNode formDefinition, HttpServletRequest request) throws FormValidationException
     {
         List<FormField> fields =  new ObjectMapper().convertValue(formDefinition.findValue("fields"), new TypeReference<List<FormField>>(){});
         FormValidation retVal = new FormValidation();
         
+        // If one of the fields is a multiple map, we must get them from the form and add them
+        List<FormField> multipleMapFields = new ArrayList<>();
+        for(FormField field: fields)
+        {
+            if(field.getFieldType().equals(FormField.FIELD_TYPE_MULTIPLE_MAP))
+            {
+                // By convention, these fields always end with "_*", where * denotes the key.
+                // So we loop through the requests and stores all that matches (begins with) fieldName
+                
+                for(Enumeration en = request.getParameterNames();en.hasMoreElements();)
+                {
+                    String parameterName = (String) en.nextElement();
+                    if(parameterName.startsWith(field.getName()))
+                    {
+                        FormField newField = new FormField();
+                        newField.setName(parameterName);
+                        newField.setDataType(field.getDataType());
+                        newField.setRequired(field.isRequired());
+                        newField.setWebValue(request.getParameter(parameterName));
+                        multipleMapFields.add(newField);
+                        retVal.addMultipleMapFormField(field.getName(), parameterName.substring(field.getName().length() + 1), newField);
+                    }
+                }
+            }
+        }
+        fields.addAll(multipleMapFields);
+        
         for(FormField field: fields)
         {
+            // Check for NULL fieldType
+            if(field.getDataType() == null)
+            {
+                throw new FormValidationException("Missing datatype definition for field " + field.getName());
+            }
+            
+            // Skip multiple map fields, as they've been exploded in the loop above.
+            if(field.getFieldType().equals(FormField.FIELD_TYPE_MULTIPLE_MAP))
+            {
+                continue;
+            }
+            
+            
+            
             retVal.addFormField(field);
-            field.setLabel(SessionLocaleUtil.getI18nText(request, field.getName()));
+            try
+            {
+                field.setLabel(SessionLocaleUtil.getI18nText(request, field.getName()));
+            }
+            catch(MissingResourceException ex)
+            {
+                field.setLabel(field.getName());
+            }
+            
             field.setWebValue(request.getParameterValues(field.getName()));
+            
             if(field.getWebValue() == null || field.getWebValue().isEmpty())
             {
                 if(field.isRequired())
diff --git a/src/main/resources/no/bioforsk/vips/logic/i18n/vipslogictexts.properties b/src/main/resources/no/bioforsk/vips/logic/i18n/vipslogictexts.properties
index bc146e28a296f3bc73cca6447e10d0ba92e0c6e2..cb9d09df328eda554c8e20a95f0a1c7844711bab 100644
--- a/src/main/resources/no/bioforsk/vips/logic/i18n/vipslogictexts.properties
+++ b/src/main/resources/no/bioforsk/vips/logic/i18n/vipslogictexts.properties
@@ -144,3 +144,13 @@ attachIdToExistingUser=Add this login to my existing user
 confirmEmailReceipt=Your email was successfully confirmed. You will be notified as soon as the organization administrator approves your application.
 confirmEmailFailure=Your email address was not confirmed. Please contact the system administrator.
 attachIdToExistingUserReceipt=The id was successfully connected to the user. You may now log in with it
+externalResources=External resources
+externalResourceIdentifier=External resource identifier
+organismId=Organism id
+parentOrganismId=Parent organism
+newOrganism=New organism
+hierarchyCategoryId=Hierarchy category
+organismUpdated=Organism was updated
+organismRegistered=Organism was registered
+organismDeleted=Organism was deleted
+organismNotDeleted=Organism was not deleted
diff --git a/src/main/resources/no/bioforsk/vips/logic/i18n/vipslogictexts_no.properties b/src/main/resources/no/bioforsk/vips/logic/i18n/vipslogictexts_no.properties
index 7080b1085e1afd8e4c14b55aca33c5b913226e49..53b6a771ed1820f960ac39e39bf6a55f77a59bac 100644
--- a/src/main/resources/no/bioforsk/vips/logic/i18n/vipslogictexts_no.properties
+++ b/src/main/resources/no/bioforsk/vips/logic/i18n/vipslogictexts_no.properties
@@ -144,3 +144,13 @@ attachIdToExistingUser=Add this login to my existing user
 confirmEmailReceipt=Din e-postadresse ble bekreftet. Du blir varslet s\u00e5 snart organisasjonens administrator godtar din s\u00f8knad.
 confirmEmailFailure=E-postadressen din ble ikke bekreftet. Vennligst kontakt systemadministrator.
 attachIdToExistingUserReceipt=Id'en ble koblet til brukeren. Du kan n\u00e5 logge inn med denne id'en.
+externalResources=Eksterne kilder
+externalResourceIdentifier=Identifikator for ekstern kilde
+organismId=Organisme-id
+parentOrganismId=Foreldreorganisme
+newOrganism=Ny organisme
+hierarchyCategoryId=Hierarkikategori
+organismUpdated=Organismen ble oppdatert
+organismRegistered=Organismen ble registrert
+organismDeleted=Organismen ble slettet
+organismNotDeleted=Organismen ble ikke slettet
diff --git a/src/main/webapp/formdefinitions/organismForm.json b/src/main/webapp/formdefinitions/organismForm.json
index 0d708cd1c31362eba57fd8aa4f3baa53267cd557..39f994459a2b8b1e3c0e4d52f2bfa823af12a5fd 100644
--- a/src/main/webapp/formdefinitions/organismForm.json
+++ b/src/main/webapp/formdefinitions/organismForm.json
@@ -9,7 +9,16 @@
         {
             "name" : "parentOrganismId",
             "dataType" : "INTEGER",
-            "required" : false
+            "fieldType" : "SELECT_SINGLE",
+            "required" : false,
+            "nullValue" : "-1"
+        },
+        {
+            "name" : "hierarchyCategoryId",
+            "dataType" : "INTEGER",
+            "fieldType" : "SELECT_SINGLE",
+            "required" : true,
+            "nullValue" : "-1"
         },
         {
             "name" : "latinName",
@@ -25,6 +34,12 @@
             "name" : "localName",
             "dataType" : "STRING",
             "required" : false
+        },
+        {
+            "name" : "externalResourceIdentifier",
+            "dataType" : "STRING",
+            "fieldType" : "MULTIPLE_MAP",
+            "required" : false
         }
         
     ]
diff --git a/src/main/webapp/templates/organism.ftl b/src/main/webapp/templates/organism.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..b794e11a7be92ef5bd042afd5484c6941e74b46d
--- /dev/null
+++ b/src/main/webapp/templates/organism.ftl
@@ -0,0 +1,62 @@
+<#include "master.ftl">
+<#macro page_head>
+        <title>${i18nBundle.organisms}</title>
+        
+</#macro>
+<#macro custom_css>
+        <link href="/css/selectize.bootstrap3.css" rel="stylesheet" media="screen" />
+</#macro>
+<#macro custom_js>
+<script src="http://code.jquery.com/ui/1.10.3/jquery-ui.min.js"></script>
+<script src="/js/selectize.min.js"></script>
+<script type="text/javascript">
+$(document).ready(function() {
+	$('#organismSearch').selectize({
+		diacritics: true, // Support for international characters
+		maxItems: 1,
+		valueField: 'organismId',
+		labelField: 'organismName',
+		preload: true,
+		onType: function(value){
+			// Filter options on typed value 
+			console.log("Type value:" + value);
+		},
+		load: function(query, callback) {
+			// Load all options 
+			// If options is empty, load all
+			//if (!query.length) return callback();
+			
+			$.ajax({
+			    url: '/rest/organism/list' + encodeURIComponent(query),
+			    type: 'GET',
+			    error: function() {
+				callback();
+			    },
+			    success: function(res) {
+				callback(res.repositories.slice(0, 10));
+			    }
+			});
+			
+			console.log("Load query: " + query);
+		}
+		// Render: add breadcrumb to search result
+	});
+});
+</script>
+</#macro>
+
+<#macro page_contents>
+        <h1>${i18nBundle.organisms}</h1>
+        <div class="row">
+		<div class="col-md-6">
+			<select id="organismSearch">
+			
+			</select>
+		Søkefelt og organismetre
+		</div>
+		<div class="col-md-6">
+		Skjema for redigering av organisme
+		</div>
+	</div>
+</#macro>
+<@page_html/>
diff --git a/src/main/webapp/templates/organismDetails.ftl b/src/main/webapp/templates/organismDetails.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..e9416f39b5937dda3e771dfcb9c94a2c5fbe5519
--- /dev/null
+++ b/src/main/webapp/templates/organismDetails.ftl
@@ -0,0 +1,42 @@
+<#include "master.ftl">
+<#macro page_head>
+        <title>${organism.getLocalName(currentLocale.language)!""}<#if organism.tradeName?has_content> / ${organism.tradeName}</#if> (${organism.latinName!""})</title>
+</#macro>
+
+<#macro page_contents>
+        <h1>${organism.getLocalName(currentLocale.language)!""}/${organism.tradeName!""}/${organism.latinName!""}</h1>
+        <dl class="dl-horizontal">
+        
+          <#if parentOrganism?has_content>
+          <dt>${i18nBundle.parentOrganismId}</dt>
+	  <dd><a href="/organism?action=viewOrganism&organismId=${parentOrganism.organismId}">${parentOrganism.getLocalName(currentLocale.language)!""}/${parentOrganism.tradeName!""}/${parentOrganism.latinName!""}</a></dd>
+	  </#if>
+	  
+	  
+	  <dt>${i18nBundle.latinName}</dt>
+	  <dd>${organism.latinName!""}</dd>
+	  
+	  <dt>${i18nBundle.tradeName}</dt>
+	  <dd>${organism.tradeName!""}</dd>
+	  
+	  <dt>${i18nBundle.localName} (${currentLocale.getDisplayLanguage(currentLocale)})</dt>
+	  <dd>${organism.getLocalName(currentLocale.language)!""}</dd>
+	  
+	  <dt>${i18nBundle.hierarchyCategoryId}</dt>
+	  <dd>${hierarchyCategories.getName(organism.hierarchyCategoryId)}</dd>
+	  
+	  <dt>${i18nBundle.externalResources}</dt>
+	  <dd>
+	  <#list organism.organismExternalResourceSet as organismExternalResource>
+	  <a href="${organismExternalResource.resourceUrl}" target="new">${organismExternalResource.externalResource.name}</a><br/>
+	  </#list>
+	  </dd>
+	  
+	</dl>
+	<#if user.isSuperUser()>
+	<a href="/organism?action=editOrganismForm&organismId=${organism.organismId}" class="btn btn-default" role="button">${i18nBundle.edit}</a>
+	</#if>
+	<a href="/organism?action=listChildOrganisms&organismId=${organism.parentOrganismId!"null"}" class="btn btn-default" role="button">${i18nBundle.back}</a>
+        
+</#macro>
+<@page_html/>
diff --git a/src/main/webapp/templates/organismForm.ftl b/src/main/webapp/templates/organismForm.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..478ef66d142a72586cef609b2fd4830b485dc414
--- /dev/null
+++ b/src/main/webapp/templates/organismForm.ftl
@@ -0,0 +1,115 @@
+<#--
+  Copyright (c) 2014 Bioforsk <http://www.bioforsk.no/>. 
+
+  This file is part of VIPSLogic.
+  VIPSLogic is free software: you can redistribute it and/or modify
+  it under the terms of the GNU Affero General Public License as published by
+  the Free Software Foundation, either version 3 of the License, or
+  (at your option) any later version.
+
+  VIPSLogic is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU Affero General Public License for more details.
+
+  You should have received a copy of the GNU Affero General Public License
+  along with VIPSLogic.  If not, see <http://www.gnu.org/licenses/>.
+	
+--><#include "master.ftl">
+<#macro page_head>
+	<#if organism.organismId?has_content>
+		<title>${i18nBundle.edit} - ${organism.latinName!organism.tradeName}</title>
+	<#else>
+        	<title>${i18nBundle.newOrganism}</title>
+        </#if>
+</#macro>
+<#macro custom_js>
+	<script src="/js/resourcebundle.js"></script>
+	<script src="/js/validateForm.js"></script>
+	<script type="text/javascript">
+		// Load main form definition (for validation)
+		loadFormDefinition("organismForm");
+	</script>
+
+</#macro>
+<#macro page_contents>
+	<#if organism.organismId?has_content>
+        <h1>${i18nBundle.edit} - ${organism.latinName!organism.tradeName}</h1>
+        <#else>
+        <h1>${i18nBundle.newOrganism}</h1>
+        </#if>
+        <#if messageKey?has_content>
+	<div class="alert alert-success">${i18nBundle(messageKey)}</div>
+	</#if>
+        <div id="errorMsgEl" class="alert alert-danger" <#if !formValidation?has_content> style="display:none;"</#if>>
+		<#if formValidation?has_content>${formValidation.validationMessages?replace("\n", "<br>")}</#if>
+	</div>
+        <#assign formId = "organismForm">
+        <form id="${formId}" role="form" action="/organism?action=organismFormSubmit" method="POST" onsubmit="return true;" onsubmit2="try{ return validateForm(this);}catch(err){alert(err);return false;}">
+        <input type="hidden" name="organismId" value="${organism.organismId!"-1"}"/>
+        <div class="form-group">
+	    <label for="parentOrganismId">${i18nBundle.parentOrganismId}</label>
+	    <select class="form-control" name="parentOrganismId" onblur="validateField(this);">
+	    	<option value="-1">${i18nBundle.pleaseSelect} ${i18nBundle.parentOrganismId?lower_case}</option>
+		<#list allOrganisms?sort_by("latinName") as parentOrganism>
+			<option value="${parentOrganism.organismId}"
+				<#if (organism.parentOrganismId?has_content && organism.parentOrganismId == parentOrganism.organismId)
+					|| (parentOrganismId?has_content && parentOrganismId == parentOrganism.organismId)
+				>
+					selected="selected"
+				</#if>
+			>${parentOrganism.latinName!""}/${parentOrganism.tradeName!""}/${parentOrganism.getLocalName(currentLocale.language)!""} (${hierarchyCategories.getName(parentOrganism.hierarchyCategoryId)})</option>
+		</#list>
+	     </select>
+	</div>
+        <div class="form-group">
+	    <label for="latinName">${i18nBundle.latinName}</label>
+	    <input type="text" class="form-control" name="latinName" placeholder="${i18nBundle.latinName}" value="${organism.latinName!""}" onblur="validateField(this);" />
+	    <span class="help-block" id="${formId}_latinName_validation"></span>
+	</div>
+	<div class="form-group">
+	    <label for="tradeName">${i18nBundle.tradeName}</label>
+	    <input type="text" class="form-control" name="tradeName" placeholder="${i18nBundle.tradeName}" value="${organism.tradeName!""}" onblur="validateField(this);" />
+	    <span class="help-block" id="${formId}_tradeName_validation"></span>
+	</div>
+	<div class="form-group">
+	    <label for="Name">${i18nBundle.localName} (${currentLocale.getDisplayLanguage(currentLocale)})</label>
+	    <input type="text" class="form-control" name="localName" placeholder="${i18nBundle.localName}" value="<#if organism.organismId?has_content>${organism.getLocalName(currentLocale.language)!""}</#if>" onblur="validateField(this);" />
+	    <span class="help-block" id="${formId}_localName_validation"></span>
+	</div>
+	<div class="form-group">
+	    <label for="hierarchyCategoryId">${i18nBundle.hierarchyCategoryId}</label>
+	    <select class="form-control" name="hierarchyCategoryId" onblur="validateField(this);">
+	    	<option value="-1">${i18nBundle.pleaseSelect} ${i18nBundle.hierarchyCategoryId?lower_case}</option>
+		<#list hierarchyCategories.hierarchyCategoryIds as hierarchyCategoryId>
+			<option value="${hierarchyCategoryId}"
+				<#if (organism.hierarchyCategoryId?has_content && organism.hierarchyCategoryId == hierarchyCategoryId)>
+					selected="selected"
+				</#if>
+			>${hierarchyCategories.getName(hierarchyCategoryId)}</option>
+		</#list>
+	     </select>
+	</div>
+	<fieldset>
+	<h3>${i18nBundle.externalResources}</h3>
+	<#if organism.organismExternalResourceSet?has_content>
+		<#list organism.organismExternalResourceSet as organismExternalResource>
+		<div class="form-group">
+		    <label for="externalResourceIdentifier_${organismExternalResource.externalResource.externalResourceId}">${i18nBundle.externalResourceIdentifier} - ${organismExternalResource.externalResource.name} (${organismExternalResource.externalResource.uri}${organismExternalResource.externalResource.identifierTemplate})</label>
+		    <input type="text" class="form-control" name="externalResourceIdentifier_${organismExternalResource.externalResource.externalResourceId}" placeholder="${i18nBundle.externalResourceIdentifier}" value="${organismExternalResource.resourceIdentifier}" />
+		</div>
+		 </#list>
+	 </#if>
+	 <#list unreferencedExternalResources as externalResource>
+	<div class="form-group">
+	    <label for="externalResourceIdentifier_${externalResource.externalResourceId}">${i18nBundle.externalResourceIdentifier} - ${externalResource.name} (${externalResource.uri}${externalResource.identifierTemplate})</label>
+	    <input type="text" class="form-control" name="externalResourceIdentifier_${externalResource.externalResourceId}" placeholder="${i18nBundle.externalResourceIdentifier}" value="" />
+	</div>
+	 </#list>
+	 
+	</fieldset>
+        <button type="submit" class="btn btn-default">${i18nBundle.submit}</button>
+        <a href="/organism?action=listChildOrganisms&organismId=${organism.parentOrganismId!"null"}" class="btn btn-default" role="button">${i18nBundle.cancel}</a>
+	</form>
+</#macro>
+<@page_html/>
diff --git a/src/main/webapp/templates/organismList.ftl b/src/main/webapp/templates/organismList.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..4dde4687083c0a7dd29d7ffcab0eafcdfb8ebcc0
--- /dev/null
+++ b/src/main/webapp/templates/organismList.ftl
@@ -0,0 +1,70 @@
+<#--
+  Copyright (c) 2014 Bioforsk <http://www.bioforsk.no/>. 
+
+  This file is part of VIPSLogic.
+  VIPSLogic is free software: you can redistribute it and/or modify
+  it under the terms of the GNU Affero General Public License as published by
+  the Free Software Foundation, either version 3 of the License, or
+  (at your option) any later version.
+
+  VIPSLogic is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU Affero General Public License for more details.
+
+  You should have received a copy of the GNU Affero General Public License
+  along with VIPSLogic.  If not, see <http://www.gnu.org/licenses/>.
+	
+--><#include "master.ftl">
+<#macro page_head>
+        <title>${i18nBundle.organisms}<#if organism.organismId?has_content> - ${organism.localName!""}<#if organism.tradeName?has_content> / ${organism.tradeName}</#if> (${organism.latinName!""})</#if></title>
+</#macro>
+<#macro page_contents>
+        <h1>${i18nBundle.organisms}</h1>
+        <#if organism.organismId?has_content>
+        <h2>${organism.getLocalName(currentLocale.language)!""}<#if organism.tradeName?has_content> / ${organism.tradeName}</#if> (<i>${organism.latinName!""}</i>)</h2>
+        </#if>
+        <#if messageKey?has_content>
+		<div class="alert alert-success">${i18nBundle(messageKey)}</div>
+	</#if>
+	<#if user.isSuperUser()>
+	<a href="/organism?action=newOrganismForm&parentOrganismId=${organism.organismId!""}" class="btn btn-default" role="button">${i18nBundle.addNew}</a>
+	</#if>
+	<#if organism.organismId?has_content>
+	<a href="/organism?action=listChildOrganisms&organismId=${organism.parentOrganismId!""}" class="btn btn-default" role="button">${i18nBundle.up}</a>
+	</#if>
+        <div class="table-responsive">
+	<table class="table table-striped">
+		<thead>
+			<th>${i18nBundle.latinName}</th>
+			<th>${i18nBundle.localName}</th>
+			<#if tradeNamePresent>
+			<th>${i18nBundle.tradeName}</th>
+			</#if>
+			<th></th>
+		</thead>
+		<tbody>
+		<#list organism.childOrganisms as childOrganism>
+		    <tr>
+		    	<td>${childOrganism.latinName!""}</td>
+		    	<td>${childOrganism.getLocalName(currentLocale.language)}</td>
+		    	<#if tradeNamePresent>
+		    	<td>${childOrganism.tradeName!""}</td>
+		    	</#if>
+		    	<td>
+		    		<#if user.isSuperUser()>
+		    			<a href="/organism?action=editOrganismForm&organismId=${childOrganism.organismId}&parentOrganismId=${organism.organismId!""}" class="btn btn-default" role="button">${i18nBundle.edit}</a>
+		    			<#if ! childOrganism.childOrganisms?has_content || childOrganism.childOrganisms?size == 0>
+		    			<button type="button" class="btn btn-danger" onclick="if(confirm('${childOrganism.getLocalName(currentLocale.language)!""}/${childOrganism.tradeName!""}/${childOrganism.latinName!""}: ${i18nBundle.confirmDelete}')) {window.location.href='/organism?action=deleteOrganism&organismId=${childOrganism.organismId}';}">${i18nBundle.delete}</button>
+		    			</#if>
+		    		</#if>
+		    		<a href="/organism?action=viewOrganism&organismId=${childOrganism.organismId}" class="btn btn-default" role="button">${i18nBundle.details}</a>
+		    		<a href="/organism?action=listChildOrganisms&organismId=${childOrganism.organismId}" class="btn btn-default" role="button">${i18nBundle.children}</a>
+		    	</td>
+		    </tr>
+		</#list>
+		</tbody>
+        </table>
+        </div>
+</#macro>
+<@page_html/>
diff --git a/src/main/webapp/templates/userForm.ftl b/src/main/webapp/templates/userForm.ftl
index 70a8d193c5fb8a01b234f1922e9f5c022b72d100..af9cb601af1f7e0fc0f8f199020a46fa3c191946 100644
--- a/src/main/webapp/templates/userForm.ftl
+++ b/src/main/webapp/templates/userForm.ftl
@@ -5,6 +5,10 @@
 <#macro custom_js>
 	<script src="/js/resourcebundle.js"></script>
 	<script src="/js/validateForm.js"></script>
+	<script type="text/javascript">
+		// Load main form definition (for validation)
+		loadFormDefinition("userForm");
+	</script>
 </#macro>
 <#macro page_contents>
         <h1>${viewUser.firstName} ${viewUser.lastName}</h1>
diff --git a/src/test/java/no/bioforsk/vips/logic/session/UserBeanTest.java b/src/test/java/no/bioforsk/vips/logic/session/UserBeanTest.java
index d2855d071b1a1ee5628b4fee64e16276c5aa0144..3ad7e84a6a95bbce0a47bc42e63ff81ec5d81eda 100644
--- a/src/test/java/no/bioforsk/vips/logic/session/UserBeanTest.java
+++ b/src/test/java/no/bioforsk/vips/logic/session/UserBeanTest.java
@@ -69,7 +69,7 @@ public class UserBeanTest {
         ResourceBundle i18nBundle = ResourceBundle.getBundle("no.bioforsk.vips.logic.i18n.vipslogictexts",new Locale("no"));
         UserBean uBean = new UserBean();
         String result = uBean.getUserEmailVerificationBody(i18nBundle, "logic.vips.bioforsk.no",StringUtils.generateRandomAlphanumericString(30));
-        System.out.println(result);
+        //System.out.println(result);
         assert(true);
     }