diff --git a/.travis.yml b/.travis.yml
index 901dee8186b32691e45cb6ec2ef6d481c1720e92..c42923886d3c4fd436a630050646a6f72a014646 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,5 +1,5 @@
 sudo: required
-dist: trusty
+dist: bionic
 
 env:
   # Install the latest docker-compose version for ci testing.
@@ -12,6 +12,9 @@ env:
   DSPACE_REST_NAMESPACE: '/server/api'
   DSPACE_REST_SSL: false
 
+services:
+  - xvfb
+
 before_install:
   # Docker Compose Install
   - curl -L https://github.com/docker/compose/releases/download/${COMPOSE_VERSION}/docker-compose-`uname -s`-`uname -m` > docker-compose
@@ -33,14 +36,6 @@ before_script:
 after_script:
   - docker-compose -f ./docker/docker-compose-travis.yml down
 
-addons:
-  apt:
-    sources:
-      - google-chrome
-    packages:
-      - dpkg
-      - google-chrome-stable
-
 language: node_js
 
 node_js:
@@ -53,8 +48,6 @@ cache:
 bundler_args: --retry 5
 
 script:
-  # Use Chromium instead of Chrome.
-  - export CHROME_BIN=chromium-browser
   - yarn run build
   - yarn run ci
   - cat coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js
diff --git a/config/environment.default.js b/config/environment.default.js
index 24386d6cf71f8e14e799e0c938b88bc60c2977f4..58193d31bc6898742bfc019d0601fb767f9c6f2c 100644
--- a/config/environment.default.js
+++ b/config/environment.default.js
@@ -185,6 +185,11 @@ module.exports = {
       undoTimeout: 10000 // 10 seconds
     }
   },
+  collection: {
+    edit: {
+      undoTimeout: 10000 // 10 seconds
+    }
+  },
   theme: {
     name: 'default',
   }
diff --git a/e2e/search-navbar/search-navbar.e2e-spec.ts b/e2e/search-navbar/search-navbar.e2e-spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b60f71919d55d55a6fe19756b90a66b4924ca2d9
--- /dev/null
+++ b/e2e/search-navbar/search-navbar.e2e-spec.ts
@@ -0,0 +1,46 @@
+import { ProtractorPage } from './search-navbar.po';
+import { browser } from 'protractor';
+
+describe('protractor SearchNavbar', () => {
+  let page: ProtractorPage;
+  let queryString: string;
+
+  beforeEach(() => {
+    page = new ProtractorPage();
+    queryString = 'the test query';
+  });
+
+  it('should go to search page with correct query if submitted (from home)', () => {
+    page.navigateToHome();
+    return checkIfSearchWorks();
+  });
+
+  it('should go to search page with correct query if submitted (from search)', () => {
+    page.navigateToSearch();
+    return checkIfSearchWorks();
+  });
+
+  it('check if can submit search box with pressing button', () => {
+    page.navigateToHome();
+    page.expandAndFocusSearchBox();
+    page.setCurrentQuery(queryString);
+    page.submitNavbarSearchForm();
+    browser.wait(() => {
+      return browser.getCurrentUrl().then((url: string) => {
+        return url.indexOf('query=' + encodeURI(queryString)) !== -1;
+      });
+    });
+  });
+
+  function checkIfSearchWorks(): boolean {
+    page.setCurrentQuery(queryString);
+    page.submitByPressingEnter();
+    browser.wait(() => {
+      return browser.getCurrentUrl().then((url: string) => {
+        return url.indexOf('query=' + encodeURI(queryString)) !== -1;
+      });
+    });
+    return false;
+  }
+
+});
diff --git a/e2e/search-navbar/search-navbar.po.ts b/e2e/search-navbar/search-navbar.po.ts
new file mode 100644
index 0000000000000000000000000000000000000000..17112ab468acfe23c3fd6137feac022462d671ce
--- /dev/null
+++ b/e2e/search-navbar/search-navbar.po.ts
@@ -0,0 +1,40 @@
+import { browser, element, by, protractor } from 'protractor';
+import { promise } from 'selenium-webdriver';
+
+export class ProtractorPage {
+  HOME = '/home';
+  SEARCH = '/search';
+
+  navigateToHome() {
+    return browser.get(this.HOME);
+  }
+
+  navigateToSearch() {
+    return browser.get(this.SEARCH);
+  }
+
+  getCurrentQuery(): promise.Promise<string> {
+    return element(by.css('#search-navbar-container form input')).getAttribute('value');
+  }
+
+  expandAndFocusSearchBox() {
+    element(by.css('#search-navbar-container form a')).click();
+  }
+
+  setCurrentQuery(query: string) {
+    element(by.css('#search-navbar-container form input[name="query"]')).sendKeys(query);
+  }
+
+  submitNavbarSearchForm() {
+    element(by.css('#search-navbar-container form .submit-icon')).click();
+  }
+
+  submitByPressingEnter() {
+    element(by.css('#search-navbar-container form input[name="query"]')).sendKeys(protractor.Key.ENTER);
+  }
+
+  submitByPressingEnter() {
+    element(by.css('#search-navbar-container form input[name="query"]')).sendKeys(protractor.Key.ENTER);
+  }
+
+}
diff --git a/e2e/search-page/search-page.po.ts b/e2e/search-page/search-page.po.ts
index fde3e68bf840bed4bc9cff1116cbfad39cf06abe..51bf86453b1bc5bd2283b5824635f17caed4d57a 100644
--- a/e2e/search-page/search-page.po.ts
+++ b/e2e/search-page/search-page.po.ts
@@ -1,4 +1,4 @@
-import { browser, element, by, protractor } from 'protractor';
+import { browser, by, element, protractor } from 'protractor';
 import { promise } from 'selenium-webdriver';
 
 export class ProtractorPage {
@@ -27,15 +27,15 @@ export class ProtractorPage {
   }
 
   setCurrentScope(scope: string) {
-    element(by.css('option[value="' + scope + '"]')).click();
+    element(by.css('#search-form option[value="' + scope + '"]')).click();
   }
 
   setCurrentQuery(query: string) {
-    element(by.css('input[name="query"]')).sendKeys(query);
+    element(by.css('#search-form input[name="query"]')).sendKeys(query);
   }
 
   submitSearchForm() {
-    element(by.css('button.search-button')).click();
+    element(by.css('#search-form button.search-button')).click();
   }
 
   getRandomScopeOption(): promise.Promise<string> {
diff --git a/protractor.conf.js b/protractor.conf.js
index 2949702a0ac5490098a506d9ae77f30dfc9a0eff..6570c9f7c36aea7f263984ceee77ecfe853422e9 100644
--- a/protractor.conf.js
+++ b/protractor.conf.js
@@ -5,7 +5,7 @@
 var SpecReporter = require('jasmine-spec-reporter').SpecReporter;
 
 exports.config = {
-  allScriptsTimeout: 11000,
+  allScriptsTimeout: 600000,
   // -----------------------------------------------------------------
   // Uncomment to run tests using a remote Selenium server
   //seleniumAddress: 'http://selenium.address:4444/wd/hub',
@@ -73,7 +73,7 @@ exports.config = {
   framework: 'jasmine',
   jasmineNodeOpts: {
     showColors: true,
-    defaultTimeoutInterval: 30000,
+    defaultTimeoutInterval: 600000,
     print: function () {}
   },
   useAllAngular2AppRoots: true,
diff --git a/resources/i18n/en.json5 b/resources/i18n/en.json5
index 16b4fa1dd91db7666d27a695aa7f123dcaba1c21..34d7daafac930dd8d7fe4af873909e783d0326a9 100644
--- a/resources/i18n/en.json5
+++ b/resources/i18n/en.json5
@@ -338,8 +338,40 @@
 
   "collection.edit.tabs.roles.title": "Collection Edit - Roles",
 
+  "collection.edit.tabs.source.external": "This collection harvests its content from an external source",
+
+  "collection.edit.tabs.source.form.errors.oaiSource.required": "You must provide a set id of the target collection.",
+
+  "collection.edit.tabs.source.form.harvestType": "Content being harvested",
+
+  "collection.edit.tabs.source.form.head": "Configure an external source",
+
+  "collection.edit.tabs.source.form.metadataConfigId": "Metadata Format",
+
+  "collection.edit.tabs.source.form.oaiSetId": "OAI specific set id",
+
+  "collection.edit.tabs.source.form.oaiSource": "OAI Provider",
+
+  "collection.edit.tabs.source.form.options.harvestType.METADATA_AND_BITSTREAMS": "Harvest metadata and bitstreams (requires ORE support)",
+
+  "collection.edit.tabs.source.form.options.harvestType.METADATA_AND_REF": "Harvest metadata and references to bitstreams (requires ORE support)",
+
+  "collection.edit.tabs.source.form.options.harvestType.METADATA_ONLY": "Harvest metadata only",
+
   "collection.edit.tabs.source.head": "Content Source",
 
+  "collection.edit.tabs.source.notifications.discarded.content": "Your changes were discarded. To reinstate your changes click the 'Undo' button",
+
+  "collection.edit.tabs.source.notifications.discarded.title": "Changed discarded",
+
+  "collection.edit.tabs.source.notifications.invalid.content": "Your changes were not saved. Please make sure all fields are valid before you save.",
+
+  "collection.edit.tabs.source.notifications.invalid.title": "Metadata invalid",
+
+  "collection.edit.tabs.source.notifications.saved.content": "Your changes to this collection's content source were saved.",
+
+  "collection.edit.tabs.source.notifications.saved.title": "Content Source saved",
+
   "collection.edit.tabs.source.title": "Collection Edit - Content Source",
 
 
@@ -382,6 +414,12 @@
 
 
 
+  "collection.source.update.notifications.error.content": "The provided settings have been tested and didn't work.",
+
+  "collection.source.update.notifications.error.title": "Server Error",
+
+
+
   "communityList.tabTitle": "DSpace - Community List",
 
   "communityList.title": "List of Communities",
@@ -997,6 +1035,8 @@
 
   "loading.collections": "Loading collections...",
 
+  "loading.content-source": "Loading content source...",
+
   "loading.community": "Loading community...",
 
   "loading.default": "Loading...",
@@ -1566,6 +1606,8 @@
 
   "search.results.no-results-link": "quotes around it",
 
+  "search.results.empty": "Your search returned no results.",
+
 
 
   "search.sidebar.close": "Back to results",
@@ -1621,8 +1663,67 @@
   "submission.general.save-later": "Save for later",
 
 
+
   "submission.sections.describe.relationship-lookup.close": "Close",
 
+  "submission.sections.describe.relationship-lookup.external-source.added": "Successfully added local entry to the selection",
+
+  "submission.sections.describe.relationship-lookup.external-source.import-button-title.Author": "Import remote author",
+
+  "submission.sections.describe.relationship-lookup.external-source.import-button-title.Journal": "Import remote journal",
+
+  "submission.sections.describe.relationship-lookup.external-source.import-button-title.Journal Issue": "Import remote journal issue",
+
+  "submission.sections.describe.relationship-lookup.external-source.import-button-title.Journal Volume": "Import remote journal volume",
+
+  "submission.sections.describe.relationship-lookup.external-source.import-modal.Author.title": "Import Remote Author",
+
+  "submission.sections.describe.relationship-lookup.external-source.import-modal.Author.added.local-entity": "Successfully added local author to the selection",
+
+  "submission.sections.describe.relationship-lookup.external-source.import-modal.Author.added.new-entity": "Successfully imported and added external author to the selection",
+
+  "submission.sections.describe.relationship-lookup.external-source.import-modal.authority": "Authority",
+
+  "submission.sections.describe.relationship-lookup.external-source.import-modal.authority.new": "Import as a new local authority entry",
+
+  "submission.sections.describe.relationship-lookup.external-source.import-modal.cancel": "Cancel",
+
+  "submission.sections.describe.relationship-lookup.external-source.import-modal.collection": "Select a collection to import new entries to",
+
+  "submission.sections.describe.relationship-lookup.external-source.import-modal.entities": "Entities",
+
+  "submission.sections.describe.relationship-lookup.external-source.import-modal.entities.new": "Import as a new local entity",
+
+  "submission.sections.describe.relationship-lookup.external-source.import-modal.head.lcname": "Importing from LC Name",
+
+  "submission.sections.describe.relationship-lookup.external-source.import-modal.head.orcidV2": "Importing from ORCID",
+
+  "submission.sections.describe.relationship-lookup.external-source.import-modal.head.sherpaJournal": "Importing from Sherpa Journal",
+
+  "submission.sections.describe.relationship-lookup.external-source.import-modal.head.sherpaPublisher": "Importing from Sherpa Publisher",
+
+  "submission.sections.describe.relationship-lookup.external-source.import-modal.import": "Import",
+
+  "submission.sections.describe.relationship-lookup.external-source.import-modal.Journal.title": "Import Remote Journal",
+
+  "submission.sections.describe.relationship-lookup.external-source.import-modal.Journal.added.local-entity": "Successfully added local journal to the selection",
+
+  "submission.sections.describe.relationship-lookup.external-source.import-modal.Journal.added.new-entity": "Successfully imported and added external journal to the selection",
+
+  "submission.sections.describe.relationship-lookup.external-source.import-modal.Journal Issue.title": "Import Remote Journal Issue",
+
+  "submission.sections.describe.relationship-lookup.external-source.import-modal.Journal Issue.added.local-entity": "Successfully added local journal issue to the selection",
+
+  "submission.sections.describe.relationship-lookup.external-source.import-modal.Journal Issue.added.new-entity": "Successfully imported and added external journal issue to the selection",
+
+  "submission.sections.describe.relationship-lookup.external-source.import-modal.Journal Volume.title": "Import Remote Journal Volume",
+
+  "submission.sections.describe.relationship-lookup.external-source.import-modal.Journal Volume.added.local-entity": "Successfully added local journal volume to the selection",
+
+  "submission.sections.describe.relationship-lookup.external-source.import-modal.Journal Volume.added.new-entity": "Successfully imported and added external journal volume to the selection",
+
+  "submission.sections.describe.relationship-lookup.external-source.import-modal.select": "Select a local match:",
+
   "submission.sections.describe.relationship-lookup.search-tab.deselect-all": "Deselect all",
 
   "submission.sections.describe.relationship-lookup.search-tab.deselect-page": "Deselect page",
@@ -1639,13 +1740,21 @@
 
   "submission.sections.describe.relationship-lookup.selected": "Selected {{ size }} items",
 
-  "submission.sections.describe.relationship-lookup.search-tab.tab-title.Author": "Search for Authors",
+  "submission.sections.describe.relationship-lookup.search-tab.tab-title.Author": "Local Authors ({{ count }})",
+
+  "submission.sections.describe.relationship-lookup.search-tab.tab-title.Journal": "Local Journals ({{ count }})",
 
-  "submission.sections.describe.relationship-lookup.search-tab.tab-title.Journal": "Search for Journals",
+  "submission.sections.describe.relationship-lookup.search-tab.tab-title.Journal Issue": "Local Journal Issues ({{ count }})",
 
-  "submission.sections.describe.relationship-lookup.search-tab.tab-title.Journal Issue": "Search for Journal Issues",
+  "submission.sections.describe.relationship-lookup.search-tab.tab-title.Journal Volume": "Local Journal Volumes ({{ count }})",
 
-  "submission.sections.describe.relationship-lookup.search-tab.tab-title.Journal Volume": "Search for Journal Volumes",
+  "submission.sections.describe.relationship-lookup.search-tab.tab-title.sherpaJournal": "Sherpa Journals ({{ count }})",
+
+  "submission.sections.describe.relationship-lookup.search-tab.tab-title.sherpaPublisher": "Sherpa Publishers ({{ count }})",
+
+  "submission.sections.describe.relationship-lookup.search-tab.tab-title.orcidV2": "ORCID ({{ count }})",
+
+  "submission.sections.describe.relationship-lookup.search-tab.tab-title.lcname": "LC Names ({{ count }})",
 
   "submission.sections.describe.relationship-lookup.search-tab.tab-title.Funding Agency": "Search for Funding Agencies",
 
@@ -1679,6 +1788,14 @@
 
   "submission.sections.describe.relationship-lookup.selection-tab.title.Journal Issue": "Selected Issue",
 
+  "submission.sections.describe.relationship-lookup.selection-tab.title.sherpaJournal": "Search Results",
+
+  "submission.sections.describe.relationship-lookup.selection-tab.title.sherpaPublisher": "Search Results",
+
+  "submission.sections.describe.relationship-lookup.selection-tab.title.orcidV2": "Search Results",
+
+  "submission.sections.describe.relationship-lookup.selection-tab.title.lcname": "Search Results",
+
   "submission.sections.describe.relationship-lookup.name-variant.notification.content": "Would you like to save \"{{ value }}\" as a name variant for this person so you and others can reuse it for future submissions? If you don\'t you can still use it for this submission.",
 
   "submission.sections.describe.relationship-lookup.name-variant.notification.confirm": "Save a new name variant",
diff --git a/src/app/+admin/admin-sidebar/admin-sidebar.component.ts b/src/app/+admin/admin-sidebar/admin-sidebar.component.ts
index 185d083764cc2f80d8879b858566972d686ccdd0..72eb306bf16e02d9c12117cce98f2789e4029a6c 100644
--- a/src/app/+admin/admin-sidebar/admin-sidebar.component.ts
+++ b/src/app/+admin/admin-sidebar/admin-sidebar.component.ts
@@ -17,6 +17,7 @@ import { CreateCollectionParentSelectorComponent } from '../../shared/dso-select
 import { EditItemSelectorComponent } from '../../shared/dso-selector/modal-wrappers/edit-item-selector/edit-item-selector.component';
 import { EditCommunitySelectorComponent } from '../../shared/dso-selector/modal-wrappers/edit-community-selector/edit-community-selector.component';
 import { EditCollectionSelectorComponent } from '../../shared/dso-selector/modal-wrappers/edit-collection-selector/edit-collection-selector.component';
+import {CreateItemParentSelectorComponent} from '../../shared/dso-selector/modal-wrappers/create-item-parent-selector/create-item-parent-selector.component';
 
 /**
  * Component representing the admin sidebar
@@ -137,18 +138,18 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit {
         parentID: 'new',
         active: false,
         visible: true,
-        // model: {
-        //   type: MenuItemType.ONCLICK,
-        //   text: 'menu.section.new_item',
-        //   function: () => {
-        //     this.modalService.open(CreateItemParentSelectorComponent);
-        //   }
-        // } as OnClickMenuItemModel,
         model: {
-          type: MenuItemType.LINK,
+          type: MenuItemType.ONCLICK,
           text: 'menu.section.new_item',
-          link: '/submit'
-        } as LinkMenuItemModel,
+          function: () => {
+            this.modalService.open(CreateItemParentSelectorComponent);
+          }
+        } as OnClickMenuItemModel,
+        // model: {
+        //   type: MenuItemType.LINK,
+        //   text: 'menu.section.new_item',
+        //   link: '/submit'
+        // } as LinkMenuItemModel,
       },
       {
         id: 'new_item_version',
diff --git a/src/app/+collection-page/edit-collection-page/collection-source/collection-source.component.html b/src/app/+collection-page/edit-collection-page/collection-source/collection-source.component.html
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..4192922c7e1c1c593afe772a17fce3d79af37e35 100644
--- a/src/app/+collection-page/edit-collection-page/collection-source/collection-source.component.html
+++ b/src/app/+collection-page/edit-collection-page/collection-source/collection-source.component.html
@@ -0,0 +1,56 @@
+<div class="container-fluid">
+  <div class="d-inline-block float-right">
+    <button class=" btn btn-danger" *ngIf="!(isReinstatable() | async)"
+            [disabled]="!(hasChanges() | async)"
+            (click)="discard()"><i
+      class="fas fa-times"></i>
+      <span class="d-none d-sm-inline">&nbsp;{{"item.edit.metadata.discard-button" | translate}}</span>
+    </button>
+    <button class="btn btn-warning" *ngIf="isReinstatable() | async"
+            (click)="reinstate()"><i
+      class="fas fa-undo-alt"></i>
+      <span class="d-none d-sm-inline">&nbsp;{{"item.edit.metadata.reinstate-button" | translate}}</span>
+    </button>
+    <button class="btn btn-primary" [disabled]="!(hasChanges() | async) || !isValid() || (initialHarvestType === harvestTypeNone && contentSource.harvestType === initialHarvestType)"
+            (click)="onSubmit()"><i
+      class="fas fa-save"></i>
+      <span class="d-none d-sm-inline">&nbsp;{{"item.edit.metadata.save-button" | translate}}</span>
+    </button>
+  </div>
+  <h4>{{ 'collection.edit.tabs.source.head' | translate }}</h4>
+  <div *ngIf="contentSource" class="form-check mb-4">
+    <input type="checkbox" class="form-check-input" id="externalSourceCheck" [checked]="(contentSource?.harvestType !== harvestTypeNone)" (change)="changeExternalSource()">
+    <label class="form-check-label" for="externalSourceCheck">{{ 'collection.edit.tabs.source.external' | translate }}</label>
+  </div>
+  <ds-loading *ngIf="!contentSource" [message]="'loading.content-source' | translate"></ds-loading>
+  <h4 *ngIf="contentSource && (contentSource?.harvestType !== harvestTypeNone)">{{ 'collection.edit.tabs.source.form.head' | translate }}</h4>
+</div>
+<ds-form *ngIf="formGroup && contentSource && (contentSource?.harvestType !== harvestTypeNone)"
+         [formId]="'collection-source-form-id'"
+         [formGroup]="formGroup"
+         [formModel]="formModel"
+         [formLayout]="formLayout"
+         [displaySubmit]="false"
+         (dfChange)="onChange($event)"
+         (submitForm)="onSubmit()"
+         (cancel)="onCancel()"></ds-form>
+<div class="container-fluid" *ngIf="(contentSource?.harvestType !== harvestTypeNone)">
+  <div class="d-inline-block float-right">
+    <button class=" btn btn-danger" *ngIf="!(isReinstatable() | async)"
+            [disabled]="!(hasChanges() | async)"
+            (click)="discard()"><i
+      class="fas fa-times"></i>
+      <span class="d-none d-sm-inline">&nbsp;{{"item.edit.metadata.discard-button" | translate}}</span>
+    </button>
+    <button class="btn btn-warning" *ngIf="isReinstatable() | async"
+            (click)="reinstate()"><i
+      class="fas fa-undo-alt"></i>
+      <span class="d-none d-sm-inline">&nbsp;{{"item.edit.metadata.reinstate-button" | translate}}</span>
+    </button>
+    <button class="btn btn-primary" [disabled]="!(hasChanges() | async) || !isValid() || (initialHarvestType === harvestTypeNone && contentSource.harvestType === initialHarvestType)"
+            (click)="onSubmit()"><i
+      class="fas fa-save"></i>
+      <span class="d-none d-sm-inline">&nbsp;{{"item.edit.metadata.save-button" | translate}}</span>
+    </button>
+  </div>
+</div>
diff --git a/src/app/+collection-page/edit-collection-page/collection-source/collection-source.component.spec.ts b/src/app/+collection-page/edit-collection-page/collection-source/collection-source.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..11ec9b1f6a8cda90b3baff97ce0074d71883dd5c
--- /dev/null
+++ b/src/app/+collection-page/edit-collection-page/collection-source/collection-source.component.spec.ts
@@ -0,0 +1,222 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { TranslateModule } from '@ngx-translate/core';
+import { RouterTestingModule } from '@angular/router/testing';
+import { ActivatedRoute, Router } from '@angular/router';
+import { of as observableOf } from 'rxjs/internal/observable/of';
+import { NO_ERRORS_SCHEMA } from '@angular/core';
+import { CollectionSourceComponent } from './collection-source.component';
+import { ContentSource, ContentSourceHarvestType } from '../../../core/shared/content-source.model';
+import { ObjectUpdatesService } from '../../../core/data/object-updates/object-updates.service';
+import { INotification, Notification } from '../../../shared/notifications/models/notification.model';
+import { NotificationType } from '../../../shared/notifications/models/notification-type';
+import { FieldUpdate } from '../../../core/data/object-updates/object-updates.reducer';
+import { NotificationsService } from '../../../shared/notifications/notifications.service';
+import { DynamicFormControlModel, DynamicFormService } from '@ng-dynamic-forms/core';
+import { hasValue } from '../../../shared/empty.util';
+import { FormControl, FormGroup } from '@angular/forms';
+import { RouterStub } from '../../../shared/testing/router-stub';
+import { GLOBAL_CONFIG } from '../../../../config';
+import { By } from '@angular/platform-browser';
+import { Collection } from '../../../core/shared/collection.model';
+import { RemoteData } from '../../../core/data/remote-data';
+import { CollectionDataService } from '../../../core/data/collection-data.service';
+import { RequestService } from '../../../core/data/request.service';
+
+const infoNotification: INotification = new Notification('id', NotificationType.Info, 'info');
+const warningNotification: INotification = new Notification('id', NotificationType.Warning, 'warning');
+const successNotification: INotification = new Notification('id', NotificationType.Success, 'success');
+
+const uuid = '29481ed7-ae6b-409a-8c51-34dd347a0ce4';
+let date: Date;
+let contentSource: ContentSource;
+let fieldUpdate: FieldUpdate;
+let objectUpdatesService: ObjectUpdatesService;
+let notificationsService: NotificationsService;
+let location: Location;
+let formService: DynamicFormService;
+let router: any;
+let collection: Collection;
+let collectionService: CollectionDataService;
+let requestService: RequestService;
+
+describe('CollectionSourceComponent', () => {
+  let comp: CollectionSourceComponent;
+  let fixture: ComponentFixture<CollectionSourceComponent>;
+
+  beforeEach(async(() => {
+    date = new Date();
+    contentSource = Object.assign(new ContentSource(), {
+      uuid: uuid,
+      metadataConfigs: [
+        {
+          id: 'dc',
+          label: 'Simple Dublin Core',
+          nameSpace: 'http://www.openarchives.org/OAI/2.0/oai_dc/'
+        },
+        {
+          id: 'qdc',
+          label: 'Qualified Dublin Core',
+          nameSpace: 'http://purl.org/dc/terms/'
+        },
+        {
+          id: 'dim',
+          label: 'DSpace Intermediate Metadata',
+          nameSpace: 'http://www.dspace.org/xmlns/dspace/dim'
+        }
+      ]
+    });
+    fieldUpdate = {
+      field: contentSource,
+      changeType: undefined
+    };
+    objectUpdatesService = jasmine.createSpyObj('objectUpdatesService',
+      {
+        getFieldUpdates: observableOf({
+          [contentSource.uuid]: fieldUpdate
+        }),
+        saveAddFieldUpdate: {},
+        discardFieldUpdates: {},
+        reinstateFieldUpdates: observableOf(true),
+        initialize: {},
+        getUpdatedFields: observableOf([contentSource]),
+        getLastModified: observableOf(date),
+        hasUpdates: observableOf(true),
+        isReinstatable: observableOf(false),
+        isValidPage: observableOf(true)
+      }
+    );
+    notificationsService = jasmine.createSpyObj('notificationsService',
+      {
+        info: infoNotification,
+        warning: warningNotification,
+        success: successNotification
+      }
+    );
+    location = jasmine.createSpyObj('location', ['back']);
+    formService = Object.assign({
+      createFormGroup: (fModel: DynamicFormControlModel[]) => {
+        const controls = {};
+        if (hasValue(fModel)) {
+          fModel.forEach((controlModel) => {
+            controls[controlModel.id] = new FormControl((controlModel as any).value);
+          });
+          return new FormGroup(controls);
+        }
+        return undefined;
+      }
+    });
+    router = Object.assign(new RouterStub(), {
+      url: 'http://test-url.com/test-url'
+    });
+    collection = Object.assign(new Collection(), {
+      uuid: 'fake-collection-id'
+    });
+    collectionService = jasmine.createSpyObj('collectionService', {
+      getContentSource: observableOf(contentSource),
+      updateContentSource: observableOf(contentSource),
+      getHarvesterEndpoint: observableOf('harvester-endpoint')
+    });
+    requestService = jasmine.createSpyObj('requestService', ['removeByHrefSubstring']);
+
+    TestBed.configureTestingModule({
+      imports: [TranslateModule.forRoot(), RouterTestingModule],
+      declarations: [CollectionSourceComponent],
+      providers: [
+        { provide: ObjectUpdatesService, useValue: objectUpdatesService },
+        { provide: NotificationsService, useValue: notificationsService },
+        { provide: Location, useValue: location },
+        { provide: DynamicFormService, useValue: formService },
+        { provide: ActivatedRoute, useValue: { parent: { data: observableOf({ dso: new RemoteData(false, false, true, null, collection) }) } } },
+        { provide: Router, useValue: router },
+        { provide: GLOBAL_CONFIG, useValue: { collection: { edit: { undoTimeout: 10 } } } as any },
+        { provide: CollectionDataService, useValue: collectionService },
+        { provide: RequestService, useValue: requestService }
+      ],
+      schemas: [NO_ERRORS_SCHEMA]
+    }).compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(CollectionSourceComponent);
+    comp = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  describe('on startup', () => {
+    let form;
+
+    beforeEach(() => {
+      form = fixture.debugElement.query(By.css('ds-form'));
+    });
+
+    it('ContentSource should be disabled', () => {
+      expect(comp.contentSource.harvestType).toEqual(ContentSourceHarvestType.None);
+    });
+
+    it('the input-form should be hidden', () => {
+      expect(form).toBeNull();
+    });
+  });
+
+  describe('when selecting the checkbox', () => {
+    let input;
+    let form;
+
+    beforeEach(() => {
+      input = fixture.debugElement.query(By.css('#externalSourceCheck')).nativeElement;
+      input.click();
+      fixture.detectChanges();
+      form = fixture.debugElement.query(By.css('ds-form'));
+    });
+
+    it('should enable ContentSource', () => {
+      expect(comp.contentSource.harvestType).not.toEqual(ContentSourceHarvestType.None);
+    });
+
+    it('should send a field update', () => {
+      expect(objectUpdatesService.saveAddFieldUpdate).toHaveBeenCalledWith(router.url, comp.contentSource)
+    });
+
+    it('should display the form', () => {
+      expect(form).not.toBeNull();
+    });
+  });
+
+  describe('isValid', () => {
+    it('should return true when ContentSource is disabled but the form invalid', () => {
+      spyOnProperty(comp.formGroup, 'valid').and.returnValue(false);
+      comp.contentSource.harvestType = ContentSourceHarvestType.None;
+      expect(comp.isValid()).toBe(true);
+    });
+
+    it('should return false when ContentSource is enabled but the form is invalid', () => {
+      spyOnProperty(comp.formGroup, 'valid').and.returnValue(false);
+      comp.contentSource.harvestType = ContentSourceHarvestType.Metadata;
+      expect(comp.isValid()).toBe(false);
+    });
+
+    it('should return true when ContentSource is enabled and the form is valid', () => {
+      spyOnProperty(comp.formGroup, 'valid').and.returnValue(true);
+      comp.contentSource.harvestType = ContentSourceHarvestType.Metadata;
+      expect(comp.isValid()).toBe(true);
+    });
+  });
+
+  describe('onSubmit', () => {
+    beforeEach(() => {
+      comp.onSubmit();
+    });
+
+    it('should re-initialize the field updates', () => {
+      expect(objectUpdatesService.initialize).toHaveBeenCalled();
+    });
+
+    it('should display a success notification', () => {
+      expect(notificationsService.success).toHaveBeenCalled();
+    });
+
+    it('should call updateContentSource on the collectionService', () => {
+      expect(collectionService.updateContentSource).toHaveBeenCalled();
+    });
+  });
+});
diff --git a/src/app/+collection-page/edit-collection-page/collection-source/collection-source.component.ts b/src/app/+collection-page/edit-collection-page/collection-source/collection-source.component.ts
index 6ec5be884d68d03de274b017baaeded26c6c0a21..5fcc74066354a550692c74b578cccee7684f4e81 100644
--- a/src/app/+collection-page/edit-collection-page/collection-source/collection-source.component.ts
+++ b/src/app/+collection-page/edit-collection-page/collection-source/collection-source.component.ts
@@ -1,4 +1,37 @@
-import { Component } from '@angular/core';
+import { Component, Inject, OnDestroy, OnInit } from '@angular/core';
+import { AbstractTrackableComponent } from '../../../shared/trackable/abstract-trackable.component';
+import {
+  DynamicFormControlModel,
+  DynamicFormGroupModel,
+  DynamicFormLayout,
+  DynamicFormService,
+  DynamicInputModel,
+  DynamicOptionControlModel,
+  DynamicRadioGroupModel,
+  DynamicSelectModel,
+  DynamicTextAreaModel
+} from '@ng-dynamic-forms/core';
+import { Location } from '@angular/common';
+import { TranslateService } from '@ngx-translate/core';
+import { ObjectUpdatesService } from '../../../core/data/object-updates/object-updates.service';
+import { NotificationsService } from '../../../shared/notifications/notifications.service';
+import { FormGroup } from '@angular/forms';
+import { hasNoValue, hasValue, isNotEmpty } from '../../../shared/empty.util';
+import { ContentSource, ContentSourceHarvestType } from '../../../core/shared/content-source.model';
+import { Observable } from 'rxjs/internal/Observable';
+import { RemoteData } from '../../../core/data/remote-data';
+import { Collection } from '../../../core/shared/collection.model';
+import { first, map, switchMap, take } from 'rxjs/operators';
+import { ActivatedRoute, Router } from '@angular/router';
+import { FieldUpdate, FieldUpdates } from '../../../core/data/object-updates/object-updates.reducer';
+import { Subscription } from 'rxjs/internal/Subscription';
+import { cloneDeep } from 'lodash';
+import { GLOBAL_CONFIG, GlobalConfig } from '../../../../config';
+import { CollectionDataService } from '../../../core/data/collection-data.service';
+import { getSucceededRemoteData } from '../../../core/shared/operators';
+import { MetadataConfig } from '../../../core/shared/metadata-config.model';
+import { INotification } from '../../../shared/notifications/models/notification.model';
+import { RequestService } from '../../../core/data/request.service';
 
 /**
  * Component for managing the content source of the collection
@@ -7,6 +40,440 @@ import { Component } from '@angular/core';
   selector: 'ds-collection-source',
   templateUrl: './collection-source.component.html',
 })
-export class CollectionSourceComponent {
-  /* TODO: Implement Collection Edit - Content Source */
+export class CollectionSourceComponent extends AbstractTrackableComponent implements OnInit, OnDestroy {
+  /**
+   * The current collection's remote data
+   */
+  collectionRD$: Observable<RemoteData<Collection>>;
+
+  /**
+   * The collection's content source
+   */
+  contentSource: ContentSource;
+
+  /**
+   * The current update to the content source
+   */
+  update$: Observable<FieldUpdate>;
+
+  /**
+   * The initial harvest type we started off with
+   * Used to compare changes
+   */
+  initialHarvestType: ContentSourceHarvestType;
+
+  /**
+   * @type {string} Key prefix used to generate form labels
+   */
+  LABEL_KEY_PREFIX = 'collection.edit.tabs.source.form.';
+
+  /**
+   * @type {string} Key prefix used to generate form error messages
+   */
+  ERROR_KEY_PREFIX = 'collection.edit.tabs.source.form.errors.';
+
+  /**
+   * @type {string} Key prefix used to generate form option labels
+   */
+  OPTIONS_KEY_PREFIX = 'collection.edit.tabs.source.form.options.';
+
+  /**
+   * The Dynamic Input Model for the OAI Provider
+   */
+  oaiSourceModel = new DynamicInputModel({
+    id: 'oaiSource',
+    name: 'oaiSource',
+    required: true,
+    validators: {
+      required: null
+    },
+    errorMessages: {
+      required: 'You must provide a set id of the target collection.'
+    }
+  });
+
+  /**
+   * The Dynamic Input Model for the OAI Set
+   */
+  oaiSetIdModel = new DynamicInputModel({
+    id: 'oaiSetId',
+    name: 'oaiSetId'
+  });
+
+  /**
+   * The Dynamic Input Model for the Metadata Format used
+   */
+  metadataConfigIdModel = new DynamicSelectModel({
+    id: 'metadataConfigId',
+    name: 'metadataConfigId'
+  });
+
+  /**
+   * The Dynamic Input Model for the type of harvesting
+   */
+  harvestTypeModel = new DynamicRadioGroupModel<string>({
+    id: 'harvestType',
+    name: 'harvestType',
+    options: [
+      {
+        value: ContentSourceHarvestType.Metadata
+      },
+      {
+        value: ContentSourceHarvestType.MetadataAndRef
+      },
+      {
+        value: ContentSourceHarvestType.MetadataAndBitstreams
+      }
+    ]
+  });
+
+  /**
+   * All input models in a simple array for easier iterations
+   */
+  inputModels = [this.oaiSourceModel, this.oaiSetIdModel, this.metadataConfigIdModel, this.harvestTypeModel];
+
+  /**
+   * The dynamic form fields used for editing the content source of a collection
+   * @type {(DynamicInputModel | DynamicTextAreaModel)[]}
+   */
+  formModel: DynamicFormControlModel[] = [
+    new DynamicFormGroupModel({
+      id: 'oaiSourceContainer',
+      group: [
+        this.oaiSourceModel
+      ]
+    }),
+    new DynamicFormGroupModel({
+      id: 'oaiSetContainer',
+      group: [
+        this.oaiSetIdModel,
+        this.metadataConfigIdModel
+      ]
+    }),
+    new DynamicFormGroupModel({
+      id: 'harvestTypeContainer',
+      group: [
+        this.harvestTypeModel
+      ]
+    })
+  ];
+
+  /**
+   * Layout used for structuring the form inputs
+   */
+  formLayout: DynamicFormLayout = {
+    oaiSource: {
+      grid: {
+        host: 'col-12 d-inline-block'
+      }
+    },
+    oaiSetId: {
+      grid: {
+        host: 'col col-sm-6 d-inline-block'
+      }
+    },
+    metadataConfigId: {
+      grid: {
+        host: 'col col-sm-6 d-inline-block'
+      }
+    },
+    harvestType: {
+      grid: {
+        host: 'col-12',
+        option: 'btn-outline-secondary'
+      }
+    },
+    oaiSetContainer: {
+      grid: {
+        host: 'row'
+      }
+    },
+    oaiSourceContainer: {
+      grid: {
+        host: 'row'
+      }
+    },
+    harvestTypeContainer: {
+      grid: {
+        host: 'row'
+      }
+    }
+  };
+
+  /**
+   * The form group of this form
+   */
+  formGroup: FormGroup;
+
+  /**
+   * Subscription to update the current form
+   */
+  updateSub: Subscription;
+
+  /**
+   * The content harvesting type used when harvesting is disabled
+   */
+  harvestTypeNone = ContentSourceHarvestType.None;
+
+  /**
+   * The previously selected harvesting type
+   * Used for switching between ContentSourceHarvestType.None and the previously selected value when enabling / disabling harvesting
+   * Defaults to ContentSourceHarvestType.Metadata
+   */
+  previouslySelectedHarvestType = ContentSourceHarvestType.Metadata;
+
+  /**
+   * Notifications displayed after clicking submit
+   * These are cleaned up every time a user submits the form to prevent error or other notifications from staying active
+   * while they shouldn't be.
+   */
+  displayedNotifications: INotification[] = [];
+
+  public constructor(public objectUpdatesService: ObjectUpdatesService,
+                     public notificationsService: NotificationsService,
+                     protected location: Location,
+                     protected formService: DynamicFormService,
+                     protected translate: TranslateService,
+                     protected route: ActivatedRoute,
+                     protected router: Router,
+                     @Inject(GLOBAL_CONFIG) protected EnvConfig: GlobalConfig,
+                     protected collectionService: CollectionDataService,
+                     protected requestService: RequestService) {
+    super(objectUpdatesService, notificationsService, translate);
+  }
+
+  /**
+   * Initialize properties to setup the Field Update and Form
+   */
+  ngOnInit(): void {
+    this.notificationsPrefix = 'collection.edit.tabs.source.notifications.';
+    this.discardTimeOut = this.EnvConfig.collection.edit.undoTimeout;
+    this.url = this.router.url;
+    if (this.url.indexOf('?') > 0) {
+      this.url = this.url.substr(0, this.url.indexOf('?'));
+    }
+    this.formGroup = this.formService.createFormGroup(this.formModel);
+    this.collectionRD$ = this.route.parent.data.pipe(first(), map((data) => data.dso));
+
+    this.collectionRD$.pipe(
+      getSucceededRemoteData(),
+      map((col) => col.payload.uuid),
+      switchMap((uuid) => this.collectionService.getContentSource(uuid)),
+      take(1)
+    ).subscribe((contentSource: ContentSource) => {
+      this.initializeOriginalContentSource(contentSource);
+    });
+
+    this.updateFieldTranslations();
+    this.translate.onLangChange
+      .subscribe(() => {
+        this.updateFieldTranslations();
+      });
+  }
+
+  /**
+   * Initialize the Field Update and subscribe on it to fire updates to the form whenever it changes
+   */
+  initializeOriginalContentSource(contentSource: ContentSource) {
+    this.contentSource = contentSource;
+    this.initialHarvestType = contentSource.harvestType;
+    this.initializeMetadataConfigs();
+    const initialContentSource = cloneDeep(this.contentSource);
+    this.objectUpdatesService.initialize(this.url, [initialContentSource], new Date());
+    this.update$ = this.objectUpdatesService.getFieldUpdates(this.url, [initialContentSource]).pipe(
+      map((updates: FieldUpdates) => updates[initialContentSource.uuid])
+    );
+    this.updateSub = this.update$.subscribe((update: FieldUpdate) => {
+      if (update) {
+        const field = update.field as ContentSource;
+        let configId;
+        if (hasValue(this.contentSource) && isNotEmpty(this.contentSource.metadataConfigs)) {
+          configId = this.contentSource.metadataConfigs[0].id;
+        }
+        if (hasValue(field) && hasValue(field.metadataConfigId)) {
+          configId = field.metadataConfigId;
+        }
+        if (hasValue(field)) {
+          this.formGroup.patchValue({
+            oaiSourceContainer: {
+              oaiSource: field.oaiSource
+            },
+            oaiSetContainer: {
+              oaiSetId: field.oaiSetId,
+              metadataConfigId: configId
+            },
+            harvestTypeContainer: {
+              harvestType: field.harvestType
+            }
+          });
+          this.contentSource = cloneDeep(field);
+        }
+        this.contentSource.metadataConfigId = configId;
+      }
+    });
+  }
+
+  /**
+   * Fill the metadataConfigIdModel's options using the contentSource's metadataConfigs property
+   */
+  initializeMetadataConfigs() {
+    this.metadataConfigIdModel.options = this.contentSource.metadataConfigs
+      .map((metadataConfig: MetadataConfig) => Object.assign({ value: metadataConfig.id, label: metadataConfig.label }));
+    if (this.metadataConfigIdModel.options.length > 0) {
+      this.formGroup.patchValue({
+        oaiSetContainer: {
+          metadataConfigId: this.metadataConfigIdModel.options[0].value
+        }
+      });
+    }
+  }
+
+  /**
+   * Used the update translations of errors and labels on init and on language change
+   */
+  private updateFieldTranslations() {
+    this.inputModels.forEach(
+      (fieldModel: DynamicFormControlModel) => {
+        this.updateFieldTranslation(fieldModel);
+      }
+    );
+  }
+
+  /**
+   * Update the translations of a DynamicInputModel
+   * @param fieldModel
+   */
+  private updateFieldTranslation(fieldModel: DynamicFormControlModel) {
+    fieldModel.label = this.translate.instant(this.LABEL_KEY_PREFIX + fieldModel.id);
+    if (isNotEmpty(fieldModel.validators)) {
+      fieldModel.errorMessages = {};
+      Object.keys(fieldModel.validators).forEach((key) => {
+        fieldModel.errorMessages[key] = this.translate.instant(this.ERROR_KEY_PREFIX + fieldModel.id + '.' + key);
+      });
+    }
+    if (fieldModel instanceof DynamicOptionControlModel) {
+      if (isNotEmpty(fieldModel.options)) {
+        fieldModel.options.forEach((option) => {
+          if (hasNoValue(option.label)) {
+            option.label = this.translate.instant(this.OPTIONS_KEY_PREFIX + fieldModel.id + '.' + option.value);
+          }
+        });
+      }
+    }
+  }
+
+  /**
+   * Fired whenever the form receives an update and makes sure the Content Source and field update is up-to-date with the changes
+   * @param event
+   */
+  onChange(event) {
+    this.updateContentSourceField(event.model, true);
+    this.saveFieldUpdate();
+  }
+
+  /**
+   * Submit the edited Content Source to the REST API, re-initialize the field update and display a notification
+   */
+  onSubmit() {
+    // Remove cached harvester request to allow for latest harvester to be displayed when switching tabs
+    this.collectionRD$.pipe(
+      getSucceededRemoteData(),
+      map((col) => col.payload.uuid),
+      switchMap((uuid) => this.collectionService.getHarvesterEndpoint(uuid)),
+      take(1)
+    ).subscribe((endpoint) => this.requestService.removeByHrefSubstring(endpoint));
+
+    // Update harvester
+    this.collectionRD$.pipe(
+      getSucceededRemoteData(),
+      map((col) => col.payload.uuid),
+      switchMap((uuid) => this.collectionService.updateContentSource(uuid, this.contentSource)),
+      take(1)
+    ).subscribe((result: ContentSource | INotification) => {
+      if (hasValue((result as any).harvestType)) {
+        this.clearNotifications();
+        this.initializeOriginalContentSource(result as ContentSource);
+        this.displayedNotifications.push(this.notificationsService.success(this.getNotificationTitle('saved'), this.getNotificationContent('saved')));
+      } else {
+        this.displayedNotifications.push(result as INotification);
+      }
+    });
+  }
+
+  /**
+   * Cancel the edit and return to the previous page
+   */
+  onCancel() {
+    this.location.back();
+  }
+
+  /**
+   * Is the current form valid to be submitted ?
+   */
+  isValid(): boolean {
+    return (this.contentSource.harvestType === ContentSourceHarvestType.None) || this.formGroup.valid;
+  }
+
+  /**
+   * Switch the external source on or off and fire a field update
+   */
+  changeExternalSource() {
+    if (this.contentSource.harvestType === ContentSourceHarvestType.None) {
+      this.contentSource.harvestType = this.previouslySelectedHarvestType;
+    } else {
+      this.previouslySelectedHarvestType = this.contentSource.harvestType;
+      this.contentSource.harvestType = ContentSourceHarvestType.None;
+    }
+    this.updateContentSource(false);
+  }
+
+  /**
+   * Loop over all inputs and update the Content Source with their value
+   * @param updateHarvestType   When set to false, the harvestType of the contentSource will be ignored in the update
+   */
+  updateContentSource(updateHarvestType: boolean) {
+    this.inputModels.forEach(
+      (fieldModel: DynamicInputModel) => {
+        this.updateContentSourceField(fieldModel, updateHarvestType)
+      }
+    );
+    this.saveFieldUpdate();
+  }
+
+  /**
+   * Update the Content Source with the value from a DynamicInputModel
+   * @param fieldModel          The fieldModel to fetch the value from and update the contentSource with
+   * @param updateHarvestType   When set to false, the harvestType of the contentSource will be ignored in the update
+   */
+  updateContentSourceField(fieldModel: DynamicInputModel, updateHarvestType: boolean) {
+    if (hasValue(fieldModel.value) && !(fieldModel.id === this.harvestTypeModel.id && !updateHarvestType)) {
+      this.contentSource[fieldModel.id] = fieldModel.value;
+    }
+  }
+
+  /**
+   * Save the current Content Source to the Object Updates cache
+   */
+  saveFieldUpdate() {
+    this.objectUpdatesService.saveAddFieldUpdate(this.url, cloneDeep(this.contentSource));
+  }
+
+  /**
+   * Clear possible active notifications
+   */
+  clearNotifications() {
+    this.displayedNotifications.forEach((notification: INotification) => {
+      this.notificationsService.remove(notification);
+    });
+    this.displayedNotifications = [];
+  }
+
+  /**
+   * Make sure open subscriptions are closed
+   */
+  ngOnDestroy(): void {
+    if (this.updateSub) {
+      this.updateSub.unsubscribe();
+    }
+  }
 }
diff --git a/src/app/+community-page/sub-collection-list/community-page-sub-collection-list.component.html b/src/app/+community-page/sub-collection-list/community-page-sub-collection-list.component.html
index 9156a99b184e91dfd08948b85c3450a1bb32531b..bf6ce7fd57358edbc039158be93015c97478ea57 100644
--- a/src/app/+community-page/sub-collection-list/community-page-sub-collection-list.component.html
+++ b/src/app/+community-page/sub-collection-list/community-page-sub-collection-list.component.html
@@ -1,14 +1,13 @@
 <ng-container *ngVar="(subCollectionsRDObs | async) as subCollectionsRD">
   <div *ngIf="subCollectionsRD?.hasSucceeded && subCollectionsRD?.payload.totalElements > 0" @fadeIn>
     <h2>{{'community.sub-collection-list.head' | translate}}</h2>
-    <ul>
-      <li *ngFor="let collection of subCollectionsRD?.payload.page">
-        <p>
-          <span class="lead"><a [routerLink]="['/collections', collection.id]">{{collection.name}}</a></span><br>
-          <span class="text-muted">{{collection.shortDescription}}</span>
-        </p>
-      </li>
-    </ul>
+    <ds-viewable-collection
+      [config]="config"
+      [sortConfig]="sortConfig"
+      [objects]="subCollectionsRD"
+      [hideGear]="false"
+      (paginationChange)="onPaginationChange($event)">
+    </ds-viewable-collection>
   </div>
   <ds-error *ngIf="subCollectionsRD?.hasFailed" message="{{'error.sub-collections' | translate}}"></ds-error>
   <ds-loading *ngIf="subCollectionsRD?.isLoading" message="{{'loading.sub-collections' | translate}}"></ds-loading>
diff --git a/src/app/+community-page/sub-collection-list/community-page-sub-collection-list.component.spec.ts b/src/app/+community-page/sub-collection-list/community-page-sub-collection-list.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..09332dda1694cf7c02d16ab0bf0b05cbeea733aa
--- /dev/null
+++ b/src/app/+community-page/sub-collection-list/community-page-sub-collection-list.component.spec.ts
@@ -0,0 +1,182 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { TranslateModule } from '@ngx-translate/core';
+import { NO_ERRORS_SCHEMA } from '@angular/core';
+import { By } from '@angular/platform-browser';
+import { RouterTestingModule } from '@angular/router/testing';
+import { NoopAnimationsModule } from '@angular/platform-browser/animations';
+
+import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
+
+import { CommunityPageSubCollectionListComponent } from './community-page-sub-collection-list.component';
+import { Community } from '../../core/shared/community.model';
+import { SharedModule } from '../../shared/shared.module';
+import { CollectionDataService } from '../../core/data/collection-data.service';
+import { FindListOptions } from '../../core/data/request.models';
+import { createSuccessfulRemoteDataObject$ } from '../../shared/testing/utils';
+import { PaginatedList } from '../../core/data/paginated-list';
+import { PageInfo } from '../../core/shared/page-info.model';
+import { HostWindowService } from '../../shared/host-window.service';
+import { HostWindowServiceStub } from '../../shared/testing/host-window-service-stub';
+import { SelectableListService } from '../../shared/object-list/selectable-list/selectable-list.service';
+
+describe('CommunityPageSubCollectionList Component', () => {
+  let comp: CommunityPageSubCollectionListComponent;
+  let fixture: ComponentFixture<CommunityPageSubCollectionListComponent>;
+  let collectionDataServiceStub: any;
+  let subCollList = [];
+
+  const collections = [Object.assign(new Community(), {
+    id: '123456789-1',
+    metadata: {
+      'dc.title': [
+        { language: 'en_US', value: 'Collection 1' }
+      ]
+    }
+  }),
+    Object.assign(new Community(), {
+      id: '123456789-2',
+      metadata: {
+        'dc.title': [
+          { language: 'en_US', value: 'Collection 2' }
+        ]
+      }
+    }),
+    Object.assign(new Community(), {
+      id: '123456789-3',
+      metadata: {
+        'dc.title': [
+          { language: 'en_US', value: 'Collection 3' }
+        ]
+      }
+    }),
+    Object.assign(new Community(), {
+      id: '123456789-4',
+      metadata: {
+        'dc.title': [
+          { language: 'en_US', value: 'Collection 4' }
+        ]
+      }
+    }),
+    Object.assign(new Community(), {
+      id: '123456789-5',
+      metadata: {
+        'dc.title': [
+          { language: 'en_US', value: 'Collection 5' }
+        ]
+      }
+    }),
+    Object.assign(new Community(), {
+      id: '123456789-6',
+      metadata: {
+        'dc.title': [
+          { language: 'en_US', value: 'Collection 6' }
+        ]
+      }
+    }),
+    Object.assign(new Community(), {
+      id: '123456789-7',
+      metadata: {
+        'dc.title': [
+          { language: 'en_US', value: 'Collection 7' }
+        ]
+      }
+    })
+  ];
+
+  const mockCommunity = Object.assign(new Community(), {
+    id: '123456789',
+    metadata: {
+      'dc.title': [
+        { language: 'en_US', value: 'Test title' }
+      ]
+    }
+  });
+
+  collectionDataServiceStub = {
+    findByParent(parentUUID: string, options: FindListOptions = {}) {
+      let currentPage = options.currentPage;
+      let elementsPerPage = options.elementsPerPage;
+      if (currentPage === undefined) {
+        currentPage = 1
+      }
+      elementsPerPage = 5;
+      const startPageIndex = (currentPage - 1) * elementsPerPage;
+      let endPageIndex = (currentPage * elementsPerPage);
+      if (endPageIndex > subCollList.length) {
+        endPageIndex = subCollList.length;
+      }
+      return createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), subCollList.slice(startPageIndex, endPageIndex)));
+
+    }
+  };
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [
+        TranslateModule.forRoot(),
+        SharedModule,
+        RouterTestingModule.withRoutes([]),
+        NgbModule.forRoot(),
+        NoopAnimationsModule
+      ],
+      declarations: [CommunityPageSubCollectionListComponent],
+      providers: [
+        { provide: CollectionDataService, useValue: collectionDataServiceStub },
+        { provide: HostWindowService, useValue: new HostWindowServiceStub(0) },
+        { provide: SelectableListService, useValue: {} },
+      ],
+      schemas: [NO_ERRORS_SCHEMA]
+    }).compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(CommunityPageSubCollectionListComponent);
+    comp = fixture.componentInstance;
+    comp.community = mockCommunity;
+  });
+
+  it('should display a list of collections', () => {
+    subCollList = collections;
+    fixture.detectChanges();
+
+    const collList = fixture.debugElement.queryAll(By.css('li'));
+    expect(collList.length).toEqual(5);
+    expect(collList[0].nativeElement.textContent).toContain('Collection 1');
+    expect(collList[1].nativeElement.textContent).toContain('Collection 2');
+    expect(collList[2].nativeElement.textContent).toContain('Collection 3');
+    expect(collList[3].nativeElement.textContent).toContain('Collection 4');
+    expect(collList[4].nativeElement.textContent).toContain('Collection 5');
+  });
+
+  it('should not display the header when list of collections is empty', () => {
+    subCollList = [];
+    fixture.detectChanges();
+
+    const subComHead = fixture.debugElement.queryAll(By.css('h2'));
+    expect(subComHead.length).toEqual(0);
+  });
+
+  it('should update list of collections on pagination change', () => {
+    subCollList = collections;
+    fixture.detectChanges();
+
+    const pagination = Object.create({
+      pagination:{
+        id: comp.pageId,
+        currentPage: 2,
+        pageSize: 5
+      },
+      sort: {
+        field: 'dc.title',
+        direction: 'ASC'
+      }
+    });
+    comp.onPaginationChange(pagination);
+    fixture.detectChanges();
+
+    const collList = fixture.debugElement.queryAll(By.css('li'));
+    expect(collList.length).toEqual(2);
+    expect(collList[0].nativeElement.textContent).toContain('Collection 6');
+    expect(collList[1].nativeElement.textContent).toContain('Collection 7');
+  });
+});
diff --git a/src/app/+community-page/sub-collection-list/community-page-sub-collection-list.component.ts b/src/app/+community-page/sub-collection-list/community-page-sub-collection-list.component.ts
index b8a5d60002f126272633ec3365f209425af26496..64c274444e2959f688d44dde5b1e9b4920df994f 100644
--- a/src/app/+community-page/sub-collection-list/community-page-sub-collection-list.component.ts
+++ b/src/app/+community-page/sub-collection-list/community-page-sub-collection-list.component.ts
@@ -1,12 +1,16 @@
 import { Component, Input, OnInit } from '@angular/core';
-import { Observable } from 'rxjs';
+
+import { BehaviorSubject } from 'rxjs';
+import { take } from 'rxjs/operators';
 
 import { RemoteData } from '../../core/data/remote-data';
 import { Collection } from '../../core/shared/collection.model';
 import { Community } from '../../core/shared/community.model';
-
 import { fadeIn } from '../../shared/animations/fade';
 import { PaginatedList } from '../../core/data/paginated-list';
+import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
+import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model';
+import { CollectionDataService } from '../../core/data/collection-data.service';
 
 @Component({
   selector: 'ds-community-page-sub-collection-list',
@@ -16,9 +20,60 @@ import { PaginatedList } from '../../core/data/paginated-list';
 })
 export class CommunityPageSubCollectionListComponent implements OnInit {
   @Input() community: Community;
-  subCollectionsRDObs: Observable<RemoteData<PaginatedList<Collection>>>;
+
+  /**
+   * The pagination configuration
+   */
+  config: PaginationComponentOptions;
+
+  /**
+   * The pagination id
+   */
+  pageId = 'community-collections-pagination';
+
+  /**
+   * The sorting configuration
+   */
+  sortConfig: SortOptions;
+
+  /**
+   * A list of remote data objects of communities' collections
+   */
+  subCollectionsRDObs: BehaviorSubject<RemoteData<PaginatedList<Collection>>> = new BehaviorSubject<RemoteData<PaginatedList<Collection>>>({} as any);
+
+  constructor(private cds: CollectionDataService) {}
 
   ngOnInit(): void {
-    this.subCollectionsRDObs = this.community.collections;
+    this.config = new PaginationComponentOptions();
+    this.config.id = this.pageId;
+    this.config.pageSize = 5;
+    this.config.currentPage = 1;
+    this.sortConfig = new SortOptions('dc.title', SortDirection.ASC);
+    this.updatePage();
+  }
+
+  /**
+   * Called when one of the pagination settings is changed
+   * @param event The new pagination data
+   */
+  onPaginationChange(event) {
+    this.config.currentPage = event.pagination.currentPage;
+    this.config.pageSize = event.pagination.pageSize;
+    this.sortConfig.field = event.sort.field;
+    this.sortConfig.direction = event.sort.direction;
+    this.updatePage();
+  }
+
+  /**
+   * Update the list of collections
+   */
+  updatePage() {
+    this.cds.findByParent(this.community.id,{
+      currentPage: this.config.currentPage,
+      elementsPerPage: this.config.pageSize,
+      sort: { field: this.sortConfig.field, direction: this.sortConfig.direction }
+    }).pipe(take(1)).subscribe((results) => {
+      this.subCollectionsRDObs.next(results);
+    });
   }
 }
diff --git a/src/app/+community-page/sub-community-list/community-page-sub-community-list.component.html b/src/app/+community-page/sub-community-list/community-page-sub-community-list.component.html
index 6cd62ba48d3c0ccd25719ab0235dcf846f9a3729..880ea9cc8ed5d3132f4bb6b0330f8615214b6fce 100644
--- a/src/app/+community-page/sub-community-list/community-page-sub-community-list.component.html
+++ b/src/app/+community-page/sub-community-list/community-page-sub-community-list.component.html
@@ -1,14 +1,13 @@
 <ng-container *ngVar="(subCommunitiesRDObs | async) as subCommunitiesRD">
   <div *ngIf="subCommunitiesRD?.hasSucceeded && subCommunitiesRD?.payload.totalElements > 0" @fadeIn>
     <h2>{{'community.sub-community-list.head' | translate}}</h2>
-    <ul>
-      <li *ngFor="let community of subCommunitiesRD?.payload.page">
-        <p>
-          <span class="lead"><a [routerLink]="['/communities', community.id]">{{community.name}}</a></span><br>
-          <span class="text-muted">{{community.shortDescription}}</span>
-        </p>
-      </li>
-    </ul>
+    <ds-viewable-collection
+      [config]="config"
+      [sortConfig]="sortConfig"
+      [objects]="subCommunitiesRD"
+      [hideGear]="false"
+      (paginationChange)="onPaginationChange($event)">
+    </ds-viewable-collection>
   </div>
   <ds-error *ngIf="subCommunitiesRD?.hasFailed" message="{{'error.sub-communities' | translate}}"></ds-error>
   <ds-loading *ngIf="subCommunitiesRD?.isLoading" message="{{'loading.sub-communities' | translate}}"></ds-loading>
diff --git a/src/app/+community-page/sub-community-list/community-page-sub-community-list.component.spec.ts b/src/app/+community-page/sub-community-list/community-page-sub-community-list.component.spec.ts
index 2feaa3afa6286e36f53a735fdd220a8e440cc01c..41502e7bd44d52d1f59d602a5aac4607d9f90278 100644
--- a/src/app/+community-page/sub-community-list/community-page-sub-community-list.component.spec.ts
+++ b/src/app/+community-page/sub-community-list/community-page-sub-community-list.component.spec.ts
@@ -1,21 +1,29 @@
-import {async, ComponentFixture, TestBed} from '@angular/core/testing';
-import {TranslateModule} from '@ngx-translate/core';
-import {NO_ERRORS_SCHEMA} from '@angular/core';
-import {CommunityPageSubCommunityListComponent} from './community-page-sub-community-list.component';
-import {Community} from '../../core/shared/community.model';
-import {RemoteData} from '../../core/data/remote-data';
-import {PaginatedList} from '../../core/data/paginated-list';
-import {PageInfo} from '../../core/shared/page-info.model';
-import {SharedModule} from '../../shared/shared.module';
-import {RouterTestingModule} from '@angular/router/testing';
-import {NoopAnimationsModule} from '@angular/platform-browser/animations';
-import {By} from '@angular/platform-browser';
-import {of as observableOf,  Observable } from 'rxjs';
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { TranslateModule } from '@ngx-translate/core';
+import { NO_ERRORS_SCHEMA } from '@angular/core';
+import { RouterTestingModule } from '@angular/router/testing';
+import { NoopAnimationsModule } from '@angular/platform-browser/animations';
+import { By } from '@angular/platform-browser';
+
+import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
+
+import { CommunityPageSubCommunityListComponent } from './community-page-sub-community-list.component';
+import { Community } from '../../core/shared/community.model';
+import { PaginatedList } from '../../core/data/paginated-list';
+import { PageInfo } from '../../core/shared/page-info.model';
+import { SharedModule } from '../../shared/shared.module';
 import { createSuccessfulRemoteDataObject$ } from '../../shared/testing/utils';
+import { FindListOptions } from '../../core/data/request.models';
+import { HostWindowService } from '../../shared/host-window.service';
+import { HostWindowServiceStub } from '../../shared/testing/host-window-service-stub';
+import { CommunityDataService } from '../../core/data/community-data.service';
+import { SelectableListService } from '../../shared/object-list/selectable-list/selectable-list.service';
 
-describe('SubCommunityList Component', () => {
+describe('CommunityPageSubCommunityListComponent Component', () => {
   let comp: CommunityPageSubCommunityListComponent;
   let fixture: ComponentFixture<CommunityPageSubCommunityListComponent>;
+  let communityDataServiceStub: any;
+  let subCommList = [];
 
   const subcommunities = [Object.assign(new Community(), {
     id: '123456789-1',
@@ -32,34 +40,92 @@ describe('SubCommunityList Component', () => {
           { language: 'en_US', value: 'SubCommunity 2' }
         ]
       }
+    }),
+    Object.assign(new Community(), {
+      id: '123456789-3',
+      metadata: {
+        'dc.title': [
+          { language: 'en_US', value: 'SubCommunity 3' }
+        ]
+      }
+    }),
+    Object.assign(new Community(), {
+      id: '12345678942',
+      metadata: {
+        'dc.title': [
+          { language: 'en_US', value: 'SubCommunity 4' }
+        ]
+      }
+    }),
+    Object.assign(new Community(), {
+      id: '123456789-5',
+      metadata: {
+        'dc.title': [
+          { language: 'en_US', value: 'SubCommunity 5' }
+        ]
+      }
+    }),
+    Object.assign(new Community(), {
+      id: '123456789-6',
+      metadata: {
+        'dc.title': [
+          { language: 'en_US', value: 'SubCommunity 6' }
+        ]
+      }
+    }),
+    Object.assign(new Community(), {
+      id: '123456789-7',
+      metadata: {
+        'dc.title': [
+          { language: 'en_US', value: 'SubCommunity 7' }
+        ]
+      }
     })
   ];
 
-  const emptySubCommunitiesCommunity = Object.assign(new Community(), {
+  const mockCommunity = Object.assign(new Community(), {
+    id: '123456789',
     metadata: {
       'dc.title': [
         { language: 'en_US', value: 'Test title' }
       ]
-    },
-    subcommunities: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), []))
+    }
   });
 
-  const mockCommunity = Object.assign(new Community(), {
-      metadata: {
-        'dc.title': [
-          { language: 'en_US', value: 'Test title' }
-        ]
-      },
-      subcommunities: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), subcommunities))
-    })
-  ;
+  communityDataServiceStub = {
+    findByParent(parentUUID: string, options: FindListOptions = {}) {
+      let currentPage = options.currentPage;
+      let elementsPerPage = options.elementsPerPage;
+      if (currentPage === undefined) {
+        currentPage = 1
+      }
+      elementsPerPage = 5;
+
+      const startPageIndex = (currentPage - 1) * elementsPerPage;
+      let endPageIndex = (currentPage * elementsPerPage);
+      if (endPageIndex > subCommList.length) {
+        endPageIndex = subCommList.length;
+      }
+      return createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), subCommList.slice(startPageIndex, endPageIndex)));
+
+    }
+  };
 
   beforeEach(async(() => {
     TestBed.configureTestingModule({
-      imports: [TranslateModule.forRoot(), SharedModule,
+      imports: [
+        TranslateModule.forRoot(),
+        SharedModule,
         RouterTestingModule.withRoutes([]),
-        NoopAnimationsModule],
+        NgbModule.forRoot(),
+        NoopAnimationsModule
+      ],
       declarations: [CommunityPageSubCommunityListComponent],
+      providers: [
+        { provide: CommunityDataService, useValue: communityDataServiceStub },
+        { provide: HostWindowService, useValue: new HostWindowServiceStub(0) },
+        { provide: SelectableListService, useValue: {} },
+      ],
       schemas: [NO_ERRORS_SCHEMA]
     }).compileComponents();
   }));
@@ -67,23 +133,52 @@ describe('SubCommunityList Component', () => {
   beforeEach(() => {
     fixture = TestBed.createComponent(CommunityPageSubCommunityListComponent);
     comp = fixture.componentInstance;
+    comp.community = mockCommunity;
+
   });
 
-  it('should display a list of subCommunities', () => {
-    comp.community = mockCommunity;
+  it('should display a list of sub-communities', () => {
+    subCommList = subcommunities;
     fixture.detectChanges();
 
     const subComList = fixture.debugElement.queryAll(By.css('li'));
-    expect(subComList.length).toEqual(2);
+    expect(subComList.length).toEqual(5);
     expect(subComList[0].nativeElement.textContent).toContain('SubCommunity 1');
     expect(subComList[1].nativeElement.textContent).toContain('SubCommunity 2');
+    expect(subComList[2].nativeElement.textContent).toContain('SubCommunity 3');
+    expect(subComList[3].nativeElement.textContent).toContain('SubCommunity 4');
+    expect(subComList[4].nativeElement.textContent).toContain('SubCommunity 5');
   });
 
-  it('should not display the header when subCommunities are empty', () => {
-    comp.community = emptySubCommunitiesCommunity;
+  it('should not display the header when list of sub-communities is empty', () => {
+    subCommList = [];
     fixture.detectChanges();
 
     const subComHead = fixture.debugElement.queryAll(By.css('h2'));
     expect(subComHead.length).toEqual(0);
   });
+
+  it('should update list of sub-communities on pagination change', () => {
+    subCommList = subcommunities;
+    fixture.detectChanges();
+
+    const pagination = Object.create({
+      pagination:{
+        id: comp.pageId,
+        currentPage: 2,
+        pageSize: 5
+      },
+      sort: {
+        field: 'dc.title',
+        direction: 'ASC'
+      }
+    });
+    comp.onPaginationChange(pagination);
+    fixture.detectChanges();
+
+    const collList = fixture.debugElement.queryAll(By.css('li'));
+    expect(collList.length).toEqual(2);
+    expect(collList[0].nativeElement.textContent).toContain('SubCommunity 6');
+    expect(collList[1].nativeElement.textContent).toContain('SubCommunity 7');
+  });
 });
diff --git a/src/app/+community-page/sub-community-list/community-page-sub-community-list.component.ts b/src/app/+community-page/sub-community-list/community-page-sub-community-list.component.ts
index 91f6d7bac1ace771eebf7c1fb13b36766394267c..1bd664021ee2db32b9b6562775d856722a4f9047 100644
--- a/src/app/+community-page/sub-community-list/community-page-sub-community-list.component.ts
+++ b/src/app/+community-page/sub-community-list/community-page-sub-community-list.component.ts
@@ -1,26 +1,82 @@
 import { Component, Input, OnInit } from '@angular/core';
 
+import { BehaviorSubject } from 'rxjs';
+import { take } from 'rxjs/operators';
+
 import { RemoteData } from '../../core/data/remote-data';
 import { Community } from '../../core/shared/community.model';
-
 import { fadeIn } from '../../shared/animations/fade';
 import { PaginatedList } from '../../core/data/paginated-list';
-import {Observable} from 'rxjs';
+import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
+import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model';
+import { CommunityDataService } from '../../core/data/community-data.service';
 
 @Component({
   selector: 'ds-community-page-sub-community-list',
   styleUrls: ['./community-page-sub-community-list.component.scss'],
   templateUrl: './community-page-sub-community-list.component.html',
-  animations:[fadeIn]
+  animations: [fadeIn]
 })
 /**
  * Component to render the sub-communities of a Community
  */
 export class CommunityPageSubCommunityListComponent implements OnInit {
   @Input() community: Community;
-  subCommunitiesRDObs: Observable<RemoteData<PaginatedList<Community>>>;
+
+  /**
+   * The pagination configuration
+   */
+  config: PaginationComponentOptions;
+
+  /**
+   * The pagination id
+   */
+  pageId = 'community-subCommunities-pagination';
+
+  /**
+   * The sorting configuration
+   */
+  sortConfig: SortOptions;
+
+  /**
+   * A list of remote data objects of communities' collections
+   */
+  subCommunitiesRDObs: BehaviorSubject<RemoteData<PaginatedList<Community>>> = new BehaviorSubject<RemoteData<PaginatedList<Community>>>({} as any);
+
+  constructor(private cds: CommunityDataService) {
+  }
 
   ngOnInit(): void {
-    this.subCommunitiesRDObs = this.community.subcommunities;
+    this.config = new PaginationComponentOptions();
+    this.config.id = this.pageId;
+    this.config.pageSize = 5;
+    this.config.currentPage = 1;
+    this.sortConfig = new SortOptions('dc.title', SortDirection.ASC);
+    this.updatePage();
+  }
+
+  /**
+   * Called when one of the pagination settings is changed
+   * @param event The new pagination data
+   */
+  onPaginationChange(event) {
+    this.config.currentPage = event.pagination.currentPage;
+    this.config.pageSize = event.pagination.pageSize;
+    this.sortConfig.field = event.sort.field;
+    this.sortConfig.direction = event.sort.direction;
+    this.updatePage();
+  }
+
+  /**
+   * Update the list of sub-communities
+   */
+  updatePage() {
+    this.cds.findByParent(this.community.id, {
+      currentPage: this.config.currentPage,
+      elementsPerPage: this.config.pageSize,
+      sort: { field: this.sortConfig.field, direction: this.sortConfig.direction }
+    }).pipe(take(1)).subscribe((results) => {
+      this.subCommunitiesRDObs.next(results);
+    });
   }
 }
diff --git a/src/app/+home-page/home-page.component.ts b/src/app/+home-page/home-page.component.ts
index 1b915ae68398422b32a859a9e8e3e6b2b5144a48..65caa0143006dc0f6016c10f9f0c17b81ea5faeb 100644
--- a/src/app/+home-page/home-page.component.ts
+++ b/src/app/+home-page/home-page.component.ts
@@ -11,14 +11,14 @@ import { Site } from '../core/shared/site.model';
 })
 export class HomePageComponent implements OnInit {
 
-  site$:Observable<Site>;
+  site$: Observable<Site>;
 
   constructor(
-    private route:ActivatedRoute,
+    private route: ActivatedRoute,
   ) {
   }
 
-  ngOnInit():void {
+  ngOnInit(): void {
     this.site$ = this.route.data.pipe(
       map((data) => data.site as Site),
     );
diff --git a/src/app/+home-page/home-page.resolver.ts b/src/app/+home-page/home-page.resolver.ts
index 1145d1d013dc36a293dbea5166066e1ddbd79d6a..6b63a4e782d98f83fd8283252c4b3f5635f9e34f 100644
--- a/src/app/+home-page/home-page.resolver.ts
+++ b/src/app/+home-page/home-page.resolver.ts
@@ -10,7 +10,7 @@ import { take } from 'rxjs/operators';
  */
 @Injectable()
 export class HomePageResolver implements Resolve<Site> {
-  constructor(private siteService:SiteDataService) {
+  constructor(private siteService: SiteDataService) {
   }
 
   /**
@@ -19,7 +19,7 @@ export class HomePageResolver implements Resolve<Site> {
    * @param {RouterStateSnapshot} state The current RouterStateSnapshot
    * @returns Observable<Site> Emits the found Site object, or an error if something went wrong
    */
-  resolve(route:ActivatedRouteSnapshot, state:RouterStateSnapshot):Observable<Site> | Promise<Site> | Site {
+  resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Site> | Promise<Site> | Site {
     return this.siteService.find().pipe(take(1));
   }
 }
diff --git a/src/app/+home-page/top-level-community-list/top-level-community-list.component.spec.ts b/src/app/+home-page/top-level-community-list/top-level-community-list.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fa164fe624de356b52d74c711ee9d334c4e172fb
--- /dev/null
+++ b/src/app/+home-page/top-level-community-list/top-level-community-list.component.spec.ts
@@ -0,0 +1,161 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { TranslateModule } from '@ngx-translate/core';
+import { NO_ERRORS_SCHEMA } from '@angular/core';
+import { RouterTestingModule } from '@angular/router/testing';
+import { NoopAnimationsModule } from '@angular/platform-browser/animations';
+import { By } from '@angular/platform-browser';
+
+import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
+
+import { TopLevelCommunityListComponent } from './top-level-community-list.component';
+import { Community } from '../../core/shared/community.model';
+import { PaginatedList } from '../../core/data/paginated-list';
+import { PageInfo } from '../../core/shared/page-info.model';
+import { SharedModule } from '../../shared/shared.module';
+import { createSuccessfulRemoteDataObject$ } from '../../shared/testing/utils';
+import { FindListOptions } from '../../core/data/request.models';
+import { HostWindowService } from '../../shared/host-window.service';
+import { HostWindowServiceStub } from '../../shared/testing/host-window-service-stub';
+import { CommunityDataService } from '../../core/data/community-data.service';
+import { SelectableListService } from '../../shared/object-list/selectable-list/selectable-list.service';
+
+describe('TopLevelCommunityList Component', () => {
+  let comp: TopLevelCommunityListComponent;
+  let fixture: ComponentFixture<TopLevelCommunityListComponent>;
+  let communityDataServiceStub: any;
+
+  const topCommList = [Object.assign(new Community(), {
+    id: '123456789-1',
+    metadata: {
+      'dc.title': [
+        { language: 'en_US', value: 'TopCommunity 1' }
+      ]
+    }
+  }),
+    Object.assign(new Community(), {
+      id: '123456789-2',
+      metadata: {
+        'dc.title': [
+          { language: 'en_US', value: 'TopCommunity 2' }
+        ]
+      }
+    }),
+    Object.assign(new Community(), {
+      id: '123456789-3',
+      metadata: {
+        'dc.title': [
+          { language: 'en_US', value: 'TopCommunity 3' }
+        ]
+      }
+    }),
+    Object.assign(new Community(), {
+      id: '12345678942',
+      metadata: {
+        'dc.title': [
+          { language: 'en_US', value: 'TopCommunity 4' }
+        ]
+      }
+    }),
+    Object.assign(new Community(), {
+      id: '123456789-5',
+      metadata: {
+        'dc.title': [
+          { language: 'en_US', value: 'TopCommunity 5' }
+        ]
+      }
+    }),
+    Object.assign(new Community(), {
+      id: '123456789-6',
+      metadata: {
+        'dc.title': [
+          { language: 'en_US', value: 'TopCommunity 6' }
+        ]
+      }
+    }),
+    Object.assign(new Community(), {
+      id: '123456789-7',
+      metadata: {
+        'dc.title': [
+          { language: 'en_US', value: 'TopCommunity 7' }
+        ]
+      }
+    })
+  ];
+
+  communityDataServiceStub = {
+    findTop(options: FindListOptions = {}) {
+      let currentPage = options.currentPage;
+      let elementsPerPage = options.elementsPerPage;
+      if (currentPage === undefined) {
+        currentPage = 1
+      }
+      elementsPerPage = 5;
+
+      const startPageIndex = (currentPage - 1) * elementsPerPage;
+      let endPageIndex = (currentPage * elementsPerPage);
+      if (endPageIndex > topCommList.length) {
+        endPageIndex = topCommList.length;
+      }
+      return createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), topCommList.slice(startPageIndex, endPageIndex)));
+
+    }
+  };
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [
+        TranslateModule.forRoot(),
+        SharedModule,
+        RouterTestingModule.withRoutes([]),
+        NgbModule.forRoot(),
+        NoopAnimationsModule
+      ],
+      declarations: [TopLevelCommunityListComponent],
+      providers: [
+        { provide: CommunityDataService, useValue: communityDataServiceStub },
+        { provide: HostWindowService, useValue: new HostWindowServiceStub(0) },
+        { provide: SelectableListService, useValue: {} },
+      ],
+      schemas: [NO_ERRORS_SCHEMA]
+    }).compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(TopLevelCommunityListComponent);
+    comp = fixture.componentInstance;
+    fixture.detectChanges();
+
+  });
+
+  it('should display a list of top-communities', () => {
+    const subComList = fixture.debugElement.queryAll(By.css('li'));
+
+    expect(subComList.length).toEqual(5);
+    expect(subComList[0].nativeElement.textContent).toContain('TopCommunity 1');
+    expect(subComList[1].nativeElement.textContent).toContain('TopCommunity 2');
+    expect(subComList[2].nativeElement.textContent).toContain('TopCommunity 3');
+    expect(subComList[3].nativeElement.textContent).toContain('TopCommunity 4');
+    expect(subComList[4].nativeElement.textContent).toContain('TopCommunity 5');
+  });
+
+  it('should update list of top-communities on pagination change', () => {
+    const pagination = Object.create({
+      pagination:{
+        id: comp.pageId,
+        currentPage: 2,
+        pageSize: 5
+      },
+      sort: {
+        field: 'dc.title',
+        direction: 'ASC'
+      }
+    });
+    comp.onPaginationChange(pagination);
+    fixture.detectChanges();
+
+    const collList = fixture.debugElement.queryAll(By.css('li'));
+    expect(collList.length).toEqual(2);
+    expect(collList[0].nativeElement.textContent).toContain('TopCommunity 6');
+    expect(collList[1].nativeElement.textContent).toContain('TopCommunity 7');
+  });
+});
diff --git a/src/app/+home-page/top-level-community-list/top-level-community-list.component.ts b/src/app/+home-page/top-level-community-list/top-level-community-list.component.ts
index 1115d785a3f8b2670dbb5487e03fe663b73354e8..02c3cb54a0b8b219cedfedfdd7d7766811ec4797 100644
--- a/src/app/+home-page/top-level-community-list/top-level-community-list.component.ts
+++ b/src/app/+home-page/top-level-community-list/top-level-community-list.component.ts
@@ -1,15 +1,15 @@
 import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
-import { BehaviorSubject, Observable } from 'rxjs';
+
+import { BehaviorSubject } from 'rxjs';
+import { take } from 'rxjs/operators';
+
 import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model';
 import { CommunityDataService } from '../../core/data/community-data.service';
 import { PaginatedList } from '../../core/data/paginated-list';
-
 import { RemoteData } from '../../core/data/remote-data';
 import { Community } from '../../core/shared/community.model';
-
 import { fadeInOut } from '../../shared/animations/fade';
 import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
-import { take } from 'rxjs/operators';
 
 /**
  * this component renders the Top-Level Community list
@@ -33,6 +33,11 @@ export class TopLevelCommunityListComponent implements OnInit {
    */
   config: PaginationComponentOptions;
 
+  /**
+   * The pagination id
+   */
+  pageId = 'top-level-pagination';
+
   /**
    * The sorting configuration
    */
@@ -40,7 +45,7 @@ export class TopLevelCommunityListComponent implements OnInit {
 
   constructor(private cds: CommunityDataService) {
     this.config = new PaginationComponentOptions();
-    this.config.id = 'top-level-pagination';
+    this.config.id = this.pageId;
     this.config.pageSize = 5;
     this.config.currentPage = 1;
     this.sortConfig = new SortOptions('dc.title', SortDirection.ASC);
@@ -55,10 +60,10 @@ export class TopLevelCommunityListComponent implements OnInit {
    * @param event The new pagination data
    */
   onPaginationChange(event) {
-    this.config.currentPage = event.page;
-    this.config.pageSize = event.pageSize;
-    this.sortConfig.field = event.sortField;
-    this.sortConfig.direction = event.sortDirection;
+    this.config.currentPage = event.pagination.currentPage;
+    this.config.pageSize = event.pagination.pageSize;
+    this.sortConfig.field = event.sort.field;
+    this.sortConfig.direction = event.sort.direction;
     this.updatePage();
   }
 
diff --git a/src/app/+item-page/edit-item-page/edit-item-page.module.ts b/src/app/+item-page/edit-item-page/edit-item-page.module.ts
index 5eda9aef101c1b143ed10bc997e67080cb0d58ac..71924cf6c859bd44da414605c175d14b80d293ab 100644
--- a/src/app/+item-page/edit-item-page/edit-item-page.module.ts
+++ b/src/app/+item-page/edit-item-page/edit-item-page.module.ts
@@ -21,7 +21,7 @@ import { ItemRelationshipsComponent } from './item-relationships/item-relationsh
 import { EditRelationshipComponent } from './item-relationships/edit-relationship/edit-relationship.component';
 import { EditRelationshipListComponent } from './item-relationships/edit-relationship-list/edit-relationship-list.component';
 import { ItemMoveComponent } from './item-move/item-move.component';
-import {VirtualMetadataComponent} from './virtual-metadata/virtual-metadata.component';
+import { VirtualMetadataComponent } from './virtual-metadata/virtual-metadata.component';
 
 /**
  * Module that contains all components related to the Edit Item page administrator functionality
diff --git a/src/app/+item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.ts b/src/app/+item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.ts
index 13214e27cf1ea5ba922666296c6940b6bdfe0981..73e3e1f8756b2aa4e1d3cdb39b95fd81e035bc2a 100644
--- a/src/app/+item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.ts
+++ b/src/app/+item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.ts
@@ -1,6 +1,6 @@
-import {Component, Input, OnInit} from '@angular/core';
-import {ObjectUpdatesService} from '../../../../core/data/object-updates/object-updates.service';
-import {Observable} from 'rxjs/internal/Observable';
+import { Component, Input, OnInit } from '@angular/core';
+import { ObjectUpdatesService } from '../../../../core/data/object-updates/object-updates.service';
+import { Observable } from 'rxjs/internal/Observable';
 import {FieldUpdate, FieldUpdates} from '../../../../core/data/object-updates/object-updates.reducer';
 import {Item} from '../../../../core/shared/item.model';
 import {map, switchMap} from 'rxjs/operators';
diff --git a/src/app/+item-page/edit-item-page/item-relationships/edit-relationship/edit-relationship.component.spec.ts b/src/app/+item-page/edit-item-page/item-relationships/edit-relationship/edit-relationship.component.spec.ts
index 70cdacd1578d24854f192e3b348cf527c28374b3..b3c3e773b22acf71d708955868aeb6ffd4ed5b0e 100644
--- a/src/app/+item-page/edit-item-page/item-relationships/edit-relationship/edit-relationship.component.spec.ts
+++ b/src/app/+item-page/edit-item-page/item-relationships/edit-relationship/edit-relationship.component.spec.ts
@@ -11,7 +11,7 @@ import { Item } from '../../../../core/shared/item.model';
 import { PaginatedList } from '../../../../core/data/paginated-list';
 import { PageInfo } from '../../../../core/shared/page-info.model';
 import { FieldChangeType } from '../../../../core/data/object-updates/object-updates.actions';
-import {NgbModal} from '@ng-bootstrap/ng-bootstrap';
+import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
 
 let objectUpdatesService;
 const url = 'http://test-url.com/test-url';
diff --git a/src/app/+item-page/edit-item-page/item-relationships/edit-relationship/edit-relationship.component.ts b/src/app/+item-page/edit-item-page/item-relationships/edit-relationship/edit-relationship.component.ts
index e8ab8b38db2306c70b17d7cb56aecd75375da7ee..d1ee99c3a7892debb579012a985dac99e8e6685a 100644
--- a/src/app/+item-page/edit-item-page/item-relationships/edit-relationship/edit-relationship.component.ts
+++ b/src/app/+item-page/edit-item-page/item-relationships/edit-relationship/edit-relationship.component.ts
@@ -1,15 +1,15 @@
-import {Component, Input, OnChanges, OnInit} from '@angular/core';
-import {combineLatest as observableCombineLatest, Observable} from 'rxjs';
-import {filter, map, switchMap, take, tap} from 'rxjs/operators';
-import {FieldChangeType} from '../../../../core/data/object-updates/object-updates.actions';
-import {DeleteRelationship, FieldUpdate} from '../../../../core/data/object-updates/object-updates.reducer';
-import {ObjectUpdatesService} from '../../../../core/data/object-updates/object-updates.service';
-import {Relationship} from '../../../../core/shared/item-relationships/relationship.model';
-import {Item} from '../../../../core/shared/item.model';
-import {getRemoteDataPayload, getSucceededRemoteData} from '../../../../core/shared/operators';
-import {ViewMode} from '../../../../core/shared/view-mode.model';
-import {hasValue, isNotEmpty} from '../../../../shared/empty.util';
-import {NgbModal, NgbModalRef} from '@ng-bootstrap/ng-bootstrap';
+import { Component, Input, OnChanges, OnInit } from '@angular/core';
+import { combineLatest as observableCombineLatest, Observable } from 'rxjs';
+import { filter, map, switchMap, take, tap } from 'rxjs/operators';
+import { FieldChangeType } from '../../../../core/data/object-updates/object-updates.actions';
+import { DeleteRelationship, FieldUpdate } from '../../../../core/data/object-updates/object-updates.reducer';
+import { ObjectUpdatesService } from '../../../../core/data/object-updates/object-updates.service';
+import { Relationship } from '../../../../core/shared/item-relationships/relationship.model';
+import { Item } from '../../../../core/shared/item.model';
+import { getRemoteDataPayload, getSucceededRemoteData } from '../../../../core/shared/operators';
+import { ViewMode } from '../../../../core/shared/view-mode.model';
+import { hasValue, isNotEmpty } from '../../../../shared/empty.util';
+import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
 
 @Component({
   // tslint:disable-next-line:component-selector
diff --git a/src/app/+item-page/edit-item-page/item-relationships/item-relationships.component.spec.ts b/src/app/+item-page/edit-item-page/item-relationships/item-relationships.component.spec.ts
index 6c0f340d65f088d9f21d09d6719dc7ab54e79168..aa812354b6c7accc1ba95610d98d9cf900dcbad7 100644
--- a/src/app/+item-page/edit-item-page/item-relationships/item-relationships.component.spec.ts
+++ b/src/app/+item-page/edit-item-page/item-relationships/item-relationships.component.spec.ts
@@ -1,32 +1,32 @@
-import {async, ComponentFixture, TestBed} from '@angular/core/testing';
-import {ItemRelationshipsComponent} from './item-relationships.component';
-import {ChangeDetectorRef, DebugElement, NO_ERRORS_SCHEMA} from '@angular/core';
-import {INotification, Notification} from '../../../shared/notifications/models/notification.model';
-import {NotificationType} from '../../../shared/notifications/models/notification-type';
-import {RouterStub} from '../../../shared/testing/router-stub';
-import {TestScheduler} from 'rxjs/testing';
-import {SharedModule} from '../../../shared/shared.module';
-import {TranslateModule} from '@ngx-translate/core';
-import {ItemDataService} from '../../../core/data/item-data.service';
-import {ObjectUpdatesService} from '../../../core/data/object-updates/object-updates.service';
-import {ActivatedRoute, Router} from '@angular/router';
-import {NotificationsService} from '../../../shared/notifications/notifications.service';
-import {GLOBAL_CONFIG} from '../../../../config';
-import {RelationshipType} from '../../../core/shared/item-relationships/relationship-type.model';
-import {Relationship} from '../../../core/shared/item-relationships/relationship.model';
-import {combineLatest as observableCombineLatest, of as observableOf} from 'rxjs';
-import {RemoteData} from '../../../core/data/remote-data';
-import {Item} from '../../../core/shared/item.model';
-import {PaginatedList} from '../../../core/data/paginated-list';
-import {PageInfo} from '../../../core/shared/page-info.model';
-import {FieldChangeType} from '../../../core/data/object-updates/object-updates.actions';
-import {RelationshipService} from '../../../core/data/relationship.service';
-import {ObjectCacheService} from '../../../core/cache/object-cache.service';
-import {getTestScheduler} from 'jasmine-marbles';
-import {RestResponse} from '../../../core/cache/response.models';
-import {RequestService} from '../../../core/data/request.service';
-import {EntityTypeService} from '../../../core/data/entity-type.service';
-import {ItemType} from '../../../core/shared/item-relationships/item-type.model';
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { ItemRelationshipsComponent } from './item-relationships.component';
+import { ChangeDetectorRef, DebugElement, NO_ERRORS_SCHEMA } from '@angular/core';
+import { INotification, Notification } from '../../../shared/notifications/models/notification.model';
+import { NotificationType } from '../../../shared/notifications/models/notification-type';
+import { RouterStub } from '../../../shared/testing/router-stub';
+import { TestScheduler } from 'rxjs/testing';
+import { SharedModule } from '../../../shared/shared.module';
+import { TranslateModule } from '@ngx-translate/core';
+import { ItemDataService } from '../../../core/data/item-data.service';
+import { ObjectUpdatesService } from '../../../core/data/object-updates/object-updates.service';
+import { ActivatedRoute, Router } from '@angular/router';
+import { NotificationsService } from '../../../shared/notifications/notifications.service';
+import { GLOBAL_CONFIG } from '../../../../config';
+import { RelationshipType } from '../../../core/shared/item-relationships/relationship-type.model';
+import { Relationship } from '../../../core/shared/item-relationships/relationship.model';
+import { combineLatest as observableCombineLatest, of as observableOf } from 'rxjs';
+import { RemoteData } from '../../../core/data/remote-data';
+import { Item } from '../../../core/shared/item.model';
+import { PaginatedList } from '../../../core/data/paginated-list';
+import { PageInfo } from '../../../core/shared/page-info.model';
+import { FieldChangeType } from '../../../core/data/object-updates/object-updates.actions';
+import { RelationshipService } from '../../../core/data/relationship.service';
+import { ObjectCacheService } from '../../../core/cache/object-cache.service';
+import { getTestScheduler } from 'jasmine-marbles';
+import { RestResponse } from '../../../core/cache/response.models';
+import { RequestService } from '../../../core/data/request.service';
+import { EntityTypeService } from '../../../core/data/entity-type.service';
+import { ItemType } from '../../../core/shared/item-relationships/item-type.model';
 
 let comp: any;
 let fixture: ComponentFixture<ItemRelationshipsComponent>;
diff --git a/src/app/+item-page/edit-item-page/item-relationships/item-relationships.component.ts b/src/app/+item-page/edit-item-page/item-relationships/item-relationships.component.ts
index b65dd60c4c9a98a6e262672b3da1c0967ca79fc6..3b0fe5f89b2320c699a4e76d933fcc4bad84f3b3 100644
--- a/src/app/+item-page/edit-item-page/item-relationships/item-relationships.component.ts
+++ b/src/app/+item-page/edit-item-page/item-relationships/item-relationships.component.ts
@@ -1,29 +1,29 @@
-import {ChangeDetectorRef, Component, Inject, OnDestroy} from '@angular/core';
-import {Item} from '../../../core/shared/item.model';
-import {DeleteRelationship, FieldUpdate, FieldUpdates} from '../../../core/data/object-updates/object-updates.reducer';
-import {Observable} from 'rxjs/internal/Observable';
-import {filter, map, switchMap, take} from 'rxjs/operators';
-import {zip as observableZip} from 'rxjs';
-import {AbstractItemUpdateComponent} from '../abstract-item-update/abstract-item-update.component';
-import {ItemDataService} from '../../../core/data/item-data.service';
-import {ObjectUpdatesService} from '../../../core/data/object-updates/object-updates.service';
-import {ActivatedRoute, Router} from '@angular/router';
-import {NotificationsService} from '../../../shared/notifications/notifications.service';
-import {TranslateService} from '@ngx-translate/core';
-import {GLOBAL_CONFIG, GlobalConfig} from '../../../../config';
-import {RelationshipService} from '../../../core/data/relationship.service';
-import {ErrorResponse, RestResponse} from '../../../core/cache/response.models';
-import {RemoteData} from '../../../core/data/remote-data';
-import {ObjectCacheService} from '../../../core/cache/object-cache.service';
-import {getRemoteDataPayload, getSucceededRemoteData} from '../../../core/shared/operators';
-import {RequestService} from '../../../core/data/request.service';
-import {Subscription} from 'rxjs/internal/Subscription';
-import {RelationshipType} from '../../../core/shared/item-relationships/relationship-type.model';
-import {ItemType} from '../../../core/shared/item-relationships/item-type.model';
-import {EntityTypeService} from '../../../core/data/entity-type.service';
-import {isNotEmptyOperator} from '../../../shared/empty.util';
-import {FieldChangeType} from '../../../core/data/object-updates/object-updates.actions';
-import {Relationship} from '../../../core/shared/item-relationships/relationship.model';
+import { ChangeDetectorRef, Component, Inject, OnDestroy } from '@angular/core';
+import { Item } from '../../../core/shared/item.model';
+import { DeleteRelationship, FieldUpdate, FieldUpdates } from '../../../core/data/object-updates/object-updates.reducer';
+import { Observable } from 'rxjs/internal/Observable';
+import { filter, map, switchMap, take } from 'rxjs/operators';
+import { zip as observableZip } from 'rxjs';
+import { AbstractItemUpdateComponent } from '../abstract-item-update/abstract-item-update.component';
+import { ItemDataService } from '../../../core/data/item-data.service';
+import { ObjectUpdatesService } from '../../../core/data/object-updates/object-updates.service';
+import { ActivatedRoute, Router } from '@angular/router';
+import { NotificationsService } from '../../../shared/notifications/notifications.service';
+import { TranslateService } from '@ngx-translate/core';
+import { GLOBAL_CONFIG, GlobalConfig } from '../../../../config';
+import { RelationshipService } from '../../../core/data/relationship.service';
+import { ErrorResponse, RestResponse } from '../../../core/cache/response.models';
+import { RemoteData } from '../../../core/data/remote-data';
+import { ObjectCacheService } from '../../../core/cache/object-cache.service';
+import { getRemoteDataPayload, getSucceededRemoteData } from '../../../core/shared/operators';
+import { RequestService } from '../../../core/data/request.service';
+import { Subscription } from 'rxjs/internal/Subscription';
+import { RelationshipType } from '../../../core/shared/item-relationships/relationship-type.model';
+import { ItemType } from '../../../core/shared/item-relationships/item-type.model';
+import { EntityTypeService } from '../../../core/data/entity-type.service';
+import { isNotEmptyOperator } from '../../../shared/empty.util';
+import { FieldChangeType } from '../../../core/data/object-updates/object-updates.actions';
+import { Relationship } from '../../../core/shared/item-relationships/relationship.model';
 
 @Component({
   selector: 'ds-item-relationships',
diff --git a/src/app/+lookup-by-id/lookup-guard.ts b/src/app/+lookup-by-id/lookup-guard.ts
index c89e3292412bfaefdc9ad82de907a04a5b76cf3d..a7ddffcd4e4505d81ad4b85dfae621bec8302f1c 100644
--- a/src/app/+lookup-by-id/lookup-guard.ts
+++ b/src/app/+lookup-by-id/lookup-guard.ts
@@ -18,7 +18,7 @@ export class LookupGuard implements CanActivate {
   constructor(private dsoService: DsoRedirectDataService) {
   }
 
-  canActivate(route: ActivatedRouteSnapshot, state:RouterStateSnapshot): Observable<boolean>  {
+  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean>  {
     const params = this.getLookupParams(route);
     return this.dsoService.findById(params.id, params.type).pipe(
       map((response: RemoteData<FindByIDRequest>) => response.hasFailed)
diff --git a/src/app/+search-page/configuration-search-page.component.ts b/src/app/+search-page/configuration-search-page.component.ts
index 33d99a9cd2bc394e35cd020015c5ebf4e2d67498..befac7f33190ecc107fb3202f5ee1ee04d54e4e0 100644
--- a/src/app/+search-page/configuration-search-page.component.ts
+++ b/src/app/+search-page/configuration-search-page.component.ts
@@ -5,10 +5,10 @@ import { ChangeDetectionStrategy, Component, Inject, Input, OnInit } from '@angu
 import { pushInOut } from '../shared/animations/push';
 import { SEARCH_CONFIG_SERVICE } from '../+my-dspace-page/my-dspace-page.component';
 import { SearchConfigurationService } from '../core/shared/search/search-configuration.service';
-import { Router } from '@angular/router';
 import { hasValue } from '../shared/empty.util';
 import { RouteService } from '../core/services/route.service';
 import { SearchService } from '../core/shared/search/search.service';
+import { Router } from '@angular/router';
 
 /**
  * This component renders a search page using a configuration as input.
@@ -61,5 +61,8 @@ export class ConfigurationSearchPageComponent extends SearchComponent implements
     if (hasValue(this.configuration)) {
       this.routeService.setParameter('configuration', this.configuration);
     }
+    if (hasValue(this.fixedFilterQuery)) {
+      this.routeService.setParameter('fixedFilterQuery', this.fixedFilterQuery);
+    }
   }
 }
diff --git a/src/app/+search-page/search-page.module.ts b/src/app/+search-page/search-page.module.ts
index e5ce67001313dc2cdfbf04a36c4b0541445014cc..456446781cb273668b75da7667de5816e3ca2175 100644
--- a/src/app/+search-page/search-page.module.ts
+++ b/src/app/+search-page/search-page.module.ts
@@ -3,12 +3,17 @@ import { CommonModule } from '@angular/common';
 import { CoreModule } from '../core/core.module';
 import { SharedModule } from '../shared/shared.module';
 import { SearchPageRoutingModule } from './search-page-routing.module';
-import { SearchPageComponent } from './search-page.component';
+import { SearchComponent } from './search.component';
+import { SidebarService } from '../shared/sidebar/sidebar.service';
+import { EffectsModule } from '@ngrx/effects';
 import { ConfigurationSearchPageComponent } from './configuration-search-page.component';
 import { ConfigurationSearchPageGuard } from './configuration-search-page.guard';
 import { SearchTrackerComponent } from './search-tracker.component';
 import { StatisticsModule } from '../statistics/statistics.module';
-import { SearchComponent } from './search.component';
+import { SearchPageComponent } from './search-page.component';
+import { SidebarFilterService } from '../shared/sidebar/filter/sidebar-filter.service';
+import { SearchFilterService } from '../core/shared/search/search-filter.service';
+import { SearchConfigurationService } from '../core/shared/search/search-configuration.service';
 
 const components = [
   SearchPageComponent,
@@ -25,8 +30,14 @@ const components = [
     CoreModule.forRoot(),
     StatisticsModule.forRoot(),
   ],
-  providers: [ConfigurationSearchPageGuard],
   declarations: components,
+  providers: [
+    SidebarService,
+    SidebarFilterService,
+    SearchFilterService,
+    ConfigurationSearchPageGuard,
+    SearchConfigurationService
+  ],
   exports: components
 })
 
diff --git a/src/app/+search-page/search-tracker.component.ts b/src/app/+search-page/search-tracker.component.ts
index 58867e3f033eb7d706a0622a01837a47211b393f..7e5aa49165dce9ab4a3c39ae4eb5adf73198c897 100644
--- a/src/app/+search-page/search-tracker.component.ts
+++ b/src/app/+search-page/search-tracker.component.ts
@@ -9,10 +9,10 @@ import { RouteService } from '../core/services/route.service';
 import { hasValue } from '../shared/empty.util';
 import { SearchSuccessResponse } from '../core/cache/response.models';
 import { SearchConfigurationService } from '../core/shared/search/search-configuration.service';
-import { Router } from '@angular/router';
 import { SearchService } from '../core/shared/search/search.service';
 import { PaginatedSearchOptions } from '../shared/search/paginated-search-options.model';
 import { SearchQueryResponse } from '../shared/search/search-query-response.model';
+import { Router } from '@angular/router';
 
 /**
  * This component triggers a page view statistic
@@ -42,7 +42,7 @@ export class SearchTrackerComponent extends SearchComponent implements OnInit {
     super(service, sidebarService, windowService, searchConfigService, routeService, router);
   }
 
-  ngOnInit():void {
+  ngOnInit(): void {
     // super.ngOnInit();
     this.getSearchOptions().pipe(
       switchMap((options) => this.service.searchEntries(options)
@@ -62,7 +62,7 @@ export class SearchTrackerComponent extends SearchComponent implements OnInit {
       .subscribe((entry) => {
         const config: PaginatedSearchOptions = entry.searchOptions;
         const searchQueryResponse: SearchQueryResponse = entry.response;
-        const filters:Array<{ filter: string, operator: string, value: string, label: string; }> = [];
+        const filters: Array<{ filter: string, operator: string, value: string, label: string; }> = [];
         const appliedFilters = searchQueryResponse.appliedFilters || [];
         for (let i = 0, filtersLength = appliedFilters.length; i < filtersLength; i++) {
           const appliedFilter = appliedFilters[i];
diff --git a/src/app/+search-page/search.component.html b/src/app/+search-page/search.component.html
index f3731607dbe62510cf7897f7d76b9a76d4ca6944..36879f33d4fa50acb87846cac96dd92d96add115 100644
--- a/src/app/+search-page/search.component.html
+++ b/src/app/+search-page/search.component.html
@@ -46,9 +46,9 @@
                   [scopes]="(scopeListRD$ | async)"
                   [inPlaceSearch]="inPlaceSearch">
   </ds-search-form>
-    <div class="row mb-3 mb-md-1">
-        <div class="labels col-sm-9 offset-sm-3">
-            <ds-search-labels *ngIf="searchEnabled" [inPlaceSearch]="inPlaceSearch"></ds-search-labels>
-        </div>
+  <div class="row mb-3 mb-md-1">
+    <div class="labels col-sm-9 offset-sm-3">
+      <ds-search-labels *ngIf="searchEnabled" [inPlaceSearch]="inPlaceSearch"></ds-search-labels>
     </div>
+  </div>
 </ng-template>
diff --git a/src/app/+search-page/search.component.ts b/src/app/+search-page/search.component.ts
index bfb99755d8a823b07e5bdcdb599d9deef51ef935..b27ebf625f56bc3d9b6060e55936caac6c90367f 100644
--- a/src/app/+search-page/search.component.ts
+++ b/src/app/+search-page/search.component.ts
@@ -11,9 +11,9 @@ import { hasValue, isNotEmpty } from '../shared/empty.util';
 import { getSucceededRemoteData } from '../core/shared/operators';
 import { RouteService } from '../core/services/route.service';
 import { SEARCH_CONFIG_SERVICE } from '../+my-dspace-page/my-dspace-page.component';
-import { SearchConfigurationService } from '../core/shared/search/search-configuration.service';
-import { SearchResult } from '../shared/search/search-result.model';
 import { PaginatedSearchOptions } from '../shared/search/paginated-search-options.model';
+import { SearchResult } from '../shared/search/search-result.model';
+import { SearchConfigurationService } from '../core/shared/search/search-configuration.service';
 import { SearchService } from '../core/shared/search/search.service';
 import { currentPath } from '../shared/utils/route.utils';
 import { Router } from '@angular/router';
diff --git a/src/app/app.component.ts b/src/app/app.component.ts
index 1f3da086c2a7f1d82f5e3689ccbc03bb0c3c97d7..2141b06a77046c7557c5e38b58a382340851d388 100644
--- a/src/app/app.component.ts
+++ b/src/app/app.component.ts
@@ -1,4 +1,4 @@
-import { filter, map, take } from 'rxjs/operators';
+import { delay, filter, map, take } from 'rxjs/operators';
 import { AfterViewInit, ChangeDetectionStrategy, Component, HostListener, Inject, OnInit, ViewEncapsulation } from '@angular/core';
 import { NavigationCancel, NavigationEnd, NavigationStart, Router } from '@angular/router';
 
@@ -125,8 +125,11 @@ export class AppComponent implements OnInit, AfterViewInit {
   }
 
   ngAfterViewInit() {
-    this.router.events
-      .subscribe((event) => {
+    this.router.events.pipe(
+      // This fixes an ExpressionChangedAfterItHasBeenCheckedError from being thrown while loading the component
+      // More information on this bug-fix: https://blog.angular-university.io/angular-debugging/
+      delay(0)
+    ).subscribe((event) => {
         if (event instanceof NavigationStart) {
           this.isLoading = true;
         } else if (
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 926575d711b715b85ce5c425a779556927e0753e..4b803608f396fb24c00fa9cbf73e8ba7e9b035be 100755
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -10,10 +10,14 @@ import { META_REDUCERS, MetaReducer, StoreModule } from '@ngrx/store';
 import { StoreDevtoolsModule } from '@ngrx/store-devtools';
 
 import { TranslateModule } from '@ngx-translate/core';
+import { ScrollToModule } from '@nicky-lenaers/ngx-scroll-to';
 
 import { storeFreeze } from 'ngrx-store-freeze';
 
 import { ENV_CONFIG, GLOBAL_CONFIG, GlobalConfig } from '../config';
+import { AdminSidebarSectionComponent } from './+admin/admin-sidebar/admin-sidebar-section/admin-sidebar-section.component';
+import { AdminSidebarComponent } from './+admin/admin-sidebar/admin-sidebar.component';
+import { ExpandableAdminSidebarSectionComponent } from './+admin/admin-sidebar/expandable-admin-sidebar-section/expandable-admin-sidebar-section.component';
 import { AppRoutingModule } from './app-routing.module';
 
 import { AppComponent } from './app.component';
@@ -23,23 +27,20 @@ import { appMetaReducers, debugMetaReducers } from './app.metareducers';
 import { appReducers, AppState } from './app.reducer';
 
 import { CoreModule } from './core/core.module';
+import { ClientCookieService } from './core/services/client-cookie.service';
+import { JournalEntitiesModule } from './entity-groups/journal-entities/journal-entities.module';
+import { ResearchEntitiesModule } from './entity-groups/research-entities/research-entities.module';
 import { FooterComponent } from './footer/footer.component';
+import { HeaderNavbarWrapperComponent } from './header-nav-wrapper/header-navbar-wrapper.component';
 import { HeaderComponent } from './header/header.component';
+import { NavbarModule } from './navbar/navbar.module';
 import { PageNotFoundComponent } from './pagenotfound/pagenotfound.component';
+import { SearchNavbarComponent } from './search-navbar/search-navbar.component';
 
 import { DSpaceRouterStateSerializer } from './shared/ngrx/dspace-router-state-serializer';
-import { NotificationsBoardComponent } from './shared/notifications/notifications-board/notifications-board.component';
 import { NotificationComponent } from './shared/notifications/notification/notification.component';
+import { NotificationsBoardComponent } from './shared/notifications/notifications-board/notifications-board.component';
 import { SharedModule } from './shared/shared.module';
-import { ScrollToModule } from '@nicky-lenaers/ngx-scroll-to';
-import { HeaderNavbarWrapperComponent } from './header-nav-wrapper/header-navbar-wrapper.component';
-import { AdminSidebarComponent } from './+admin/admin-sidebar/admin-sidebar.component';
-import { AdminSidebarSectionComponent } from './+admin/admin-sidebar/admin-sidebar-section/admin-sidebar-section.component';
-import { ExpandableAdminSidebarSectionComponent } from './+admin/admin-sidebar/expandable-admin-sidebar-section/expandable-admin-sidebar-section.component';
-import { NavbarModule } from './navbar/navbar.module';
-import { ClientCookieService } from './core/services/client-cookie.service';
-import { JournalEntitiesModule } from './entity-groups/journal-entities/journal-entities.module';
-import { ResearchEntitiesModule } from './entity-groups/research-entities/research-entities.module';
 
 export function getConfig() {
   return ENV_CONFIG;
@@ -112,7 +113,8 @@ const DECLARATIONS = [
   FooterComponent,
   PageNotFoundComponent,
   NotificationComponent,
-  NotificationsBoardComponent
+  NotificationsBoardComponent,
+  SearchNavbarComponent,
 ];
 
 const EXPORTS = [
@@ -128,7 +130,7 @@ const EXPORTS = [
     ...PROVIDERS
   ],
   declarations: [
-    ...DECLARATIONS
+    ...DECLARATIONS,
   ],
   exports: [
     ...EXPORTS
diff --git a/src/app/core/auth/auth.interceptor.ts b/src/app/core/auth/auth.interceptor.ts
index da760b8faa236802f0cf091abecfec91312e540f..08e892bbd97b1473ab09263a6126b3a490ebb9a2 100644
--- a/src/app/core/auth/auth.interceptor.ts
+++ b/src/app/core/auth/auth.interceptor.ts
@@ -56,7 +56,7 @@ export class AuthInterceptor implements HttpInterceptor {
     return http.url && http.url.endsWith('/authn/logout');
   }
 
-  private makeAuthStatusObject(authenticated:boolean, accessToken?: string, error?: string): AuthStatus {
+  private makeAuthStatusObject(authenticated: boolean, accessToken?: string, error?: string): AuthStatus {
     const authStatus = new AuthStatus();
     authStatus.id = null;
     authStatus.okay = true;
diff --git a/src/app/core/cache/models/normalized-external-source-entry.model.ts b/src/app/core/cache/models/normalized-external-source-entry.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..de262949e70ef6770e60743e77a903dc0b43f2bb
--- /dev/null
+++ b/src/app/core/cache/models/normalized-external-source-entry.model.ts
@@ -0,0 +1,42 @@
+import { autoserialize, autoserializeAs, inheritSerialization } from 'cerialize';
+import { NormalizedObject } from './normalized-object.model';
+import { ExternalSourceEntry } from '../../shared/external-source-entry.model';
+import { mapsTo } from '../builders/build-decorators';
+import { MetadataMap, MetadataMapSerializer } from '../../shared/metadata.models';
+
+/**
+ * Normalized model class for an external source entry
+ */
+@mapsTo(ExternalSourceEntry)
+@inheritSerialization(NormalizedObject)
+export class NormalizedExternalSourceEntry extends NormalizedObject<ExternalSourceEntry> {
+  /**
+   * Unique identifier
+   */
+  @autoserialize
+  id: string;
+
+  /**
+   * The value to display
+   */
+  @autoserialize
+  display: string;
+
+  /**
+   * The value to store the entry with
+   */
+  @autoserialize
+  value: string;
+
+  /**
+   * The ID of the external source this entry originates from
+   */
+  @autoserialize
+  externalSource: string;
+
+  /**
+   * Metadata of the entry
+   */
+  @autoserializeAs(MetadataMapSerializer)
+  metadata: MetadataMap;
+}
diff --git a/src/app/core/cache/models/normalized-external-source.model.ts b/src/app/core/cache/models/normalized-external-source.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fd9a42fb721182eec336e0f0adde35ea6f0705e4
--- /dev/null
+++ b/src/app/core/cache/models/normalized-external-source.model.ts
@@ -0,0 +1,29 @@
+import { autoserialize, inheritSerialization } from 'cerialize';
+import { NormalizedObject } from './normalized-object.model';
+import { ExternalSource } from '../../shared/external-source.model';
+import { mapsTo } from '../builders/build-decorators';
+
+/**
+ * Normalized model class for an external source
+ */
+@mapsTo(ExternalSource)
+@inheritSerialization(NormalizedObject)
+export class NormalizedExternalSource extends NormalizedObject<ExternalSource> {
+  /**
+   * Unique identifier
+   */
+  @autoserialize
+  id: string;
+
+  /**
+   * The name of this external source
+   */
+  @autoserialize
+  name: string;
+
+  /**
+   * Is the source hierarchical?
+   */
+  @autoserialize
+  hierarchical: boolean;
+}
diff --git a/src/app/core/cache/response.models.ts b/src/app/core/cache/response.models.ts
index 3915eca23fe28f89b6ed0fa0289943e3f602022a..5f4e15e13820ef0174b571884578faf0fddd08a3 100644
--- a/src/app/core/cache/response.models.ts
+++ b/src/app/core/cache/response.models.ts
@@ -14,6 +14,7 @@ import { DSpaceObject } from '../shared/dspace-object.model';
 import { NormalizedAuthStatus } from '../auth/models/normalized-auth-status.model';
 import { MetadataSchema } from '../metadata/metadata-schema.model';
 import { MetadataField } from '../metadata/metadata-field.model';
+import { ContentSource } from '../shared/content-source.model';
 
 /* tslint:disable:max-classes-per-file */
 export class RestResponse {
@@ -288,4 +289,17 @@ export class FilteredDiscoveryQueryResponse extends RestResponse {
     super(true, statusCode, statusText);
   }
 }
+
+/**
+ * A successful response containing exactly one MetadataSchema
+ */
+export class ContentSourceSuccessResponse extends RestResponse {
+  constructor(
+    public contentsource: ContentSource,
+    public statusCode: number,
+    public statusText: string,
+  ) {
+    super(true, statusCode, statusText);
+  }
+}
 /* tslint:enable:max-classes-per-file */
diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts
index 470e9abe0a01c76651f84a08120fec8445bd3cfa..1621c4081dfe9d13d73fd9b7096900872e79c119 100644
--- a/src/app/core/core.module.ts
+++ b/src/app/core/core.module.ts
@@ -119,6 +119,7 @@ import { MetadatafieldParsingService } from './data/metadatafield-parsing.servic
 import { NormalizedSubmissionUploadsModel } from './config/models/normalized-config-submission-uploads.model';
 import { NormalizedBrowseEntry } from './shared/normalized-browse-entry.model';
 import { BrowseDefinition } from './shared/browse-definition.model';
+import { ContentSourceResponseParsingService } from './data/content-source-response-parsing.service';
 import { MappedCollectionsReponseParsingService } from './data/mapped-collections-reponse-parsing.service';
 import { ObjectSelectService } from '../shared/object-select/object-select.service';
 import {EntityTypeService} from './data/entity-type.service';
@@ -137,6 +138,10 @@ import { SearchConfigurationService } from './shared/search/search-configuration
 import { SelectableListService } from '../shared/object-list/selectable-list/selectable-list.service';
 import { RelationshipTypeService } from './data/relationship-type.service';
 import { SidebarService } from '../shared/sidebar/sidebar.service';
+import { NormalizedExternalSource } from './cache/models/normalized-external-source.model';
+import { NormalizedExternalSourceEntry } from './cache/models/normalized-external-source-entry.model';
+import { ExternalSourceService } from './data/external-source.service';
+import { LookupRelationService } from './data/lookup-relation.service';
 
 /**
  * When not in production, endpoint responses can be mocked for testing purposes
@@ -242,6 +247,7 @@ const PROVIDERS = [
   ClaimedTaskDataService,
   PoolTaskDataService,
   EntityTypeService,
+  ContentSourceResponseParsingService,
   SearchService,
   SidebarService,
   SearchFilterService,
@@ -249,6 +255,8 @@ const PROVIDERS = [
   SearchConfigurationService,
   SelectableListService,
   RelationshipTypeService,
+  ExternalSourceService,
+  LookupRelationService,
   // register AuthInterceptor as HttpInterceptor
   {
     provide: HTTP_INTERCEPTORS,
@@ -294,7 +302,9 @@ export const normalizedModels =
     NormalizedPoolTask,
     NormalizedRelationship,
     NormalizedRelationshipType,
-    NormalizedItemType
+    NormalizedItemType,
+    NormalizedExternalSource,
+    NormalizedExternalSourceEntry
   ];
 
 @NgModule({
diff --git a/src/app/core/data/bitstream-format-data.service.ts b/src/app/core/data/bitstream-format-data.service.ts
index b5c2b708dc1a5fb58634688d1fc286c4b27f05c2..c30330a0a30fee9cbe910f64caaa21934d2aaf24 100644
--- a/src/app/core/data/bitstream-format-data.service.ts
+++ b/src/app/core/data/bitstream-format-data.service.ts
@@ -25,8 +25,12 @@ import {
 import { hasValue } from '../../shared/empty.util';
 import { RequestEntry } from './request.reducer';
 import { CoreState } from '../core.reducers';
+import { coreSelector } from '../core.selectors';
 
-const bitstreamFormatsStateSelector = (state: CoreState) => state.bitstreamFormats;
+const bitstreamFormatsStateSelector = createSelector(
+  coreSelector,
+  (state: CoreState) => state.bitstreamFormats
+);
 const selectedBitstreamFormatSelector = createSelector(bitstreamFormatsStateSelector,
   (bitstreamFormatRegistryState: BitstreamFormatRegistryState) => bitstreamFormatRegistryState.selectedBitstreamFormats);
 
diff --git a/src/app/core/data/change-analyzer.ts b/src/app/core/data/change-analyzer.ts
index 6b5a69259bc5e6fa32009636a932cfa22f43b995..c45c9e55b77b3a548960ba997467b42f6f9271a1 100644
--- a/src/app/core/data/change-analyzer.ts
+++ b/src/app/core/data/change-analyzer.ts
@@ -17,5 +17,5 @@ export interface ChangeAnalyzer<T extends CacheableObject> {
    * @param {NormalizedObject} object2
    *    The second object to compare
    */
-  diff(object1: T | NormalizedObject<T>, object2: T | NormalizedObject<T>):  Operation[];
+  diff(object1: T | NormalizedObject<T>, object2: T | NormalizedObject<T>): Operation[];
 }
diff --git a/src/app/core/data/collection-data.service.spec.ts b/src/app/core/data/collection-data.service.spec.ts
index a3e1a916e3ed31d9a210e07c37530ac2819c6d9a..c8f056bf19111611413248c3ef4241c54447d05a 100644
--- a/src/app/core/data/collection-data.service.spec.ts
+++ b/src/app/core/data/collection-data.service.spec.ts
@@ -1,44 +1,132 @@
 import { CollectionDataService } from './collection-data.service';
-import { HALEndpointServiceStub } from '../../shared/testing/hal-endpoint-service-stub';
-import { getMockRequestService } from '../../shared/mocks/mock-request.service';
-import { HALEndpointService } from '../shared/hal-endpoint.service';
 import { RequestService } from './request.service';
+import { TranslateService } from '@ngx-translate/core';
+import { getMockRequestService } from '../../shared/mocks/mock-request.service';
+import { HALEndpointServiceStub } from '../../shared/testing/hal-endpoint-service-stub';
+import { NotificationsServiceStub } from '../../shared/testing/notifications-service-stub';
+import { getMockTranslateService } from '../../shared/mocks/mock-translate.service';
+import { fakeAsync, tick } from '@angular/core/testing';
+import { ContentSourceRequest, GetRequest, RequestError, UpdateContentSourceRequest } from './request.models';
+import { ContentSource } from '../shared/content-source.model';
+import { of as observableOf } from 'rxjs/internal/observable/of';
+import { RequestEntry } from './request.reducer';
+import { ErrorResponse, RestResponse } from '../cache/response.models';
 import { ObjectCacheService } from '../cache/object-cache.service';
-import { GetRequest } from './request.models';
 import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
 
+const url = 'fake-url';
+const collectionId = 'fake-collection-id';
+
 describe('CollectionDataService', () => {
   let service: CollectionDataService;
-  let objectCache: ObjectCacheService;
+
   let requestService: RequestService;
-  let halService: HALEndpointService;
+  let translate: TranslateService;
+  let notificationsService: any;
   let rdbService: RemoteDataBuildService;
+  let objectCache: ObjectCacheService;
+  let halService: any;
+
+  describe('when the requests are successful', () => {
+    beforeEach(() => {
+      createService();
+    });
 
-  const url = 'fake-collections-url';
+    describe('when calling getContentSource', () => {
+      let contentSource$;
 
-  beforeEach(() => {
-    objectCache = jasmine.createSpyObj('objectCache', {
-      remove: jasmine.createSpy('remove')
+      beforeEach(() => {
+        contentSource$ = service.getContentSource(collectionId);
+      });
+
+      it('should configure a new ContentSourceRequest', fakeAsync(() => {
+        contentSource$.subscribe();
+        tick();
+        expect(requestService.configure).toHaveBeenCalledWith(jasmine.any(ContentSourceRequest));
+      }));
     });
-    requestService = getMockRequestService();
-    halService = Object.assign(new HALEndpointServiceStub(url));
-    rdbService = jasmine.createSpyObj('rdbService', {
-      buildList: jasmine.createSpy('buildList')
+
+    describe('when calling updateContentSource', () => {
+      let returnedContentSource$;
+      let contentSource;
+
+      beforeEach(() => {
+        contentSource = new ContentSource();
+        returnedContentSource$ = service.updateContentSource(collectionId, contentSource);
+      });
+
+      it('should configure a new UpdateContentSourceRequest', fakeAsync(() => {
+        returnedContentSource$.subscribe();
+        tick();
+        expect(requestService.configure).toHaveBeenCalledWith(jasmine.any(UpdateContentSourceRequest));
+      }));
     });
 
-    service = new CollectionDataService(requestService, rdbService, null, null, null, objectCache, halService, null, null, null);
-  });
+    describe('getMappedItems', () => {
+      let result;
 
-  describe('getMappedItems', () => {
-    let result;
+      beforeEach(() => {
+        result = service.getMappedItems('collection-id');
+      });
 
+      it('should configure a GET request', () => {
+        expect(requestService.configure).toHaveBeenCalledWith(jasmine.any(GetRequest));
+      });
+    });
+
+  });
+
+  describe('when the requests are unsuccessful', () => {
     beforeEach(() => {
-      result = service.getMappedItems('collection-id');
+      createService(observableOf(Object.assign(new RequestEntry(), {
+        response: new ErrorResponse(Object.assign({
+          statusCode: 422,
+          statusText: 'Unprocessable Entity',
+          message: 'Error message'
+        }))
+      })));
     });
 
-    it('should configure a GET request', () => {
-      expect(requestService.configure).toHaveBeenCalledWith(jasmine.any(GetRequest));
+    describe('when calling updateContentSource', () => {
+      let returnedContentSource$;
+      let contentSource;
+
+      beforeEach(() => {
+        contentSource = new ContentSource();
+        returnedContentSource$ = service.updateContentSource(collectionId, contentSource);
+      });
+
+      it('should configure a new UpdateContentSourceRequest', fakeAsync(() => {
+        returnedContentSource$.subscribe();
+        tick();
+        expect(requestService.configure).toHaveBeenCalledWith(jasmine.any(UpdateContentSourceRequest));
+      }));
+
+      it('should display an error notification', fakeAsync(() => {
+        returnedContentSource$.subscribe();
+        tick();
+        expect(notificationsService.error).toHaveBeenCalled();
+      }));
     });
   });
 
+  /**
+   * Create a CollectionDataService used for testing
+   * @param requestEntry$   Supply a requestEntry to be returned by the REST API (optional)
+   */
+  function createService(requestEntry$?) {
+    requestService = getMockRequestService(requestEntry$);
+    rdbService = jasmine.createSpyObj('rdbService', {
+      buildList: jasmine.createSpy('buildList')
+    });
+    objectCache = jasmine.createSpyObj('objectCache', {
+      remove: jasmine.createSpy('remove')
+    });
+    halService = new HALEndpointServiceStub(url);
+    notificationsService = new NotificationsServiceStub();
+    translate = getMockTranslateService();
+
+    service = new CollectionDataService(requestService, rdbService, null, null, null, objectCache, halService, notificationsService, null, null, translate);
+  }
+
 });
diff --git a/src/app/core/data/collection-data.service.ts b/src/app/core/data/collection-data.service.ts
index 0c032e676655ad69ae2ab7a4eddc0471270cdf0b..ed05c99e279c203c273074a3a35915fcb860eaba 100644
--- a/src/app/core/data/collection-data.service.ts
+++ b/src/app/core/data/collection-data.service.ts
@@ -12,25 +12,45 @@ import { CommunityDataService } from './community-data.service';
 import { RequestService } from './request.service';
 import { HALEndpointService } from '../shared/hal-endpoint.service';
 import { NotificationsService } from '../../shared/notifications/notifications.service';
-import { HttpClient } from '@angular/common/http';
+import { HttpClient, HttpHeaders } from '@angular/common/http';
 import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service';
 import { DSOChangeAnalyzer } from './dso-change-analyzer.service';
 import { Observable } from 'rxjs/internal/Observable';
-import {FindListOptions, FindListRequest, GetRequest} from './request.models';
+import {
+  ContentSourceRequest,
+  RestRequest,
+  UpdateContentSourceRequest,
+  GetRequest,
+  FindListOptions
+} from './request.models';
 import { RemoteData } from './remote-data';
 import { PaginatedList } from './paginated-list';
-import { configureRequest } from '../shared/operators';
+import { ContentSource } from '../shared/content-source.model';
+import {
+  configureRequest,
+  filterSuccessfulResponses,
+  getRequestFromRequestHref,
+  getResponseFromEntry
+} from '../shared/operators';
+import { ContentSourceSuccessResponse, RestResponse } from '../cache/response.models';
+import { HttpOptions } from '../dspace-rest-v2/dspace-rest-v2.service';
+import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer';
+import { hasValue, isNotEmpty, isNotEmptyOperator } from '../../shared/empty.util';
+import { NotificationOptions } from '../../shared/notifications/models/notification-options.model';
+import { TranslateService } from '@ngx-translate/core';
+import { SearchParam } from '../cache/models/search-param.model';
 import { DSOResponseParsingService } from './dso-response-parsing.service';
 import { ResponseParsingService } from './parsing.service';
 import { GenericConstructor } from '../shared/generic-constructor';
-import { hasValue, isNotEmptyOperator } from '../../shared/empty.util';
 import { DSpaceObject } from '../shared/dspace-object.model';
-import { SearchParam } from '../cache/models/search-param.model';
+import { INotification } from '../../shared/notifications/models/notification.model';
 import { PaginatedSearchOptions } from '../../shared/search/paginated-search-options.model';
 
 @Injectable()
 export class CollectionDataService extends ComColDataService<Collection> {
   protected linkPath = 'collections';
+  protected errorTitle = 'collection.source.update.notifications.error.title';
+  protected contentSourceError = 'collection.source.update.notifications.error.content';
 
   constructor(
     protected requestService: RequestService,
@@ -42,7 +62,8 @@ export class CollectionDataService extends ComColDataService<Collection> {
     protected halService: HALEndpointService,
     protected notificationsService: NotificationsService,
     protected http: HttpClient,
-    protected comparator: DSOChangeAnalyzer<Collection>
+    protected comparator: DSOChangeAnalyzer<Collection>,
+    protected translate: TranslateService
   ) {
     super();
   }
@@ -97,6 +118,81 @@ export class CollectionDataService extends ComColDataService<Collection> {
     );
   }
 
+  /**
+   * Get the endpoint for the collection's content harvester
+   * @param collectionId
+   */
+  getHarvesterEndpoint(collectionId: string): Observable<string> {
+    return this.halService.getEndpoint(this.linkPath).pipe(
+      switchMap((href: string) => this.halService.getEndpoint('harvester', `${href}/${collectionId}`))
+    );
+  }
+
+  /**
+   * Get the collection's content harvester
+   * @param collectionId
+   */
+  getContentSource(collectionId: string): Observable<ContentSource> {
+    return this.getHarvesterEndpoint(collectionId).pipe(
+      map((href: string) => new ContentSourceRequest(this.requestService.generateRequestId(), href)),
+      configureRequest(this.requestService),
+      map((request: RestRequest) => request.href),
+      getRequestFromRequestHref(this.requestService),
+      filterSuccessfulResponses(),
+      map((response: ContentSourceSuccessResponse) => response.contentsource)
+    );
+  }
+
+  /**
+   * Update the settings of the collection's content harvester
+   * @param collectionId
+   * @param contentSource
+   */
+  updateContentSource(collectionId: string, contentSource: ContentSource): Observable<ContentSource | INotification> {
+    const requestId = this.requestService.generateRequestId();
+    const serializedContentSource = new DSpaceRESTv2Serializer(ContentSource).serialize(contentSource);
+    const request$ = this.getHarvesterEndpoint(collectionId).pipe(
+      take(1),
+      map((href: string) => {
+        const options: HttpOptions = Object.create({});
+        let headers = new HttpHeaders();
+        headers = headers.append('Content-Type', 'application/json');
+        options.headers = headers;
+        return new UpdateContentSourceRequest(requestId, href, JSON.stringify(serializedContentSource), options);
+      })
+    );
+
+    // Execute the post/put request
+    request$.pipe(
+      configureRequest(this.requestService)
+    ).subscribe();
+
+    // Return updated ContentSource
+    return this.requestService.getByUUID(requestId).pipe(
+      getResponseFromEntry(),
+      map((response: RestResponse) => {
+        if (!response.isSuccessful) {
+          if (hasValue((response as any).errorMessage)) {
+            if (response.statusCode === 422) {
+              return this.notificationsService.error(this.translate.instant(this.errorTitle), this.translate.instant(this.contentSourceError), new NotificationOptions(-1));
+            } else {
+              return this.notificationsService.error(this.translate.instant(this.errorTitle), (response as any).errorMessage, new NotificationOptions(-1));
+            }
+          }
+        } else {
+          return response;
+        }
+      }),
+      isNotEmptyOperator(),
+      map((response: ContentSourceSuccessResponse | INotification) => {
+        if (isNotEmpty((response as any).contentsource)) {
+          return (response as ContentSourceSuccessResponse).contentsource;
+        }
+        return response as INotification;
+      })
+    );
+  }
+
   /**
    * Fetches the endpoint used for mapping items to a collection
    * @param collectionId   The id of the collection to map items to
diff --git a/src/app/core/data/content-source-response-parsing.service.ts b/src/app/core/data/content-source-response-parsing.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4e0490148b3b4064d8ef186aea023feb7060f4ba
--- /dev/null
+++ b/src/app/core/data/content-source-response-parsing.service.ts
@@ -0,0 +1,31 @@
+import { Injectable } from '@angular/core';
+import { ResponseParsingService } from './parsing.service';
+import { RestRequest } from './request.models';
+import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
+import { ContentSourceSuccessResponse, RestResponse } from '../cache/response.models';
+import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer';
+import { ContentSource } from '../shared/content-source.model';
+import { MetadataConfig } from '../shared/metadata-config.model';
+
+@Injectable()
+/**
+ * A ResponseParsingService used to parse DSpaceRESTV2Response coming from the REST API to a ContentSource object
+ * wrapped in a ContentSourceSuccessResponse
+ */
+export class ContentSourceResponseParsingService implements ResponseParsingService {
+
+  parse(request: RestRequest, data: DSpaceRESTV2Response): RestResponse {
+    const payload = data.payload;
+
+    const deserialized = new DSpaceRESTv2Serializer(ContentSource).deserialize(payload);
+
+    let metadataConfigs = [];
+    if (payload._embedded && payload._embedded.harvestermetadata && payload._embedded.harvestermetadata.configs) {
+      metadataConfigs = new DSpaceRESTv2Serializer(MetadataConfig).serializeArray(payload._embedded.harvestermetadata.configs);
+    }
+    deserialized.metadataConfigs = metadataConfigs;
+
+    return new ContentSourceSuccessResponse(deserialized, data.statusCode, data.statusText);
+  }
+
+}
diff --git a/src/app/core/data/data.service.spec.ts b/src/app/core/data/data.service.spec.ts
index 1ea265281336c8c4694cc1748c497b553963dd0d..ca5f2cc12eba9485fb6af233014dd242ee3576b3 100644
--- a/src/app/core/data/data.service.spec.ts
+++ b/src/app/core/data/data.service.spec.ts
@@ -17,6 +17,7 @@ import { NormalizedObjectBuildService } from '../cache/builders/normalized-objec
 import { NotificationsService } from '../../shared/notifications/notifications.service';
 import { Item } from '../shared/item.model';
 import * as uuidv4 from 'uuid/v4';
+import { createSuccessfulRemoteDataObject$ } from '../../shared/testing/utils';
 
 const endpoint = 'https://rest.api/core';
 
@@ -191,8 +192,7 @@ describe('DataService', () => {
       dso2.self = selfLink;
       dso2.metadata = [{ key: 'dc.title', value: name2 }];
 
-      spyOn(service, 'findByHref').and.returnValues(observableOf(dso));
-      spyOn(objectCache, 'getObjectBySelfLink').and.returnValues(observableOf(dso));
+      spyOn(service, 'findByHref').and.returnValue(createSuccessfulRemoteDataObject$(dso));
       spyOn(objectCache, 'addPatch');
     });
 
diff --git a/src/app/core/data/data.service.ts b/src/app/core/data/data.service.ts
index f1d76b47fdc8fc7fb0c8a3d0d65647be0080f6aa..d55b7353eb93daf3099ba0e9034e6deb5c2281c3 100644
--- a/src/app/core/data/data.service.ts
+++ b/src/app/core/data/data.service.ts
@@ -37,7 +37,7 @@ import { Operation } from 'fast-json-patch';
 import { ObjectCacheService } from '../cache/object-cache.service';
 import { DSpaceObject } from '../shared/dspace-object.model';
 import { NotificationsService } from '../../shared/notifications/notifications.service';
-import { configureRequest, getResponseFromEntry } from '../shared/operators';
+import { configureRequest, getRemoteDataPayload, getResponseFromEntry, getSucceededRemoteData } from '../shared/operators';
 import { ErrorResponse, RestResponse } from '../cache/response.models';
 import { NotificationOptions } from '../../shared/notifications/models/notification-options.model';
 import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer';
@@ -248,8 +248,11 @@ export abstract class DataService<T extends CacheableObject> {
    * @param {DSpaceObject} object The given object
    */
   update(object: T): Observable<RemoteData<T>> {
-    const oldVersion$ = this.objectCache.getObjectBySelfLink(object.self);
-    return oldVersion$.pipe(take(1), mergeMap((oldVersion: NormalizedObject<T>) => {
+    const oldVersion$ = this.findByHref(object.self);
+    return oldVersion$.pipe(
+      getSucceededRemoteData(),
+      getRemoteDataPayload(),
+      mergeMap((oldVersion: T) => {
         const operations = this.comparator.diff(oldVersion, object);
         if (isNotEmpty(operations)) {
           this.objectCache.addPatch(object.self, operations);
@@ -257,7 +260,6 @@ export abstract class DataService<T extends CacheableObject> {
         return this.findByHref(object.self);
       }
     ));
-
   }
 
   /**
diff --git a/src/app/core/data/external-source.service.spec.ts b/src/app/core/data/external-source.service.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..77a2a85dfd4f548619327275eceb01907172799e
--- /dev/null
+++ b/src/app/core/data/external-source.service.spec.ts
@@ -0,0 +1,76 @@
+import { ExternalSourceService } from './external-source.service';
+import { createPaginatedList, createSuccessfulRemoteDataObject$ } from '../../shared/testing/utils';
+import { ExternalSourceEntry } from '../shared/external-source-entry.model';
+import { of as observableOf } from 'rxjs';
+import { GetRequest } from './request.models';
+
+describe('ExternalSourceService', () => {
+  let service: ExternalSourceService;
+
+  let requestService;
+  let rdbService;
+  let halService;
+
+  const entries = [
+    Object.assign(new ExternalSourceEntry(), {
+      id: '0001-0001-0001-0001',
+      display: 'John Doe',
+      value: 'John, Doe',
+      metadata: {
+        'dc.identifier.uri': [
+          {
+            value: 'https://orcid.org/0001-0001-0001-0001'
+          }
+        ]
+      }
+    }),
+    Object.assign(new ExternalSourceEntry(), {
+      id: '0001-0001-0001-0002',
+      display: 'Sampson Megan',
+      value: 'Sampson, Megan',
+      metadata: {
+        'dc.identifier.uri': [
+          {
+            value: 'https://orcid.org/0001-0001-0001-0002'
+          }
+        ]
+      }
+    })
+  ];
+
+  function init() {
+    requestService = jasmine.createSpyObj('requestService', {
+      generateRequestId: 'request-uuid',
+      configure: {}
+    });
+    rdbService = jasmine.createSpyObj('rdbService', {
+      buildList: createSuccessfulRemoteDataObject$(createPaginatedList(entries))
+    });
+    halService = jasmine.createSpyObj('halService', {
+      getEndpoint: observableOf('external-sources-REST-endpoint')
+    });
+    service = new ExternalSourceService(requestService, rdbService, undefined, undefined, undefined, halService, undefined, undefined, undefined);
+  }
+
+  beforeEach(() => {
+    init();
+  });
+
+  describe('getExternalSourceEntries', () => {
+    let result;
+
+    beforeEach(() => {
+      result = service.getExternalSourceEntries('test');
+    });
+
+    it('should configure a GetRequest', () => {
+      expect(requestService.configure).toHaveBeenCalledWith(jasmine.any(GetRequest));
+    });
+
+    it('should return the entries', () => {
+      result.subscribe((resultRD) => {
+        expect(resultRD.payload.page).toBe(entries);
+      });
+    });
+  });
+});
diff --git a/src/app/core/data/external-source.service.ts b/src/app/core/data/external-source.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c32c13a20fdbe39a0ab853fbcfc534dacb902480
--- /dev/null
+++ b/src/app/core/data/external-source.service.ts
@@ -0,0 +1,85 @@
+import { Injectable } from '@angular/core';
+import { DataService } from './data.service';
+import { ExternalSource } from '../shared/external-source.model';
+import { RequestService } from './request.service';
+import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
+import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service';
+import { Store } from '@ngrx/store';
+import { CoreState } from '../core.reducers';
+import { ObjectCacheService } from '../cache/object-cache.service';
+import { HALEndpointService } from '../shared/hal-endpoint.service';
+import { NotificationsService } from '../../shared/notifications/notifications.service';
+import { HttpClient } from '@angular/common/http';
+import { FindListOptions, GetRequest } from './request.models';
+import { Observable } from 'rxjs/internal/Observable';
+import { distinctUntilChanged, map, switchMap } from 'rxjs/operators';
+import { PaginatedSearchOptions } from '../../shared/search/paginated-search-options.model';
+import { hasValue, isNotEmptyOperator } from '../../shared/empty.util';
+import { configureRequest } from '../shared/operators';
+import { RemoteData } from './remote-data';
+import { PaginatedList } from './paginated-list';
+import { ExternalSourceEntry } from '../shared/external-source-entry.model';
+import { DefaultChangeAnalyzer } from './default-change-analyzer.service';
+
+/**
+ * A service handling all external source requests
+ */
+@Injectable()
+export class ExternalSourceService extends DataService<ExternalSource> {
+  protected linkPath = 'externalsources';
+
+  constructor(
+    protected requestService: RequestService,
+    protected rdbService: RemoteDataBuildService,
+    protected dataBuildService: NormalizedObjectBuildService,
+    protected store: Store<CoreState>,
+    protected objectCache: ObjectCacheService,
+    protected halService: HALEndpointService,
+    protected notificationsService: NotificationsService,
+    protected http: HttpClient,
+    protected comparator: DefaultChangeAnalyzer<ExternalSource>) {
+    super();
+  }
+
+  /**
+   * Get the endpoint to browse external sources
+   * @param options
+   * @param linkPath
+   */
+  getBrowseEndpoint(options: FindListOptions = {}, linkPath: string = this.linkPath): Observable<string> {
+    return this.halService.getEndpoint(linkPath);
+  }
+
+  /**
+   * Get the endpoint for an external source's entries
+   * @param externalSourceId  The id of the external source to fetch entries for
+   */
+  getEntriesEndpoint(externalSourceId: string): Observable<string> {
+    return this.getBrowseEndpoint().pipe(
+      map((href) => this.getIDHref(href, externalSourceId)),
+      switchMap((href) => this.halService.getEndpoint('entries', href))
+    );
+  }
+
+  /**
+   * Get the entries for an external source
+   * @param externalSourceId  The id of the external source to fetch entries for
+   * @param searchOptions     The search options to limit results to
+   */
+  getExternalSourceEntries(externalSourceId: string, searchOptions?: PaginatedSearchOptions): Observable<RemoteData<PaginatedList<ExternalSourceEntry>>> {
+    const requestUuid = this.requestService.generateRequestId();
+
+    const href$ = this.getEntriesEndpoint(externalSourceId).pipe(
+      isNotEmptyOperator(),
+      distinctUntilChanged(),
+      map((endpoint: string) => hasValue(searchOptions) ? searchOptions.toRestUrl(endpoint) : endpoint)
+    );
+
+    href$.pipe(
+      map((endpoint: string) => new GetRequest(requestUuid, endpoint)),
+      configureRequest(this.requestService)
+    ).subscribe();
+
+    return this.rdbService.buildList(href$);
+  }
+}
diff --git a/src/app/core/data/item-data.service.spec.ts b/src/app/core/data/item-data.service.spec.ts
index 6f2719f3741e500626124270f5d5c7322db0336a..8263601e28fd480a85940f930e6c24b8ed0e60c7 100644
--- a/src/app/core/data/item-data.service.spec.ts
+++ b/src/app/core/data/item-data.service.spec.ts
@@ -15,6 +15,7 @@ import { NormalizedObjectBuildService } from '../cache/builders/normalized-objec
 import { HttpClient } from '@angular/common/http';
 import { RequestEntry } from './request.reducer';
 import { getMockRequestService } from '../../shared/mocks/mock-request.service';
+import { ExternalSourceEntry } from '../shared/external-source-entry.model';
 
 describe('ItemDataService', () => {
   let scheduler: TestScheduler;
@@ -194,4 +195,24 @@ describe('ItemDataService', () => {
     });
   });
 
+  describe('importExternalSourceEntry', () => {
+    let result;
+
+    const externalSourceEntry = Object.assign(new ExternalSourceEntry(), {
+      display: 'John, Doe',
+      value: 'John, Doe',
+      self: 'http://test-rest.com/server/api/integration/externalSources/orcidV2/entryValues/0000-0003-4851-8004'
+    });
+
+    beforeEach(() => {
+      service = initTestService();
+      spyOn(requestService, 'configure');
+      result = service.importExternalSourceEntry(externalSourceEntry, 'collection-id');
+    });
+
+    it('should configure a POST request', () => {
+      result.subscribe(() => expect(requestService.configure).toHaveBeenCalledWith(jasmine.any(PostRequest)));
+    });
+  });
+
 });
diff --git a/src/app/core/data/item-data.service.ts b/src/app/core/data/item-data.service.ts
index b729c0fafe0eada6f7713282cb0856038a218702..cd7e70dc32ee1906a2482f4b43eccf3162dc02d3 100644
--- a/src/app/core/data/item-data.service.ts
+++ b/src/app/core/data/item-data.service.ts
@@ -37,6 +37,7 @@ import { HttpOptions } from '../dspace-rest-v2/dspace-rest-v2.service';
 import { Collection } from '../shared/collection.model';
 import { RemoteData } from './remote-data';
 import { PaginatedList } from './paginated-list';
+import { ExternalSourceEntry } from '../shared/external-source-entry.model';
 
 @Injectable()
 export class ItemDataService extends DataService<Item> {
@@ -248,6 +249,40 @@ export class ItemDataService extends DataService<Item> {
     );
   }
 
+  /**
+   * Import an external source entry into a collection
+   * @param externalSourceEntry
+   * @param collectionId
+   */
+  public importExternalSourceEntry(externalSourceEntry: ExternalSourceEntry, collectionId: string): Observable<RemoteData<Item>> {
+    const options: HttpOptions = Object.create({});
+    let headers = new HttpHeaders();
+    headers = headers.append('Content-Type', 'text/uri-list');
+    options.headers = headers;
+
+    const requestId = this.requestService.generateRequestId();
+    const href$ = this.halService.getEndpoint(this.linkPath).pipe(map((href) => `${href}?owningCollection=${collectionId}`));
+
+    href$.pipe(
+      find((href: string) => hasValue(href)),
+      map((href: string) => {
+        const request = new PostRequest(requestId, href, externalSourceEntry.self, options);
+        this.requestService.configure(request);
+      })
+    ).subscribe();
+
+    return this.requestService.getByUUID(requestId).pipe(
+      find((request: RequestEntry) => request.completed),
+      getResponseFromEntry(),
+      map((response: any) => {
+        if (isNotEmpty(response.resourceSelfLinks)) {
+          return response.resourceSelfLinks[0];
+        }
+      }),
+      switchMap((selfLink: string) => this.findByHref(selfLink))
+    );
+  }
+
   /**
    * Get the endpoint for an item's bitstreams
    * @param itemId
diff --git a/src/app/core/data/lookup-relation.service.spec.ts b/src/app/core/data/lookup-relation.service.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c9fc7fc50d1f1aa92741cf70f407fb1c24e4e663
--- /dev/null
+++ b/src/app/core/data/lookup-relation.service.spec.ts
@@ -0,0 +1,132 @@
+import { LookupRelationService } from './lookup-relation.service';
+import { ExternalSourceService } from './external-source.service';
+import { SearchService } from '../shared/search/search.service';
+import { createPaginatedList, createSuccessfulRemoteDataObject$ } from '../../shared/testing/utils';
+import { PaginatedList } from './paginated-list';
+import { PageInfo } from '../shared/page-info.model';
+import { PaginatedSearchOptions } from '../../shared/search/paginated-search-options.model';
+import { RelationshipOptions } from '../../shared/form/builder/models/relationship-options.model';
+import { SearchResult } from '../../shared/search/search-result.model';
+import { Item } from '../shared/item.model';
+import { skip, take } from 'rxjs/operators';
+import { ExternalSource } from '../shared/external-source.model';
+import { RequestService } from './request.service';
+import { of as observableOf } from 'rxjs';
+
+describe('LookupRelationService', () => {
+  let service: LookupRelationService;
+  let externalSourceService: ExternalSourceService;
+  let searchService: SearchService;
+  let requestService: RequestService;
+
+  const totalExternal = 8;
+  const optionsWithQuery = new PaginatedSearchOptions({ query: 'test-query' });
+  const relationship = Object.assign(new RelationshipOptions(), {
+    filter: 'test-filter',
+    configuration: 'test-configuration'
+  });
+  const localResults = [
+    Object.assign(new SearchResult(), {
+      indexableObject: Object.assign(new Item(), {
+        uuid: 'test-item-uuid',
+        handle: 'test-item-handle'
+      })
+    })
+  ];
+  const externalSource = Object.assign(new ExternalSource(), {
+    id: 'orcidV2',
+    name: 'orcidV2',
+    hierarchical: false
+  });
+  const searchServiceEndpoint = 'http://test-rest.com/server/api/core/search';
+
+  function init() {
+    externalSourceService = jasmine.createSpyObj('externalSourceService', {
+      getExternalSourceEntries: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo({ elementsPerPage: 1, totalElements: totalExternal, totalPages: totalExternal, currentPage: 1 }), [{}]))
+    });
+    searchService = jasmine.createSpyObj('searchService', {
+      search: createSuccessfulRemoteDataObject$(createPaginatedList(localResults)),
+      getEndpoint: observableOf(searchServiceEndpoint)
+    });
+    requestService = jasmine.createSpyObj('requestService', ['removeByHrefSubstring']);
+    service = new LookupRelationService(externalSourceService, searchService, requestService);
+  }
+
+  beforeEach(() => {
+    init();
+  });
+
+  describe('getLocalResults', () => {
+    let result;
+
+    beforeEach(() => {
+      result = service.getLocalResults(relationship, optionsWithQuery);
+    });
+
+    it('should return the local results', () => {
+      result.subscribe((resultsRD) => {
+        expect(resultsRD.payload.page).toBe(localResults);
+      });
+    });
+
+    it('should set the searchConfig to contain a fixedFilter and configuration', () => {
+      expect(service.searchConfig).toEqual(Object.assign(new PaginatedSearchOptions({}), optionsWithQuery,
+        { fixedFilter: relationship.filter, configuration: relationship.searchConfiguration }
+      ));
+    });
+  });
+
+  describe('getTotalLocalResults', () => {
+    let result;
+
+    beforeEach(() => {
+      result = service.getTotalLocalResults(relationship, optionsWithQuery);
+    });
+
+    it('should start with 0', () => {
+      result.pipe(take(1)).subscribe((amount) => {
+        expect(amount).toEqual(0)
+      });
+    });
+
+    it('should return the correct total amount', () => {
+      result.pipe(skip(1)).subscribe((amount) => {
+        expect(amount).toEqual(localResults.length)
+      });
+    });
+
+    it('should not set searchConfig', () => {
+      expect(service.searchConfig).toBeUndefined();
+    });
+  });
+
+  describe('getTotalExternalResults', () => {
+    let result;
+
+    beforeEach(() => {
+      result = service.getTotalExternalResults(externalSource, optionsWithQuery);
+    });
+
+    it('should start with 0', () => {
+      result.pipe(take(1)).subscribe((amount) => {
+        expect(amount).toEqual(0)
+      });
+    });
+
+    it('should return the correct total amount', () => {
+      result.pipe(skip(1)).subscribe((amount) => {
+        expect(amount).toEqual(totalExternal)
+      });
+    });
+  });
+
+  describe('removeLocalResultsCache', () => {
+    beforeEach(() => {
+      service.removeLocalResultsCache();
+    });
+
+    it('should call requestService\'s removeByHrefSubstring with the search endpoint', () => {
+      expect(requestService.removeByHrefSubstring).toHaveBeenCalledWith(searchServiceEndpoint);
+    });
+  });
+});
diff --git a/src/app/core/data/lookup-relation.service.ts b/src/app/core/data/lookup-relation.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..395976cbc3990d885d9918ca58adef8f0485b80f
--- /dev/null
+++ b/src/app/core/data/lookup-relation.service.ts
@@ -0,0 +1,103 @@
+import { ExternalSourceService } from './external-source.service';
+import { SearchService } from '../shared/search/search.service';
+import { concat, map, multicast, startWith, take, takeWhile } from 'rxjs/operators';
+import { PaginatedSearchOptions } from '../../shared/search/paginated-search-options.model';
+import { ReplaySubject } from 'rxjs/internal/ReplaySubject';
+import { RemoteData } from './remote-data';
+import { PaginatedList } from './paginated-list';
+import { SearchResult } from '../../shared/search/search-result.model';
+import { DSpaceObject } from '../shared/dspace-object.model';
+import { RelationshipOptions } from '../../shared/form/builder/models/relationship-options.model';
+import { Observable } from 'rxjs/internal/Observable';
+import { Item } from '../shared/item.model';
+import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
+import { getAllSucceededRemoteData, getRemoteDataPayload } from '../shared/operators';
+import { Injectable } from '@angular/core';
+import { ExternalSource } from '../shared/external-source.model';
+import { ExternalSourceEntry } from '../shared/external-source-entry.model';
+import { RequestService } from './request.service';
+
+/**
+ * A service for retrieving local and external entries information during a relation lookup
+ */
+@Injectable()
+export class LookupRelationService {
+  /**
+   * The search config last used for retrieving local results
+   */
+  public searchConfig: PaginatedSearchOptions;
+
+  /**
+   * Pagination options for retrieving exactly one result
+   */
+  private singleResultOptions = Object.assign(new PaginationComponentOptions(), {
+    id: 'single-result-options',
+    pageSize: 1
+  });
+
+  constructor(protected externalSourceService: ExternalSourceService,
+              protected searchService: SearchService,
+              protected requestService: RequestService) {
+  }
+
+  /**
+   * Retrieve the available local entries for a relationship
+   * @param relationship    Relationship options
+   * @param searchOptions   Search options to filter results
+   * @param setSearchConfig Optionally choose if we should store the used search config in a local variable (defaults to true)
+   */
+  getLocalResults(relationship: RelationshipOptions, searchOptions: PaginatedSearchOptions, setSearchConfig = true): Observable<RemoteData<PaginatedList<SearchResult<Item>>>> {
+    const newConfig = Object.assign(new PaginatedSearchOptions({}), searchOptions,
+      { fixedFilter: relationship.filter, configuration: relationship.searchConfiguration }
+    );
+    if (setSearchConfig) {
+      this.searchConfig = newConfig;
+    }
+    return this.searchService.search(newConfig).pipe(
+      /* Make sure to only listen to the first x results, until loading is finished */
+      /* TODO: in Rxjs 6.4.0 and up, we can replace this with takeWhile(predicate, true) - see https://stackoverflow.com/a/44644237 */
+      multicast(
+        () => new ReplaySubject(1),
+        (subject) => subject.pipe(
+          takeWhile((rd: RemoteData<PaginatedList<SearchResult<DSpaceObject>>>) => rd.isLoading),
+          concat(subject.pipe(take(1)))
+        )
+      ) as any
+    ) as Observable<RemoteData<PaginatedList<SearchResult<Item>>>>;
+  }
+
+  /**
+   * Calculate the total local entries available for the given relationship
+   * @param relationship  Relationship options
+   * @param searchOptions Search options to filter results
+   */
+  getTotalLocalResults(relationship: RelationshipOptions, searchOptions: PaginatedSearchOptions): Observable<number> {
+    return this.getLocalResults(relationship, Object.assign(new PaginatedSearchOptions({}), searchOptions, { pagination: this.singleResultOptions }), false).pipe(
+      getAllSucceededRemoteData(),
+      getRemoteDataPayload(),
+      map((results: PaginatedList<SearchResult<Item>>) => results.totalElements),
+      startWith(0)
+    );
+  }
+
+  /**
+   * Calculate the total external entries available for a given external source
+   * @param externalSource  External Source
+   * @param searchOptions   Search options to filter results
+   */
+  getTotalExternalResults(externalSource: ExternalSource, searchOptions: PaginatedSearchOptions): Observable<number> {
+    return this.externalSourceService.getExternalSourceEntries(externalSource.id, Object.assign(new PaginatedSearchOptions({}), searchOptions, { pagination: this.singleResultOptions })).pipe(
+      getAllSucceededRemoteData(),
+      getRemoteDataPayload(),
+      map((results: PaginatedList<ExternalSourceEntry>) => results.totalElements),
+      startWith(0)
+    );
+  }
+
+  /**
+   * Remove cached requests from local results
+   */
+  removeLocalResultsCache() {
+    this.searchService.getEndpoint().subscribe((href) => this.requestService.removeByHrefSubstring(href));
+  }
+}
diff --git a/src/app/core/data/object-updates/object-updates.service.ts b/src/app/core/data/object-updates/object-updates.service.ts
index 5872a500f7957d16f5d9c634fb1099a99c763e8c..367b73ee30f6bfe1eab574d6ae3074d5fbcc47a1 100644
--- a/src/app/core/data/object-updates/object-updates.service.ts
+++ b/src/app/core/data/object-updates/object-updates.service.ts
@@ -1,7 +1,7 @@
-import {Injectable} from '@angular/core';
-import {createSelector, MemoizedSelector, select, Store} from '@ngrx/store';
-import {CoreState} from '../../core.reducers';
-import {coreSelector} from '../../core.selectors';
+import { Injectable } from '@angular/core';
+import { createSelector, MemoizedSelector, select, Store } from '@ngrx/store';
+import { CoreState } from '../../core.reducers';
+import { coreSelector } from '../../core.selectors';
 import {
   FieldState,
   FieldUpdates,
@@ -11,7 +11,7 @@ import {
   ObjectUpdatesState,
   VirtualMetadataSource
 } from './object-updates.reducer';
-import {Observable} from 'rxjs';
+import { Observable } from 'rxjs';
 import {
   AddFieldUpdateAction,
   DiscardObjectUpdatesAction,
@@ -23,9 +23,9 @@ import {
   SetEditableFieldUpdateAction,
   SetValidFieldUpdateAction
 } from './object-updates.actions';
-import {distinctUntilChanged, filter, map, switchMap} from 'rxjs/operators';
-import {hasNoValue, hasValue, isEmpty, isNotEmpty} from '../../../shared/empty.util';
-import {INotification} from '../../../shared/notifications/models/notification.model';
+import { distinctUntilChanged, filter, map, switchMap } from 'rxjs/operators';
+import { hasNoValue, hasValue, isEmpty, isNotEmpty } from '../../../shared/empty.util';
+import { INotification } from '../../../shared/notifications/models/notification.model';
 
 function objectUpdatesStateSelector(): MemoizedSelector<CoreState, ObjectUpdatesState> {
   return createSelector(coreSelector, (state: CoreState) => state['cache/object-updates']);
@@ -100,9 +100,11 @@ export class ObjectUpdatesService {
     return objectUpdates.pipe(
       switchMap((objectEntry) => {
         const fieldUpdates: FieldUpdates = {};
-        Object.keys(objectEntry.fieldStates).forEach((uuid) => {
-          fieldUpdates[uuid] = objectEntry.fieldUpdates[uuid];
-        });
+        if (hasValue(objectEntry)) {
+          Object.keys(objectEntry.fieldStates).forEach((uuid) => {
+            fieldUpdates[uuid] = objectEntry.fieldUpdates[uuid];
+          });
+        }
         return this.getFieldUpdatesExclusive(url, initialFields).pipe(
           map((fieldUpdatesExclusive) => {
             Object.keys(fieldUpdatesExclusive).forEach((uuid) => {
diff --git a/src/app/core/data/relationship.service.spec.ts b/src/app/core/data/relationship.service.spec.ts
index 64d270f417398ec34d1c49a64c097c2b5fb01176..99442da58d04dd47e99b49f31ad7d33224783ca3 100644
--- a/src/app/core/data/relationship.service.spec.ts
+++ b/src/app/core/data/relationship.service.spec.ts
@@ -54,10 +54,12 @@ describe('RelationshipService', () => {
   });
 
   const relatedItem1 = Object.assign(new Item(), {
+    self: 'fake-item-url/author1',
     id: 'author1',
     uuid: 'author1'
   });
   const relatedItem2 = Object.assign(new Item(), {
+    self: 'fake-item-url/author2',
     id: 'author2',
     uuid: 'author2'
   });
@@ -120,7 +122,7 @@ describe('RelationshipService', () => {
       expect(requestService.configure).toHaveBeenCalledWith(expected);
     });
 
-    it('should clear the related items their cache', () => {
+    it('should clear the cache of the related items', () => {
       expect(objectCache.remove).toHaveBeenCalledWith(relatedItem1.self);
       expect(objectCache.remove).toHaveBeenCalledWith(item.self);
       expect(requestService.removeByHrefSubstring).toHaveBeenCalledWith(relatedItem1.self);
diff --git a/src/app/core/data/relationship.service.ts b/src/app/core/data/relationship.service.ts
index 0def17b2aed70c099dcdbdd7e9e3f3585910b22c..db8f40168702df01ea5404b65a4c1c6c87cdf3ae 100644
--- a/src/app/core/data/relationship.service.ts
+++ b/src/app/core/data/relationship.service.ts
@@ -1,46 +1,35 @@
+import { HttpClient, HttpHeaders } from '@angular/common/http';
 import { Injectable } from '@angular/core';
-import { RequestService } from './request.service';
-import { HALEndpointService } from '../shared/hal-endpoint.service';
-import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
-import { hasValue, hasValueOperator, isNotEmpty, isNotEmptyOperator } from '../../shared/empty.util';
+import { MemoizedSelector, select, Store } from '@ngrx/store';
+import { combineLatest, combineLatest as observableCombineLatest } from 'rxjs';
 import { distinctUntilChanged, filter, map, mergeMap, startWith, switchMap, take, tap } from 'rxjs/operators';
-import {
-  configureRequest,
-  getRemoteDataPayload,
-  getResponseFromEntry,
-  getSucceededRemoteData
-} from '../shared/operators';
+import { compareArraysUsingIds, paginatedRelationsToItems, relationsToItems } from '../../+item-page/simple/item-types/shared/item-relationships-utils';
+import { AppState, keySelector } from '../../app.reducer';
+import { hasValue, hasValueOperator, isNotEmpty, isNotEmptyOperator } from '../../shared/empty.util';
+import { ReorderableRelationship } from '../../shared/form/builder/ds-dynamic-form-ui/existing-metadata-list-element/existing-metadata-list-element.component';
+import { RemoveNameVariantAction, SetNameVariantAction } from '../../shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/name-variant.actions';
+import { NameVariantListState } from '../../shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/name-variant.reducer';
+import { NotificationsService } from '../../shared/notifications/notifications.service';
+import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service';
+import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
+import { configureRequest, getRemoteDataPayload, getResponseFromEntry, getSucceededRemoteData } from '../shared/operators';
+import { SearchParam } from '../cache/models/search-param.model';
+import { ObjectCacheService } from '../cache/object-cache.service';
 import { DeleteRequest, FindListOptions, PostRequest, RestRequest } from './request.models';
-import { Observable } from 'rxjs/internal/Observable';
 import { RestResponse } from '../cache/response.models';
-import { Item } from '../shared/item.model';
-import { Relationship } from '../shared/item-relationships/relationship.model';
+import { CoreState } from '../core.reducers';
+import { HttpOptions } from '../dspace-rest-v2/dspace-rest-v2.service';
+import { HALEndpointService } from '../shared/hal-endpoint.service';
 import { RelationshipType } from '../shared/item-relationships/relationship-type.model';
-import { RemoteData } from './remote-data';
-import { combineLatest, combineLatest as observableCombineLatest } from 'rxjs';
+import { RemoteData, RemoteDataState } from './remote-data';
 import { PaginatedList } from './paginated-list';
 import { ItemDataService } from './item-data.service';
-import {
-  compareArraysUsingIds,
-  paginatedRelationsToItems,
-  relationsToItems
-} from '../../+item-page/simple/item-types/shared/item-relationships-utils';
-import { ObjectCacheService } from '../cache/object-cache.service';
+import { Relationship } from '../shared/item-relationships/relationship.model';
+import { Item } from '../shared/item.model';
 import { DataService } from './data.service';
-import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service';
-import { MemoizedSelector, select, Store } from '@ngrx/store';
-import { CoreState } from '../core.reducers';
-import { NotificationsService } from '../../shared/notifications/notifications.service';
-import { HttpClient, HttpHeaders } from '@angular/common/http';
 import { DefaultChangeAnalyzer } from './default-change-analyzer.service';
-import { SearchParam } from '../cache/models/search-param.model';
-import { HttpOptions } from '../dspace-rest-v2/dspace-rest-v2.service';
-import { AppState, keySelector } from '../../app.reducer';
-import { NameVariantListState } from '../../shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/name-variant.reducer';
-import {
-  RemoveNameVariantAction,
-  SetNameVariantAction
-} from '../../shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/name-variant.actions';
+import { RequestService } from './request.service';
+import { Observable } from 'rxjs/internal/Observable';
 
 const relationshipListsStateSelector = (state: AppState) => state.relationshipLists;
 
@@ -147,9 +136,9 @@ export class RelationshipService extends DataService<Relationship> {
     this.findById(relationshipId).pipe(
       getSucceededRemoteData(),
       getRemoteDataPayload(),
-      switchMap((relationship: Relationship) => combineLatest(
-        relationship.leftItem.pipe(getSucceededRemoteData(), getRemoteDataPayload()),
-        relationship.rightItem.pipe(getSucceededRemoteData(), getRemoteDataPayload())
+      switchMap((rel: Relationship) => combineLatest(
+        rel.leftItem.pipe(getSucceededRemoteData(), getRemoteDataPayload()),
+        rel.rightItem.pipe(getSucceededRemoteData(), getRemoteDataPayload())
         )
       ),
       take(1)
@@ -165,10 +154,10 @@ export class RelationshipService extends DataService<Relationship> {
    */
   private removeRelationshipItemsFromCache(item) {
     this.objectCache.remove(item.self);
-    this.requestService.removeByHrefSubstring(item.self);
+    this.requestService.removeByHrefSubstring(item.uuid);
     combineLatest(
       this.objectCache.hasBySelfLinkObservable(item.self),
-      this.requestService.hasByHrefObservable(item.self)
+      this.requestService.hasByHrefObservable(item.uuid)
     ).pipe(
       filter(([existsInOC, existsInRC]) => !existsInOC && !existsInRC),
       take(1),
@@ -374,7 +363,7 @@ export class RelationshipService extends DataService<Relationship> {
    * @param nameVariant The name variant to set for the matching relationship
    */
   public updateNameVariant(item1: Item, item2: Item, relationshipLabel: string, nameVariant: string): Observable<RemoteData<Relationship>> {
-    return this.getRelationshipByItemsAndLabel(item1, item2, relationshipLabel)
+    const update$: Observable<RemoteData<Relationship>> = this.getRelationshipByItemsAndLabel(item1, item2, relationshipLabel)
       .pipe(
         switchMap((relation: Relationship) =>
           relation.relationshipType.pipe(
@@ -395,14 +384,44 @@ export class RelationshipService extends DataService<Relationship> {
           }
           return this.update(updatedRelationship);
         }),
-        // skipWhile((relationshipRD: RemoteData<Relationship>) => !relationshipRD.isSuccessful)
-        tap((relationshipRD: RemoteData<Relationship>) => {
-          if (relationshipRD.hasSucceeded) {
-            this.removeRelationshipItemsFromCache(item1);
-            this.removeRelationshipItemsFromCache(item2);
-          }
-        }),
-      )
+      );
+
+    update$.pipe(
+      filter((relationshipRD: RemoteData<Relationship>) => relationshipRD.state === RemoteDataState.RequestPending),
+      take(1),
+    ).subscribe(() => {
+      this.removeRelationshipItemsFromCache(item1);
+      this.removeRelationshipItemsFromCache(item2);
+    });
+
+    return update$
+  }
+
+  /**
+   * Method to update the the right or left place of a relationship
+   * The useLeftItem field in the reorderable relationship determines which place should be updated
+   * @param reoRel
+   */
+  public updatePlace(reoRel: ReorderableRelationship): Observable<RemoteData<Relationship>> {
+    let updatedRelationship;
+    if (reoRel.useLeftItem) {
+      updatedRelationship = Object.assign(new Relationship(), reoRel.relationship, { rightPlace: reoRel.newIndex });
+    } else {
+      updatedRelationship = Object.assign(new Relationship(), reoRel.relationship, { leftPlace: reoRel.newIndex });
+    }
+
+    const update$ = this.update(updatedRelationship);
+
+    update$.pipe(
+      filter((relationshipRD: RemoteData<Relationship>) => relationshipRD.state === RemoteDataState.ResponsePending),
+      take(1),
+    ).subscribe((relationshipRD: RemoteData<Relationship>) => {
+      if (relationshipRD.state === RemoteDataState.ResponsePending) {
+        this.removeRelationshipItemsFromCacheByRelationship(reoRel.relationship.id);
+      }
+    });
+
+    return update$;
   }
 
   /**
diff --git a/src/app/core/data/request.models.ts b/src/app/core/data/request.models.ts
index ca864f99dee71f9c33f1930dc02a616992c1783f..2305fc2d5d30fdb61ada6118e38dc310f9a4cac5 100644
--- a/src/app/core/data/request.models.ts
+++ b/src/app/core/data/request.models.ts
@@ -18,6 +18,7 @@ import { MetadataschemaParsingService } from './metadataschema-parsing.service';
 import { MetadatafieldParsingService } from './metadatafield-parsing.service';
 import { URLCombiner } from '../url-combiner/url-combiner';
 import { TaskResponseParsingService } from '../tasks/task-response-parsing.service';
+import { ContentSourceResponseParsingService } from './content-source-response-parsing.service';
 import { MappedCollectionsReponseParsingService } from './mapped-collections-reponse-parsing.service';
 
 /* tslint:disable:max-classes-per-file */
@@ -378,6 +379,26 @@ export class CreateRequest extends PostRequest {
   }
 }
 
+export class ContentSourceRequest extends GetRequest {
+  constructor(uuid: string, href: string) {
+    super(uuid, href);
+  }
+
+  getResponseParser(): GenericConstructor<ResponseParsingService> {
+    return ContentSourceResponseParsingService;
+  }
+}
+
+export class UpdateContentSourceRequest extends PutRequest {
+  constructor(uuid: string, href: string, public body?: any, public options?: HttpOptions) {
+    super(uuid, href, body, options);
+  }
+
+  getResponseParser(): GenericConstructor<ResponseParsingService> {
+    return ContentSourceResponseParsingService;
+  }
+}
+
 /**
  * Request to delete an object based on its identifier
  */
diff --git a/src/app/core/data/site-data.service.spec.ts b/src/app/core/data/site-data.service.spec.ts
index 09fa7fb457b0ea462b75f648a8620e80b0aba9d1..6148135f50ab269183f9bae94942c8840172a3ab 100644
--- a/src/app/core/data/site-data.service.spec.ts
+++ b/src/app/core/data/site-data.service.spec.ts
@@ -19,12 +19,12 @@ import { PaginatedList } from './paginated-list';
 import { RemoteData } from './remote-data';
 
 describe('SiteDataService', () => {
-  let scheduler:TestScheduler;
-  let service:SiteDataService;
-  let halService:HALEndpointService;
-  let requestService:RequestService;
-  let rdbService:RemoteDataBuildService;
-  let objectCache:ObjectCacheService;
+  let scheduler: TestScheduler;
+  let service: SiteDataService;
+  let halService: HALEndpointService;
+  let requestService: RequestService;
+  let rdbService: RemoteDataBuildService;
+  let objectCache: ObjectCacheService;
 
   const testObject = Object.assign(new Site(), {
     uuid: '9b4f22f4-164a-49db-8817-3316b6ee5746',
@@ -33,7 +33,7 @@ describe('SiteDataService', () => {
   const requestUUID = '34cfed7c-f597-49ef-9cbe-ea351f0023c2';
   const options = Object.assign(new FindListOptions(), {});
 
-  const getRequestEntry$ = (successful:boolean, statusCode:number, statusText:string) => {
+  const getRequestEntry$ = (successful: boolean, statusCode: number, statusText: string) => {
     return observableOf({
       response: new RestResponse(successful, statusCode, statusText)
     } as RequestEntry);
diff --git a/src/app/core/data/site-data.service.ts b/src/app/core/data/site-data.service.ts
index 7550594cda47f60abe99b4c8a54a1c15a8c7629d..c1a1b2069bdef7ffbc701f6dfaedb8526a578341 100644
--- a/src/app/core/data/site-data.service.ts
+++ b/src/app/core/data/site-data.service.ts
@@ -22,47 +22,41 @@ import { getSucceededRemoteData } from '../shared/operators';
  * Service responsible for handling requests related to the Site object
  */
 @Injectable()
-export class SiteDataService extends DataService<Site> {
-​
+export class SiteDataService extends DataService<Site> {​
   protected linkPath = 'sites';
   protected forceBypassCache = false;
-​
 
   constructor(
-    protected requestService:RequestService,
-    protected rdbService:RemoteDataBuildService,
-    protected dataBuildService:NormalizedObjectBuildService,
-    protected store:Store<CoreState>,
-    protected objectCache:ObjectCacheService,
-    protected halService:HALEndpointService,
-    protected notificationsService:NotificationsService,
-    protected http:HttpClient,
-    protected comparator:DSOChangeAnalyzer<Site>,
+    protected requestService: RequestService,
+    protected rdbService: RemoteDataBuildService,
+    protected dataBuildService: NormalizedObjectBuildService,
+    protected store: Store<CoreState>,
+    protected objectCache: ObjectCacheService,
+    protected halService: HALEndpointService,
+    protected notificationsService: NotificationsService,
+    protected http: HttpClient,
+    protected comparator: DSOChangeAnalyzer<Site>,
   ) {
     super();
   }
 
-​
-
   /**
    * Get the endpoint for browsing the site object
    * @param {FindListOptions} options
    * @param {Observable<string>} linkPath
    */
-  getBrowseEndpoint(options:FindListOptions, linkPath?:string):Observable<string> {
+  getBrowseEndpoint(options: FindListOptions, linkPath?: string): Observable<string> {
     return this.halService.getEndpoint(this.linkPath);
   }
 
-​
-
   /**
    * Retrieve the Site Object
    */
-  find():Observable<Site> {
+  find(): Observable<Site> {
     return this.findAll().pipe(
       getSucceededRemoteData(),
-      map((remoteData:RemoteData<PaginatedList<Site>>) => remoteData.payload),
-      map((list:PaginatedList<Site>) => list.page[0])
+      map((remoteData: RemoteData<PaginatedList<Site>>) => remoteData.payload),
+      map((list: PaginatedList<Site>) => list.page[0])
     );
   }
 }
diff --git a/src/app/core/history/selectors.ts b/src/app/core/history/selectors.ts
index a04d3839b147c2a3b715952be273fa0567f15fab..5c77cd65f0f63f4a1e7de468c717b242ad3d9cae 100644
--- a/src/app/core/history/selectors.ts
+++ b/src/app/core/history/selectors.ts
@@ -1,3 +1,8 @@
 import { CoreState } from '../core.reducers';
+import { createSelector } from '@ngrx/store';
+import { coreSelector } from '../core.selectors';
 
-export const historySelector = (state: CoreState) => state.history;
+export const historySelector = createSelector(
+  coreSelector,
+  (state: CoreState) => state.history
+);
diff --git a/src/app/core/services/route.service.spec.ts b/src/app/core/services/route.service.spec.ts
index 525329d50ff0a447bcdd79a5074bc1d768d8dc39..07ff56d879f8b196dc4ea65ccd5f177ca270b28d 100644
--- a/src/app/core/services/route.service.spec.ts
+++ b/src/app/core/services/route.service.spec.ts
@@ -142,7 +142,11 @@ describe('RouteService', () => {
 
   describe('getHistory', () => {
     it('should dispatch AddUrlToHistoryAction on NavigationEnd event', () => {
-      serviceAsAny.store = observableOf({ history: ['url', 'newurl'] });
+      serviceAsAny.store = observableOf({
+        core: {
+          history: ['url', 'newurl']
+        }
+      });
 
       service.getHistory().subscribe((history) => {
         expect(history).toEqual(['url', 'newurl']);
diff --git a/src/app/core/services/route.service.ts b/src/app/core/services/route.service.ts
index 2a0df6d16a72795c4f9c9df1ab4b9a308bc21495..661f4acf94b7b1f30fc1790f902cb14e113a72bf 100644
--- a/src/app/core/services/route.service.ts
+++ b/src/app/core/services/route.service.ts
@@ -176,10 +176,20 @@ export class RouteService {
     );
   }
 
+  /**
+   * Add a parameter to the current route
+   * @param key   The parameter name
+   * @param value The parameter value
+   */
   public addParameter(key, value) {
     this.store.dispatch(new AddParameterAction(key, value));
   }
 
+  /**
+   * Set a parameter in the current route (overriding the previous value)
+   * @param key   The parameter name
+   * @param value The parameter value
+   */
   public setParameter(key, value) {
     this.store.dispatch(new SetParameterAction(key, value));
   }
diff --git a/src/app/core/shared/content-source.model.ts b/src/app/core/shared/content-source.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..cd53c2d81e62d51b652b833ba9bcc83305de05a6
--- /dev/null
+++ b/src/app/core/shared/content-source.model.ts
@@ -0,0 +1,60 @@
+import { autoserialize, autoserializeAs, deserializeAs, deserialize } from 'cerialize';
+import { MetadataConfig } from './metadata-config.model';
+
+/**
+ * The type of content harvesting used
+ */
+export enum ContentSourceHarvestType {
+  None = 'NONE',
+  Metadata = 'METADATA_ONLY',
+  MetadataAndRef = 'METADATA_AND_REF',
+  MetadataAndBitstreams = 'METADATA_AND_BITSTREAMS'
+}
+
+/**
+ * A model class that holds information about the Content Source of a Collection
+ */
+export class ContentSource {
+  /**
+   * Unique identifier, this is necessary to store the ContentSource in FieldUpdates
+   * Because the ContentSource coming from the REST API doesn't have a UUID, we're using the selflink
+   */
+  @deserializeAs('self')
+  uuid: string;
+
+  /**
+   * OAI Provider / Source
+   */
+  @autoserializeAs('oai_source')
+  oaiSource: string;
+
+  /**
+   * OAI Specific set ID
+   */
+  @autoserializeAs('oai_set_id')
+  oaiSetId: string;
+
+  /**
+   * The ID of the metadata format used
+   */
+  @autoserializeAs('metadata_config_id')
+  metadataConfigId: string;
+
+  /**
+   * Type of content being harvested
+   * Defaults to 'NONE', meaning the collection doesn't harvest its content from an external source
+   */
+  @autoserializeAs('harvest_type')
+  harvestType = ContentSourceHarvestType.None;
+
+  /**
+   * The available metadata configurations
+   */
+  metadataConfigs: MetadataConfig[];
+
+  /**
+   * The REST link to itself
+   */
+  @deserialize
+  self: string;
+}
diff --git a/src/app/core/shared/external-source-entry.model.ts b/src/app/core/shared/external-source-entry.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2451aa4d24afedce050ce252e5f93cbac885643c
--- /dev/null
+++ b/src/app/core/shared/external-source-entry.model.ts
@@ -0,0 +1,48 @@
+import { MetadataMap } from './metadata.models';
+import { ResourceType } from './resource-type';
+import { ListableObject } from '../../shared/object-collection/shared/listable-object.model';
+import { GenericConstructor } from './generic-constructor';
+
+/**
+ * Model class for a single entry from an external source
+ */
+export class ExternalSourceEntry extends ListableObject {
+  static type = new ResourceType('externalSourceEntry');
+
+  /**
+   * Unique identifier
+   */
+  id: string;
+
+  /**
+   * The value to display
+   */
+  display: string;
+
+  /**
+   * The value to store the entry with
+   */
+  value: string;
+
+  /**
+   * The ID of the external source this entry originates from
+   */
+  externalSource: string;
+
+  /**
+   * Metadata of the entry
+   */
+  metadata: MetadataMap;
+
+  /**
+   * The link to the rest endpoint where this External Source Entry can be found
+   */
+  self: string;
+
+  /**
+   * Method that returns as which type of object this object should be rendered
+   */
+  getRenderTypes(): Array<string | GenericConstructor<ListableObject>> {
+    return [this.constructor as GenericConstructor<ListableObject>];
+  }
+}
diff --git a/src/app/core/shared/external-source.model.ts b/src/app/core/shared/external-source.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a158f18f5d6fefdc65a0ed15ff93f614d7992e46
--- /dev/null
+++ b/src/app/core/shared/external-source.model.ts
@@ -0,0 +1,29 @@
+import { ResourceType } from './resource-type';
+import { CacheableObject } from '../cache/object-cache.reducer';
+
+/**
+ * Model class for an external source
+ */
+export class ExternalSource extends CacheableObject {
+  static type = new ResourceType('externalsource');
+
+  /**
+   * Unique identifier
+   */
+  id: string;
+
+  /**
+   * The name of this external source
+   */
+  name: string;
+
+  /**
+   * Is the source hierarchical?
+   */
+  hierarchical: boolean;
+
+  /**
+   * The link to the rest endpoint where this External Source can be found
+   */
+  self: string;
+}
diff --git a/src/app/core/shared/metadata-config.model.ts b/src/app/core/shared/metadata-config.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..861d04586ef2bfc21056edcce04adc7f32056025
--- /dev/null
+++ b/src/app/core/shared/metadata-config.model.ts
@@ -0,0 +1,19 @@
+/**
+ * A model class that holds information about a certain metadata configuration
+ */
+export class MetadataConfig {
+  /**
+   * A unique indentifier
+   */
+  id: string;
+
+  /**
+   * The label used for display
+   */
+  label: string;
+
+  /**
+   * The namespace of the metadata
+   */
+  nameSpace: string;
+}
diff --git a/src/app/core/shared/search/search.service.ts b/src/app/core/shared/search/search.service.ts
index f6886c268ee43ef737cebbbb7ededa2078a12e5b..141f261990f2cd7316fa1266aeae44a5efc147bd 100644
--- a/src/app/core/shared/search/search.service.ts
+++ b/src/app/core/shared/search/search.service.ts
@@ -89,9 +89,9 @@ export class SearchService implements OnDestroy {
     }
   }
 
-  getEndpoint(searchOptions?:PaginatedSearchOptions):Observable<string> {
+  getEndpoint(searchOptions?: PaginatedSearchOptions): Observable<string> {
     return this.halService.getEndpoint(this.searchLinkPath).pipe(
-      map((url:string) => {
+      map((url: string) => {
         if (hasValue(searchOptions)) {
           return (searchOptions as PaginatedSearchOptions).toRestUrl(url);
         } else {
@@ -117,16 +117,15 @@ export class SearchService implements OnDestroy {
    * @param responseMsToLive The amount of milliseconds for the response to live in cache
    * @returns {Observable<RequestEntry>} Emits an observable with the request entries
    */
-  searchEntries(searchOptions?: PaginatedSearchOptions, responseMsToLive?:number)
-    :Observable<{searchOptions: PaginatedSearchOptions, requestEntry: RequestEntry}> {
+  searchEntries(searchOptions?: PaginatedSearchOptions, responseMsToLive?: number): Observable<{searchOptions: PaginatedSearchOptions, requestEntry: RequestEntry}> {
 
     const hrefObs = this.getEndpoint(searchOptions);
 
     const requestObs = hrefObs.pipe(
-      map((url:string) => {
+      map((url: string) => {
         const request = new this.request(this.requestService.generateRequestId(), url);
 
-        const getResponseParserFn:() => GenericConstructor<ResponseParsingService> = () => {
+        const getResponseParserFn: () => GenericConstructor<ResponseParsingService> = () => {
           return this.parser;
         };
 
@@ -139,8 +138,8 @@ export class SearchService implements OnDestroy {
       configureRequest(this.requestService),
     );
     return requestObs.pipe(
-      switchMap((request:RestRequest) => this.requestService.getByHref(request.href)),
-      map(((requestEntry:RequestEntry) => ({
+      switchMap((request: RestRequest) => this.requestService.getByHref(request.href)),
+      map(((requestEntry: RequestEntry) => ({
         searchOptions: searchOptions,
         requestEntry: requestEntry
       })))
@@ -152,16 +151,15 @@ export class SearchService implements OnDestroy {
    * @param searchEntries: The request entries from the search method
    * @returns {Observable<RemoteData<PaginatedList<SearchResult<DSpaceObject>>>>} Emits a paginated list with all search results found
    */
-  getPaginatedResults(searchEntries:Observable<{ searchOptions:PaginatedSearchOptions, requestEntry:RequestEntry }>)
-    :Observable<RemoteData<PaginatedList<SearchResult<DSpaceObject>>>> {
-    const requestEntryObs:Observable<RequestEntry> = searchEntries.pipe(
+  getPaginatedResults(searchEntries: Observable<{ searchOptions: PaginatedSearchOptions, requestEntry: RequestEntry }>): Observable<RemoteData<PaginatedList<SearchResult<DSpaceObject>>>> {
+    const requestEntryObs: Observable<RequestEntry> = searchEntries.pipe(
       map((entry) => entry.requestEntry),
     );
 
     // get search results from response cache
-    const sqrObs:Observable<SearchQueryResponse> = requestEntryObs.pipe(
+    const sqrObs: Observable<SearchQueryResponse> = requestEntryObs.pipe(
       filterSuccessfulResponses(),
-      map((response:SearchSuccessResponse) => response.results),
+      map((response: SearchSuccessResponse) => response.results),
     );
 
     // turn dspace href from search results to effective list of DSpaceObjects
diff --git a/src/app/core/submission/submission-rest.service.ts b/src/app/core/submission/submission-rest.service.ts
index b4f8185767b2d984a1477c4e6e7d63317eeba03b..32ba0700020aa69c47e2cb4fc8e50abf8f408da6 100644
--- a/src/app/core/submission/submission-rest.service.ts
+++ b/src/app/core/submission/submission-rest.service.ts
@@ -20,6 +20,7 @@ import { HALEndpointService } from '../shared/hal-endpoint.service';
 import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
 import { ErrorResponse, RestResponse, SubmissionSuccessResponse } from '../cache/response.models';
 import { getResponseFromEntry } from '../shared/operators';
+import {URLCombiner} from '../url-combiner/url-combiner';
 
 /**
  * The service handling all submission REST requests
@@ -65,9 +66,15 @@ export class SubmissionRestService {
    *    The base endpoint for the type of object
    * @param resourceID
    *    The identifier for the object
+   * @param collectionId
+   *    The owning collection for the object
    */
-  protected getEndpointByIDHref(endpoint, resourceID): string {
-    return isNotEmpty(resourceID) ? `${endpoint}/${resourceID}` : `${endpoint}`;
+  protected getEndpointByIDHref(endpoint, resourceID, collectionId?: string): string {
+    let url = isNotEmpty(resourceID) ? `${endpoint}/${resourceID}` : `${endpoint}`;
+    if (collectionId) {
+      url = new URLCombiner(url, `?owningCollection=${collectionId}`).toString();
+    }
+    return url;
   }
 
   /**
@@ -130,12 +137,14 @@ export class SubmissionRestService {
    *    The [HttpOptions] object
    * @return Observable<SubmitDataResponseDefinitionObject>
    *     server response
+   * @param collectionId
+   *    The owning collection id
    */
-  public postToEndpoint(linkName: string, body: any, scopeId?: string, options?: HttpOptions): Observable<SubmitDataResponseDefinitionObject> {
+  public postToEndpoint(linkName: string, body: any, scopeId?: string, options?: HttpOptions, collectionId?: string): Observable<SubmitDataResponseDefinitionObject> {
     const requestId = this.requestService.generateRequestId();
     return this.halService.getEndpoint(linkName).pipe(
       filter((href: string) => isNotEmpty(href)),
-      map((endpointURL: string) => this.getEndpointByIDHref(endpointURL, scopeId)),
+      map((endpointURL: string) => this.getEndpointByIDHref(endpointURL, scopeId, collectionId)),
       distinctUntilChanged(),
       map((endpointURL: string) => new SubmissionPostRequest(requestId, endpointURL, body, options)),
       tap((request: PostRequest) => this.requestService.configure(request)),
diff --git a/src/app/entity-groups/research-entities/metadata-representations/person/person-item-metadata-list-element.component.ts b/src/app/entity-groups/research-entities/metadata-representations/person/person-item-metadata-list-element.component.ts
index f3d0a28fda1949b46f2db92559613051b559bc36..867b5890ebaa68cf39c965abe52daf31ac8c4fd0 100644
--- a/src/app/entity-groups/research-entities/metadata-representations/person/person-item-metadata-list-element.component.ts
+++ b/src/app/entity-groups/research-entities/metadata-representations/person/person-item-metadata-list-element.component.ts
@@ -1,4 +1,4 @@
-import { Component } from '@angular/core';
+import { Component, OnInit } from '@angular/core';
 import { metadataRepresentationComponent } from '../../../../shared/metadata-representation/metadata-representation.decorator';
 import { MetadataRepresentationType } from '../../../../core/shared/metadata-representation/metadata-representation.model';
 import { ItemMetadataRepresentationListElementComponent } from '../../../../shared/object-list/metadata-representation-list-element/item/item-metadata-representation-list-element.component';
diff --git a/src/app/entity-groups/research-entities/research-entities.module.ts b/src/app/entity-groups/research-entities/research-entities.module.ts
index 86c2a375da8bda2354d502044befa95d26b67876..cef3b4539b8ef75396dc756a876d6829c4a2fed6 100644
--- a/src/app/entity-groups/research-entities/research-entities.module.ts
+++ b/src/app/entity-groups/research-entities/research-entities.module.ts
@@ -25,6 +25,7 @@ import { PersonInputSuggestionsComponent } from './submission/item-list-elements
 import { NameVariantModalComponent } from './submission/name-variant-modal/name-variant-modal.component';
 import { OrgUnitInputSuggestionsComponent } from './submission/item-list-elements/org-unit/org-unit-suggestions/org-unit-input-suggestions.component';
 import { OrgUnitSearchResultListSubmissionElementComponent } from './submission/item-list-elements/org-unit/org-unit-search-result-list-submission-element.component';
+import { ExternalSourceEntryListSubmissionElementComponent } from './submission/item-list-elements/external-source-entry/external-source-entry-list-submission-element.component';
 
 const ENTRY_COMPONENTS = [
   OrgUnitComponent,
@@ -48,7 +49,8 @@ const ENTRY_COMPONENTS = [
   PersonInputSuggestionsComponent,
   NameVariantModalComponent,
   OrgUnitSearchResultListSubmissionElementComponent,
-  OrgUnitInputSuggestionsComponent
+  OrgUnitInputSuggestionsComponent,
+  ExternalSourceEntryListSubmissionElementComponent
 ];
 
 @NgModule({
diff --git a/src/app/entity-groups/research-entities/submission/item-list-elements/external-source-entry/external-source-entry-list-submission-element.component.html b/src/app/entity-groups/research-entities/submission/item-list-elements/external-source-entry/external-source-entry-list-submission-element.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..9d4a3566adc9a6e53d15bbce38b9a638eed43399
--- /dev/null
+++ b/src/app/entity-groups/research-entities/submission/item-list-elements/external-source-entry/external-source-entry-list-submission-element.component.html
@@ -0,0 +1,4 @@
+<div class="d-inline-block">
+  <div>{{object.display}}</div>
+  <div *ngIf="uri"><a target="_blank" [href]="uri.value">{{uri.value}}</a></div>
+</div>
diff --git a/src/app/entity-groups/research-entities/submission/item-list-elements/external-source-entry/external-source-entry-list-submission-element.component.scss b/src/app/entity-groups/research-entities/submission/item-list-elements/external-source-entry/external-source-entry-list-submission-element.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/app/entity-groups/research-entities/submission/item-list-elements/external-source-entry/external-source-entry-list-submission-element.component.spec.ts b/src/app/entity-groups/research-entities/submission/item-list-elements/external-source-entry/external-source-entry-list-submission-element.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fa153b8c5e0ccc15ac4e47200d3b57c41ed79fbd
--- /dev/null
+++ b/src/app/entity-groups/research-entities/submission/item-list-elements/external-source-entry/external-source-entry-list-submission-element.component.spec.ts
@@ -0,0 +1,47 @@
+import { ExternalSourceEntryListSubmissionElementComponent } from './external-source-entry-list-submission-element.component';
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { ExternalSourceEntry } from '../../../../../core/shared/external-source-entry.model';
+import { TranslateModule } from '@ngx-translate/core';
+import { NO_ERRORS_SCHEMA } from '@angular/core';
+
+describe('ExternalSourceEntryListSubmissionElementComponent', () => {
+  let component: ExternalSourceEntryListSubmissionElementComponent;
+  let fixture: ComponentFixture<ExternalSourceEntryListSubmissionElementComponent>;
+
+  const uri = 'https://orcid.org/0001-0001-0001-0001';
+  const entry = Object.assign(new ExternalSourceEntry(), {
+    id: '0001-0001-0001-0001',
+    display: 'John Doe',
+    value: 'John, Doe',
+    metadata: {
+      'dc.identifier.uri': [
+        {
+          value: uri
+        }
+      ]
+    }
+  });
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ExternalSourceEntryListSubmissionElementComponent],
+      imports: [TranslateModule.forRoot()],
+      schemas: [NO_ERRORS_SCHEMA]
+    }).compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(ExternalSourceEntryListSubmissionElementComponent);
+    component = fixture.componentInstance;
+    component.object = entry;
+    fixture.detectChanges();
+  });
+
+  it('should display the entry\'s display value', () => {
+    expect(fixture.nativeElement.textContent).toContain(entry.display);
+  });
+
+  it('should display the entry\'s uri', () => {
+    expect(fixture.nativeElement.textContent).toContain(uri);
+  });
+});
diff --git a/src/app/entity-groups/research-entities/submission/item-list-elements/external-source-entry/external-source-entry-list-submission-element.component.ts b/src/app/entity-groups/research-entities/submission/item-list-elements/external-source-entry/external-source-entry-list-submission-element.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4612996e915ad852660c113152d64e25342fb5b5
--- /dev/null
+++ b/src/app/entity-groups/research-entities/submission/item-list-elements/external-source-entry/external-source-entry-list-submission-element.component.ts
@@ -0,0 +1,28 @@
+import { AbstractListableElementComponent } from '../../../../../shared/object-collection/shared/object-collection-element/abstract-listable-element.component';
+import { ExternalSourceEntry } from '../../../../../core/shared/external-source-entry.model';
+import { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
+import { ViewMode } from '../../../../../core/shared/view-mode.model';
+import { Context } from '../../../../../core/shared/context.model';
+import { Component, Inject, OnInit } from '@angular/core';
+import { Metadata } from '../../../../../core/shared/metadata.utils';
+import { MetadataValue } from '../../../../../core/shared/metadata.models';
+
+@listableObjectComponent(ExternalSourceEntry, ViewMode.ListElement, Context.SubmissionModal)
+@Component({
+  selector: 'ds-external-source-entry-list-submission-element',
+  styleUrls: ['./external-source-entry-list-submission-element.component.scss'],
+  templateUrl: './external-source-entry-list-submission-element.component.html'
+})
+/**
+ * The component for displaying a list element of an external source entry
+ */
+export class ExternalSourceEntryListSubmissionElementComponent extends AbstractListableElementComponent<ExternalSourceEntry> implements OnInit {
+  /**
+   * The metadata value for the object's uri
+   */
+  uri: MetadataValue;
+
+  ngOnInit(): void {
+    this.uri = Metadata.first(this.object.metadata, 'dc.identifier.uri');
+  }
+}
diff --git a/src/app/entity-groups/research-entities/submission/name-variant-modal/name-variant-modal.component.ts b/src/app/entity-groups/research-entities/submission/name-variant-modal/name-variant-modal.component.ts
index 75817d786a3bfb1495b3c1c3b9ce6713dce16526..eb6f7d01ac545f3334ea04b404624d7fc1702ee5 100644
--- a/src/app/entity-groups/research-entities/submission/name-variant-modal/name-variant-modal.component.ts
+++ b/src/app/entity-groups/research-entities/submission/name-variant-modal/name-variant-modal.component.ts
@@ -10,7 +10,13 @@ import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
   templateUrl: './name-variant-modal.component.html',
   styleUrls: ['./name-variant-modal.component.scss']
 })
+/**
+ * The component for the modal to add a name variant to an item
+ */
 export class NameVariantModalComponent {
+  /**
+   * The name variant
+   */
   @Input() value: string;
 
   constructor(public modal: NgbActiveModal) {
diff --git a/src/app/header/header.component.html b/src/app/header/header.component.html
index a03fd01c533919d34b63804290e639060fd2b0bb..58f7cb1ecff884fa114d289efdb9d7d45d5e876e 100644
--- a/src/app/header/header.component.html
+++ b/src/app/header/header.component.html
@@ -1,20 +1,20 @@
 <header>
-    <div class="container">
-        <a class="navbar-brand my-2" routerLink="/home">
-            <img src="assets/images/dspace-logo.svg"/>
-        </a>
+  <div class="container">
+    <a class="navbar-brand my-2" routerLink="/home">
+      <img src="assets/images/dspace-logo.svg"/>
+    </a>
 
-        <nav class="navbar navbar-light navbar-expand-md float-right px-0">
-            <a routerLink="/search" class="px-1"><i class="fas fa-search fa-lg fa-fw" [title]="'nav.search' | translate"></i></a>
-            <ds-lang-switch></ds-lang-switch>
-            <ds-auth-nav-menu></ds-auth-nav-menu>
-            <div class="pl-2">
-                <button class="navbar-toggler" type="button" (click)="toggleNavbar()"
-                        aria-controls="collapsingNav"
-                        aria-expanded="false" aria-label="Toggle navigation">
-                    <span class="navbar-toggler-icon fas fa-bars fa-fw" aria-hidden="true"></span>
-                </button>
-            </div>
-        </nav>
-    </div>
+    <nav class="navbar navbar-light navbar-expand-md float-right px-0">
+      <ds-search-navbar></ds-search-navbar>
+      <ds-lang-switch></ds-lang-switch>
+      <ds-auth-nav-menu></ds-auth-nav-menu>
+      <div class="pl-2">
+        <button class="navbar-toggler" type="button" (click)="toggleNavbar()"
+                aria-controls="collapsingNav"
+                aria-expanded="false" aria-label="Toggle navigation">
+          <span class="navbar-toggler-icon fas fa-bars fa-fw" aria-hidden="true"></span>
+        </button>
+      </div>
+    </nav>
+  </div>
 </header>
diff --git a/src/app/search-navbar/search-navbar.component.html b/src/app/search-navbar/search-navbar.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..13d792c80f70daad144d06e01758c8b5a773b3e2
--- /dev/null
+++ b/src/app/search-navbar/search-navbar.component.html
@@ -0,0 +1,12 @@
+<div id="search-navbar-container" [title]="'nav.search' | translate" (dsClickOutside)="collapse()">
+  <div class="d-inline-block position-relative">
+    <form [formGroup]="searchForm" (ngSubmit)="onSubmit(searchForm.value)" autocomplete="on">
+      <input #searchInput [@toggleAnimation]="isExpanded" id="query" name="query"
+             formControlName="query" type="text" placeholder="{{searchExpanded ? ('nav.search' | translate) : ''}}"
+             class="d-inline-block bg-transparent position-absolute form-control dropdown-menu-right p-1">
+      <a class="sticky-top submit-icon" (click)="searchExpanded ? onSubmit(searchForm.value) : expand()">
+        <em class="fas fa-search fa-lg fa-fw"></em>
+      </a>
+    </form>
+  </div>
+</div>
diff --git a/src/app/search-navbar/search-navbar.component.scss b/src/app/search-navbar/search-navbar.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..3606c47afcb6fe957b0acfb7d5c36ce13e8872e6
--- /dev/null
+++ b/src/app/search-navbar/search-navbar.component.scss
@@ -0,0 +1,25 @@
+input[type="text"] {
+  margin-top: -0.5 * $font-size-base;
+
+  &:focus {
+    background-color: rgba(255, 255, 255, 0.5) !important;
+  }
+
+  &.collapsed {
+    opacity: 0;
+  }
+}
+
+a.submit-icon {
+  cursor: pointer;
+}
+
+
+
+@media screen and (max-width: map-get($grid-breakpoints, sm)) {
+  #query:focus {
+    max-width: 250px !important;
+    width: 40vw !important;
+  }
+}
+
diff --git a/src/app/search-navbar/search-navbar.component.spec.ts b/src/app/search-navbar/search-navbar.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2a03acd2a235466a7b79b028e683b74e0f67b67d
--- /dev/null
+++ b/src/app/search-navbar/search-navbar.component.spec.ts
@@ -0,0 +1,121 @@
+import { async, ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
+import { FormsModule, ReactiveFormsModule } from '@angular/forms';
+import { By } from '@angular/platform-browser';
+import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+import { Router } from '@angular/router';
+import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
+import { SearchService } from '../core/shared/search/search.service';
+import { MockTranslateLoader } from '../shared/mocks/mock-translate-loader';
+
+import { SearchNavbarComponent } from './search-navbar.component';
+
+describe('SearchNavbarComponent', () => {
+  let component: SearchNavbarComponent;
+  let fixture: ComponentFixture<SearchNavbarComponent>;
+  let mockSearchService: any;
+  let router: Router;
+  let routerStub;
+
+  beforeEach(async(() => {
+    mockSearchService = {
+      getSearchLink() {
+        return '/search';
+      }
+    };
+
+    routerStub = {
+      navigate: (commands) => commands
+    };
+    TestBed.configureTestingModule({
+      imports: [
+        FormsModule,
+        ReactiveFormsModule,
+        BrowserAnimationsModule,
+        TranslateModule.forRoot({
+          loader: {
+            provide: TranslateLoader,
+            useClass: MockTranslateLoader
+          }
+        })],
+      declarations: [SearchNavbarComponent],
+      providers: [
+        { provide: SearchService, useValue: mockSearchService },
+        { provide: Router, useValue: routerStub }
+      ]
+    })
+      .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(SearchNavbarComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+    router = (component as any).router;
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+
+  describe('when you click on search icon', () => {
+    beforeEach(fakeAsync(() => {
+      spyOn(component, 'expand').and.callThrough();
+      spyOn(component, 'onSubmit').and.callThrough();
+      spyOn(router, 'navigate').and.callThrough();
+      const searchIcon = fixture.debugElement.query(By.css('#search-navbar-container form .submit-icon'));
+      searchIcon.triggerEventHandler('click', {
+        preventDefault: () => {/**/
+        }
+      });
+      tick();
+      fixture.detectChanges();
+    }));
+
+    it('input expands', () => {
+      expect(component.expand).toHaveBeenCalled();
+    });
+
+    describe('empty query', () => {
+      describe('press submit button', () => {
+        beforeEach(fakeAsync(() => {
+          const searchIcon = fixture.debugElement.query(By.css('#search-navbar-container form .submit-icon'));
+          searchIcon.triggerEventHandler('click', {
+            preventDefault: () => {/**/
+            }
+          });
+          tick();
+          fixture.detectChanges();
+        }));
+        it('to search page with empty query', () => {
+          expect(component.onSubmit).toHaveBeenCalledWith({ query: '' });
+          expect(router.navigate).toHaveBeenCalled();
+        });
+      });
+    });
+
+    describe('fill in some query', () => {
+      let searchInput;
+      beforeEach(async () => {
+        await fixture.whenStable();
+        fixture.detectChanges();
+        searchInput = fixture.debugElement.query(By.css('#search-navbar-container form input'));
+        searchInput.nativeElement.value = 'test';
+        searchInput.nativeElement.dispatchEvent(new Event('input'));
+        fixture.detectChanges();
+      });
+      describe('press submit button', () => {
+        beforeEach(fakeAsync(() => {
+          const searchIcon = fixture.debugElement.query(By.css('#search-navbar-container form .submit-icon'));
+          searchIcon.triggerEventHandler('click', null);
+          tick();
+          fixture.detectChanges();
+        }));
+        it('to search page with query', async () => {
+          expect(component.onSubmit).toHaveBeenCalledWith({ query: 'test' });
+          expect(router.navigate).toHaveBeenCalled();
+        });
+      });
+    })
+
+  });
+});
diff --git a/src/app/search-navbar/search-navbar.component.ts b/src/app/search-navbar/search-navbar.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1bedfb73ef2296f5167430abe2e3756b921a4a59
--- /dev/null
+++ b/src/app/search-navbar/search-navbar.component.ts
@@ -0,0 +1,71 @@
+import { Component, ElementRef, ViewChild } from '@angular/core';
+import { FormBuilder } from '@angular/forms';
+import { Router } from '@angular/router';
+import { SearchService } from '../core/shared/search/search.service';
+import { expandSearchInput } from '../shared/animations/slide';
+
+/**
+ * The search box in the header that expands on focus and collapses on focus out
+ */
+@Component({
+  selector: 'ds-search-navbar',
+  templateUrl: './search-navbar.component.html',
+  styleUrls: ['./search-navbar.component.scss'],
+  animations: [expandSearchInput]
+})
+export class SearchNavbarComponent {
+
+  // The search form
+  searchForm;
+  // Whether or not the search bar is expanded, boolean for html ngIf, string fo AngularAnimation state change
+  searchExpanded = false;
+  isExpanded = 'collapsed';
+
+  // Search input field
+  @ViewChild('searchInput') searchField: ElementRef;
+
+  constructor(private formBuilder: FormBuilder, private router: Router, private searchService: SearchService) {
+    this.searchForm = this.formBuilder.group(({
+      query: '',
+    }));
+  }
+
+  /**
+   * Expands search bar by angular animation, see expandSearchInput
+   */
+  expand() {
+    this.searchExpanded = true;
+    this.isExpanded = 'expanded';
+    this.editSearch();
+  }
+
+  /**
+   * Collapses & blurs search bar by angular animation, see expandSearchInput
+   */
+  collapse() {
+    this.searchField.nativeElement.blur();
+    this.searchExpanded = false;
+    this.isExpanded = 'collapsed';
+  }
+
+  /**
+   * Focuses on input search bar so search can be edited
+   */
+  editSearch(): void {
+    this.searchField.nativeElement.focus();
+  }
+
+  /**
+   * Submits the search (on enter or on search icon click)
+   * @param data  Data for the searchForm, containing the search query
+   */
+  onSubmit(data: any) {
+    this.collapse();
+    const linkToNavigateTo = this.searchService.getSearchLink().split('/');
+    this.searchForm.reset();
+    this.router.navigate(linkToNavigateTo, {
+      queryParams: Object.assign({}, { page: 1 }, data),
+      queryParamsHandling: 'merge'
+    });
+  }
+}
diff --git a/src/app/shared/animations/slide.ts b/src/app/shared/animations/slide.ts
index 38bfaaddcaaa6b3aaaa4c0ba2799ee2734eed4a4..7928a25659e14fa5614509c4f3a8833177eb03cd 100644
--- a/src/app/shared/animations/slide.ts
+++ b/src/app/shared/animations/slide.ts
@@ -1,13 +1,4 @@
-import {
-  animate,
-  animateChild,
-  group,
-  query,
-  state,
-  style,
-  transition,
-  trigger
-} from '@angular/animations';
+import { animate, animateChild, group, query, state, style, transition, trigger } from '@angular/animations';
 
 export const slide = trigger('slide', [
   state('expanded', style({ height: '*' })),
@@ -70,3 +61,30 @@ export const slideSidebarPadding = trigger('slideSidebarPadding', [
   transition('hidden <=> expanded', [animate('200ms')]),
   transition('shown <=> expanded', [animate('200ms')]),
 ]);
+
+export const expandSearchInput = trigger('toggleAnimation', [
+  state('collapsed', style({
+    width: '30px',
+    opacity: '0'
+  })),
+  state('expanded', style({
+    width: '250px',
+    opacity: '1'
+  })),
+  transition('* => collapsed', group([
+    animate('300ms ease-in-out', style({
+      width: '30px'
+    })),
+    animate('300ms ease-in', style({
+      opacity: '0'
+    }))
+  ])),
+  transition('* => expanded', group([
+    animate('300ms ease-out', style({
+      opacity: '1'
+    })),
+    animate('300ms ease-in-out', style({
+      width: '250px'
+    }))
+  ]))
+]);
diff --git a/src/app/shared/comcol-forms/edit-comcol-page/edit-comcol-page.component.ts b/src/app/shared/comcol-forms/edit-comcol-page/edit-comcol-page.component.ts
index 0f9d4c55b471960198a97b662fdaf53f60d9496e..2fa05fa28b5c79cd499c9bdbfd6c5ed63b989c6b 100644
--- a/src/app/shared/comcol-forms/edit-comcol-page/edit-comcol-page.component.ts
+++ b/src/app/shared/comcol-forms/edit-comcol-page/edit-comcol-page.component.ts
@@ -2,10 +2,8 @@ import { Component, OnInit } from '@angular/core';
 import { Observable } from 'rxjs';
 import { ActivatedRoute, Router } from '@angular/router';
 import { RemoteData } from '../../../core/data/remote-data';
-import { isNotEmpty, isNotUndefined } from '../../empty.util';
+import { isNotEmpty } from '../../empty.util';
 import { first, map } from 'rxjs/operators';
-import { getSucceededRemoteData } from '../../../core/shared/operators';
-import { DataService } from '../../../core/data/data.service';
 import { DSpaceObject } from '../../../core/shared/dspace-object.model';
 
 /**
diff --git a/src/app/shared/dso-selector/modal-wrappers/create-item-parent-selector/create-item-parent-selector.component.spec.ts b/src/app/shared/dso-selector/modal-wrappers/create-item-parent-selector/create-item-parent-selector.component.spec.ts
index 2c9b2499ab1bc316b1b4ef6ef5edc21c87fa0566..b3058ab879ee3dd157f42b258ac2ef6c82865c9b 100644
--- a/src/app/shared/dso-selector/modal-wrappers/create-item-parent-selector/create-item-parent-selector.component.spec.ts
+++ b/src/app/shared/dso-selector/modal-wrappers/create-item-parent-selector/create-item-parent-selector.component.spec.ts
@@ -59,9 +59,8 @@ describe('CreateItemParentSelectorComponent', () => {
   });
 
   it('should call navigate on the router with the correct create path when navigate is called', () => {
-    /* TODO when there is a specific submission path */
-    // component.navigate(item);
-    // expect(router.navigate).toHaveBeenCalledWith([createPath]);
+    component.navigate(collection);
+    expect(router.navigate).toHaveBeenCalledWith(['/submit'], { queryParams: { collection: collection.uuid } });
   });
 
 });
diff --git a/src/app/shared/dso-selector/modal-wrappers/create-item-parent-selector/create-item-parent-selector.component.ts b/src/app/shared/dso-selector/modal-wrappers/create-item-parent-selector/create-item-parent-selector.component.ts
index 29af9f624e0a306976fc4d4347182e5889b3ca7e..02a0bd79cd9acec2578f574394b0c23de45c53ce 100644
--- a/src/app/shared/dso-selector/modal-wrappers/create-item-parent-selector/create-item-parent-selector.component.ts
+++ b/src/app/shared/dso-selector/modal-wrappers/create-item-parent-selector/create-item-parent-selector.component.ts
@@ -1,5 +1,5 @@
 import { Component, OnInit } from '@angular/core';
-import { ActivatedRoute, Router } from '@angular/router';
+import { ActivatedRoute, NavigationExtras, Router } from '@angular/router';
 import { DSpaceObjectType } from '../../../../core/shared/dspace-object-type.model';
 import { DSpaceObject } from '../../../../core/shared/dspace-object.model';
 import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
@@ -28,6 +28,11 @@ export class CreateItemParentSelectorComponent extends DSOSelectorModalWrapperCo
    * Navigate to the item create page
    */
   navigate(dso: DSpaceObject) {
-   // There's no submit path per collection yet...
+    const navigationExtras: NavigationExtras = {
+      queryParams: {
+        ['collection']: dso.uuid,
+      }
+    };
+    this.router.navigate(['/submit'], navigationExtras);
   }
 }
diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.html b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.html
index 144848b478bfabdf26edc94fe9c566f462ba5aaf..a31171d7ef37337418259a5d3206d3ea3eec88ce 100644
--- a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.html
+++ b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.html
@@ -53,16 +53,15 @@
 
 
     <div *ngIf="hasRelationLookup" class="mt-3">
-        <ul class="list-unstyled">
-            <li *ngFor="let value of ( selectedValues$ | async)">
-              <button type="button" class="close float-left" aria-label="Close button"
-                      (click)="removeSelection(value.selectedResult)">
-                  <span aria-hidden="true">&times;</span>
-              </button>
-              <span class="d-inline-block align-middle ml-1">
-              <ds-metadata-representation-loader [mdRepresentation]="value.mdRep"></ds-metadata-representation-loader>
-          </span>
-            </li>
+        <ul class="list-unstyled" cdkDropList (cdkDropListDropped)="moveSelection($event)">
+            <ds-existing-metadata-list-element cdkDrag
+                                               *ngFor="let reorderable of reorderables; trackBy: trackReorderable"
+                                               [reoRel]="reorderable"
+                                               [submissionItem]="item"
+                                               [listId]="listId"
+                                               [metadataFields]="model.metadataFields"
+                                               [relationshipOptions]="model.relationship">
+            </ds-existing-metadata-list-element>
         </ul>
     </div>
 </div>
diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.ts
index 637da20790f78a2f8a3237eaf4a0f2be2eb8d900..466ad8ac2ab60cd9e547191eeed9b6a6a46cfb5c 100644
--- a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.ts
+++ b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.ts
@@ -1,12 +1,13 @@
 import {
-  ChangeDetectionStrategy,
+  ChangeDetectionStrategy, ChangeDetectorRef,
   Component,
   ComponentFactoryResolver,
   ContentChildren,
   EventEmitter,
   Input,
   NgZone,
-  OnChanges, OnDestroy,
+  OnChanges,
+  OnDestroy,
   OnInit,
   Output,
   QueryList,
@@ -49,7 +50,10 @@ import {
   DynamicNGBootstrapTimePickerComponent
 } from '@ng-dynamic-forms/ui-ng-bootstrap';
 import { TranslateService } from '@ngx-translate/core';
-import { MetadataRepresentation } from '../../../../core/shared/metadata-representation/metadata-representation.model';
+import {
+  Reorderable,
+  ReorderableRelationship
+} from './existing-metadata-list-element/existing-metadata-list-element.component';
 
 import { DYNAMIC_FORM_CONTROL_TYPE_TYPEAHEAD } from './models/typeahead/dynamic-typeahead.model';
 import { DYNAMIC_FORM_CONTROL_TYPE_SCROLLABLE_DROPDOWN } from './models/scrollable-dropdown/dynamic-scrollable-dropdown.model';
@@ -71,9 +75,8 @@ import { DsDynamicFormArrayComponent } from './models/array-group/dynamic-form-a
 import { DsDynamicRelationGroupComponent } from './models/relation-group/dynamic-relation-group.components';
 import { DYNAMIC_FORM_CONTROL_TYPE_RELATION_GROUP } from './models/relation-group/dynamic-relation-group.model';
 import { DsDatePickerInlineComponent } from './models/date-picker-inline/dynamic-date-picker-inline.component';
-import { map, switchMap, take, tap } from 'rxjs/operators';
-import { combineLatest as observableCombineLatest, Observable, Subscription } from 'rxjs';
-import { SelectableListState } from '../../../object-list/selectable-list/selectable-list.reducer';
+import { map, startWith, switchMap, find } from 'rxjs/operators';
+import { combineLatest as observableCombineLatest, Observable, of as observableOf, Subscription } from 'rxjs';
 import { SearchResult } from '../../../search/search-result.model';
 import { DSpaceObject } from '../../../../core/shared/dspace-object.model';
 import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
@@ -82,23 +85,19 @@ import { SelectableListService } from '../../../object-list/selectable-list/sele
 import { DsDynamicDisabledComponent } from './models/disabled/dynamic-disabled.component';
 import { DYNAMIC_FORM_CONTROL_TYPE_DISABLED } from './models/disabled/dynamic-disabled.model';
 import { DsDynamicLookupRelationModalComponent } from './relation-lookup-modal/dynamic-lookup-relation-modal.component';
-import {
-  getAllSucceededRemoteData,
-  getRemoteDataPayload,
-  getSucceededRemoteData
-} from '../../../../core/shared/operators';
+import { getAllSucceededRemoteData, getRemoteDataPayload, getSucceededRemoteData } from '../../../../core/shared/operators';
 import { RemoteData } from '../../../../core/data/remote-data';
 import { Item } from '../../../../core/shared/item.model';
 import { ItemDataService } from '../../../../core/data/item-data.service';
-import { RemoveRelationshipAction } from './relation-lookup-modal/relationship.actions';
 import { Store } from '@ngrx/store';
 import { AppState } from '../../../../app.reducer';
 import { SubmissionObjectDataService } from '../../../../core/submission/submission-object-data.service';
 import { SubmissionObject } from '../../../../core/submission/models/submission-object.model';
 import { PaginatedList } from '../../../../core/data/paginated-list';
 import { ItemSearchResult } from '../../../object-collection/shared/item-search-result.model';
-import { ItemMetadataRepresentation } from '../../../../core/shared/metadata-representation/item/item-metadata-representation.model';
-import { MetadataValue } from '../../../../core/shared/metadata.models';
+import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';
+import { Relationship } from '../../../../core/shared/item-relationships/relationship.model';
+import { Collection } from '../../../../core/shared/collection.model';
 
 export function dsDynamicFormControlMapFn(model: DynamicFormControlModel): Type<DynamicFormControl> | null {
   switch (model.type) {
@@ -182,16 +181,15 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo
   @Input() hasErrorMessaging = false;
   @Input() layout = null as DynamicFormLayout;
   @Input() model: any;
-  relationships$: Observable<Array<SearchResult<Item>>>;
+  reorderables$: Observable<ReorderableRelationship[]>;
+  reorderables: ReorderableRelationship[];
   hasRelationLookup: boolean;
   modalRef: NgbModalRef;
   item: Item;
+  collection: Collection;
   listId: string;
   searchConfig: string;
-  selectedValues$: Observable<Array<{
-    selectedResult: SearchResult<Item>,
-    mdRep: MetadataRepresentation
-  }>>;
+
   /**
    * List of subscriptions to unsubscribe from
    */
@@ -224,7 +222,8 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo
     private relationshipService: RelationshipService,
     private zone: NgZone,
     private store: Store<AppState>,
-    private submissionObjectService: SubmissionObjectDataService
+    private submissionObjectService: SubmissionObjectDataService,
+    private ref: ChangeDetectorRef
   ) {
 
     super(componentFactoryResolver, layoutService, validationService, dynamicFormInstanceService);
@@ -235,44 +234,58 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo
    */
   ngOnInit(): void {
     this.hasRelationLookup = hasValue(this.model.relationship);
+    this.reorderables = [];
     if (this.hasRelationLookup) {
+
       this.listId = 'list-' + this.model.relationship.relationshipType;
-      const item$ = this.submissionObjectService
+
+      const submissionObject$ = this.submissionObjectService
         .findById(this.model.submissionId).pipe(
           getAllSucceededRemoteData(),
-          getRemoteDataPayload(),
-          switchMap((submissionObject: SubmissionObject) => (submissionObject.item as Observable<RemoteData<Item>>).pipe(getAllSucceededRemoteData(), getRemoteDataPayload())));
+          getRemoteDataPayload()
+        );
+
+      const item$ = submissionObject$.pipe(switchMap((submissionObject: SubmissionObject) => (submissionObject.item as Observable<RemoteData<Item>>).pipe(getAllSucceededRemoteData(), getRemoteDataPayload())));
+      const collection$ = submissionObject$.pipe(switchMap((submissionObject: SubmissionObject) => (submissionObject.collection as Observable<RemoteData<Collection>>).pipe(getAllSucceededRemoteData(), getRemoteDataPayload())));
 
       this.subs.push(item$.subscribe((item) => this.item = item));
+      this.subs.push(collection$.subscribe((collection) => this.collection = collection));
+      this.reorderables$ = item$.pipe(
+        switchMap((item) => this.relationService.getItemRelationshipsByLabel(item, this.model.relationship.relationshipType)
+          .pipe(
+            getAllSucceededRemoteData(),
+            getRemoteDataPayload(),
+            map((relationshipList: PaginatedList<Relationship>) => relationshipList.page),
+            startWith([]),
+            switchMap((relationships: Relationship[]) =>
+              observableCombineLatest(
+                relationships.map((relationship: Relationship) =>
+                  relationship.leftItem.pipe(
+                    getSucceededRemoteData(),
+                    getRemoteDataPayload(),
+                    map((leftItem: Item) => {
+                      return new ReorderableRelationship(relationship, leftItem.uuid !== this.item.uuid)
+                    }),
+                  )
+                ))),
+            map((relationships: ReorderableRelationship[]) =>
+              relationships
+                .sort((a: Reorderable, b: Reorderable) => {
+                  return Math.sign(a.getPlace() - b.getPlace());
+                })
+            )
+          )
+        )
+      );
+
+      this.subs.push(this.reorderables$.subscribe((rs) => {
+        this.reorderables = rs;
+        this.ref.detectChanges();
+      }));
 
       this.relationService.getRelatedItemsByLabel(this.item, this.model.relationship.relationshipType).pipe(
         map((items: RemoteData<PaginatedList<Item>>) => items.payload.page.map((item) => Object.assign(new ItemSearchResult(), { indexableObject: item }))),
       ).subscribe((relatedItems: Array<SearchResult<Item>>) => this.selectableListService.select(this.listId, relatedItems));
-
-      this.relationships$ = this.selectableListService.getSelectableList(this.listId).pipe(
-        map((listState: SelectableListState) => hasValue(listState) && hasValue(listState.selection) ? listState.selection : []),
-      ) as Observable<Array<SearchResult<Item>>>;
-      this.selectedValues$ =
-        observableCombineLatest(item$, this.relationships$).pipe(
-          map(([item, relatedItems]: [Item, Array<SearchResult<DSpaceObject>>]) => {
-              return relatedItems
-              .map((element: SearchResult<Item>) => {
-                const relationMD: MetadataValue = item.firstMetadata(this.model.relationship.metadataField, { value: element.indexableObject.uuid });
-                if (hasValue(relationMD)) {
-                  const metadataRepresentationMD: MetadataValue = item.firstMetadata(this.model.metadataFields, { authority: relationMD.authority });
-                  return {
-                    selectedResult: element,
-                    mdRep: Object.assign(
-                      new ItemMetadataRepresentation(metadataRepresentationMD),
-                      element.indexableObject
-                    )
-                  };
-                }
-              }).filter(hasValue)
-            }
-          )
-        );
-
     }
   }
 
@@ -331,15 +344,33 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo
     modalComp.label = this.model.label;
     modalComp.metadataFields = this.model.metadataFields;
     modalComp.item = this.item;
+    modalComp.collection = this.collection;
   }
 
   /**
-   * Method to remove a selected relationship from the item
-   * @param object The second item in the relationship, the submitted item being the first
+   * Method to move a relationship inside the list of relationships
+   * This will update the view and update the right or left place field of the relationships in the list
+   * @param event
    */
-  removeSelection(object: SearchResult<Item>) {
-    this.selectableListService.deselectSingle(this.listId, object);
-    this.store.dispatch(new RemoveRelationshipAction(this.item, object.indexableObject, this.model.relationship.relationshipType))
+  moveSelection(event: CdkDragDrop<Relationship>) {
+    this.zone.runOutsideAngular(() => {
+      moveItemInArray(this.reorderables, event.previousIndex, event.currentIndex);
+      const reorderables: Reorderable[] = this.reorderables.map((reo: Reorderable, index: number) => {
+          reo.oldIndex = reo.getPlace();
+          reo.newIndex = index;
+          return reo;
+        }
+      );
+      observableCombineLatest(
+        reorderables.map((rel: ReorderableRelationship) => {
+          if (rel.oldIndex !== rel.newIndex) {
+            return this.relationshipService.updatePlace(rel);
+          } else {
+            return observableOf(undefined) as Observable<RemoteData<Relationship>>;
+          }
+        })
+      ).subscribe();
+    })
   }
 
   /**
@@ -350,4 +381,11 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo
       .filter((sub) => hasValue(sub))
       .forEach((sub) => sub.unsubscribe());
   }
+
+  /**
+   * Prevent unnecessary rerendering so fields don't lose focus
+   */
+  trackReorderable(index, reorderable: Reorderable) {
+    return hasValue(reorderable) ? reorderable.getId() : undefined;
+  }
 }
diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/existing-metadata-list-element/existing-metadata-list-element.component.html b/src/app/shared/form/builder/ds-dynamic-form-ui/existing-metadata-list-element/existing-metadata-list-element.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..960dd78767fdd4ef34c1b11a38c2650815aa1df7
--- /dev/null
+++ b/src/app/shared/form/builder/ds-dynamic-form-ui/existing-metadata-list-element/existing-metadata-list-element.component.html
@@ -0,0 +1,11 @@
+<li *ngIf="metadataRepresentation">
+    <button type="button" class="close float-left" aria-label="Move button" cdkDragHandle>
+        <i aria-hidden="true" class="fas fa-arrows-alt fa-xs"></i>
+    </button>
+    <button type="button" class="close float-left" aria-label="Close button" (click)="removeSelection()">
+        <span aria-hidden="true">&times;</span>
+    </button>
+    <span class="d-inline-block align-middle ml-1">
+              <ds-metadata-representation-loader [mdRepresentation]="metadataRepresentation"></ds-metadata-representation-loader>
+          </span>
+</li>
diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/existing-metadata-list-element/existing-metadata-list-element.component.scss b/src/app/shared/form/builder/ds-dynamic-form-ui/existing-metadata-list-element/existing-metadata-list-element.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/existing-metadata-list-element/existing-metadata-list-element.component.spec.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/existing-metadata-list-element/existing-metadata-list-element.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fa13febcd1d24fbc6a509d641fd10dbb9ecaab11
--- /dev/null
+++ b/src/app/shared/form/builder/ds-dynamic-form-ui/existing-metadata-list-element/existing-metadata-list-element.component.spec.ts
@@ -0,0 +1,92 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { ExistingMetadataListElementComponent, Reorderable, ReorderableRelationship } from './existing-metadata-list-element.component';
+import { NO_ERRORS_SCHEMA } from '@angular/core';
+import { SelectableListService } from '../../../../object-list/selectable-list/selectable-list.service';
+import { select, Store } from '@ngrx/store';
+import { Item } from '../../../../../core/shared/item.model';
+import { Relationship } from '../../../../../core/shared/item-relationships/relationship.model';
+import { RelationshipOptions } from '../../models/relationship-options.model';
+import { createSuccessfulRemoteDataObject$ } from '../../../../testing/utils';
+import { RemoveRelationshipAction } from '../relation-lookup-modal/relationship.actions';
+import { ItemSearchResult } from '../../../../object-collection/shared/item-search-result.model';
+
+describe('ExistingMetadataListElementComponent', () => {
+  let component: ExistingMetadataListElementComponent;
+  let fixture: ComponentFixture<ExistingMetadataListElementComponent>;
+  let selectionService;
+  let store;
+  let listID;
+  let submissionItem;
+  let relationship;
+  let reoRel;
+  let metadataFields;
+  let relationshipOptions;
+  let uuid1;
+  let uuid2;
+  let relatedItem;
+  let leftItemRD$;
+  let rightItemRD$;
+  let relatedSearchResult;
+
+  function init() {
+    uuid1 = '91ce578d-2e63-4093-8c73-3faafd716000';
+    uuid2 = '0e9dba1c-e1c3-4e05-a539-446f08ef57a7';
+    selectionService = jasmine.createSpyObj('selectionService', ['deselectSingle']);
+    store = jasmine.createSpyObj('store', ['dispatch']);
+    listID = '1234-listID';
+    submissionItem = Object.assign(new Item(), { uuid: uuid1 });
+    metadataFields = ['dc.contributor.author'];
+    relationshipOptions = Object.assign(new RelationshipOptions(), { relationshipType: 'isPublicationOfAuthor', filter: 'test.filter', searchConfiguration: 'personConfiguration', nameVariants: true })
+    relatedItem = Object.assign(new Item(), { uuid: uuid2 });
+    leftItemRD$ = createSuccessfulRemoteDataObject$(relatedItem);
+    rightItemRD$ = createSuccessfulRemoteDataObject$(submissionItem);
+    relatedSearchResult = Object.assign(new ItemSearchResult(), { indexableObject: relatedItem });
+
+    relationship = Object.assign(new Relationship(), { leftItem: leftItemRD$, rightItem: rightItemRD$ });
+    reoRel = new ReorderableRelationship(relationship, true);
+  }
+
+  beforeEach(async(() => {
+    init();
+    TestBed.configureTestingModule({
+      declarations: [ExistingMetadataListElementComponent],
+      providers: [
+        { provide: SelectableListService, useValue: selectionService },
+        { provide: Store, useValue: store },
+      ],
+      schemas: [NO_ERRORS_SCHEMA]
+    })
+      .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(ExistingMetadataListElementComponent);
+    component = fixture.componentInstance;
+    component.listId = listID;
+    component.submissionItem = submissionItem;
+    component.reoRel = reoRel;
+    component.metadataFields = metadataFields;
+    component.relationshipOptions = relationshipOptions;
+    fixture.detectChanges();
+    component.ngOnChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+
+  describe('removeSelection', () => {
+    it('should deselect the object in the selectable list service', () => {
+      component.removeSelection();
+      expect(selectionService.deselectSingle).toHaveBeenCalledWith(listID, relatedSearchResult);
+    });
+
+    it('should dispatch a RemoveRelationshipAction', () => {
+      component.removeSelection();
+      const action = new RemoveRelationshipAction(submissionItem, relatedItem, relationshipOptions.relationshipType);
+      expect(store.dispatch).toHaveBeenCalledWith(action);
+
+    });
+  })
+});
diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/existing-metadata-list-element/existing-metadata-list-element.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/existing-metadata-list-element/existing-metadata-list-element.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..09aaa253c69f9891a78ca24f2221211f922d3bd1
--- /dev/null
+++ b/src/app/shared/form/builder/ds-dynamic-form-ui/existing-metadata-list-element/existing-metadata-list-element.component.ts
@@ -0,0 +1,123 @@
+import { Component, Input, OnChanges, OnDestroy, OnInit } from '@angular/core';
+import { Item } from '../../../../../core/shared/item.model';
+import { MetadataRepresentation } from '../../../../../core/shared/metadata-representation/metadata-representation.model';
+import { getAllSucceededRemoteData, getRemoteDataPayload } from '../../../../../core/shared/operators';
+import { hasValue, isNotEmpty } from '../../../../empty.util';
+import { Subscription } from 'rxjs';
+import { filter } from 'rxjs/operators';
+import { Relationship } from '../../../../../core/shared/item-relationships/relationship.model';
+import { MetadataValue } from '../../../../../core/shared/metadata.models';
+import { ItemMetadataRepresentation } from '../../../../../core/shared/metadata-representation/item/item-metadata-representation.model';
+import { RelationshipOptions } from '../../models/relationship-options.model';
+import { RemoveRelationshipAction } from '../relation-lookup-modal/relationship.actions';
+import { SelectableListService } from '../../../../object-list/selectable-list/selectable-list.service';
+import { Store } from '@ngrx/store';
+import { AppState } from '../../../../../app.reducer';
+import { ItemSearchResult } from '../../../../object-collection/shared/item-search-result.model';
+
+// tslint:disable:max-classes-per-file
+/**
+ * Abstract class that defines objects that can be reordered
+ */
+export abstract class Reorderable {
+  constructor(public oldIndex?: number, public newIndex?: number) {
+  }
+
+  abstract getId(): string;
+
+  abstract getPlace(): number;
+}
+
+/**
+ * Represents a single relationship that can be reordered in a list of multiple relationships
+ */
+export class ReorderableRelationship extends Reorderable {
+  relationship: Relationship;
+  useLeftItem: boolean;
+
+  constructor(relationship: Relationship, useLeftItem: boolean, oldIndex?: number, newIndex?: number) {
+    super(oldIndex, newIndex);
+    this.relationship = relationship;
+    this.useLeftItem = useLeftItem;
+  }
+
+  getId(): string {
+    return this.relationship.id;
+  }
+
+  getPlace(): number {
+    if (this.useLeftItem) {
+      return this.relationship.rightPlace
+    } else {
+      return this.relationship.leftPlace
+    }
+  }
+}
+
+/**
+ * Represents a single existing relationship value as metadata in submission
+ */
+@Component({
+  selector: 'ds-existing-metadata-list-element',
+  templateUrl: './existing-metadata-list-element.component.html',
+  styleUrls: ['./existing-metadata-list-element.component.scss']
+})
+export class ExistingMetadataListElementComponent implements OnChanges, OnDestroy {
+  @Input() listId: string;
+  @Input() submissionItem: Item;
+  @Input() reoRel: ReorderableRelationship;
+  @Input() metadataFields: string[];
+  @Input() relationshipOptions: RelationshipOptions;
+  metadataRepresentation: MetadataRepresentation;
+  relatedItem: Item;
+
+  /**
+   * List of subscriptions to unsubscribe from
+   */
+  private subs: Subscription[] = [];
+
+  constructor(
+    private selectableListService: SelectableListService,
+    private store: Store<AppState>
+  ) {
+  }
+
+  ngOnChanges() {
+    const item$ = this.reoRel.useLeftItem ?
+      this.reoRel.relationship.leftItem : this.reoRel.relationship.rightItem;
+    this.subs.push(item$.pipe(
+      getAllSucceededRemoteData(),
+      getRemoteDataPayload(),
+      filter((item: Item) => hasValue(item) && isNotEmpty(item.uuid))
+    ).subscribe((item: Item) => {
+      this.relatedItem = item;
+      const relationMD: MetadataValue = this.submissionItem.firstMetadata(this.relationshipOptions.metadataField, { value: this.relatedItem.uuid });
+      if (hasValue(relationMD)) {
+        const metadataRepresentationMD: MetadataValue = this.submissionItem.firstMetadata(this.metadataFields, { authority: relationMD.authority });
+        this.metadataRepresentation = Object.assign(
+          new ItemMetadataRepresentation(metadataRepresentationMD),
+          this.relatedItem
+        )
+      }
+    }));
+  }
+
+  /**
+   * Removes the selected relationship from the list
+   */
+  removeSelection() {
+    this.selectableListService.deselectSingle(this.listId, Object.assign(new ItemSearchResult(), { indexableObject: this.relatedItem }));
+    this.store.dispatch(new RemoveRelationshipAction(this.submissionItem, this.relatedItem, this.relationshipOptions.relationshipType))
+  }
+
+  /**
+   * Unsubscribe from all subscriptions
+   */
+  ngOnDestroy(): void {
+    this.subs
+      .filter((sub) => hasValue(sub))
+      .forEach((sub) => sub.unsubscribe());
+  }
+
+}
+// tslint:enable:max-classes-per-file
diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/disabled/dynamic-disabled.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/disabled/dynamic-disabled.component.ts
index 490be050efa20c9dd046650b3fbcf9d63d642725..9032cb48cb9b121994939360cd27c5fe75e7d63b 100644
--- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/disabled/dynamic-disabled.component.ts
+++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/disabled/dynamic-disabled.component.ts
@@ -11,6 +11,9 @@ import { DynamicDisabledModel } from './dynamic-disabled.model';
   selector: 'ds-dynamic-disabled',
   templateUrl: './dynamic-disabled.component.html'
 })
+/**
+ * Component for displaying a form input with a disabled property
+ */
 export class DsDynamicDisabledComponent extends DynamicFormControlComponent {
 
   @Input() formId: string;
diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component.html b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component.html
index 52f983e7230585d253a2251528556677403ed42c..328cdc6763301a67d38deab630347bb5e37387ad 100644
--- a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component.html
+++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component.html
@@ -7,7 +7,7 @@
 </div>
 <div class="modal-body">
     <ngb-tabset>
-        <ngb-tab [title]="'submission.sections.describe.relationship-lookup.search-tab.tab-title.' + label | translate">
+        <ngb-tab [title]="'submission.sections.describe.relationship-lookup.search-tab.tab-title.' + label | translate  : {count: (totalInternal$ | async)}">
             <ng-template ngbTabContent>
                 <ds-dynamic-lookup-relation-search-tab
                         [selection$]="selection$"
@@ -21,6 +21,22 @@
                 </ds-dynamic-lookup-relation-search-tab>
             </ng-template>
         </ngb-tab>
+        <ngb-tab *ngFor="let source of (externalSourcesRD$ | async)?.payload?.page; let idx = index"
+                 [title]="'submission.sections.describe.relationship-lookup.search-tab.tab-title.' + source.id | translate  : {count: (totalExternal$ | async)[idx]}">
+            <ng-template ngbTabContent>
+                <ds-dynamic-lookup-relation-external-source-tab
+                        [label]="label"
+                        [listId]="listId"
+                        [item]="item"
+                        [collection]="collection"
+                        [relationship]="relationshipOptions"
+                        [context]="context"
+                        [externalSource]="source"
+                        (importedObject)="imported($event)"
+                        class="d-block pt-3">
+                </ds-dynamic-lookup-relation-external-source-tab>
+            </ng-template>
+        </ngb-tab>
         <ngb-tab [title]="'submission.sections.describe.relationship-lookup.selection-tab.tab-title' | translate : {count: (selection$ | async)?.length}">
             <ng-template ngbTabContent>
                 <ds-dynamic-lookup-relation-selection-tab
@@ -42,4 +58,4 @@
     <div>
         <button type="button" class="btn btn-danger" (click)="close()">{{ ('submission.sections.describe.relationship-lookup.close' | translate) }}</button>
     </div>
-</div>
\ No newline at end of file
+</div>
diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component.scss b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component.scss
index 4fb77a7590d30738b6c349f1a0bf0f390f430a92..42c94c1f689961bc91a1f35f456e1b5b9d04265e 100644
--- a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component.scss
+++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component.scss
@@ -1,3 +1,11 @@
 .modal-footer {
     justify-content: space-between;
 }
+
+/* Render child-modals slightly smaller than this modal to avoid complete overlap */
+:host {
+  ::ng-deep .modal-content {
+    width: 90%;
+    margin: 5%;
+  }
+}
diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component.spec.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component.spec.ts
index a4f77fd36491f650305c4506a1faf644e60769f6..d1b289bf113eb85c3e8589506d95db62be6354de 100644
--- a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component.spec.ts
+++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component.spec.ts
@@ -13,6 +13,12 @@ import { Item } from '../../../../../core/shared/item.model';
 import { ItemSearchResult } from '../../../../object-collection/shared/item-search-result.model';
 import { RelationshipOptions } from '../../models/relationship-options.model';
 import { AddRelationshipAction, RemoveRelationshipAction } from './relationship.actions';
+import { SearchConfigurationService } from '../../../../../core/shared/search/search-configuration.service';
+import { PaginatedSearchOptions } from '../../../../search/paginated-search-options.model';
+import { ExternalSource } from '../../../../../core/shared/external-source.model';
+import { createPaginatedList, createSuccessfulRemoteDataObject$ } from '../../../../testing/utils';
+import { ExternalSourceService } from '../../../../../core/data/external-source.service';
+import { LookupRelationService } from '../../../../../core/data/lookup-relation.service';
 
 describe('DsDynamicLookupRelationModalComponent', () => {
   let component: DsDynamicLookupRelationModalComponent;
@@ -28,6 +34,24 @@ describe('DsDynamicLookupRelationModalComponent', () => {
   let relationship;
   let nameVariant;
   let metadataField;
+  let pSearchOptions;
+  let externalSourceService;
+  let lookupRelationService;
+
+  const externalSources = [
+    Object.assign(new ExternalSource(), {
+      id: 'orcidV2',
+      name: 'orcidV2',
+      hierarchical: false
+    }),
+    Object.assign(new ExternalSource(), {
+      id: 'sherpaPublisher',
+      name: 'sherpaPublisher',
+      hierarchical: false
+    })
+  ];
+  const totalLocal = 10;
+  const totalExternal = 8;
 
   function init() {
     item = Object.assign(new Item(), { uuid: '7680ca97-e2bd-4398-bfa7-139a8673dc42', metadata: {} });
@@ -41,6 +65,14 @@ describe('DsDynamicLookupRelationModalComponent', () => {
     relationship = { filter: 'filter', relationshipType: 'isAuthorOfPublication', nameVariants: true } as RelationshipOptions;
     nameVariant = 'Doe, J.';
     metadataField = 'dc.contributor.author';
+    pSearchOptions = new PaginatedSearchOptions({});
+    externalSourceService = jasmine.createSpyObj('externalSourceService', {
+      findAll: createSuccessfulRemoteDataObject$(createPaginatedList(externalSources))
+    });
+    lookupRelationService = jasmine.createSpyObj('lookupRelationService', {
+      getTotalLocalResults: observableOf(totalLocal),
+      getTotalExternalResults: observableOf(totalExternal)
+    });
   }
 
   beforeEach(async(() => {
@@ -49,6 +81,13 @@ describe('DsDynamicLookupRelationModalComponent', () => {
       declarations: [DsDynamicLookupRelationModalComponent],
       imports: [TranslateModule.forRoot(), RouterTestingModule.withRoutes([]), NgbModule.forRoot()],
       providers: [
+        {
+          provide: SearchConfigurationService, useValue: {
+            paginatedSearchOptions: observableOf(pSearchOptions)
+          }
+        },
+        { provide: ExternalSourceService, useValue: externalSourceService },
+        { provide: LookupRelationService, useValue: lookupRelationService },
         {
           provide: SelectableListService, useValue: selectableListService
         },
diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component.ts
index 7e6a9b3981dceea285a48efa5f88a08048f1d204..edf54bf08bf2ae4d3529878c23ad124b44937c0e 100644
--- a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component.ts
+++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component.ts
@@ -1,5 +1,5 @@
 import { Component, NgZone, OnDestroy, OnInit } from '@angular/core';
-import { combineLatest, Observable, Subscription } from 'rxjs';
+import { combineLatest, Observable, Subscription, zip as observableZip } from 'rxjs';
 import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
 import { hasValue } from '../../../../empty.util';
 import { map, skip, switchMap, take } from 'rxjs/operators';
@@ -11,7 +11,11 @@ import { ListableObject } from '../../../../object-collection/shared/listable-ob
 import { RelationshipOptions } from '../../models/relationship-options.model';
 import { SearchResult } from '../../../../search/search-result.model';
 import { Item } from '../../../../../core/shared/item.model';
-import { getRemoteDataPayload, getSucceededRemoteData } from '../../../../../core/shared/operators';
+import {
+  getAllSucceededRemoteData,
+  getRemoteDataPayload,
+  getSucceededRemoteData
+} from '../../../../../core/shared/operators';
 import { AddRelationshipAction, RemoveRelationshipAction, UpdateRelationshipAction } from './relationship.actions';
 import { RelationshipService } from '../../../../../core/data/relationship.service';
 import { RelationshipTypeService } from '../../../../../core/data/relationship-type.service';
@@ -20,6 +24,11 @@ import { AppState } from '../../../../../app.reducer';
 import { Context } from '../../../../../core/shared/context.model';
 import { Relationship } from '../../../../../core/shared/item-relationships/relationship.model';
 import { MetadataValue } from '../../../../../core/shared/metadata.models';
+import { LookupRelationService } from '../../../../../core/data/lookup-relation.service';
+import { RemoteData } from '../../../../../core/data/remote-data';
+import { PaginatedList } from '../../../../../core/data/paginated-list';
+import { ExternalSource } from '../../../../../core/shared/external-source.model';
+import { ExternalSourceService } from '../../../../../core/data/external-source.service';
 
 @Component({
   selector: 'ds-dynamic-lookup-relation-modal',
@@ -37,23 +46,81 @@ import { MetadataValue } from '../../../../../core/shared/metadata.models';
  * Represents a modal where the submitter can select items to be added as a certain relationship type to the object being submitted
  */
 export class DsDynamicLookupRelationModalComponent implements OnInit, OnDestroy {
+  /**
+   * The label to use to display i18n messages (describing the type of relationship)
+   */
   label: string;
+
+  /**
+   * Options for searching related items
+   */
   relationshipOptions: RelationshipOptions;
+
+  /**
+   * The ID of the list to add/remove selected items to/from
+   */
   listId: string;
+
+  /**
+   * The item we're adding relationships to
+   */
   item;
+
+  /**
+   * The collection we're submitting an item to
+   */
+  collection;
+
+  /**
+   * Is the selection repeatable?
+   */
   repeatable: boolean;
+
+  /**
+   * The list of selected items
+   */
   selection$: Observable<ListableObject[]>;
+
+  /**
+   * The context to display lists
+   */
   context: Context;
+
+  /**
+   * The metadata-fields describing these relationships
+   */
   metadataFields: string;
+
+  /**
+   * A map of subscriptions within this component
+   */
   subMap: {
     [uuid: string]: Subscription
   } = {};
 
+  /**
+   * A list of the available external sources configured for this relationship
+   */
+  externalSourcesRD$: Observable<RemoteData<PaginatedList<ExternalSource>>>;
+
+  /**
+   * The total amount of internal items for the current options
+   */
+  totalInternal$: Observable<number>;
+
+  /**
+   * The total amount of results for each external source using the current options
+   */
+  totalExternal$: Observable<number[]>;
+
   constructor(
     public modal: NgbActiveModal,
     private selectableListService: SelectableListService,
     private relationshipService: RelationshipService,
     private relationshipTypeService: RelationshipTypeService,
+    private externalSourceService: ExternalSourceService,
+    private lookupRelationService: LookupRelationService,
+    private searchConfigService: SearchConfigurationService,
     private zone: NgZone,
     private store: Store<AppState>
   ) {
@@ -70,13 +137,19 @@ export class DsDynamicLookupRelationModalComponent implements OnInit, OnDestroy
       this.context = Context.SubmissionModal;
     }
 
-    // this.setExistingNameVariants();
+    this.externalSourcesRD$ = this.externalSourceService.findAll();
+
+    this.setTotals();
   }
 
   close() {
     this.modal.close();
   }
 
+  /**
+   * Select (a list of) objects and add them to the store
+   * @param selectableObjects
+   */
   select(...selectableObjects: Array<SearchResult<Item>>) {
     this.zone.runOutsideAngular(
       () => {
@@ -104,6 +177,10 @@ export class DsDynamicLookupRelationModalComponent implements OnInit, OnDestroy
       });
   }
 
+  /**
+   * Add a subscription updating relationships with name variants
+   * @param sri The search result to track name variants for
+   */
   private addNameVariantSubscription(sri: SearchResult<Item>) {
     const nameVariant$ = this.relationshipService.getNameVariant(this.listId, sri.indexableObject.uuid);
     this.subMap[sri.indexableObject.uuid] = nameVariant$.pipe(
@@ -111,6 +188,10 @@ export class DsDynamicLookupRelationModalComponent implements OnInit, OnDestroy
     ).subscribe((nameVariant: string) => this.store.dispatch(new UpdateRelationshipAction(this.item, sri.indexableObject, this.relationshipOptions.relationshipType, nameVariant)))
   }
 
+  /**
+   * Deselect (a list of) objects and remove them from the store
+   * @param selectableObjects
+   */
   deselect(...selectableObjects: Array<SearchResult<Item>>) {
     this.zone.runOutsideAngular(
       () => selectableObjects.forEach((object) => {
@@ -120,6 +201,9 @@ export class DsDynamicLookupRelationModalComponent implements OnInit, OnDestroy
     );
   }
 
+  /**
+   * Set existing name variants for items by the item's virtual metadata
+   */
   private setExistingNameVariants() {
     const virtualMDs: MetadataValue[] = this.item.allMetadata(this.metadataFields).filter((mdValue) => mdValue.isVirtual);
 
@@ -154,6 +238,37 @@ export class DsDynamicLookupRelationModalComponent implements OnInit, OnDestroy
     )
   }
 
+  /**
+   * Called when an external object has been imported, resets the total values and adds the object to the selected list
+   * @param object
+   */
+  imported(object) {
+    this.setTotals();
+    this.select(object);
+  }
+
+  /**
+   * Calculate and set the total entries available for each tab
+   */
+  setTotals() {
+    this.totalInternal$ = this.searchConfigService.paginatedSearchOptions.pipe(
+      switchMap((options) => this.lookupRelationService.getTotalLocalResults(this.relationshipOptions, options))
+    );
+
+    const externalSourcesAndOptions$ = combineLatest(
+      this.externalSourcesRD$.pipe(
+        getAllSucceededRemoteData(),
+        getRemoteDataPayload()
+      ),
+      this.searchConfigService.paginatedSearchOptions
+    );
+
+    this.totalExternal$ = externalSourcesAndOptions$.pipe(
+      switchMap(([sources, options]) =>
+        observableZip(...sources.page.map((source: ExternalSource) => this.lookupRelationService.getTotalExternalResults(source, options))))
+    );
+  }
+
   ngOnDestroy() {
     Object.values(this.subMap).forEach((subscription) => subscription.unsubscribe());
   }
diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/dynamic-lookup-relation-external-source-tab.component.html b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/dynamic-lookup-relation-external-source-tab.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..04737c44e47b3e677c46a71df7dd5225b0c6344e
--- /dev/null
+++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/dynamic-lookup-relation-external-source-tab.component.html
@@ -0,0 +1,31 @@
+<div class="row">
+  <div class="col-4">
+    <h3>{{ 'submission.sections.describe.relationship-lookup.selection-tab.settings' | translate}}</h3>
+    <ds-page-size-selector></ds-page-size-selector>
+  </div>
+  <div class="col-8">
+    <ds-search-form [query]="(searchConfigService.paginatedSearchOptions | async)?.query" [inPlaceSearch]="true"></ds-search-form>
+    <div>
+      <h3>{{ 'submission.sections.describe.relationship-lookup.selection-tab.title.' + externalSource.id | translate}}</h3>
+      <ng-container *ngVar="(entriesRD$ | async) as entriesRD">
+        <ds-viewable-collection *ngIf="entriesRD?.hasSucceeded && !entriesRD?.isLoading && entriesRD?.payload?.page?.length > 0" @fadeIn
+                                [objects]="entriesRD"
+                                [selectionConfig]="{ repeatable: repeatable, listId: listId }"
+                                [config]="initialPagination"
+                                [hideGear]="true"
+                                [context]="context"
+                                [importable]="true"
+                                [importConfig]="importConfig"
+                                (importObject)="import($event)">
+        </ds-viewable-collection>
+        <ds-loading *ngIf="!entriesRD || !entriesRD?.payload || entriesRD?.isLoading"
+                    message="{{'loading.search-results' | translate}}"></ds-loading>
+        <ds-error   *ngIf="entriesRD?.hasFailed && (!entriesRD?.error || entriesRD?.error?.statusCode != 400)"
+                    message="{{'error.search-results' | translate}}"></ds-error>
+        <div *ngIf="entriesRD?.payload?.page?.length == 0 || entriesRD?.error?.statusCode == 400" id="empty-external-entry-list">
+          {{ 'search.results.empty' | translate }}
+        </div>
+      </ng-container>
+    </div>
+  </div>
+</div>
diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/dynamic-lookup-relation-external-source-tab.component.scss b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/dynamic-lookup-relation-external-source-tab.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/dynamic-lookup-relation-external-source-tab.component.spec.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/dynamic-lookup-relation-external-source-tab.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..00242ad9cea0dfdfacb93d7d469da2c02171d983
--- /dev/null
+++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/dynamic-lookup-relation-external-source-tab.component.spec.ts
@@ -0,0 +1,192 @@
+import { DsDynamicLookupRelationExternalSourceTabComponent } from './dynamic-lookup-relation-external-source-tab.component';
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { VarDirective } from '../../../../../utils/var.directive';
+import { TranslateModule } from '@ngx-translate/core';
+import { RouterTestingModule } from '@angular/router/testing';
+import { EventEmitter, NO_ERRORS_SCHEMA } from '@angular/core';
+import { PaginatedSearchOptions } from '../../../../../search/paginated-search-options.model';
+import { SearchConfigurationService } from '../../../../../../core/shared/search/search-configuration.service';
+import { of as observableOf } from 'rxjs/internal/observable/of';
+import {
+  createFailedRemoteDataObject$,
+  createPaginatedList,
+  createPendingRemoteDataObject$,
+  createSuccessfulRemoteDataObject$
+} from '../../../../../testing/utils';
+import { ExternalSourceService } from '../../../../../../core/data/external-source.service';
+import { ExternalSource } from '../../../../../../core/shared/external-source.model';
+import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+import { By } from '@angular/platform-browser';
+import { ExternalSourceEntry } from '../../../../../../core/shared/external-source-entry.model';
+import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
+import { SelectableListService } from '../../../../../object-list/selectable-list/selectable-list.service';
+import { Item } from '../../../../../../core/shared/item.model';
+import { Collection } from '../../../../../../core/shared/collection.model';
+import { RelationshipOptions } from '../../../models/relationship-options.model';
+import { ExternalSourceEntryImportModalComponent } from './external-source-entry-import-modal/external-source-entry-import-modal.component';
+
+describe('DsDynamicLookupRelationExternalSourceTabComponent', () => {
+  let component: DsDynamicLookupRelationExternalSourceTabComponent;
+  let fixture: ComponentFixture<DsDynamicLookupRelationExternalSourceTabComponent>;
+  let pSearchOptions;
+  let externalSourceService;
+  let selectableListService;
+  let modalService;
+
+  const externalSource = {
+    id: 'orcidV2',
+    name: 'orcidV2',
+    hierarchical: false
+  } as ExternalSource;
+  const externalEntries = [
+    Object.assign({
+      id: '0001-0001-0001-0001',
+      display: 'John Doe',
+      value: 'John, Doe',
+      metadata: {
+        'dc.identifier.uri': [
+          {
+            value: 'https://orcid.org/0001-0001-0001-0001'
+          }
+        ]
+      }
+    }),
+    Object.assign({
+      id: '0001-0001-0001-0002',
+      display: 'Sampson Megan',
+      value: 'Sampson, Megan',
+      metadata: {
+        'dc.identifier.uri': [
+          {
+            value: 'https://orcid.org/0001-0001-0001-0002'
+          }
+        ]
+      }
+    }),
+    Object.assign({
+      id: '0001-0001-0001-0003',
+      display: 'Edwards Anna',
+      value: 'Edwards, Anna',
+      metadata: {
+        'dc.identifier.uri': [
+          {
+            value: 'https://orcid.org/0001-0001-0001-0003'
+          }
+        ]
+      }
+    })
+  ] as ExternalSourceEntry[];
+  const item = Object.assign(new Item(), { id: 'submission-item' });
+  const collection = Object.assign(new Collection(), { id: 'submission-collection' });
+  const relationship = Object.assign(new RelationshipOptions(), { relationshipType: 'isAuthorOfPublication' });
+  const label = 'Author';
+
+  function init() {
+    pSearchOptions = new PaginatedSearchOptions({
+      query: 'test'
+    });
+    externalSourceService = jasmine.createSpyObj('externalSourceService', {
+      getExternalSourceEntries: createSuccessfulRemoteDataObject$(createPaginatedList(externalEntries))
+    });
+    selectableListService = jasmine.createSpyObj('selectableListService', ['selectSingle']);
+  }
+
+  beforeEach(async(() => {
+    init();
+    TestBed.configureTestingModule({
+      declarations: [DsDynamicLookupRelationExternalSourceTabComponent, VarDirective],
+      imports: [TranslateModule.forRoot(), RouterTestingModule.withRoutes([]), NgbModule.forRoot(), BrowserAnimationsModule],
+      providers: [
+        {
+          provide: SearchConfigurationService, useValue: {
+            paginatedSearchOptions: observableOf(pSearchOptions)
+          }
+        },
+        { provide: ExternalSourceService, useValue: externalSourceService },
+        { provide: SelectableListService, useValue: selectableListService }
+      ],
+      schemas: [NO_ERRORS_SCHEMA]
+    }).compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(DsDynamicLookupRelationExternalSourceTabComponent);
+    component = fixture.componentInstance;
+    component.externalSource = externalSource;
+    component.item = item;
+    component.collection = collection;
+    component.relationship = relationship;
+    component.label = label;
+    modalService = (component as any).modalService;
+    fixture.detectChanges();
+  });
+
+  describe('when the external entries finished loading successfully', () => {
+    it('should display a ds-viewable-collection component', () => {
+      const viewableCollection = fixture.debugElement.query(By.css('ds-viewable-collection'));
+      expect(viewableCollection).toBeDefined();
+    });
+  });
+
+  describe('when the external entries are loading', () => {
+    beforeEach(() => {
+      component.entriesRD$ = createPendingRemoteDataObject$(undefined);
+      fixture.detectChanges();
+    });
+
+    it('should not display a ds-viewable-collection component', () => {
+      const viewableCollection = fixture.debugElement.query(By.css('ds-viewable-collection'));
+      expect(viewableCollection).toBeNull();
+    });
+
+    it('should display a ds-loading component', () => {
+      const loading = fixture.debugElement.query(By.css('ds-loading'));
+      expect(loading).not.toBeNull();
+    });
+  });
+
+  describe('when the external entries failed loading', () => {
+    beforeEach(() => {
+      component.entriesRD$ = createFailedRemoteDataObject$(undefined);
+      fixture.detectChanges();
+    });
+
+    it('should not display a ds-viewable-collection component', () => {
+      const viewableCollection = fixture.debugElement.query(By.css('ds-viewable-collection'));
+      expect(viewableCollection).toBeNull();
+    });
+
+    it('should display a ds-error component', () => {
+      const error = fixture.debugElement.query(By.css('ds-error'));
+      expect(error).not.toBeNull();
+    });
+  });
+
+  describe('when the external entries return an empty list', () => {
+    beforeEach(() => {
+      component.entriesRD$ = createSuccessfulRemoteDataObject$(createPaginatedList([]));
+      fixture.detectChanges();
+    });
+
+    it('should not display a ds-viewable-collection component', () => {
+      const viewableCollection = fixture.debugElement.query(By.css('ds-viewable-collection'));
+      expect(viewableCollection).toBeNull();
+    });
+
+    it('should display a message the list is empty', () => {
+      const empty = fixture.debugElement.query(By.css('#empty-external-entry-list'));
+      expect(empty).not.toBeNull();
+    });
+  });
+
+  describe('import', () => {
+    beforeEach(() => {
+      spyOn(modalService, 'open').and.returnValue(Object.assign({ componentInstance: Object.assign({ importedObject: new EventEmitter<any>() }) }));
+      component.import(externalEntries[0]);
+    });
+
+    it('should open a new ExternalSourceEntryImportModalComponent', () => {
+      expect(modalService.open).toHaveBeenCalledWith(ExternalSourceEntryImportModalComponent, jasmine.any(Object))
+    });
+  });
+});
diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/dynamic-lookup-relation-external-source-tab.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/dynamic-lookup-relation-external-source-tab.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c8b3b3d311e8298fbe22dc8d37966c4b06f09dbe
--- /dev/null
+++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/dynamic-lookup-relation-external-source-tab.component.ts
@@ -0,0 +1,163 @@
+import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core';
+import { SEARCH_CONFIG_SERVICE } from '../../../../../../+my-dspace-page/my-dspace-page.component';
+import { SearchConfigurationService } from '../../../../../../core/shared/search/search-configuration.service';
+import { Router } from '@angular/router';
+import { ExternalSourceService } from '../../../../../../core/data/external-source.service';
+import { Observable } from 'rxjs/internal/Observable';
+import { RemoteData } from '../../../../../../core/data/remote-data';
+import { PaginatedList } from '../../../../../../core/data/paginated-list';
+import { ExternalSourceEntry } from '../../../../../../core/shared/external-source-entry.model';
+import { ExternalSource } from '../../../../../../core/shared/external-source.model';
+import { startWith, switchMap } from 'rxjs/operators';
+import { PaginatedSearchOptions } from '../../../../../search/paginated-search-options.model';
+import { Context } from '../../../../../../core/shared/context.model';
+import { ListableObject } from '../../../../../object-collection/shared/listable-object.model';
+import { fadeIn, fadeInOut } from '../../../../../animations/fade';
+import { PaginationComponentOptions } from '../../../../../pagination/pagination-component-options.model';
+import { RelationshipOptions } from '../../../models/relationship-options.model';
+import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
+import { ExternalSourceEntryImportModalComponent } from './external-source-entry-import-modal/external-source-entry-import-modal.component';
+import { Subscription } from 'rxjs/internal/Subscription';
+import { hasValue } from '../../../../../empty.util';
+import { SelectableListService } from '../../../../../object-list/selectable-list/selectable-list.service';
+import { Item } from '../../../../../../core/shared/item.model';
+import { Collection } from '../../../../../../core/shared/collection.model';
+
+@Component({
+  selector: 'ds-dynamic-lookup-relation-external-source-tab',
+  styleUrls: ['./dynamic-lookup-relation-external-source-tab.component.scss'],
+  templateUrl: './dynamic-lookup-relation-external-source-tab.component.html',
+  providers: [
+    {
+      provide: SEARCH_CONFIG_SERVICE,
+      useClass: SearchConfigurationService
+    }
+  ],
+  animations: [
+    fadeIn,
+    fadeInOut
+  ]
+})
+/**
+ * Component rendering the tab content of an external source during submission lookup
+ * Shows a list of entries matching the current search query with the option to import them into the repository
+ */
+export class DsDynamicLookupRelationExternalSourceTabComponent implements OnInit, OnDestroy {
+  /**
+   * The label to use for all messages (added to the end of relevant i18n keys)
+   */
+  @Input() label: string;
+
+  /**
+   * The ID of the list to add/remove selected items to/from
+   */
+  @Input() listId: string;
+
+  /**
+   * The item in submission
+   */
+  @Input() item: Item;
+
+  /**
+   * The collection the user is submitting an item into
+   */
+  @Input() collection: Collection;
+
+  /**
+   * The relationship-options for the current lookup
+   */
+  @Input() relationship: RelationshipOptions;
+
+  /**
+   * The context to displaying lists for
+   */
+  @Input() context: Context;
+
+  /**
+   * Emit an event when an object has been imported (or selected from similar local entries)
+   */
+  @Output() importedObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
+
+  /**
+   * The initial pagination options
+   */
+  initialPagination = Object.assign(new PaginationComponentOptions(), {
+    id: 'submission-external-source-relation-list',
+    pageSize: 5
+  });
+
+  /**
+   * The external source we're selecting entries for
+   */
+  @Input() externalSource: ExternalSource;
+
+  /**
+   * The displayed list of entries
+   */
+  entriesRD$: Observable<RemoteData<PaginatedList<ExternalSourceEntry>>>;
+
+  /**
+   * Config to use for the import buttons
+   */
+  importConfig;
+
+  /**
+   * The modal for importing the entry
+   */
+  modalRef: NgbModalRef;
+
+  /**
+   * Subscription to the modal's importedObject event-emitter
+   */
+  importObjectSub: Subscription;
+
+  constructor(private router: Router,
+              public searchConfigService: SearchConfigurationService,
+              private externalSourceService: ExternalSourceService,
+              private modalService: NgbModal,
+              private selectableListService: SelectableListService) {
+  }
+
+  /**
+   * Get the entries for the selected external source
+   */
+  ngOnInit(): void {
+    this.entriesRD$ = this.searchConfigService.paginatedSearchOptions.pipe(
+      switchMap((searchOptions: PaginatedSearchOptions) =>
+        this.externalSourceService.getExternalSourceEntries(this.externalSource.id, searchOptions).pipe(startWith(undefined)))
+    );
+    this.importConfig = {
+      buttonLabel: 'submission.sections.describe.relationship-lookup.external-source.import-button-title.' + this.label
+    };
+  }
+
+  /**
+   * Start the import of an entry by opening up an import modal window
+   * @param entry The entry to import
+   */
+  import(entry) {
+    this.modalRef = this.modalService.open(ExternalSourceEntryImportModalComponent, {
+      size: 'lg',
+      container: 'ds-dynamic-lookup-relation-modal'
+    });
+    const modalComp = this.modalRef.componentInstance;
+    modalComp.externalSourceEntry = entry;
+    modalComp.item = this.item;
+    modalComp.collection = this.collection;
+    modalComp.relationship = this.relationship;
+    modalComp.label = this.label;
+    this.importObjectSub = modalComp.importedObject.subscribe((object) => {
+      this.selectableListService.selectSingle(this.listId, object);
+      this.importedObject.emit(object);
+    });
+  }
+
+  /**
+   * Unsubscribe from open subscriptions
+   */
+  ngOnDestroy(): void {
+    if (hasValue(this.importObjectSub)) {
+      this.importObjectSub.unsubscribe();
+    }
+  }
+}
diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/external-source-entry-import-modal/external-source-entry-import-modal.component.html b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/external-source-entry-import-modal/external-source-entry-import-modal.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..a4fc356ef96fdc381522a67ff41f8f73aacb367c
--- /dev/null
+++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/external-source-entry-import-modal/external-source-entry-import-modal.component.html
@@ -0,0 +1,61 @@
+<div class="modal-header">
+  <h4 class="modal-title" id="modal-title">{{ (labelPrefix + label + '.title') | translate }}</h4>
+  <button type="button" class="close" aria-label="Close button" aria-describedby="modal-title"
+          (click)="modal.dismiss()">
+    <span aria-hidden="true">&times;</span>
+  </button>
+</div>
+<div class="modal-body">
+  <h4>{{ (labelPrefix + 'head.' + externalSourceEntry.externalSource | translate) }}</h4>
+  <div id="external-source-entry-information" class="mb-3">
+    <div><span>{{externalSourceEntry.display}}</span></div>
+    <div *ngIf="uri"><a href="{{uri.value}}">{{uri.value}}</a></div>
+  </div>
+
+  <h4>{{ (labelPrefix + 'select' | translate) }}</h4>
+
+  <div id="external-source-entry-entities" class="mb-3">
+    <h5 class="font-weight-bold">{{ (labelPrefix + 'entities' | translate) }}</h5>
+
+    <div id="external-source-entry-collection" class="mb-3">
+      <div class="form-group">
+        <label for="collection">{{ (labelPrefix + 'collection' | translate) }}</label>
+        <input type="text" class="form-control" id="collection" placeholder="Enter collection ID" [(ngModel)]="collectionId">
+      </div>
+    </div>
+
+    <ds-search-results *ngIf="(localEntitiesRD$ | async)?.payload?.page?.length > 0"
+                       [searchResults]="(localEntitiesRD$ | async)"
+                       [sortConfig]="this.lookupRelationService.searchConfig?.sort"
+                       [searchConfig]="this.lookupRelationService.searchConfig"
+                       [selectable]="true"
+                       [disableHeader]="true"
+                       [hidePaginationDetail]="true"
+                       [selectionConfig]="{ repeatable: false, listId: entityListId }"
+                       [linkType]="linkTypes.ExternalLink"
+                       [context]="context"
+                       (deselectObject)="deselectEntity()"
+                       (selectObject)="selectEntity($event)">
+    </ds-search-results>
+    <div class="ml-4">
+      <input class="form-check-input" type="radio" name="new-entity" id="new-entity" value="new-entity" (click)="selectNewEntity()" [checked]="selectedImportType === importType.NewEntity" />
+      <label class="form-check-label" for="new-entity">{{ (labelPrefix + 'entities.new' | translate) }}</label>
+    </div>
+  </div>
+  <div id="external-source-entry-authority" *ngIf="authorityEnabled">
+    <h5 class="font-weight-bold">{{ (labelPrefix + 'authority' | translate) }}</h5>
+
+    <div class="ml-4">
+      <input class="form-check-input" type="radio" name="new-authority" id="new-authority" value="new-authority" (click)="selectNewAuthority()" [checked]="selectedImportType === importType.NewAuthority" />
+      <label class="form-check-label" for="new-authority">{{ (labelPrefix + 'authority.new' | translate) }}</label>
+    </div>
+  </div>
+</div>
+<div class="modal-footer">
+  <div>
+    <button type="button" class="btn btn-outline-secondary" (click)="close()">{{ (labelPrefix + 'cancel' | translate) }}</button>
+  </div>
+  <div>
+    <button type="button" class="btn btn-primary" [disabled]="selectedImportType === importType.None" (click)="import()">{{ (labelPrefix + 'import' | translate) }}</button>
+  </div>
+</div>
diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/external-source-entry-import-modal/external-source-entry-import-modal.component.scss b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/external-source-entry-import-modal/external-source-entry-import-modal.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..7db9839e38444348cad05bacdfc8d61449da7275
--- /dev/null
+++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/external-source-entry-import-modal/external-source-entry-import-modal.component.scss
@@ -0,0 +1,3 @@
+.modal-footer {
+  justify-content: space-between;
+}
diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/external-source-entry-import-modal/external-source-entry-import-modal.component.spec.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/external-source-entry-import-modal/external-source-entry-import-modal.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5248f955737b83ca565cb2884aed2148f5ead829
--- /dev/null
+++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/external-source-entry-import-modal/external-source-entry-import-modal.component.spec.ts
@@ -0,0 +1,194 @@
+import { ExternalSourceEntryImportModalComponent, ImportType } from './external-source-entry-import-modal.component';
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { TranslateModule } from '@ngx-translate/core';
+import { RouterTestingModule } from '@angular/router/testing';
+import { NO_ERRORS_SCHEMA } from '@angular/core';
+import { NgbActiveModal, NgbModule } from '@ng-bootstrap/ng-bootstrap';
+import { LookupRelationService } from '../../../../../../../core/data/lookup-relation.service';
+import { ExternalSourceEntry } from '../../../../../../../core/shared/external-source-entry.model';
+import { Item } from '../../../../../../../core/shared/item.model';
+import { ItemSearchResult } from '../../../../../../object-collection/shared/item-search-result.model';
+import { createPaginatedList, createSuccessfulRemoteDataObject$ } from '../../../../../../testing/utils';
+import { Collection } from '../../../../../../../core/shared/collection.model';
+import { RelationshipOptions } from '../../../../models/relationship-options.model';
+import { SelectableListService } from '../../../../../../object-list/selectable-list/selectable-list.service';
+import { ItemDataService } from '../../../../../../../core/data/item-data.service';
+import { NotificationsService } from '../../../../../../notifications/notifications.service';
+
+describe('DsDynamicLookupRelationExternalSourceTabComponent', () => {
+  let component: ExternalSourceEntryImportModalComponent;
+  let fixture: ComponentFixture<ExternalSourceEntryImportModalComponent>;
+  let lookupRelationService: LookupRelationService;
+  let selectService: SelectableListService;
+  let itemService: ItemDataService;
+  let notificationsService: NotificationsService;
+  let modalStub: NgbActiveModal;
+
+  const uri = 'https://orcid.org/0001-0001-0001-0001';
+  const entry = Object.assign(new ExternalSourceEntry(), {
+    id: '0001-0001-0001-0001',
+    display: 'John Doe',
+    value: 'John, Doe',
+    metadata: {
+      'dc.identifier.uri': [
+        {
+          value: uri
+        }
+      ]
+    }
+  });
+
+  const label = 'Author';
+  const relationship = Object.assign(new RelationshipOptions(), { relationshipType: 'isAuthorOfPublication' });
+  const submissionCollection = Object.assign(new Collection(), { uuid: '9398affe-a977-4992-9a1d-6f00908a259f' });
+  const submissionItem = Object.assign(new Item(), { uuid: '26224069-5f99-412a-9e9b-7912a7e35cb1' });
+  const item1 = Object.assign(new Item(), { uuid: 'e1c51c69-896d-42dc-8221-1d5f2ad5516e' });
+  const item2 = Object.assign(new Item(), { uuid: 'c8279647-1acc-41ae-b036-951d5f65649b' });
+  const item3 = Object.assign(new Item(), { uuid: 'c3bcbff5-ec0c-4831-8e4c-94b9c933ccac' });
+  const searchResult1 = Object.assign(new ItemSearchResult(), { indexableObject: item1 });
+  const searchResult2 = Object.assign(new ItemSearchResult(), { indexableObject: item2 });
+  const searchResult3 = Object.assign(new ItemSearchResult(), { indexableObject: item3 });
+  const importedItem = Object.assign(new Item(), { uuid: '5d0098fc-344a-4067-a57d-457092b72e82' });
+
+  function init() {
+    lookupRelationService = jasmine.createSpyObj('lookupRelationService', {
+      getLocalResults: createSuccessfulRemoteDataObject$(createPaginatedList([searchResult1, searchResult2, searchResult3])),
+      removeLocalResultsCache: {}
+    });
+    selectService = jasmine.createSpyObj('selectService', ['deselectAll']);
+    notificationsService = jasmine.createSpyObj('notificationsService', ['success']);
+    itemService = jasmine.createSpyObj('itemService', {
+      importExternalSourceEntry: createSuccessfulRemoteDataObject$(importedItem)
+    });
+    modalStub = jasmine.createSpyObj('modal', ['close']);
+  }
+
+  beforeEach(async(() => {
+    init();
+    TestBed.configureTestingModule({
+      declarations: [ExternalSourceEntryImportModalComponent],
+      imports: [TranslateModule.forRoot(), RouterTestingModule.withRoutes([]), NgbModule.forRoot()],
+      providers: [
+        { provide: LookupRelationService, useValue: lookupRelationService },
+        { provide: SelectableListService, useValue: selectService },
+        { provide: NotificationsService, useValue: notificationsService },
+        { provide: ItemDataService, useValue: itemService },
+        { provide: NgbActiveModal, useValue: modalStub }
+      ],
+      schemas: [NO_ERRORS_SCHEMA]
+    }).compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(ExternalSourceEntryImportModalComponent);
+    component = fixture.componentInstance;
+    component.externalSourceEntry = entry;
+    component.label = label;
+    component.relationship = relationship;
+    component.collection = submissionCollection;
+    component.item = submissionItem;
+    fixture.detectChanges();
+  });
+
+  describe('close', () => {
+    beforeEach(() => {
+      component.close();
+    });
+
+    it('should close the modal', () => {
+      expect(modalStub.close).toHaveBeenCalled();
+    });
+  });
+
+  describe('selectEntity', () => {
+    const entity = Object.assign(new Item(), { uuid: 'd8698de5-5b05-4ea4-9d02-da73803a50f9' });
+
+    beforeEach(() => {
+      component.selectEntity(entity);
+    });
+
+    it('should set selected entity', () => {
+      expect(component.selectedEntity).toBe(entity);
+    });
+
+    it('should set the import type to local entity', () => {
+      expect(component.selectedImportType).toEqual(ImportType.LocalEntity);
+    });
+  });
+
+  describe('deselectEntity', () => {
+    const entity = Object.assign(new Item(), { uuid: 'd8698de5-5b05-4ea4-9d02-da73803a50f9' });
+
+    beforeEach(() => {
+      component.selectedImportType = ImportType.LocalEntity;
+      component.selectedEntity = entity;
+      component.deselectEntity();
+    });
+
+    it('should remove the selected entity', () => {
+      expect(component.selectedEntity).toBeUndefined();
+    });
+
+    it('should set the import type to none', () => {
+      expect(component.selectedImportType).toEqual(ImportType.None);
+    });
+  });
+
+  describe('selectNewEntity', () => {
+    describe('when current import type is set to new entity', () => {
+      beforeEach(() => {
+        component.selectedImportType = ImportType.NewEntity;
+        component.selectNewEntity();
+      });
+
+      it('should set the import type to none', () => {
+        expect(component.selectedImportType).toEqual(ImportType.None);
+      });
+    });
+
+    describe('when current import type is not set to new entity', () => {
+      beforeEach(() => {
+        component.selectedImportType = ImportType.None;
+        component.selectNewEntity();
+      });
+
+      it('should set the import type to new entity', () => {
+        expect(component.selectedImportType).toEqual(ImportType.NewEntity);
+      });
+
+      it('should deselect the entity and authority list', () => {
+        expect(selectService.deselectAll).toHaveBeenCalledWith(component.entityListId);
+        expect(selectService.deselectAll).toHaveBeenCalledWith(component.authorityListId);
+      });
+    });
+  });
+
+  describe('selectNewAuthority', () => {
+    describe('when current import type is set to new authority', () => {
+      beforeEach(() => {
+        component.selectedImportType = ImportType.NewAuthority;
+        component.selectNewAuthority();
+      });
+
+      it('should set the import type to none', () => {
+        expect(component.selectedImportType).toEqual(ImportType.None);
+      });
+    });
+
+    describe('when current import type is not set to new authority', () => {
+      beforeEach(() => {
+        component.selectedImportType = ImportType.None;
+        component.selectNewAuthority();
+      });
+
+      it('should set the import type to new authority', () => {
+        expect(component.selectedImportType).toEqual(ImportType.NewAuthority);
+      });
+
+      it('should deselect the entity and authority list', () => {
+        expect(selectService.deselectAll).toHaveBeenCalledWith(component.entityListId);
+        expect(selectService.deselectAll).toHaveBeenCalledWith(component.authorityListId);
+      });
+    });
+  });
+});
diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/external-source-entry-import-modal/external-source-entry-import-modal.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/external-source-entry-import-modal/external-source-entry-import-modal.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7e0fe787177a522f1b9d0e8a0d51cfdcd70c0c30
--- /dev/null
+++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/external-source-entry-import-modal/external-source-entry-import-modal.component.ts
@@ -0,0 +1,311 @@
+import { Component, EventEmitter, OnInit } from '@angular/core';
+import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
+import { ExternalSourceEntry } from '../../../../../../../core/shared/external-source-entry.model';
+import { MetadataValue } from '../../../../../../../core/shared/metadata.models';
+import { Metadata } from '../../../../../../../core/shared/metadata.utils';
+import { Observable } from 'rxjs/internal/Observable';
+import { RemoteData } from '../../../../../../../core/data/remote-data';
+import { PaginatedList } from '../../../../../../../core/data/paginated-list';
+import { SearchResult } from '../../../../../../search/search-result.model';
+import { Item } from '../../../../../../../core/shared/item.model';
+import { RelationshipOptions } from '../../../../models/relationship-options.model';
+import { LookupRelationService } from '../../../../../../../core/data/lookup-relation.service';
+import { PaginatedSearchOptions } from '../../../../../../search/paginated-search-options.model';
+import { CollectionElementLinkType } from '../../../../../../object-collection/collection-element-link.type';
+import { Context } from '../../../../../../../core/shared/context.model';
+import { SelectableListService } from '../../../../../../object-list/selectable-list/selectable-list.service';
+import { ListableObject } from '../../../../../../object-collection/shared/listable-object.model';
+import { Collection } from '../../../../../../../core/shared/collection.model';
+import { ItemDataService } from '../../../../../../../core/data/item-data.service';
+import { PaginationComponentOptions } from '../../../../../../pagination/pagination-component-options.model';
+import { getRemoteDataPayload, getSucceededRemoteData } from '../../../../../../../core/shared/operators';
+import { take } from 'rxjs/operators';
+import { ItemSearchResult } from '../../../../../../object-collection/shared/item-search-result.model';
+import { NotificationsService } from '../../../../../../notifications/notifications.service';
+import { TranslateService } from '@ngx-translate/core';
+
+/**
+ * The possible types of import for the external entry
+ */
+export enum ImportType {
+  None = 'None',
+  LocalEntity = 'LocalEntity',
+  LocalAuthority = 'LocalAuthority',
+  NewEntity = 'NewEntity',
+  NewAuthority = 'NewAuthority'
+}
+
+@Component({
+  selector: 'ds-external-source-entry-import-modal',
+  styleUrls: ['./external-source-entry-import-modal.component.scss'],
+  templateUrl: './external-source-entry-import-modal.component.html'
+})
+/**
+ * Component to display a modal window for importing an external source entry
+ * Shows information about the selected entry and a selectable list of local entities and authorities with similar names
+ * and the ability to add one of those results to the selection instead of the external entry.
+ * The other option is to import the external entry as a new entity or authority into the repository.
+ */
+export class ExternalSourceEntryImportModalComponent implements OnInit {
+  /**
+   * The prefix for every i18n key within this modal
+   */
+  labelPrefix = 'submission.sections.describe.relationship-lookup.external-source.import-modal.';
+
+  /**
+   * The label to use for all messages (added to the end of relevant i18n keys)
+   */
+  label: string;
+
+  /**
+   * The external source entry
+   */
+  externalSourceEntry: ExternalSourceEntry;
+
+  /**
+   * The item in submission
+   */
+  item: Item;
+
+  /**
+   * The collection the user is submitting in
+   */
+  collection: Collection;
+
+  /**
+   * The ID of the collection to import entries to
+   */
+  collectionId: string;
+
+  /**
+   * The current relationship-options used for filtering results
+   */
+  relationship: RelationshipOptions;
+
+  /**
+   * The metadata value for the entry's uri
+   */
+  uri: MetadataValue;
+
+  /**
+   * Local entities with a similar name
+   */
+  localEntitiesRD$: Observable<RemoteData<PaginatedList<SearchResult<Item>>>>;
+
+  /**
+   * Search options to use for fetching similar results
+   */
+  searchOptions: PaginatedSearchOptions;
+
+  /**
+   * The type of link to render in listable elements
+   */
+  linkTypes = CollectionElementLinkType;
+
+  /**
+   * The context we're currently in (submission)
+   */
+  context = Context.SubmissionModal;
+
+  /**
+   * List ID for selecting local entities
+   */
+  entityListId = 'external-source-import-entity';
+
+  /**
+   * List ID for selecting local authorities
+   */
+  authorityListId = 'external-source-import-authority';
+
+  /**
+   * ImportType enum
+   */
+  importType = ImportType;
+
+  /**
+   * The type of import the user currently has selected
+   */
+  selectedImportType = ImportType.None;
+
+  /**
+   * The selected local entity
+   */
+  selectedEntity: ListableObject;
+
+  /**
+   * The selected local authority
+   */
+  selectedAuthority: ListableObject;
+
+  /**
+   * An object has been imported, send it to the parent component
+   */
+  importedObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
+
+  /**
+   * Should it display the ability to import the entry as an authority?
+   */
+  authorityEnabled = false;
+
+  constructor(public modal: NgbActiveModal,
+              public lookupRelationService: LookupRelationService,
+              private selectService: SelectableListService,
+              private itemService: ItemDataService,
+              private notificationsService: NotificationsService,
+              private translateService: TranslateService) {
+  }
+
+  ngOnInit(): void {
+    this.uri = Metadata.first(this.externalSourceEntry.metadata, 'dc.identifier.uri');
+    const pagination = Object.assign(new PaginationComponentOptions(), { id: 'external-entry-import', pageSize: 5 });
+    this.searchOptions = Object.assign(new PaginatedSearchOptions({ query: this.externalSourceEntry.value, pagination: pagination }));
+    this.localEntitiesRD$ = this.lookupRelationService.getLocalResults(this.relationship, this.searchOptions);
+    this.collectionId = this.collection.id;
+  }
+
+  /**
+   * Close the window
+   */
+  close() {
+    this.modal.close();
+  }
+
+  /**
+   * Perform the import of the external entry
+   */
+  import() {
+    switch (this.selectedImportType) {
+      case ImportType.LocalEntity : {
+        this.importLocalEntity();
+        break;
+      }
+      case ImportType.NewEntity : {
+        this.importNewEntity();
+        break;
+      }
+      case ImportType.LocalAuthority : {
+        this.importLocalAuthority();
+        break;
+      }
+      case ImportType.NewAuthority : {
+        this.importNewAuthority();
+        break;
+      }
+    }
+    this.selectedImportType = ImportType.None;
+    this.deselectAllLists();
+    this.close();
+  }
+
+  /**
+   * Import the selected local entity
+   */
+  importLocalEntity() {
+    if (this.selectedEntity !== undefined) {
+      this.notificationsService.success(this.translateService.get(this.labelPrefix + this.label + '.added.local-entity'));
+      this.importedObject.emit(this.selectedEntity);
+    }
+  }
+
+  /**
+   * Create and import a new entity from the external entry
+   */
+  importNewEntity() {
+    this.itemService.importExternalSourceEntry(this.externalSourceEntry, this.collectionId).pipe(
+      getSucceededRemoteData(),
+      getRemoteDataPayload(),
+      take(1)
+    ).subscribe((item: Item) => {
+      this.lookupRelationService.removeLocalResultsCache();
+      const searchResult = Object.assign(new ItemSearchResult(), {
+        indexableObject: item
+      });
+      this.notificationsService.success(this.translateService.get(this.labelPrefix + this.label + '.added.new-entity'));
+      this.importedObject.emit(searchResult);
+    });
+  }
+
+  /**
+   * Import the selected local authority
+   */
+  importLocalAuthority() {
+    // TODO: Implement ability to import local authorities
+  }
+
+  /**
+   * Create and import a new authority from the external entry
+   */
+  importNewAuthority() {
+    // TODO: Implement ability to import new authorities
+  }
+
+  /**
+   * Deselected a local entity
+   */
+  deselectEntity() {
+    this.selectedEntity = undefined;
+    if (this.selectedImportType === ImportType.LocalEntity) {
+      this.selectedImportType = ImportType.None;
+    }
+  }
+
+  /**
+   * Selected a local entity
+   * @param entity
+   */
+  selectEntity(entity) {
+    this.selectedEntity = entity;
+    this.selectedImportType = ImportType.LocalEntity;
+  }
+
+  /**
+   * Selected/deselected the new entity option
+   */
+  selectNewEntity() {
+    if (this.selectedImportType === ImportType.NewEntity) {
+      this.selectedImportType = ImportType.None;
+    } else {
+      this.selectedImportType = ImportType.NewEntity;
+      this.deselectAllLists();
+    }
+  }
+
+  /**
+   * Deselected a local authority
+   */
+  deselectAuthority() {
+    this.selectedAuthority = undefined;
+    if (this.selectedImportType === ImportType.LocalAuthority) {
+      this.selectedImportType = ImportType.None;
+    }
+  }
+
+  /**
+   * Selected a local authority
+   * @param authority
+   */
+  selectAuthority(authority) {
+    this.selectedAuthority = authority;
+    this.selectedImportType = ImportType.LocalAuthority;
+  }
+
+  /**
+   * Selected/deselected the new authority option
+   */
+  selectNewAuthority() {
+    if (this.selectedImportType === ImportType.NewAuthority) {
+      this.selectedImportType = ImportType.None;
+    } else {
+      this.selectedImportType = ImportType.NewAuthority;
+      this.deselectAllLists();
+    }
+  }
+
+  /**
+   * Deselect every element from both entity and authority lists
+   */
+  deselectAllLists() {
+    this.selectService.deselectAll(this.entityListId);
+    this.selectService.deselectAll(this.authorityListId);
+  }
+}
diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/relationship.effects.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/relationship.effects.ts
index 1880090d13b393fe2a42779d19b9306dcd756fa8..201d50e511a52e5e949168a1aa9989a399e7ff3a 100644
--- a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/relationship.effects.ts
+++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/relationship.effects.ts
@@ -3,6 +3,7 @@ import { Actions, Effect, ofType } from '@ngrx/effects';
 import { debounceTime, map, mergeMap, take, tap } from 'rxjs/operators';
 import { BehaviorSubject } from 'rxjs';
 import { RelationshipService } from '../../../../../core/data/relationship.service';
+import { getSucceededRemoteData } from '../../../../../core/shared/operators';
 import { AddRelationshipAction, RelationshipAction, RelationshipActionTypes, RemoveRelationshipAction, UpdateRelationshipAction } from './relationship.actions';
 import { Item } from '../../../../../core/shared/item.model';
 import { hasNoValue, hasValue, hasValueOperator } from '../../../../empty.util';
@@ -88,7 +89,7 @@ export class RelationshipEffects {
             this.nameVariantUpdates[identifier] = nameVariant;
           } else {
             this.relationshipService.updateNameVariant(item1, item2, relationshipType, nameVariant)
-              .pipe()
+              .pipe(getSucceededRemoteData())
               .subscribe();
           }
         }
diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/search-tab/dynamic-lookup-relation-search-tab.component.html b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/search-tab/dynamic-lookup-relation-search-tab.component.html
index 4e2da1f12bb0d075d087a2410fa03ced8e415519..36197b33c4a6d49288dd0c0bb439bc4316e65705 100644
--- a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/search-tab/dynamic-lookup-relation-search-tab.component.html
+++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/search-tab/dynamic-lookup-relation-search-tab.component.html
@@ -3,7 +3,7 @@
                        [resultCount]="(resultsRD$ | async)?.payload?.totalElements"
                        [inPlaceSearch]="true" [showViewModes]="false"></ds-search-sidebar>
     <div class="col-8">
-        <ds-search-form [inPlaceSearch]="true"></ds-search-form>
+        <ds-search-form [query]="(searchConfigService.paginatedSearchOptions | async)?.query" [inPlaceSearch]="true"></ds-search-form>
 
         <ds-search-labels [inPlaceSearch]="true"></ds-search-labels>
 
@@ -56,8 +56,8 @@
             </div>
         </div>
         <ds-search-results [searchResults]="(resultsRD$ | async)"
-                           [sortConfig]="this.searchConfig?.sort"
-                           [searchConfig]="this.searchConfig"
+                           [sortConfig]="this.lookupRelationService.searchConfig?.sort"
+                           [searchConfig]="this.lookupRelationService.searchConfig"
                            [selectable]="true"
                            [selectionConfig]="{ repeatable: repeatable, listId: listId }"
                            [linkType]="linkTypes.ExternalLink"
@@ -66,4 +66,4 @@
                            (selectObject)="selectObject.emit($event)">
         </ds-search-results>
     </div>
-</div>
\ No newline at end of file
+</div>
diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/search-tab/dynamic-lookup-relation-search-tab.component.spec.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/search-tab/dynamic-lookup-relation-search-tab.component.spec.ts
index bd83a4823741a81ed053bfc5c257c3c6f96a804a..ced6c8b88bbba47d7a8509db4cf65b3de85b352d 100644
--- a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/search-tab/dynamic-lookup-relation-search-tab.component.spec.ts
+++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/search-tab/dynamic-lookup-relation-search-tab.component.spec.ts
@@ -15,6 +15,8 @@ import { createSuccessfulRemoteDataObject$ } from '../../../../../testing/utils'
 import { PaginatedList } from '../../../../../../core/data/paginated-list';
 import { ItemSearchResult } from '../../../../../object-collection/shared/item-search-result.model';
 import { Item } from '../../../../../../core/shared/item.model';
+import { ActivatedRoute } from '@angular/router';
+import { LookupRelationService } from '../../../../../../core/data/lookup-relation.service';
 
 describe('DsDynamicLookupRelationSearchTabComponent', () => {
   let component: DsDynamicLookupRelationSearchTabComponent;
@@ -34,6 +36,7 @@ describe('DsDynamicLookupRelationSearchTabComponent', () => {
 
   let results;
   let selectableListService;
+  let lookupRelationService;
 
   function init() {
     relationship = { filter: 'filter', relationshipType: 'isAuthorOfPublication', nameVariants: true } as RelationshipOptions;
@@ -51,6 +54,10 @@ describe('DsDynamicLookupRelationSearchTabComponent', () => {
 
     results = new PaginatedList(undefined, [searchResult1, searchResult2, searchResult3]);
     selectableListService = jasmine.createSpyObj('selectableListService', ['deselect', 'select', 'deselectAll']);
+    lookupRelationService = jasmine.createSpyObj('lookupRelationService', {
+      getLocalResults: createSuccessfulRemoteDataObject$(results)
+    });
+    lookupRelationService.searchConfig = {};
   }
 
   beforeEach(async(() => {
@@ -75,6 +82,8 @@ describe('DsDynamicLookupRelationSearchTabComponent', () => {
             }
           }
         },
+        { provide: ActivatedRoute, useValue: { snapshot: { queryParams: {} } } },
+        { provide: LookupRelationService, useValue: lookupRelationService }
       ],
       schemas: [NO_ERRORS_SCHEMA]
     })
diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/search-tab/dynamic-lookup-relation-search-tab.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/search-tab/dynamic-lookup-relation-search-tab.component.ts
index 254c4ac4ff781685bb0fe5d2ee366128abdfd108..948463161029ef1891a1c34b6511ac1cf4e9c6ce 100644
--- a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/search-tab/dynamic-lookup-relation-search-tab.component.ts
+++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/search-tab/dynamic-lookup-relation-search-tab.component.ts
@@ -11,14 +11,16 @@ import { RelationshipOptions } from '../../../models/relationship-options.model'
 import { PaginationComponentOptions } from '../../../../../pagination/pagination-component-options.model';
 import { ListableObject } from '../../../../../object-collection/shared/listable-object.model';
 import { SearchService } from '../../../../../../core/shared/search/search.service';
-import { Router } from '@angular/router';
+import { ActivatedRoute, Router } from '@angular/router';
 import { SelectableListService } from '../../../../../object-list/selectable-list/selectable-list.service';
 import { hasValue, isNotEmpty } from '../../../../../empty.util';
 import { concat, map, multicast, switchMap, take, takeWhile, tap } from 'rxjs/operators';
+import { DSpaceObject } from '../../../../../../core/shared/dspace-object.model';
 import { getSucceededRemoteData } from '../../../../../../core/shared/operators';
 import { RouteService } from '../../../../../../core/services/route.service';
 import { CollectionElementLinkType } from '../../../../../object-collection/collection-element-link.type';
 import { Context } from '../../../../../../core/shared/context.model';
+import { LookupRelationService } from '../../../../../../core/data/lookup-relation.service';
 
 @Component({
   selector: 'ds-dynamic-lookup-relation-search-tab',
@@ -36,32 +38,87 @@ import { Context } from '../../../../../../core/shared/context.model';
  * Tab for inside the lookup model that represents the items that can be used as a relationship in this submission
  */
 export class DsDynamicLookupRelationSearchTabComponent implements OnInit, OnDestroy {
+  /**
+   * Options for searching related items
+   */
   @Input() relationship: RelationshipOptions;
+
+  /**
+   * The ID of the list to add/remove selected items to/from
+   */
   @Input() listId: string;
+
+  /**
+   * Is the selection repeatable?
+   */
   @Input() repeatable: boolean;
+
+  /**
+   * The list of selected items
+   */
   @Input() selection$: Observable<ListableObject[]>;
+
+  /**
+   * The context to display lists
+   */
   @Input() context: Context;
 
+  /**
+   * Send an event to deselect an object from the list
+   */
   @Output() deselectObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
+
+  /**
+   * Send an event to select an object from the list
+   */
   @Output() selectObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
+
+  /**
+   * Search results
+   */
   resultsRD$: Observable<RemoteData<PaginatedList<SearchResult<Item>>>>;
-  searchConfig: PaginatedSearchOptions;
+
+  /**
+   * Are all results selected?
+   */
   allSelected: boolean;
+
+  /**
+   * Are some results selected?
+   */
   someSelected$: Observable<boolean>;
+
+  /**
+   * Is it currently loading to select all results?
+   */
   selectAllLoading: boolean;
+
+  /**
+   * Subscription to unsubscribe from
+   */
   subscription;
+
+  /**
+   * The initial pagination to use
+   */
   initialPagination = Object.assign(new PaginationComponentOptions(), {
     id: 'submission-relation-list',
     pageSize: 5
   });
+
+  /**
+   * The type of links to display
+   */
   linkTypes = CollectionElementLinkType;
 
   constructor(
     private searchService: SearchService,
     private router: Router,
+    private route: ActivatedRoute,
     private selectableListService: SelectableListService,
-    private searchConfigService: SearchConfigurationService,
+    public searchConfigService: SearchConfigurationService,
     private routeService: RouteService,
+    public lookupRelationService: LookupRelationService
   ) {
   }
 
@@ -75,24 +132,8 @@ export class DsDynamicLookupRelationSearchTabComponent implements OnInit, OnDest
 
     this.someSelected$ = this.selection$.pipe(map((selection) => isNotEmpty(selection)));
     this.resultsRD$ = this.searchConfigService.paginatedSearchOptions.pipe(
-      map((options) => {
-        return Object.assign(new PaginatedSearchOptions({}), options, { fixedFilter: this.relationship.filter, configuration: this.relationship.searchConfiguration })
-      }),
-      switchMap((options) => {
-        this.searchConfig = options;
-        return this.searchService.search(options).pipe(
-          /* Make sure to only listen to the first x results, until loading is finished */
-          /* TODO: in Rxjs 6.4.0 and up, we can replace this with takeWhile(predicate, true) - see https://stackoverflow.com/a/44644237 */
-          multicast(
-            () => new ReplaySubject(1),
-            (subject) => subject.pipe(
-              takeWhile((rd: RemoteData<PaginatedList<SearchResult<Item>>>) => rd.isLoading),
-              concat(subject.pipe(take(1)))
-            )
-          ) as any
-        )
-      })
-    ) as Observable<RemoteData<PaginatedList<SearchResult<Item>>>>;
+      switchMap((options) => this.lookupRelationService.getLocalResults(this.relationship, options))
+    );
   }
 
   /**
@@ -100,7 +141,7 @@ export class DsDynamicLookupRelationSearchTabComponent implements OnInit, OnDest
    */
   resetRoute() {
     this.router.navigate([], {
-      queryParams: Object.assign({}, { page: 1, pageSize: this.initialPagination.pageSize }),
+      queryParams: Object.assign({}, { pageSize: this.initialPagination.pageSize }, this.route.snapshot.queryParams, { page: 1 })
     });
   }
 
@@ -143,7 +184,7 @@ export class DsDynamicLookupRelationSearchTabComponent implements OnInit, OnDest
       currentPage: 1,
       pageSize: 9999
     });
-    const fullSearchConfig = Object.assign(this.searchConfig, { pagination: fullPagination });
+    const fullSearchConfig = Object.assign(this.lookupRelationService.searchConfig, { pagination: fullPagination });
     const results$ = this.searchService.search(fullSearchConfig) as Observable<RemoteData<PaginatedList<SearchResult<Item>>>>;
     results$.pipe(
       getSucceededRemoteData(),
diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/selection-tab/dynamic-lookup-relation-selection-tab.component.spec.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/selection-tab/dynamic-lookup-relation-selection-tab.component.spec.ts
index 203a4df0b074e887507301119e3d980de4c1fc35..18e5d3c3abdef8cc34b43382af301e5f14f8c4a1 100644
--- a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/selection-tab/dynamic-lookup-relation-selection-tab.component.spec.ts
+++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/selection-tab/dynamic-lookup-relation-selection-tab.component.spec.ts
@@ -73,11 +73,6 @@ describe('DsDynamicLookupRelationSelectionTabComponent', () => {
     expect(component).toBeTruthy();
   });
 
-  it('should call navigate on the router when is called resetRoute', () => {
-    component.resetRoute();
-    expect(router.navigate).toHaveBeenCalled();
-  });
-
   it('should call navigate on the router when is called resetRoute', () => {
     component.selectionRD$ = createSelection([]);
     fixture.detectChanges();
diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/selection-tab/dynamic-lookup-relation-selection-tab.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/selection-tab/dynamic-lookup-relation-selection-tab.component.ts
index 8aa3dc3828743342acbd86964e433aa90e09ec45..f4746853f6ce9109b10feb3646f131e805301ef6 100644
--- a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/selection-tab/dynamic-lookup-relation-selection-tab.component.ts
+++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/selection-tab/dynamic-lookup-relation-selection-tab.component.ts
@@ -29,15 +29,49 @@ import { Context } from '../../../../../../core/shared/context.model';
  * Tab for inside the lookup model that represents the currently selected relationships
  */
 export class DsDynamicLookupRelationSelectionTabComponent {
+  /**
+   * The label to use to display i18n messages (describing the type of relationship)
+   */
   @Input() label: string;
+
+  /**
+   * The ID of the list to add/remove selected items to/from
+   */
   @Input() listId: string;
+
+  /**
+   * Is the selection repeatable?
+   */
   @Input() repeatable: boolean;
+
+  /**
+   * The list of selected items
+   */
   @Input() selection$: Observable<ListableObject[]>;
+
+  /**
+   * The paginated list of selected items
+   */
   @Input() selectionRD$: Observable<RemoteData<PaginatedList<ListableObject>>>;
+
+  /**
+   * The context to display lists
+   */
   @Input() context: Context;
+
+  /**
+   * Send an event to deselect an object from the list
+   */
   @Output() deselectObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
+
+  /**
+   * Send an event to select an object from the list
+   */
   @Output() selectObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
 
+  /**
+   * The initial pagination to use
+   */
   initialPagination = Object.assign(new PaginationComponentOptions(), {
     id: 'submission-relation-list',
     pageSize: 5
@@ -51,7 +85,6 @@ export class DsDynamicLookupRelationSelectionTabComponent {
    * Set up the selection and pagination on load
    */
   ngOnInit() {
-    this.resetRoute();
     this.selectionRD$ = this.searchConfigService.paginatedSearchOptions
       .pipe(
         map((options: PaginatedSearchOptions) => options.pagination),
@@ -75,13 +108,4 @@ export class DsDynamicLookupRelationSelectionTabComponent {
         })
       )
   }
-
-  /**
-   * Method to reset the route when the window is opened to make sure no strange pagination issues appears
-   */
-  resetRoute() {
-    this.router.navigate([], {
-      queryParams: Object.assign({}, { page: 1, pageSize: this.initialPagination.pageSize }),
-    });
-  }
 }
diff --git a/src/app/shared/lang-switch/lang-switch.component.spec.ts b/src/app/shared/lang-switch/lang-switch.component.spec.ts
index 5b10578f77e668973b5751ebe26968f4642b4bc4..3d7aca46b634af37020fd567fa993a130c4c7fb5 100644
--- a/src/app/shared/lang-switch/lang-switch.component.spec.ts
+++ b/src/app/shared/lang-switch/lang-switch.component.spec.ts
@@ -78,7 +78,7 @@ describe('LangSwitchComponent', () => {
       }).compileComponents()
         .then(() => {
           translate = TestBed.get(TranslateService);
-          translate.addLangs(mockConfig.languages.filter((langConfig:LangConfig) => langConfig.active === true).map((a) => a.code));
+          translate.addLangs(mockConfig.languages.filter((langConfig: LangConfig) => langConfig.active === true).map((a) => a.code));
           translate.setDefaultLang('en');
           translate.use('en');
           http = TestBed.get(HttpTestingController);
diff --git a/src/app/shared/mocks/mock-angulartics.service.ts b/src/app/shared/mocks/mock-angulartics.service.ts
index 5581e183d17461acb0e74abcead4fb694631cabd..a7516eb44af67ee50bf620e32d2c3d9ca0ab507a 100644
--- a/src/app/shared/mocks/mock-angulartics.service.ts
+++ b/src/app/shared/mocks/mock-angulartics.service.ts
@@ -1,5 +1,5 @@
 /* tslint:disable:no-empty */
 export class AngularticsMock {
   public eventTrack(action, properties) { }
-  public startTracking():void {}
+  public startTracking(): void {}
 }
diff --git a/src/app/shared/object-collection/object-collection.component.html b/src/app/shared/object-collection/object-collection.component.html
index 57e1ccd81a0e2c073ea13ffc20c08a378b89b2cc..e696170a6f3b635d7502e6c80e7e51d7194f29cd 100644
--- a/src/app/shared/object-collection/object-collection.component.html
+++ b/src/app/shared/object-collection/object-collection.component.html
@@ -5,6 +5,7 @@
                 [hideGear]="hideGear"
                 [linkType]="linkType"
                 [context]="context"
+                [hidePaginationDetail]="hidePaginationDetail"
                 (paginationChange)="onPaginationChange($event)"
                 (pageChange)="onPageChange($event)"
                 (pageSizeChange)="onPageSizeChange($event)"
@@ -14,6 +15,9 @@
                 (sortFieldChange)="onSortFieldChange($event)"
                 [selectable]="selectable"
                 [selectionConfig]="selectionConfig"
+                [importable]="importable"
+                [importConfig]="importConfig"
+                (importObject)="importObject.emit($event)"
                 *ngIf="(currentMode$ | async) === viewModeEnum.ListElement">
 </ds-object-list>
 
@@ -23,6 +27,7 @@
                 [hideGear]="hideGear"
                 [linkType]="linkType"
                 [context]="context"
+                [hidePaginationDetail]="hidePaginationDetail"
                 (paginationChange)="onPaginationChange($event)"
                 (pageChange)="onPageChange($event)"
                 (pageSizeChange)="onPageSizeChange($event)"
@@ -37,6 +42,7 @@
                   [hideGear]="hideGear"
                   [linkType]="linkType"
                   [context]="context"
+                  [hidePaginationDetail]="hidePaginationDetail"
                   *ngIf="(currentMode$ | async) === viewModeEnum.DetailedListElement">
 </ds-object-detail>
 
diff --git a/src/app/shared/object-collection/object-collection.component.ts b/src/app/shared/object-collection/object-collection.component.ts
index f09ba3953e838579332fe5b111d85874aaf84041..f39bf07123c66e21e61e4f20728f46dce07a031e 100644
--- a/src/app/shared/object-collection/object-collection.component.ts
+++ b/src/app/shared/object-collection/object-collection.component.ts
@@ -53,6 +53,21 @@ export class ObjectCollectionComponent implements OnInit {
   @Output() deselectObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
   @Output() selectObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
 
+  /**
+   * Whether or not to add an import button to the object elements
+   */
+  @Input() importable = false;
+
+  /**
+   * The config to use for the import button
+   */
+  @Input() importConfig: { buttonLabel: string };
+
+  /**
+   * Send an import event to the parent component
+   */
+  @Output() importObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
+
   /**
    * The link type of the rendered list elements
    */
@@ -63,6 +78,11 @@ export class ObjectCollectionComponent implements OnInit {
    */
   @Input() context: Context;
 
+  /**
+   * Option for hiding the pagination detail
+   */
+  @Input() hidePaginationDetail = false;
+
   /**
    * the page info of the list
    */
diff --git a/src/app/shared/object-collection/shared/importable-list-item-control/importable-list-item-control.component.html b/src/app/shared/object-collection/shared/importable-list-item-control/importable-list-item-control.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..ca3b08665395402b3b5866f5a79b5f1c0a82a684
--- /dev/null
+++ b/src/app/shared/object-collection/shared/importable-list-item-control/importable-list-item-control.component.html
@@ -0,0 +1,7 @@
+<div class="d-inline-block mr-2">
+  <button (click)="importObject.emit(object)"
+          class="btn btn-outline-primary btn-sm float-left"
+          title="{{importConfig?.buttonLabel | translate}}">
+    <i class="fas fa-cloud-download-alt fa-fw"></i>
+  </button>
+</div>
diff --git a/src/app/shared/object-collection/shared/importable-list-item-control/importable-list-item-control.component.ts b/src/app/shared/object-collection/shared/importable-list-item-control/importable-list-item-control.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f381a02d86de2d0cbfa148e321cb5c540585ae28
--- /dev/null
+++ b/src/app/shared/object-collection/shared/importable-list-item-control/importable-list-item-control.component.ts
@@ -0,0 +1,26 @@
+import { Component, EventEmitter, Input, Output } from '@angular/core';
+import { ListableObject } from '../listable-object.model';
+
+@Component({
+  selector: 'ds-importable-list-item-control',
+  templateUrl: './importable-list-item-control.component.html'
+})
+/**
+ * Component adding an import button to a list item
+ */
+export class ImportableListItemControlComponent {
+  /**
+   * The item or metadata to determine the component for
+   */
+  @Input() object: ListableObject;
+
+  /**
+   * Extra configuration for the import button
+   */
+  @Input() importConfig: { buttonLabel: string };
+
+  /**
+   * Output the object to import
+   */
+  @Output() importObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
+}
diff --git a/src/app/shared/object-detail/object-detail.component.ts b/src/app/shared/object-detail/object-detail.component.ts
index fb68316251a8809a5d261ff74665ed4698262de8..45efb4a0b7ec9f17f1283365a3ff01b6d702aba4 100644
--- a/src/app/shared/object-detail/object-detail.component.ts
+++ b/src/app/shared/object-detail/object-detail.component.ts
@@ -89,7 +89,7 @@ export class ObjectDetailComponent {
   /**
    * Option for hiding the pagination detail
    */
-  public hidePaginationDetail = true;
+  @Input() hidePaginationDetail = true;
 
   /**
    * An event fired when the page is changed.
diff --git a/src/app/shared/object-grid/object-grid.component.html b/src/app/shared/object-grid/object-grid.component.html
index 348536bfed238d41128c65c7154dad749a5df6a5..0afd623d868a4859014acd668147ce7efb319d0d 100644
--- a/src/app/shared/object-grid/object-grid.component.html
+++ b/src/app/shared/object-grid/object-grid.component.html
@@ -5,6 +5,7 @@
   [sortOptions]="sortConfig"
   [hideGear]="hideGear"
   [hidePagerWhenSinglePage]="hidePagerWhenSinglePage"
+  [hidePaginationDetail]="hidePaginationDetail"
   (pageChange)="onPageChange($event)"
   (pageSizeChange)="onPageSizeChange($event)"
   (sortDirectionChange)="onSortDirectionChange($event)"
diff --git a/src/app/shared/object-grid/object-grid.component.ts b/src/app/shared/object-grid/object-grid.component.ts
index 2da4abe13b87d0ad5745f230bdd3b6dc1195ae16..c6f8347217c0c57cd7e069d332afe31df682a3ba 100644
--- a/src/app/shared/object-grid/object-grid.component.ts
+++ b/src/app/shared/object-grid/object-grid.component.ts
@@ -69,6 +69,11 @@ export class ObjectGridComponent implements OnInit {
    */
   @Input() context: Context;
 
+  /**
+   * Option for hiding the pagination detail
+   */
+  @Input() hidePaginationDetail = false;
+
   /**
    * Behavior subject to output the current listable objects
    */
diff --git a/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.html b/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.html
index bcd5c3c02715cd0832e69c209044aebdfa4a5eed..e1478b52063e9ab822d6aaf78824070d287b9770 100644
--- a/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.html
+++ b/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.html
@@ -2,6 +2,7 @@
   <ng-container *ngIf="status">
     <ds-mydspace-item-status [status]="status"></ds-mydspace-item-status>
   </ng-container>
+  <ds-item-type-badge [object]="item"></ds-item-type-badge>
   <ds-truncatable [id]="item.id">
     <h3 [innerHTML]="item.firstMetadataValue('dc.title') || ('mydspace.results.no-title' | translate)" [ngClass]="{'lead': true,'text-muted': !item.firstMetadataValue('dc.title')}"></h3>
     <div>
diff --git a/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.spec.ts b/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.spec.ts
index eb531d2f9369358390a3f4e7735f40742457dea8..6366539157286a93762aedf3bafeec235e830316 100644
--- a/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.spec.ts
+++ b/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.spec.ts
@@ -47,6 +47,23 @@ const mockItemWithoutAuthorAndDate: Item = Object.assign(new Item(), {
     ]
   }
 });
+const mockItemWithEntityType: Item = Object.assign(new Item(), {
+  bundles: observableOf({}),
+  metadata: {
+    'dc.title': [
+      {
+        language: 'en_US',
+        value: 'This is just another title'
+      }
+    ],
+    'relationship.type': [
+      {
+        language: null,
+        value: 'Publication'
+      }
+    ]
+  }
+});
 
 describe('ItemListPreviewComponent', () => {
   beforeEach(async(() => {
@@ -128,4 +145,16 @@ describe('ItemListPreviewComponent', () => {
       expect(dateField).not.toBeNull();
     });
   });
+
+  describe('When the item has an entity type', () => {
+    beforeEach(() => {
+      component.item = mockItemWithEntityType;
+      fixture.detectChanges();
+    });
+
+    it('should show the entity type span', () => {
+      const entityField = fixture.debugElement.query(By.css('ds-item-type-badge'));
+      expect(entityField).not.toBeNull();
+    });
+  });
 });
diff --git a/src/app/shared/object-list/object-list.component.html b/src/app/shared/object-list/object-list.component.html
index 887be96785e7468873da937c6fcd383aedcaea76..5f6b1d1ec87bc9bb544d6d74e01a0ee9cd47dc91 100644
--- a/src/app/shared/object-list/object-list.component.html
+++ b/src/app/shared/object-list/object-list.component.html
@@ -5,6 +5,7 @@
         [sortOptions]="sortConfig"
         [hideGear]="hideGear"
         [hidePagerWhenSinglePage]="hidePagerWhenSinglePage"
+        [hidePaginationDetail]="hidePaginationDetail"
         (pageChange)="onPageChange($event)"
         (pageSizeChange)="onPageSizeChange($event)"
         (sortDirectionChange)="onSortDirectionChange($event)"
@@ -19,6 +20,11 @@
                                                     (deselectObject)="deselectObject.emit($event)"
                                                     (selectObject)="selectObject.emit($event)"></ds-selectable-list-item-control>
               </span>
+          <span *ngIf="importable">
+            <ds-importable-list-item-control  [object]="object"
+                                              [importConfig]="importConfig"
+                                              (importObject)="importObject.emit($event)"></ds-importable-list-item-control>
+          </span>
             <ds-listable-object-component-loader [object]="object" [viewMode]="viewMode" [index]="i" [context]="context" [linkType]="linkType"
                                                  [listID]="selectionConfig?.listId"></ds-listable-object-component-loader>
         </li>
diff --git a/src/app/shared/object-list/object-list.component.ts b/src/app/shared/object-list/object-list.component.ts
index 6ca7adb3f9edb057cfbde64d912b4a78b416c4c7..60544c4ec52f6c871098d6bf81c707227e217127 100644
--- a/src/app/shared/object-list/object-list.component.ts
+++ b/src/app/shared/object-list/object-list.component.ts
@@ -61,6 +61,21 @@ export class ObjectListComponent {
    */
   @Input() context: Context;
 
+  /**
+   * Option for hiding the pagination detail
+   */
+  @Input() hidePaginationDetail = false;
+
+  /**
+   * Whether or not to add an import button to the object
+   */
+  @Input() importable = false;
+
+  /**
+   * Config used for the import button
+   */
+  @Input() importConfig: { importLabel: string };
+
   /**
    * The current listable objects
    */
@@ -119,6 +134,12 @@ export class ObjectListComponent {
   @Output() deselectObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
 
   @Output() selectObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
+
+  /**
+   * Send an import event to the parent component
+   */
+  @Output() importObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
+
   /**
    * An event fired when the sort field is changed.
    * Event's payload equals to the newly selected sort field.
diff --git a/src/app/shared/pagination/pagination.component.html b/src/app/shared/pagination/pagination.component.html
index c16a1530263521e81f0cc4624cd5dc9347c2872e..649fe686ffccdab73a7691a80bf947dc0624ff54 100644
--- a/src/app/shared/pagination/pagination.component.html
+++ b/src/app/shared/pagination/pagination.component.html
@@ -1,5 +1,5 @@
 <div *ngIf="currentPageState == undefined || currentPageState == currentPage">
-  <div  class="pagination-masked clearfix top">
+  <div *ngIf="(!hidePaginationDetail && collectionSize > 0) || !hideGear" class="pagination-masked clearfix top">
     <div class="row">
       <div *ngIf="!hidePaginationDetail && collectionSize > 0" class="col-auto pagination-info">
           <span class="align-middle hidden-xs-down">{{ 'pagination.showing.label' | translate }}</span>
diff --git a/src/app/shared/pagination/pagination.component.ts b/src/app/shared/pagination/pagination.component.ts
index 014f01f1525aaa8aab1e43767cb3ed593ebf560b..9c378d1aff8cf31d84a3fb8e08db04b1d6d135bf 100644
--- a/src/app/shared/pagination/pagination.component.ts
+++ b/src/app/shared/pagination/pagination.component.ts
@@ -225,10 +225,14 @@ export class PaginationComponent implements OnDestroy, OnInit {
   }
 
   /**
+   * @param cdRef
+   *    ChangeDetectorRef is a singleton service provided by Angular.
    * @param route
    *    Route is a singleton service provided by Angular.
    * @param router
    *    Router is a singleton service provided by Angular.
+   * @param hostWindowService
+   *    the HostWindowService singleton.
    */
   constructor(private cdRef: ChangeDetectorRef,
               private route: ActivatedRoute,
@@ -243,7 +247,7 @@ export class PaginationComponent implements OnDestroy, OnInit {
    *    The page being navigated to.
    */
   public doPageChange(page: number) {
-    this.updateRoute({ page: page.toString() });
+    this.updateRoute({ pageId: this.id, page: page.toString() });
   }
 
   /**
@@ -253,7 +257,7 @@ export class PaginationComponent implements OnDestroy, OnInit {
    *    The page size being navigated to.
    */
   public doPageSizeChange(pageSize: number) {
-    this.updateRoute({ page: 1, pageSize: pageSize });
+    this.updateRoute({ pageId: this.id, page: 1, pageSize: pageSize });
   }
 
   /**
@@ -263,7 +267,7 @@ export class PaginationComponent implements OnDestroy, OnInit {
    *    The sort direction being navigated to.
    */
   public doSortDirectionChange(sortDirection: SortDirection) {
-    this.updateRoute({ page: 1, sortDirection: sortDirection });
+    this.updateRoute({ pageId: this.id, page: 1, sortDirection: sortDirection });
   }
 
   /**
@@ -273,7 +277,7 @@ export class PaginationComponent implements OnDestroy, OnInit {
    *    The sort field being navigated to.
    */
   public doSortFieldChange(field: string) {
-    this.updateRoute({ page: 1, sortField: field });
+    this.updateRoute({ pageId: this.id, page: 1, sortField: field });
   }
 
   /**
@@ -413,27 +417,30 @@ export class PaginationComponent implements OnDestroy, OnInit {
    * Method to update all pagination variables to the current query parameters
    */
   private setFields() {
-    // (+) converts string to a number
-    const page = this.currentQueryParams.page;
-    if (this.currentPage !== +page) {
-      this.setPage(+page);
-    }
+    // set fields only when page id is the one configured for this pagination instance
+    if (this.currentQueryParams.pageId === this.id) {
+      // (+) converts string to a number
+      const page = this.currentQueryParams.page;
+      if (this.currentPage !== +page) {
+        this.setPage(+page);
+      }
 
-    const pageSize = this.currentQueryParams.pageSize;
-    if (this.pageSize !== +pageSize) {
-      this.setPageSize(+pageSize);
-    }
+      const pageSize = this.currentQueryParams.pageSize;
+      if (this.pageSize !== +pageSize) {
+        this.setPageSize(+pageSize);
+      }
 
-    const sortDirection = this.currentQueryParams.sortDirection;
-    if (this.sortDirection !== sortDirection) {
-      this.setSortDirection(sortDirection);
-    }
+      const sortDirection = this.currentQueryParams.sortDirection;
+      if (this.sortDirection !== sortDirection) {
+        this.setSortDirection(sortDirection);
+      }
 
-    const sortField = this.currentQueryParams.sortField;
-    if (this.sortField !== sortField) {
-      this.setSortField(sortField);
+      const sortField = this.currentQueryParams.sortField;
+      if (this.sortField !== sortField) {
+        this.setSortField(sortField);
+      }
+      this.cdRef.detectChanges();
     }
-    this.cdRef.detectChanges();
   }
 
   /**
diff --git a/src/app/shared/search/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts b/src/app/shared/search/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts
index 3bed8e739743d89864cef7de05ad4d843644d4d9..1d6a85b95b12319e48fec1ca3a67a83eacc81ff9 100644
--- a/src/app/shared/search/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts
+++ b/src/app/shared/search/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts
@@ -24,6 +24,7 @@ import { getSucceededRemoteData } from '../../../../../core/shared/operators';
 import { InputSuggestion } from '../../../../input-suggestions/input-suggestions.model';
 import { SearchOptions } from '../../../search-options.model';
 import { SEARCH_CONFIG_SERVICE } from '../../../../../+my-dspace-page/my-dspace-page.component';
+import { currentPath } from '../../../../utils/route.utils';
 
 @Component({
   selector: 'ds-search-facet-filter',
@@ -185,7 +186,7 @@ export class SearchFacetFilterComponent implements OnInit, OnDestroy {
    */
   public getSearchLink(): string {
     if (this.inPlaceSearch) {
-      return '';
+      return currentPath(this.router);
     }
     return this.searchService.getSearchLink();
   }
diff --git a/src/app/shared/search/search-labels/search-label/search-label.component.spec.ts b/src/app/shared/search/search-labels/search-label/search-label.component.spec.ts
index 8c6860c2d3621d6ad0e1a50d581256ea5289c8d7..5de87be3bc7d21b41bbe7480c3cbf1aba48d1eb4 100644
--- a/src/app/shared/search/search-labels/search-label/search-label.component.spec.ts
+++ b/src/app/shared/search/search-labels/search-label/search-label.component.spec.ts
@@ -40,7 +40,7 @@ describe('SearchLabelComponent', () => {
       providers: [
         { provide: SearchService, useValue: new SearchServiceStub(searchLink) },
         { provide: SEARCH_CONFIG_SERVICE, useValue: new SearchConfigurationServiceStub() },
-        { provide: Router, useValue: {} }
+        { provide: Router, useValue: {}}
         // { provide: SearchConfigurationService, useValue: {getCurrentFrontendFilters : () =>  observableOf({})} }
       ],
       schemas: [NO_ERRORS_SCHEMA]
diff --git a/src/app/shared/search/search-results/search-results.component.html b/src/app/shared/search/search-results/search-results.component.html
index ab1e96c58f1334105bdc095aead54147c8594294..cbc56d1080a7b7b3b586ce395e5b793ebf8a7a5c 100644
--- a/src/app/shared/search/search-results/search-results.component.html
+++ b/src/app/shared/search/search-results/search-results.component.html
@@ -8,6 +8,7 @@
             [selectable]="selectable"
             [selectionConfig]="selectionConfig"
             [context]="context"
+            [hidePaginationDetail]="hidePaginationDetail"
             (deselectObject)="deselectObject.emit($event)"
             (selectObject)="selectObject.emit($event)"
             >
diff --git a/src/app/shared/search/search-results/search-results.component.ts b/src/app/shared/search/search-results/search-results.component.ts
index f245b5f9ae3d3a79b54a5c1c98fbddf0a0d4ac52..b094e69a57c2317e405831aa7e6f53654a115269 100644
--- a/src/app/shared/search/search-results/search-results.component.ts
+++ b/src/app/shared/search/search-results/search-results.component.ts
@@ -67,6 +67,11 @@ export class SearchResultsComponent {
 
   @Input() context: Context;
 
+  /**
+   * Option for hiding the pagination detail
+   */
+  @Input() hidePaginationDetail = false;
+
   @Input() selectionConfig: {repeatable: boolean, listId: string};
 
   @Output() deselectObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts
index eb73514d7612bd40c10494726000ee61db4c9625..685787c5a439092ff40a73de272696a9d42ca7b4 100644
--- a/src/app/shared/shared.module.ts
+++ b/src/app/shared/shared.module.ts
@@ -48,10 +48,7 @@ import { LogOutComponent } from './log-out/log-out.component';
 import { FormComponent } from './form/form.component';
 import { DsDynamicTypeaheadComponent } from './form/builder/ds-dynamic-form-ui/models/typeahead/dynamic-typeahead.component';
 import { DsDynamicScrollableDropdownComponent } from './form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.component';
-import {
-  DsDynamicFormControlContainerComponent,
-  dsDynamicFormControlMapFn
-} from './form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component';
+import { DsDynamicFormControlContainerComponent, dsDynamicFormControlMapFn } from './form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component';
 import { DsDynamicFormComponent } from './form/builder/ds-dynamic-form-ui/ds-dynamic-form.component';
 import { DYNAMIC_FORM_CONTROL_MAP_FN, DynamicFormsCoreModule } from '@ng-dynamic-forms/core';
 import { DynamicFormsNGBootstrapUIModule } from '@ng-dynamic-forms/ui-ng-bootstrap';
@@ -174,8 +171,12 @@ import { PageWithSidebarComponent } from './sidebar/page-with-sidebar.component'
 import { SidebarDropdownComponent } from './sidebar/sidebar-dropdown.component';
 import { SidebarFilterComponent } from './sidebar/filter/sidebar-filter.component';
 import { SidebarFilterSelectedOptionComponent } from './sidebar/filter/sidebar-filter-selected-option.component';
-import { MetadataRepresentationListComponent } from '../+item-page/simple/metadata-representation-list/metadata-representation-list.component';
 import { SelectableListItemControlComponent } from './object-collection/shared/selectable-list-item-control/selectable-list-item-control.component';
+import { DsDynamicLookupRelationExternalSourceTabComponent } from './form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/dynamic-lookup-relation-external-source-tab.component';
+import { ExternalSourceEntryImportModalComponent } from './form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/external-source-entry-import-modal/external-source-entry-import-modal.component';
+import { ImportableListItemControlComponent } from './object-collection/shared/importable-list-item-control/importable-list-item-control.component';
+import { DragDropModule } from '@angular/cdk/drag-drop';
+import { ExistingMetadataListElementComponent } from './form/builder/ds-dynamic-form-ui/existing-metadata-list-element/existing-metadata-list-element.component';
 
 const MODULES = [
   // Do NOT include UniversalModule, HttpModule, or JsonpModule here
@@ -198,6 +199,7 @@ const MODULES = [
   MomentModule,
   TextMaskModule,
   MenuModule,
+  DragDropModule
 ];
 
 const ROOT_MODULES = [
@@ -334,7 +336,10 @@ const COMPONENTS = [
   ItemSelectComponent,
   CollectionSelectComponent,
   MetadataRepresentationLoaderComponent,
-  SelectableListItemControlComponent
+  SelectableListItemControlComponent,
+  ExternalSourceEntryImportModalComponent,
+  ImportableListItemControlComponent,
+  ExistingMetadataListElementComponent
 ];
 
 const ENTRY_COMPONENTS = [
@@ -395,7 +400,9 @@ const ENTRY_COMPONENTS = [
   SearchFacetRangeOptionComponent,
   SearchAuthorityFilterComponent,
   DsDynamicLookupRelationSearchTabComponent,
-  DsDynamicLookupRelationSelectionTabComponent
+  DsDynamicLookupRelationSelectionTabComponent,
+  DsDynamicLookupRelationExternalSourceTabComponent,
+  ExternalSourceEntryImportModalComponent
 ];
 
 const SHARED_ITEM_PAGE_COMPONENTS = [
@@ -437,7 +444,8 @@ const DIRECTIVES = [
     ...DIRECTIVES,
     ...ENTRY_COMPONENTS,
     ...SHARED_ITEM_PAGE_COMPONENTS,
-    PublicationSearchResultListElementComponent
+    PublicationSearchResultListElementComponent,
+    ExistingMetadataListElementComponent
   ],
   providers: [
     ...PROVIDERS
diff --git a/src/app/shared/sidebar/filter/sidebar-filter-selected-option.component.ts b/src/app/shared/sidebar/filter/sidebar-filter-selected-option.component.ts
index 5c80a9cd87cabb248f58f54334ac2a299a7ad892..4f1d2415ae1a66ef6065628ca24e026d621ac633 100644
--- a/src/app/shared/sidebar/filter/sidebar-filter-selected-option.component.ts
+++ b/src/app/shared/sidebar/filter/sidebar-filter-selected-option.component.ts
@@ -10,6 +10,6 @@ import { Component, EventEmitter, Input, Output } from '@angular/core';
  * Represents a single selected option in a sidebar filter
  */
 export class SidebarFilterSelectedOptionComponent {
-  @Input() label:string;
-  @Output() click:EventEmitter<any> = new EventEmitter<any>();
+  @Input() label: string;
+  @Output() click: EventEmitter<any> = new EventEmitter<any>();
 }
diff --git a/src/app/shared/sidebar/filter/sidebar-filter.actions.ts b/src/app/shared/sidebar/filter/sidebar-filter.actions.ts
index 2391274489e1b88c81b5aff10cd508e18ecddb82..644bebd94958fe854fd8a29910a831ce241ce457 100644
--- a/src/app/shared/sidebar/filter/sidebar-filter.actions.ts
+++ b/src/app/shared/sidebar/filter/sidebar-filter.actions.ts
@@ -45,7 +45,7 @@ export class FilterInitializeAction extends SidebarFilterAction {
   type = SidebarFilterActionTypes.INITIALIZE;
   initiallyExpanded;
 
-  constructor(name:string, initiallyExpanded:boolean) {
+  constructor(name: string, initiallyExpanded: boolean) {
     super(name);
     this.initiallyExpanded = initiallyExpanded;
   }
diff --git a/src/app/shared/sidebar/filter/sidebar-filter.component.ts b/src/app/shared/sidebar/filter/sidebar-filter.component.ts
index 2a98565639504a3e1d43ecbbf57ccbdb233f7dae..5a019d41df12a3bbd0e1fc84b6590cb6e8b85b6d 100644
--- a/src/app/shared/sidebar/filter/sidebar-filter.component.ts
+++ b/src/app/shared/sidebar/filter/sidebar-filter.component.ts
@@ -15,13 +15,13 @@ import { slide } from '../../animations/slide';
  */
 export class SidebarFilterComponent implements OnInit {
 
-  @Input() name:string;
-  @Input() type:string;
-  @Input() label:string;
+  @Input() name: string;
+  @Input() type: string;
+  @Input() label: string;
   @Input() expanded = true;
   @Input() singleValue = false;
-  @Input() selectedValues:Observable<string[]>;
-  @Output() removeValue:EventEmitter<any> = new EventEmitter<any>();
+  @Input() selectedValues: Observable<string[]>;
+  @Output() removeValue: EventEmitter<any> = new EventEmitter<any>();
 
   /**
    * True when the filter is 100% collapsed in the UI
@@ -31,10 +31,10 @@ export class SidebarFilterComponent implements OnInit {
   /**
    * Emits true when the filter is currently collapsed in the store
    */
-  collapsed$:Observable<boolean>;
+  collapsed$: Observable<boolean>;
 
   constructor(
-    protected filterService:SidebarFilterService,
+    protected filterService: SidebarFilterService,
   ) {
   }
 
@@ -49,7 +49,7 @@ export class SidebarFilterComponent implements OnInit {
    * Method to change this.collapsed to false when the slide animation ends and is sliding open
    * @param event The animation event
    */
-  finishSlide(event:any):void {
+  finishSlide(event: any): void {
     if (event.fromState === 'collapsed') {
       this.closed = false;
     }
@@ -59,13 +59,13 @@ export class SidebarFilterComponent implements OnInit {
    * Method to change this.collapsed to true when the slide animation starts and is sliding closed
    * @param event The animation event
    */
-  startSlide(event:any):void {
+  startSlide(event: any): void {
     if (event.toState === 'collapsed') {
       this.closed = true;
     }
   }
 
-  ngOnInit():void {
+  ngOnInit(): void {
     this.closed = !this.expanded;
     this.initializeFilter();
     this.collapsed$ = this.isCollapsed();
@@ -82,7 +82,7 @@ export class SidebarFilterComponent implements OnInit {
    * Checks if the filter is currently collapsed
    * @returns {Observable<boolean>} Emits true when the current state of the filter is collapsed, false when it's expanded
    */
-  private isCollapsed():Observable<boolean> {
+  private isCollapsed(): Observable<boolean> {
     return this.filterService.isCollapsed(this.name);
   }
 
diff --git a/src/app/shared/sidebar/filter/sidebar-filter.reducer.ts b/src/app/shared/sidebar/filter/sidebar-filter.reducer.ts
index d25737eaa96b672ea01520fc12f8bd50e700a211..672a7a2a2d6c74a30f4646d64558d1088ef5b05b 100644
--- a/src/app/shared/sidebar/filter/sidebar-filter.reducer.ts
+++ b/src/app/shared/sidebar/filter/sidebar-filter.reducer.ts
@@ -8,17 +8,17 @@ import {
  * Interface that represents the state for a single filters
  */
 export interface SidebarFilterState {
-  filterCollapsed:boolean,
+  filterCollapsed: boolean,
 }
 
 /**
  * Interface that represents the state for all available filters
  */
 export interface SidebarFiltersState {
-  [name:string]:SidebarFilterState
+  [name: string]: SidebarFilterState
 }
 
-const initialState:SidebarFiltersState = Object.create(null);
+const initialState: SidebarFiltersState = Object.create(null);
 
 /**
  * Performs a  filter action on the current state
@@ -26,7 +26,7 @@ const initialState:SidebarFiltersState = Object.create(null);
  * @param {SidebarFilterAction} action The action that should be performed
  * @returns {SidebarFiltersState} The state after the action is performed
  */
-export function sidebarFilterReducer(state = initialState, action:SidebarFilterAction):SidebarFiltersState {
+export function sidebarFilterReducer(state = initialState, action: SidebarFilterAction): SidebarFiltersState {
 
   switch (action.type) {
 
diff --git a/src/app/shared/sidebar/filter/sidebar-filter.service.ts b/src/app/shared/sidebar/filter/sidebar-filter.service.ts
index 6dab18c5d7bdd5800e218dadcc6d5e75419f59a2..b67de24f9ee7bdb952bce0a1cb3c48ecccc656c1 100644
--- a/src/app/shared/sidebar/filter/sidebar-filter.service.ts
+++ b/src/app/shared/sidebar/filter/sidebar-filter.service.ts
@@ -16,7 +16,7 @@ import { hasValue } from '../../empty.util';
 @Injectable()
 export class SidebarFilterService {
 
-  constructor(private store:Store<SidebarFiltersState>) {
+  constructor(private store: Store<SidebarFiltersState>) {
   }
 
   /**
@@ -24,7 +24,7 @@ export class SidebarFilterService {
    * @param {string} filter The filter for which the action is dispatched
    * @param {boolean} expanded If the filter should be open from the start
    */
-  public initializeFilter(filter:string, expanded:boolean):void {
+  public initializeFilter(filter: string, expanded: boolean): void {
     this.store.dispatch(new FilterInitializeAction(filter, expanded));
   }
 
@@ -32,7 +32,7 @@ export class SidebarFilterService {
    * Dispatches a collapse action to the store for a given filter
    * @param {string} filterName The filter for which the action is dispatched
    */
-  public collapse(filterName:string):void {
+  public collapse(filterName: string): void {
     this.store.dispatch(new FilterCollapseAction(filterName));
   }
 
@@ -40,7 +40,7 @@ export class SidebarFilterService {
    * Dispatches an expand action to the store for a given filter
    * @param {string} filterName The filter for which the action is dispatched
    */
-  public expand(filterName:string):void {
+  public expand(filterName: string): void {
     this.store.dispatch(new FilterExpandAction(filterName));
   }
 
@@ -48,7 +48,7 @@ export class SidebarFilterService {
    * Dispatches a toggle action to the store for a given filter
    * @param {string} filterName The filter for which the action is dispatched
    */
-  public toggle(filterName:string):void {
+  public toggle(filterName: string): void {
     this.store.dispatch(new FilterToggleAction(filterName));
   }
 
@@ -57,10 +57,10 @@ export class SidebarFilterService {
    * @param {string} filterName The filtername for which the collapsed state is checked
    * @returns {Observable<boolean>} Emits the current collapsed state of the given filter, if it's unavailable, return false
    */
-  isCollapsed(filterName:string):Observable<boolean> {
+  isCollapsed(filterName: string): Observable<boolean> {
     return this.store.pipe(
       select(filterByNameSelector(filterName)),
-      map((object:SidebarFilterState) => {
+      map((object: SidebarFilterState) => {
         if (object) {
           return object.filterCollapsed;
         } else {
@@ -73,14 +73,14 @@ export class SidebarFilterService {
 
 }
 
-const filterStateSelector = (state:SidebarFiltersState) => state.sidebarFilter;
+const filterStateSelector = (state: SidebarFiltersState) => state.sidebarFilter;
 
-function filterByNameSelector(name:string):MemoizedSelector<SidebarFiltersState, SidebarFilterState> {
+function filterByNameSelector(name: string): MemoizedSelector<SidebarFiltersState, SidebarFilterState> {
   return keySelector<SidebarFilterState>(name);
 }
 
-export function keySelector<T>(key:string):MemoizedSelector<SidebarFiltersState, T> {
-  return createSelector(filterStateSelector, (state:SidebarFilterState) => {
+export function keySelector<T>(key: string): MemoizedSelector<SidebarFiltersState, T> {
+  return createSelector(filterStateSelector, (state: SidebarFilterState) => {
     if (hasValue(state)) {
       return state[key];
     } else {
diff --git a/src/app/shared/sidebar/page-with-sidebar.component.spec.ts b/src/app/shared/sidebar/page-with-sidebar.component.spec.ts
index 77f59090ab2b0f899c27e1117e061eb9b6039972..e9211797a91d3c348b3029a905cf52c542400317 100644
--- a/src/app/shared/sidebar/page-with-sidebar.component.spec.ts
+++ b/src/app/shared/sidebar/page-with-sidebar.component.spec.ts
@@ -7,8 +7,8 @@ import { HostWindowService } from '../host-window.service';
 import { NoopAnimationsModule } from '@angular/platform-browser/animations';
 
 describe('PageWithSidebarComponent', () => {
-  let comp:PageWithSidebarComponent;
-  let fixture:ComponentFixture<PageWithSidebarComponent>;
+  let comp: PageWithSidebarComponent;
+  let fixture: ComponentFixture<PageWithSidebarComponent>;
 
   const sidebarService = {
     isCollapsed: observableOf(true),
@@ -42,7 +42,7 @@ describe('PageWithSidebarComponent', () => {
   });
 
   describe('when sidebarCollapsed is true in mobile view', () => {
-    let menu:HTMLElement;
+    let menu: HTMLElement;
 
     beforeEach(() => {
       menu = fixture.debugElement.query(By.css('#mock-id-sidebar-content')).nativeElement;
@@ -58,7 +58,7 @@ describe('PageWithSidebarComponent', () => {
   });
 
   describe('when sidebarCollapsed is false in mobile view', () => {
-    let menu:HTMLElement;
+    let menu: HTMLElement;
 
     beforeEach(() => {
       menu = fixture.debugElement.query(By.css('#mock-id-sidebar-content')).nativeElement;
@@ -70,6 +70,5 @@ describe('PageWithSidebarComponent', () => {
     it('should open the menu', () => {
       expect(menu.classList).toContain('active');
     });
-
   });
 });
diff --git a/src/app/shared/sidebar/page-with-sidebar.component.ts b/src/app/shared/sidebar/page-with-sidebar.component.ts
index 8b7f987a37a0479f99518db6df9f23aafea9c0c5..44fa238d3b2747fbbf17d0de1bd737a904997737 100644
--- a/src/app/shared/sidebar/page-with-sidebar.component.ts
+++ b/src/app/shared/sidebar/page-with-sidebar.component.ts
@@ -18,13 +18,13 @@ import { map } from 'rxjs/operators';
  * the template outlet (inside the page-width-sidebar tags).
  */
 export class PageWithSidebarComponent implements OnInit {
-  @Input() id:string;
-  @Input() sidebarContent:TemplateRef<any>;
+  @Input() id: string;
+  @Input() sidebarContent: TemplateRef<any>;
 
   /**
    * Emits true if were on a small screen
    */
-  isXsOrSm$:Observable<boolean>;
+  isXsOrSm$: Observable<boolean>;
 
   /**
    * The width of the sidebar (bootstrap columns)
@@ -35,16 +35,16 @@ export class PageWithSidebarComponent implements OnInit {
   /**
    * Observable for whether or not the sidebar is currently collapsed
    */
-  isSidebarCollapsed$:Observable<boolean>;
+  isSidebarCollapsed$: Observable<boolean>;
 
-  sidebarClasses:Observable<string>;
+  sidebarClasses: Observable<string>;
 
-  constructor(protected sidebarService:SidebarService,
-              protected windowService:HostWindowService,
+  constructor(protected sidebarService: SidebarService,
+              protected windowService: HostWindowService,
   ) {
   }
 
-  ngOnInit():void {
+  ngOnInit(): void {
     this.isXsOrSm$ = this.windowService.isXsOrSm();
     this.isSidebarCollapsed$ = this.isSidebarCollapsed();
     this.sidebarClasses = this.isSidebarCollapsed$.pipe(
@@ -56,21 +56,21 @@ export class PageWithSidebarComponent implements OnInit {
    * Check if the sidebar is collapsed
    * @returns {Observable<boolean>} emits true if the sidebar is currently collapsed, false if it is expanded
    */
-  private isSidebarCollapsed():Observable<boolean> {
+  private isSidebarCollapsed(): Observable<boolean> {
     return this.sidebarService.isCollapsed;
   }
 
   /**
    * Set the sidebar to a collapsed state
    */
-  public closeSidebar():void {
+  public closeSidebar(): void {
     this.sidebarService.collapse()
   }
 
   /**
    * Set the sidebar to an expanded state
    */
-  public openSidebar():void {
+  public openSidebar(): void {
     this.sidebarService.expand();
   }
 
diff --git a/src/app/shared/sidebar/sidebar-dropdown.component.ts b/src/app/shared/sidebar/sidebar-dropdown.component.ts
index 313538eded281c83a9522365eb9c8ba343f1ad8b..471d357e253e4b5ad188eecfec2844f417f6af81 100644
--- a/src/app/shared/sidebar/sidebar-dropdown.component.ts
+++ b/src/app/shared/sidebar/sidebar-dropdown.component.ts
@@ -10,7 +10,7 @@ import { Component, EventEmitter, Input, Output } from '@angular/core';
  * The options should still be provided in the content.
  */
 export class SidebarDropdownComponent {
-  @Input() id:string;
-  @Input() label:string;
-  @Output() change:EventEmitter<any> = new EventEmitter<number>();
+  @Input() id: string;
+  @Input() label: string;
+  @Output() change: EventEmitter<any> = new EventEmitter<number>();
 }
diff --git a/src/app/shared/trackable/abstract-trackable.component.ts b/src/app/shared/trackable/abstract-trackable.component.ts
index cd1b425f1058387864523fd35747aac4aa257340..bb1f4b31b4c8a96c5dcdfe28de2b935540966208 100644
--- a/src/app/shared/trackable/abstract-trackable.component.ts
+++ b/src/app/shared/trackable/abstract-trackable.component.ts
@@ -63,7 +63,7 @@ export class AbstractTrackableComponent {
    * Get translated notification title
    * @param key
    */
-  private getNotificationTitle(key: string) {
+  protected getNotificationTitle(key: string) {
     return this.translateService.instant(this.notificationsPrefix + key + '.title');
   }
 
@@ -71,7 +71,7 @@ export class AbstractTrackableComponent {
    * Get translated notification content
    * @param key
    */
-  private getNotificationContent(key: string) {
+  protected getNotificationContent(key: string) {
     return this.translateService.instant(this.notificationsPrefix + key + '.content');
 
   }
diff --git a/src/app/shared/utils/object-keys-pipe.ts b/src/app/shared/utils/object-keys-pipe.ts
index fd3d018b883071202306906e71ba3226cb29ec79..1320dbc4bf901a408c081da37ca2198a34e9ad84 100644
--- a/src/app/shared/utils/object-keys-pipe.ts
+++ b/src/app/shared/utils/object-keys-pipe.ts
@@ -10,7 +10,7 @@ export class ObjectKeysPipe implements PipeTransform {
    * @param value An object
    * @returns {any} Array with all keys the input object
    */
-  transform(value, args:string[]): any {
+  transform(value, args: string[]): any {
     const keys = [];
     Object.keys(value).forEach((k) => keys.push(k));
     return keys;
diff --git a/src/app/shared/utils/object-values-pipe.ts b/src/app/shared/utils/object-values-pipe.ts
index 79efd1cb761de38ab07a87aacc4041cb5272d24c..bb511b4e5c132a3c4af5f253f4751579d406684a 100644
--- a/src/app/shared/utils/object-values-pipe.ts
+++ b/src/app/shared/utils/object-values-pipe.ts
@@ -10,7 +10,7 @@ export class ObjectValuesPipe implements PipeTransform {
    * @param value An object
    * @returns {any} Array with all values of the input object
    */
-  transform(value, args:string[]): any {
+  transform(value, args: string[]): any {
     const values = [];
     Object.values(value).forEach((v) => values.push(v));
     return values;
diff --git a/src/app/statistics/angulartics/dspace-provider.spec.ts b/src/app/statistics/angulartics/dspace-provider.spec.ts
index 922a3152fd3785d36ae8038af178eef8cb822608..8491d8e80cd7f27eec923166c71e5ec45812454c 100644
--- a/src/app/statistics/angulartics/dspace-provider.spec.ts
+++ b/src/app/statistics/angulartics/dspace-provider.spec.ts
@@ -5,9 +5,9 @@ import { filter } from 'rxjs/operators';
 import { of as observableOf } from 'rxjs';
 
 describe('Angulartics2DSpace', () => {
-  let provider:Angulartics2DSpace;
-  let angulartics2:Angulartics2;
-  let statisticsService:jasmine.SpyObj<StatisticsService>;
+  let provider: Angulartics2DSpace;
+  let angulartics2: Angulartics2;
+  let statisticsService: jasmine.SpyObj<StatisticsService>;
 
   beforeEach(() => {
     angulartics2 = {
diff --git a/src/app/statistics/angulartics/dspace-provider.ts b/src/app/statistics/angulartics/dspace-provider.ts
index 9ab01f60233c28ade50fc0aed5b58d2382d7ed77..cd1aab94bd7208dcf4a6be855e1858124a78b2b7 100644
--- a/src/app/statistics/angulartics/dspace-provider.ts
+++ b/src/app/statistics/angulartics/dspace-provider.ts
@@ -9,15 +9,15 @@ import { StatisticsService } from '../statistics.service';
 export class Angulartics2DSpace {
 
   constructor(
-    private angulartics2:Angulartics2,
-    private statisticsService:StatisticsService,
+    private angulartics2: Angulartics2,
+    private statisticsService: StatisticsService,
   ) {
   }
 
   /**
    * Activates this plugin
    */
-  startTracking():void {
+  startTracking(): void {
     this.angulartics2.eventTrack
       .pipe(this.angulartics2.filterDeveloperMode())
       .subscribe((event) => this.eventTrack(event));
diff --git a/src/app/statistics/angulartics/dspace/view-tracker.component.ts b/src/app/statistics/angulartics/dspace/view-tracker.component.ts
index 1151287ea84ed03cbb7ce8776b7a60818d8797ee..85588aeb97aee4a1c7508ac431bbfd84c274feea 100644
--- a/src/app/statistics/angulartics/dspace/view-tracker.component.ts
+++ b/src/app/statistics/angulartics/dspace/view-tracker.component.ts
@@ -11,14 +11,14 @@ import { DSpaceObject } from '../../../core/shared/dspace-object.model';
   templateUrl: './view-tracker.component.html',
 })
 export class ViewTrackerComponent implements OnInit {
-  @Input() object:DSpaceObject;
+  @Input() object: DSpaceObject;
 
   constructor(
-    public angulartics2:Angulartics2
+    public angulartics2: Angulartics2
   ) {
   }
 
-  ngOnInit():void {
+  ngOnInit(): void {
     this.angulartics2.eventTrack.next({
       action: 'pageView',
       properties: {object: this.object},
diff --git a/src/app/statistics/statistics.module.ts b/src/app/statistics/statistics.module.ts
index a67ff7613c4ebb7f9f12e0502df1f5ca63050428..58ac1f07ab326615887a304a1bc6973be96442e8 100644
--- a/src/app/statistics/statistics.module.ts
+++ b/src/app/statistics/statistics.module.ts
@@ -25,7 +25,7 @@ import { StatisticsService } from './statistics.service';
  * This module handles the statistics
  */
 export class StatisticsModule {
-  static forRoot():ModuleWithProviders {
+  static forRoot(): ModuleWithProviders {
     return {
       ngModule: StatisticsModule,
       providers: [
diff --git a/src/app/statistics/statistics.service.spec.ts b/src/app/statistics/statistics.service.spec.ts
index c6cc4c10b55f9fc457bca669849f9668492a7898..1d659aac2baa888f54b44daf485e56d9907981df 100644
--- a/src/app/statistics/statistics.service.spec.ts
+++ b/src/app/statistics/statistics.service.spec.ts
@@ -8,10 +8,10 @@ import { DSpaceObjectType } from '../core/shared/dspace-object-type.model';
 import { SearchOptions } from '../shared/search/search-options.model';
 
 describe('StatisticsService', () => {
-  let service:StatisticsService;
-  let requestService:jasmine.SpyObj<RequestService>;
+  let service: StatisticsService;
+  let requestService: jasmine.SpyObj<RequestService>;
   const restURL = 'https://rest.api';
-  const halService:any = new HALEndpointServiceStub(restURL);
+  const halService: any = new HALEndpointServiceStub(restURL);
 
   function initTestService() {
     return new StatisticsService(
@@ -25,9 +25,9 @@ describe('StatisticsService', () => {
     service = initTestService();
 
     it('should send a request to track an item view ', () => {
-      const mockItem:any = {uuid: 'mock-item-uuid', type: 'item'};
+      const mockItem: any = {uuid: 'mock-item-uuid', type: 'item'};
       service.trackViewEvent(mockItem);
-      const request:TrackRequest = requestService.configure.calls.mostRecent().args[0];
+      const request: TrackRequest = requestService.configure.calls.mostRecent().args[0];
       expect(request.body).toBeDefined('request.body');
       const body = JSON.parse(request.body);
       expect(body.targetId).toBe('mock-item-uuid');
@@ -39,7 +39,7 @@ describe('StatisticsService', () => {
     requestService = getMockRequestService();
     service = initTestService();
 
-    const mockSearch:any = new SearchOptions({
+    const mockSearch: any = new SearchOptions({
       query: 'mock-query',
     });
 
@@ -51,7 +51,7 @@ describe('StatisticsService', () => {
     };
     const sort = {by: 'search-field', order: 'ASC'};
     service.trackSearchEvent(mockSearch, page, sort);
-    const request:TrackRequest = requestService.configure.calls.mostRecent().args[0];
+    const request: TrackRequest = requestService.configure.calls.mostRecent().args[0];
     const body = JSON.parse(request.body);
 
     it('should specify the right query', () => {
@@ -79,7 +79,7 @@ describe('StatisticsService', () => {
     requestService = getMockRequestService();
     service = initTestService();
 
-    const mockSearch:any = new SearchOptions({
+    const mockSearch: any = new SearchOptions({
       query: 'mock-query',
       configuration: 'mock-configuration',
       dsoType: DSpaceObjectType.ITEM,
@@ -108,7 +108,7 @@ describe('StatisticsService', () => {
       }
     ];
     service.trackSearchEvent(mockSearch, page, sort, filters);
-    const request:TrackRequest = requestService.configure.calls.mostRecent().args[0];
+    const request: TrackRequest = requestService.configure.calls.mostRecent().args[0];
     const body = JSON.parse(request.body);
 
     it('should specify the dsoType', () => {
diff --git a/src/app/submission/form/collection/submission-form-collection.component.ts b/src/app/submission/form/collection/submission-form-collection.component.ts
index d318bfe6871eac6dcd2c657f182b9632d7bc490a..f84764d6a4bcf1b005cab42213de592ceac2f475 100644
--- a/src/app/submission/form/collection/submission-form-collection.component.ts
+++ b/src/app/submission/form/collection/submission-form-collection.component.ts
@@ -1,8 +1,28 @@
-import { ChangeDetectorRef, Component, EventEmitter, HostListener, Input, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core';
+import {
+  ChangeDetectorRef,
+  Component,
+  EventEmitter,
+  HostListener,
+  Input,
+  OnChanges,
+  OnInit,
+  Output,
+  SimpleChanges
+} from '@angular/core';
 import { FormControl } from '@angular/forms';
 
 import { BehaviorSubject, combineLatest, Observable, of as observableOf, Subscription } from 'rxjs';
-import { debounceTime, distinctUntilChanged, filter, map, mergeMap, reduce, startWith, flatMap, find } from 'rxjs/operators';
+import {
+  debounceTime,
+  distinctUntilChanged,
+  filter,
+  find,
+  flatMap,
+  map,
+  mergeMap,
+  reduce,
+  startWith
+} from 'rxjs/operators';
 
 import { Collection } from '../../../core/shared/collection.model';
 import { CommunityDataService } from '../../../core/data/community-data.service';
@@ -227,8 +247,7 @@ export class SubmissionFormCollectionComponent implements OnChanges, OnInit {
             } else {
               return listCollection.filter((v) => v.collection.name.toLowerCase().indexOf(searchTerm.toLowerCase()) > -1).slice(0, 5);
             }
-        })
-      );
+          }));
       }
     }
   }
diff --git a/src/app/submission/submission.service.spec.ts b/src/app/submission/submission.service.spec.ts
index 3a95b0747ba8796e6f93f8262ac77b96992f5315..1b346a56ebb2a2f9efa38abd312025d3e53f48b4 100644
--- a/src/app/submission/submission.service.spec.ts
+++ b/src/app/submission/submission.service.spec.ts
@@ -401,6 +401,14 @@ describe('SubmissionService test suite', () => {
       service.createSubmission();
 
       expect((service as any).restService.postToEndpoint).toHaveBeenCalled();
+      expect((service as any).restService.postToEndpoint).toHaveBeenCalledWith('workspaceitems', {}, null, null, undefined);
+    });
+
+    it('should create a new submission with collection', () => {
+      service.createSubmission(collectionId);
+
+      expect((service as any).restService.postToEndpoint).toHaveBeenCalled();
+      expect((service as any).restService.postToEndpoint).toHaveBeenCalledWith('workspaceitems', {}, null, null, collectionId);
     });
   });
 
diff --git a/src/app/submission/submission.service.ts b/src/app/submission/submission.service.ts
index 766aa518f3f7037d2e98ea243fe35c419704b0c3..ac0cd7bec8535661713a554d8eb4d934d578df72 100644
--- a/src/app/submission/submission.service.ts
+++ b/src/app/submission/submission.service.ts
@@ -103,11 +103,13 @@ export class SubmissionService {
   /**
    * Perform a REST call to create a new workspaceitem and return response
    *
+   * @param collectionId
+   *    The owning collection id
    * @return Observable<SubmissionObject>
    *    observable of SubmissionObject
    */
-  createSubmission(): Observable<SubmissionObject> {
-    return this.restService.postToEndpoint(this.workspaceLinkPath, {}).pipe(
+  createSubmission(collectionId?: string): Observable<SubmissionObject> {
+    return this.restService.postToEndpoint(this.workspaceLinkPath, {}, null, null, collectionId).pipe(
       map((workspaceitem: SubmissionObject[]) => workspaceitem[0] as SubmissionObject),
       catchError(() => observableOf({} as SubmissionObject)))
   }
diff --git a/src/app/submission/submit/submission-submit.component.spec.ts b/src/app/submission/submit/submission-submit.component.spec.ts
index 771171a2d1e65ded70bc74bffa2f1badd0152f85..ca3316669f54b8a43935e2694a38bd31be735546 100644
--- a/src/app/submission/submit/submission-submit.component.spec.ts
+++ b/src/app/submission/submit/submission-submit.component.spec.ts
@@ -1,6 +1,6 @@
 import { async, ComponentFixture, fakeAsync, TestBed } from '@angular/core/testing';
 import { RouterTestingModule } from '@angular/router/testing';
-import { Router } from '@angular/router';
+import { ActivatedRoute, Router } from '@angular/router';
 import { NO_ERRORS_SCHEMA, ViewContainerRef } from '@angular/core';
 
 import { of as observableOf } from 'rxjs';
@@ -14,6 +14,7 @@ import { getMockTranslateService } from '../../shared/mocks/mock-translate.servi
 import { RouterStub } from '../../shared/testing/router-stub';
 import { mockSubmissionObject } from '../../shared/mocks/mock-submission';
 import { SubmissionSubmitComponent } from './submission-submit.component';
+import { ActivatedRouteStub } from '../../shared/testing/active-router-stub';
 
 describe('SubmissionSubmitComponent Component', () => {
 
@@ -39,6 +40,7 @@ describe('SubmissionSubmitComponent Component', () => {
         { provide: SubmissionService, useClass: SubmissionServiceStub },
         { provide: TranslateService, useValue: getMockTranslateService() },
         { provide: Router, useValue: new RouterStub() },
+        { provide: ActivatedRoute, useValue: new ActivatedRouteStub() },
         ViewContainerRef
       ],
       schemas: [NO_ERRORS_SCHEMA]
diff --git a/src/app/submission/submit/submission-submit.component.ts b/src/app/submission/submit/submission-submit.component.ts
index 448ccf97e20691aabfdf45560b7056bbec7594de..0aa0380a251e32a22d84ce286f59e5a186b32686 100644
--- a/src/app/submission/submit/submission-submit.component.ts
+++ b/src/app/submission/submit/submission-submit.component.ts
@@ -1,5 +1,5 @@
 import { ChangeDetectorRef, Component, OnDestroy, OnInit, ViewContainerRef } from '@angular/core';
-import { Router } from '@angular/router';
+import { ActivatedRoute, Router } from '@angular/router';
 
 import { Subscription } from 'rxjs';
 
@@ -27,6 +27,12 @@ export class SubmissionSubmitComponent implements OnDestroy, OnInit {
    */
   public collectionId: string;
 
+  /**
+   * The collection id input to create a new submission
+   * @type {string}
+   */
+  public collectionParam: string;
+
   /**
    * The submission self url
    * @type {string}
@@ -60,13 +66,18 @@ export class SubmissionSubmitComponent implements OnDestroy, OnInit {
    * @param {Router} router
    * @param {TranslateService} translate
    * @param {ViewContainerRef} viewContainerRef
+   * @param {ActivatedRoute} route
    */
   constructor(private changeDetectorRef: ChangeDetectorRef,
               private notificationsService: NotificationsService,
               private router: Router,
               private submissionService: SubmissionService,
               private translate: TranslateService,
-              private viewContainerRef: ViewContainerRef) {
+              private viewContainerRef: ViewContainerRef,
+              private route: ActivatedRoute) {
+    this.route
+      .queryParams
+      .subscribe((params) => { this.collectionParam = (params.collection); });
   }
 
   /**
@@ -75,7 +86,7 @@ export class SubmissionSubmitComponent implements OnDestroy, OnInit {
   ngOnInit() {
     // NOTE execute the code on the browser side only, otherwise it is executed twice
     this.subs.push(
-      this.submissionService.createSubmission()
+      this.submissionService.createSubmission(this.collectionParam)
         .subscribe((submissionObject: SubmissionObject) => {
           // NOTE new submission is created on the browser side only
           if (isNotNull(submissionObject)) {
diff --git a/src/config/collection-page-config.interface.ts b/src/config/collection-page-config.interface.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b0103fd1768b32563f07cf86a6948323c1bd7368
--- /dev/null
+++ b/src/config/collection-page-config.interface.ts
@@ -0,0 +1,7 @@
+import { Config } from './config.interface';
+
+export interface CollectionPageConfig extends Config {
+  edit: {
+    undoTimeout: number;
+  }
+}
diff --git a/src/config/global-config.interface.ts b/src/config/global-config.interface.ts
index 22b4b0500fc985308b45a3aaaccb38eb068d4d5b..dec23ff67611c4d6375aca756cebd20e3300ae0c 100644
--- a/src/config/global-config.interface.ts
+++ b/src/config/global-config.interface.ts
@@ -8,6 +8,7 @@ import { FormConfig } from './form-config.interfaces';
 import {LangConfig} from './lang-config.interface';
 import { BrowseByConfig } from './browse-by-config.interface';
 import { ItemPageConfig } from './item-page-config.interface';
+import { CollectionPageConfig } from './collection-page-config.interface';
 import { Theme } from './theme.inferface';
 
 export interface GlobalConfig extends Config {
@@ -26,5 +27,6 @@ export interface GlobalConfig extends Config {
   languages: LangConfig[];
   browseBy: BrowseByConfig;
   item: ItemPageConfig;
+  collection: CollectionPageConfig;
   themes: Theme[];
 }
diff --git a/tslint.json b/tslint.json
index b4f905d3245a996ddf88139fa6e88650a5a6acde..51edf5e1d7be5d04c687fc6e0da5f7ca746c0e7d 100644
--- a/tslint.json
+++ b/tslint.json
@@ -40,7 +40,7 @@
     ],
     "no-access-missing-member": false,
     "no-arg": true,
-    "no-attribute-parameter-decorator": true,
+    "no-attribute-decorator": true,
     "no-bitwise": true,
     "no-console": [
       true,
@@ -81,9 +81,8 @@
       false
     ],
     "prefer-const": true,
-    "pipe-naming": [
+    "pipe-prefix": [
       true,
-      "camelCase",
       "ds"
     ],
     "quotemark": [
@@ -114,6 +113,13 @@
         "parameter": "nospace",
         "property-declaration": "nospace",
         "variable-declaration": "nospace"
+      },
+      {
+        "call-signature": "onespace",
+        "index-signature": "onespace",
+        "parameter": "onespace",
+        "property-declaration": "onespace",
+        "variable-declaration": "onespace"
       }
     ],
     "unified-signatures": true,
@@ -147,10 +153,10 @@
     "no-input-rename": true,
     "no-output-rename": true,
     "templates-use-public": false,
-    "use-host-property-decorator": true,
-    "use-input-property-decorator": true,
+    "no-host-metadata-property": true,
+    "no-inputs-metadata-property": true,
     "use-life-cycle-interface": false,
-    "use-output-property-decorator": true,
+    "no-outputs-metadata-property": true,
     "use-pipe-transform-interface": true
 //    "rxjs-collapse-imports": true,
 //    "rxjs-pipeable-operators-only": true,