diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..fdf344181aa2e620a74ceb60f909662c6d058f70
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,55 @@
+image: docker:stable
+image: ruby:2.7.5
+
+stages:
+  - build
+  - test
+  - deploy
+
+services:
+  - docker:dind
+  - postgres:13-alpine
+  - redis:6
+
+variables:
+  POSTGRES_DB: 'hyrax_test'
+  POSTGRES_DB_TEST: 'hyrax_test'
+  POSTGRES_USER: 'postgres'
+  POSTGRES_PASSWORD: 'password'
+  POSTGRES_HOST_AUTH_METHOD: trust
+
+build-job:
+  stage: build
+  before_script:
+    - apt-get update -qq && apt-get install -y -qq nodejs
+    - cd hyrax
+    - bundle install --jobs 4 --retry 3
+  script:
+    - export FITS_ROOT=~/rdms-hyrax/
+    - mkdir -p ${FITS_ROOT}
+    - wget -q https://github.com/harvard-lts/fits/releases/download/1.5.0/fits-1.5.0.zip -O ${FITS_ROOT}/fits-1.5.0.zip
+    - unzip -q ${FITS_ROOT}/fits-1.5.0.zip -d ${FITS_ROOT}/fits-1.5.0
+    - chmod a+x ${FITS_ROOT}/fits-1.5.0/fits.sh
+    - rm ${FITS_ROOT}/fits-1.5.0.zip
+    - bundle exec rake db:test:prepare
+
+test-job:
+  stage: test
+  coverage: '/coverage: \d+.\d+% of statements/'
+  before_script:
+    - apt-get update -qq && apt-get install -y -qq nodejs
+    - apt-get update -qq && apt-get install -y -qq software-properties-common
+    - apt-add-repository 'deb http://security.debian.org/debian-security stretch/updates main'
+    - apt-get update && apt-get -y install openjdk-8-jdk libmediainfo-dev
+    - update-alternatives --set java /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java
+    - cd hyrax
+    - bundle install --jobs 4 --retry 3
+  script:
+    - export FITS_PATH=~/rdms-hyrax/fits-1.3.0/fits.sh
+    - if test -f "lib/tasks/test_servers.rake"; then echo 'Running full tests' && bundle exec rake test:servers:start && bundle exec rspec && bundle exec rake test:servers:stop; else echo "Running limited tests"; bundle exec rspec --exclude-pattern "**/features/*_spec.rb"; fi
+
+deploy-job:
+  stage: deploy
+  script:
+    - echo "Deploying application..."
+    - echo "Application successfully deployed."
diff --git a/README.md b/README.md
index c88f355041a790be01cbf0c22818b025b0375ee0..6724d2e5b1b3b3e32cf2bcac9040b3ca7b12b480 100644
--- a/README.md
+++ b/README.md
@@ -26,44 +26,37 @@ You should see the containers being built and the services start.
 There are 2 `docker-compose` files provided in the repository, which build the containers running the services as shown above
 * [docker-compose.yml](https://gitlab.ruhr-uni-bochum.de/FDM/rdm-system/rdms/-/blob/master/docker-compose.yml) is the main docker-compose file. It builds all the core servcies required to run the application
 
-  
-
 * [docker-compose.override.yml](https://gitlab.ruhr-uni-bochum.de/FDM/rdm-system/rdms/-/blob/master/docker-compose.override.yml) is used along with the main [docker-compose.yml](https://gitlab.ruhr-uni-bochum.de/FDM/rdm-system/rdms/-/blob/master/docker-compose.yml) file in development, mainly to expose ports for the various services.
 
 ### Containers running in docker
 
 * [fcrepo](https://gitlab.ruhr-uni-bochum.de/FDM/rdm-system/rdms/-/blob/master/docker-compose.yml#L13-L22) is the container running the [Fedora 4 commons repository](https://wiki.duraspace.org/display/FEDORA47/Fedora+4.7+Documentation), an rdf document store. 
 
-  By default, this runs the fedora service on port 8080 internally in docker (http://fcrepo:8080/fcrepo/rest).
-
-  
+  By default, this runs the fedora service on port 8080 internally in docker.
+  http://fcrepo:8080/fcrepo/rest
 
 * [Solr container](https://gitlab.ruhr-uni-bochum.de/FDM/rdm-system/rdms/-/blob/master/docker-compose.yml#L24-L45) runs [SOLR](lucene.apache.org/solr/), an enterprise search server. 
 
-  By default, this runs the SOLR service on port 8983 internally in docker (http://solr:8983).
-
-  
+  By default, this runs the SOLR service on port 8983 internally in docker. 
+  http://solr:8983
 
 * [db containers](https://gitlab.ruhr-uni-bochum.de/FDM/rdm-system/rdms/-/blob/master/docker-compose.yml#L47-L76) running a postgres database for use by the Hyrax application (appdb) and Fedora (fcrepodb). 
 
   By default, this runs the database service on port 5432 internally in docker.
 
-  
-
 * [redis container](https://gitlab.ruhr-uni-bochum.de/FDM/rdm-system/rdms/-/blob/master/docker-compose.yml#L121-L132) running [redis](https://redis.io/), used by Hyrax to manage background tasks. 
 
   By default, this runs the redis service on port 6379 internally in docker.
 
-  
-
 * [app container](https://gitlab.ruhr-uni-bochum.de/FDM/rdm-system/rdms/-/blob/master/docker-compose.yml#L78-L96) sets up the Hyrax application, which is then used by 2 services - web and workers.
   
   * [Web container](https://gitlab.ruhr-uni-bochum.de/FDM/rdm-system/rdms/-/blob/master/docker-compose.yml#L98-L110) runs the application. 
   
-    By default, this runs on port 3000 internally in docker (http://web:3000). 
-  
-    This container runs [docker-entrypoint.sh](https://gitlab.ruhr-uni-bochum.de/FDM/rdm-system/rdms/-/blob/master/hyrax/docker-entrypoint.sh), on startup. The bash script 
-  
+    By default, this runs on port 3000 internally in docker.
+  http://web:3000 
+    
+  This container runs [docker-entrypoint.sh](https://gitlab.ruhr-uni-bochum.de/FDM/rdm-system/rdms/-/blob/master/hyrax/docker-entrypoint.sh), on startup. The bash script 
+    
     * creates the log folder
     * checks the bundle (and installs It in development)
     * does the database setup
@@ -75,14 +68,11 @@ There are 2 `docker-compose` files provided in the repository, which build the c
     
       * Creates the default admin set and collection types
     * Starts the rails server
-    
   
   * [Wokers container](https://gitlab.ruhr-uni-bochum.de/FDM/rdm-system/rdms/-/blob/master/docker-compose.yml#L112-L119) runs the background tasks, using [sidekiq](https://github.com/mperham/sidekiq) and redis. 
   
-    By default, this runs the worker service. 
-
     Hyrax processes long-running or particularly slow work in background jobs to speed up the web request/response cycle. When a user submits a file through a work (using the web or an import task), there a number of background jobs that are run, initilated by the hyrax actor stack, as explained [here](https://samvera.github.io/what-happens-deposit-2.0.html).
-    
+
     You can monitor the background workers using the materials data repository service at http://web:3000/sidekiq when logged in as an admin user. 
 
 
diff --git a/docker-compose.yml b/docker-compose.yml
index fccdd3df77f98ac341f19d7f64401ba34286fcdc..eb4a70d97e58d027a50c480bcda22dde1bc5c415 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -166,6 +166,8 @@ services:
       - PHP_MEMORY_LIMIT=2048M
     env_file:
       - .env
+    ports:
+      - 9000:9000
     networks:
       internal:
         aliases:
diff --git a/hyrax/.gitignore b/hyrax/.gitignore
index 81452db9256834c30e6e2a6881468c0b9a09eb54..02f3113a5adb271deab4ecbed24a644c3ed02a7b 100644
--- a/hyrax/.gitignore
+++ b/hyrax/.gitignore
@@ -16,6 +16,7 @@
 /tmp/*
 !/log/.keep
 !/tmp/.keep
+/fits.log
 
 # Ignore uploaded files in development
 /storage/*
diff --git a/hyrax/Dockerfile b/hyrax/Dockerfile
index ca5d1ddd9c59b74957d9c2524ae3ce941a971f7e..389e81998778303c3423c000fede8c06d9262e81 100644
--- a/hyrax/Dockerfile
+++ b/hyrax/Dockerfile
@@ -1,4 +1,4 @@
-FROM ruby:2.7-buster
+FROM ruby:2.7.5-buster
 
 # Setup build variables
 ARG RAILS_ENV
diff --git a/hyrax/Gemfile b/hyrax/Gemfile
index d1dfee0b7f4ab1dc042066d22709b5bd379042c8..5da32b463b8ef7a0f0ab78929f158a5f4bff5108 100644
--- a/hyrax/Gemfile
+++ b/hyrax/Gemfile
@@ -15,6 +15,7 @@ gem 'sass-rails', '~> 5.0'
 gem 'uglifier', '>= 1.3.0'
 # See https://github.com/rails/execjs#readme for more supported runtimes
 # gem 'mini_racer', platforms: :ruby
+gem 'therubyracer'
 
 # Use CoffeeScript for .coffee assets and views
 gem 'coffee-rails', '~> 4.2'
@@ -68,6 +69,7 @@ group :development, :test do
 end
 
 gem 'rsolr', '>= 1.0', '< 3'
+gem 'solrizer', '~> 4.1'
 gem 'bootstrap-sass', '~> 3.0'
 gem 'twitter-typeahead-rails', '0.11.1.pre.corejavascript'
 gem 'jquery-rails'
@@ -84,4 +86,6 @@ gem 'bootstrap-datepicker-rails'
 gem 'pg'
 
 gem 'riiif', '~> 2.3'
+gem 'rinku'
 gem 'coveralls', require: false
+gem 'database_cleaner'
diff --git a/hyrax/Gemfile.lock b/hyrax/Gemfile.lock
index 9b11bbf4253ca53d7fc2d41f00c67e45ad684e4e..2425189e2366cf60538b97b67876bb498c40aadb 100644
--- a/hyrax/Gemfile.lock
+++ b/hyrax/Gemfile.lock
@@ -181,6 +181,12 @@ GEM
       thor (>= 0.19.4, < 2.0)
       tins (~> 1.6)
     crass (1.0.6)
+    database_cleaner (2.0.1)
+      database_cleaner-active_record (~> 2.0.0)
+    database_cleaner-active_record (2.0.1)
+      activerecord (>= 5.a)
+      database_cleaner-core (~> 2.0.0)
+    database_cleaner-core (2.0.1)
     declarative (0.0.20)
     declarative-builder (0.1.0)
       declarative-option (< 0.2.0)
@@ -531,6 +537,7 @@ GEM
       rdf (~> 3.0)
     legato (0.7.0)
       multi_json
+    libv8 (3.16.14.19)
     link_header (0.0.8)
     linkeddata (3.2.0)
       json-ld (~> 3.2)
@@ -732,6 +739,7 @@ GEM
       redis (>= 3.0.4)
     redlock (1.2.2)
       redis (>= 3.0.0, < 5.0)
+    ref (2.0.0)
     reform (2.5.0)
       disposable (>= 0.4.2, < 0.5.0)
       representable (>= 2.4.0, < 3.1.0)
@@ -755,6 +763,7 @@ GEM
       deprecation (>= 1.0.0)
       iiif-image-api (>= 0.1.0)
       railties (>= 4.2, < 7)
+    rinku (2.0.6)
     rsolr (2.5.0)
       builder (>= 2.1.2)
       faraday (>= 0.9, < 3, != 2.0.0)
@@ -842,6 +851,10 @@ GEM
       retriable
       ruby-progressbar
       rubyzip
+    solrizer (4.1.0)
+      activesupport
+      nokogiri
+      xml-simple
     sparql (3.2.1)
       builder (~> 3.2)
       ebnf (~> 2.2)
@@ -878,6 +891,9 @@ GEM
     temple (0.8.2)
     term-ansicolor (1.7.1)
       tins (~> 1.0)
+    therubyracer (0.12.3)
+      libv8 (~> 3.16.14.15)
+      ref
     thor (1.2.1)
     thread_safe (0.3.6)
     tilt (2.0.10)
@@ -929,6 +945,8 @@ GEM
     websocket-driver (0.7.5)
       websocket-extensions (>= 0.1.0)
     websocket-extensions (0.1.5)
+    xml-simple (1.1.9)
+      rexml
     xpath (3.2.0)
       nokogiri (~> 1.8)
 
@@ -944,6 +962,7 @@ DEPENDENCIES
   chromedriver-helper
   coffee-rails (~> 4.2)
   coveralls
+  database_cleaner
   devise
   devise-guests (~> 0.6)
   factory_bot_rails
@@ -958,15 +977,18 @@ DEPENDENCIES
   rails (~> 5.2.6)
   redis (~> 4.0)
   riiif (~> 2.3)
+  rinku
   rsolr (>= 1.0, < 3)
   rspec-rails
   sass-rails (~> 5.0)
   selenium-webdriver
   sidekiq
   solr_wrapper (>= 0.3)
+  solrizer (~> 4.1)
   spring
   spring-watcher-listen (~> 2.0.0)
   sqlite3 (~> 1.3.0)
+  therubyracer
   turbolinks (~> 5)
   twitter-typeahead-rails (= 0.11.1.pre.corejavascript)
   tzinfo-data
diff --git a/hyrax/app/actors/hyrax/actors/crc_dataset_actor.rb b/hyrax/app/actors/hyrax/actors/crc_dataset_actor.rb
new file mode 100644
index 0000000000000000000000000000000000000000..d6d851776d0995e1d46ab9862802955733bcaf33
--- /dev/null
+++ b/hyrax/app/actors/hyrax/actors/crc_dataset_actor.rb
@@ -0,0 +1,8 @@
+# Generated via
+#  `rails generate hyrax:work CrcDataset`
+module Hyrax
+  module Actors
+    class CrcDatasetActor < Hyrax::Actors::BaseActor
+    end
+  end
+end
diff --git a/hyrax/app/assets/javascripts/application.js b/hyrax/app/assets/javascripts/application.js
index 40590c10450e4542b40fecf848fdb3f86c516028..800fe2aab4b30ff2295161fff885204353e261d9 100644
--- a/hyrax/app/assets/javascripts/application.js
+++ b/hyrax/app/assets/javascripts/application.js
@@ -1,8 +1,8 @@
 // This is a manifest file that'll be compiled into application.js, which will include all the files
 // listed below.
 //
-// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, or any plugin's
-// vendor/assets/javascripts directory can be referenced here using a relative path.
+// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
+// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
 //
 // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
 // compiled file. JavaScript code in this file should be added after the last require_* statement.
@@ -10,16 +10,16 @@
 // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
 // about supported directives.
 //
-//= 
-//= require activestorage
-//= require turbolinks
-//
-// Required by Blacklight
 //= require jquery
 //= require jquery_ujs
 //= require dataTables/jquery.dataTables
 //= require dataTables/bootstrap/3/jquery.dataTables.bootstrap
+//= require bootstrap-datepicker
+//
+// Required by Blacklight
 //= require blacklight/blacklight
+//= require hydra-editor/editMetadata
+//= require tinymce
 
 //= require_tree .
 //= require hyrax
diff --git a/hyrax/app/assets/javascripts/date_picker_options.js b/hyrax/app/assets/javascripts/date_picker_options.js
new file mode 100644
index 0000000000000000000000000000000000000000..6da1cd897618e371bc5bb1470d9a6802dae2885c
--- /dev/null
+++ b/hyrax/app/assets/javascripts/date_picker_options.js
@@ -0,0 +1,3 @@
+Blacklight.onLoad(function() {
+  $.fn.datepicker.defaults.format = "dd/mm/yyyy";
+});
diff --git a/hyrax/app/assets/javascripts/manage_repeating_nested_fields.js b/hyrax/app/assets/javascripts/manage_repeating_nested_fields.js
new file mode 100644
index 0000000000000000000000000000000000000000..9e5d1c5ea5b61187c115d055dc26ee7a27b34078
--- /dev/null
+++ b/hyrax/app/assets/javascripts/manage_repeating_nested_fields.js
@@ -0,0 +1,38 @@
+Blacklight.onLoad(function() {
+  $('.multi-nested').manage_nested_fields();
+});
+
+(function($){
+  var DEFAULTS = {
+    /* callback to run after add is called */
+    add:    null,
+    /* callback to run after remove is called */
+    remove: null,
+
+    controlsHtml:      '<span class=\"input-group-btn field-controls\">',
+    fieldWrapperClass: '.field-wrapper',
+    warningClass:      '.has-warning',
+    listClass:         '.listing',
+    removeInputClass:   '.remove-hidden',
+
+    addHtml:           '<button type=\"button\" class=\"btn btn-link add\"><span class=\"glyphicon glyphicon-plus\"></span><span class="controls-add-text"></span></button>',
+    addText:           'Add another',
+
+    // removeHtml:        '<button type=\"button\" class=\"btn btn-link remove\"><span class=\"glyphicon glyphicon-remove\"></span><span class="controls-remove-text"></span> <span class=\"sr-only\"> previous <span class="controls-field-name-text">field</span></span></button>',
+    // removeText:         'Remove',
+
+    labelControls:      true
+  };
+
+  $.fn.manage_nested_fields = function(option) {
+    // var nested_editor = require('./nested_field_manager')
+    return this.each(function() {
+      var $this = $(this);
+      var data  = $this.data('manage_nested_fields');
+      var options = $.extend({}, DEFAULTS, $this.data(), typeof option == 'object' && option);
+
+      if (!data) $this.data('manage_nested_fields', (data = new NestedFieldManager(this, options)));
+    })
+  }
+
+})(jQuery);
diff --git a/hyrax/app/assets/javascripts/nested_field_manager.js b/hyrax/app/assets/javascripts/nested_field_manager.js
new file mode 100644
index 0000000000000000000000000000000000000000..16708e528de744d039f5702d4228998c71139acb
--- /dev/null
+++ b/hyrax/app/assets/javascripts/nested_field_manager.js
@@ -0,0 +1,170 @@
+'use strict';
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+var NestedFieldManager = function () {
+    function NestedFieldManager(element, options) {
+        _classCallCheck(this, NestedFieldManager);
+
+        this.element = $(element);
+        this.options = options;
+        this.warningClass = options.warningClass;
+        this.listClass = options.listClass;
+        this.fieldWrapperClass = options.fieldWrapperClass;
+        this.removeInputClass = options.removeInputClass;
+
+        this.init();
+    }
+
+    _createClass(NestedFieldManager, [{
+        key: 'init',
+        value: function init() {
+            // this._addInitialClasses();
+            this._addAriaLiveRegions();
+            this._attachEvents();
+            this._addCallbacks();
+        }
+    }, {
+        key: '_addAriaLiveRegions',
+        value: function _addAriaLiveRegions() {
+            $(this.element).find('.listing').attr('aria-live', 'polite');
+        }
+    }, {
+        key: '_attachEvents',
+        value: function _attachEvents() {
+            var _this = this;
+
+            this.element.on('click', '.remove', function (e) {
+                return _this.removeFromList(e);
+            });
+            this.element.on('click', '.add', function (e) {
+                return _this.addToList(e);
+            });
+        }
+    }, {
+        key: '_addCallbacks',
+        value: function _addCallbacks() {
+            this.element.bind('manage_nested_fields:add', this.options.add);
+            this.element.bind('manage_nested_fields:remove', this.options.remove);
+        }
+    }, {
+        key: 'addToList',
+        value: function addToList(event) {
+            event.preventDefault();
+            var $listing = $(event.target).closest('.multi-nested').find(this.listClass);
+            var $listElements = $listing.children('li');
+            var $activeField = $listElements.last();
+            var $newId = $listElements.length;
+            var $currentId = $newId - 1;
+            if (this.inputIsEmpty($activeField)) {
+                this.displayEmptyWarning();
+                $activeField.removeAttr('style');
+                // $activeField.find('.remove-box').val('0');
+            } else {
+                this.clearEmptyWarning();
+                $listing.append(this._newField($activeField, $currentId, $newId));
+            }
+            this._manageFocus();
+        }
+    }, {
+        key: 'inputIsEmpty',
+        value: function inputIsEmpty($activeField) {
+            var $children = $activeField.find('.form-control').not(':hidden');
+            var empty = 0;
+            $children.each(function () {
+                if ($.trim(this.value) === "") empty++;
+            });
+            return empty === $children.length;
+        }
+    }, {
+        key: 'clearEmptyWarning',
+        value: function clearEmptyWarning() {
+            var $listing = $(this.listClass, this.element);
+            $listing.children(this.warningClass).remove();
+        }
+    }, {
+        key: 'displayEmptyWarning',
+        value: function displayEmptyWarning() {
+            var $listing = $(this.listClass, this.element);
+            var $warningMessage = $("<div class=\'message has-warning\'>cannot add another with empty field</div>");
+            $listing.children(this.warningClass).remove();
+            $listing.append($warningMessage);
+        }
+    }, {
+        key: '_newField',
+        value: function _newField($activeField, $currentId, $newId) {
+            var $newField = this.createNewField($activeField, $currentId, $newId);
+            return $newField;
+        }
+    }, {
+        key: '_manageFocus',
+        value: function _manageFocus() {
+            $(this.element).find(this.listClass).children('li:visible:last').find('.form-control').filter(':visible:first').focus();
+        }
+    }, {
+        key: 'createNewField',
+        value: function createNewField($activeField, $currentId, $newId) {
+            var $newField = $activeField.clone();
+            this.updateIndexInLabel($newField, $currentId, $newId);
+            var $newChildren = $newField.find('.form-control');
+            $newChildren.val('').removeProp('required').removeAttr('style');
+            this.updateIndexInId($newChildren, $currentId, $newId);
+            this.updateIndexInName($newChildren, $currentId, $newId);
+            $newChildren.first().focus();
+            this.element.trigger("manage_nested_fields:add", $newChildren.first());
+            return $newField;
+        }
+    }, {
+        key: 'updateIndexInLabel',
+        value: function updateIndexInLabel($newField, $currentId, $newId) {
+            // Modify name in label
+            var currentLabelPart = 'attributes_' + $currentId + '_';
+            var newLabelPart = 'attributes_' + $newId + '_';
+            $newField.find('label').each(function () {
+                var currentLabel = $(this).attr('for');
+                var newLabel = currentLabel.replace(currentLabelPart, newLabelPart);
+                $(this).attr('for', newLabel);
+            });
+            return $newField;
+        }
+    }, {
+        key: 'updateIndexInId',
+        value: function updateIndexInId($newChildren, $currentId, $newId) {
+            // modify id and name in newChildren
+            var $currentIdPart = new RegExp('attributes_' + $currentId + '_');
+            var $newIdPart = 'attributes_' + $newId + '_';
+            $newChildren.each(function () {
+                var $currentId = $(this).attr('id');
+                var $newId = $currentId.replace($currentIdPart, $newIdPart);
+                $(this).attr('id', $newId);
+            });
+            return $newChildren;
+        }
+    }, {
+        key: 'updateIndexInName',
+        value: function updateIndexInName($newChildren, $currentId, $newId) {
+            // modify id and name in newChildren
+            var $currentNamePart = new RegExp('[' + $currentId + ']');
+            var $newnamePart = '[' + $newId + ']';
+            $newChildren.each(function () {
+                var $currentName = $(this).attr('name');
+                var $newName = $currentName.replace($currentNamePart, $newnamePart);
+                $(this).attr('name', $newName);
+            });
+            return $newChildren;
+        }
+    }, {
+        key: 'removeFromList',
+        value: function removeFromList(event) {
+            event.preventDefault();
+            var $activeField = $(event.target).parents(this.fieldWrapperClass);
+            $activeField.find(this.removeInputClass).val('1');
+            $activeField.hide();
+            this._manageFocus();
+        }
+    }]);
+
+    return NestedFieldManager;
+}();
\ No newline at end of file
diff --git a/hyrax/app/assets/stylesheets/rdms.scss b/hyrax/app/assets/stylesheets/rdms.scss
new file mode 100644
index 0000000000000000000000000000000000000000..b2a12769b45bc2058b0330ba524f86f31f2d64b4
--- /dev/null
+++ b/hyrax/app/assets/stylesheets/rdms.scss
@@ -0,0 +1,45 @@
+//-------------- colours ---------------
+$rdms-grey: #F4F4F4;
+$rdms-green: #AFCA0B;
+$rdms-green-light: #BEEB7D;
+
+//-------------- form css --------------
+form .listing,
+form .inner-listing {
+    list-style: none;
+}
+
+form .row {
+    padding-bottom: 5px;
+}
+
+form .field-wrapper:not(:last-child) {
+    padding-bottom: 1em;
+    margin-bottom: 1em;
+    border-bottom: 2px solid $rdms-green;
+}
+
+.multi-nested .form-group,
+.multi-nested .listing {
+    margin-bottom: 0px;
+}
+
+.multi-nested .btn {
+    padding-top: 0px;
+}
+
+.multi-nested {
+    margin-bottom: 1em;
+    background-color: $rdms-grey;
+    padding: 10px;
+}
+
+form .field-wrapper label[required="required"]::after {
+    content: " *";
+    color: red;
+}
+
+
+.custom_field .listing li:first-of-type{
+    width: 100% !important;
+}
diff --git a/hyrax/app/controllers/catalog_controller.rb b/hyrax/app/controllers/catalog_controller.rb
index 364047613e262a1c698a83a0b6a0d9dbfbd9a66d..66fc250a7e59960abca8b9b83da402910e791236 100644
--- a/hyrax/app/controllers/catalog_controller.rb
+++ b/hyrax/app/controllers/catalog_controller.rb
@@ -50,8 +50,8 @@ class CatalogController < ApplicationController
     #   The ordering of the field names is the order of the display
     config.add_facet_field "human_readable_type_sim", label: "Type", limit: 5
     config.add_facet_field "resource_type_sim", label: "Resource Type", limit: 5
-    config.add_facet_field "creator_sim", limit: 5
-    config.add_facet_field "contributor_sim", label: "Contributor", limit: 5
+    # config.add_facet_field "creator_sim", limit: 5
+    # config.add_facet_field "contributor_sim", label: "Contributor", limit: 5
     config.add_facet_field "keyword_sim", limit: 5
     config.add_facet_field "subject_sim", limit: 5
     config.add_facet_field "language_sim", limit: 5
@@ -60,6 +60,13 @@ class CatalogController < ApplicationController
     config.add_facet_field "file_format_sim", limit: 5
     config.add_facet_field "member_of_collection_ids_ssim", limit: 5, label: 'Collections', helper_method: :collection_title_by_id
 
+    facet_fields = (DatasetIndexer.facet_fields +
+      CrcDatasetIndexer.facet_fields
+    ).uniq
+    facet_fields.each do |fld|
+      config.add_facet_field fld, limit: 5
+    end
+
     # The generic_type and depositor are not displayed on the facet list
     # They are used to give a label to the filters that comes from the user profile
     config.add_facet_field "generic_type_sim", if: false
@@ -76,8 +83,8 @@ class CatalogController < ApplicationController
     config.add_index_field "description_tesim", itemprop: 'description', helper_method: :iconify_auto_link
     config.add_index_field "keyword_tesim", itemprop: 'keywords', link_to_search: "keyword_sim"
     config.add_index_field "subject_tesim", itemprop: 'about', link_to_search: "subject_sim"
-    config.add_index_field "creator_tesim", itemprop: 'creator', link_to_search: "creator_sim"
-    config.add_index_field "contributor_tesim", itemprop: 'contributor', link_to_search: "contributor_sim"
+    # config.add_index_field "creator_tesim", itemprop: 'creator', link_to_search: "creator_sim"
+    # config.add_index_field "contributor_tesim", itemprop: 'contributor', link_to_search: "contributor_sim"
     config.add_index_field "proxy_depositor_ssim", label: "Depositor", helper_method: :link_to_profile
     config.add_index_field "depositor_tesim", label: "Owner", helper_method: :link_to_profile
     config.add_index_field "publisher_tesim", itemprop: 'publisher', link_to_search: "publisher_sim"
@@ -114,6 +121,13 @@ class CatalogController < ApplicationController
     config.add_show_field "format_tesim"
     config.add_show_field "identifier_tesim"
 
+    show_fields = (DatasetIndexer.show_fields +
+      CrcDatasetIndexer.show_fields
+    ).uniq
+    show_fields.each do |fld|
+      config.add_show_field fld
+    end
+
     # "fielded" search configuration. Used by pulldown among other places.
     # For supported keys in hash, see rdoc for Blacklight::SearchFields
     #
diff --git a/hyrax/app/controllers/hyrax/crc_datasets_controller.rb b/hyrax/app/controllers/hyrax/crc_datasets_controller.rb
new file mode 100644
index 0000000000000000000000000000000000000000..fb529f4eb055959e0ed5b3eae34a7349a2afa445
--- /dev/null
+++ b/hyrax/app/controllers/hyrax/crc_datasets_controller.rb
@@ -0,0 +1,14 @@
+# Generated via
+#  `rails generate hyrax:work CrcDataset`
+module Hyrax
+  # Generated controller for CrcDataset
+  class CrcDatasetsController < ApplicationController
+    # Adds Hyrax behaviors to the controller.
+    include Hyrax::WorksControllerBehavior
+    include Hyrax::BreadcrumbsForWorks
+    self.curation_concern_type = ::CrcDataset
+
+    # Use this line if you want to use a custom presenter
+    self.show_presenter = Hyrax::CrcDatasetPresenter
+  end
+end
diff --git a/hyrax/app/forms/hyrax/crc_dataset_form.rb b/hyrax/app/forms/hyrax/crc_dataset_form.rb
new file mode 100644
index 0000000000000000000000000000000000000000..1e1407a79da21d5d13dcf3108d2288b485404c4c
--- /dev/null
+++ b/hyrax/app/forms/hyrax/crc_dataset_form.rb
@@ -0,0 +1,147 @@
+# Generated via
+#  `rails generate hyrax:work CrcDataset`
+module Hyrax
+  # Generated form for CrcDataset
+  class CrcDatasetForm < Hyrax::Forms::WorkForm
+    self.model_class = ::CrcDataset
+    self.terms -= [
+      # Fields not interested in
+      :creator, :contributor, :rights_notes, :rights_statement,
+      :access_right, :date_created, :identifier, :based_near, :related_url, :source,
+      # Fields interested in, but removing to re-order
+      :title, :alternative_title, :resource_type, :description, :abstract, :keyword, :license,
+      :publisher, :subject, :language,
+      # Fields that are not displayed
+      # :import_url, :date_modified, :date_uploaded, :depositor, :bibliographic_citation, :label, :relative_path
+    ]
+
+    self.terms += [
+      # Adding all fields in order of display in form
+      :doi,
+      :title,
+      :alternative_title,
+      :complex_person,
+      :abstract,
+      :description,
+      :complex_identifier,
+      :complex_date,
+      :modality,
+      :complex_subject,
+      :approval_number,
+      :keyword,
+      :crc_resource_type,
+      :subject,
+      :publisher,
+      :language,
+      :complex_funding_reference,
+      :complex_relation,
+      :software_version,
+      :extra_information
+    ]
+
+    self.required_fields -= [
+      # Fields not interested in
+      :creator, :rights_statement,
+      # Fields interested in, but removing to re-order
+      :title
+    ]
+
+    self.required_fields += [
+      # Adding all required fields in order of display in form
+      :title, :complex_person, :abstract, :keyword, :crc_resource_type, :license
+    ]
+
+    protected
+
+    def self.permitted_date_params
+      [:id,
+       :_destroy,
+       {
+         date: [],
+         description: []
+       }
+      ]
+    end
+
+    def self.permitted_identifier_params
+      [:id,
+       :_destroy,
+       {
+         identifier: [],
+         scheme: [],
+         label: []
+       }
+      ]
+    end
+
+    def self.permitted_person_params
+      [:id,
+       :_destroy,
+       :corresponding_author,
+       :display_order,
+       {
+         last_name: [],
+         first_name: [],
+         name: [],
+         role: [],
+         orcid: [],
+         affiliation: [],
+       }
+      ]
+    end
+
+    def self.permitted_relation_params
+      [:id,
+       :_destroy,
+       {
+         title: [],
+         url: [],
+         complex_identifier_attributes: permitted_identifier_params,
+         relationship: []
+       }
+      ]
+    end
+
+    def self.permitted_fundref_params
+      [:id,
+       :_destroy,
+       {
+         funder_identifier: [],
+         funder_name: [],
+         award_number: [],
+         award_uri: [],
+         award_title: []
+       }
+      ]
+    end
+
+    def self.permitted_subject_params
+      [:id,
+       :_destroy,
+       {
+         subject_identifier: [],
+         subject_species: [],
+         subject_type: [],
+         subject_sex: [],
+         subject_age: [],
+       }
+      ]
+    end
+
+    def self.build_permitted_params
+      permitted = super
+      permitted << :doi
+      permitted << :modality
+      permitted << :crc_resource_type
+      permitted << :approval_number
+      permitted << :extra_information
+      permitted << :software_version
+      permitted << { complex_date_attributes: permitted_date_params }
+      permitted << { complex_identifier_attributes: permitted_identifier_params }
+      permitted << { complex_person_attributes: permitted_person_params }
+      permitted << { complex_relation_attributes: permitted_relation_params }
+      permitted << { complex_funding_reference_attributes: permitted_fundref_params }
+      permitted << { complex_subject_attributes: permitted_subject_params }
+    end
+  end
+end
diff --git a/hyrax/app/forms/hyrax/dataset_form.rb b/hyrax/app/forms/hyrax/dataset_form.rb
index ff6de5d22c9f0e4ef423b2e6e644243a5aeb1ea4..a6e4a731b23561b1cce324454acff9dc38e09e6d 100644
--- a/hyrax/app/forms/hyrax/dataset_form.rb
+++ b/hyrax/app/forms/hyrax/dataset_form.rb
@@ -4,6 +4,121 @@ module Hyrax
   # Generated form for Dataset
   class DatasetForm < Hyrax::Forms::WorkForm
     self.model_class = ::Dataset
-    self.terms += [:resource_type]
+
+    self.terms -= [
+      # Fields not interested in
+      :creator, :contributor, :rights_notes, :rights_statement,
+      :access_right, :date_created, :identifier, :based_near, :related_url, :source,
+      # Fields interested in, but removing to re-order
+      :title, :alternative_title, :resource_type, :description, :abstract, :keyword, :license,
+      :publisher, :subject, :language,
+      # Fields that are not displayed
+      # :import_url, :date_modified, :date_uploaded, :depositor, :bibliographic_citation, :label, :relative_path
+    ]
+
+    self.terms += [
+      # Adding all fields in order of display in form
+      :doi,
+      :title,
+      :alternative_title,
+      :complex_person,
+      :resource_type,
+      :abstract,
+      :description,
+      :keyword,
+      :subject,
+      :publisher,
+      :language,
+      :complex_date,
+      :complex_identifier,
+      :complex_funding_reference,
+      :complex_relation,
+    ]
+
+    self.required_fields -= [
+      # Fields not interested in
+      :creator, :rights_statement,
+      # Fields interested in, but removing to re-order
+      :title
+    ]
+
+    self.required_fields += [
+      # Adding all required fields in order of display in form
+      :title, :complex_person, :abstract, :keyword, :license
+    ]
+
+    protected
+
+    def self.permitted_date_params
+      [:id,
+       :_destroy,
+       {
+         date: [],
+         description: []
+       }
+      ]
+    end
+
+    def self.permitted_identifier_params
+      [:id,
+       :_destroy,
+       {
+         identifier: [],
+         scheme: [],
+         label: []
+       }
+      ]
+    end
+
+    def self.permitted_person_params
+      [:id,
+        :_destroy,
+        :corresponding_author,
+        :display_order,
+       {
+         last_name: [],
+         first_name: [],
+         name: [],
+         role: [],
+         orcid: [],
+         affiliation: [],
+       }
+      ]
+    end
+
+    def self.permitted_relation_params
+      [:id,
+       :_destroy,
+       {
+         title: [],
+         url: [],
+         complex_identifier_attributes: permitted_identifier_params,
+         relationship: []
+       }
+      ]
+    end
+
+    def self.permitted_fundref_params
+      [:id,
+       :_destroy,
+       {
+         funder_identifier: [],
+         funder_name: [],
+         award_number: [],
+         award_uri: [],
+         award_title: []
+       }
+      ]
+    end
+
+    def self.build_permitted_params
+      permitted = super
+      permitted << :doi
+      permitted << { complex_date_attributes: permitted_date_params }
+      permitted << { complex_identifier_attributes: permitted_identifier_params }
+      permitted << { complex_person_attributes: permitted_person_params }
+      permitted << { complex_relation_attributes: permitted_relation_params }
+      permitted << { complex_funding_reference_attributes: permitted_fundref_params }
+    end
   end
 end
diff --git a/hyrax/app/indexers/complex_field/date_indexer.rb b/hyrax/app/indexers/complex_field/date_indexer.rb
new file mode 100644
index 0000000000000000000000000000000000000000..0753f9c445cf570dc811923c9da8d2e695bab264
--- /dev/null
+++ b/hyrax/app/indexers/complex_field/date_indexer.rb
@@ -0,0 +1,113 @@
+module ComplexField
+  module DateIndexer
+    def generate_solr_document
+      super.tap do |solr_doc|
+        index_date(solr_doc)
+      end
+    end
+
+    def index_date(solr_doc)
+      return if object.complex_date.blank?
+      # json object as complex_date displayable
+      solr_doc[Solrizer.solr_name('complex_date', :displayable)] = object.complex_date.to_json
+      # date as complex_date searchable
+      dates = object.complex_date.map { |d| d.date.reject(&:blank?) }.flatten
+      # cope with just a year being supplied
+      begin
+        dates_utc = dates.map { |d| d.tr('0-9a-zA-Z', '0-9a-zA-Z') }.map { |d| d.length <= 4 ? DateTime.strptime(d, '%Y').utc.iso8601 : DateTime.parse(d).utc.iso8601 } unless dates.blank?
+      rescue ArgumentError
+        dates_utc = dates.map { |d| d.tr('0-9a-zA-Z', '0-9a-zA-Z') }.map { |d| DateTime.parse("#{d}-01").utc.iso8601 } unless dates.blank?
+      end
+      solr_doc[Solrizer.solr_name('complex_date', :stored_searchable, type: :date)] = dates_utc unless dates.blank?
+      solr_doc[Solrizer.solr_name('complex_date', :dateable)] = dates_utc unless dates.blank?
+      # add year
+      years = dates_utc.map { |d| DateTime.parse(d).strftime('%Y') } unless dates.blank?
+      solr_doc[Solrizer.solr_name('complex_year', :stored_searchable)] = years unless dates.blank?
+      solr_doc[Solrizer.solr_name('complex_year', :facetable)] = years unless dates.blank?
+      object.complex_date.each do |d|
+        next if d.date.reject(&:blank?).blank?
+        label = 'other'
+        unless d.description.blank?
+          # Finding its display label for indexing
+          term = DateService.new.find_by_id(d.description.first)
+          label = term['label'] if term.any?
+        end
+        label = label.downcase.tr(' ', '_')
+  	    # Not indexing date as sortbale as it needs to be single valued
+        # fld_name = Solrizer.solr_name("complex_date_#{label.downcase.tr(' ', '_')}", :stored_sortable, type: :date)
+        # solr_doc[fld_name] = [] unless solr_doc.include?(fld_name)
+        # solr_doc[fld_name] << DateTime.parse(d.date.reject(&:blank?).first).utc.iso8601
+        # solr_doc[fld_name] = solr_doc[fld_name].uniq.first
+        # date as complex_date_type dateable
+        vals = d.date.reject(&:blank?)
+        fld_name = Solrizer.solr_name("complex_date_#{label}", :dateable)
+        solr_doc[fld_name] = [] unless solr_doc.include?(fld_name)
+        begin
+          dates_utc = vals.map { |d| d.tr('0-9a-zA-Z', '0-9a-zA-Z') }.map { |d| d.length <= 4 ? DateTime.strptime(d, '%Y').utc.iso8601 : DateTime.parse(d).utc.iso8601 } unless dates.blank?
+        rescue ArgumentError
+          dates_utc = vals.map { |d| d.tr('0-9a-zA-Z', '0-9a-zA-Z') }.map { |d| DateTime.parse("#{d}-01").utc.iso8601 } unless dates.blank?
+        end
+        solr_doc[fld_name] << dates_utc unless dates_utc.blank?
+        solr_doc[fld_name].flatten!
+        # Add years
+        year_fld = Solrizer.solr_name("complex_year_#{label}", :facetable)
+        years = dates_utc.map { |d| DateTime.parse(d).strftime("%Y") }
+        solr_doc[year_fld] = [] unless solr_doc.include?(year_fld)
+        solr_doc[year_fld] << years unless years.blank?
+        solr_doc[year_fld].flatten!
+        # date as complex_date_type displayable
+        fld_name = Solrizer.solr_name("complex_date_#{label}", :displayable)
+        solr_doc[fld_name] = [] unless solr_doc.include?(fld_name)
+        solr_doc[fld_name] << vals
+        solr_doc[fld_name].flatten!
+      end
+    end
+
+    def self.date_facet_fields
+      # solr fields that will be treated as facets
+      fields = []
+      # change all dates to years
+      fields << Solrizer.solr_name('complex_year_accepted', :facetable)
+      fields << Solrizer.solr_name('complex_year_available', :facetable)
+      fields << Solrizer.solr_name('complex_year_copyrighted', :facetable)
+      fields << Solrizer.solr_name('complex_year_collected', :facetable)
+      fields << Solrizer.solr_name('complex_year_created', :facetable)
+      fields << Solrizer.solr_name('complex_year_issued', :facetable)
+      fields << Solrizer.solr_name('complex_year_published', :facetable)
+      fields << Solrizer.solr_name('complex_year_submitted', :facetable)
+      fields << Solrizer.solr_name('complex_year_updated', :facetable)
+      fields << Solrizer.solr_name('complex_year_valid', :facetable)
+      fields << Solrizer.solr_name('complex_year_processed', :facetable)
+      fields << Solrizer.solr_name('complex_year_purchased', :facetable)
+      fields << Solrizer.solr_name('complex_year_other', :facetable)
+      fields
+    end
+
+    def self.date_search_fields
+      # solr fields that will be used for a search
+      fields = []
+      fields << Solrizer.solr_name('complex_date', :stored_searchable, type: :date)
+      fields << Solrizer.solr_name('complex_year', :stored_searchable)
+      fields
+    end
+
+    def self.date_show_fields
+      # solr fields that will be used to display results on the record page
+      fields = []
+      fields << Solrizer.solr_name('complex_date_accepted', :displayable)
+      fields << Solrizer.solr_name('complex_date_available', :displayable)
+      fields << Solrizer.solr_name('complex_date_copyrighted', :displayable)
+      fields << Solrizer.solr_name('complex_date_collected', :displayable)
+      fields << Solrizer.solr_name('complex_date_created', :displayable)
+      fields << Solrizer.solr_name('complex_date_issued', :displayable)
+      fields << Solrizer.solr_name('complex_date_published', :displayable)
+      fields << Solrizer.solr_name('complex_date_submitted', :displayable)
+      fields << Solrizer.solr_name('complex_date_updated', :displayable)
+      fields << Solrizer.solr_name('complex_date_valid', :displayable)
+      fields << Solrizer.solr_name('complex_date_processed', :displayable)
+      fields << Solrizer.solr_name('complex_date_purchased', :displayable)
+      fields << Solrizer.solr_name('complex_date_other', :displayable)
+      fields
+    end
+  end
+end
diff --git a/hyrax/app/indexers/complex_field/fundref_indexer.rb b/hyrax/app/indexers/complex_field/fundref_indexer.rb
new file mode 100644
index 0000000000000000000000000000000000000000..59581bf196baa4666e92f3542229c7a43ce06a27
--- /dev/null
+++ b/hyrax/app/indexers/complex_field/fundref_indexer.rb
@@ -0,0 +1,54 @@
+module ComplexField
+  module FundrefIndexer
+    def generate_solr_document
+      super.tap do |solr_doc|
+        index_fundref(solr_doc)
+      end
+    end
+
+    def index_fundref(solr_doc)
+      # funding reference object in json
+      fld_name = Solrizer.solr_name('complex_funding_reference', :displayable)
+      solr_doc[fld_name] = object.complex_funding_reference.to_json
+      # funder_identifier - symbol
+      fld_name = Solrizer.solr_name('funder_identifier', :symbol)
+      solr_doc[fld_name] = object.complex_funding_reference.map { |r| r.funder_identifier.reject(&:blank?).first }
+      # funder_name - searchable
+      fld_name = Solrizer.solr_name('funder', :stored_searchable)
+      solr_doc[fld_name] = object.complex_funding_reference.map { |r| r.funder_name.reject(&:blank?).first }
+      # funder_name - facetable
+      fld_name = Solrizer.solr_name('funder', :facetable)
+      solr_doc[fld_name] = object.complex_funding_reference.map { |r| r.funder_name.reject(&:blank?).first }
+      # award_number - symbol
+      fld_name = Solrizer.solr_name('award_number', :symbol)
+      solr_doc[fld_name] = object.complex_funding_reference.map { |r| r.award_number.reject(&:blank?).first }
+      # award_title - searchable
+      fld_name = Solrizer.solr_name('award_title', :stored_searchable)
+      solr_doc[fld_name] = object.complex_funding_reference.map { |r| r.award_title.reject(&:blank?).first }
+    end
+
+    def self.fundref_facet_fields
+      # solr fields that will be treated as facets
+      fields = []
+      fields << Solrizer.solr_name('funder', :facetable)
+      fields
+    end
+
+    def self.fundref_search_fields
+      # solr fields that will be used for a search
+      fields = []
+      fields << Solrizer.solr_name('funder_identifier', :symbol)
+      fields << Solrizer.solr_name('funder', :stored_searchable)
+      fields << Solrizer.solr_name('award_number', :symbol)
+      fields << Solrizer.solr_name('award_title', :stored_searchable)
+      fields
+    end
+
+    def self.fundref_show_fields
+      # solr fields that will be used to display results on the record page
+      fields = []
+      fields << Solrizer.solr_name('complex_funding_reference', :displayable)
+      fields
+    end
+  end
+end
diff --git a/hyrax/app/indexers/complex_field/identifier_indexer.rb b/hyrax/app/indexers/complex_field/identifier_indexer.rb
new file mode 100644
index 0000000000000000000000000000000000000000..5d01be90bffc7d8d9420a3725c84ac7eef0a4cdc
--- /dev/null
+++ b/hyrax/app/indexers/complex_field/identifier_indexer.rb
@@ -0,0 +1,52 @@
+module ComplexField
+  module IdentifierIndexer
+    def generate_solr_document
+      super.tap do |solr_doc|
+        index_identifier(solr_doc)
+      end
+    end
+
+    def index_identifier(solr_doc)
+      solr_doc[Solrizer.solr_name('complex_identifier', :symbol)] = object.complex_identifier.map { |i| i.identifier.reject(&:blank?).first }
+      solr_doc[Solrizer.solr_name('complex_identifier', :displayable)] = object.complex_identifier.to_json
+      facetable_ids = %w(group_id project_id)
+      object.complex_identifier.each do |i|
+        unless i.scheme.blank? || i.identifier.blank?
+          label = i.scheme.reject(&:blank?).first.downcase.tr(' ', '_')
+          fld_name = Solrizer.solr_name("complex_identifier_#{label}", :symbol)
+          solr_doc[fld_name] = [] unless solr_doc.include?(fld_name)
+          solr_doc[fld_name] << i.identifier.reject(&:blank?)
+          solr_doc[fld_name].flatten!
+          if facetable_ids.include?(label)
+            fld_name = Solrizer.solr_name("complex_identifier_#{label}", :facetable)
+            solr_doc[fld_name] = [] unless solr_doc.include?(fld_name)
+            solr_doc[fld_name] << i.identifier.reject(&:blank?)
+            solr_doc[fld_name].flatten!
+          end
+        end
+      end
+    end
+
+    def self.identifier_facet_fields
+      # solr fields that will be used for a search
+      fields = []
+      fields << Solrizer.solr_name('complex_identifier_group_id', :facetable)
+      fields << Solrizer.solr_name('complex_identifier_project_id', :facetable)
+      fields
+    end
+
+    def self.identifier_search_fields
+      # solr fields that will be used for a search
+      fields = []
+      fields << Solrizer.solr_name('complex_identifier', :symbol)
+      fields
+    end
+
+    def self.identifier_show_fields
+      # solr fields that will be used to display results on the record page
+      fields = []
+      fields << Solrizer.solr_name('complex_identifier', :displayable)
+      fields
+    end
+  end
+end
diff --git a/hyrax/app/indexers/complex_field/person_indexer.rb b/hyrax/app/indexers/complex_field/person_indexer.rb
new file mode 100644
index 0000000000000000000000000000000000000000..8d97de84e1b12ea134eefb0837be7ca450c771e2
--- /dev/null
+++ b/hyrax/app/indexers/complex_field/person_indexer.rb
@@ -0,0 +1,84 @@
+module ComplexField
+  module PersonIndexer
+    def generate_solr_document
+      super.tap do |solr_doc|
+        index_person(solr_doc)
+      end
+    end
+
+    def index_person(solr_doc)
+      creators = object.complex_person.map { |c| (c.first_name + c.last_name).reject(&:blank?).join(' ') }
+      creators << object.complex_person.map { |c| c.name.reject(&:blank?) }
+      creators = creators.flatten.uniq.reject(&:blank?)
+      solr_doc[Solrizer.solr_name('complex_person', :stored_searchable)] = creators
+      solr_doc[Solrizer.solr_name('complex_person', :facetable)] = creators
+      solr_doc[Solrizer.solr_name('complex_person', :displayable)] = object.complex_person.to_json
+      object.complex_person.each do |c|
+        # index creator by role
+        person_name = c.name.reject(&:blank?)
+        person_name = (c.first_name + c.last_name).reject(&:blank?).join(' ') if person_name.blank?
+        label = 'other'
+        label = RoleService.new.label(c.role.first) unless c.role.blank?
+        label = label.downcase.tr(' ', '_')
+        # complex_person by role as stored_searchable
+        fld_name = Solrizer.solr_name("complex_person_#{label}", :stored_searchable)
+        solr_doc[fld_name] = [] unless solr_doc.include?(fld_name)
+        solr_doc[fld_name] << person_name
+        solr_doc[fld_name].flatten!
+        # complex_person by role as facetable
+        fld_name = Solrizer.solr_name("complex_person_#{label}", :facetable)
+        solr_doc[fld_name] = [] unless solr_doc.include?(fld_name)
+        solr_doc[fld_name] << person_name
+        solr_doc[fld_name].flatten!
+        # identifier
+        fld_name = Solrizer.solr_name('complex_person_identifier', :symbol)
+        vals = c.orcid.reject(&:blank?)
+        solr_doc[fld_name] = [] unless solr_doc.include?(fld_name)
+        solr_doc[fld_name] << vals
+        solr_doc[fld_name] = solr_doc[fld_name].flatten.uniq
+        # affiliation
+        vals = c.affiliation.reject(&:blank?)
+        fld_name = Solrizer.solr_name('complex_person_affiliation', :stored_searchable)
+        solr_doc[fld_name] = [] unless solr_doc.include?(fld_name)
+        solr_doc[fld_name] << vals
+        solr_doc[fld_name] = solr_doc[fld_name].flatten.uniq
+        fld_name = Solrizer.solr_name('complex_person_affiliation', :facetable)
+        solr_doc[fld_name] = [] unless solr_doc.include?(fld_name)
+        solr_doc[fld_name] << vals
+        solr_doc[fld_name] = solr_doc[fld_name].flatten.uniq
+
+      end
+    end
+
+    def self.person_facet_fields
+      # solr fields that will be treated as facets
+      fields = []
+      fields << Solrizer.solr_name('complex_person_other', :facetable)
+      fields << Solrizer.solr_name('complex_person_author', :facetable)
+      fields << Solrizer.solr_name('complex_person_editor', :facetable)
+      fields << Solrizer.solr_name('complex_person_translator', :facetable)
+      fields << Solrizer.solr_name('complex_person_data_depositor', :facetable)
+      fields << Solrizer.solr_name('complex_person_data_curator', :facetable)
+      fields << Solrizer.solr_name('complex_person_operator', :facetable)
+      fields << Solrizer.solr_name('complex_person_contact_person', :facetable)
+      fields << Solrizer.solr_name('complex_person_affiliation', :facetable)
+      fields
+    end
+
+    def self.person_search_fields
+      # solr fields that will be used for a search
+      fields = []
+      fields << Solrizer.solr_name('complex_person', :stored_searchable)
+      fields << Solrizer.solr_name('complex_person_identifier', :symbol)
+      fields << Solrizer.solr_name('complex_person_affiliation', :stored_searchable)
+      fields
+    end
+
+    def self.person_show_fields
+      # solr fields that will be used to display results on the record page
+      fields = []
+      fields << Solrizer.solr_name('complex_person', :displayable)
+      fields
+    end
+  end
+end
diff --git a/hyrax/app/indexers/complex_field/relation_indexer.rb b/hyrax/app/indexers/complex_field/relation_indexer.rb
new file mode 100644
index 0000000000000000000000000000000000000000..52b158abc4678a93e0bfd98d8a0422c00ac2e9ed
--- /dev/null
+++ b/hyrax/app/indexers/complex_field/relation_indexer.rb
@@ -0,0 +1,40 @@
+module ComplexField
+  module RelationIndexer
+    def generate_solr_document
+      super.tap do |solr_doc|
+        index_relation(solr_doc)
+      end
+    end
+
+    def index_relation(solr_doc)
+      solr_doc[Solrizer.solr_name('complex_relation', :displayable)] = object.complex_relation.to_json
+      solr_doc[Solrizer.solr_name('complex_relation_title', :stored_searchable)] = object.complex_relation.map { |r| r.title.reject(&:blank?).first }
+      object.complex_relation.each do |r|
+        unless r.title.blank? || r.relationship.blank? || only_blank_strings?(r.relationship)
+          fld_name = Solrizer.solr_name('complex_relation_relationship', :facetable)
+          solr_doc[fld_name] = [] unless solr_doc.include?(fld_name)
+          solr_doc[fld_name] << r.relationship.reject(&:blank?)
+          solr_doc[fld_name].flatten!
+          relationship = r.relationship.reject(&:blank?).first.downcase.tr(' ', '_')
+          fld_name = Solrizer.solr_name("complex_relation_#{relationship}", :facetable)
+          solr_doc[fld_name] = [] unless solr_doc.include?(fld_name)
+          solr_doc[fld_name] << r.title.reject(&:blank?)
+          solr_doc[fld_name].flatten!
+
+          fld_name = Solrizer.solr_name("complex_relation_#{relationship}", :stored_searchable)
+          solr_doc[fld_name] = [] unless solr_doc.include?(fld_name)
+          solr_doc[fld_name] << r.title.reject(&:blank?)
+          solr_doc[fld_name].flatten!
+        end
+      end
+    end
+
+    ##
+    # If the complex relation is only an array of blank strings, it doesn't need to be indexed
+    # @return [Boolean]
+    def only_blank_strings?(relationship)
+      relationship.delete("")
+      relationship.empty?
+    end
+  end
+end
diff --git a/hyrax/app/indexers/complex_field/subject_indexer.rb b/hyrax/app/indexers/complex_field/subject_indexer.rb
new file mode 100644
index 0000000000000000000000000000000000000000..9916f7745c2197941c06a66ca13f6c0fcb258069
--- /dev/null
+++ b/hyrax/app/indexers/complex_field/subject_indexer.rb
@@ -0,0 +1,89 @@
+module ComplexField
+  module SubjectIndexer
+    def generate_solr_document
+      super.tap do |solr_doc|
+        index_subject(solr_doc)
+      end
+    end
+
+    def index_subject(solr_doc)
+      solr_doc[Solrizer.solr_name('complex_subject', :displayable)] = object.complex_subject.to_json
+      object.complex_subject.each do |c|
+        # identifier
+        fld_name = Solrizer.solr_name('complex_subject_id', :symbol)
+        vals = c.subject_identifier.reject(&:blank?)
+        solr_doc[fld_name] = [] unless solr_doc.include?(fld_name)
+        solr_doc[fld_name] << vals
+        solr_doc[fld_name] = solr_doc[fld_name].flatten.uniq
+        # species
+        vals = c.subject_species.reject(&:blank?)
+        fld_name = Solrizer.solr_name('complex_subject_species', :stored_searchable)
+        solr_doc[fld_name] = [] unless solr_doc.include?(fld_name)
+        solr_doc[fld_name] << vals
+        solr_doc[fld_name] = solr_doc[fld_name].flatten.uniq
+        fld_name = Solrizer.solr_name('complex_subject_species', :facetable)
+        solr_doc[fld_name] = [] unless solr_doc.include?(fld_name)
+        solr_doc[fld_name] << vals
+        solr_doc[fld_name] = solr_doc[fld_name].flatten.uniq
+        # type
+        vals = c.subject_type.reject(&:blank?)
+        fld_name = Solrizer.solr_name('complex_subject_type', :stored_searchable)
+        solr_doc[fld_name] = [] unless solr_doc.include?(fld_name)
+        solr_doc[fld_name] << vals
+        solr_doc[fld_name] = solr_doc[fld_name].flatten.uniq
+        fld_name = Solrizer.solr_name('complex_subject_type', :facetable)
+        solr_doc[fld_name] = [] unless solr_doc.include?(fld_name)
+        solr_doc[fld_name] << vals
+        solr_doc[fld_name] = solr_doc[fld_name].flatten.uniq
+        # sex
+        vals = c.subject_sex.reject(&:blank?)
+        fld_name = Solrizer.solr_name('complex_subject_sex', :stored_searchable)
+        solr_doc[fld_name] = [] unless solr_doc.include?(fld_name)
+        solr_doc[fld_name] << vals
+        solr_doc[fld_name] = solr_doc[fld_name].flatten.uniq
+        fld_name = Solrizer.solr_name('complex_subject_sex', :facetable)
+        solr_doc[fld_name] = [] unless solr_doc.include?(fld_name)
+        solr_doc[fld_name] << vals
+        solr_doc[fld_name] = solr_doc[fld_name].flatten.uniq
+        # age
+        vals = c.subject_age.reject(&:blank?)
+        fld_name = Solrizer.solr_name('complex_subject_age', :stored_searchable)
+        solr_doc[fld_name] = [] unless solr_doc.include?(fld_name)
+        solr_doc[fld_name] << vals
+        solr_doc[fld_name] = solr_doc[fld_name].flatten.uniq
+        fld_name = Solrizer.solr_name('complex_subject_age', :facetable)
+        solr_doc[fld_name] = [] unless solr_doc.include?(fld_name)
+        solr_doc[fld_name] << vals
+        solr_doc[fld_name] = solr_doc[fld_name].flatten.uniq
+      end
+    end
+
+    def self.subject_facet_fields
+      # solr fields that will be treated as facets
+      fields = []
+      fields << Solrizer.solr_name('complex_subject_species', :facetable)
+      fields << Solrizer.solr_name('complex_subject_type', :facetable)
+      fields << Solrizer.solr_name('complex_subject_sex', :facetable)
+      fields << Solrizer.solr_name('complex_subject_age', :facetable)
+      fields
+    end
+
+    def self.subject_search_fields
+      # solr fields that will be used for a search
+      fields = []
+      fields << Solrizer.solr_name('complex_subject_identifier', :symbol)
+      fields << Solrizer.solr_name('complex_subject_species', :stored_searchable)
+      fields << Solrizer.solr_name('complex_subject_type', :stored_searchable)
+      fields << Solrizer.solr_name('complex_subject_sex', :stored_searchable)
+      fields << Solrizer.solr_name('complex_subject_age', :stored_searchable)
+      fields
+    end
+
+    def self.subject_show_fields
+      # solr fields that will be used to display results on the record page
+      fields = []
+      fields << Solrizer.solr_name('complex_subject', :displayable)
+      fields
+    end
+  end
+end
diff --git a/hyrax/app/indexers/crc_dataset_indexer.rb b/hyrax/app/indexers/crc_dataset_indexer.rb
new file mode 100644
index 0000000000000000000000000000000000000000..8d9511d27562000e60d61a91467228b072075c76
--- /dev/null
+++ b/hyrax/app/indexers/crc_dataset_indexer.rb
@@ -0,0 +1,55 @@
+# Generated via
+#  `rails generate hyrax:work CrcDataset`
+class CrcDatasetIndexer < RdmsIndexer
+  # This indexes the default metadata. You can remove it if you want to
+  # provide your own metadata and indexing.
+  include Hyrax::IndexesBasicMetadata
+
+  # Fetch remote labels for based_near. You can remove this if you don't want
+  # this behavior
+  include Hyrax::IndexesLinkedMetadata
+
+  # Custom indexers for crc dataset model
+  include ComplexField::PersonIndexer
+  include ComplexField::DateIndexer
+  include ComplexField::IdentifierIndexer
+  include ComplexField::FundrefIndexer
+  include ComplexField::RelationIndexer
+  include ComplexField::SubjectIndexer
+
+  def self.facet_fields
+    # solr fields that will be treated as facets
+    super.tap do |fields|
+      fields << Solrizer.solr_name('modality', :facetable)
+      fields.concat ComplexField::IdentifierIndexer.identifier_facet_fields
+      fields.concat ComplexField::DateIndexer.date_facet_fields
+      fields.concat ComplexField::PersonIndexer.person_facet_fields
+      fields.concat ComplexField::FundrefIndexer.fundref_facet_fields
+      fields.concat ComplexField::SubjectIndexer.subject_facet_fields
+    end
+  end
+
+  def self.search_fields
+    # solr fields that will be used for a search
+    super.tap do |fields|
+      fields << Solrizer.solr_name('modality', :stored_searchable)
+      fields.concat ComplexField::IdentifierIndexer.identifier_search_fields
+      fields.concat ComplexField::DateIndexer.date_search_fields
+      fields.concat ComplexField::PersonIndexer.person_search_fields
+      fields.concat ComplexField::FundrefIndexer.fundref_search_fields
+      fields.concat ComplexField::SubjectIndexer.subject_search_fields
+    end
+  end
+
+  def self.show_fields
+    # solr fields that will be used to display results on the record page
+    super.tap do |fields|
+      fields << Solrizer.solr_name('modality', :stored_searchable)
+      fields.concat ComplexField::IdentifierIndexer.identifier_show_fields
+      fields.concat ComplexField::DateIndexer.date_show_fields
+      fields.concat ComplexField::PersonIndexer.person_show_fields
+      fields.concat ComplexField::FundrefIndexer.fundref_show_fields
+      fields.concat ComplexField::SubjectIndexer.subject_show_fields
+    end
+  end
+end
diff --git a/hyrax/app/indexers/dataset_indexer.rb b/hyrax/app/indexers/dataset_indexer.rb
index f7a770b564af75f4175c5bbcd8b71bded0a530a8..c7b9369bb275c7097f94f57a07fea28b6271b3a9 100644
--- a/hyrax/app/indexers/dataset_indexer.rb
+++ b/hyrax/app/indexers/dataset_indexer.rb
@@ -1,6 +1,6 @@
 # Generated via
 #  `rails generate hyrax:work Dataset`
-class DatasetIndexer < Hyrax::WorkIndexer
+class DatasetIndexer < RdmsIndexer
   # This indexes the default metadata. You can remove it if you want to
   # provide your own metadata and indexing.
   include Hyrax::IndexesBasicMetadata
@@ -9,10 +9,40 @@ class DatasetIndexer < Hyrax::WorkIndexer
   # this behavior
   include Hyrax::IndexesLinkedMetadata
 
-  # Uncomment this block if you want to add custom indexing behavior:
-  # def generate_solr_document
-  #  super.tap do |solr_doc|
-  #    solr_doc['my_custom_field_ssim'] = object.my_custom_property
-  #  end
-  # end
+  # Custom indexers for dataset model
+  include ComplexField::PersonIndexer
+  include ComplexField::DateIndexer
+  include ComplexField::IdentifierIndexer
+  include ComplexField::FundrefIndexer
+  include ComplexField::RelationIndexer
+
+  def self.facet_fields
+    # solr fields that will be treated as facets
+    super.tap do |fields|
+      fields.concat ComplexField::DateIndexer.date_facet_fields
+      fields.concat ComplexField::PersonIndexer.person_facet_fields
+      fields.concat ComplexField::FundrefIndexer.fundref_facet_fields
+    end
+  end
+
+  def self.search_fields
+    # solr fields that will be used for a search
+    super.tap do |fields|
+      fields.concat ComplexField::IdentifierIndexer.identifier_search_fields
+      fields.concat ComplexField::DateIndexer.date_search_fields
+      fields.concat ComplexField::PersonIndexer.person_search_fields
+      fields.concat ComplexField::FundrefIndexer.fundref_search_fields
+    end
+  end
+
+  def self.show_fields
+    # solr fields that will be used to display results on the record page
+    super.tap do |fields|
+      fields.concat ComplexField::IdentifierIndexer.identifier_show_fields
+      fields.concat ComplexField::DateIndexer.date_show_fields
+      fields.concat ComplexField::PersonIndexer.person_show_fields
+      fields.concat ComplexField::FundrefIndexer.fundref_show_fields
+    end
+  end
+
 end
diff --git a/hyrax/app/indexers/rdms_indexer.rb b/hyrax/app/indexers/rdms_indexer.rb
new file mode 100644
index 0000000000000000000000000000000000000000..3f9617f436866fa6c63df0aadd46ea7c9424132e
--- /dev/null
+++ b/hyrax/app/indexers/rdms_indexer.rb
@@ -0,0 +1,85 @@
+class RdmsIndexer < Hyrax::WorkIndexer
+  # This indexes the default metadata. You can remove it if you want to
+  # provide your own metadata and indexing.
+  include Hyrax::IndexesBasicMetadata
+
+  # Fetch remote labels for based_near. You can remove this if you don't want
+  # this behavior
+  include Hyrax::IndexesLinkedMetadata
+
+  def self.facet_fields
+    # solr fields that will be treated as facets
+    [
+      # core and basic metadata fields - not interested in these
+      # Solrizer.solr_name('creator', :facetable),
+      # Solrizer.solr_name('contributor', :facetable),
+      # Solrizer.solr_name('based_near_label', :facetable),
+
+      # system fields
+      # Solrizer.solr_name('file_format', :facetable),
+      # Solrizer.solr_name('human_readable_type', :facetable),
+      # Solrizer.solr_name('member_of_collections', :symbol),
+      #
+      # # core and basic metadata fields
+      # Solrizer.solr_name('keyword', :facetable),
+      # Solrizer.solr_name('language', :facetable),
+      # Solrizer.solr_name('publisher', :facetable),
+      # Solrizer.solr_name('resource_type', :facetable),
+      # Solrizer.solr_name('subject', :facetable),
+      # Solrizer.solr_name('visibility', :stored_sortable),
+    ]
+  end
+
+  def self.search_fields
+    # solr fields that will be used for a search
+    [
+      # Solrizer.solr_name('title', :stored_searchable),
+      # Solrizer.solr_name('description', :stored_searchable),
+      # Solrizer.solr_name('keyword', :stored_searchable),
+      # Solrizer.solr_name('subject', :stored_searchable),
+      # Solrizer.solr_name('publisher', :stored_searchable),
+      # Solrizer.solr_name('language', :stored_searchable),
+      # Solrizer.solr_name('date_uploaded', :stored_searchable),
+      # Solrizer.solr_name('date_modified', :stored_searchable),
+      # Solrizer.solr_name('date_published', :stored_searchable),
+      # Solrizer.solr_name('date_created', :stored_searchable),
+      # Solrizer.solr_name('rights_statement', :stored_searchable),
+      # Solrizer.solr_name('license', :stored_searchable),
+      # Solrizer.solr_name('resource_type', :stored_searchable),
+      # Solrizer.solr_name('format', :stored_searchable),
+      # Solrizer.solr_name('identifier', :stored_searchable),
+      # Solrizer.solr_name('place', :stored_searchable),
+      # Solrizer.solr_name('status', :stored_searchable),
+      # Solrizer.solr_name('issue', :stored_searchable),
+      # Solrizer.solr_name('licensed_date', :stored_searchable)
+    ]
+  end
+
+  def self.show_fields
+    # solr fields that will be used to display results on the record page
+    [
+      # Solrizer.solr_name('creator', :stored_searchable),
+      # Solrizer.solr_name('contributor', :stored_searchable),
+      # Solrizer.solr_name('based_near_label', :stored_searchable),
+      # Solrizer.solr_name('title', :stored_searchable),
+      # Solrizer.solr_name('description', :stored_searchable),
+      # Solrizer.solr_name('keyword', :stored_searchable),
+      # Solrizer.solr_name('subject', :stored_searchable),
+      # Solrizer.solr_name('publisher', :stored_searchable),
+      # Solrizer.solr_name('language', :stored_searchable),
+      # Solrizer.solr_name('date_uploaded', :stored_searchable),
+      # Solrizer.solr_name('date_modified', :stored_searchable),
+      # Solrizer.solr_name('date_published', :stored_searchable),
+      # Solrizer.solr_name('date_created', :stored_searchable),
+      # Solrizer.solr_name('rights_statement', :stored_searchable),
+      # Solrizer.solr_name('license', :stored_searchable),
+      # Solrizer.solr_name('resource_type', :stored_searchable),
+      # Solrizer.solr_name('format', :stored_searchable),
+      # Solrizer.solr_name('identifier', :stored_searchable),
+      # Solrizer.solr_name('place', :stored_searchable),
+      # Solrizer.solr_name('status', :stored_searchable),
+      # Solrizer.solr_name('issue', :stored_searchable),
+      # Solrizer.solr_name('licensed_date', :stored_searchable)
+    ]
+  end
+end
diff --git a/hyrax/app/indexers/work_indexer.rb b/hyrax/app/indexers/work_indexer.rb
new file mode 100644
index 0000000000000000000000000000000000000000..372fe6974af7887f1fcfb0e13bd843e61fd172fa
--- /dev/null
+++ b/hyrax/app/indexers/work_indexer.rb
@@ -0,0 +1,5 @@
+# Generated via
+#  `rails generate hyrax:work Work`
+class WorkIndexer < RdmsIndexer
+  # Add custom indexers for work model
+end
diff --git a/hyrax/app/inputs/nested_attributes_input.rb b/hyrax/app/inputs/nested_attributes_input.rb
new file mode 100644
index 0000000000000000000000000000000000000000..edc07cde743e4e5a857ae3c69115a1fe6ba005c8
--- /dev/null
+++ b/hyrax/app/inputs/nested_attributes_input.rb
@@ -0,0 +1,119 @@
+class NestedAttributesInput < MultiValueInput
+
+  def input(wrapper_options)
+    super
+  end
+
+  def nested_input(wrapper_options, values, parent=@builder.object_name)
+    @rendered_first_element = false
+    input_html_classes.unshift('string')
+    input_html_options[:name] ||= "#{parent}[#{attribute_name}][]"
+    input_html_options[:repeats] = false
+    nested_outer_wrapper do
+      buffer_each(values) do |value, index|
+        nested_inner_wrapper do
+          build_field(value, index, parent)
+        end
+      end
+    end
+  end
+
+  protected
+
+    def nested_outer_wrapper
+      "    <ul class=\"inner-listing\">\n        #{yield}\n      </ul>\n"
+    end
+
+    def nested_inner_wrapper
+      <<-HTML
+          <li class="field-wrapper">
+            #{yield}
+          </li>
+        HTML
+    end
+
+    def build_field(value, index, parent=@builder.object_name)
+      options = input_html_options.dup
+      if !value.kind_of? ActiveTriples::Resource
+        association = @builder.object.model.send(attribute_name)
+        value = association.build
+      end
+      # if value.kind_of? ActiveTriples::Resource
+      options[:name] = name_for(attribute_name, index, 'hidden_label'.freeze, parent)
+      options[:id] = id_for(attribute_name, index, 'hidden_label'.freeze, parent)
+
+      if value.new_record?
+        build_options_for_new_row(attribute_name, index, options)
+      else
+        build_options_for_existing_row(attribute_name, index, value, options)
+      end
+      # end
+
+      options[:required] = nil if @rendered_first_element
+
+      options[:class] ||= []
+      options[:class] += ["#{input_dom_id} form-control multi-text-field"]
+      options[:'aria-labelledby'] = label_id
+
+      @rendered_first_element = true
+
+      out = ''
+      out << build_components(attribute_name, value, index, options, parent)
+      out << hidden_id_field(value, index, parent) unless value.new_record?
+      out
+    end
+
+    def destroy_widget(attribute_name, index, field_label="field", parent=@builder.object_name)
+      out = ''
+      out << hidden_destroy_field(attribute_name, index, parent)
+      out << "    <button type=\"button\" class=\"btn btn-link remove\">"
+      out << "      <span class=\"glyphicon glyphicon-remove\"></span>"
+      out << "      <span class=\"controls-remove-text\">Remove</span>"
+      out << "      <span class=\"sr-only\"> previous <span class=\"controls-field-name-text\"> #{field_label}</span></span>"
+      out << "    </button>"
+      out
+    end
+
+    def hidden_id_field(value, index, parent=@builder.object_name)
+      name = id_name_for(attribute_name, index, parent)
+      id = id_for(attribute_name, index, 'id'.freeze, parent)
+      hidden_value = value.new_record? ? '' : value.rdf_subject
+      @builder.hidden_field(attribute_name, name: name, id: id, value: hidden_value, data: { id: 'remote' })
+    end
+
+    def hidden_destroy_field(attribute_name, index, parent=@builder.object_name)
+      name = destroy_name_for(attribute_name, index, parent)
+      id = id_for(attribute_name, index, '_destroy'.freeze, parent)
+      hidden_value = false
+      @builder.hidden_field(attribute_name, name: name, id: id,
+        value: hidden_value, data: { destroy: true }, class: 'form-control remove-hidden')
+    end
+
+    def build_options_for_new_row(_attribute_name, _index, options)
+      options[:value] = ''
+    end
+
+    def build_options_for_existing_row(_attribute_name, _index, value, options)
+      options[:value] = value.rdf_label.first || "Unable to fetch label for #{value.rdf_subject}"
+    end
+
+    def name_for(attribute_name, index, field, parent=@builder.object_name)
+      "#{parent}[#{attribute_name}_attributes][#{index}][#{field}][]"
+    end
+
+    def id_name_for(attribute_name, index, parent=@builder.object_name)
+      singular_input_name_for(attribute_name, index, 'id', parent)
+    end
+
+    def destroy_name_for(attribute_name, index, parent=@builder.object_name)
+      singular_input_name_for(attribute_name, index, '_destroy', parent)
+    end
+
+    def singular_input_name_for(attribute_name, index, field, parent=@builder.object_name)
+      "#{parent}[#{attribute_name}_attributes][#{index}][#{field}]"
+    end
+
+    def id_for(attribute_name, index, field, parent=@builder.object_name)
+      [parent, "#{attribute_name}_attributes", index, field].join('_'.freeze)
+    end
+end
diff --git a/hyrax/app/inputs/nested_date_input.rb b/hyrax/app/inputs/nested_date_input.rb
new file mode 100644
index 0000000000000000000000000000000000000000..cfe3aeda750d11027e13860eaacf28036cf7b828
--- /dev/null
+++ b/hyrax/app/inputs/nested_date_input.rb
@@ -0,0 +1,55 @@
+class NestedDateInput < NestedAttributesInput
+
+protected
+
+  def build_components(attribute_name, value, index, options, parent=@builder.object_name)
+    out = ''
+
+    # Inherit required for fields validated in nested attributes
+    required  = false
+    if object.required?(:complex_date) and index == 0
+      required = true
+    end
+
+    # Add remove elemnt only if element repeats
+    repeats =options.delete(:repeats)
+    repeats = true if repeats.nil?
+
+    # --- description and date - single row
+    out << "<div class='row'>"
+
+    # description
+    field = :description
+    field_name = name_for(attribute_name, index, field, parent)
+    field_id = id_for(attribute_name, index, field, parent)
+    field_value = value.send(field).first
+    date_options = DateService.new.select_all_options
+    out << "  <div class='col-md-3'>"
+    out << template.select_tag(field_name, template.options_for_select(date_options, field_value),
+        label: '', class: 'select form-control', prompt: 'choose type', id: field_id)
+    out << '  </div>'
+
+    # --- date
+    field = :date
+    field_name = name_for(attribute_name, index, field, parent)
+    field_id = id_for(attribute_name, index, field, parent)
+    field_value = value.send(field).first
+
+    out << "  <div class='col-md-6'>"
+    out << @builder.text_field(field_name,
+        options.merge(value: field_value, name: field_name, id: field_id,
+            data: { provide: 'datepicker' }, required: required))
+    out << '  </div>'
+
+    # --- delete checkbox
+    if repeats == true
+      field_label = 'Date'
+      out << "  <div class='col-md-3'>"
+      out << destroy_widget(attribute_name, index, field_label, parent)
+      out << '  </div>'
+    end
+
+    out << '</div>' # last row
+    out
+  end
+end
diff --git a/hyrax/app/inputs/nested_funding_reference_input.rb b/hyrax/app/inputs/nested_funding_reference_input.rb
new file mode 100644
index 0000000000000000000000000000000000000000..732dc155b8a9e9ec2a0be12c38af809e627e643a
--- /dev/null
+++ b/hyrax/app/inputs/nested_funding_reference_input.rb
@@ -0,0 +1,115 @@
+class NestedFundingReferenceInput < NestedAttributesInput
+
+protected
+
+  def build_components(attribute_name, value, index, options, parent=@builder.object_name)
+    out = ''
+
+    # Inherit required for fields validated in nested attributes
+    required  = false
+    if object.required?(:complex_funding_reference) and index == 0
+      required = true
+    end
+
+    # Add remove element only if element repeats
+    repeats = options.delete(:repeats)
+    repeats = true if repeats.nil?
+
+    # --- funder identifier
+    field = :funder_identifier
+    field_name = name_for(attribute_name, index, field, parent)
+    field_id = id_for(attribute_name, index, field, parent)
+    field_value = value.send(field).first
+
+    out << "<div class='row'>"
+    out << "  <div class='col-md-3'>"
+    out << template.label_tag(field_name, field.to_s.humanize, required: required)
+    out << '  </div>'
+
+    out << "  <div class='col-md-9'>"
+    out << @builder.text_field(field_name,
+        options.merge(value: field_value, name: field_name, id: field_id, required: required))
+    out << '  </div>'
+    out << '</div>' # row
+
+    # --- funder name
+    field = :funder_name
+    field_name = name_for(attribute_name, index, field, parent)
+    field_id = id_for(attribute_name, index, field, parent)
+    field_value = value.send(field).first
+
+    out << "<div class='row'>"
+    out << "  <div class='col-md-3'>"
+    out << template.label_tag(field_name, field.to_s.humanize, required: required)
+    out << '  </div>'
+
+    out << "  <div class='col-md-9'>"
+    out << @builder.text_field(field_name,
+                               options.merge(value: field_value, name: field_name, id: field_id, required: required))
+    out << '  </div>'
+    out << '</div>' # row
+
+    # --- award number
+    field = :award_number
+    field_name = name_for(attribute_name, index, field, parent)
+    field_id = id_for(attribute_name, index, field, parent)
+    field_value = value.send(field).first
+
+    out << "<div class='row'>"
+    out << "  <div class='col-md-3'>"
+    out << template.label_tag(field_name, field.to_s.humanize, required: required)
+    out << '  </div>'
+
+    out << "  <div class='col-md-9'>"
+    out << @builder.text_field(field_name,
+                               options.merge(value: field_value, name: field_name, id: field_id, required: required))
+    out << '  </div>'
+    out << '</div>' # row
+
+    # --- award uri
+    field = :award_uri
+    field_name = name_for(attribute_name, index, field, parent)
+    field_id = id_for(attribute_name, index, field, parent)
+    field_value = value.send(field).first
+
+    out << "<div class='row'>"
+    out << "  <div class='col-md-3'>"
+    out << template.label_tag(field_name, field.to_s.humanize, required: required)
+    out << '  </div>'
+
+    out << "  <div class='col-md-9'>"
+    out << @builder.text_field(field_name,
+                               options.merge(value: field_value, name: field_name, id: field_id, required: required))
+    out << '  </div>'
+    out << '</div>' # row
+
+    # last row
+    out << "<div class='row'>"
+
+    # --- award title
+    field = :award_title
+    field_name = name_for(attribute_name, index, field, parent)
+    field_id = id_for(attribute_name, index, field, parent)
+    field_value = value.send(field).first
+
+    out << "  <div class='col-md-3'>"
+    out << template.label_tag(field_name, field.to_s.humanize, required: false)
+    out << '  </div>'
+
+    out << "  <div class='col-md-6'>"
+    out << @builder.text_field(field_name,
+        options.merge(value: field_value, name: field_name, id: field_id))
+    out << '  </div>'
+
+    # --- delete checkbox
+    if repeats == true
+      field_label = 'Additional funding reference'
+      out << "  <div class='col-md-3'>"
+      out << destroy_widget(attribute_name, index, field_label, parent)
+      out << '  </div>'
+    end
+
+    out << '</div>' # last row
+    out
+  end
+end
diff --git a/hyrax/app/inputs/nested_identifier_input.rb b/hyrax/app/inputs/nested_identifier_input.rb
new file mode 100644
index 0000000000000000000000000000000000000000..aaf3b05ab510e1ca4d72b963dd9ab74648a4f4b4
--- /dev/null
+++ b/hyrax/app/inputs/nested_identifier_input.rb
@@ -0,0 +1,55 @@
+class NestedIdentifierInput < NestedAttributesInput
+
+protected
+
+  def build_components(attribute_name, value, index, options, parent=@builder.object_name)
+    out = ''
+
+    # Inherit required for fields validated in nested attributes
+    required  = false
+    if object.required?(:complex_identifier) and index == 0
+      required = true
+    end
+
+    # Add remove elemnt only if element repeats
+    repeats = options.delete(:repeats)
+    repeats = true if repeats.nil?
+
+    # --- scheme and id - single row
+    out << "<div class='row'>"
+
+    scheme_field = :scheme
+    scheme_field_name = name_for(attribute_name, index, scheme_field, parent)
+    scheme_field_id = id_for(attribute_name, index, scheme_field, parent)
+    scheme_field_value = value.send(scheme_field).first
+
+    identifier_field = :identifier
+    identifier_field_name = name_for(attribute_name, index, identifier_field, parent)
+    identifier_field_id = id_for(attribute_name, index, identifier_field, parent)
+    identifier_field_value = value.send(identifier_field).first
+    # --- scheme
+    id_options = IdentifierService.new.select_all_options
+    out << "  <div class='col-md-3'>"
+    out << template.select_tag(scheme_field_name,
+                               template.options_for_select(id_options, scheme_field_value),
+                               label: '', class: 'select form-control', prompt: 'choose type', id: scheme_field_id)
+    out << '  </div>'
+
+    # --- identifier
+    out << "  <div class='col-md-6'>"
+    out << @builder.text_field(identifier_field_name,
+                               options.merge(value: identifier_field_value, name: identifier_field_name, id: identifier_field_id,
+                                             required: required))
+    out << '  </div>'
+
+    # --- delete checkbox
+    if repeats == true
+      out << "  <div class='col-md-3'>"
+      out << destroy_widget(attribute_name, index, 'Identifier', parent)
+      out << '  </div>'
+    end
+
+    out << '</div>' # last row
+    out
+  end
+end
diff --git a/hyrax/app/inputs/nested_person_input.rb b/hyrax/app/inputs/nested_person_input.rb
new file mode 100644
index 0000000000000000000000000000000000000000..cf9aa3e382a4c189637910e2f95c41ca8fbd7575
--- /dev/null
+++ b/hyrax/app/inputs/nested_person_input.rb
@@ -0,0 +1,134 @@
+class NestedPersonInput < NestedAttributesInput
+
+protected
+
+  def build_components(attribute_name, value, index, options, parent=@builder.object_name)
+    out = ''
+
+    # Inherit required for fields validated in nested attributes
+    required  = false
+    if object.required?(:complex_person) and index == 0
+      required = true
+    end
+
+    # Add remove elemnt only if element repeats
+    repeats = options.delete(:repeats)
+    repeats = true if repeats.nil?
+
+    parent_attribute = name_for(attribute_name, index, '', parent)[0..-5]
+
+    # --- last_name
+    field = :last_name
+    field_name = name_for(attribute_name, index, field, parent)
+    field_id = id_for(attribute_name, index, field, parent)
+    field_value = value.send(field).first
+
+    out << "<div class='row'>"
+    out << "  <div class='col-md-3'>"
+    out << template.label_tag(field_name, I18n.t('rdms.fields.last_name'), required: required)
+    out << '  </div>'
+
+    out << "  <div class='col-md-9'>"
+    out << @builder.text_field(field_name,
+        options.merge(value: field_value, name: field_name, id: field_id, required: required, placeholder: "Alphabets"))
+    out << '  </div>'
+    out << '</div>' # row
+
+    # --- first_name
+    field = :first_name
+    field_name = name_for(attribute_name, index, field, parent)
+    field_id = id_for(attribute_name, index, field, parent)
+    field_value = value.send(field).first
+
+    out << "<div class='row'>"
+    out << "  <div class='col-md-3'>"
+    out << template.label_tag(field_name, I18n.t('rdms.fields.first_name'), required: required)
+    out << '  </div>'
+
+    out << "  <div class='col-md-9'>"
+    out << @builder.text_field(field_name,
+        options.merge(value: field_value, name: field_name, id: field_id, required: required, placeholder: "Alphabets"))
+    out << '  </div>'
+    out << '</div>' # row
+
+    # --- name
+    field = :name
+    field_name = name_for(attribute_name, index, field, parent)
+    field_id = id_for(attribute_name, index, field, parent)
+    field_value = value.send(field).first
+
+    out << "<div class='row'>"
+    out << "  <div class='col-md-3'>"
+    out << template.label_tag(field_name, I18n.t('rdms.fields.full_name'), required: required)
+    out << '  </div>'
+
+    out << "  <div class='col-md-9'>"
+    out << @builder.text_field(field_name,
+        options.merge(value: field_value, name: field_name, id: field_id, required: required, placeholder: "SURNAME, Given Names"))
+    out << '  </div>'
+    out << '</div>' # row
+
+    # --- role
+    role_options = RoleService.new.select_all_options
+    field = :role
+    field_name = name_for(attribute_name, index, field, parent)
+    field_id = id_for(attribute_name, index, field, parent)
+    field_value = value.send(field).first
+
+    out << "<div class='row'>"
+    out << "  <div class='col-md-3'>"
+    out << template.label_tag(field_name, field.to_s.humanize, required: required)
+    out << '  </div>'
+
+    out << "  <div class='col-md-9'>"
+    out << template.select_tag(field_name, template.options_for_select(role_options, field_value),
+        prompt: 'Select role played', label: '', class: 'select form-control', id: field_id, required: required)
+    out << '  </div>'
+    out << '</div>' # row
+
+    # --- orcid
+    field = :orcid
+    field_name = name_for(attribute_name, index, field, parent)
+    field_id = id_for(attribute_name, index, field, parent)
+    field_value = value.send(field).first
+
+    out << "<div class='row'>"
+    out << "  <div class='col-md-3'>"
+    out << template.label_tag(field_name, field.to_s.humanize, required: required)
+    out << '  </div>'
+
+    out << "  <div class='col-md-9'>"
+    out << @builder.text_field(field_name,
+        options.merge(value: field_value, name: field_name, id: field_id, required: required, placeholder: "https://orcid.org/0000-0000-0000-0000"))
+    out << '  </div>'
+    out << '</div>' # row
+
+    # --- affiliation
+    field = :affiliation
+    field_name = name_for(attribute_name, index, field, parent)
+    field_id = id_for(attribute_name, index, field, parent)
+    field_value = value.send(field).first
+
+    out << "<div class='row'>"
+    out << "  <div class='col-md-3'>"
+    out << template.label_tag(field_name, field.to_s.humanize, required: required)
+    out << '  </div>'
+
+    out << "  <div class='col-md-9'>"
+    out << @builder.text_field(field_name,
+                               options.merge(value: field_value, name: field_name, id: field_id, required: required, placeholder: "affiliation"))
+    out << '  </div>'
+    out << '</div>' # row
+
+    if repeats == true
+      field_label = 'Person'
+      out << "<div class='row'>"
+      out << "  <div class='col-md-3'>"
+      out << destroy_widget(attribute_name, index, field_label, parent)
+      out << '  </div>'
+      out << '</div>' # last row
+    end
+
+    out
+  end
+end
diff --git a/hyrax/app/inputs/nested_relation_input.rb b/hyrax/app/inputs/nested_relation_input.rb
new file mode 100644
index 0000000000000000000000000000000000000000..352ff8a953a2b14df3b7a9695153f447ac6c8f2d
--- /dev/null
+++ b/hyrax/app/inputs/nested_relation_input.rb
@@ -0,0 +1,95 @@
+class NestedRelationInput < NestedAttributesInput
+
+protected
+
+  def build_components(attribute_name, value, index, options, parent=@builder.object_name)
+    out = ''
+
+    # Inherit required for fields validated in nested attributes
+    required  = false
+    if object.required?(:complex_relation) and index == 0
+      required = true
+    end
+
+    # --- title
+    field = :title
+    field_name = name_for(attribute_name, index, field, parent)
+    field_id = id_for(attribute_name, index, field, parent)
+    field_value = value.send(field).first
+
+    out << "<div class='row'>"
+    out << "  <div class='col-md-3'>"
+    out << template.label_tag(field_name, 'Title', required: required)
+    out << '  </div>'
+
+    out << "  <div class='col-md-9'>"
+    out << @builder.text_field(field_name,
+        options.merge(value: field_value, name: field_name, id: field_id, required: required))
+    out << '  </div>'
+    out << '</div>' # row
+
+    # --- url
+    field = :url
+    field_name = name_for(attribute_name, index, field, parent)
+    field_id = id_for(attribute_name, index, field, parent)
+    field_value = value.send(field).first
+
+    out << "<div class='row'>"
+    out << "  <div class='col-md-3'>"
+    out << template.label_tag(field_name, field.to_s.humanize, required: required)
+    out << '  </div>'
+
+    out << "  <div class='col-md-9'>"
+    out << @builder.text_field(field_name,
+        options.merge(value: field_value, name: field_name, id: field_id, required: required))
+    out << '  </div>'
+    out << '</div>' # row
+
+    # # --- identifier
+    # field = :identifier
+    # field_value = value.send(field).first
+    # field_id = id_for(attribute_name, index, field, parent)
+    # field_name = name_for(attribute_name, index, field, parent)
+
+    # out << "<div class='row'>"
+    # out << "  <div class='col-md-3'>"
+    # out << template.label_tag(field_name, field.to_s.humanize, required: false)
+    # out << '  </div>'
+
+    # out << "  <div class='col-md-9'>"
+    # out << @builder.text_field(field_name,
+    #     options.merge(value: field_value, name: field_name, id: field_id, required: false))
+    # out << '  </div>'
+    # out << '</div>' # row
+
+    # last row
+    out << "<div class='row'>"
+
+    # --- relationship
+    field = :relationship
+    field_name = name_for(attribute_name, index, field, parent)
+    field_id = id_for(attribute_name, index, field, parent)
+    field_value = value.send(field).first
+    role_options = RelationshipService.new.select_all_options
+
+    out << "  <div class='col-md-3'>"
+    out << template.label_tag(field_name, 'Relationship', required: required)
+    out << '  </div>'
+
+    out << "  <div class='col-md-6'>"
+    out << template.select_tag(field_name,
+        template.options_for_select(role_options, field_value),
+        label: '', class: 'select form-control', prompt: 'choose relationship',
+        id: field_id, required: required)
+    out << '  </div>'
+
+    # --- delete checkbox
+    field_label ='Related work'
+    out << "  <div class='col-md-3'>"
+    out << destroy_widget(attribute_name, index, field_label, parent)
+    out << '  </div>'
+
+    out << '</div>' # last row
+    out
+  end
+end
diff --git a/hyrax/app/inputs/nested_subject_input.rb b/hyrax/app/inputs/nested_subject_input.rb
new file mode 100644
index 0000000000000000000000000000000000000000..30a22afb6d6458c12508670e64917f91d75969fb
--- /dev/null
+++ b/hyrax/app/inputs/nested_subject_input.rb
@@ -0,0 +1,116 @@
+class NestedSubjectInput < NestedAttributesInput
+
+protected
+
+  def build_components(attribute_name, value, index, options, parent=@builder.object_name)
+    out = ''
+
+    # Inherit required for fields validated in nested attributes
+    required  = false
+    if object.required?(:complex_subject) and index == 0
+      required = true
+    end
+
+    # Add remove elemnt only if element repeats
+    repeats = options.delete(:repeats)
+    repeats = true if repeats.nil?
+
+    parent_attribute = name_for(attribute_name, index, '', parent)[0..-5]
+
+    # --- identifier
+    field = :subject_identifier
+    field_name = name_for(attribute_name, index, field, parent)
+    field_id = id_for(attribute_name, index, field, parent)
+    field_value = value.send(field).first
+
+    out << "<div class='row'>"
+    out << "  <div class='col-md-3'>"
+    out << template.label_tag(field_name, I18n.t('rdms.fields.complex_subject_identifier'), required: required)
+    out << '  </div>'
+
+    out << "  <div class='col-md-9'>"
+    out << @builder.text_field(field_name,
+        options.merge(value: field_value, name: field_name, id: field_id, required: required, placeholder: "identifier"))
+    out << '  </div>'
+    out << '</div>' # row
+
+    # --- species
+    field = :subject_species
+    field_name = name_for(attribute_name, index, field, parent)
+    field_id = id_for(attribute_name, index, field, parent)
+    field_value = value.send(field).first
+
+    out << "<div class='row'>"
+    out << "  <div class='col-md-3'>"
+    out << template.label_tag(field_name, I18n.t('rdms.fields.complex_subject_species'), required: required)
+    out << '  </div>'
+
+    out << "  <div class='col-md-9'>"
+    out << @builder.text_field(field_name,
+        options.merge(value: field_value, name: field_name, id: field_id, required: required, placeholder: "species"))
+    out << '  </div>'
+    out << '</div>' # row
+
+    # --- type
+    field = :subject_type
+    field_name = name_for(attribute_name, index, field, parent)
+    field_id = id_for(attribute_name, index, field, parent)
+    field_value = value.send(field).first
+
+    out << "<div class='row'>"
+    out << "  <div class='col-md-3'>"
+    out << template.label_tag(field_name, I18n.t('rdms.fields.complex_subject_type'), required: required)
+    out << '  </div>'
+
+    out << "  <div class='col-md-9'>"
+    out << @builder.text_field(field_name,
+        options.merge(value: field_value, name: field_name, id: field_id, required: required, placeholder: "type"))
+    out << '  </div>'
+    out << '</div>' # row
+
+    # --- sex
+    field = :subject_sex
+    field_name = name_for(attribute_name, index, field, parent)
+    field_id = id_for(attribute_name, index, field, parent)
+    field_value = value.send(field).first
+
+    out << "<div class='row'>"
+    out << "  <div class='col-md-3'>"
+    out << template.label_tag(field_name, I18n.t('rdms.fields.complex_subject_sex'), required: required)
+    out << '  </div>'
+
+    out << "  <div class='col-md-9'>"
+    out << @builder.text_field(field_name,
+                               options.merge(value: field_value, name: field_name, id: field_id, required: required, placeholder: "sex"))
+    out << '  </div>'
+    out << '</div>' # row
+
+    # --- age
+    field = :subject_age
+    field_name = name_for(attribute_name, index, field, parent)
+    field_id = id_for(attribute_name, index, field, parent)
+    field_value = value.send(field).first
+
+    out << "<div class='row'>"
+    out << "  <div class='col-md-3'>"
+    out << template.label_tag(field_name, I18n.t('rdms.fields.complex_subject_age'), required: required)
+    out << '  </div>'
+
+    out << "  <div class='col-md-9'>"
+    out << @builder.text_field(field_name,
+        options.merge(value: field_value, name: field_name, id: field_id, required: required, placeholder: "age"))
+    out << '  </div>'
+    out << '</div>' # row
+
+    if repeats == true
+      field_label = 'Subject'
+      out << "<div class='row'>"
+      out << "  <div class='col-md-3'>"
+      out << destroy_widget(attribute_name, index, field_label, parent)
+      out << '  </div>'
+      out << '</div>' # last row
+    end
+
+    out
+  end
+end
diff --git a/hyrax/app/models/ability.rb b/hyrax/app/models/ability.rb
index 43ef51e588871cd97e1ac54465abf0425f49139d..f413b0d8b202b45a1f691672f4ac222fbcfd5521 100644
--- a/hyrax/app/models/ability.rb
+++ b/hyrax/app/models/ability.rb
@@ -28,6 +28,6 @@ class Ability
   def create_content
     # everyone who is logged in can create content
     # return unless registered_user?
-    can :create, ::Dataset if current_user
+    can :create, [::Dataset, ::CrcDataset] if current_user
   end
 end
diff --git a/hyrax/app/models/concerns/common_methods.rb b/hyrax/app/models/concerns/common_methods.rb
new file mode 100644
index 0000000000000000000000000000000000000000..2bbfa4bfebc11a42d2d501271aad97e5789d9758
--- /dev/null
+++ b/hyrax/app/models/concerns/common_methods.rb
@@ -0,0 +1,17 @@
+module CommonMethods
+  extend ActiveSupport::Concern
+
+  included do
+    def final_parent
+      parent
+    end
+
+    def persisted?
+      !new_record?
+    end
+
+    def new_record?
+      id.start_with?('#')
+    end
+  end
+end
diff --git a/hyrax/app/models/concerns/complex_date.rb b/hyrax/app/models/concerns/complex_date.rb
new file mode 100644
index 0000000000000000000000000000000000000000..8154f5161d0fa6a3ad991767556495a2a63039e9
--- /dev/null
+++ b/hyrax/app/models/concerns/complex_date.rb
@@ -0,0 +1,17 @@
+class ComplexDate < ActiveTriples::Resource
+  include CommonMethods
+
+  configure type: ::RDF::Vocab::VCARD.Date
+  property :date, predicate: ::RDF::Vocab::DWC.eventDate
+  property :description, predicate: ::RDF::Vocab::DC.description
+
+  ## Necessary to get AT to create hash URIs.
+  def initialize(uri, parent)
+    if uri.try(:node?)
+      uri = RDF::URI("#date#{uri.to_s.gsub('_:', '')}")
+    elsif uri.start_with?("#")
+      uri = RDF::URI(uri)
+    end
+    super
+  end
+end
\ No newline at end of file
diff --git a/hyrax/app/models/concerns/complex_funding_reference.rb b/hyrax/app/models/concerns/complex_funding_reference.rb
new file mode 100644
index 0000000000000000000000000000000000000000..d59e8d1fef14ade5e446769a55c5c8100fcf0ee6
--- /dev/null
+++ b/hyrax/app/models/concerns/complex_funding_reference.rb
@@ -0,0 +1,19 @@
+class ComplexFundingReference < ActiveTriples::Resource
+  include CommonMethods
+  configure type: ::RDF::Vocab::Rdms.FundingReference
+  property :funder_identifier, predicate: ::RDF::Vocab::Rdms.funderIdentifier
+  property :funder_name, predicate: ::RDF::Vocab::Rdms.funderName
+  property :award_number, predicate: ::RDF::Vocab::Rdms.awardNumber
+  property :award_uri, predicate: ::RDF::Vocab::Rdms.awardURI
+  property :award_title, predicate: ::RDF::Vocab::Rdms.awardTitle
+
+  ## Necessary to get AT to create hash URIs.
+  def initialize(uri, parent)
+    if uri.try(:node?)
+      uri = RDF::URI("#fundref#{uri.to_s.gsub('_:', '')}")
+    elsif uri.start_with?("#")
+      uri = RDF::URI(uri)
+    end
+    super
+  end
+end
diff --git a/hyrax/app/models/concerns/complex_identifier.rb b/hyrax/app/models/concerns/complex_identifier.rb
new file mode 100644
index 0000000000000000000000000000000000000000..77e8c25038d7579470ca8da199b01c9007305e80
--- /dev/null
+++ b/hyrax/app/models/concerns/complex_identifier.rb
@@ -0,0 +1,19 @@
+class ComplexIdentifier < ActiveTriples::Resource
+  include CommonMethods
+
+  configure type: ::RDF::Vocab::MODS.IdentifierGroup
+  property :identifier, predicate: ::RDF::Vocab::DataCite.hasIdentifier
+  property :scheme, predicate: ::RDF::Vocab::DataCite.usesIdentifierScheme
+  property :label, predicate: ::RDF::Vocab::SKOS.prefLabel
+
+  ## Necessary to get AT to create hash URIs.
+  def initialize(uri, parent)
+    if uri.try(:node?)
+      uri = RDF::URI("#identifier#{uri.to_s.gsub('_:', '')}")
+    elsif uri.start_with?("#")
+      uri = RDF::URI(uri)
+    end
+    super
+  end
+
+end
diff --git a/hyrax/app/models/concerns/complex_person.rb b/hyrax/app/models/concerns/complex_person.rb
new file mode 100644
index 0000000000000000000000000000000000000000..d8d3ff687dbdb7c2e8acc80646b7c86bfded13ff
--- /dev/null
+++ b/hyrax/app/models/concerns/complex_person.rb
@@ -0,0 +1,24 @@
+class ComplexPerson < ActiveTriples::Resource
+  include CommonMethods
+
+  configure type: ::RDF::Vocab::FOAF.Person
+  property :first_name, predicate: ::RDF::Vocab::FOAF.givenName
+  property :last_name, predicate: ::RDF::Vocab::FOAF.familyName
+  property :name, predicate: ::RDF::Vocab::VCARD.hasName
+  property :email, predicate: ::RDF::Vocab::FOAF.mbox
+  property :role, predicate: ::RDF::Vocab::MODS.roleRelationship
+  property :orcid, predicate: ::RDF::Vocab::DataCite.hasIdentifier
+  property :affiliation, predicate: ::RDF::Vocab::VMD.affiliation
+  property :display_order, predicate: ::RDF::Vocab::Rdms.order, multiple: false
+
+  ## Necessary to get AT to create hash URIs.
+  def initialize(uri, parent)
+    if uri.try(:node?)
+      uri = RDF::URI("#person#{uri.to_s.gsub('_:', '')}")
+    elsif uri.start_with?("#")
+      uri = RDF::URI(uri)
+    end
+    super
+  end
+
+end
diff --git a/hyrax/app/models/concerns/complex_relation.rb b/hyrax/app/models/concerns/complex_relation.rb
new file mode 100644
index 0000000000000000000000000000000000000000..336680e1f32fe7c69ed99bbe667c00e1f712b797
--- /dev/null
+++ b/hyrax/app/models/concerns/complex_relation.rb
@@ -0,0 +1,23 @@
+class ComplexRelation < ActiveTriples::Resource
+  include CommonMethods
+
+  configure type: ::RDF::Vocab::PROV.Association
+  property :title, predicate: ::RDF::Vocab::DC.title
+  property :url, predicate: ::RDF::Vocab::MODS.locationUrl
+  property :complex_identifier, predicate: ::RDF::Vocab::MODS.identifierGroup,
+            class_name:"ComplexIdentifier"
+  accepts_nested_attributes_for :complex_identifier
+  #property :relationship, predicate: ::RDF::Vocab::EBUCore.roleDefinition
+  property :relationship, predicate: ::RDF::Vocab::EBUCore.roleType
+
+  ## Necessary to get AT to create hash URIs.
+  def initialize(uri, parent)
+    if uri.try(:node?)
+      uri = RDF::URI("#relation#{uri.to_s.gsub('_:', '')}")
+    elsif uri.start_with?("#")
+      uri = RDF::URI(uri)
+    end
+    super
+  end
+
+end
diff --git a/hyrax/app/models/concerns/complex_subject.rb b/hyrax/app/models/concerns/complex_subject.rb
new file mode 100644
index 0000000000000000000000000000000000000000..dfc9ff211e28da4084f00122f7a5123231399704
--- /dev/null
+++ b/hyrax/app/models/concerns/complex_subject.rb
@@ -0,0 +1,21 @@
+class ComplexSubject < ActiveTriples::Resource
+  include CommonMethods
+
+  configure type: ::RDF::Vocab::Rdms.Subject
+  property :subject_identifier, predicate: ::RDF::Vocab::Rdms.subjectIdentifier
+  property :subject_species, predicate: ::RDF::Vocab::Rdms.subjectSpecies
+  property :subject_type, predicate: ::RDF::Vocab::Rdms.subjectType
+  property :subject_sex, predicate: ::RDF::Vocab::Rdms.subjectSex
+  property :subject_age, predicate: ::RDF::Vocab::Rdms.subjectAge
+
+  ## Necessary to get AT to create hash URIs.
+  def initialize(uri, parent)
+    if uri.try(:node?)
+      uri = RDF::URI("#subject#{uri.to_s.gsub('_:', '')}")
+    elsif uri.start_with?("#")
+      uri = RDF::URI(uri)
+    end
+    super
+  end
+
+end
diff --git a/hyrax/app/models/concerns/complex_validation.rb b/hyrax/app/models/concerns/complex_validation.rb
new file mode 100644
index 0000000000000000000000000000000000000000..6d9f3557e99709bd2d4b2467b81c5d4bbdbda7c9
--- /dev/null
+++ b/hyrax/app/models/concerns/complex_validation.rb
@@ -0,0 +1,76 @@
+module ComplexValidation
+  extend ActiveSupport::Concern
+  included do
+    # nested_to_array
+    resource_class.send(:define_method, :nested_to_array) do |attributes, key|
+      vals = attributes.fetch(key, [])
+      vals = [vals] unless vals.kind_of? Array
+      vals
+    end
+
+    # get_val_blank
+    resource_class.send(:define_method, :get_val_blank) do |attributes, key|
+      vals = nested_to_array(attributes, key)
+      vals.all?(&:blank?)
+    end
+
+    # person_blank
+    #   Requires first name or last name or name
+    resource_class.send(:define_method, :person_blank) do |attributes|
+      return true if attributes.blank?
+      first_name_blank = get_val_blank(attributes, :first_name)
+      last_name_blank = get_val_blank(attributes, :last_name)
+      name_blank = get_val_blank(attributes, :name)
+      first_name_blank && last_name_blank && name_blank
+    end
+
+    # date_blank
+    #   Requires date
+    resource_class.send(:define_method, :date_blank) do |attributes|
+      return true if attributes.blank?
+      get_val_blank(attributes, :date)
+    end
+
+    # identifier_blank
+    #   Requires identifier
+    resource_class.send(:define_method, :identifier_blank) do |attributes|
+      return true if attributes.blank?
+      get_val_blank(attributes, :identifier)
+    end
+
+    # funding reference blank
+    # Require one of the fields to be filled in
+    resource_class.send(:define_method, :fundref_blank) do |attributes|
+      return true if attributes.blank?
+      id_blank = get_val_blank(attributes, :funder_identifier)
+      name_blank = get_val_blank(attributes, :funder_name)
+      award_number_blank = get_val_blank(attributes, :award_number)
+      award_uri_blank = get_val_blank(attributes, :award_uri)
+      award_title_blank = get_val_blank(attributes, :award_title)
+      id_blank && name_blank && award_number_blank && award_uri_blank && award_title_blank
+    end
+
+    # relation_blank
+    #   Requires title / url / identifier and relationship
+    resource_class.send(:define_method, :relation_blank) do |attributes|
+      return true if attributes.blank?
+      identifiers_blank = get_id_blank(attributes, :complex_identifier_attributes)
+      title_blank = get_val_blank(attributes, :title)
+      url_blank = get_val_blank(attributes, :url)
+      rel_blank = get_val_blank(attributes, :relationship)
+      (title_blank && url_blank && identifiers_blank) || rel_blank
+    end
+
+    # get_id_blank
+    resource_class.send(:define_method, :get_id_blank) do |attributes, key|
+      identifiers_blank = true
+      identifiers = nested_to_array(attributes, key)
+      identifiers.each do |id|
+        ids = nested_to_array(id, :identifier)
+        identifiers_blank = identifiers_blank && ids.all?(&:blank?)
+      end
+      identifiers_blank
+    end
+
+  end
+end
\ No newline at end of file
diff --git a/hyrax/app/models/concerns/datacite_metadata.rb b/hyrax/app/models/concerns/datacite_metadata.rb
new file mode 100644
index 0000000000000000000000000000000000000000..2e798bd9a360d053d33acd2d361120cd81ab62d6
--- /dev/null
+++ b/hyrax/app/models/concerns/datacite_metadata.rb
@@ -0,0 +1,29 @@
+module DataciteMetadata
+  property :doi, predicate: ::RDF::Vocab::DataCite.doi, multiple: false do |index|
+    index.as :symbol
+  end
+
+  property :complex_person, predicate: ::RDF::Vocab::SIOC.has_creator, class_name:"ComplexPerson"
+
+  # property :date_published, predicate: ::RDF::Vocab::Rdms.datePublished, multiple: false do |index|
+  #   index.type :date
+  #   index.as :stored_sortable
+  # end
+
+  property :complex_date, predicate: ::RDF::Vocab::DC.date, class_name:"ComplexDate"
+
+  property :complex_identifier, predicate: ::RDF::Vocab::Rdms.identifier, class_name:"ComplexIdentifier"
+
+  # Geolocation - would this be used? Ignoring for now.
+
+  property :complex_funding_reference, predicate: ::RDF::Vocab::DataCite.fundref, class_name:"ComplexFundingReference"
+
+  property :complex_relation, predicate: ::RDF::Vocab::DC.relation, class_name:"ComplexRelation"
+
+  include ComplexValidation
+  accepts_nested_attributes_for :complex_person, reject_if: :person_blank, allow_destroy: true
+  accepts_nested_attributes_for :complex_date, reject_if: :date_blank, allow_destroy: true
+  accepts_nested_attributes_for :complex_identifier, reject_if: :identifier_blank, allow_destroy: true
+  accepts_nested_attributes_for :complex_funding_reference, reject_if: :fundref_blank, allow_destroy: true
+  accepts_nested_attributes_for :complex_relation, reject_if: :relation_blank, allow_destroy: true
+end
\ No newline at end of file
diff --git a/hyrax/app/models/crc_dataset.rb b/hyrax/app/models/crc_dataset.rb
new file mode 100644
index 0000000000000000000000000000000000000000..d0e93d366fb39fb78b142d1cc93c62144034984e
--- /dev/null
+++ b/hyrax/app/models/crc_dataset.rb
@@ -0,0 +1,109 @@
+# Generated via
+#  `rails generate hyrax:work CrcDataset`
+class CrcDataset < ActiveFedora::Base
+  include ::Hyrax::WorkBehavior
+
+  self.indexer = CrcDatasetIndexer
+  # Change this to restrict which works can be added as a child.
+  # self.valid_child_concerns = []
+  validates :title, presence: { message: 'Your CRC dataset must have a title.' }
+
+  # ------ properties from core metadata ------
+  # property date_modified          - not displayed (filled in by the system)
+  # property date_uploaded          - not displayed (filled in by the system)
+  # property depositor              - not displayed (filled in by the system)
+  # property title                  - keep
+
+  # ------ properties from basic metadata ------
+  # property alternative_title      - keep
+  # property label                  - not displayed, used for file version label
+  # property relative_path          - not displayed, used for file storage
+  # property import_url             - not displayed, used for file imports
+  # property resource_type          - keep
+  # property creator                - do not display
+  # property contributor            - do not display
+  # property description            - keep
+  # property abstract               - keep
+  # property keyword                - keep
+  # property license                - keep
+  # property rights_notes           - do not display
+  # property rights_statement       - do not display
+  # property access_right           - do not display
+  # property publisher              - keep
+  # property date_created           - do not display
+  # property subject                - keep
+  # property language               - keep
+  # property identifier             - do not display (used to store alternate identifiers)
+  # property based_near             - do not display
+  # property related_url            - do not display
+  # property bibliographic_citation - not displayed (generated by the system)
+  # property source                 - do not display (should be filled in by the system for bulk imports)
+
+  # ------ properties from Datacite metadata ------
+  property :doi, predicate: ::RDF::Vocab::DataCite.doi, multiple: false do |index|
+    index.as :symbol
+  end
+
+  property :complex_person, predicate: ::RDF::Vocab::SIOC.has_creator, class_name:"ComplexPerson"
+
+  # property :date_published, predicate: ::RDF::Vocab::Rdms.datePublished, multiple: false do |index|
+  #   index.type :date
+  #   index.as :stored_sortable
+  # end
+
+  property :complex_date, predicate: ::RDF::Vocab::DC.date, class_name:"ComplexDate"
+
+  property :complex_identifier, predicate: ::RDF::Vocab::Rdms.identifier, class_name:"ComplexIdentifier"
+
+  # size and format to be obtained from the files attached
+
+  # label property in basic metadata can be used for version label
+
+  # description and abstract is in the basic metadata. Ignoring other descriptions for now.
+
+  # Geolocation - would this be used? Ignoring for now.
+
+  property :complex_funding_reference, predicate: ::RDF::Vocab::DataCite.fundref, class_name:"ComplexFundingReference"
+
+  property :complex_relation, predicate: ::RDF::Vocab::DC.relation, class_name:"ComplexRelation"
+
+
+  # ------ properties from CRC metadata ------
+  property :crc_resource_type, predicate: ::RDF::Vocab::Rdms.crcResourceType, multiple: false do |index|
+    index.as :stored_searchable, :facetable
+  end
+
+  property :modality, predicate: ::RDF::Vocab::Rdms.modality, multiple: false do |index|
+    index.as :stored_searchable, :facetable
+  end
+
+  property :complex_subject, predicate: ::RDF::Vocab::Rdms.subject, class_name:"ComplexSubject"
+
+  property :approval_number, predicate: ::RDF::Vocab::Rdms.approvalNumber, multiple: false do |index|
+    index.as :symbol
+  end
+
+  property :extra_information, predicate: ::RDF::Vocab::Rdms.extraInformation do |index|
+    index.as :stored_searchable
+  end
+
+  property :software_version, predicate: ::RDF::Vocab::Rdms.softwareVersion do |index|
+    index.as :stored_searchable
+  end
+
+  # ------ properties from DublinCore metadata ------
+  property :coverage, predicate: ::RDF::Vocab::DC.coverage do |index|
+    index.as :stored_searchable
+  end
+
+  # This must be included at the end, because it finalizes the metadata
+  # schema (by adding accepts_nested_attributes)
+  include ::Hyrax::BasicMetadata
+  include ComplexValidation
+  accepts_nested_attributes_for :complex_person, reject_if: :person_blank, allow_destroy: true
+  accepts_nested_attributes_for :complex_date, reject_if: :date_blank, allow_destroy: true
+  accepts_nested_attributes_for :complex_identifier, reject_if: :identifier_blank, allow_destroy: true
+  accepts_nested_attributes_for :complex_funding_reference, reject_if: :fundref_blank, allow_destroy: true
+  accepts_nested_attributes_for :complex_relation, reject_if: :relation_blank, allow_destroy: true
+  accepts_nested_attributes_for :complex_subject, reject_if: :all_blank, allow_destroy: true
+end
diff --git a/hyrax/app/models/dataset.rb b/hyrax/app/models/dataset.rb
index 836578cae2f63cbfab84ac1a784f2654aa860f43..17ca0c228aa7253882f2190fe4db46d68410e520 100644
--- a/hyrax/app/models/dataset.rb
+++ b/hyrax/app/models/dataset.rb
@@ -1,3 +1,4 @@
+require "./lib/vocabularies/rdms"
 # Generated via
 #  `rails generate hyrax:work Dataset`
 class Dataset < ActiveFedora::Base
@@ -6,9 +7,73 @@ class Dataset < ActiveFedora::Base
   self.indexer = DatasetIndexer
   # Change this to restrict which works can be added as a child.
   # self.valid_child_concerns = []
-  validates :title, presence: { message: 'Your work must have a title.' }
+  validates :title, presence: { message: 'Your dataset must have a title.' }
+
+  # ------ properties from core metadata ------
+  # property date_modified          - not displayed (filled in by the system)
+  # property date_uploaded          - not displayed (filled in by the system)
+  # property depositor              - not displayed (filled in by the system)
+  # property title                  - keep
+
+  # ------ properties from basic metadata ------
+  # property alternative_title      - keep
+  # property label                  - not displayed, used for file version label
+  # property relative_path          - not displayed, used for file storage
+  # property import_url             - not displayed, used for file imports
+  # property resource_type          - keep
+  # property creator                - do not display
+  # property contributor            - do not display
+  # property description            - keep
+  # property abstract               - keep
+  # property keyword                - keep
+  # property license                - keep
+  # property rights_notes           - do not display
+  # property rights_statement       - do not display
+  # property access_right           - do not display
+  # property publisher              - keep
+  # property date_created           - do not display
+  # property subject                - keep
+  # property language               - keep
+  # property identifier             - do not display (used to store alternate identifiers)
+  # property based_near             - do not display
+  # property related_url            - do not display
+  # property bibliographic_citation - not displayed (generated by the system)
+  # property source                 - do not display (should be filled in by the system for bulk imports)
+
+  property :doi, predicate: ::RDF::Vocab::DataCite.doi, multiple: false do |index|
+    index.as :symbol
+  end
+
+  property :complex_person, predicate: ::RDF::Vocab::SIOC.has_creator, class_name:"ComplexPerson"
+
+  # property :date_published, predicate: ::RDF::Vocab::Rdms.datePublished, multiple: false do |index|
+  #   index.type :date
+  #   index.as :stored_sortable
+  # end
+
+  property :complex_date, predicate: ::RDF::Vocab::DC.date, class_name:"ComplexDate"
+
+  property :complex_identifier, predicate: ::RDF::Vocab::Rdms.identifier, class_name:"ComplexIdentifier"
+
+  # size and format to be obtained from the files attached
+
+  # label property in basic metadata can be used for version label
+
+  # description and abstract is in the basic metadata. Ignoring other descriptions for now.
+
+  # Geolocation - would this be used? Ignoring for now.
+
+  property :complex_funding_reference, predicate: ::RDF::Vocab::DataCite.fundref, class_name:"ComplexFundingReference"
+
+  property :complex_relation, predicate: ::RDF::Vocab::DC.relation, class_name:"ComplexRelation"
 
   # This must be included at the end, because it finalizes the metadata
   # schema (by adding accepts_nested_attributes)
   include ::Hyrax::BasicMetadata
+  include ComplexValidation
+  accepts_nested_attributes_for :complex_person, reject_if: :person_blank, allow_destroy: true
+  accepts_nested_attributes_for :complex_date, reject_if: :date_blank, allow_destroy: true
+  accepts_nested_attributes_for :complex_identifier, reject_if: :identifier_blank, allow_destroy: true
+  accepts_nested_attributes_for :complex_funding_reference, reject_if: :fundref_blank, allow_destroy: true
+  accepts_nested_attributes_for :complex_relation, reject_if: :relation_blank, allow_destroy: true
 end
diff --git a/hyrax/app/models/doi.rb b/hyrax/app/models/doi.rb
new file mode 100644
index 0000000000000000000000000000000000000000..7d78764eff08151572872b029e9b970c631251b6
--- /dev/null
+++ b/hyrax/app/models/doi.rb
@@ -0,0 +1,40 @@
+class DOI
+  attr_reader :identifier
+
+  DOI_URL_REGEXP = /^https?\:\/\/(?:dx\.)?doi\.org\/(.+)$/i
+  DOI_PREFIX_REGEXP = /^doi\:(?:\s*)(.+)$/i
+  INFO_DOI_PREFIX_REGEXP = /^info\:(?:\s*)doi\/(.+)$/i
+
+  def initialize(value)
+    # Example valid DOIs which should be parsed:
+    # * 10.5555/12345678
+    # * doi:10.5555/12345678
+    # * info:doi/10.5555/12345678       # from RFC4452
+    # * http://dx.doi.org/10.5555/12345678
+    # * https://doi.org/10.5555/12345678
+    # * 10/hvx
+    # * doi:10/hvx
+    # * http://doi.org/hvx
+
+    # check if the value has a doi: or url prefix, and if so, extract the raw doi
+    if (match = DOI.match_doi_prefix(value))
+      @identifier = match.captures.first
+    else
+      # the value is not a doi URL or prefixed with doi: or info:doi/, so just assume it is a raw doi
+      @identifier = value
+    end
+  end
+
+  def url
+    "https://doi.org/#{@identifier}"
+  end
+
+  def label
+    "doi:#{@identifier}"
+  end
+
+  def self.match_doi_prefix(value)
+    return nil unless value.is_a?(String) && value.present?
+    value.match(DOI_URL_REGEXP) || value.match(DOI_PREFIX_REGEXP) || value.match(INFO_DOI_PREFIX_REGEXP)
+  end
+end
diff --git a/hyrax/app/models/handle.rb b/hyrax/app/models/handle.rb
new file mode 100644
index 0000000000000000000000000000000000000000..a87518364bef3d56040825e7a584e9f58e2b82c7
--- /dev/null
+++ b/hyrax/app/models/handle.rb
@@ -0,0 +1,37 @@
+class Handle
+  attr_reader :identifier
+
+  HDL_URL_REGEXP = /^https?\:\/\/(?:hdl\.)?handle\.net\/(.+)$/i
+  HDL_PREFIX_REGEXP = /^hdl\:(?:\s*)(.+)$/i
+  INFO_HDL_PREFIX_REGEXP = /^info\:(?:\s*)hdl\/(.+)$/i
+
+  def initialize(value)
+    # Example valid Handles which should be parsed:
+    # * 4263537/400
+    # * hdl:4263537/400
+    # * info:hdl/4263537/400       # from RFC4452
+    # * http://hdl.handle.net/4263537/400
+    # * https://hdl.handle.net/4263537/400
+
+    # check if the value has a hdl: or url prefix, and if so, extract the raw handle
+    if (match = Handle.match_hdl_prefix(value))
+      @identifier = match.captures.first
+    else
+      # the value is not a handle URL or prefixed with hdl: or info:hdl/, so just assume it is a raw handle
+      @identifier = value
+    end
+  end
+
+  def url
+    "https://hdl.handle.net/#{@identifier}"
+  end
+
+  def label
+    "hdl:#{@identifier}"
+  end
+
+  def self.match_hdl_prefix(value)
+    return nil unless value.is_a?(String) && value.present?
+    value.match(HDL_URL_REGEXP) || value.match(HDL_PREFIX_REGEXP) || value.match(INFO_HDL_PREFIX_REGEXP)
+  end
+end
diff --git a/hyrax/app/models/solr_document.rb b/hyrax/app/models/solr_document.rb
index d8673266226df7c4d4977284f82bee8c5bbbfdd4..fac691a2044926c6eb2616c6982abfbbfb1792ce 100644
--- a/hyrax/app/models/solr_document.rb
+++ b/hyrax/app/models/solr_document.rb
@@ -25,4 +25,48 @@ class SolrDocument
   # Do content negotiation for AF models. 
 
   use_extension( Hydra::ContentNegotiation )
+
+  def complex_date
+    self[Solrizer.solr_name('complex_date', :displayable)]
+  end
+
+  def complex_identifier
+    self[Solrizer.solr_name('complex_identifier', :displayable)]
+  end
+
+  def complex_person
+    self[Solrizer.solr_name('complex_person', :displayable)]
+  end
+
+  def doi
+    self[Solrizer.solr_name('doi', :symbol)]
+  end
+
+  def complex_relation
+    self[Solrizer.solr_name('complex_relation', :displayable)]
+  end
+  
+  def complex_funding_reference
+    self[Solrizer.solr_name('complex_funding_reference', :displayable)]
+  end
+
+  def modality
+    self[Solrizer.solr_name('modality', :stored_searchable)]
+  end
+
+  def complex_subject
+    self[Solrizer.solr_name('complex_subject', :displayable)]
+  end
+
+  def approval_number
+    self[Solrizer.solr_name('approval_number', :symbol)]
+  end
+
+  def extra_information
+    self[Solrizer.solr_name('extra_information', :stored_searchable)]
+  end
+
+  def software_version
+    self[Solrizer.solr_name('software_version', :stored_searchable)]
+  end
 end
diff --git a/hyrax/app/presenters/hyrax/crc_dataset_presenter.rb b/hyrax/app/presenters/hyrax/crc_dataset_presenter.rb
new file mode 100644
index 0000000000000000000000000000000000000000..7f2895ced19067f58836dca28c774f010410ef7e
--- /dev/null
+++ b/hyrax/app/presenters/hyrax/crc_dataset_presenter.rb
@@ -0,0 +1,9 @@
+# Generated via
+#  `rails generate hyrax:work CrcDataset`
+module Hyrax
+  class CrcDatasetPresenter < Hyrax::WorkShowPresenter
+    delegate :complex_date, :complex_identifier, :doi, :complex_person, :complex_funding_reference,
+             :complex_relation, :modality, :complex_subject, :approval_number, :extra_information,
+             :software_version, to: :solr_document
+  end
+end
diff --git a/hyrax/app/presenters/hyrax/dataset_presenter.rb b/hyrax/app/presenters/hyrax/dataset_presenter.rb
index 5cca1d77f105a8130670d28bc654afb37fd2f3bb..8b127d9b7b5399fbdf696e2131efd449f58e5d29 100644
--- a/hyrax/app/presenters/hyrax/dataset_presenter.rb
+++ b/hyrax/app/presenters/hyrax/dataset_presenter.rb
@@ -2,5 +2,7 @@
 #  `rails generate hyrax:work Dataset`
 module Hyrax
   class DatasetPresenter < Hyrax::WorkShowPresenter
+    delegate :complex_date, :complex_identifier, :doi, :complex_person,
+             :complex_funding_reference, :complex_relation, to: :solr_document
   end
 end
diff --git a/hyrax/app/renderers/nested_attribute_renderer.rb b/hyrax/app/renderers/nested_attribute_renderer.rb
new file mode 100644
index 0000000000000000000000000000000000000000..0fa7693d42266c39e0986ee29d8b81d937bb9b52
--- /dev/null
+++ b/hyrax/app/renderers/nested_attribute_renderer.rb
@@ -0,0 +1,109 @@
+class NestedAttributeRenderer < Hyrax::Renderers::FacetedAttributeRenderer
+
+  # Draw the dl row for the attribute
+  def render_dl_row
+    markup = ''
+    return markup if values.blank? && !options[:include_empty]
+    inner_markup = ''
+    attributes = microdata_object_attributes(field).merge(class: "attribute attribute-#{field}")
+    Array(values).each do |value|
+      inner_text = attribute_value_to_html(value.to_s)
+      inner_markup << "<li#{html_attributes(attributes)}>#{inner_text}</li>" if inner_text.present?
+    end
+    
+    if !options[:include_empty] and inner_markup.present?
+      markup << %(<dt>#{label}</dt>\n<dd><ul class='tabular'>)
+      markup << inner_markup
+      markup << %(</ul></dd>)
+    end
+    sanitize(markup)
+  end
+
+  private
+
+  def parse_value(value)
+    if value.kind_of?(String)
+      value = JSON.parse(value)
+    end
+    unless value.kind_of?(Array)
+      value = [value]
+    end
+    value
+  end
+
+  def get_row(label, val)
+    row = ''
+    return row if val.blank?
+    row += '<div class="row">'
+    row += "<div class=\"col-md-3\"><label>#{label}</label></div>"
+    if label =~ /^doi$/i || DOI.match_doi_prefix(val)
+      row += "<div class=\"col-md-9\">#{get_doi_hyperlink(val)}</div>"
+    elsif Handle.match_hdl_prefix(val)
+      row += "<div class=\"col-md-9\">#{get_handle_hyperlink(val)}</div>"
+    elsif label =~/^orcid|rights$/i
+      row += "<div class=\"col-md-9\">#{get_hyperlink(val)}</div>"
+    else
+      row += "<div class=\"col-md-9\">#{val}</div>"
+    end
+    row += "</div>"
+    row
+  end
+
+  def get_label_row(label)
+    row = ''
+    row += '<div class="row label-row">'
+    row += "<div class=\"col-md-12\"><label>#{label}</label></div>"
+    row += "</div>"
+    row
+  end
+
+  def get_doi_hyperlink(val)
+    doi = DOI.new(val)
+    link_to(doi.label, doi.url, target: '_blank')
+  end
+
+  def get_handle_hyperlink(val)
+    handle = Handle.new(val)
+    link_to(handle.label, handle.url, target: '_blank')
+  end
+
+  def get_hyperlink(val)
+    Rinku.auto_link(val, :all, 'target="_blank"')
+  end
+
+  def get_nested_output(field, label, nested_value, renderer_class, display_label=false)
+    output_html = ''
+    unless nested_value.kind_of?(Array)
+      nested_value = [nested_value]
+    end
+    renderer = renderer_class.new(field, nested_value)
+    nested_value.each do |val|
+      inner_html = renderer.attribute_value_to_html(val)
+      unless inner_html.blank?
+        output_html += get_label_row(label) if display_label
+        output_html += inner_html
+      end
+    end
+    output_html
+  end
+
+  def get_inner_html(html)
+    html_out = ''
+    unless html.blank?
+      html_out = '<div class="each_metadata">'
+      html_out += html
+      html_out += '</div>'
+    end
+    html_out
+  end
+
+  def get_ouput_html(html)
+    html_out = ''
+    unless html.blank?
+      html_out = '<div class="metadata_property">'
+      html_out += html
+      html_out += '</div>'
+    end
+    html_out
+  end
+end
diff --git a/hyrax/app/renderers/nested_date_attribute_renderer.rb b/hyrax/app/renderers/nested_date_attribute_renderer.rb
new file mode 100644
index 0000000000000000000000000000000000000000..7234315907a899f17533e488d249f08ba8906e97
--- /dev/null
+++ b/hyrax/app/renderers/nested_date_attribute_renderer.rb
@@ -0,0 +1,27 @@
+class NestedDateAttributeRenderer < NestedAttributeRenderer
+  def attribute_value_to_html(input_value)
+    html = ''
+    return html if input_value.blank?
+    value = parse_value(input_value)
+    value.each do |v|
+      label = 'Date'
+      val = ''
+      if v.dig('description').present? and v['description'][0].present?
+        label = v['description'][0]
+        term = DateService.new.find_by_id(label)
+        label = term['label'] if term.any?
+      end
+      
+      if v.dig('date').present? and v['date'][0].present?
+        begin
+          val = Date.parse(v['date'][0]).to_formatted_s(:standard)
+        rescue ArgumentError
+          val = v['date'][0]
+        end
+      end
+      html += get_row(label, val)
+    end
+    html_out = get_ouput_html(html)
+    %(#{html_out})
+  end
+end
diff --git a/hyrax/app/renderers/nested_funding_reference_attribute_renderer.rb b/hyrax/app/renderers/nested_funding_reference_attribute_renderer.rb
new file mode 100644
index 0000000000000000000000000000000000000000..36619570d17949eb596c3d02d5fa5f2de8d29ff7
--- /dev/null
+++ b/hyrax/app/renderers/nested_funding_reference_attribute_renderer.rb
@@ -0,0 +1,38 @@
+class NestedFundingReferenceAttributeRenderer < NestedAttributeRenderer
+  def attribute_value_to_html(input_value)
+    html = ''
+    return html if input_value.blank?
+    value = parse_value(input_value)
+    value.each do |v|
+      each_html = ''
+      unless v.dig('funder_identifier').blank?
+        label = 'Funder identifier'
+        val = v['funder_identifier'][0]
+        each_html += get_row(label, val)
+      end
+      unless v.dig('funder_name').blank?
+        label = 'Funder name'
+        val = v['funder_name'][0]
+        each_html += get_row(label, val)
+      end
+      unless v.dig('award_number').blank?
+        label = 'Award number'
+        val = v['award_number'][0]
+        each_html += get_row(label, val)
+      end
+      unless v.dig('award_uri').blank?
+        label = 'Award URI'
+        val = v['award_uri'][0]
+        each_html += get_row(label, val)
+      end
+      unless v.dig('award_title').blank?
+        label = 'Award title'
+        val = v['award_title'][0]
+        each_html += get_row(label, val)
+      end
+      html += get_inner_html(each_html)
+    end
+    html_out = get_ouput_html(html)
+    %(#{html_out})
+  end
+end
\ No newline at end of file
diff --git a/hyrax/app/renderers/nested_identifier_attribute_renderer.rb b/hyrax/app/renderers/nested_identifier_attribute_renderer.rb
new file mode 100644
index 0000000000000000000000000000000000000000..3107036734db9302ee47288f77136cbd742615fe
--- /dev/null
+++ b/hyrax/app/renderers/nested_identifier_attribute_renderer.rb
@@ -0,0 +1,24 @@
+class NestedIdentifierAttributeRenderer < NestedAttributeRenderer
+  def attribute_value_to_html(input_value)
+    html = ''
+    return html if input_value.blank?
+    value = parse_value(input_value)
+    value.each do |v|
+      scheme = 'Identifier'
+      val = ''
+      unless v.dig('scheme').blank?
+        scheme = v['scheme'][0]
+        term = IdentifierService.new.find_by_id(v['scheme'][0])
+        scheme = term['label'] if term.any?
+      end
+
+      unless v.dig('identifier').blank?
+        val = v['identifier'][0]
+      end
+      
+      html += get_row(scheme, val)
+    end
+    html_out = get_ouput_html(html)
+    %(#{html_out})
+  end
+end
diff --git a/hyrax/app/renderers/nested_person_attribute_renderer.rb b/hyrax/app/renderers/nested_person_attribute_renderer.rb
new file mode 100644
index 0000000000000000000000000000000000000000..e64052c3bf5bd73da680712a12e9d7da23676821
--- /dev/null
+++ b/hyrax/app/renderers/nested_person_attribute_renderer.rb
@@ -0,0 +1,61 @@
+class NestedPersonAttributeRenderer < NestedAttributeRenderer
+  def attribute_value_to_html(input_value)
+    html = ''
+    return html if input_value.blank?
+    value = parse_value(input_value)
+    value.each do |v|
+      each_html = ''
+      # creator name
+      if v.dig('name').present? and v['name'][0].present?
+        label = "Name"
+        val = link_to(ERB::Util.h(v['name'][0]), search_path(v['name'][0]))
+        each_html += get_row(label, val)
+      else
+        creator_name = []
+        
+        unless v.dig('first_name').blank?
+          creator_name = v['first_name']
+        end
+
+        unless v.dig('last_name').blank?
+          creator_name += v['last_name']
+        end
+
+        creator_name = creator_name.join(' ').strip
+        if creator_name.present?
+          label = "Name"
+          val = link_to(ERB::Util.h(creator_name), search_path(creator_name))
+          each_html += get_row(label, val)
+        end
+      end
+
+      # role
+      unless v.dig('role').blank?
+        label = 'Role'
+        val = v['role'][0]
+        term = RoleService.new.find_by_id(val)
+        val = term['id'] if term.any? #using id as proxy for English-only text
+        each_html += get_row(label, val)
+      end
+
+      # Workaround for nested properties
+      # orcid
+      unless v.dig('orcid').blank?
+        label = 'ORCID'
+        val = v['orcid'][0]
+        each_html += get_row(label, val)
+      end
+
+      # affiliation
+      unless v.dig('affiliation').blank?
+        label = 'Affiliation'
+        val = v['affiliation'][0]
+        each_html += get_row(label, val)
+      end
+     
+      html += get_inner_html(each_html)
+    end
+    html_out = get_ouput_html(html)
+    %(#{html_out})
+  end
+end
diff --git a/hyrax/app/renderers/nested_relation_attribute_renderer.rb b/hyrax/app/renderers/nested_relation_attribute_renderer.rb
new file mode 100644
index 0000000000000000000000000000000000000000..8554cf43d07f5f8c279b54ea923b778bf6161507
--- /dev/null
+++ b/hyrax/app/renderers/nested_relation_attribute_renderer.rb
@@ -0,0 +1,41 @@
+class NestedRelationAttributeRenderer < NestedAttributeRenderer
+  def attribute_value_to_html(input_value)
+    html = ''
+    return html if input_value.blank?
+    value = parse_value(input_value)
+    value.each do |v|
+      each_html = ''
+      # title with url
+      title = ''
+      unless v.dig('title').blank?
+        title = v['title'][0]
+      end
+
+      unless v.dig('url').blank?
+        link = link_to(title, v['url'][0], target: :_blank)
+        title = "<span class='glyphicon glyphicon-new-window'></span>&nbsp;#{link}"
+      end
+      
+      each_html += get_row('Title', title)
+
+      # complex identifier
+      unless v.dig('complex_identifier').blank?
+        label = 'Identifier'
+        renderer_class = NestedIdentifierAttributeRenderer
+        each_html += get_nested_output(field, label, v['complex_identifier'], renderer_class, false)
+      end
+
+      # Relationship
+      unless v.dig('relationship').blank?
+        val = v['relationship'][0]
+        term = RelationshipService.new.find_by_id(val)
+        val = term['label'] if term.any?
+        each_html += get_row('Relationship', val)
+      end
+      
+      html += get_inner_html(each_html)
+    end
+    html_out = get_ouput_html(html)
+    %(#{html_out})
+  end
+end
diff --git a/hyrax/app/renderers/nested_subject_attribute_renderer.rb b/hyrax/app/renderers/nested_subject_attribute_renderer.rb
new file mode 100644
index 0000000000000000000000000000000000000000..262e3d7dbea71d0030a2b45b845f2859833bf276
--- /dev/null
+++ b/hyrax/app/renderers/nested_subject_attribute_renderer.rb
@@ -0,0 +1,43 @@
+class NestedSubjectAttributeRenderer < NestedAttributeRenderer
+  def attribute_value_to_html(input_value)
+    html = ''
+    return html if input_value.blank?
+    value = parse_value(input_value)
+    value.each do |v|
+      each_html = ''
+      # Identifier
+      unless v.dig('subject_identifier').blank?
+        label = t('rdms.fields.complex_subject_identifier')
+        val = v['subject_identifier'][0]
+        each_html += get_row(label, val)
+      end
+      # Species
+      unless v.dig('subject_species').blank?
+        label = t('rdms.fields.complex_subject_species')
+        val = v['subject_species'][0]
+        each_html += get_row(label, val)
+      end
+      # type
+      unless v.dig('subject_type').blank?
+        label = t('rdms.fields.complex_subject_type')
+        val = v['subject_type'][0]
+        each_html += get_row(label, val)
+      end
+      # sex
+      unless v.dig('subject_sex').blank?
+        label = t('rdms.fields.complex_subject_sex')
+        val = v['subject_sex'][0]
+        each_html += get_row(label, val)
+      end
+      # age
+      unless v.dig('subject_age').blank?
+        label = t('rdms.fields.complex_subject_age')
+        val = v['subject_age'][0]
+        each_html += get_row(label, val)
+      end
+      html += get_inner_html(each_html)
+    end
+    html_out = get_ouput_html(html)
+    %(#{html_out})
+  end
+end
diff --git a/hyrax/app/services/crc_resource_type_service.rb b/hyrax/app/services/crc_resource_type_service.rb
new file mode 100644
index 0000000000000000000000000000000000000000..3a68cb367af34f6d966ea15972484bc9ceafb7e2
--- /dev/null
+++ b/hyrax/app/services/crc_resource_type_service.rb
@@ -0,0 +1,6 @@
+# Provide select options for CRC Resource types
+class CrcResourceTypeService < QaSelectServiceExtended
+  def initialize(_authority_name = nil)
+    super('crc_resource_types')
+  end
+end
diff --git a/hyrax/app/services/date_service.rb b/hyrax/app/services/date_service.rb
new file mode 100644
index 0000000000000000000000000000000000000000..415253a3d496034f27a00f116b56101bde46d91d
--- /dev/null
+++ b/hyrax/app/services/date_service.rb
@@ -0,0 +1,6 @@
+# Provide select options for dates
+class DateService < QaSelectServiceExtended
+  def initialize(_authority_name = nil)
+    super('dates')
+  end
+end
diff --git a/hyrax/app/services/identifier_service.rb b/hyrax/app/services/identifier_service.rb
new file mode 100644
index 0000000000000000000000000000000000000000..2c813d44d1e6fed39fc70109267705f59e353abe
--- /dev/null
+++ b/hyrax/app/services/identifier_service.rb
@@ -0,0 +1,7 @@
+# Provide select options for analysis fields
+class IdentifierService < QaSelectServiceExtended
+  def initialize(_authority_name = nil)
+    super('identifiers')
+  end
+end
+
diff --git a/hyrax/app/services/license_service.rb b/hyrax/app/services/license_service.rb
new file mode 100644
index 0000000000000000000000000000000000000000..cd53bf4b130865f237e36ba3fc4d3840f3b945d9
--- /dev/null
+++ b/hyrax/app/services/license_service.rb
@@ -0,0 +1,6 @@
+# Provide select options for analysis fields
+class LicenseService < QaSelectServiceExtended
+  def initialize(_authority_name = nil)
+    super('licenses')
+  end
+end
diff --git a/hyrax/app/services/qa_select_service_extended.rb b/hyrax/app/services/qa_select_service_extended.rb
new file mode 100644
index 0000000000000000000000000000000000000000..ee9e47b1b177590ad5289ce09937bfcbcca3c11c
--- /dev/null
+++ b/hyrax/app/services/qa_select_service_extended.rb
@@ -0,0 +1,28 @@
+class QaSelectServiceExtended < Hyrax::QaSelectService
+  def find_by_id_or_label(term, &block)
+    a = authority.all.select { |e| (e[:label] == term || e[:id] == term) && e[:active] == true }
+    if a.any?
+      a.first
+    else
+      {}
+    end
+  end
+
+  def find_by_id(term, &block)
+    a = authority.all.select { |e| e[:id] == term && e[:active] == true }
+    if a.any?
+      a.first
+    else
+      {}
+    end
+  end
+
+  def find_by_label(term, &block)
+    a = authority.all.select { |e| e[:label] == term && e[:active] == true }
+    if a.any?
+      a.first
+    else
+      {}
+    end
+  end
+end
diff --git a/hyrax/app/services/relationship_service.rb b/hyrax/app/services/relationship_service.rb
new file mode 100644
index 0000000000000000000000000000000000000000..32b3876c1ceba1fc7da85e1df32797a3d1495a92
--- /dev/null
+++ b/hyrax/app/services/relationship_service.rb
@@ -0,0 +1,6 @@
+# Provide select options for roles
+class RelationshipService < QaSelectServiceExtended
+  def initialize(_authority_name = nil)
+    super('relationships')
+  end
+end
diff --git a/hyrax/app/services/rights_statement_service.rb b/hyrax/app/services/rights_statement_service.rb
new file mode 100644
index 0000000000000000000000000000000000000000..a601c760e1d30c31ff5368f4489f91b4e4e5ae06
--- /dev/null
+++ b/hyrax/app/services/rights_statement_service.rb
@@ -0,0 +1,6 @@
+# Provide select options for analysis fields
+class RightsStatementService < QaSelectServiceExtended
+  def initialize(_authority_name = nil)
+    super('rights_statements')
+  end
+end
diff --git a/hyrax/app/services/role_service.rb b/hyrax/app/services/role_service.rb
new file mode 100644
index 0000000000000000000000000000000000000000..d9994a903498acb8a507e7f41b0923201031f3bd
--- /dev/null
+++ b/hyrax/app/services/role_service.rb
@@ -0,0 +1,6 @@
+# Provide select options for roles
+class RoleService < QaSelectServiceExtended
+  def initialize(_authority_name = nil)
+    super('roles')
+  end
+end
diff --git a/hyrax/app/views/hyrax/crc_datasets/_attribute_rows.html.erb b/hyrax/app/views/hyrax/crc_datasets/_attribute_rows.html.erb
new file mode 100644
index 0000000000000000000000000000000000000000..4e8be8e0305416daf7ffe53cba48145838073557
--- /dev/null
+++ b/hyrax/app/views/hyrax/crc_datasets/_attribute_rows.html.erb
@@ -0,0 +1,41 @@
+<!-- Accordion wrapper -->
+<div class="panel-group" id="accordion">
+
+  <!-- Description accordion -->
+  <div class="panel panel-default">
+    <!-- Accordion header -->
+    <div class="panel-heading" data-toggle="collapse" data-target="#description">
+      <h4 class="panel-title">
+        <a class="accordion-toggle">Description</a>
+      </h4>
+    </div> <!-- Accordion header -->
+    <!-- Accordion body -->
+    <div id="description" class="panel-collapse collapse in">
+      <div class="panel-body">
+        <%= presenter.attribute_to_html(:doi, label: t('rdms.fields.doi'), html_dl: true) %>
+        <%= presenter.attribute_to_html(:title, label: t('rdms.fields.title'), html_dl: true) %>
+        <%= presenter.attribute_to_html(:alternative_title, label: t('rdms.fields.alternative_title'), html_dl: true) %>
+        <%= presenter.attribute_to_html(:complex_person, render_as: :nested_person, label: t('rdms.fields.complex_person'), html_dl: true) %>
+        <%= presenter.attribute_to_html(:resource_type, render_as: :faceted, html_dl: true) %>
+        <%= presenter.attribute_to_html(:abstract, render_as: :faceted, html_dl: true) %>
+        <%= presenter.attribute_to_html(:description, render_as: :faceted, html_dl: true) %>
+        <%= presenter.attribute_to_html(:date_created, render_as: :linked, search_field: 'date_created_tesim', html_dl: true) %>
+        <%= presenter.attribute_to_html(:date_modified, label: t('hyrax.base.show.last_modified'), html_dl: true) %>
+        <%= presenter.attribute_to_html(:complex_date, render_as: :nested_date, label: t('rdms.fields.complex_date'), html_dl: true) %>
+        <%= presenter.attribute_to_html(:complex_identifier, render_as: :nested_identifier, label: t('rdms.fields.complex_identifier'), html_dl: true) %>
+        <%= presenter.attribute_to_html(:modality, label: t('rdms.fields.modality'), html_dl: true) %>
+        <%= presenter.attribute_to_html(:complex_subject, render_as: :nested_subject, label: t('rdms.fields.complex_subject'), html_dl: true) %>
+        <%= presenter.attribute_to_html(:approval_number, label: t('rdms.fields.approval_number'), html_dl: true) %>
+        <%= presenter.attribute_to_html(:keyword, render_as: :faceted, html_dl: true) %>
+        <%= presenter.attribute_to_html(:subject, render_as: :faceted, html_dl: true) %>
+        <%= presenter.attribute_to_html(:publisher, render_as: :faceted, html_dl: true) %>
+        <%= presenter.attribute_to_html(:language, render_as: :faceted, html_dl: true) %>
+        <%= presenter.attribute_to_html(:complex_funding_reference, render_as: :nested_funding_reference, label: t('rdms.fields.complex_funding_reference'), html_dl: true) %>
+        <%= presenter.attribute_to_html(:complex_relation, render_as: :nested_relation, label: t('rdms.fields.complex_relation'), html_dl: true) %>
+        <%= presenter.attribute_to_html(:software_version, label: t('rdms.fields.software_version'), html_dl: true) %>
+        <%= presenter.attribute_to_html(:extra_information, label: t('rdms.fields.extra_information'), html_dl: true) %>
+      </div> <!-- panel body -->
+    </div> <!-- Accordion body -->
+  </div> <!-- Description accordion -->
+</div>
+<!-- Accordion wrapper -->
diff --git a/hyrax/app/views/hyrax/crc_datasets/_crc_dataset.html.erb b/hyrax/app/views/hyrax/crc_datasets/_crc_dataset.html.erb
new file mode 100644
index 0000000000000000000000000000000000000000..ddce373d3229a77cfa9f0e99ef0a4f7f358e77b3
--- /dev/null
+++ b/hyrax/app/views/hyrax/crc_datasets/_crc_dataset.html.erb
@@ -0,0 +1,2 @@
+<%# This is a search result view %>
+<%= render 'catalog/document', document: crc_dataset, document_counter: crc_dataset_counter  %>
diff --git a/hyrax/app/views/hyrax/datasets/_attribute_rows.html.erb b/hyrax/app/views/hyrax/datasets/_attribute_rows.html.erb
new file mode 100644
index 0000000000000000000000000000000000000000..f591b6dcf431b74d7557109e9e20687a132d1f20
--- /dev/null
+++ b/hyrax/app/views/hyrax/datasets/_attribute_rows.html.erb
@@ -0,0 +1,36 @@
+<!-- Accordion wrapper -->
+<div class="panel-group" id="accordion">
+
+  <!-- Description accordion -->
+  <div class="panel panel-default">
+    <!-- Accordion header -->
+    <div class="panel-heading" data-toggle="collapse" data-target="#description">
+      <h4 class="panel-title">
+        <a class="accordion-toggle">Description</a>
+      </h4>
+    </div> <!-- Accordion header -->
+    <!-- Accordion body -->
+    <div id="description" class="panel-collapse collapse in">
+      <div class="panel-body">
+        <%= presenter.attribute_to_html(:doi, label: 'DOI', html_dl: true) %>
+        <%= presenter.attribute_to_html(:title, label: t('Title'), html_dl: true) %>
+        <%= presenter.attribute_to_html(:alternative_title, label: t('rdms.fields.alternative_title'), html_dl: true) %>
+        <%= presenter.attribute_to_html(:complex_person, render_as: :nested_person, label: t('rdms.fields.complex_person'), html_dl: true) %>
+        <%= presenter.attribute_to_html(:resource_type, render_as: :faceted, html_dl: true) %>
+        <%= presenter.attribute_to_html(:abstract, render_as: :faceted, html_dl: true) %>
+        <%= presenter.attribute_to_html(:description, render_as: :faceted, html_dl: true) %>
+        <%= presenter.attribute_to_html(:keyword, render_as: :faceted, html_dl: true) %>
+        <%= presenter.attribute_to_html(:subject, render_as: :faceted, html_dl: true) %>
+        <%= presenter.attribute_to_html(:publisher, render_as: :faceted, html_dl: true) %>
+        <%= presenter.attribute_to_html(:language, render_as: :faceted, html_dl: true) %>
+        <%= presenter.attribute_to_html(:date_created, render_as: :linked, search_field: 'date_created_tesim', html_dl: true) %>
+        <%= presenter.attribute_to_html(:date_modified, label: t('hyrax.base.show.last_modified'), html_dl: true) %>
+        <%= presenter.attribute_to_html(:complex_date, render_as: :nested_date, label: t('rdms.fields.complex_date'), html_dl: true) %>
+        <%= presenter.attribute_to_html(:complex_identifier, render_as: :nested_identifier, label: t('rdms.fields.complex_identifier'), html_dl: true) %>
+        <%= presenter.attribute_to_html(:complex_funding_reference, render_as: :nested_funding_reference, label: t('rdms.fields.complex_funding_reference'), html_dl: true) %>
+        <%= presenter.attribute_to_html(:complex_relation, render_as: :nested_relation, label: t('rdms.fields.complex_relation'), html_dl: true) %>
+      </div> <!-- panel body -->
+    </div> <!-- Accordion body -->
+  </div> <!-- Description accordion -->
+</div>
+<!-- Accordion wrapper -->
diff --git a/hyrax/app/views/records/edit_fields/_complex_date.html.erb b/hyrax/app/views/records/edit_fields/_complex_date.html.erb
new file mode 100644
index 0000000000000000000000000000000000000000..727c3462aad35322ecb8e018238685057d662250
--- /dev/null
+++ b/hyrax/app/views/records/edit_fields/_complex_date.html.erb
@@ -0,0 +1,15 @@
+<div class="multi-nested multi_value custom_field">
+  <%= f.input :complex_date,
+              as: :nested_date,
+              include_blank: false,
+              input_html: {
+                class: '',
+                data: {name: :complex_date}
+              },
+              required: f.object.required?(:complex_date)
+  %>
+  <button type="button" class="btn btn-link add">
+    <span class="glyphicon glyphicon-plus"></span>
+    <span class="controls-add-text">Add another date</span>
+  </button>
+</div>
diff --git a/hyrax/app/views/records/edit_fields/_complex_funding_reference.html.erb b/hyrax/app/views/records/edit_fields/_complex_funding_reference.html.erb
new file mode 100644
index 0000000000000000000000000000000000000000..70e0ec9bfa6478f982d094b1c5ba46d0170fec6f
--- /dev/null
+++ b/hyrax/app/views/records/edit_fields/_complex_funding_reference.html.erb
@@ -0,0 +1,15 @@
+<div class="multi-nested">
+  <%= f.input :complex_funding_reference,
+              as: :nested_funding_reference,
+              include_blank: false,
+              input_html: {
+                class: '',
+                data: {name: :complex_funding_reference}
+              },
+              required: f.object.required?(:complex_funding_reference)
+  %>
+  <button type="button" class="btn btn-link add">
+    <span class="glyphicon glyphicon-plus"></span>
+    <span class="controls-add-text">Add another funding reference</span>
+  </button>
+</div>
diff --git a/hyrax/app/views/records/edit_fields/_complex_identifier.html.erb b/hyrax/app/views/records/edit_fields/_complex_identifier.html.erb
new file mode 100644
index 0000000000000000000000000000000000000000..10d01bb25c8bf1106f83f7728f3add7a006a6e91
--- /dev/null
+++ b/hyrax/app/views/records/edit_fields/_complex_identifier.html.erb
@@ -0,0 +1,15 @@
+<div class="multi-nested multi_value custom_field">
+  <%= f.input :complex_identifier,
+              as: :nested_identifier,
+              include_blank: false,
+              input_html: {
+                class: '',
+                data: {name: :complex_identifier}
+              },
+              required: f.object.required?(:complex_identifier)
+  %>
+  <button type="button" class="btn btn-link add">
+    <span class="glyphicon glyphicon-plus"></span>
+    <span class="controls-add-text">Add another identifier</span>
+  </button>
+</div>
diff --git a/hyrax/app/views/records/edit_fields/_complex_person.html.erb b/hyrax/app/views/records/edit_fields/_complex_person.html.erb
new file mode 100644
index 0000000000000000000000000000000000000000..f33ada4ffe7a3099456b3ab9ba63907c91b6bd53
--- /dev/null
+++ b/hyrax/app/views/records/edit_fields/_complex_person.html.erb
@@ -0,0 +1,15 @@
+<div class="multi-nested multi_value custom_field">
+  <%= f.input :complex_person,
+              as: :nested_person,
+              include_blank: false,
+              input_html: {
+                class: '',
+                data: {name: :complex_person}
+              },
+              required: f.object.required?(:complex_person)
+  %>
+  <button type="button" class="btn btn-link add">
+    <span class="glyphicon glyphicon-plus"></span>
+    <span class="controls-add-text">Add another person</span>
+  </button>
+</div>
diff --git a/hyrax/app/views/records/edit_fields/_complex_relation.html.erb b/hyrax/app/views/records/edit_fields/_complex_relation.html.erb
new file mode 100644
index 0000000000000000000000000000000000000000..d6b0648c6c7c6f7277d81b07c14e837986ddf578
--- /dev/null
+++ b/hyrax/app/views/records/edit_fields/_complex_relation.html.erb
@@ -0,0 +1,15 @@
+<div class="multi-nested">
+  <%= f.input :complex_relation,
+              as: :nested_relation,
+              include_blank: false,
+              input_html: {
+                class: '',
+                data: {name: :complex_relation}
+              },
+              required: f.object.required?(:complex_relation)
+  %>
+  <button type="button" class="btn btn-link add">
+    <span class="glyphicon glyphicon-plus"></span>
+    <span class="controls-add-text">Add another relationship</span>
+  </button>
+</div>
diff --git a/hyrax/app/views/records/edit_fields/_complex_subject.html.erb b/hyrax/app/views/records/edit_fields/_complex_subject.html.erb
new file mode 100644
index 0000000000000000000000000000000000000000..7f385432e595b0d4426b26a27335b86442f7ffb7
--- /dev/null
+++ b/hyrax/app/views/records/edit_fields/_complex_subject.html.erb
@@ -0,0 +1,15 @@
+<div class="multi-nested multi_value custom_field">
+  <%= f.input :complex_subject,
+              as: :nested_subject,
+              include_blank: false,
+              input_html: {
+                class: '',
+                data: {name: :complex_subject}
+              },
+              required: f.object.required?(:complex_subject)
+  %>
+  <button type="button" class="btn btn-link add">
+    <span class="glyphicon glyphicon-plus"></span>
+    <span class="controls-add-text">Add another subject</span>
+  </button>
+</div>
diff --git a/hyrax/app/views/records/edit_fields/_crc_resource_type.html.erb b/hyrax/app/views/records/edit_fields/_crc_resource_type.html.erb
new file mode 100644
index 0000000000000000000000000000000000000000..40848b2ab82f71a3acd1dd8780e4c2e441fb0e94
--- /dev/null
+++ b/hyrax/app/views/records/edit_fields/_crc_resource_type.html.erb
@@ -0,0 +1,7 @@
+<% service = CrcResourceTypeService.new %>
+<%= f.input :crc_resource_type, as: :select,
+            collection: service.select_all_options,
+            include_blank: true,
+            item_helper: service.method(:include_current_value),
+            input_html: { class: 'form-control' }
+%>
diff --git a/hyrax/config/application.rb b/hyrax/config/application.rb
index 4d48786828e8ced9c4223d81f9fbf840e56c42e6..a3cf5499a6b3adfc04f2c38d340187b289ac7e9b 100644
--- a/hyrax/config/application.rb
+++ b/hyrax/config/application.rb
@@ -15,5 +15,17 @@ module Hyrax3App
     # Application configuration can go into files in config/initializers
     # -- all .rb files in that directory are automatically loaded after loading
     # the framework and any gems in your application.
+
+    config.active_job.queue_adapter = :sidekiq
+
+    # The locale is set by a query parameter, so if it's not found render 404
+    config.action_dispatch.rescue_responses.merge!(
+      'I18n::InvalidLocale' => :not_found
+    )
+    config.i18n.default_locale = :en
+    config.i18n.available_locales = [:en, :de]
+    config.i18n.fallbacks = [:en]
+
+    config.action_dispatch.default_headers['Referrer-Policy'] = 'strict-origin-when-cross-origin'
   end
 end
diff --git a/hyrax/config/authorities/crc_resource_types.yml b/hyrax/config/authorities/crc_resource_types.yml
new file mode 100644
index 0000000000000000000000000000000000000000..202e45f1b32ae62b182045ae5ddb1c44a93a97e0
--- /dev/null
+++ b/hyrax/config/authorities/crc_resource_types.yml
@@ -0,0 +1,7 @@
+terms:
+  - id: Analysed
+    term: Analysed
+  - id: Measured
+    term: Measured
+  - id: Simulated
+    term: Simulated
diff --git a/hyrax/config/authorities/dates.yml b/hyrax/config/authorities/dates.yml
new file mode 100644
index 0000000000000000000000000000000000000000..61038691d668dcd70b1669a80509f94f342f3645
--- /dev/null
+++ b/hyrax/config/authorities/dates.yml
@@ -0,0 +1,31 @@
+terms:
+  - id: http://purl.org/dc/terms/dateAccepted
+    term: Accepted
+    active: true
+  - id: Available
+    term: Available (Embargo release)
+    active: true
+  - id: http://bibframe.org/vocab/copyrightDate
+    term: Copyrighted
+    active: true
+  - id: Collected
+    term: Collected
+    active: true
+  - id: http://purl.org/dc/terms/created
+    term: Created
+    active: true
+  - id: Deposited
+    term: Deposited
+    active: true
+  - id: record date
+    term: Record date
+    active: true
+  - id: Registered
+    term: Registered
+    active: true
+  - id: http://bibframe.org/vocab/providerDate
+    term: Submitted
+    active: true
+  - id: http://bibframe.org/vocab/changeDate
+    term: Updated
+    active: true
diff --git a/hyrax/config/authorities/identifiers.yml b/hyrax/config/authorities/identifiers.yml
new file mode 100644
index 0000000000000000000000000000000000000000..9d9ccbd0b978a73ba4c4b3c8aca40ffa2401a884
--- /dev/null
+++ b/hyrax/config/authorities/identifiers.yml
@@ -0,0 +1,43 @@
+terms:
+  - id: group id
+    term: Group id
+    active: true
+  - id: identifier local
+    term: Identifier - Local
+    active: true
+  - id: identifier persistent
+    term: Identifier - Persistent
+    active: true
+  - id: DOI
+    term: DOI
+    active: true
+  - id: handle identifier
+    term: Handle identifier
+    active: true
+  - id: edu person principal name
+    term: eduPersonPrincipalName
+    active: true
+  - id: edu person targeted id
+    term: eduPersonTargetedID
+    active: true
+  - id: full image url
+    term: Full Image URL
+    active: true
+  - id: ORCID
+    term: ORCID
+    active: true
+  - id: project id
+    term: Project ID
+    active: true
+  - id: thumbnail image url
+    term: Thumbnail Image URL
+    active: true
+  - id: web image url
+    term: Web Image URL
+    active: true
+  - id: previous identifier
+    term: Previous identifier
+    active: true
+  - id: other
+    term: Other
+    active: true
diff --git a/hyrax/config/authorities/licenses.yml b/hyrax/config/authorities/licenses.yml
index 89dd602ade439e648242728c2a5aef31ad821801..653aff475bd9e071f6aa91c14d0e74b1ead69f2e 100644
--- a/hyrax/config/authorities/licenses.yml
+++ b/hyrax/config/authorities/licenses.yml
@@ -1,25 +1,7 @@
 terms:
-    - id: http://creativecommons.org/licenses/by/3.0/us/
-      term: Attribution 3.0 United States
-      active: false
-    - id: http://creativecommons.org/licenses/by-sa/3.0/us/
-      term: Attribution-ShareAlike 3.0 United States
-      active: false
-    - id: http://creativecommons.org/licenses/by-nc/3.0/us/
-      term: Attribution-NonCommercial 3.0 United States
-      active: false
-    - id: http://creativecommons.org/licenses/by-nd/3.0/us/
-      term: Attribution-NoDerivs 3.0 United States
-      active: false
-    - id: http://creativecommons.org/licenses/by-nc-nd/3.0/us/
-      term: Attribution-NonCommercial-NoDerivs 3.0 United States
-      active: false
-    - id: http://creativecommons.org/licenses/by-nc-sa/3.0/us/
-      term: Attribution-NonCommercial-ShareAlike 3.0 United States
-      active: false
-    - id: http://www.europeana.eu/portal/rights/rr-r.html
-      term: All rights reserved
-      active: false
+    - id: http://rightsstatements.org/vocab/InC/1.0/
+      term: In Copyright
+      active: true
     - id: https://creativecommons.org/licenses/by/4.0/
       term: Creative Commons BY Attribution 4.0 International
       active: true
@@ -44,3 +26,12 @@ terms:
     - id: http://creativecommons.org/publicdomain/mark/1.0/
       term: Creative Commons Public Domain Mark 1.0
       active: true
+    - id: http://www.apache.org/licenses/LICENSE-2.0
+      term: Apache License 2.0
+      active: true
+    - id: http://www.gnu.org/licenses/gpl.html
+      term: GNU General Public License
+      active: true
+    - id: http://opensource.org/licenses/MIT
+      term: MIT License
+      active: true
diff --git a/hyrax/config/authorities/relationships.yml b/hyrax/config/authorities/relationships.yml
new file mode 100644
index 0000000000000000000000000000000000000000..601ce6d33dab7c1e37c09a31266cbe04298ea58a
--- /dev/null
+++ b/hyrax/config/authorities/relationships.yml
@@ -0,0 +1,112 @@
+terms:
+  - id: cites
+    term: cites
+    active: true
+  - id: isCitedBy
+    term: is cited by
+    active: true
+  - id: isSupplementTo
+    term: is supplement to
+    active: true
+  - id: isSupplementedBy
+    term: is supplemented by
+    active: true
+  - id: continues
+    term: continues
+    active: true
+  - id: isContinuedBy
+    term: is continued by
+    active: true
+  - id: hasMetadata
+    term: has metadata
+    active: true
+  - id: isMetadataFor
+    term: is metadata for
+    active: true
+  - id: isNewVersionOf
+    term: is new version of
+    active: true
+  - id: isPreviousVersionOf
+    term: is previous version of
+    active: true
+  - id: isPartOf
+    term: is part of
+    active: true
+  - id: hasPart
+    term: has part
+    active: true
+  - id: isReferencedBy
+    term: is referenced by
+    active: true
+  - id: references
+    term: references
+    active: true
+  - id: isDocumentedBy
+    term: is documented by
+    active: true
+  - id: documents
+    term: documents
+    active: true
+  - id: isCompiledBy
+    term: is compiled by
+    active: true
+  - id: compiles
+    term: compiles
+    active: true
+  - id: isVariantFormOf
+    term: is variant form of
+    active: true
+  - id: isOriginalFormOf
+    term: is original form of
+    active: true
+  - id: isIdenticalTo
+    term: is identical to
+    active: true
+  - id: isReviewedBy
+    term: is reviewed by
+    active: true
+  - id: reviews
+    term: reviews
+    active: true
+  - id: isDerivedFrom
+    term: is derived from
+    active: true
+  - id: isSourceOf
+    term: is source of
+    active: true
+  - id: isCommentOn
+    term: is comment on
+    active: true
+  - id: hasComment
+    term: has comment
+    active: true
+  - id: isReplyTo
+    term: is reply to
+    active: true
+  - id: hasReply
+    term: has reply
+    active: true
+  - id: basedOnData
+    term: based on data
+    active: true
+  - id: hasRelatedMaterial
+    term: has related material
+    active: true
+  - id: isBasedOn
+    term: is based on
+    active: true
+  - id: isBasisFor
+    term: is basis for
+    active: true
+  - id: requires
+    term: requires
+    active: true
+  - id: isRequiredBy
+    term: is required by
+    active: true
+  - id: hasParent
+    term: has parent
+    active: true
+  - id: isParentOf
+    term: is parent of
+    active: true
diff --git a/hyrax/config/authorities/resource_types.yml b/hyrax/config/authorities/resource_types.yml
index 69c22256a6287501d9b005fa94fd48c7c7ee3b3c..5eeb04c5ef0c177015fa6c50740ce11f6c68a437 100644
--- a/hyrax/config/authorities/resource_types.yml
+++ b/hyrax/config/authorities/resource_types.yml
@@ -5,8 +5,6 @@ terms:
     term: Audio
   - id: Book
     term: Book
-  - id: Capstone Project
-    term: Capstone Project
   - id: Conference Proceeding
     term: Conference Proceeding
   - id: Dataset
@@ -17,22 +15,14 @@ terms:
     term: Image
   - id: Journal
     term: Journal
-  - id: Map or Cartographic Material
-    term: Map or Cartographic Material
-  - id: Masters Thesis
-    term: Masters Thesis
   - id: Part of Book
     term: Part of Book
   - id: Poster
     term: Poster
   - id: Presentation
     term: Presentation
-  - id: Project
-    term: Project
   - id: Report
     term: Report
-  - id: Research Paper
-    term: Research Paper
   - id: Software or Program Code
     term: Software or Program Code
   - id: Video
diff --git a/hyrax/config/authorities/rights_statements.yml b/hyrax/config/authorities/rights_statements.yml
index 719e7576afebfdc69fd54dfeeec47c50ffcc4672..653aff475bd9e071f6aa91c14d0e74b1ead69f2e 100644
--- a/hyrax/config/authorities/rights_statements.yml
+++ b/hyrax/config/authorities/rights_statements.yml
@@ -1,37 +1,37 @@
 terms:
-- id: http://rightsstatements.org/vocab/InC/1.0/
-  term: "In Copyright"
-  active: true
-- id: http://rightsstatements.org/vocab/InC-OW-EU/1.0/
-  term: "In Copyright - EU Orphan Work"
-  active: true
-- id: http://rightsstatements.org/vocab/InC-EDU/1.0/
-  term: "In Copyright - Educational Use Permitted"
-  active: true
-- id: http://rightsstatements.org/vocab/InC-NC/1.0/
-  term: "In Copyright - Non-Commercial Use Permitted"
-  active: true
-- id: http://rightsstatements.org/vocab/InC-RUU/1.0/
-  term: "In Copyright - Rights-holder(s) Unlocatable or Unidentifiable"
-  active: true
-- id: http://rightsstatements.org/vocab/NoC-CR/1.0/
-  term: "No Copyright - Contractual Restrictions"
-  active: true
-- id: http://rightsstatements.org/vocab/NoC-NC/1.0/
-  term: "No Copyright - Non-Commercial Use Only "
-  active: true
-- id: http://rightsstatements.org/vocab/NoC-OKLR/1.0/
-  term: "No Copyright - Other Known Legal Restrictions"
-  active: true
-- id: http://rightsstatements.org/vocab/NoC-US/1.0/
-  term: "No Copyright - United States"
-  active: true
-- id: http://rightsstatements.org/vocab/CNE/1.0/
-  term: "Copyright Not Evaluated"
-  active: true
-- id: http://rightsstatements.org/vocab/UND/1.0/
-  term: "Copyright Undetermined"
-  active: true
-- id: http://rightsstatements.org/vocab/NKC/1.0/
-  term: "No Known Copyright"
-  active: true
+    - id: http://rightsstatements.org/vocab/InC/1.0/
+      term: In Copyright
+      active: true
+    - id: https://creativecommons.org/licenses/by/4.0/
+      term: Creative Commons BY Attribution 4.0 International
+      active: true
+    - id: https://creativecommons.org/licenses/by-sa/4.0/
+      term: Creative Commons BY-SA Attribution-ShareAlike 4.0 International
+      active: true
+    - id: https://creativecommons.org/licenses/by-nd/4.0/
+      term: Creative Commons BY-ND Attribution-NoDerivatives 4.0 International
+      active: true
+    - id: https://creativecommons.org/licenses/by-nc/4.0/
+      term: Creative Commons BY-NC Attribution-NonCommercial 4.0 International
+      active: true
+    - id: https://creativecommons.org/licenses/by-nc-nd/4.0/
+      term: Creative Commons BY-NC-ND Attribution-NonCommercial-NoDerivs 4.0 International
+      active: true
+    - id: https://creativecommons.org/licenses/by-nc-sa/4.0/
+      term: Creative Commons BY-NC-SA Attribution-NonCommercial-ShareAlike 4.0 International
+      active: true
+    - id: http://creativecommons.org/publicdomain/zero/1.0/
+      term: Creative Commons CC0 1.0 Universal
+      active: true
+    - id: http://creativecommons.org/publicdomain/mark/1.0/
+      term: Creative Commons Public Domain Mark 1.0
+      active: true
+    - id: http://www.apache.org/licenses/LICENSE-2.0
+      term: Apache License 2.0
+      active: true
+    - id: http://www.gnu.org/licenses/gpl.html
+      term: GNU General Public License
+      active: true
+    - id: http://opensource.org/licenses/MIT
+      term: MIT License
+      active: true
diff --git a/hyrax/config/authorities/roles.yml b/hyrax/config/authorities/roles.yml
new file mode 100644
index 0000000000000000000000000000000000000000..93e508a38308f9de9178bd3dbcb211667c24c59d
--- /dev/null
+++ b/hyrax/config/authorities/roles.yml
@@ -0,0 +1,22 @@
+terms:
+  - id: author
+    term: author
+    active: true
+  - id: editor
+    term: editor
+    active: true
+  - id: translator
+    term: translator
+    active: true
+  - id: data depositor
+    term: data depositor
+    active: true
+  - id: data curator
+    term: data curator
+    active: true
+  - id: contact person
+    term: non-author contact
+    active: true
+  - id: operator
+    term: operator
+    active: true
diff --git a/hyrax/config/environments/development.rb b/hyrax/config/environments/development.rb
index 298ac03b7407c42ae47be21d961be4b1a0b639ea..3fe58d504e1b05a5505c4f1a0b0e4bfd9e036407 100644
--- a/hyrax/config/environments/development.rb
+++ b/hyrax/config/environments/development.rb
@@ -30,6 +30,9 @@ Rails.application.configure do
   # Store uploaded files on the local file system (see config/storage.yml for options)
   config.active_storage.service = :local
 
+  # Use Sidekiq to process background jobs
+  config.active_job.queue_adapter = :sidekiq
+
   # Don't care if the mailer can't send.
   config.action_mailer.raise_delivery_errors = false
 
@@ -55,9 +58,12 @@ Rails.application.configure do
   # Raises error for missing translations
   # config.action_view.raise_on_missing_translations = true
 
+  Rails.application.routes.default_url_options = { host: ENV.fetch('APP_HOST') { 'localhost' } }
+
   # Use an evented file watcher to asynchronously detect changes in source code,
   # routes, locales, etc. This feature depends on the listen gem.
   config.file_watcher = ActiveSupport::EventedFileUpdateChecker
+  config.web_console.whitelisted_ips = ["172.0.0.0/8", '192.168.0.0/16', '127.0.0.1']
 
   config.force_ssl = false
   if ENV['APP_HOST'].present?
diff --git a/hyrax/config/environments/production.rb b/hyrax/config/environments/production.rb
index 52dfc44d78208f962ddf371af5722811ef6b2c08..cf85b5457a0c4491960f476e2aad61aaa4af4f10 100644
--- a/hyrax/config/environments/production.rb
+++ b/hyrax/config/environments/production.rb
@@ -17,6 +17,9 @@ Rails.application.configure do
   config.consider_all_requests_local       = false
   config.action_controller.perform_caching = true
 
+  # Use Sidekiq to process background jobs
+  config.active_job.queue_adapter = :sidekiq
+
   # Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"]
   # or in config/master.key. This key is used to decrypt credentials (and other encrypted files).
   # config.require_master_key = true
@@ -54,15 +57,41 @@ Rails.application.configure do
   # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ]
 
   # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
-  # config.force_ssl = true
+  if ENV["RAILS_FORCE_SSL"].present? && (ENV["RAILS_FORCE_SSL"].to_s.downcase == 'false') then
+    config.force_ssl = false
+    Rails.application.routes.default_url_options = \
+      Hyrax::Engine.routes.default_url_options = \
+      {protocol: 'http', host: ENV['APP_HOST']}
+    config.application_url = "http://#{ENV['APP_HOST']}"
+  else
+    config.force_ssl = true #default if nothing specified is more secure.
+    Rails.application.routes.default_url_options = \
+      Hyrax::Engine.routes.default_url_options = \
+      {protocol: 'https', host: ENV['APP_HOST']}
+    config.application_url = "https://#{ENV['APP_HOST']}"
+  end
 
-  # Use the lowest log level to ensure availability of diagnostic information
+  # Use the lowest log level (:debug) to ensure availability of diagnostic information
   # when problems arise.
   config.log_level = :info
 
   # Prepend all log lines with the following tags.
   config.log_tags = [ :request_id ]
 
+  # Use default logging formatter so that PID and timestamp are not suppressed.
+  config.log_formatter = ::Logger::Formatter.new
+
+  # Use a different logger for distributed setups.
+  # require 'syslog/logger'
+  # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name')
+
+  if ENV["RAILS_LOG_TO_STDOUT"].present?
+    logger = ActiveSupport::Logger.new(STDOUT)
+    logger.formatter = config.log_formatter
+    config.logger = ActiveSupport::TaggedLogging.new(logger)
+  end
+
+
   # Use a different cache store in production.
   # config.cache_store = :mem_cache_store
 
@@ -83,33 +112,15 @@ Rails.application.configure do
   # Send deprecation notices to registered listeners.
   config.active_support.deprecation = :notify
 
-  # Use default logging formatter so that PID and timestamp are not suppressed.
-  config.log_formatter = ::Logger::Formatter.new
-
-  # Use a different logger for distributed setups.
-  # require 'syslog/logger'
-  # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name')
-
-  if ENV["RAILS_LOG_TO_STDOUT"].present?
-    logger           = ActiveSupport::Logger.new(STDOUT)
-    logger.formatter = config.log_formatter
-    config.logger    = ActiveSupport::TaggedLogging.new(logger)
-  end
-
   # Do not dump schema after migrations.
   config.active_record.dump_schema_after_migration = false
 
-  if ENV["RAILS_FORCE_SSL"].present? && (ENV["RAILS_FORCE_SSL"].to_s.downcase == 'false') then
-    config.force_ssl = false
-    Rails.application.routes.default_url_options = \
-      Hyrax::Engine.routes.default_url_options = \
-      {protocol: 'http', host: ENV['APP_HOST']}
-    config.application_url = "http://#{ENV['APP_HOST']}"
-  else
-    config.force_ssl = true #default if nothing specified is more secure.
-    Rails.application.routes.default_url_options = \
-      Hyrax::Engine.routes.default_url_options = \
-      {protocol: 'https', host: ENV['APP_HOST']}
-    config.application_url = "https://#{ENV['APP_HOST']}"
-  end
+  config.action_mailer.perform_deliveries = true
+  config.action_mailer.delivery_method = :smtp
+  config.action_mailer.smtp_settings = {
+    address: ENV['SMTP_HOST'],
+    port: ENV['SMTP_PORT'],
+    enable_starttls_auto: false
+  }
+
 end
diff --git a/hyrax/config/environments/test.rb b/hyrax/config/environments/test.rb
index 77553f7e7faff573cfe37d6d1fa3bac331025bb9..6546a62d81bbc63a65257000aecf9831bd6351cb 100644
--- a/hyrax/config/environments/test.rb
+++ b/hyrax/config/environments/test.rb
@@ -15,7 +15,7 @@ Rails.application.configure do
   # Configure public file server for tests with Cache-Control for performance.
   config.public_file_server.enabled = true
   config.public_file_server.headers = {
-    'Cache-Control' => "public, max-age=#{1.hour.to_i}"
+    'Cache-Control' => "public, max-age=#{1.hour.seconds.to_i}"
   }
 
   # Show full error reports and disable caching.
@@ -44,6 +44,9 @@ Rails.application.configure do
   # Raises error for missing translations
   # config.action_view.raise_on_missing_translations = true
 
+  # Don't use redis for storing queued jobs when running tests
+  config.active_job.queue_adapter = :test
+
   if ENV['APP_HOST'].present?
     Rails.application.routes.default_url_options = {protocol: 'http', host: ENV['APP_HOST']}
     Hyrax::Engine.routes.default_url_options = {protocol: 'http', host: ENV['APP_HOST']}
diff --git a/hyrax/config/initializers/clamav.rb b/hyrax/config/initializers/clamav.rb
index 7dcb60d7e3425d53447fc1a3babc650bd8ba3a12..7cd26d6a28befc8f9639aa7adc17df41afdcc07e 100644
--- a/hyrax/config/initializers/clamav.rb
+++ b/hyrax/config/initializers/clamav.rb
@@ -1,2 +1 @@
-# frozen_string_literal: true
 ClamAV.instance.loaddb if defined? ClamAV
diff --git a/hyrax/config/initializers/hyrax.rb b/hyrax/config/initializers/hyrax.rb
index f470e5472516290cd4fdfb29cc6cc870ffd079dd..70ad02ac8dbf57dd8f1a541c0f882baeb69d7d27 100644
--- a/hyrax/config/initializers/hyrax.rb
+++ b/hyrax/config/initializers/hyrax.rb
@@ -2,6 +2,8 @@
 Hyrax.config do |config|
   # Injected via `rails g hyrax:work Dataset`
   config.register_curation_concern :dataset
+  # Injected via `rails g hyrax:work CrcDataset`
+  config.register_curation_concern :crc_dataset
   # Register roles that are expected by your implementation.
   # @see Hyrax::RoleRegistry for additional details.
   # @note there are magical roles as defined in Hyrax::RoleRegistry::MAGIC_ROLES
@@ -60,14 +62,18 @@ Hyrax.config do |config|
   # where NOID = 10-character string and UUID = 32-character string w/ hyphens
   config.enable_noids = true
 
-  # Template for your repository's NOID IDs
-  # config.noid_template = ".reeddeeddk"
+  # In a test environment, use the file-based NOID generator to avoid problems with the tables being wiped between tests
+  if ENV['RAILS_ENV'] == 'test'
+    puts 'Using file-based NOIDs for tests'
+    # Template for your repository's NOID IDs
+    config.noid_template = "tzrb-.zddddd"
 
-  # Use the database-backed minter class
-  # config.noid_minter_class = Noid::Rails::Minter::Db
+    # Use the database-backed minter class
+    config.noid_minter_class = Noid::Rails::Minter::File
 
-  # Store identifier minter's state in a file for later replayability
-  # config.minter_statefile = '/tmp/minter-state'
+    # Store identifier minter's state in a file for later replayability
+    config.minter_statefile = 'tmp/minter'
+  end
 
   # Prefix for Redis keys
   # config.redis_namespace = "hyrax"
diff --git a/hyrax/config/initializers/redis_config.rb b/hyrax/config/initializers/redis_config.rb
index 313cf19c3d9cdb2ff364b34c0893a38623fa5088..aad03ef3bd37c82ffe4d2eac4eca36dcfe779324 100644
--- a/hyrax/config/initializers/redis_config.rb
+++ b/hyrax/config/initializers/redis_config.rb
@@ -1,4 +1,3 @@
-# frozen_string_literal: true
 require 'redis'
 config = YAML.safe_load(ERB.new(IO.read(Rails.root.join('config', 'redis.yml'))).result)[Rails.env].with_indifferent_access
 Redis.current = Redis.new(config.merge(thread_safe: true))
diff --git a/hyrax/config/initializers/riiif.rb b/hyrax/config/initializers/riiif.rb
index b29200cc14e01296b83444d626df80d3914f6266..76e7e59bf93779a8317a5920a32ec4c59f0d8b11 100644
--- a/hyrax/config/initializers/riiif.rb
+++ b/hyrax/config/initializers/riiif.rb
@@ -1,4 +1,3 @@
-# frozen_string_literal: true
 ActiveSupport::Reloader.to_prepare do
   Riiif::Image.file_resolver = Riiif::HttpFileResolver.new
   Riiif::Image.info_service = lambda do |id, _file|
diff --git a/hyrax/config/initializers/sidekiq.rb b/hyrax/config/initializers/sidekiq.rb
new file mode 100644
index 0000000000000000000000000000000000000000..351838d72a28f03922915e37236944b360b45fdf
--- /dev/null
+++ b/hyrax/config/initializers/sidekiq.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+config = YAML.safe_load(ERB.new(IO.read(Rails.root + 'config' + 'redis.yml')).result)[Rails.env].with_indifferent_access
+redis_config = config.merge(thread_safe: true)
+
+Sidekiq::Logging.logger.level = Logger::WARN if ENV['RAILS_ENV'] == 'production'
+
+redis_conn = { url: "redis://#{config[:host]}:#{config[:port]}/" }
+
+Sidekiq.configure_server do |s|
+  # s.redis = redis_conn
+  s.redis = redis_config
+end
+
+Sidekiq.configure_client do |s|
+  # s.redis = redis_conn
+  s.redis = redis_config
+end
diff --git a/hyrax/config/initializers/simple_form.rb b/hyrax/config/initializers/simple_form.rb
index b92657cf48025dcb9d8db06241ea0abad4fb8952..a561d3acb2fd9f7a37004dd60345ffb061eb773e 100644
--- a/hyrax/config/initializers/simple_form.rb
+++ b/hyrax/config/initializers/simple_form.rb
@@ -1,4 +1,3 @@
-# frozen_string_literal: true
 # NOTE: This is a modified version of simple_form's default config file.
 #       The only changes were to move the input to after the hint and error.
 
diff --git a/hyrax/config/initializers/simple_form_bootstrap.rb b/hyrax/config/initializers/simple_form_bootstrap.rb
index 094cb3bcd70c681b1b69dab194654aa674c21866..c5ee0408dbde5d739095396aa771c8085b99f73a 100644
--- a/hyrax/config/initializers/simple_form_bootstrap.rb
+++ b/hyrax/config/initializers/simple_form_bootstrap.rb
@@ -1,4 +1,3 @@
-# frozen_string_literal: true
 # NOTE: This is a modified version of simple_form's default config file.
 #       The only changes were to move the inputs to after the hints and errors.
 
diff --git a/hyrax/config/initializers/work_form_override.rb b/hyrax/config/initializers/work_form_override.rb
new file mode 100644
index 0000000000000000000000000000000000000000..174cb570b028cf32c9f9185403692de4a3271275
--- /dev/null
+++ b/hyrax/config/initializers/work_form_override.rb
@@ -0,0 +1,10 @@
+# Overriding changes introduced to the initialize_field method in Hyrax commit 
+# https://github.com/samvera/hyrax/commit/89ffdb757a7ae545e303919d2277901237a5fd30
+# To solve issue https://github.com/CottageLabs/willow-hyrax/issues/6
+Rails.configuration.to_prepare do
+  Hyrax::Forms::WorkForm.class_eval do
+    def initialize_field(key)
+      super unless [:embargo_release_date, :lease_expiration_date].include?(key)
+    end
+  end
+end
diff --git a/hyrax/config/locales/crc_dataset.de.yml b/hyrax/config/locales/crc_dataset.de.yml
new file mode 100644
index 0000000000000000000000000000000000000000..cce037314ebeb66a9b5d9084a9c3683cd61f2a48
--- /dev/null
+++ b/hyrax/config/locales/crc_dataset.de.yml
@@ -0,0 +1,8 @@
+de:
+  hyrax:
+    icons:
+      crc_dataset:     'fa fa-file-text-o'
+    select_type:
+      crc_dataset:
+        description:        "Crc dataset Werke"
+        name:               "Crc Dataset"
diff --git a/hyrax/config/locales/crc_dataset.en.yml b/hyrax/config/locales/crc_dataset.en.yml
new file mode 100644
index 0000000000000000000000000000000000000000..23309b6904902ccaf47da688fea3179d83a745c2
--- /dev/null
+++ b/hyrax/config/locales/crc_dataset.en.yml
@@ -0,0 +1,21 @@
+en:
+  hyrax:
+    icons:
+      crc_dataset:     'fa fa-file-text-o'
+    select_type:
+      crc_dataset:
+        description:        "CRC dataset works"
+        name:               "CRC Dataset"
+  simple_form:
+    labels:
+      dataset:
+        complex_date:                     "Date"
+        complex_funding_reference:        "Funding reference"
+        complex_identifier:               "Identifiers"
+        complex_person:                   "Creators and Contributors"
+        complex_relation:                 "Related items"
+        complex_subject:                  "Subject"
+        approval_number:                  "Animal/Ethics Approval No."
+        extra_information:                "Extra information"
+        modality:                         "Modality"
+        software_version:                 "Software version"
diff --git a/hyrax/config/locales/crc_dataset.es.yml b/hyrax/config/locales/crc_dataset.es.yml
new file mode 100644
index 0000000000000000000000000000000000000000..fd578d048872176a13d7c1a54960d255cb747d3a
--- /dev/null
+++ b/hyrax/config/locales/crc_dataset.es.yml
@@ -0,0 +1,10 @@
+es:
+  hyrax:
+    icons:
+      crc_dataset:     'fa fa-file-text-o'
+    select_type:
+      crc_dataset:
+        # TODO: translate `human_name` into Spanish
+        description:        "Crc dataset trabajos"
+        name:               "Crc Dataset"
+        # TODO: translate `human_name` into Spanish
diff --git a/hyrax/config/locales/crc_dataset.fr.yml b/hyrax/config/locales/crc_dataset.fr.yml
new file mode 100644
index 0000000000000000000000000000000000000000..7cb6311ab8b1af8c7373cc14de7be3d4236b6e77
--- /dev/null
+++ b/hyrax/config/locales/crc_dataset.fr.yml
@@ -0,0 +1,8 @@
+fr:
+  hyrax:
+    icons:
+      crc_dataset:     'fa fa-file-text-o'
+    select_type:
+      crc_dataset:
+        description:        "Crc dataset Å“uvres"
+        name:               "Crc Dataset"
diff --git a/hyrax/config/locales/crc_dataset.it.yml b/hyrax/config/locales/crc_dataset.it.yml
new file mode 100644
index 0000000000000000000000000000000000000000..de9cf83f2e471946ad58c21c30e7da4ca8a369cc
--- /dev/null
+++ b/hyrax/config/locales/crc_dataset.it.yml
@@ -0,0 +1,8 @@
+it:
+  hyrax:
+    icons:
+      crc_dataset:     'fa fa-file-text-o'
+    select_type:
+      crc_dataset:
+        description:        "Crc dataset opere"
+        name:               "Crc Dataset"
diff --git a/hyrax/config/locales/crc_dataset.pt-BR.yml b/hyrax/config/locales/crc_dataset.pt-BR.yml
new file mode 100644
index 0000000000000000000000000000000000000000..35e3d451fe4964329e5bee74ea5c786f14246b78
--- /dev/null
+++ b/hyrax/config/locales/crc_dataset.pt-BR.yml
@@ -0,0 +1,8 @@
+pt-BR:
+  hyrax:
+    icons:
+      crc_dataset:     'fa fa-file-text-o'
+    select_type:
+      crc_dataset:
+        description:        "Crc dataset obras"
+        name:               "Crc Dataset"
diff --git a/hyrax/config/locales/crc_dataset.zh.yml b/hyrax/config/locales/crc_dataset.zh.yml
new file mode 100644
index 0000000000000000000000000000000000000000..30a9be85fd43db0e6be4749abddf1ee3a61d7577
--- /dev/null
+++ b/hyrax/config/locales/crc_dataset.zh.yml
@@ -0,0 +1,10 @@
+zh:
+  hyrax:
+    icons:
+      crc_dataset:     'fa fa-file-text-o'
+    select_type:
+      crc_dataset:
+        # TODO: translate `human_name` into Chinese
+        description:        "Crc dataset 作品"
+        name:               "Crc Dataset"
+        # TODO: translate `human_name` into Chinese
diff --git a/hyrax/config/locales/dataset.en.yml b/hyrax/config/locales/dataset.en.yml
index dc79608c678531dbff3b83948229cb8aa24efb1c..60dd81276355c3f40a98a2b751b2985c60a78090 100644
--- a/hyrax/config/locales/dataset.en.yml
+++ b/hyrax/config/locales/dataset.en.yml
@@ -6,3 +6,12 @@ en:
       dataset:
         description:        "Dataset works"
         name:               "Dataset"
+  simple_form:
+    labels:
+      dataset:
+        complex_date:                     "Date"
+        complex_funding_reference:        "Funding reference"
+        complex_identifier:               "Identifiers"
+        complex_person:                   "Creators and Contributors"
+        complex_relation:                 "Related items"
+        complex_subject:                  "Subject"
diff --git a/hyrax/config/locales/en.yml b/hyrax/config/locales/en.yml
index decc5a85735df127f96b89043ed6c6911713941a..ae4051ff4f4da708db2839dec58ec720fa73059f 100644
--- a/hyrax/config/locales/en.yml
+++ b/hyrax/config/locales/en.yml
@@ -30,4 +30,41 @@
 # available at http://guides.rubyonrails.org/i18n.html.
 
 en:
-  hello: "Hello world"
+  rdms:
+    fields:
+      alternative_title:        "Alternative title"
+      approval_number:          "Animal/Ethics Approval No."
+      complex_date:             "Date"
+      complex_funding_reference: "Funding reference"
+      complex_identifier:       "Identifier"
+      complex_person:           "Creator or Contributor"
+      complex_relation:         "Related item"
+      complex_subject:          "Medical Subject"
+      complex_subject_identifier: "Subject identifier"
+      complex_subject_species:  "Subject species"
+      complex_subject_type:     "Subject type"
+      complex_subject_sex:      "Subject sex"
+      complex_subject_age:      "Subject age"
+      doi:                      "DOI"
+      extra_information:        "Extra information"
+      first_name:               "Given name"
+      full_name:                "Full name"
+      last_name:                "Surname"
+      modality:                 "Modality"
+      software_version:         "Software version"
+      title:                    "Title"
+  simple_form:
+    hints:
+      defaults:
+        title: The title of your work.
+        abstract: Abstract or a free-text description for the work.
+        alternative_title: Alternative title or the title in another language, if applicable.
+        keyword: '<strong>Note:</strong> To enter multiple keywords, click on "Add another Keyword." Do not list them in a single field.'
+        license: License for your work.
+        resource_type: Type of content. More than one type may be selected.
+        publisher: The person or group making the work available.
+        complex_person: 'Authors or contributors to the work. Enter the Surname, the Given name, and the full name again in "Name", and select their Role.'
+        complex_identifier: External identifiers, if applicable.
+    placeholders:
+      defaults:
+        language: "De"
diff --git a/hyrax/config/locales/hyrax.en.yml b/hyrax/config/locales/hyrax.en.yml
index 15fc22f4168dd28a4aa6b310b34541a2986d435b..111713e54b67fdd830bd0a147369bca52ea1275e 100644
--- a/hyrax/config/locales/hyrax.en.yml
+++ b/hyrax/config/locales/hyrax.en.yml
@@ -12,7 +12,63 @@ en:
           language_sim: Language
           publisher_sim: Publisher
           subject_sim: Subject
+          # complex date
+          complex_date_dtsim: Date
+          complex_date_accepted_dtsim: Date accepted
+          complex_year_accepted_sim: Date accepted
+          complex_date_available_dtsim: Date available
+          complex_year_available_sim: Date available
+          complex_date_copyrighted_dtsim: Date copyrighted
+          complex_year_copyrighted_sim: Date copyrighted
+          complex_date_collected_dtsim: Date collected
+          complex_year_collected_sim: Date collected
+          complex_date_created_dtsim: Date created
+          complex_year_created_sim: Date created
+          complex_date_issued_dtsim: Date issued
+          complex_year_issued_sim: Date issued
+          complex_date_published_dtsim: Date published
+          complex_year_published_sim: Date published
+          complex_date_submitted_dtsim: Date submitted
+          complex_year_submitted_sim: Date submitted
+          complex_date_updated_dtsim: Date updated
+          complex_year_updated_sim: Date updated
+          complex_date_valid_dtsim: Date valid
+          complex_year_valid_sim: Date valid
+          complex_date_processed_dtsim: Date processed
+          complex_year_processed_sim: Date processed
+          complex_date_purchased_dtsim: Date purchased
+          complex_year_purchased_sim: Date purchased
+          complex_date_other_dtsim: Date
+          complex_year_other_sim: Date
+          # complex funder
+          funder_sim: Funder
+          # complex identifier
+          complex_identifier_ssm: Identifier
+          complex_identifier_group_id_ssim: Group id
+          complex_identifier_project_id_ssim: Project id
+          # complex person
+          complex_person_sim: Creator
+          complex_person_other_sim: Creator
+          complex_person_author_sim: Author
+          complex_person_editor_sim: Editor
+          complex_person_translator_sim: Translator
+          complex_person_data_depositor_sim: Data depositor
+          complex_person_data_curator_sim: Data curator
+          complex_person_contact_person_sim: Contact person
+          complex_person_corresponding_author_sim: Corresponding author
+          complex_person_operator_sim: Operator
+          complex_person_affiliation_sim: Affiliation
+          # complex relation
+          complex_relation_relationship_sim: Relationship
+          # complex subject
+          complex_subject_species_sim: Subject species
+          complex_subject_type_sim: Subject type
+          complex_subject_sex_sim: Subject sex
+          complex_subject_age_sim: Subject age
+          # modality
+          modality_sim: Modality
         index:
+          approval_number_ssm: Animal/Ethics Approval No.
           based_near_tesim: Location
           contributor_tesim: Contributor
           creator_tesim: Creator
@@ -20,15 +76,64 @@ en:
           date_modified_dtsi: Date Modified
           date_uploaded_dtsi: Date Uploaded
           description_tesim: Description
+          extra_information_tesim: Extra information
           file_format_tesim: File Format
           identifier_tesim: Identifier
           keyword_tesim: Keyword
           language_tesim: Language
           license_tesim: License
+          modality_tesim: Modality
           publisher_tesim: Publisher
           rights_statement_tesim: Rights Statement
+          software_version_tesim: Software version
           subject_tesim: Subject
+          # complex date
+          complex_date_dtsim: Date
+          complex_date_accepted_dtsim: Date accepted
+          complex_date_available_dtsim: Date available
+          complex_date_copyrighted_dtsim: Date copyrighted
+          complex_date_collected_dtsim: Date collected
+          complex_date_created_dtsim: Date created
+          complex_date_issued_dtsim: Date issued
+          complex_date_published_dtsim: Date published
+          complex_date_submitted_dtsim: Date submitted
+          complex_date_updated_dtsim: Date updated
+          complex_date_valid_dtsim: Date valid
+          complex_date_processed_dtsim: Date processed
+          complex_date_purchased_dtsim: Date purchased
+          complex_date_other_dtsim: Date
+          # complex funder
+          award_title_tesim: Award title
+          award_number_ssim: Award number
+          funder_tesim: Funder
+          funder_identifier_ssim: Funder identifier
+          # complex identifier
+          complex_identifier_ssim: Identifier
+          complex_identifier_orcid_ssim: ORCID
+          complex_identifier_local_id_ssim: Local identifier
+          complex_identifier_group_id_ssim: Group id
+          complex_identifier_project_id_ssim: Project id
+          # complex person
+          complex_person_other_tesim: Creator
+          complex_person_author_tesim: Author
+          complex_person_editor_tesim: Editor
+          complex_person_translator_tesim: Translator
+          complex_person_data_depositor_tesim: Data depositor
+          complex_person_data_curator_tesim: Data curator
+          complex_person_operator_tesim: Operator
+          complex_person_contact_person_tesim: Contact person
+          complex_person_organization_tesim: Organization
+          complex_person_sub_organization_tesim: Sub organization
+          # complex relation
+          complex_relation_title_tesim: Title of related item
+          # complex subject
+          complex_subject_identifier_ssim: Subject identifier
+          complex_subject_species_tesim: Subject species
+          complex_subject_type_tesim: Subject type
+          complex_subject_sex_tesim: Subject sex
+          complex_subject_age_tesim: Subject age
         show:
+          approval_number_ssm: Animal/Ethics Approval No.
           based_near_tesim: Location
           contributor_tesim: Contributor
           creator_tesim: Creator
@@ -36,15 +141,53 @@ en:
           date_modified_dtsi: Date Modified
           date_uploaded_dtsi: Date Uploaded
           description_tesim: Description
+          extra_information_tesim: Extra information
           file_format_tesim: File Format
           identifier_tesim: Identifier
           keyword_tesim: Keyword
           language_tesim: Language
           license_tesim: License
+          modality_tesim: Modality
           publisher_tesim: Publisher
           rights_statement_tesim: Rights Statement
+          software_version_tesim: Software version
           subject_tesim: Subject
           title_tesim: Title
+          # complex date
+          complex_date_ssm: Date
+          complex_date_accepted_ssm: Date accepted
+          complex_date_available_ssm: Date available
+          complex_date_copyrighted_ssm: Date copyrighted
+          complex_date_collected_ssm: Date collected
+          complex_date_created_ssm: Date created
+          complex_date_issued_ssm: Date issued
+          complex_date_published_ssm: Date published
+          complex_date_submitted_ssm: Date submitted
+          complex_date_updated_ssm: Date updated
+          complex_date_valid_ssm: Date valid
+          complex_date_processed_ssm: Date processed
+          complex_date_purchased_ssm: Date purchased
+          complex_date_other_ssm: Date
+          # complex funding
+          complex_funding_reference_ssm: Funding reference
+          # complex identifier
+          complex_identifier_ssm: Identifier
+          # complex persom
+          complex_person_ssm: Creator
+          complex_person_other_ssm: Creator
+          complex_person_author_ssm: Author
+          complex_person_editor_ssm: Editor
+          complex_person_translator_ssm: Translator
+          complex_person_data_depositor_ssm: Data depositor
+          complex_person_data_curator_ssm: Data curator
+          complex_person_operator_ssm: Operator
+          complex_person_contact_person_ssm: Contact person
+          complex_person_affiliation_ssm: Affiliation
+          complex_person_identifier_ssm: Orcid
+          # Complex relation
+          complex_relation_ssm: Related item
+          # Complex subject
+          complex_subject_ssm: Subject
   hyrax:
     account_name: My Institution Account Id
     directory:
diff --git a/hyrax/config/routes.rb b/hyrax/config/routes.rb
index 8b03d5478635511f83677c9f0338aaf82ccb4817..af02a3550c857bd20ccbf7102374202612e50424 100644
--- a/hyrax/config/routes.rb
+++ b/hyrax/config/routes.rb
@@ -12,6 +12,11 @@ Rails.application.routes.draw do
   devise_for :users
   mount Hydra::RoleManagement::Engine => '/'
 
+  authenticate :user, lambda { |u| u.admin? } do
+    require 'sidekiq/web'
+    mount Sidekiq::Web => '/sidekiq'
+  end
+
   mount Qa::Engine => '/authorities'
   mount Hyrax::Engine, at: '/'
   resources :welcome, only: 'index'
@@ -30,5 +35,7 @@ Rails.application.routes.draw do
       delete 'clear'
     end
   end
+
+
   # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
 end
diff --git a/hyrax/config/solr_wrapper_test.yml b/hyrax/config/solr_wrapper_test.yml
index 46571e69ed2846a248f105487810d7d21fe4b6b5..82cd86755dec5ec5b11d3294c8728c4b21928f25 100644
--- a/hyrax/config/solr_wrapper_test.yml
+++ b/hyrax/config/solr_wrapper_test.yml
@@ -1,7 +1,10 @@
 #config/solr_wrapper_test.yml
-# version: 6.1.0
+version: 8.11.2
 port: 8985
 instance_dir: tmp/solr-test
+solr_options: {
+  force: true
+}
 collection:
     persist: false
     dir: solr/conf
diff --git a/hyrax/lib/vocabularies/rdms.rb b/hyrax/lib/vocabularies/rdms.rb
new file mode 100644
index 0000000000000000000000000000000000000000..840df402833734c3767857599f47401a59329c33
--- /dev/null
+++ b/hyrax/lib/vocabularies/rdms.rb
@@ -0,0 +1,30 @@
+module RDF
+  module Vocab
+    class Rdms < RDF::Vocabulary("http://www.example.com/vocabs/rdms/")
+      property 'Subject' # class for Rdms.subject
+      property 'datePublished'
+      property 'identifier'
+      property 'order'
+      property 'versionLabel'
+      # vocabulary to store funding information
+      property 'FundingReference'
+      property 'funderIdentifier'
+      property 'funderName'
+      property 'awardNumber'
+      property 'awardTitle'
+      property 'awardURI'
+      # CRC metadata
+      property 'approvalNumber'
+      property 'extraInformation'
+      property 'modality'
+      property 'crcResourceType'
+      property 'softwareVersion'
+      property 'subject' # of type Rdms.Subject
+      property 'SubjectIdentifier'
+      property 'SubjectSpecies'
+      property 'SubjectType'
+      property 'SubjectSex'
+      property 'SubjectAge'
+    end
+  end
+end
diff --git a/hyrax/spec/actors/hyrax/actors/crc_dataset_actor_spec.rb b/hyrax/spec/actors/hyrax/actors/crc_dataset_actor_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..bdcc0d83200289c3f1adb6ce041ce28ed74b2567
--- /dev/null
+++ b/hyrax/spec/actors/hyrax/actors/crc_dataset_actor_spec.rb
@@ -0,0 +1,9 @@
+# Generated via
+#  `rails generate hyrax:work CrcDataset`
+require 'rails_helper'
+
+RSpec.describe Hyrax::Actors::CrcDatasetActor do
+  it "has tests" do
+    skip "Add your tests here"
+  end
+end
diff --git a/hyrax/spec/controllers/hyrax/crc_datasets_controller_spec.rb b/hyrax/spec/controllers/hyrax/crc_datasets_controller_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..c288ba14599b01e669005ca6494e49173b8f1310
--- /dev/null
+++ b/hyrax/spec/controllers/hyrax/crc_datasets_controller_spec.rb
@@ -0,0 +1,9 @@
+# Generated via
+#  `rails generate hyrax:work CrcDataset`
+require 'rails_helper'
+
+RSpec.describe Hyrax::CrcDatasetsController do
+  it "has tests" do
+    skip "Add your tests here"
+  end
+end
diff --git a/hyrax/spec/factories/admin_sets.rb b/hyrax/spec/factories/admin_sets.rb
new file mode 100644
index 0000000000000000000000000000000000000000..fb85e397eedba992b0cbcfc67494112fc20d175c
--- /dev/null
+++ b/hyrax/spec/factories/admin_sets.rb
@@ -0,0 +1,30 @@
+FactoryBot.define do
+  factory :admin_set do
+    sequence(:title) { |n| ["Title #{n}"] }
+
+    # Given the relationship between permission template and admin set, when
+    # an admin set is created via a factory, I believe it is appropriate to go ahead and
+    # create the corresponding permission template
+    #
+    # This way, we can go ahead
+    after(:create) do |admin_set, evaluator|
+      if evaluator.with_permission_template
+        attributes = { source_id: admin_set.id }
+        attributes = evaluator.with_permission_template.merge(attributes) if evaluator.with_permission_template.respond_to?(:merge)
+        # There is a unique constraint on permission_templates.source_id; I don't want to
+        # create a permission template if one already exists for this admin_set
+        # create(:permission_template, attributes) unless Hyrax::PermissionTemplate.find_by(source_id: admin_set.id)
+        create(:permission_template, attributes) unless Hyrax::PermissionTemplate.find_by(source_id: admin_set.id)
+        # Hyrax::Workflow::WorkflowFactory.create
+        Hyrax::Workflow::WorkflowImporter.load_workflows
+        mediated_workflow = admin_set.permission_template.available_workflows.where(name: "nims_mediated_deposit").first
+        Sipity::Workflow.activate!(permission_template: admin_set.permission_template, workflow_id: mediated_workflow.id)
+      end
+    end
+
+    transient do
+      # false, true, or Hash with keys for permission_template
+      with_permission_template { false }
+    end
+  end
+end
\ No newline at end of file
diff --git a/hyrax/spec/factories/crc_dataset.rb b/hyrax/spec/factories/crc_dataset.rb
new file mode 100644
index 0000000000000000000000000000000000000000..1338f0ee25029fde7c619d6a077dc800151a4e2f
--- /dev/null
+++ b/hyrax/spec/factories/crc_dataset.rb
@@ -0,0 +1,199 @@
+FactoryBot.define do
+
+  factory :crc_dataset do
+    transient do
+      user { create(:user) }
+      # Set to true (or a hash) if you want to create an admin set
+      with_admin_set { false }
+    end
+
+    title { ["CRC dataset"] }
+    access_control
+
+    trait :open do
+      visibility { 'open' }
+      title { ["Open CRC dataset"] }
+    end
+
+    trait :authenticated do
+      visibility { 'authenticated' }
+      title { ["Authenticated CRC dataset"] }
+    end
+
+    trait :embargo do
+      visibility { 'embargo' }
+      title { ["Embargo CRC dataset"] }
+    end
+
+    trait :lease do
+      visibility { 'lease' }
+      title { ["Leased CRC dataset"] }
+    end
+
+    trait :restricted do
+      visibility { 'restricted' }
+      title { ["Restricted CRC dataset"] }
+    end
+
+    trait :with_abstract do
+      abstract { ["Abstract-Description-123"] }
+    end
+
+    trait :with_abstract_seq do
+      sequence(:abstract) { |n| ["Abstract-#{n}"] }
+    end
+
+    trait :with_alternative_title do
+      alternative_title { ['Alternative-Title-123'] }
+    end
+
+    trait :with_keyword do
+      keyword { ['Keyword-123'] }
+    end
+
+    trait :with_keyword_seq do
+      sequence(:keyword) { |n| ["Keyword-#{n}"] }
+    end
+
+    trait :with_subject do
+      subject { ['Subject-123'] }
+    end
+
+    trait :with_subject_seq do
+      sequence(:subject) { |n| ["Subject-#{n}"] }
+    end
+
+    trait :with_language do
+      language { ['Faroese'] }
+    end
+
+    trait :with_publisher do
+      publisher { ['Publisher-123'] }
+    end
+
+    trait :with_resource_type do
+      resource_type { ['Resource-Type-123'] }
+    end
+
+    trait :with_article_resource_type do
+      resource_type { ['Article'] }
+    end
+
+    trait :with_source do
+      source { ['Source-123'] }
+    end
+
+    trait :with_doi do
+      doi { "1234-1567" }
+    end
+
+    trait :with_complex_person do
+      complex_person_attributes {
+        [{
+           name: 'Anamika',
+           role: ['operator'],
+           orcid: '123456',
+           affiliation: 'University',
+         }]
+      }
+    end
+
+    trait :with_complex_author do
+      complex_person_attributes {
+        [{
+           name: 'Anamika',
+           role: ['author'],
+           orcid: '123456',
+           affiliation: 'University',
+         }]
+      }
+    end
+
+    trait :with_detailed_complex_author do
+      complex_person_attributes {
+        [{
+           name: 'Anamika',
+           first_name: 'First name',
+           last_name: 'Last name',
+           email: 'a@example.com',
+           role: ['author'],
+           orcid: '23542345234',
+           affiliation: 'My org'
+         }]
+      }
+    end
+
+    trait :with_detailed_complex_people do
+      complex_person_attributes {
+        [{
+           name: 'Anamika',
+           first_name: 'First name',
+           last_name: 'Last name',
+           role: ['author'],
+           orcid: '123456',
+           affiliation: 'University',
+         },
+         {
+           name: 'Cee Jay',
+           first_name: 'First name',
+           last_name: 'Last name',
+           role: ['editor'],
+           orcid: '112233445566',
+           affiliation: 'My journal org'
+         }]
+      }
+    end
+
+    trait :with_complex_date do
+      complex_date_attributes {
+        [{
+           date: '1978-10-28',
+           description: 'Collected'
+         }]
+      }
+    end
+
+    trait :with_complex_identifier do
+      complex_identifier_attributes {
+        [{
+           identifier: '10.0.1111',
+           scheme: 'DOI'
+         }]
+      }
+    end
+
+    trait :with_complex_relation do
+      complex_relation_attributes {
+        [{
+           title: 'A relation label',
+           url: 'http://example.com/relation',
+           complex_identifier_attributes: [{
+                                             identifier: ['info:hdl/4263537/400'],
+                                             scheme: 'identifier persistent'
+                                           }],
+           relationship: 'isNewVersionOf'
+         }]
+      }
+    end
+
+    trait :with_rights do
+      rights_statement {
+        [
+          'http://creativecommons.org/publicdomain/zero/1.0/'
+        ]
+      }
+    end
+
+    trait :with_complex_funding_reference do
+      complex_funding_reference_attributes {
+        [{
+           funder_identifier: 'f1234',
+           funder_name: 'Bank',
+           award_number: 'a1234',
+           award_uri: 'http://example.com/a1234',
+           award_title: 'No free lunch'
+         }]
+      }
+    end
+  end
+end
diff --git a/hyrax/spec/factories/dataset.rb b/hyrax/spec/factories/dataset.rb
new file mode 100644
index 0000000000000000000000000000000000000000..4a49f859a2a8c36d50b44efad15b41dd8c50dd4f
--- /dev/null
+++ b/hyrax/spec/factories/dataset.rb
@@ -0,0 +1,199 @@
+FactoryBot.define do
+
+  factory :dataset do
+    transient do
+      user { create(:user) }
+      # Set to true (or a hash) if you want to create an admin set
+      with_admin_set { false }
+    end
+
+    title { ["Dataset"] }
+    access_control
+
+    trait :open do
+      visibility { 'open' }
+      title { ["Open Dataset"] }
+    end
+
+    trait :authenticated do
+      visibility { 'authenticated' }
+      title { ["Authenticated Dataset"] }
+    end
+
+    trait :embargo do
+      visibility { 'embargo' }
+      title { ["Embargo Dataset"] }
+    end
+
+    trait :lease do
+      visibility { 'lease' }
+      title { ["Leased Dataset"] }
+    end
+
+    trait :restricted do
+      visibility { 'restricted' }
+      title { ["Restricted Dataset"] }
+    end
+
+    trait :with_abstract do
+      abstract { ["Abstract-Description-123"] }
+    end
+
+    trait :with_abstract_seq do
+      sequence(:abstract) { |n| ["Abstract-#{n}"] }
+    end
+
+    trait :with_alternative_title do
+      alternative_title { ['Alternative-Title-123'] }
+    end
+
+    trait :with_keyword do
+      keyword { ['Keyword-123'] }
+    end
+
+    trait :with_keyword_seq do
+      sequence(:keyword) { |n| ["Keyword-#{n}"] }
+    end
+
+    trait :with_subject do
+      subject { ['Subject-123'] }
+    end
+
+    trait :with_subject_seq do
+      sequence(:subject) { |n| ["Subject-#{n}"] }
+    end
+
+    trait :with_language do
+      language { ['Faroese'] }
+    end
+
+    trait :with_publisher do
+      publisher { ['Publisher-123'] }
+    end
+
+    trait :with_resource_type do
+      resource_type { ['Resource-Type-123'] }
+    end
+
+    trait :with_article_resource_type do
+      resource_type { ['Article'] }
+    end
+
+    trait :with_source do
+      source { ['Source-123'] }
+    end
+
+    trait :with_doi do
+      doi { "1234-1567" }
+    end
+
+    trait :with_complex_person do
+       complex_person_attributes {
+         [{
+          name: 'Anamika',
+          role: ['operator'],
+          orcid: '123456',
+          affiliation: 'University',
+        }]
+       }
+    end
+
+    trait :with_complex_author do
+      complex_person_attributes {
+        [{
+         name: 'Anamika',
+         role: ['author'],
+         orcid: '123456',
+         affiliation: 'University',
+        }]
+      }
+    end
+
+    trait :with_detailed_complex_author do
+      complex_person_attributes {
+        [{
+          name: 'Anamika',
+          first_name: 'First name',
+          last_name: 'Last name',
+          email: 'a@example.com',
+          role: ['author'],
+          orcid: '23542345234',
+          affiliation: 'My org'
+        }]
+      }
+    end
+
+    trait :with_detailed_complex_people do
+      complex_person_attributes {
+        [{
+           name: 'Anamika',
+           first_name: 'First name',
+           last_name: 'Last name',
+           role: ['author'],
+           orcid: '123456',
+           affiliation: 'University',
+          },
+          {
+           name: 'Cee Jay',
+           first_name: 'First name',
+           last_name: 'Last name',
+           role: ['editor'],
+           orcid: '112233445566',
+           affiliation: 'My journal org'
+        }]
+      }
+    end
+
+    trait :with_complex_date do
+      complex_date_attributes {
+        [{
+          date: '1978-10-28',
+          description: 'Collected'
+         }]
+      }
+    end
+
+    trait :with_complex_identifier do
+      complex_identifier_attributes {
+        [{
+             identifier: '10.0.1111',
+             scheme: 'DOI'
+        }]
+      }
+    end
+
+    trait :with_complex_relation do
+      complex_relation_attributes {
+        [{
+          title: 'A relation label',
+          url: 'http://example.com/relation',
+          complex_identifier_attributes: [{
+            identifier: ['info:hdl/4263537/400'],
+            scheme: 'identifier persistent'
+          }],
+          relationship: 'isNewVersionOf'
+        }]
+      }
+    end
+
+    trait :with_rights do
+      rights_statement {
+        [
+          'http://creativecommons.org/publicdomain/zero/1.0/'
+        ]
+      }
+    end
+
+    trait :with_complex_funding_reference do
+      complex_funding_reference_attributes {
+        [{
+           funder_identifier: 'f1234',
+           funder_name: 'Bank',
+           award_number: 'a1234',
+           award_uri: 'http://example.com/a1234',
+           award_title: 'No free lunch'
+         }]
+      }
+    end
+  end
+end
diff --git a/hyrax/spec/factories/fedora.rb b/hyrax/spec/factories/fedora.rb
new file mode 100644
index 0000000000000000000000000000000000000000..6e9919f73e8338ae05b16c3da98102fac241f914
--- /dev/null
+++ b/hyrax/spec/factories/fedora.rb
@@ -0,0 +1,24 @@
+FactoryBot.define do
+
+  factory :access_control, class: Hydra::AccessControl do
+    #visibility Hydra::AccessControls::AccessRight::VISIBILITY_TEXT_VALUE_PRIVATE
+    #access_rights Hydra::AccessControls::AccessRight::VISIBILITY_TEXT_VALUE_PRIVATE
+
+    skip_create
+  end
+
+  factory :list_source, class: ActiveFedora::Aggregation::ListSource do
+    skip_create
+  end
+
+  factory :relation, class: ActiveTriples::Relation do
+    skip_create
+  end
+
+  trait :private do
+    visibility { Hydra::AccessControls::AccessRight::VISIBILITY_TEXT_VALUE_PRIVATE }
+  end
+
+
+end
+
diff --git a/hyrax/spec/factories/file_sets.rb b/hyrax/spec/factories/file_sets.rb
new file mode 100644
index 0000000000000000000000000000000000000000..00542d640876c3d4fbd9f4133c68fa7ec6d7cda1
--- /dev/null
+++ b/hyrax/spec/factories/file_sets.rb
@@ -0,0 +1,45 @@
+FactoryBot.define do
+  factory :file_set do
+    transient do
+      user { create(:user) }
+      content { File.open('spec/fixtures/csv/example.csv', 'r') }
+    end
+    after(:build) do |fs, evaluator|
+      fs.apply_depositor_metadata evaluator.user.user_key
+    end
+
+    after(:create) do |file, evaluator|
+      Hydra::Works::UploadFileToFileSet.call(file, evaluator.content) if evaluator.content
+    end
+
+    trait :open do
+      visibility { 'open' }
+      title { ["Open File Set"] }
+    end
+
+    trait :authenticated do
+      visibility { 'authenticated' }
+      title { ["Authenticated File Set"] }
+    end
+
+    trait :embargo do
+      visibility { 'embargo' }
+      title { ["Embargo File Set"] }
+    end
+
+    trait :lease do
+      visibility { 'lease' }
+      title { ["Leased File Set"] }
+    end
+
+    trait :restricted do
+      visibility { 'restricted' }
+      title { ["Restricted File Set"] }
+    end
+
+    trait :long_filename do
+      visibility { 'open' }
+      content { File.open('spec/fixtures/csv/ファイル名の長さがエスケープ後に256文字以上になる、長い日本語のファイル名を持つファイル.csv', 'r') }
+    end
+  end
+end
diff --git a/hyrax/spec/factories/override_new_record.rb b/hyrax/spec/factories/override_new_record.rb
new file mode 100644
index 0000000000000000000000000000000000000000..0ecda3953c3292be680d3eb61ba4e854a59b42c8
--- /dev/null
+++ b/hyrax/spec/factories/override_new_record.rb
@@ -0,0 +1,11 @@
+FactoryBot.define do
+  trait :override_new_record do
+    after(:build) do |instance|
+      # we need to override the new_record? method; otherwise it will check with via the Fedora API and initiate an HTTP call
+      instance.define_singleton_method(:new_record?) do
+        true
+      end
+    end
+  end
+end
+
diff --git a/hyrax/spec/factories/permission_templates.rb b/hyrax/spec/factories/permission_templates.rb
new file mode 100644
index 0000000000000000000000000000000000000000..776b478267ca354b9267bcf1150cc2e5daf6002b
--- /dev/null
+++ b/hyrax/spec/factories/permission_templates.rb
@@ -0,0 +1,79 @@
+FactoryBot.define do
+  factory :permission_template, class: Hyrax::PermissionTemplate do
+    # Given that there is a one to one strong relation between permission_template and admin_set,
+    # with a unique index on the source_id, I don't want to have duplication in source_id
+    sequence(:source_id) { |n| format("%010d", n) }
+
+    before(:create) do |permission_template, evaluator|
+      if evaluator.with_admin_set
+        source_id = permission_template.source_id
+        admin_set =
+          if source_id.present?
+            begin
+              AdminSet.find(source_id)
+            rescue ActiveFedora::ObjectNotFoundError
+              create(:admin_set, id: source_id)
+            end
+          else
+            create(:admin_set)
+          end
+        permission_template.source_id = admin_set.id
+      elsif evaluator.with_collection
+        source_id = permission_template.source_id
+        collection =
+          if source_id.present?
+            begin
+              Collection.find(source_id)
+            rescue ActiveFedora::ObjectNotFoundError
+              create(:collection, id: source_id)
+            end
+          else
+            create(:collection)
+          end
+        permission_template.source_id = collection.id
+      end
+    end
+
+    after(:create) do |permission_template, evaluator|
+      if evaluator.with_workflows
+        Hyrax::Workflow::WorkflowImporter.load_workflow_for(permission_template: permission_template)
+        Sipity::Workflow.activate!(permission_template: permission_template, workflow_id: permission_template.available_workflows.pluck(:id).first)
+      end
+      if evaluator.with_active_workflow
+        workflow = create(:workflow, active: true, permission_template: permission_template)
+        create(:workflow_action, workflow: workflow) # Need to create a single action that can be taken
+      end
+      AccessHelper.create_access(permission_template, 'user', :manage, evaluator.manage_users) if evaluator.manage_users.present?
+      AccessHelper.create_access(permission_template, 'group', :manage, evaluator.manage_groups) if evaluator.manage_groups.present?
+      AccessHelper.create_access(permission_template, 'user', :deposit, evaluator.deposit_users) if evaluator.deposit_users.present?
+      AccessHelper.create_access(permission_template, 'group', :deposit, evaluator.deposit_groups) if evaluator.deposit_groups.present?
+      AccessHelper.create_access(permission_template, 'user', :view, evaluator.view_users) if evaluator.view_users.present?
+      AccessHelper.create_access(permission_template, 'group', :view, evaluator.view_groups) if evaluator.view_groups.present?
+    end
+
+    transient do
+      with_admin_set { false }
+      with_collection { false }
+      with_workflows { false }
+      with_active_workflow { false }
+      manage_users { nil }
+      manage_groups { nil }
+      deposit_users { nil }
+      deposit_groups { nil }
+      view_users { nil }
+      view_groups { nil }
+    end
+  end
+
+  class AccessHelper
+    def self.create_access(permission_template_id, agent_type, access, agent_ids)
+      agent_ids.each do |agent_id|
+        FactoryBot.create(:permission_template_access,
+                          access,
+                          permission_template: permission_template_id,
+                          agent_type: agent_type,
+                          agent_id: agent_id)
+      end
+    end
+  end
+end
diff --git a/hyrax/spec/factories/roles.rb b/hyrax/spec/factories/roles.rb
new file mode 100644
index 0000000000000000000000000000000000000000..0d16fc940edd698fe6917279ce09e3e881ae2c46
--- /dev/null
+++ b/hyrax/spec/factories/roles.rb
@@ -0,0 +1,10 @@
+# Based on: https://github.com/samvera/hyrax/blob/master/spec/factories/users.rb
+FactoryBot.define do
+  factory :role do
+    sequence(:name) { |n| "role-#{n}"}
+
+    trait :admin do
+      name { 'admin' }
+    end
+  end
+end
diff --git a/hyrax/spec/factories/uploaded_file.rb b/hyrax/spec/factories/uploaded_file.rb
new file mode 100644
index 0000000000000000000000000000000000000000..07960a7fdb7c800a8ddd518e471cd03069f41aab
--- /dev/null
+++ b/hyrax/spec/factories/uploaded_file.rb
@@ -0,0 +1,6 @@
+FactoryBot.define do
+  factory :uploaded_file, class: Hyrax::UploadedFile do
+    file { Rack::Test::UploadedFile.new("#{::Rails.root}/spec/fixtures/image.jp2") }
+    user_id { 1 }
+  end
+end
diff --git a/hyrax/spec/factories/users.rb b/hyrax/spec/factories/users.rb
new file mode 100644
index 0000000000000000000000000000000000000000..0f018df4f5fc8a3d2ae71a286151d82290664762
--- /dev/null
+++ b/hyrax/spec/factories/users.rb
@@ -0,0 +1,47 @@
+# Based on: https://github.com/samvera/hyrax/blob/master/spec/factories/users.rb
+FactoryBot.define do
+  factory :user do
+    sequence(:email) { |n| "user#{n}@example.com" }
+    sequence(:username) { |n| "user#{n}" }
+    sequence(:display_name) { |n| "User #{n}"}
+    sequence(:user_identifier) { |n| "identifier_#{n}" }
+    password { 'password' }
+
+    transient do
+      # Allow for custom groups when a user is instantiated.
+      # @example create(:user, groups: 'avacado')
+      groups { [] }
+    end
+
+    trait :mock_groups do
+      # TODO: Register the groups for the given user key such that we can remove the following from other specs:
+      #   `allow(::User.group_service).to receive(:byname).and_return(user.user_key => ['admin'])``
+      after(:build) do |user, evaluator|
+        # In case we have the instance but it has not been persisted
+        ::RSpec::Mocks.allow_message(user, :groups).and_return(Array.wrap(evaluator.groups))
+        # Given that we are stubbing the class, we need to allow for the original to be called
+        ::RSpec::Mocks.allow_message(user.class.group_service, :fetch_groups).and_call_original
+        # We need to ensure that each instantiation of the admin user behaves as expected.
+        # This resolves the issue of both the created object being used as well as re-finding the created object.
+        ::RSpec::Mocks.allow_message(user.class.group_service, :fetch_groups).with(user: user).and_return(Array.wrap(evaluator.groups))
+      end
+    end
+
+    trait :guest do
+      guest { true }
+    end
+
+    trait :email do
+      sequence(:display_name) { |n| "Email user #{n}"}
+      guest { true }
+      employee_type_code { '60' }
+    end
+
+    trait :admin do
+      roles { build_list :role, 1, :admin }
+      guest { false }
+      employee_type_code { '11' }
+    end
+
+  end
+end
diff --git a/hyrax/spec/features/create_crc_dataset_spec.rb b/hyrax/spec/features/create_crc_dataset_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..753f7f2d212a7989af2463d212a7bb9ef4c182d3
--- /dev/null
+++ b/hyrax/spec/features/create_crc_dataset_spec.rb
@@ -0,0 +1,70 @@
+# Generated via
+#  `rails generate hyrax:work CrcDataset`
+require 'rails_helper'
+include Warden::Test::Helpers
+
+# NOTE: If you generated more than one work, you have to set "js: true"
+RSpec.feature 'Create a CrcDataset', js: false do
+  context 'a logged in user' do
+    let(:user_attributes) do
+      { email: 'test@example.com' }
+    end
+    let(:user) do
+      User.new(user_attributes) { |u| u.save(validate: false) }
+    end
+    let(:admin_set_id) { Hyrax::AdminSetCreateService.find_or_create_default_admin_set.id.to_s }
+    let(:permission_template) { Hyrax::PermissionTemplate.find_or_create_by!(source_id: admin_set_id) }
+    let(:workflow) { Sipity::Workflow.create!(active: true, name: 'test-workflow', permission_template: permission_template) }
+
+    before do
+      # Create a single action that can be taken
+      Sipity::WorkflowAction.create!(name: 'submit', workflow: workflow)
+
+      # Grant the user access to deposit into the admin set.
+      Hyrax::PermissionTemplateAccess.create!(
+        permission_template_id: permission_template.id,
+        agent_type: 'user',
+        agent_id: user.user_key,
+        access: 'deposit'
+      )
+      login_as user
+    end
+
+    scenario do
+      pending 'Changes may be required for this test to pass.  See TODO in test.'
+
+      visit '/dashboard'
+      click_link "Works"
+      click_link "Add new work"
+
+      # TODO: If you generate more than one work uncomment these lines
+      # choose "payload_concern", option: "CrcDataset"
+      # click_button "Create work"
+
+      expect(page).to have_content "Add New Crc dataset"
+      click_link "Files" # switch tab
+      expect(page).to have_content "Add files"
+      expect(page).to have_content "Add folder"
+      within('div#add-files') do
+        attach_file("files[]", "#{Hyrax::Engine.root}/spec/fixtures/image.jp2", visible: false)
+        attach_file("files[]", "#{Hyrax::Engine.root}/spec/fixtures/jp2_fits.xml", visible: false)
+      end
+      click_link "Descriptions" # switch tab
+      fill_in('Title', with: 'My Test Work')
+      fill_in('Creator', with: 'Doe, Jane')
+      select('In Copyright', from: 'Rights statement')
+
+      # With selenium and the chrome driver, focus remains on the
+      # select box. Click outside the box so the next line can't find
+      # its element
+      find('body').click
+      choose('crc_dataset_visibility_open')
+      expect(page).to have_content('Please note, making something visible to the world (i.e. marking this as Public) may be viewed as publishing which could impact your ability to')
+      check('agreement')
+
+      click_on('Save')
+      expect(page).to have_content('My Test Work')
+      expect(page).to have_content "Your files are being processed by Hyrax in the background."
+    end
+  end
+end
diff --git a/hyrax/spec/forms/hyrax/crc_dataset_form_spec.rb b/hyrax/spec/forms/hyrax/crc_dataset_form_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..4548417db6eabad3c0dcf66aad62fb35e4b20fb7
--- /dev/null
+++ b/hyrax/spec/forms/hyrax/crc_dataset_form_spec.rb
@@ -0,0 +1,9 @@
+# Generated via
+#  `rails generate hyrax:work CrcDataset`
+require 'rails_helper'
+
+RSpec.describe Hyrax::CrcDatasetForm do
+  it "has tests" do
+    skip "Add your tests here"
+  end
+end
diff --git a/hyrax/spec/indexers/dataset_indexer_spec.rb b/hyrax/spec/indexers/dataset_indexer_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..853d812f5b7a29b1796e85fbee1af7ddb11cabf5
--- /dev/null
+++ b/hyrax/spec/indexers/dataset_indexer_spec.rb
@@ -0,0 +1,264 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+require 'json'
+
+RSpec.describe DatasetIndexer do
+  describe 'indexes a date active triple resource with all the attributes' do
+    before do
+      dates = [
+        {
+          date: '1988-10-28',
+          description: 'http://bibframe.org/vocab/providerDate',
+        }, {
+          date: '2018-01-01'
+        }, {
+          description: 'http://bibframe.org/vocab/changeDate'
+        }, {
+          description: 'http://purl.org/dc/terms/dateAccepted',
+          date: ''
+        }
+      ]
+
+      obj = build(:dataset, complex_date_attributes: dates)
+
+      @solr_document = obj.to_solr
+    end
+
+    it 'rejects blank dates' do
+      expect(@solr_document).not_to include('complex_date_updated_ssm')
+      expect(@solr_document).not_to include('complex_year_updated_sim')
+      expect(@solr_document).not_to include('complex_date_accepted_ssm')
+      expect(@solr_document).not_to include('complex_year_accepted_sim')
+    end
+
+    it 'indexes as displayable' do
+      expect(@solr_document).to include('complex_date_ssm')
+      expect(JSON.parse(@solr_document['complex_date_ssm'])).not_to be_empty
+    end
+
+    it 'indexes as dateable' do
+      expect(@solr_document['complex_date_dtsim']).to match_array(
+        ["1988-10-28T00:00:00Z", "2018-01-01T00:00:00Z"])
+    end
+
+    it 'indexes year as facetable' do
+      expect(@solr_document['complex_year_sim']).to match_array(
+        ["1988", "2018"])
+    end
+
+    it 'indexes each type as dateable' do
+      expect(@solr_document['complex_date_submitted_dtsim']).to match_array(["1988-10-28T00:00:00Z"])
+    end
+
+    it 'indexes each type as displayable' do
+      expect(@solr_document['complex_date_submitted_ssm']).to match_array(["1988-10-28"])
+    end
+
+    it 'indexes each year as facetable' do
+      expect(@solr_document['complex_year_submitted_sim']).to match_array(["1988"])
+    end
+  end
+
+  describe 'indexes an identifier active triple resource with all the attributes' do
+    before do
+      ids = [
+        {
+          identifier: '0000-0000-0000-0000',
+          scheme: 'ORCID'
+        }, {
+          identifier: '1234',
+          scheme: 'identifier local'
+        }, {
+          identifier: '12345345234',
+          scheme: 'Orcid'
+        }
+      ]
+      obj = build(:dataset, complex_identifier_attributes: ids)
+      
+      @solr_document = obj.to_solr
+    end
+
+    it 'indexes as displayable' do
+      expect(@solr_document).to include('complex_identifier_ssm')
+      expect(JSON.parse(@solr_document['complex_identifier_ssm'])).not_to be_empty
+    end
+
+    it 'indexes as symbol' do
+      expect(@solr_document['complex_identifier_ssim']).to match_array(['0000-0000-0000-0000', '1234', '12345345234'])
+    end
+
+    it 'indexes each type as symbol' do
+      expect(@solr_document['complex_identifier_orcid_ssim']).to match_array(['0000-0000-0000-0000', '12345345234'])
+      expect(@solr_document['complex_identifier_identifier_local_ssim']).to match_array(['1234'])
+    end
+  end
+
+  describe 'indexes the person active triple resource with all the attributes' do
+    before do
+      people = [
+        {
+          first_name: ['Foo'],
+          last_name: 'Bar',
+          role: "author",
+          affiliation: "org",
+          orcid: "32123"
+        }, {
+          name: 'Big Baz',
+          role: "editor",
+          affiliation: "org 2",
+          orcid: "32125"
+      }, {
+          name: 'Joe Blogg',
+          role: "author",
+        }, {
+          first_name: ['James'],
+          last_name: 'Bond',
+          name: 'James Bond',
+          role: "data depositor",
+          affiliation: "org",
+          orcid: "32127"
+        }
+      ]
+
+      obj = build(:dataset, complex_person_attributes: people)
+
+      @solr_document = obj.to_solr
+    end
+
+    it 'indexes person as displayable' do
+      expect(@solr_document).to include('complex_person_ssm')
+      expect(JSON.parse(@solr_document['complex_person_ssm'])).not_to be_empty
+    end
+
+    it 'indexes name as facetable' do
+      expect(@solr_document['complex_person_sim']).to match_array(['Foo Bar', 'Big Baz', 'Joe Blogg', 'James Bond'])
+    end
+
+    it 'indexes name as stored searchable' do
+      expect(@solr_document['complex_person_tesim']).to match_array(['Foo Bar', 'Big Baz', 'Joe Blogg', 'James Bond'])
+    end
+
+    it 'index name by role as stored searchable' do
+      expect(@solr_document['complex_person_author_tesim']).to match_array(['Foo Bar', 'Joe Blogg'])
+      expect(@solr_document['complex_person_editor_tesim']).to match_array(['Big Baz'])
+      expect(@solr_document['complex_person_data_depositor_tesim']).to match_array(['James Bond'])
+    end
+
+    it 'index name by role as facetable' do
+      expect(@solr_document['complex_person_author_sim']).to match_array(['Foo Bar', 'Joe Blogg'])
+      expect(@solr_document['complex_person_editor_sim']).to match_array(['Big Baz'])
+      expect(@solr_document['complex_person_data_depositor_sim']).to match_array(['James Bond'])
+    end
+
+    it 'indexes the identifier as a symbol' do
+      puts @solr_document.keys()
+      expect(@solr_document['complex_person_identifier_ssim']).to match_array(['32123', '32125', '32127'])
+    end
+
+    it 'indexes affiliation as stored searchable' do
+      expect(@solr_document['complex_person_affiliation_tesim']).to match_array(['org', 'org 2'])
+    end
+
+    it 'indexes affiliation as facetable' do
+      expect(@solr_document['complex_person_affiliation_sim']).to match_array(['org', 'org 2'])
+    end
+  end
+
+  describe 'indexes the funding reference active triple resource with all the attributes' do
+    before do
+      fund_ref = [
+        {
+          funder_identifier: 'f12345',
+          funder_name: 'Bar',
+          award_number: 'c',
+          award_title: 'Title of the award'
+        },
+        {
+          funder_identifier: 'f22345',
+          funder_name: 'Baz',
+          award_number: 'a223345',
+          award_title: 'Another award'
+        }
+      ]
+
+      obj = build(:dataset, complex_funding_reference_attributes: fund_ref)
+
+      @solr_document = obj.to_solr
+    end
+
+    it 'indexes as displayable' do
+      expect(@solr_document).to include('complex_funding_reference_ssm')
+      expect(JSON.parse(@solr_document['complex_funding_reference_ssm'])).not_to be_empty
+    end
+
+    it 'indexes funder name as stored searchable' do
+      expect(@solr_document['funder_tesim']).to match_array(%w[Bar Baz])
+    end
+
+    it 'indexes funder name as facetable' do
+      expect(@solr_document['funder_sim']).to match_array(%w[Bar Baz])
+    end
+
+    it 'indexes award title as stored searchable' do
+      expect(@solr_document['award_title_tesim']).to match_array(['Title of the award', 'Another award'])
+    end
+  end
+
+  describe 'indexes the relation active triple resource with all the attributes' do
+    before do
+      relationships = [
+        {
+          title: 'A related item',
+          url: 'http://example.com/relation',
+          complex_identifier_attributes: [{
+            identifier: ['123456'],
+            label: ['local']
+          }],
+          relationship: 'isPartOf'
+        }, {
+          title: 'A 2nd related item',
+          url: 'http://example.com/relation2',
+          relationship: 'isPartOf'
+        }, {
+          title: 'A 3rd relation item',
+          url: 'http://example.com/relation3',
+          relationship: 'isNewVersionOf'
+        }
+      ]
+
+      obj = build(:dataset, complex_relation_attributes: relationships)
+
+      @solr_document = obj.to_solr
+    end
+
+    it 'indexes as displayable' do
+      expect(@solr_document).to include('complex_relation_ssm')
+      expect(JSON.parse(@solr_document['complex_relation_ssm'])).not_to be_empty
+    end
+
+    it 'indexes the title as stored searchable' do
+      expect(@solr_document['complex_relation_title_tesim']).to match_array(
+        ['A related item', 'A 2nd related item', 'A 3rd relation item'])
+    end
+
+    it 'indexes the relationship as facetable' do
+      expect(@solr_document['complex_relation_relationship_sim']).to match_array(
+        ['isPartOf', 'isPartOf', 'isNewVersionOf'])
+    end
+
+    it 'indexes the relation by relationship as stored searchable' do
+      expect(@solr_document['complex_relation_ispartof_tesim']).to match_array(
+        ['A related item', 'A 2nd related item'])
+      expect(@solr_document['complex_relation_isnewversionof_tesim']).to match_array(
+        ['A 3rd relation item'])
+    end
+
+    it 'indexes the relation by relationship as facetable' do
+      expect(@solr_document['complex_relation_ispartof_sim']).to match_array(
+        ['A related item', 'A 2nd related item'])
+      expect(@solr_document['complex_relation_isnewversionof_sim']).to match_array(
+        ['A 3rd relation item'])
+    end
+  end
+end
diff --git a/hyrax/spec/models/concerns/common_methods_spec.rb b/hyrax/spec/models/concerns/common_methods_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..64fe6bb45ef79a59c5609836b9486ddbe5089416
--- /dev/null
+++ b/hyrax/spec/models/concerns/common_methods_spec.rb
@@ -0,0 +1,37 @@
+require 'rails_helper'
+
+RSpec.describe CommonMethods do
+  let(:test_class) { Struct.new(:id, :parent) { include CommonMethods } }
+
+  context 'new record' do
+    subject { test_class.new('#new', 'parent') }
+
+    it 'is a new record' do
+      expect(subject.new_record?).to be true
+    end
+
+    it 'is not persisted' do
+      expect(subject.persisted?).to be false
+    end
+
+    it 'has a final_parent' do
+      expect(subject.final_parent).to eql 'parent'
+    end
+  end
+
+  context 'existing record' do
+    subject { test_class.new('existing', 'parent') }
+
+    it 'is not a new record' do
+      expect(subject.new_record?).to be false
+    end
+
+    it 'is persisted' do
+      expect(subject.persisted?).to be true
+    end
+
+    it 'has a final_parent' do
+      expect(subject.final_parent).to eql 'parent'
+    end
+  end
+end
\ No newline at end of file
diff --git a/hyrax/spec/models/concerns/complex_date_spec.rb b/hyrax/spec/models/concerns/complex_date_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..93882bb8634f0e0ac11343dc7290ab981a7961d0
--- /dev/null
+++ b/hyrax/spec/models/concerns/complex_date_spec.rb
@@ -0,0 +1,95 @@
+require 'rails_helper'
+
+RSpec.describe ComplexDate do
+  before do
+    class ExampleWork < ActiveFedora::Base
+      property :complex_date, predicate: ::RDF::Vocab::DC.date,
+        class_name:"ComplexDate"
+      accepts_nested_attributes_for :complex_date
+    end
+  end
+  after do
+    Object.send(:remove_const, :ExampleWork)
+  end
+
+  context 'uri with a #' do
+    before do
+      # special hack to force code path for testing
+      allow_any_instance_of(RDF::Node).to receive(:node?) { false }
+      allow_any_instance_of(RDF::Node).to receive(:start_with?) { true }
+    end
+    subject do
+      ExampleWork
+          .new({ complex_date_attributes: [{date: '2018-01-02'}]})
+          .complex_date
+          .first
+          .date
+    end
+    it { is_expected.to eq ['2018-01-02'] }
+  end
+
+  it 'has the correct uri' do
+    @obj = ExampleWork.new
+    @obj.attributes = {
+      complex_date_attributes: [
+        {
+          date: '1978-10-06'
+        }
+      ]
+    }
+    expect(@obj.complex_date.first.id).to include('#date')
+  end
+
+  it 'creates a date active triple resource with all the attributes' do
+    @obj = ExampleWork.new
+    @obj.attributes = {
+      complex_date_attributes: [
+        {
+          date: '1978-10-28',
+          description: 'Some kind of a date',
+        }
+      ]
+    }
+    expect(@obj.complex_date.first).to be_kind_of ActiveTriples::Resource
+    expect(@obj.complex_date.first.date).to eq ['1978-10-28']
+    expect(@obj.complex_date.first.description).to eq ['Some kind of a date']
+  end
+
+  describe 'when reject_if is a symbol' do
+    before do
+      class ExampleWork2 < ExampleWork
+        include ComplexValidation
+        accepts_nested_attributes_for :complex_date, reject_if: :date_blank
+      end
+    end
+    after do
+      Object.send(:remove_const, :ExampleWork2)
+    end
+
+    it 'creates a date active triple resource with just the date' do
+      @obj = ExampleWork2.new
+      @obj.attributes = {
+        complex_date_attributes: [
+          {
+            date: '1984-09-01'
+          }
+        ]
+      }
+      expect(@obj.complex_date.first).to be_kind_of ActiveTriples::Resource
+      expect(@obj.complex_date.first.date).to eq ['1984-09-01']
+      expect(@obj.complex_date.first.description).to be_empty
+    end
+
+    it 'rejects a date active triple with no date' do
+      @obj = ExampleWork2.new
+      @obj.attributes = {
+        complex_date_attributes: [
+          {
+            description: 'Local date'
+          }
+        ]
+      }
+      expect(@obj.complex_date).to be_empty
+    end
+  end
+end
diff --git a/hyrax/spec/models/concerns/complex_funding_reference_spec.rb b/hyrax/spec/models/concerns/complex_funding_reference_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..0ad885eb89d491c0c70d9f8058f8246ef3c765ad
--- /dev/null
+++ b/hyrax/spec/models/concerns/complex_funding_reference_spec.rb
@@ -0,0 +1,102 @@
+require 'rails_helper'
+
+RSpec.describe ComplexFundingReference do
+  before do
+    class ExampleWork < ActiveFedora::Base
+      property :complex_funding_reference, predicate: ::RDF::Vocab::DataCite.fundref,
+               class_name:"ComplexFundingReference"
+      accepts_nested_attributes_for :complex_funding_reference
+    end
+  end
+  after do
+    Object.send(:remove_const, :ExampleWork)
+  end
+
+  context 'uri with a #' do
+    before do
+      # special hack to force code path for testing
+      allow_any_instance_of(RDF::Node).to receive(:node?) { false }
+      allow_any_instance_of(RDF::Node).to receive(:start_with?) { true }
+    end
+    subject do
+      ExampleWork
+        .new({ complex_funding_reference_attributes: [{funder_identifier: '12345'}]})
+        .complex_funding_reference
+        .first
+        .funder_identifier
+    end
+    it { is_expected.to eq ['12345'] }
+  end
+
+  it 'has the correct uri' do
+    @obj = ExampleWork.new
+    @obj.attributes = {
+      complex_funding_reference_attributes: [
+        {
+          funder_identifier: '12345'
+        }
+      ]
+    }
+    expect(@obj.complex_funding_reference.first.id).to include('#fundref')
+  end
+
+  it 'creates a fund ref active triple resource with all the attributes' do
+    @obj = ExampleWork.new
+    @obj.attributes = {
+      complex_funding_reference_attributes: [
+        {
+          funder_identifier: '12456',
+          funder_name: 'Funder name',
+          award_number: 'a323',
+          award_uri: 'http://award.com/a323',
+          award_title: 'Award title for a323'
+        }
+      ]
+    }
+    expect(@obj.complex_funding_reference.first).to be_kind_of ActiveTriples::Resource
+    expect(@obj.complex_funding_reference.first.funder_identifier).to eq ['12456']
+    expect(@obj.complex_funding_reference.first.funder_name).to eq ['Funder name']
+    expect(@obj.complex_funding_reference.first.award_number).to eq ['a323']
+    expect(@obj.complex_funding_reference.first.award_uri).to eq ['http://award.com/a323']
+    expect(@obj.complex_funding_reference.first.award_title).to eq ['Award title for a323']
+  end
+
+  describe 'when reject_if is a symbol' do
+    before do
+      class ExampleWork2 < ExampleWork
+        include ComplexValidation
+        accepts_nested_attributes_for :complex_funding_reference, reject_if: :fundref_blank
+      end
+    end
+    after do
+      Object.send(:remove_const, :ExampleWork2)
+    end
+
+    it 'creates a fund ref active triple resource when any value is filled' do
+      @obj = ExampleWork2.new
+      @obj.attributes = {
+        complex_funding_reference_attributes: [
+          {
+            funder_identifier: '12456'
+          }
+        ]
+      }
+      expect(@obj.complex_funding_reference.first).to be_kind_of ActiveTriples::Resource
+      expect(@obj.complex_funding_reference.first.funder_identifier).to eq ['12456']
+      expect(@obj.complex_funding_reference.first.funder_name).to be_empty
+    end
+
+    it 'rejects a fund ref active triple with no values' do
+      @obj = ExampleWork2.new
+      @obj.attributes = {
+        complex_funding_reference_attributes: [
+          {
+            funder_identifier: ''
+          }
+        ]
+      }
+      expect(@obj.complex_funding_reference).to be_empty
+    end
+  end
+
+end
diff --git a/hyrax/spec/models/concerns/complex_identifier_spec.rb b/hyrax/spec/models/concerns/complex_identifier_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..8f587ca82b4e4a57bacf0d9842258f0a1f5b05e3
--- /dev/null
+++ b/hyrax/spec/models/concerns/complex_identifier_spec.rb
@@ -0,0 +1,98 @@
+require 'rails_helper'
+
+RSpec.describe ComplexIdentifier do
+  before do
+    class ExampleWork < ActiveFedora::Base
+      property :complex_identifier, predicate: ::RDF::Vocab::MODS.identifierGroup,
+               class_name:"ComplexIdentifier"
+      accepts_nested_attributes_for :complex_identifier
+    end
+  end
+  after do
+    Object.send(:remove_const, :ExampleWork)
+  end
+
+  context 'uri with a #' do
+    before do
+      # special hack to force code path for testing
+      allow_any_instance_of(RDF::Node).to receive(:node?) { false }
+      allow_any_instance_of(RDF::Node).to receive(:start_with?) { true }
+    end
+    subject do
+      ExampleWork
+        .new({ complex_identifier_attributes: [{ identifier: '0000-0000-0000-0000' }]})
+        .complex_identifier
+        .first
+        .identifier
+    end
+    it { is_expected.to eq ['0000-0000-0000-0000'] }
+  end
+
+  it 'has the correct uri' do
+    @obj = ExampleWork.new
+    @obj.attributes = {
+      complex_identifier_attributes: [
+        {
+          identifier: '0000-0000-0000-0000'
+        }
+      ]
+    }
+    expect(@obj.complex_identifier.first.id).to include('#identifier')
+  end
+
+  it 'creates an identifier active triple resource with all the attributes' do
+    @obj = ExampleWork.new
+    @obj.attributes = {
+      complex_identifier_attributes: [
+        {
+          identifier: '0000-0000-0000-0000',
+          scheme: 'uri_of_ORCID_scheme',
+          label: 'ORCID'
+        }
+      ]
+    }
+    expect(@obj.complex_identifier.first).to be_kind_of ActiveTriples::Resource
+    expect(@obj.complex_identifier.first.identifier).to eq ['0000-0000-0000-0000']
+    expect(@obj.complex_identifier.first.scheme).to eq ['uri_of_ORCID_scheme']
+    expect(@obj.complex_identifier.first.label).to eq ['ORCID']
+  end
+
+  describe 'when reject_if is a symbol' do
+    before do
+      class ExampleWork2 < ExampleWork
+        include ComplexValidation
+        accepts_nested_attributes_for :complex_identifier, reject_if: :identifier_blank
+      end
+    end
+    after do
+      Object.send(:remove_const, :ExampleWork2)
+    end
+
+    it 'creates an identifier active triple resource with just the identifier' do
+      @obj = ExampleWork2.new
+      @obj.attributes = {
+        complex_identifier_attributes: [
+          {
+            identifier: '1234'
+          }
+        ]
+      }
+      expect(@obj.complex_identifier.first).to be_kind_of ActiveTriples::Resource
+      expect(@obj.complex_identifier.first.identifier).to eq ['1234']
+      expect(@obj.complex_identifier.first.label).to be_empty
+      expect(@obj.complex_identifier.first.scheme).to be_empty
+    end
+
+    it 'rejects an identifier active triple with no ientifier' do
+      @obj = ExampleWork2.new
+      @obj.attributes = {
+        complex_identifier_attributes: [
+          {
+            label: 'Local'
+          }
+        ]
+      }
+      expect(@obj.complex_identifier).to be_empty
+    end
+  end
+end
diff --git a/hyrax/spec/models/concerns/complex_person_spec.rb b/hyrax/spec/models/concerns/complex_person_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..03001404cf3fd2b37b49367d85fd970108750a93
--- /dev/null
+++ b/hyrax/spec/models/concerns/complex_person_spec.rb
@@ -0,0 +1,156 @@
+require 'rails_helper'
+
+RSpec.describe ComplexPerson do
+  before do
+    class ExampleWork < ActiveFedora::Base
+      property :complex_person, predicate: ::RDF::Vocab::SIOC.has_creator, class_name:"ComplexPerson"
+      accepts_nested_attributes_for :complex_person
+    end
+  end
+  after do
+    Object.send(:remove_const, :ExampleWork)
+  end
+
+  describe 'complex_person_attributes' do
+    subject do
+       ExampleWork.new({
+        complex_person_attributes: [{
+          first_name: 'Foo',
+          last_name: 'Bar',
+          name: 'Foo Bar',
+          email: 'foo.bar@example.com',
+          role: 'Author',
+          orcid: '1234567',
+          affiliation: 'Org'
+        }]
+      }).complex_person.first
+    end
+
+    it 'creates a person active triple resource with an id and all properties' do
+      expect(subject).to be_kind_of ActiveTriples::Resource
+      expect(subject.id).to include('#person')
+      expect(subject.first_name).to eq ['Foo']
+      expect(subject.last_name).to eq ['Bar']
+      expect(subject.name).to eq ['Foo Bar']
+      expect(subject.email).to eq ['foo.bar@example.com']
+      expect(subject.role).to eq ['Author']
+      expect(subject.orcid).to eq ['1234567']
+      expect(subject.affiliation).to eq ['Org']
+    end
+  end
+
+  describe "when reject_if is a symbol" do
+    before do
+      class ExampleWork2 < ExampleWork
+        include ComplexValidation
+        accepts_nested_attributes_for :complex_person, reject_if: :person_blank
+      end
+    end
+    after do
+      Object.send(:remove_const, :ExampleWork2)
+    end
+
+    context 'accepts valid complex_person_attributes' do
+      subject do
+        ExampleWork2
+            .new({ complex_person_attributes: complex_person_attributes })
+            .complex_person
+            .first
+      end
+
+      context 'name' do
+        let(:complex_person_attributes) { [{name: 'Anamika'}] }
+
+        it 'creates a person active triple resource with name' do
+          expect(subject).to be_kind_of ActiveTriples::Resource
+          expect(subject.name).to eq ['Anamika']
+          expect(subject.first_name).to be_empty
+          expect(subject.last_name).to be_empty
+          expect(subject.email).to be_empty
+          expect(subject.role).to be_empty
+          expect(subject.orcid).to be_empty
+          expect(subject.affiliation).to be_empty
+        end
+      end
+
+      context 'first_name' do
+        let(:complex_person_attributes) { [{first_name: 'Anamika'}] }
+
+        it 'creates a person active triple resource with first name' do
+          expect(subject).to be_kind_of ActiveTriples::Resource
+          expect(subject.name).to be_empty
+          expect(subject.first_name).to eq ['Anamika']
+          expect(subject.last_name).to be_empty
+          expect(subject.email).to be_empty
+          expect(subject.role).to be_empty
+          expect(subject.orcid).to be_empty
+          expect(subject.affiliation).to be_empty
+        end
+      end
+
+      context 'last_name' do
+        let(:complex_person_attributes) { [{last_name: 'Anamika'}] }
+
+        it 'creates a person active triple resource with last name' do
+          expect(subject).to be_kind_of ActiveTriples::Resource
+          expect(subject.name).to be_empty
+          expect(subject.first_name).to be_empty
+          expect(subject.last_name).to eq ['Anamika']
+          expect(subject.email).to be_empty
+          expect(subject.role).to be_empty
+          expect(subject.orcid).to be_empty
+          expect(subject.affiliation).to be_empty
+        end
+      end
+
+      context 'name, affiliation and role' do
+        let(:complex_person_attributes) do
+          [{
+            name: 'Anamika',
+            affiliation: 'org',
+            role: 'Creator'
+          }]
+        end
+
+        it 'creates a person active triple resource with name, affiliation and role' do
+          expect(subject).to be_kind_of ActiveTriples::Resource
+          expect(subject.name).to eq ['Anamika']
+          expect(subject.first_name).to be_empty
+          expect(subject.last_name).to be_empty
+          expect(subject.email).to be_empty
+          expect(subject.role).to eq ['Creator']
+          expect(subject.orcid).to be_empty
+          expect(subject.affiliation).to eq ['org']
+        end
+      end
+    end
+
+    context 'rejects person active triple with invalid complex_person_attributes' do
+      subject do
+        ExampleWork2
+            .new({ complex_person_attributes: complex_person_attributes })
+            .complex_person
+      end
+
+      context 'no name and only orcid' do
+        let(:complex_person_attributes) { [{ orcide: 'http://orcid.org/person/123456' }] }
+        it { is_expected.to be_empty }
+      end
+
+      context 'no name and only role' do
+        let(:complex_person_attributes) { [{ role: 'Creator' }] }
+        it { is_expected.to be_empty }
+      end
+
+      context 'no name and only affiliation' do
+        let(:complex_person_attributes) { [{ affiliation: 'Some org' }] }
+        it { is_expected.to be_empty }
+      end
+
+      context 'no name and only email' do
+        let(:complex_person_attributes) { [{ email: 'me@me.org' }] }
+        it { is_expected.to be_empty }
+      end
+    end
+  end
+end
diff --git a/hyrax/spec/models/concerns/complex_relation_spec.rb b/hyrax/spec/models/concerns/complex_relation_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..3144fd6d7b8c9d0dfec94da554d4ee66bd30cd38
--- /dev/null
+++ b/hyrax/spec/models/concerns/complex_relation_spec.rb
@@ -0,0 +1,290 @@
+require 'rails_helper'
+
+RSpec.describe ComplexRelation do
+  before do
+    class ExampleWork < ActiveFedora::Base
+      property :complex_relation, predicate: ::RDF::Vocab::DC.relation,
+        class_name:"ComplexRelation"
+      accepts_nested_attributes_for :complex_relation
+    end
+  end
+  after do
+    Object.send(:remove_const, :ExampleWork)
+  end
+
+  context 'uri with a #' do
+    before do
+      # special hack to force code path for testing
+      allow_any_instance_of(RDF::Node).to receive(:node?) { false }
+      allow_any_instance_of(RDF::Node).to receive(:start_with?) { true }
+    end
+    subject do
+      ExampleWork
+          .new({ complex_relation_attributes: [{ title: 'Foo' }]})
+          .complex_relation
+          .first
+          .title
+    end
+    it { is_expected.to eq ['Foo'] }
+  end
+
+  it 'has the correct uri' do
+    @obj = ExampleWork.new
+    @obj.attributes = {
+      complex_relation_attributes: [
+        {
+          title: 'A relation title',
+          url: 'http://example.com/relation',
+          relationship: 'IsPartOf'
+        }
+      ]
+    }
+    expect(@obj.complex_relation.first.id).to include('#relation')
+  end
+
+  it 'creates a relation active triple resource with all the attributes' do
+    @obj = ExampleWork.new
+    @obj.attributes = {
+      complex_relation_attributes: [
+        {
+          title: 'My first publication',
+          url: 'http://example.com/relation',
+          complex_identifier_attributes: [{
+            identifier: ['123456'],
+            label: ['local']
+          }],
+          relationship: 'IsPartOf'
+        }
+      ]
+    }
+    expect(@obj.complex_relation.first).to be_kind_of ActiveTriples::Resource
+    expect(@obj.complex_relation.first.title).to eq ['My first publication']
+    expect(@obj.complex_relation.first.url).to eq ['http://example.com/relation']
+    expect(@obj.complex_relation.first.complex_identifier.first).to be_kind_of ActiveTriples::Resource
+    expect(@obj.complex_relation.first.complex_identifier.first.identifier).to eq ['123456']
+    expect(@obj.complex_relation.first.complex_identifier.first.label).to eq ['local']
+    expect(@obj.complex_relation.first.relationship).to eq ['IsPartOf']
+  end
+
+  describe "when reject_if is a symbol" do
+    before do
+      class ExampleWork2 < ExampleWork
+        include ComplexValidation
+        accepts_nested_attributes_for :complex_relation, reject_if: :relation_blank
+      end
+    end
+    after do
+      Object.send(:remove_const, :ExampleWork2)
+    end
+
+    it 'creates a relation active triple resource with title and relationship' do
+      @obj = ExampleWork2.new
+      @obj.attributes = {
+        complex_relation_attributes: [
+          {
+            title: 'A relation title',
+            relationship: 'IsPartOf'
+          }
+        ]
+      }
+      expect(@obj.complex_relation.first).to be_kind_of ActiveTriples::Resource
+      expect(@obj.complex_relation.first.title).to eq ['A relation title']
+      expect(@obj.complex_relation.first.url).to be_empty
+      expect(@obj.complex_relation.first.complex_identifier).to be_empty
+      expect(@obj.complex_relation.first.relationship).to eq ['IsPartOf']
+    end
+
+    it 'creates a relation active triple resource with url and relationship' do
+      @obj = ExampleWork2.new
+      @obj.attributes = {
+        complex_relation_attributes: [
+          {
+            url: 'http://example.com/relation',
+            relationship: 'isPreviousVersionOf'
+          }
+        ]
+      }
+      expect(@obj.complex_relation.first).to be_kind_of ActiveTriples::Resource
+      expect(@obj.complex_relation.first.title).to be_empty
+      expect(@obj.complex_relation.first.url).to eq ['http://example.com/relation']
+      expect(@obj.complex_relation.first.complex_identifier).to be_empty
+      expect(@obj.complex_relation.first.relationship).to eq ['isPreviousVersionOf']
+    end
+
+    it 'creates a relation active triple resource with identifier and relationship' do
+      @obj = ExampleWork2.new
+      @obj.attributes = {
+        complex_relation_attributes: [
+          {
+            complex_identifier_attributes: [{
+              identifier: ['123456']
+            }],
+            relationship: 'isSupplementTo'
+          }
+        ]
+      }
+      expect(@obj.complex_relation.first).to be_kind_of ActiveTriples::Resource
+      expect(@obj.complex_relation.first.title).to be_empty
+      expect(@obj.complex_relation.first.url).to be_empty
+      expect(@obj.complex_relation.first.complex_identifier.first).to be_kind_of ActiveTriples::Resource
+      expect(@obj.complex_relation.first.complex_identifier.first.identifier).to eq ['123456']
+      expect(@obj.complex_relation.first.complex_identifier.first.label).to be_empty
+      expect(@obj.complex_relation.first.relationship).to eq ['isSupplementTo']
+    end
+
+    it 'creates a relation active triple resource with title, url and relationship' do
+      @obj = ExampleWork2.new
+      @obj.attributes = {
+        complex_relation_attributes: [
+          {
+            title: 'A relation title',
+            url: 'http://example.com/relation',
+            relationship: 'isContinuedBy'
+          }
+        ]
+      }
+      expect(@obj.complex_relation.first).to be_kind_of ActiveTriples::Resource
+      expect(@obj.complex_relation.first.title).to eq ['A relation title']
+      expect(@obj.complex_relation.first.url).to eq ['http://example.com/relation']
+      expect(@obj.complex_relation.first.complex_identifier).to be_empty
+      expect(@obj.complex_relation.first.relationship).to eq ['isContinuedBy']
+    end
+
+    it 'creates a relation active triple resource with title, identifier and relationship' do
+      @obj = ExampleWork2.new
+      @obj.attributes = {
+        complex_relation_attributes: [
+          {
+            title: 'A relation title',
+            complex_identifier_attributes: [{
+              identifier: ['123456']
+            }],
+            relationship: 'isContinuedBy'
+          }
+        ]
+      }
+      expect(@obj.complex_relation.first).to be_kind_of ActiveTriples::Resource
+      expect(@obj.complex_relation.first.title).to eq ['A relation title']
+      expect(@obj.complex_relation.first.url).to be_empty
+      expect(@obj.complex_relation.first.complex_identifier.first).to be_kind_of ActiveTriples::Resource
+      expect(@obj.complex_relation.first.complex_identifier.first.identifier).to eq ['123456']
+      expect(@obj.complex_relation.first.complex_identifier.first.label).to be_empty
+      expect(@obj.complex_relation.first.relationship).to eq ['isContinuedBy']
+    end
+
+    it 'creates a relation active triple resource with title, url, identifier and relationship' do
+      @obj = ExampleWork2.new
+      @obj.attributes = {
+        complex_relation_attributes: [
+          {
+            title: 'A relation title',
+            url: 'http://example.com/relation',
+            complex_identifier_attributes: [{
+              identifier: ['123456'],
+              label: 'Local'
+            }],
+            relationship: 'isDocumentedBy'
+          }
+        ]
+      }
+      expect(@obj.complex_relation.first).to be_kind_of ActiveTriples::Resource
+      expect(@obj.complex_relation.first.title).to eq ['A relation title']
+      expect(@obj.complex_relation.first.url).to eq ['http://example.com/relation']
+      expect(@obj.complex_relation.first.complex_identifier.first).to be_kind_of ActiveTriples::Resource
+      expect(@obj.complex_relation.first.complex_identifier.first.identifier).to eq ['123456']
+      expect(@obj.complex_relation.first.complex_identifier.first.label).to eq ['Local']
+      expect(@obj.complex_relation.first.relationship).to eq ['isDocumentedBy']
+    end
+
+    it 'creates a relation active triple resource with url, identifier and relationship' do
+      @obj = ExampleWork2.new
+      @obj.attributes = {
+        complex_relation_attributes: [
+          {
+            url: 'http://example.com/relation',
+            complex_identifier_attributes: [{
+              identifier: ['123456'],
+              label: 'Local'
+            }],
+            relationship: 'isDerivedFrom'
+          }
+        ]
+      }
+      expect(@obj.complex_relation.first).to be_kind_of ActiveTriples::Resource
+      expect(@obj.complex_relation.first.url).to eq ['http://example.com/relation']
+      expect(@obj.complex_relation.first.complex_identifier.first).to be_kind_of ActiveTriples::Resource
+      expect(@obj.complex_relation.first.complex_identifier.first.identifier).to eq ['123456']
+      expect(@obj.complex_relation.first.complex_identifier.first.label).to eq ['Local']
+      expect(@obj.complex_relation.first.relationship).to eq ['isDerivedFrom']
+    end
+
+    it 'rejects relation active triple with just title and no relationship' do
+      @obj = ExampleWork2.new
+      @obj.attributes = {
+        complex_relation_attributes: [
+          {
+            title: 'Local'
+          }
+        ]
+      }
+      expect(@obj.complex_relation).to be_empty
+    end
+
+    it 'rejects relation active triple with just url and no relationship' do
+      @obj = ExampleWork2.new
+      @obj.attributes = {
+        complex_relation_attributes: [
+          {
+            url: 'http://example.com/relation'
+          }
+        ]
+      }
+      expect(@obj.complex_relation).to be_empty
+    end
+
+    it 'rejects relation active triple with just identifier and no relationship' do
+      @obj = ExampleWork2.new
+      @obj.attributes = {
+        complex_relation_attributes: [
+          {
+            complex_identifier_attributes: [{
+              identifier: ['123456'],
+              label: 'Local'
+            }],
+          }
+        ]
+      }
+      expect(@obj.complex_relation).to be_empty
+    end
+
+    it 'rejects relation active triple with just reltionship and no identifying information' do
+      @obj = ExampleWork2.new
+      @obj.attributes = {
+        complex_relation_attributes: [
+          {
+            relationship: 'isPartOf'
+          }
+        ]
+      }
+      expect(@obj.complex_relation).to be_empty
+    end
+
+    it 'rejects relation active triple with no reltionship' do
+      @obj = ExampleWork2.new
+      @obj.attributes = {
+        complex_relation_attributes: [
+          {
+            title: 'test relation',
+            url: 'http://example.com/relation',
+            complex_identifier_attributes: [{
+              identifier: ['123456'],
+              label: 'Local'
+            }]
+          }
+        ]
+      }
+      expect(@obj.complex_relation).to be_empty
+    end
+
+  end
+end
diff --git a/hyrax/spec/models/crc_dataset_spec.rb b/hyrax/spec/models/crc_dataset_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..dd88c8a1c414ec7e4d0f54f7136f87c0a3e63649
--- /dev/null
+++ b/hyrax/spec/models/crc_dataset_spec.rb
@@ -0,0 +1,528 @@
+# Generated via
+#  `rails generate hyrax:work CrcDataset`
+require 'rails_helper'
+
+RSpec.describe CrcDataset do
+  it 'has human readable type for the CRC dataset' do
+    @obj = build(:crc_dataset)
+    expect(@obj.human_readable_type).to eq('Crc Dataset')
+  end
+
+  describe 'date_modified' do
+    it 'has date_modified as singular' do
+      @obj = build(:crc_dataset, date_modified: '2018/04/23')
+      expect(@obj.date_modified).to eq '2018/04/23'
+    end
+  end
+
+  describe 'date_uploaded' do
+    it 'has date_uploaded as singular' do
+      @obj = build(:crc_dataset, date_uploaded: '2018 01 02')
+      expect(@obj.date_uploaded).to eq '2018 01 02'
+    end
+  end
+
+  describe 'depositor' do
+    it 'has depositor' do
+      @obj = build(:crc_dataset, depositor: 'Name of depositor')
+      expect(@obj.depositor).to eq 'Name of depositor'
+    end
+  end
+
+  describe 'title' do
+    it 'requires title' do
+      @obj = build(:crc_dataset, title: nil)
+      expect{@obj.save!}.to raise_error(ActiveFedora::RecordInvalid,
+        'Validation failed: Title Your CRC dataset must have a title.')
+    end
+
+    it 'has a multi valued title field' do
+      @obj = build(:crc_dataset, title: ['test CRC dataset'])
+      expect(@obj.title).to eq ['test CRC dataset']
+    end
+  end
+
+  describe 'alternative_title' do
+    it 'has alternative_title' do
+      @obj = build(:crc_dataset, alternative_title: ['Alternative Title'])
+      expect(@obj.alternative_title).to eq ['Alternative Title']
+    end
+  end
+
+  describe 'label' do
+    it 'has label as singular' do
+      @obj = build(:crc_dataset, label: 'Label 1')
+      expect(@obj.label).to eq 'Label 1'
+    end
+  end
+
+  describe 'relative_path' do
+    it 'has relative_path as singular' do
+      @obj = build(:crc_dataset, relative_path: 'relative/path/to/file')
+      expect(@obj.relative_path).to eq 'relative/path/to/file'
+    end
+  end
+
+  describe 'import_url' do
+    it 'has import_url as singular' do
+      @obj = build(:crc_dataset, import_url: 'http://example.com/import/url')
+      expect(@obj.import_url).to eq 'http://example.com/import/url'
+    end
+  end
+
+  describe 'resource_type' do
+    it 'has resource_type' do
+      @obj = build(:crc_dataset, resource_type: ['CRC Dataset'])
+      expect(@obj.resource_type).to eq ['CRC Dataset']
+    end
+  end
+
+  describe 'creator' do
+    it 'has creator' do
+      @obj = build(:crc_dataset, creator: ['Creator 1'])
+      expect(@obj.creator).to eq ['Creator 1']
+    end
+  end
+
+  describe 'contributor' do
+    it 'has contributor' do
+      @obj = build(:crc_dataset, contributor: ['contributor 1'])
+      expect(@obj.contributor).to eq ['contributor 1']
+    end
+  end
+
+  describe 'description' do
+    it 'has description' do
+      @obj = build(:crc_dataset, description: ['description 1'])
+      expect(@obj.description).to eq ['description 1']
+    end
+  end
+
+  describe 'abstract' do
+    it 'has abstract' do
+      @obj = build(:crc_dataset, abstract: ['abstract 1'])
+      expect(@obj.abstract).to eq ['abstract 1']
+    end
+  end
+
+  describe 'keyword' do
+    it 'has keyword' do
+      @obj = build(:crc_dataset, keyword: ['keyword 2', '3 keyword', 'keyword 1'])
+      expect(@obj.keyword).to eq ['keyword 2', '3 keyword', 'keyword 1']
+    end
+  end
+
+  describe 'license' do
+    it 'has license' do
+      @obj = build(:crc_dataset, license: ['CC-0'])
+      expect(@obj.license).to eq ['CC-0']
+    end
+  end
+
+  describe 'rights_notes' do
+    it 'has rights_notes' do
+      @obj = build(:crc_dataset, rights_notes: ['rights_notes 1'])
+      expect(@obj.rights_notes).to eq ['rights_notes 1']
+    end
+  end
+
+  describe 'rights_statement' do
+    it 'has rights_statement' do
+      @obj = build(:crc_dataset, rights_statement: ['rights_statement 1'])
+      expect(@obj.rights_statement).to eq ['rights_statement 1']
+    end
+  end
+
+  describe 'access_right' do
+    it 'has access_right' do
+      @obj = build(:crc_dataset, access_right: ['access_right 1'])
+      expect(@obj.access_right).to eq ['access_right 1']
+    end
+  end
+
+  describe 'publisher' do
+    it 'has publisher' do
+      @obj = build(:crc_dataset, publisher: ['publisher 1'])
+      expect(@obj.publisher).to eq ['publisher 1']
+    end
+  end
+
+  describe 'date_created' do
+    it 'has date_created' do
+      @obj = build(:crc_dataset, date_created: ['date_created 1'])
+      expect(@obj.date_created).to eq ['date_created 1']
+    end
+  end
+
+  describe 'subject' do
+    it 'has subject' do
+      @obj = build(:crc_dataset, subject: ['subject 1'])
+      expect(@obj.subject).to eq ['subject 1']
+    end
+  end
+
+  describe 'language' do
+    it 'has language' do
+      @obj = build(:crc_dataset, language: ['language 1'])
+      expect(@obj.language).to eq ['language 1']
+    end
+  end
+
+  describe 'identifier' do
+    it 'has identifier' do
+      @obj = build(:crc_dataset, identifier: ['identifier 1'])
+      expect(@obj.identifier).to eq ['identifier 1']
+    end
+  end
+
+  describe 'based_near' do
+    it 'has based_near' do
+      @obj = build(:crc_dataset, based_near: ['me'])
+      expect(@obj.based_near).to eq ['me']
+    end
+  end
+
+  describe 'related_url' do
+    it 'has related_url' do
+      @obj = build(:crc_dataset, related_url: ['http://example.com/related/url'])
+      expect(@obj.related_url).to eq ['http://example.com/related/url']
+    end
+  end
+
+  describe 'bibliographic_citation' do
+    it 'has bibliographic_citation' do
+      @obj = build(:crc_dataset, bibliographic_citation: ['bibliographic_citation 1'])
+      expect(@obj.bibliographic_citation).to eq ['bibliographic_citation 1']
+    end
+  end
+
+  describe 'source' do
+    it 'has source' do
+      @obj = build(:crc_dataset, source: ['Source 1'])
+      expect(@obj.source).to eq ['Source 1']
+    end
+  end
+
+  describe 'doi' do
+    it 'has doi as singular' do
+      @obj = build(:crc_dataset, doi: '123167641234')
+      expect(@obj.doi).to eq '123167641234'
+    end
+  end
+
+  describe 'complex_person' do
+    it 'creates a person active triple resource with name' do
+      @obj = build(:crc_dataset,
+                   complex_person_attributes: [{
+                                                 name: 'Anamika'
+                                               }]
+      )
+      expect(@obj.complex_person.first).to be_kind_of ActiveTriples::Resource
+      expect(@obj.complex_person.first.name).to eq ['Anamika']
+      expect(@obj.complex_person.first.first_name).to be_empty
+      expect(@obj.complex_person.first.last_name).to be_empty
+      expect(@obj.complex_person.first.email).to be_empty
+      expect(@obj.complex_person.first.affiliation).to be_empty
+      expect(@obj.complex_person.first.role).to be_empty
+      expect(@obj.complex_person.first.orcid).to be_empty
+      expect(@obj.complex_person.first.display_order).to be_empty
+    end
+
+    it 'creates a person active triple resource with name, affiliation, orcid, role and display order' do
+      @obj = build(:crc_dataset,
+                   complex_person_attributes: [{
+                                                 name: 'Anamika',
+                                                 affiliation: 'Some org',
+                                                 role: 'Creator',
+                                                 orcid: '1231234',
+                                                 display_order: 1
+                                               }]
+      )
+      expect(@obj.complex_person.first).to be_kind_of ActiveTriples::Resource
+      expect(@obj.complex_person.first.name).to eq ['Anamika']
+      expect(@obj.complex_person.first.first_name).to be_empty
+      expect(@obj.complex_person.first.last_name).to be_empty
+      expect(@obj.complex_person.first.email).to be_empty
+      expect(@obj.complex_person.first.role).to eq ['Creator']
+      expect(@obj.complex_person.first.orcid).to eq ['1231234']
+      expect(@obj.complex_person.first.display_order).to eq([1])
+      expect(@obj.complex_person.first.affiliation).to eq(['Some org'])
+    end
+
+    it 'rejects person active triple with no name and only orcid' do
+      @obj = build(:crc_dataset, complex_person_attributes: [{
+                                                           orcid: 'http://example.com/person/123456'
+                                                         }]
+      )
+      expect(@obj.complex_person).to be_empty
+    end
+  end
+
+  describe 'complex_date' do
+    it 'creates a date active triple resource with all the attributes' do
+      @obj = build(:crc_dataset,
+                   complex_date_attributes: [{
+                                               date: '1978-10-28',
+                                               description: 'Some kind of a date',
+                                             }]
+      )
+      expect(@obj.complex_date.first).to be_kind_of ActiveTriples::Resource
+      expect(@obj.complex_date.first.date).to eq ['1978-10-28']
+      expect(@obj.complex_date.first.description).to eq ['Some kind of a date']
+    end
+
+    it 'creates a date active triple resource with just the date' do
+      @obj = build(:crc_dataset,
+                   complex_date_attributes: [{
+                                               date: '1984-09-01'
+                                             }]
+      )
+      expect(@obj.complex_date.first).to be_kind_of ActiveTriples::Resource
+      expect(@obj.complex_date.first.date).to eq ['1984-09-01']
+      expect(@obj.complex_date.first.description).to be_empty
+    end
+
+    it 'rejects a date active triple with no date' do
+      @obj = build(:crc_dataset,
+                   complex_date_attributes: [{
+                                               description: 'Local date'
+                                             }]
+      )
+      expect(@obj.complex_date).to be_empty
+    end
+  end
+
+  describe 'complex_identifier' do
+    it 'creates an identifier active triple resource with all the attributes' do
+      @obj = build(:crc_dataset,
+                   complex_identifier_attributes: [{
+                                                     identifier: '0000-0000-0000-0000',
+                                                     scheme: 'uri_of_ORCID_scheme',
+                                                     label: 'ORCID'
+                                                   }]
+      )
+      expect(@obj.complex_identifier.first).to be_kind_of ActiveTriples::Resource
+      expect(@obj.complex_identifier.first.identifier).to eq ['0000-0000-0000-0000']
+      expect(@obj.complex_identifier.first.scheme).to eq ['uri_of_ORCID_scheme']
+      expect(@obj.complex_identifier.first.label).to eq ['ORCID']
+    end
+
+    it 'creates an identifier active triple resource with just the identifier' do
+      @obj = build(:crc_dataset,
+                   complex_identifier_attributes: [{
+                                                     identifier: '1234'
+                                                   }]
+      )
+      expect(@obj.complex_identifier.first).to be_kind_of ActiveTriples::Resource
+      expect(@obj.complex_identifier.first.identifier).to eq ['1234']
+      expect(@obj.complex_identifier.first.label).to be_empty
+      expect(@obj.complex_identifier.first.scheme).to be_empty
+    end
+
+    it 'rejects an identifier active triple with no identifier' do
+      @obj = build(:crc_dataset,
+                   complex_identifier_attributes: [{
+                                                     label: 'Local'
+                                                   }]
+      )
+      expect(@obj.complex_identifier).to be_empty
+    end
+  end
+
+  describe 'complex_funding_reference' do
+    it 'creates a complex funding reference active triple resource with funding reference' do
+      @obj = build(:crc_dataset,
+                   complex_funding_reference_attributes: [{
+                                                            funder_identifier: 'f1234',
+                                                            funder_name: 'Bank',
+                                                            award_title: 'No free lunch'
+                                                          }]
+      )
+      expect(@obj.complex_funding_reference.first).to be_kind_of ActiveTriples::Resource
+      expect(@obj.complex_funding_reference.first.id).to include('#fundref')
+      expect(@obj.complex_funding_reference.first.funder_identifier).to eq ['f1234']
+      expect(@obj.complex_funding_reference.first.funder_name).to eq ['Bank']
+      expect(@obj.complex_funding_reference.first.award_number).to be_empty
+      expect(@obj.complex_funding_reference.first.award_uri).to be_empty
+      expect(@obj.complex_funding_reference.first.award_title).to eq ['No free lunch']
+    end
+
+    it 'creates a complex funding reference active triple resource with all the attributes' do
+      @obj = build(:crc_dataset,
+                   complex_funding_reference_attributes: [{
+                                                            funder_identifier: 'f1234',
+                                                            funder_name: 'Bank',
+                                                            award_number: 'a1234',
+                                                            award_uri: 'http://award.com/a1234',
+                                                            award_title: 'No free lunch'
+                                                          }]
+      )
+      expect(@obj.complex_funding_reference.first).to be_kind_of ActiveTriples::Resource
+      expect(@obj.complex_funding_reference.first.id).to include('#fundref')
+      expect(@obj.complex_funding_reference.first.funder_identifier).to eq ['f1234']
+      expect(@obj.complex_funding_reference.first.funder_name).to eq ['Bank']
+      expect(@obj.complex_funding_reference.first.award_number).to eq ['a1234']
+      expect(@obj.complex_funding_reference.first.award_uri).to eq ['http://award.com/a1234']
+      expect(@obj.complex_funding_reference.first.award_title).to eq ['No free lunch']
+    end
+
+    it 'rejects a complex funding reference active triple with no attributes' do
+      @obj = build(:crc_dataset,
+                   complex_funding_reference_attributes: [{
+                                                            funder_identifier: '',
+                                                            funder_name: ''
+                                                          }]
+      )
+      expect(@obj.complex_funding_reference).to be_empty
+    end
+  end
+
+  describe 'complex_relation' do
+    it 'creates a relation active triple resource with all the attributes' do
+      @obj = build(:crc_dataset,
+                   complex_relation_attributes: [{
+                                                   title: 'A related item',
+                                                   url: 'http://example.com/relation',
+                                                   complex_identifier_attributes: [{
+                                                                                     identifier: ['123456'],
+                                                                                     label: ['local']
+                                                                                   }],
+                                                   relationship: 'IsPartOf'
+                                                 }]
+      )
+      expect(@obj.complex_relation.first).to be_kind_of ActiveTriples::Resource
+      expect(@obj.complex_relation.first.title).to eq ['A related item']
+      expect(@obj.complex_relation.first.url).to eq ['http://example.com/relation']
+      expect(@obj.complex_relation.first.complex_identifier.first).to be_kind_of ActiveTriples::Resource
+      expect(@obj.complex_relation.first.complex_identifier.first.identifier).to eq ['123456']
+      expect(@obj.complex_relation.first.complex_identifier.first.label).to eq ['local']
+      expect(@obj.complex_relation.first.relationship).to eq ['IsPartOf']
+    end
+
+    it 'creates a relation active triple resource with title, url, identifier and relationship role' do
+      @obj = build(:crc_dataset,
+                   complex_relation_attributes: [{
+                                                   title: 'A relation label',
+                                                   url: 'http://example.com/relation',
+                                                   complex_identifier_attributes: [{
+                                                                                     identifier: ['123456']
+                                                                                   }],
+                                                   relationship: 'isNewVersionOf'
+                                                 }]
+      )
+      expect(@obj.complex_relation.first).to be_kind_of ActiveTriples::Resource
+      expect(@obj.complex_relation.first.title).to eq ['A relation label']
+      expect(@obj.complex_relation.first.url).to eq ['http://example.com/relation']
+      expect(@obj.complex_relation.first.complex_identifier.first).to be_kind_of ActiveTriples::Resource
+      expect(@obj.complex_relation.first.complex_identifier.first.identifier).to eq ['123456']
+      expect(@obj.complex_relation.first.complex_identifier.first.label).to be_empty
+      expect(@obj.complex_relation.first.relationship).to eq ['isNewVersionOf']
+    end
+
+    it 'rejects relation active triple with url' do
+      @obj = build(:crc_dataset, complex_relation_attributes: [{
+                                                                 url: 'http://example.com/relation'
+                                                               }]
+      )
+      expect(@obj.complex_relation).to be_empty
+    end
+
+    it 'rejects relation active triple with identifier' do
+      @obj = build(:crc_dataset,
+                   complex_relation_attributes: [{
+                                                   complex_identifier_attributes: [{
+                                                                                     identifier: ['123456'],
+                                                                                     label: 'Local'
+                                                                                   }],
+                                                 }]
+      )
+      expect(@obj.complex_relation).to be_empty
+    end
+
+    it 'rejects relation active triple with reltionship name' do
+      @obj = build(:crc_dataset, complex_relation_attributes: [{
+                                                                 relationship: 'isPartOf'
+                                                               }]
+      )
+      expect(@obj.complex_relation).to be_empty
+    end
+  end
+
+  # ------ properties from CRC metadata ------
+  describe `crc_resource_type` do
+    it 'has crc_resource_type' do
+      @obj = build(:crc_dataset, crc_resource_type: 'Analysed')
+      expect(@obj.crc_resource_type).to eq 'Analysed'
+    end
+  end
+
+  describe `modality` do
+    it 'has modality' do
+      @obj = build(:crc_dataset, modality: 'modality 1')
+      expect(@obj.modality).to eq 'modality 1'
+    end
+  end
+
+  describe 'complex_subject' do
+    it 'creates a subject active triple resource with species' do
+      @obj = build(:crc_dataset,
+                   complex_subject_attributes: [{
+                                                  subject_species: 'human',
+                                                 }]
+      )
+
+      expect(@obj.complex_subject.first).to be_kind_of ActiveTriples::Resource
+      expect(@obj.complex_subject.first.subject_species).to eq ['human']
+      expect(@obj.complex_subject.first.subject_type).to be_empty
+      expect(@obj.complex_subject.first.subject_sex).to be_empty
+      expect(@obj.complex_subject.first.subject_age).to be_empty
+    end
+
+    it 'creates a subject active triple resource with species, type, sex and age' do
+      @obj = build(:crc_dataset,
+                   complex_subject_attributes: [{
+                                                  subject_species: 'human',
+                                                  subject_type: 'disease',
+                                                  subject_sex: 'm',
+                                                  subject_age: '22'
+                                                 }]
+      )
+      expect(@obj.complex_subject.first).to be_kind_of ActiveTriples::Resource
+      expect(@obj.complex_subject.first.subject_species).to eq ['human']
+      expect(@obj.complex_subject.first.subject_type).to eq ['disease']
+      expect(@obj.complex_subject.first.subject_sex).to eq ['m']
+      expect(@obj.complex_subject.first.subject_age).to eq ['22']
+    end
+
+  end
+
+  describe 'approval_number' do
+    it 'has approval_number' do
+      @obj = build(:crc_dataset, approval_number: 'approval_number 1')
+      expect(@obj.approval_number).to eq 'approval_number 1'
+    end
+  end
+
+  describe 'extra_information' do
+    it 'has extra_information' do
+      @obj = build(:crc_dataset, extra_information: ['extra_information'])
+      expect(@obj.extra_information).to eq ['extra_information']
+    end
+  end
+
+  describe `software_version` do
+    it 'has software_version' do
+      @obj = build(:crc_dataset, software_version: ['software_version 1'])
+      expect(@obj.software_version).to eq ['software_version 1']
+    end
+  end
+
+  # ------ properties from DublinCore metadata ------
+  describe `coverage` do
+    it 'has coverage' do
+      @obj = build(:crc_dataset, coverage: ['Europe'])
+      expect(@obj.coverage).to eq ['Europe']
+    end
+  end
+
+end
diff --git a/hyrax/spec/models/dataset_spec.rb b/hyrax/spec/models/dataset_spec.rb
index 77a91571f0ebf6de4ef3ddbdb176076508f885c0..e3a1a340cc6e8286a787bb8751070d0a5793e05f 100644
--- a/hyrax/spec/models/dataset_spec.rb
+++ b/hyrax/spec/models/dataset_spec.rb
@@ -3,7 +3,449 @@
 require 'rails_helper'
 
 RSpec.describe Dataset do
-  it "has tests" do
-    skip "Add your tests here"
+  it 'has human readable type for the dataset' do
+    @obj = build(:dataset)
+    expect(@obj.human_readable_type).to eq('Dataset')
   end
+
+  describe 'date_modified' do
+    it 'has date_modified as singular' do
+      @obj = build(:dataset, date_modified: '2018/04/23')
+      expect(@obj.date_modified).to eq '2018/04/23'
+    end
+  end
+
+  describe 'date_uploaded' do
+    it 'has date_uploaded as singular' do
+      @obj = build(:dataset, date_uploaded: '2018 01 02')
+      expect(@obj.date_uploaded).to eq '2018 01 02'
+    end
+  end
+
+  describe 'depositor' do
+    it 'has depositor' do
+      @obj = build(:dataset, depositor: 'Name of depositor')
+      expect(@obj.depositor).to eq 'Name of depositor'
+    end
+  end
+
+  describe 'title' do
+    it 'requires title' do
+      @obj = build(:dataset, title: nil)
+      expect{@obj.save!}.to raise_error(ActiveFedora::RecordInvalid,
+        'Validation failed: Title Your dataset must have a title.')
+    end
+
+    it 'has a multi valued title field' do
+      @obj = build(:dataset, title: ['test dataset'])
+      expect(@obj.title).to eq ['test dataset']
+    end
+  end
+
+  describe 'alternative_title' do
+    it 'has alternative_title' do
+      @obj = build(:dataset, alternative_title: ['Alternative Title'])
+      expect(@obj.alternative_title).to eq ['Alternative Title']
+    end
+  end
+
+  describe 'label' do
+    it 'has label as singular' do
+      @obj = build(:dataset, label: 'Label 1')
+      expect(@obj.label).to eq 'Label 1'
+    end
+  end
+
+  describe 'relative_path' do
+    it 'has relative_path as singular' do
+      @obj = build(:dataset, relative_path: 'relative/path/to/file')
+      expect(@obj.relative_path).to eq 'relative/path/to/file'
+    end
+  end
+
+  describe 'import_url' do
+    it 'has import_url as singular' do
+      @obj = build(:dataset, import_url: 'http://example.com/import/url')
+      expect(@obj.import_url).to eq 'http://example.com/import/url'
+    end
+  end
+
+  describe 'resource_type' do
+    it 'has resource_type' do
+      @obj = build(:dataset, resource_type: ['Dataset'])
+      expect(@obj.resource_type).to eq ['Dataset']
+    end
+  end
+
+  describe 'creator' do
+    it 'has creator' do
+      @obj = build(:dataset, creator: ['Creator 1'])
+      expect(@obj.creator).to eq ['Creator 1']
+    end
+  end
+
+  describe 'contributor' do
+    it 'has contributor' do
+      @obj = build(:dataset, contributor: ['contributor 1'])
+      expect(@obj.contributor).to eq ['contributor 1']
+    end
+  end
+
+  describe 'description' do
+    it 'has description' do
+      @obj = build(:dataset, description: ['description 1'])
+      expect(@obj.description).to eq ['description 1']
+    end
+  end
+
+  describe 'abstract' do
+    it 'has abstract' do
+      @obj = build(:dataset, abstract: ['abstract 1'])
+      expect(@obj.abstract).to eq ['abstract 1']
+    end
+  end
+
+  describe 'keyword' do
+    it 'has keyword' do
+      @obj = build(:dataset, keyword: ['keyword 2', '3 keyword', 'keyword 1'])
+      expect(@obj.keyword).to eq ['keyword 2', '3 keyword', 'keyword 1']
+    end
+  end
+
+  describe 'license' do
+    it 'has license' do
+      @obj = build(:dataset, license: ['CC-0'])
+      expect(@obj.license).to eq ['CC-0']
+    end
+  end
+
+  describe 'rights_notes' do
+    it 'has rights_notes' do
+      @obj = build(:dataset, rights_notes: ['rights_notes 1'])
+      expect(@obj.rights_notes).to eq ['rights_notes 1']
+    end
+  end
+
+  describe 'rights_statement' do
+    it 'has rights_statement' do
+      @obj = build(:dataset, rights_statement: ['rights_statement 1'])
+      expect(@obj.rights_statement).to eq ['rights_statement 1']
+    end
+  end
+
+  describe 'access_right' do
+    it 'has access_right' do
+      @obj = build(:dataset, access_right: ['access_right 1'])
+      expect(@obj.access_right).to eq ['access_right 1']
+    end
+  end
+
+  describe 'publisher' do
+    it 'has publisher' do
+      @obj = build(:dataset, publisher: ['publisher 1'])
+      expect(@obj.publisher).to eq ['publisher 1']
+    end
+  end
+
+  describe 'date_created' do
+    it 'has date_created' do
+      @obj = build(:dataset, date_created: ['date_created 1'])
+      expect(@obj.date_created).to eq ['date_created 1']
+    end
+  end
+
+  describe 'subject' do
+    it 'has subject' do
+      @obj = build(:dataset, subject: ['subject 1'])
+      expect(@obj.subject).to eq ['subject 1']
+    end
+  end
+
+  describe 'language' do
+    it 'has language' do
+      @obj = build(:dataset, language: ['language 1'])
+      expect(@obj.language).to eq ['language 1']
+    end
+  end
+
+  describe 'identifier' do
+    it 'has identifier' do
+      @obj = build(:dataset, identifier: ['identifier 1'])
+      expect(@obj.identifier).to eq ['identifier 1']
+    end
+  end
+
+  describe 'based_near' do
+    it 'has based_near' do
+      @obj = build(:dataset, based_near: ['me'])
+      expect(@obj.based_near).to eq ['me']
+    end
+  end
+
+  describe 'related_url' do
+    it 'has related_url' do
+      @obj = build(:dataset, related_url: ['http://example.com/related/url'])
+      expect(@obj.related_url).to eq ['http://example.com/related/url']
+    end
+  end
+
+  describe 'bibliographic_citation' do
+    it 'has bibliographic_citation' do
+      @obj = build(:dataset, bibliographic_citation: ['bibliographic_citation 1'])
+      expect(@obj.bibliographic_citation).to eq ['bibliographic_citation 1']
+    end
+  end
+
+  describe 'source' do
+    it 'has source' do
+      @obj = build(:dataset, source: ['Source 1'])
+      expect(@obj.source).to eq ['Source 1']
+    end
+  end
+
+  describe 'doi' do
+    it 'has doi as singular' do
+      @obj = build(:dataset, doi: '123167641234')
+      expect(@obj.doi).to eq '123167641234'
+    end
+  end
+
+  describe 'complex_person' do
+    it 'creates a person active triple resource with name' do
+      @obj = build(:dataset,
+                   complex_person_attributes: [{
+                                                 name: 'Anamika'
+                                               }]
+      )
+      expect(@obj.complex_person.first).to be_kind_of ActiveTriples::Resource
+      expect(@obj.complex_person.first.name).to eq ['Anamika']
+      expect(@obj.complex_person.first.first_name).to be_empty
+      expect(@obj.complex_person.first.last_name).to be_empty
+      expect(@obj.complex_person.first.email).to be_empty
+      expect(@obj.complex_person.first.affiliation).to be_empty
+      expect(@obj.complex_person.first.role).to be_empty
+      expect(@obj.complex_person.first.orcid).to be_empty
+      expect(@obj.complex_person.first.display_order).to be_empty
+    end
+
+    it 'creates a person active triple resource with name, affiliation, orcid, role and display order' do
+      @obj = build(:dataset,
+                   complex_person_attributes: [{
+                                                 name: 'Anamika',
+                                                 affiliation: 'Some org',
+                                                 role: 'Creator',
+                                                 orcid: '1231234',
+                                                 display_order: 1
+                                               }]
+      )
+      expect(@obj.complex_person.first).to be_kind_of ActiveTriples::Resource
+      expect(@obj.complex_person.first.name).to eq ['Anamika']
+      expect(@obj.complex_person.first.first_name).to be_empty
+      expect(@obj.complex_person.first.last_name).to be_empty
+      expect(@obj.complex_person.first.email).to be_empty
+      expect(@obj.complex_person.first.role).to eq ['Creator']
+      expect(@obj.complex_person.first.orcid).to eq ['1231234']
+      expect(@obj.complex_person.first.display_order).to eq([1])
+      expect(@obj.complex_person.first.affiliation).to eq(['Some org'])
+    end
+
+    it 'rejects person active triple with no name and only orcid' do
+      @obj = build(:dataset, complex_person_attributes: [{
+                                                           orcid: 'http://example.com/person/123456'
+                                                         }]
+      )
+      expect(@obj.complex_person).to be_empty
+    end
+  end
+
+  describe 'complex_date' do
+    it 'creates a date active triple resource with all the attributes' do
+      @obj = build(:dataset,
+        complex_date_attributes: [{
+          date: '1978-10-28',
+          description: 'Some kind of a date',
+        }]
+      )
+      expect(@obj.complex_date.first).to be_kind_of ActiveTriples::Resource
+      expect(@obj.complex_date.first.date).to eq ['1978-10-28']
+      expect(@obj.complex_date.first.description).to eq ['Some kind of a date']
+    end
+
+    it 'creates a date active triple resource with just the date' do
+      @obj = build(:dataset,
+        complex_date_attributes: [{
+          date: '1984-09-01'
+        }]
+      )
+      expect(@obj.complex_date.first).to be_kind_of ActiveTriples::Resource
+      expect(@obj.complex_date.first.date).to eq ['1984-09-01']
+      expect(@obj.complex_date.first.description).to be_empty
+    end
+
+    it 'rejects a date active triple with no date' do
+      @obj = build(:dataset,
+        complex_date_attributes: [{
+          description: 'Local date'
+        }]
+      )
+      expect(@obj.complex_date).to be_empty
+    end
+  end
+
+  describe 'complex_identifier' do
+    it 'creates an identifier active triple resource with all the attributes' do
+      @obj = build(:dataset,
+        complex_identifier_attributes: [{
+          identifier: '0000-0000-0000-0000',
+          scheme: 'uri_of_ORCID_scheme',
+          label: 'ORCID'
+        }]
+      )
+      expect(@obj.complex_identifier.first).to be_kind_of ActiveTriples::Resource
+      expect(@obj.complex_identifier.first.identifier).to eq ['0000-0000-0000-0000']
+      expect(@obj.complex_identifier.first.scheme).to eq ['uri_of_ORCID_scheme']
+      expect(@obj.complex_identifier.first.label).to eq ['ORCID']
+    end
+
+    it 'creates an identifier active triple resource with just the identifier' do
+      @obj = build(:dataset,
+        complex_identifier_attributes: [{
+          identifier: '1234'
+        }]
+      )
+      expect(@obj.complex_identifier.first).to be_kind_of ActiveTriples::Resource
+      expect(@obj.complex_identifier.first.identifier).to eq ['1234']
+      expect(@obj.complex_identifier.first.label).to be_empty
+      expect(@obj.complex_identifier.first.scheme).to be_empty
+    end
+
+    it 'rejects an identifier active triple with no identifier' do
+      @obj = build(:dataset,
+        complex_identifier_attributes: [{
+          label: 'Local'
+        }]
+      )
+      expect(@obj.complex_identifier).to be_empty
+    end
+  end
+
+  describe 'complex_funding_reference' do
+    it 'creates a complex funding reference active triple resource with funding reference' do
+      @obj = build(:dataset,
+                   complex_funding_reference_attributes: [{
+                                                            funder_identifier: 'f1234',
+                                                            funder_name: 'Bank',
+                                                            award_title: 'No free lunch'
+                                                          }]
+      )
+      expect(@obj.complex_funding_reference.first).to be_kind_of ActiveTriples::Resource
+      expect(@obj.complex_funding_reference.first.id).to include('#fundref')
+      expect(@obj.complex_funding_reference.first.funder_identifier).to eq ['f1234']
+      expect(@obj.complex_funding_reference.first.funder_name).to eq ['Bank']
+      expect(@obj.complex_funding_reference.first.award_number).to be_empty
+      expect(@obj.complex_funding_reference.first.award_uri).to be_empty
+      expect(@obj.complex_funding_reference.first.award_title).to eq ['No free lunch']
+    end
+
+    it 'creates a complex funding reference active triple resource with all the attributes' do
+      @obj = build(:dataset,
+                   complex_funding_reference_attributes: [{
+                                                            funder_identifier: 'f1234',
+                                                            funder_name: 'Bank',
+                                                            award_number: 'a1234',
+                                                            award_uri: 'http://award.com/a1234',
+                                                            award_title: 'No free lunch'
+                                                          }]
+      )
+      expect(@obj.complex_funding_reference.first).to be_kind_of ActiveTriples::Resource
+      expect(@obj.complex_funding_reference.first.id).to include('#fundref')
+      expect(@obj.complex_funding_reference.first.funder_identifier).to eq ['f1234']
+      expect(@obj.complex_funding_reference.first.funder_name).to eq ['Bank']
+      expect(@obj.complex_funding_reference.first.award_number).to eq ['a1234']
+      expect(@obj.complex_funding_reference.first.award_uri).to eq ['http://award.com/a1234']
+      expect(@obj.complex_funding_reference.first.award_title).to eq ['No free lunch']
+    end
+
+    it 'rejects a complex funding reference active triple with no attributes' do
+      @obj = build(:dataset,
+                   complex_funding_reference_attributes: [{
+                                                 funder_identifier: '',
+                                                 funder_name: ''
+                                               }]
+      )
+      expect(@obj.complex_funding_reference).to be_empty
+    end
+  end
+
+  describe 'complex_relation' do
+    it 'creates a relation active triple resource with all the attributes' do
+      @obj = build(:dataset,
+                   complex_relation_attributes: [{
+                                                   title: 'A related item',
+                                                   url: 'http://example.com/relation',
+                                                   complex_identifier_attributes: [{
+                                                                                     identifier: ['123456'],
+                                                                                     label: ['local']
+                                                                                   }],
+                                                   relationship: 'IsPartOf'
+                                                 }]
+      )
+      expect(@obj.complex_relation.first).to be_kind_of ActiveTriples::Resource
+      expect(@obj.complex_relation.first.title).to eq ['A related item']
+      expect(@obj.complex_relation.first.url).to eq ['http://example.com/relation']
+      expect(@obj.complex_relation.first.complex_identifier.first).to be_kind_of ActiveTriples::Resource
+      expect(@obj.complex_relation.first.complex_identifier.first.identifier).to eq ['123456']
+      expect(@obj.complex_relation.first.complex_identifier.first.label).to eq ['local']
+      expect(@obj.complex_relation.first.relationship).to eq ['IsPartOf']
+    end
+
+    it 'creates a relation active triple resource with title, url, identifier and relationship role' do
+      @obj = build(:dataset,
+                   complex_relation_attributes: [{
+                                                   title: 'A relation label',
+                                                   url: 'http://example.com/relation',
+                                                   complex_identifier_attributes: [{
+                                                                                     identifier: ['123456']
+                                                                                   }],
+                                                   relationship: 'isNewVersionOf'
+                                                 }]
+      )
+      expect(@obj.complex_relation.first).to be_kind_of ActiveTriples::Resource
+      expect(@obj.complex_relation.first.title).to eq ['A relation label']
+      expect(@obj.complex_relation.first.url).to eq ['http://example.com/relation']
+      expect(@obj.complex_relation.first.complex_identifier.first).to be_kind_of ActiveTriples::Resource
+      expect(@obj.complex_relation.first.complex_identifier.first.identifier).to eq ['123456']
+      expect(@obj.complex_relation.first.complex_identifier.first.label).to be_empty
+      expect(@obj.complex_relation.first.relationship).to eq ['isNewVersionOf']
+    end
+
+    it 'rejects relation active triple with url' do
+      @obj = build(:dataset, complex_relation_attributes: [{
+                                                             url: 'http://example.com/relation'
+                                                           }]
+      )
+      expect(@obj.complex_relation).to be_empty
+    end
+
+    it 'rejects relation active triple with identifier' do
+      @obj = build(:dataset,
+                   complex_relation_attributes: [{
+                                                   complex_identifier_attributes: [{
+                                                                                     identifier: ['123456'],
+                                                                                     label: 'Local'
+                                                                                   }],
+                                                 }]
+      )
+      expect(@obj.complex_relation).to be_empty
+    end
+
+    it 'rejects relation active triple with reltionship name' do
+      @obj = build(:dataset, complex_relation_attributes: [{
+                                                             relationship: 'isPartOf'
+                                                           }]
+      )
+      expect(@obj.complex_relation).to be_empty
+    end
+  end
+
 end
diff --git a/hyrax/spec/presenters/dataset_presenter_spec.rb b/hyrax/spec/presenters/dataset_presenter_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..812481265c795b4ff1680f15eb6c91f079bf70f4
--- /dev/null
+++ b/hyrax/spec/presenters/dataset_presenter_spec.rb
@@ -0,0 +1,101 @@
+require 'rails_helper'
+
+RSpec.describe Hyrax::DatasetPresenter do
+  DatabaseCleaner.clean
+  let(:dataset) { create(:dataset, :open, :with_alternative_title, depositor: 'despositor') }
+  let(:solr_document) { SolrDocument.new(dataset.to_solr) }
+  let(:host) { double(host: 'http://example.org') }
+  let(:user) { nil }
+  let(:presenter) { described_class.new(solr_document, Ability.new(user), host) }
+
+  before do
+    DatabaseCleaner.clean
+  end
+
+  describe '#export_as_ttl' do
+    subject { presenter.export_as_ttl }
+    let(:export_regex) {
+      [
+        %r(<http://example.org/concern/datasets/#{dataset.id}> a),
+        %r(<http://projecthydra.org/works/models#Work>),
+        %r(<http://pcdm.org/models#Object>),
+        %r(<http://purl.org/dc/terms/title> "Open Dataset";),
+        %r(<http://purl.org/dc/terms/alternative> "Alternative-Title-123";),
+        %r(<http://www.w3.org/ns/auth/acl#accessControl> <http://example.org/catalog/([a-f0-9\\-]*)+>;),
+        %r(<info:fedora/fedora-system:def/model#hasModel> "Dataset" .)
+      ]
+    }
+    let(:abstract_regex) { %r(<http://purl.org/dc/elements/1.1/description> "Abstract-Description-123";) }
+    let(:supervisor_regex) { %r(<http://www.nims.go.jp/vocabs/ngdr/supervisor-approval> "Professor-Supervisor-Approval";) }
+    let(:depositor_regex) { %r(<http://example.org/concern/datasets/#{dataset.id}> <http://id.loc.gov/vocabulary/relators/dpt> "depositor") }
+
+    it 'exports' do
+      export_regex.each do |regex|
+        expect(subject).to match(regex)
+      end
+    end
+
+    context 'anonymous user' do
+      it { is_expected.not_to match(abstract_regex) }
+      it { is_expected.not_to match(supervisor_regex) }
+      it { is_expected.not_to match(depositor_regex) }
+    end
+  end
+
+  describe '#export_as_nt' do
+    subject { presenter.export_as_nt }
+    let(:export_regex) {
+      [
+        %r(<http://example.org/concern/datasets/#{dataset.id}> <http://www.w3.org/ns/auth/acl#accessControl> <http://example.org/catalog/([a-f0-9\\-]*)+>),
+        %r(<http://example.org/concern/datasets/#{dataset.id}> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://projecthydra.org/works/models#Work>),
+        %r(<http://example.org/concern/datasets/#{dataset.id}> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://pcdm.org/models#Object>),
+        %r(<http://example.org/concern/datasets/#{dataset.id}> <info:fedora/fedora-system:def/model#hasModel> "Dataset"),
+        %r(<http://example.org/concern/datasets/#{dataset.id}> <http://purl.org/dc/terms/alternative> "Alternative-Title-123"),
+        %r(<http://example.org/concern/datasets/#{dataset.id}> <http://purl.org/dc/terms/title> "Open Dataset")
+      ]
+    }
+    let(:abstract_regex) { %r(<http://example.org/concern/datasets/#{dataset.id}> <http://purl.org/dc/elements/1.1/description> "Abstract-Description-123") }
+    let(:supervisor_regex) { %r(<http://example.org/concern/datasets/#{dataset.id}> <http://www.nims.go.jp/vocabs/ngdr/supervisor-approval> "Professor-Supervisor-Approval") }
+    let(:depositor_regex) { %r(<http://example.org/concern/publications/#{dataset.id}> <http://id.loc.gov/vocabulary/relators/dpt> "depositor") }
+
+    it 'exports' do
+      export_regex.each do |regex|
+        expect(subject).to match(regex)
+      end
+    end
+
+    context 'anonymous user' do
+      it { is_expected.not_to match(abstract_regex) }
+      it { is_expected.not_to match(supervisor_regex) }
+      it { is_expected.not_to match(depositor_regex) }
+    end
+  end
+
+  describe '#export_as_jsonld' do
+    subject { JSON.parse(presenter.export_as_jsonld) }
+    # NB: it is important to use => rather than a colon: - otherwise the strings get symbolised
+
+    it 'exports' do
+      expect(subject["@context"]).to include(
+        "pcdmterms" => "http://pcdm.org/models#",
+        "worksterms" => "http://projecthydra.org/works/models#",
+        "dc" => "http://purl.org/dc/terms/",
+        "acl" => "http://www.w3.org/ns/auth/acl#",
+        "system" => "info:fedora/fedora-system:",
+        "model" => "system:def/model#"
+      )
+      expect(subject["@id"]).to eql "http://example.org/concern/datasets/#{dataset.id}"
+      expect(subject["dc:title"]).to eql "Open Dataset"
+      expect(subject["dc:alternative"]).to eql "Alternative-Title-123"
+      expect(subject["model:hasModel"]).to eql "Dataset"
+      expect(subject["acl:accessControl"]).to include("@id")
+      expect(subject["@type"]).to match_array %w(pcdm:Object worksterms:Work)
+    end
+
+    context 'anonymous user' do
+      it { is_expected.not_to include("dc11:description" => "Abstract-Description-123") }
+      it { is_expected.not_to include("nimsrdp:supervisor-approval" => "Professor-Supervisor-Approval") }
+      it { is_expected.not_to include("marcrelators:dpt" => "depositor") }
+    end
+  end
+end
diff --git a/hyrax/spec/presenters/hyrax/crc_dataset_presenter_spec.rb b/hyrax/spec/presenters/hyrax/crc_dataset_presenter_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..bd60a0ea7b0497e376a17969c73c376a9357dae3
--- /dev/null
+++ b/hyrax/spec/presenters/hyrax/crc_dataset_presenter_spec.rb
@@ -0,0 +1,9 @@
+# Generated via
+#  `rails generate hyrax:work CrcDataset`
+require 'rails_helper'
+
+RSpec.describe Hyrax::CrcDatasetPresenter do
+  it "has tests" do
+    skip "Add your tests here"
+  end
+end
diff --git a/hyrax/spec/rails_helper.rb b/hyrax/spec/rails_helper.rb
index b6317b5a3431da55461a28b456cd3b4798691513..6e7668258b4bad5ea1f639e787791b7d55b23646 100644
--- a/hyrax/spec/rails_helper.rb
+++ b/hyrax/spec/rails_helper.rb
@@ -1,10 +1,15 @@
 # This file is copied to spec/ when you run 'rails generate rspec:install'
 require 'spec_helper'
 ENV['RAILS_ENV'] ||= 'test'
-require_relative '../config/environment'
+require File.expand_path('../../config/environment', __FILE__)
 # Prevent database truncation if the environment is production
 abort("The Rails environment is running in production mode!") if Rails.env.production?
 require 'rspec/rails'
+require 'support/factory_bot'
+require 'support/input_support'
+# require 'capybara/rails'  # might need to turn this on in future
+# require 'capybara/rspec'  # might need to turn this on in future
+
 # Add additional requires below this line. Rails is not loaded until this point!
 
 # Requires supporting ruby files with custom matchers and macros, etc, in
@@ -33,6 +38,8 @@ end
 RSpec.configure do |config|
   # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
   config.fixture_path = "#{::Rails.root}/spec/fixtures"
+  config.include Devise::Test::ControllerHelpers, type: :view
+  config.include Devise::Test::ControllerHelpers, type: :controller
 
   # If you're not using ActiveRecord, or you'd prefer not to run each of your
   # examples within a transaction, remove the following line or assign false
diff --git a/hyrax/spec/renderers/nested_date_attribute_renderer_spec.rb b/hyrax/spec/renderers/nested_date_attribute_renderer_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..88692589a7c544cbb001d0b2fbef868f0f6d918e
--- /dev/null
+++ b/hyrax/spec/renderers/nested_date_attribute_renderer_spec.rb
@@ -0,0 +1,24 @@
+require 'rails_helper'
+
+RSpec.describe NestedDateAttributeRenderer do
+  let(:html) { described_class.new('Date', nested_value.to_json).render }
+  subject { Capybara.string(html) }
+
+  context 'valid date' do
+    let(:nested_value) { build(:dataset, :with_complex_date).complex_date.first }
+    it 'generates the correct fields' do
+      is_expected.to have_css('th', text: 'Date')
+      is_expected.to have_css('div.row label', text: 'Collected')
+      is_expected.to have_css('div.row', text: '28/10/1978')
+    end
+  end
+
+  context 'invalid date' do
+    let(:nested_value) { { date: ["Foo"], description: ["Bar"] } }
+    it 'generates the correct fields' do
+      is_expected.to have_css('th', text: 'Date')
+      is_expected.to have_css('div.row label', text: 'Bar')
+      is_expected.to have_css('div.row', text: 'Foo')
+    end
+  end
+end
diff --git a/hyrax/spec/renderers/nested_funding_reference_attribute_renderer_spec.rb b/hyrax/spec/renderers/nested_funding_reference_attribute_renderer_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..24def1b62a0f262d9735206e63c3324f06fa3aa0
--- /dev/null
+++ b/hyrax/spec/renderers/nested_funding_reference_attribute_renderer_spec.rb
@@ -0,0 +1,23 @@
+require 'rails_helper'
+
+RSpec.describe NestedFundingReferenceAttributeRenderer do
+  let(:html) { described_class.new('Funding Reference', nested_value.to_json).render }
+  let(:nested_value) { build(:dataset, :with_complex_funding_reference).complex_funding_reference.first }
+  subject { Capybara.string(html) }
+
+  it 'generates the correct fields' do
+    is_expected.to have_css('th', text: 'Funding reference')
+
+    is_expected.to have_css('div.row label', text: 'Funder identifier')
+    is_expected.to have_css('div.row', text: 'f1234')
+
+    is_expected.to have_css('div.row label', text: 'Funder name')
+    is_expected.to have_css('div.row', text: 'Bank')
+
+    is_expected.to have_css('div.row label', text: 'Award number')
+    is_expected.to have_css('div.row', text: 'a1234')
+
+    is_expected.to have_css('div.row label', text: 'Award title')
+    is_expected.to have_css('div.row', text: 'No free lunch')
+  end
+end
diff --git a/hyrax/spec/renderers/nested_identifier_attribute_renderer_spec.rb b/hyrax/spec/renderers/nested_identifier_attribute_renderer_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..64425b32b5c2c1b252c640febf5b290105e1699e
--- /dev/null
+++ b/hyrax/spec/renderers/nested_identifier_attribute_renderer_spec.rb
@@ -0,0 +1,25 @@
+require 'rails_helper'
+
+RSpec.describe NestedIdentifierAttributeRenderer do
+  let(:html) { described_class.new('Identifier', nested_value.to_json).render }
+
+  context 'doi' do
+    let(:nested_value) { build(:dataset, :with_complex_identifier).complex_identifier.first }
+    subject { Capybara.string(html) }
+    it 'generates the correct fields' do
+      is_expected.to have_css('th', text: 'Identifier')
+      is_expected.to have_css('div.row label', text: 'DOI')
+      is_expected.to have_css('div.row a[href="https://doi.org/10.0.1111"]', text: 'doi:10.0.1111')
+    end
+  end
+
+  context 'handle' do
+    let(:nested_value) { build(:dataset, :with_complex_relation).complex_relation.first.complex_identifier.first }
+    subject { Capybara.string(html) }
+    it 'generates the correct fields' do
+      is_expected.to have_css('th', text: 'Identifier')
+      is_expected.to have_css('div.row label', text: 'Identifier - Persistent')
+      is_expected.to have_css('div.row a[href="https://hdl.handle.net/4263537/400"]', text: 'hdl:4263537/400')
+    end
+  end
+end
diff --git a/hyrax/spec/renderers/nested_person_attribute_renderer_spec.rb b/hyrax/spec/renderers/nested_person_attribute_renderer_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..13d45eb4876aff21893acc5aeeaa032f93e9e81a
--- /dev/null
+++ b/hyrax/spec/renderers/nested_person_attribute_renderer_spec.rb
@@ -0,0 +1,35 @@
+require 'rails_helper'
+
+RSpec.describe NestedPersonAttributeRenderer do
+  let(:html) { described_class.new('Person', nested_value.to_json).render }
+  subject { Capybara.string(html) }
+
+  context 'with name' do
+    let(:nested_value) { build(:dataset, :with_complex_person).complex_person.first }
+
+    it 'generates the correct fields' do
+      is_expected.to have_css('th', text: 'Person')
+
+      is_expected.to have_css('div.row label', text: 'Name')
+      is_expected.to have_css('div.row a', text: 'Anamika')
+
+      is_expected.to have_css('div.row label', text: 'Affiliation')
+      is_expected.to have_css('div.row', text: 'University')
+
+      is_expected.to have_css('div.row label', text: 'Role')
+      is_expected.to have_css('div.row', text: 'operator')
+
+      is_expected.to have_css('div.row label', text: 'ORCID')
+      is_expected.to have_css('div.row', text: '123456')
+    end
+  end
+
+  context 'with first_name and last_name' do
+    let(:nested_value) { { first_name: ['Foo'], last_name: ['Bar'] } }
+    it 'generates the correct fields' do
+      is_expected.to have_css('th', text: 'Person')
+      is_expected.to have_css('div.row label', text: 'Name')
+      is_expected.to have_css('div.row a', text: 'Foo Bar')
+    end
+  end
+end
diff --git a/hyrax/spec/renderers/nested_relation_attribute_renderer_spec.rb b/hyrax/spec/renderers/nested_relation_attribute_renderer_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..64eb22f33a9a58595926bd443e2a7e9afda3b3ef
--- /dev/null
+++ b/hyrax/spec/renderers/nested_relation_attribute_renderer_spec.rb
@@ -0,0 +1,20 @@
+require 'rails_helper'
+
+RSpec.describe NestedRelationAttributeRenderer do
+  let(:html) { described_class.new('Relationship', nested_value.to_json).render }
+  let(:nested_value) { build(:dataset, :with_complex_relation).complex_relation.first }
+  subject { Capybara.string(html) }
+
+  it 'generates the correct fields' do
+    is_expected.to have_css('th', text: 'Relationship')
+
+    is_expected.to have_css('div.row label', text: 'Title')
+    is_expected.to have_link("A relation label", href: 'http://example.com/relation')
+
+    is_expected.to have_css('div.row label', text: 'Identifier - Persistent')
+    is_expected.to have_css('div.row a[href="https://hdl.handle.net/4263537/400"]', text: 'hdl:4263537/400')
+
+    is_expected.to have_css('div.row label', text: 'Relationship')
+    is_expected.to have_css('div.row', text: 'is new version of')
+  end
+end
diff --git a/hyrax/spec/spec_helper.rb b/hyrax/spec/spec_helper.rb
index a0d4080592b01aca1eaf0946a567fc81bae334f2..30bc7f28f569b67615aac2c7804d14d2b85c43f7 100644
--- a/hyrax/spec/spec_helper.rb
+++ b/hyrax/spec/spec_helper.rb
@@ -1,94 +1,30 @@
-# This file was generated by the `rails generate rspec:install` command. Conventionally, all
-# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
-# The generated `.rspec` file contains `--require spec_helper` which will cause
-# this file to always be loaded, without a need to explicitly require it in any
-# files.
-#
-# Given that it is always loaded, you are encouraged to keep this file as
-# light-weight as possible. Requiring heavyweight dependencies from this file
-# will add to the boot time of your test suite on EVERY test run, even for an
-# individual file that may not need all of that loaded. Instead, consider making
-# a separate helper file that requires the additional dependencies and performs
-# the additional setup, and require it from the spec files that actually need
-# it.
-#
-# See https://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
+require 'coveralls'
+require 'simplecov'
+# require 'webdrivers'
+require 'sidekiq/testing' # use fake Redis for testing
+
+Coveralls.wear!
+SimpleCov.start 'rails' do
+  # we do not unit test importers, as this is run-once code
+  add_filter "lib/importers"
+
+  # additional code coverage groups for Hyrax
+  add_group 'Actors', 'app/actors'
+  add_group 'Forms', 'app/forms'
+  add_group 'Indexers', 'app/indexers'
+  add_group 'Inputs', 'app/inputs'
+  add_group 'Presenters', 'app/presenters'
+  add_group 'Renderers', 'app/renderers'
+  add_group 'Services', 'app/services'
+end
 RSpec.configure do |config|
-  # rspec-expectations config goes here. You can use an alternate
-  # assertion/expectation library such as wrong or the stdlib/minitest
-  # assertions if you prefer.
   config.expect_with :rspec do |expectations|
-    # This option will default to `true` in RSpec 4. It makes the `description`
-    # and `failure_message` of custom matchers include text for helper methods
-    # defined using `chain`, e.g.:
-    #     be_bigger_than(2).and_smaller_than(4).description
-    #     # => "be bigger than 2 and smaller than 4"
-    # ...rather than:
-    #     # => "be bigger than 2"
     expectations.include_chain_clauses_in_custom_matcher_descriptions = true
   end
 
-  # rspec-mocks config goes here. You can use an alternate test double
-  # library (such as bogus or mocha) by changing the `mock_with` option here.
   config.mock_with :rspec do |mocks|
-    # Prevents you from mocking or stubbing a method that does not exist on
-    # a real object. This is generally recommended, and will default to
-    # `true` in RSpec 4.
     mocks.verify_partial_doubles = true
   end
 
-  # This option will default to `:apply_to_host_groups` in RSpec 4 (and will
-  # have no way to turn it off -- the option exists only for backwards
-  # compatibility in RSpec 3). It causes shared context metadata to be
-  # inherited by the metadata hash of host groups and examples, rather than
-  # triggering implicit auto-inclusion in groups with matching metadata.
   config.shared_context_metadata_behavior = :apply_to_host_groups
-
-# The settings below are suggested to provide a good initial experience
-# with RSpec, but feel free to customize to your heart's content.
-=begin
-  # This allows you to limit a spec run to individual examples or groups
-  # you care about by tagging them with `:focus` metadata. When nothing
-  # is tagged with `:focus`, all examples get run. RSpec also provides
-  # aliases for `it`, `describe`, and `context` that include `:focus`
-  # metadata: `fit`, `fdescribe` and `fcontext`, respectively.
-  config.filter_run_when_matching :focus
-
-  # Allows RSpec to persist some state between runs in order to support
-  # the `--only-failures` and `--next-failure` CLI options. We recommend
-  # you configure your source control system to ignore this file.
-  config.example_status_persistence_file_path = "spec/examples.txt"
-
-  # Limits the available syntax to the non-monkey patched syntax that is
-  # recommended. For more details, see:
-  # https://relishapp.com/rspec/rspec-core/docs/configuration/zero-monkey-patching-mode
-  config.disable_monkey_patching!
-
-  # Many RSpec users commonly either run the entire suite or an individual
-  # file, and it's useful to allow more verbose output when running an
-  # individual spec file.
-  if config.files_to_run.one?
-    # Use the documentation formatter for detailed output,
-    # unless a formatter has already been configured
-    # (e.g. via a command-line flag).
-    config.default_formatter = "doc"
-  end
-
-  # Print the 10 slowest examples and example groups at the
-  # end of the spec run, to help surface which specs are running
-  # particularly slow.
-  config.profile_examples = 10
-
-  # Run specs in random order to surface order dependencies. If you find an
-  # order dependency and want to debug it, you can fix the order by providing
-  # the seed, which is printed after each run.
-  #     --seed 1234
-  config.order = :random
-
-  # Seed global randomization in this process using the `--seed` CLI option.
-  # Setting this allows you to use `--seed` to deterministically reproduce
-  # test failures related to randomization by passing the same `--seed` value
-  # as the one that triggered the failure.
-  Kernel.srand config.seed
-=end
 end
diff --git a/hyrax/spec/support/factory_bot.rb b/hyrax/spec/support/factory_bot.rb
new file mode 100644
index 0000000000000000000000000000000000000000..4f266ff94213efe933483f4f3ae7f69655547cb5
--- /dev/null
+++ b/hyrax/spec/support/factory_bot.rb
@@ -0,0 +1,13 @@
+# spec/support/factory_bot.rb
+RSpec.configure do |config|
+  config.include FactoryBot::Syntax::Methods
+end
+
+# RSpec without Rails
+RSpec.configure do |config|
+  config.include FactoryBot::Syntax::Methods
+
+  # config.before(:suite) do
+  #   FactoryBot.find_definitions
+  # end
+end
diff --git a/hyrax/spec/support/input_support.rb b/hyrax/spec/support/input_support.rb
new file mode 100644
index 0000000000000000000000000000000000000000..83f54bf37bf7324312d437e42b37c90923a87ba9
--- /dev/null
+++ b/hyrax/spec/support/input_support.rb
@@ -0,0 +1,8 @@
+module InputSupport
+  extend ActiveSupport::Concern
+  include RSpec::Rails::HelperExampleGroup
+end
+
+RSpec.configure do |config|
+  config.include InputSupport, type: :input
+end
diff --git a/hyrax/spec/views/hyrax/datasets/_attribute_rows.html_spec.rb b/hyrax/spec/views/hyrax/datasets/_attribute_rows.html_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..37b7b2a2d24115b8f107d742d0d9b980b451f6c9
--- /dev/null
+++ b/hyrax/spec/views/hyrax/datasets/_attribute_rows.html_spec.rb
@@ -0,0 +1,43 @@
+require 'rails_helper'
+include Warden::Test::Helpers
+
+RSpec.describe 'hyrax/datasets/_attribute_rows' do
+  user = User.find_by(email: 'admin@hyrax')
+  let(:partial) { 'hyrax/datasets/attribute_rows' }
+  let(:dataset) { create(:dataset, :open,  :with_doi, :with_complex_person, :with_complex_date,
+                         :with_complex_identifier, :with_complex_funding_reference, :with_complex_relation) }
+  let(:presenter) { Hyrax::DatasetPresenter.new(SolrDocument.new(dataset.to_solr), Ability.new(user), controller.request) }
+
+  before do
+    allow(controller).to receive(:current_user).and_return(user)
+    login_as user if user.present?
+    render partial: partial, locals: { presenter: presenter }
+  end
+
+  # NB: the visibility of individual metadata components is set in app/models/ability.rb
+  # This test confirms the current expected behaviour (which is that most metadata is visible)
+
+  context 'authenticated user' do
+    it 'shows the correct metadata' do
+      # doi
+      expect(rendered).to have_content('1234-1567')
+      # complex person
+      expect(rendered).to have_content('Anamika')
+      expect(rendered).to have_content('operator')
+      expect(rendered).to have_content('University')
+      # complex identifier
+      expect(rendered).to have_content('doi:10.0.1111')
+      # complex relation
+      expect(rendered).to have_content('A relation label')
+      expect(rendered).to have_content('is new version of')
+      # complex funder reference
+      expect(rendered).to have_content('f1234')
+      expect(rendered).to have_content('Bank')
+      expect(rendered).to have_content('a1234')
+      expect(rendered).to have_content('http://example.com/a1234')
+      expect(rendered).to have_content('No free lunch')
+      # Abstract/Description is not displayed in this table partial
+      expect(rendered).not_to have_content('Abstract-Description-123')
+    end
+  end
+end