diff --git a/README.md b/README.md
index 8f2320dbf3270f74ecc97c145d1315d120f92e6c..cb2f41130fbc341778c11413763b94432457d67f 100644
--- a/README.md
+++ b/README.md
@@ -14,7 +14,7 @@ If you're looking for the 2016 Angular 2 DSpace UI prototype, you can find it [h
 Quick start
 -----------
 
-**Ensure you're running [Node](https://nodejs.org) >= `v8.0.x`, [npm](https://www.npmjs.com/) >= `v3.x` and [yarn](https://yarnpkg.com) >= `v0.20.x`**
+**Ensure you're running [Node](https://nodejs.org) >= `v8.0.x`, [npm](https://www.npmjs.com/) >= `v5.x` and [yarn](https://yarnpkg.com) >= `v1.x`**
 
 ```bash
 # clone the repo
@@ -65,7 +65,7 @@ Requirements
 ------------
 
 -	[Node.js](https://nodejs.org), [npm](https://www.npmjs.com/), and [yarn](https://yarnpkg.com)
--	Ensure you're running node >= `v5.x`, npm >= `v3.x` and yarn >= `v0.20.x`
+-	Ensure you're running node >= `v8.x`, npm >= `v5.x` and yarn >= `v1.x`
 
 If you have [`nvm`](https://github.com/creationix/nvm#install-script) or [`nvm-windows`](https://github.com/coreybutler/nvm-windows) installed, which is highly recommended, you can run `nvm install --lts && nvm use` to install and start using the latest Node LTS.
 
diff --git a/config/environment.default.js b/config/environment.default.js
index f70f132fa455bae0655392a4763eecc60fb0e457..804d80b0f20f9a3aa6b28eb02e854ebaca8dcd10 100644
--- a/config/environment.default.js
+++ b/config/environment.default.js
@@ -10,7 +10,7 @@ module.exports = {
   // The REST API server settings.
   rest: {
     ssl: true,
-    host: 'dspace7.4science.it',
+    host: 'dspace7.4science.cloud',
     port: 443,
     // NOTE: Space is capitalized because 'namespace' is a reserved string in TypeScript
     nameSpace: '/dspace-spring-rest/api'
diff --git a/package.json b/package.json
index 1f75da6c8be8a3cc81733b90e9f7b97094857657..cc687ea269e6f470a09b5b26deaa7c4ce590d168 100644
--- a/package.json
+++ b/package.json
@@ -23,9 +23,9 @@
     "prebuild": "yarn run clean:dist",
     "prebuild:aot": "yarn run prebuild",
     "prebuild:prod": "yarn run prebuild",
-    "build": "webpack --progress --mode development",
-    "build:aot": "webpack --env.aot --env.server --mode development && webpack --env.aot --env.client --mode development",
-    "build:prod": "webpack --env.aot --env.server --mode production && webpack --env.aot --env.client --mode production",
+    "build": "node ./webpack/run-webpack.js --progress --mode development",
+    "build:aot": "node ./webpack/run-webpack.js --env.aot --env.server --mode development && node ./webpack/run-webpack.js --env.aot --env.client --mode development",
+    "build:prod": "node ./webpack/run-webpack.js --env.aot --env.server --mode production && node ./webpack/run-webpack.js --env.aot --env.client --mode production",
     "postbuild:prod": "yarn run rollup",
     "rollup": "rollup -c rollup.config.js",
     "prestart": "yarn run build:prod",
@@ -40,7 +40,7 @@
     "server": "node dist/server.js",
     "server:watch": "nodemon dist/server.js",
     "server:watch:debug": "nodemon --debug dist/server.js",
-    "webpack:watch": "webpack -w --mode development",
+    "webpack:watch": "node ./webpack/run-webpack.js -w --mode development",
     "watch": "yarn run build && npm-run-all -p webpack:watch server:watch",
     "watch:debug": "yarn run build && npm-run-all -p webpack:watch server:watch:debug",
     "predebug": "yarn run build",
diff --git a/resources/i18n/en.json b/resources/i18n/en.json
index 875a624c7e4b23af6e76e15dabe43e13a12778af..4260568a7e2891468501e5a2f1a6f8e9d17a4f07 100644
--- a/resources/i18n/en.json
+++ b/resources/i18n/en.json
@@ -91,12 +91,14 @@
   },
   "item": {
     "page": {
-      "author": "Author",
+      "author": "Authors",
       "abstract": "Abstract",
       "date": "Date",
       "uri": "URI",
       "files": "Files",
       "collections": "Collections",
+      "subject": "Keywords",
+      "citation": "Citation",
       "filesection": {
         "download": "Download",
         "name": "Name:",
@@ -270,6 +272,114 @@
       }
     }
   },
+  "relationships": {
+    "isPublicationOf": "Publications",
+    "isProjectOf": "Research Projects",
+    "isOrgUnitOf": "Organizational Units",
+    "isAuthorOf": "Authors",
+    "isPersonOf": "Authors",
+    "isJournalOf": "Journals",
+    "isSingleJournalOf": "Journal",
+    "isVolumeOf": "Journal Volumes",
+    "isSingleVolumeOf": "Journal Volume",
+    "isIssueOf": "Journal Issues",
+    "isJournalIssueOf": "Journal Issue",
+    "isPublicationOfJournalIssue": "Articles"
+  },
+  "person": {
+    "page": {
+      "titleprefix": "Person: ",
+      "jobtitle": "Job Title",
+      "lastname": "Last Name",
+      "firstname": "First Name",
+      "email": "Email Address",
+      "orcid": "ORCID",
+      "birthdate": "Birth Date",
+      "staffid": "Staff ID",
+      "link": {
+        "full": "Show all metadata"
+      }
+    },
+    "listelement": {
+      "badge": "Person"
+    }
+  },
+  "project": {
+    "page": {
+      "titleprefix": "Research Project: ",
+      "status": "Status",
+      "contributor": "Contributors",
+      "funder": "Funders",
+      "id": "ID",
+      "expectedcompletion": "Expected Completion",
+      "description": "Description",
+      "keyword": "Keywords"
+    },
+    "listelement": {
+      "badge": "Research Project"
+    }
+  },
+  "orgunit": {
+    "page": {
+      "titleprefix": "Organizational Unit: ",
+      "dateestablished": "Date established",
+      "city": "City",
+      "country": "Country",
+      "id": "ID",
+      "description": "Description"
+    },
+    "listelement": {
+      "badge": "Organizational Unit"
+    }
+  },
+  "journal": {
+    "page": {
+      "titleprefix": "Journal: ",
+      "issn": "ISSN",
+      "publisher": "Publisher",
+      "description": "Description",
+      "editor": "Editor-in-Chief"
+    },
+    "listelement": {
+      "badge": "Journal"
+    }
+  },
+  "journalvolume": {
+    "page": {
+      "titleprefix": "Journal Volume: ",
+      "volume": "Volume",
+      "issuedate": "Issue Date",
+      "description": "Description"
+    },
+    "listelement": {
+      "badge": "Journal Volume"
+    }
+  },
+  "journalissue": {
+    "page": {
+      "titleprefix": "Journal Issue: ",
+      "number": "Number",
+      "issuedate": "Issue Date",
+      "description": "Description",
+      "keyword": "Keywords",
+      "journal-title": "Journal Title",
+      "journal-issn": "Journal ISSN"
+    },
+    "listelement": {
+      "badge": "Journal Issue"
+    }
+  },
+  "publication": {
+    "page": {
+      "titleprefix": "Publication: ",
+      "journal-title": "Journal Title",
+      "journal-issn": "Journal ISSN",
+      "volume-title": "Volume Title"
+    },
+    "listelement": {
+      "badge": "Publication"
+    }
+  },
   "nav": {
     "browse": {
       "header": "All of DSpace"
@@ -282,6 +392,7 @@
     },
     "login": "Log In",
     "logout": "Log Out",
+    "mydspace": "MyDSpace",
     "language": "Language switch",
     "search": "Search"
   },
@@ -318,12 +429,82 @@
       "help": "Select a community to browse its collections."
     }
   },
+  "mydspace": {
+    "title": "MyDSpace",
+    "description": "",
+    "new-submission": "New submission",
+    "results": {
+      "head": "Your submissions",
+      "no-results": "There were no items to show",
+      "no-title": "No title",
+      "no-authors": "No Authors",
+      "no-date": "No Date",
+      "no-abstract": "No Abstract",
+      "no-files": "No Files",
+      "no-uri": "No Uri",
+      "no-collections": "No Collections"
+    },
+    "messages": {
+      "title": "Messages",
+      "to": "To",
+      "hide-msg": "Hide message",
+      "show-msg": "Show message",
+      "no-messages": "No messages yet.",
+      "no-content": "No content.",
+      "send-btn": "Send",
+      "subject-placeholder": "Subject...",
+      "description-placeholder": "Insert your message here...",
+      "mark-as-read": "Mark as read",
+      "mark-as-unread": "Mark as unread",
+      "submitter-help": "Select this option to send a message to controller.",
+      "controller-help": "Select this option to send a message to item's submitter."
+    },
+    "show": {
+      "workspace": "Your Submissions",
+      "workflow": "All tasks"
+    },
+    "status": {
+      "workflow": "Workflow",
+      "validation": "Validation",
+      "waiting-for-controller": "Waiting for controller",
+      "workspace": "Workspace",
+      "archived": "Archived"
+    },
+    "view-btn": "View",
+    "general": {
+      "text-here": "HERE"
+    },
+    "upload": {
+      "upload-successful": "New workspace item created. Click {{here}} for edit it.",
+      "upload-multiple-successful": "{{qty}} new workspace items created.",
+      "upload-failed": "Error creating new workspace. Please verify the content uploaded before retry."
+    }
+  },
   "search": {
+    "journal": {
+      "title": "DSpace Angular :: Journal Search",
+      "results": {
+        "head": "Journal Search Results"
+      }
+    },
+    "person": {
+      "title": "DSpace Angular :: Person Search",
+      "results": {
+        "head": "Person Search Results"
+      }
+    },
+    "publication": {
+      "title": "DSpace Angular :: Publication Search",
+      "results": {
+        "head": "Publication Search Results"
+      }
+    },
     "title": "DSpace Angular :: Search",
     "description": "",
     "form": {
       "search": "Search",
-      "search_dspace": "Search DSpace"
+      "search_dspace": "Search DSpace",
+      "search_mydspace": "Search MyDSpace"
     },
     "results": {
       "head": "Search Results",
@@ -343,9 +524,13 @@
         "rpp": "Results per page"
       }
     },
+    "switch-configuration": {
+      "title":"Show"
+    },
     "view-switch": {
       "show-list": "Show as list",
-      "show-grid": "Show as grid"
+      "show-grid": "Show as grid",
+      "show-detail": "Show detail"
     },
     "filters": {
       "head": "Filters",
@@ -355,7 +540,12 @@
         "f.dateIssued.min": "Start date",
         "f.dateIssued.max": "End date",
         "f.subject": "Subject",
-        "f.has_content_in_original_bundle": "Has files"
+        "f.has_content_in_original_bundle": "Has files",
+        "f.entityType": "Item Type",
+        "f.namedresourcetype": "Status",
+        "f.dateSubmitted": "Date submitted",
+        "f.itemtype": "Type",
+        "f.submitter": "Submitter"
       },
       "filter": {
         "show-more": "Show more",
@@ -383,6 +573,30 @@
         },
         "has_content_in_original_bundle": {
           "head": "Has files"
+        },
+        "entityType": {
+          "placeholder": "Item Type",
+          "head": "Item Type"
+        },
+        "namedresourcetype": {
+          "placeholder": "Status",
+          "head": "Status"
+        },
+        "dateSubmitted": {
+          "placeholder": "Date submitted",
+          "head": "Date submitted"
+        },
+        "itemtype": {
+          "placeholder": "Type",
+          "head": "Type"
+        },
+        "submitter": {
+          "placeholder": "Submitter",
+          "head": "Submitter"
+        },
+        "objectpeople": {
+          "placeholder": "People",
+          "head": "People"
         }
       }
     }
@@ -603,6 +817,7 @@
     "item": "Loading item...",
     "objects": "Loading...",
     "search-results": "Loading search results...",
+    "mydspace-results": "Loading items...",
     "browse-by": "Loading items...",
     "browse-by-page": "Loading page..."
   },
@@ -794,6 +1009,49 @@
           }
         }
       }
+    },
+    "workflow": {
+      "generic": {
+        "delete": "Delete",
+        "delete-help": "If you would to discard this item, select \"Delete\".  You will then be asked to confirm it.",
+        "edit": "Edit",
+        "edit-help": "Select this option to change the item's metadata.",
+        "view": "View",
+        "view-help": "Select this option to view the item's metadata."
+      },
+      "tasks": {
+        "generic": {
+          "processing": "Processing...",
+          "success": "Operation successful",
+          "error": "Error occurred during operation...",
+          "submitter": "Submitter"
+        },
+        "claimed": {
+          "approve": "Approve",
+          "approve_help": "If you have reviewed the item and it is suitable for inclusion in the collection, select \"Approve\".",
+          "edit": "Edit",
+          "edit_help": "Select this option to change the item's metadata.",
+          "reject": {
+            "submit": "Reject",
+            "reason": {
+              "submit": "Reject item",
+              "title": "Reason",
+              "info": "Please enter your reason for rejecting the submission into the box below, indicating whether the submitter may fix a problem and resubmit.",
+              "placeholder": "Describe the reason of reject"
+            }
+          },
+          "reject_help": "If you have reviewed the item and found it is <strong>not</strong> suitable for inclusion in the collection, select \"Reject\".  You will then be asked to enter a message indicating why the item is unsuitable, and whether the submitter should change something and resubmit.",
+          "return": "Return to pool",
+          "return_help": "Return the task to the pool so that another user may perform the task."
+
+        },
+        "pool": {
+          "claim": "Claim",
+          "claim_help": "Assign this task to yourself.",
+          "show-detail": "Show detail",
+          "hide-detail": "Hide detail"
+        }
+      }
     }
   },
   "uploader": {
diff --git a/resources/images/orgunit-placeholder.svg b/resources/images/orgunit-placeholder.svg
new file mode 100644
index 0000000000000000000000000000000000000000..1dae3d607ebaa2ba51f09af94dcecdc6cd0e6347
--- /dev/null
+++ b/resources/images/orgunit-placeholder.svg
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" focusable="false" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
+	 x="0px" y="0px" width="125.348px" height="161.348px" viewBox="62.326 80.326 125.348 161.348"
+	 enable-background="new 62.326 80.326 125.348 161.348" xml:space="preserve">
+<rect x="62.5" y="80.5" fill="#FFFFFF" stroke="#000000" stroke-width="0.3478" stroke-miterlimit="10" width="125" height="161"/>
+<path fill="#43515F" d="M135.5,171.5h-21c-2.899,0-5.25,2.352-5.25,5.25v21c0,2.898,2.351,5.25,5.25,5.25h21
+	c2.898,0,5.25-2.352,5.25-5.25v-21C140.75,173.852,138.398,171.5,135.5,171.5z M104,124.25c0-2.899-2.351-5.25-5.25-5.25h-21
+	c-2.899,0-5.25,2.351-5.25,5.25v21c0,2.899,2.351,5.25,5.25,5.25h15.704l12.002,21.007c1.822-3.127,5.171-5.257,9.043-5.257h0.046
+	L104,147.794V140h36.75v-10.5H104V124.25z M172.25,119h-21c-2.898,0-5.25,2.351-5.25,5.25v21c0,2.899,2.352,5.25,5.25,5.25h21
+	c2.898,0,5.25-2.351,5.25-5.25v-21C177.5,121.351,175.148,119,172.25,119z"/>
+</svg>
diff --git a/resources/images/person-placeholder.svg b/resources/images/person-placeholder.svg
new file mode 100644
index 0000000000000000000000000000000000000000..bbe84ec8455f1e8419e6e6f3119182f0f14f4d37
--- /dev/null
+++ b/resources/images/person-placeholder.svg
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" focusable="false" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
+	 x="0px" y="0px" width="125.348px" height="161.348px" viewBox="62.674 80.674 125.348 161.348"
+	 enable-background="new 62.674 80.674 125.348 161.348" xml:space="preserve">
+<rect x="62.847" y="80.847" fill="#FFFFFF" stroke="#000000" stroke-width="0.3478" stroke-miterlimit="10" width="125" height="161"/>
+<path fill="#43515F" d="M125.347,167.91c16.304,0,29.531-13.228,29.531-29.531c0-16.303-13.228-29.531-29.531-29.531
+	c-16.303,0-29.531,13.227-29.531,29.531S109.044,167.91,125.347,167.91z M151.597,174.472h-11.301
+	c-4.552,2.092-9.617,3.281-14.95,3.281c-5.332,0-10.377-1.189-14.95-3.281h-11.3c-14.499,0-26.25,11.751-26.25,26.25v3.281
+	c0,5.435,4.409,9.844,9.844,9.844h85.312c5.435,0,9.844-4.408,9.844-9.844v-3.281C177.847,186.223,166.096,174.472,151.597,174.472z
+	"/>
+</svg>
diff --git a/resources/images/project-placeholder.svg b/resources/images/project-placeholder.svg
new file mode 100644
index 0000000000000000000000000000000000000000..75ce1003fea407fa5a6f9384d1ecc12c8f85157f
--- /dev/null
+++ b/resources/images/project-placeholder.svg
@@ -0,0 +1,118 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 width="125.5px" height="161.5px" viewBox="62.25 80.25 125.5 161.5" enable-background="new 62.25 80.25 125.5 161.5"
+	 xml:space="preserve">
+<rect x="62.5" y="80.5" fill="#FFFFFF" stroke="#000000" stroke-width="0.5" stroke-miterlimit="10" width="125" height="161"/>
+<g>
+	<path fill="#43515F" d="M176.007,108.235h-65.078c-0.972,0-1.759,0.788-1.759,1.759v7.035h-7.035c-0.972,0-1.759,0.788-1.759,1.759
+		v9.246c-5.482,3.937-8.794,10.281-8.794,17.136c0,3.284,0.808,6.52,2.278,9.449c-0.313,0.902-0.52,1.855-0.52,2.863
+		c0,0.32,0.062,0.625,0.095,0.936l-9.667,9.666c-0.323-0.029-0.65-0.049-0.981-0.049c-5.82,0-10.553,4.733-10.553,10.553
+		c0,3.115,1.364,5.91,3.517,7.844v25.574c0,0.971,0.787,1.759,1.759,1.759h10.553c0.973,0,1.759-0.788,1.759-1.759v-25.574
+		c2.153-1.932,3.518-4.727,3.518-7.844c0-1.349-0.264-2.635-0.727-3.82l7.762-7.764v45.002c0,0.971,0.786,1.759,1.759,1.759h65.077
+		c0.973,0,1.759-0.787,1.759-1.759v-5.275h7.036c0.972,0,1.759-0.789,1.759-1.76v-94.978
+		C177.765,109.023,176.979,108.235,176.007,108.235z M102.965,130.539c0.044-0.022,0.092-0.037,0.134-0.063l0.193-0.129l3.835,3.834
+		c-4.083,2.049-6.752,6.234-6.752,10.99c0,0.586,0.048,1.169,0.132,1.749c0.029,0.197,0.083,0.387,0.122,0.583
+		c0.074,0.376,0.148,0.752,0.257,1.12c0.014,0.051,0.02,0.104,0.035,0.153c-0.103,0.014-0.201,0.049-0.302,0.067
+		c-0.204,0.035-0.401,0.084-0.6,0.134c-0.3,0.075-0.592,0.167-0.881,0.274c-0.219,0.082-0.437,0.157-0.646,0.253
+		c-0.256,0.118-0.496,0.261-0.739,0.401c-0.371,0.214-0.718,0.454-1.052,0.719c-0.192,0.153-0.393,0.292-0.57,0.461
+		c-0.681-1.891-1.031-3.896-1.031-5.914C95.1,139.241,98.084,133.785,102.965,130.539z M106.321,154.306
+		c0.119,0.157,0.227,0.318,0.329,0.487c0.063,0.107,0.122,0.217,0.177,0.327c0.08,0.156,0.155,0.313,0.218,0.477
+		c0.116,0.304,0.208,0.611,0.268,0.914c0.06,0.317,0.099,0.639,0.099,0.971c0,0.406-0.058,0.803-0.148,1.191
+		c-0.028,0.118-0.072,0.227-0.105,0.341c-0.081,0.265-0.176,0.521-0.297,0.77c-0.06,0.122-0.126,0.242-0.197,0.362
+		c-0.135,0.229-0.291,0.448-0.459,0.653c-0.081,0.099-0.157,0.202-0.243,0.296c-0.266,0.281-0.556,0.544-0.883,0.767
+		c-0.843,0.566-1.855,0.898-2.944,0.898c-0.397,0-0.781-0.052-1.152-0.136c-0.009,0-0.018-0.007-0.026-0.01
+		c-0.115-0.027-0.222-0.069-0.333-0.102c-0.132-0.041-0.266-0.073-0.392-0.121c-0.164-0.064-0.318-0.145-0.473-0.225
+		c-0.103-0.052-0.202-0.105-0.301-0.163c-0.153-0.091-0.305-0.187-0.447-0.292c-0.075-0.056-0.146-0.119-0.218-0.181
+		c-0.259-0.215-0.496-0.45-0.708-0.709c-0.06-0.072-0.123-0.141-0.177-0.216c-0.105-0.143-0.203-0.294-0.294-0.449
+		c-0.059-0.098-0.111-0.198-0.164-0.301c-0.079-0.154-0.158-0.31-0.222-0.473c-0.056-0.146-0.097-0.299-0.141-0.45
+		c-0.026-0.094-0.063-0.181-0.084-0.276c-0.001-0.007-0.005-0.014-0.007-0.021c-0.084-0.369-0.137-0.754-0.137-1.152
+		c0-0.776,0.178-1.507,0.48-2.172c0.005-0.012,0.016-0.021,0.021-0.033c0.193-0.417,0.442-0.793,0.726-1.138
+		c0.049-0.058,0.095-0.119,0.146-0.176c0.127-0.142,0.264-0.274,0.404-0.401c0.077-0.069,0.158-0.134,0.241-0.198
+		c0.141-0.113,0.28-0.226,0.431-0.322c0.214-0.137,0.438-0.261,0.672-0.364c0.118-0.052,0.241-0.091,0.362-0.137
+		c0.178-0.065,0.359-0.124,0.544-0.169c0.107-0.026,0.214-0.052,0.323-0.072c0.303-0.053,0.608-0.093,0.925-0.093
+		c0.365,0,0.739,0.044,1.147,0.135h0.001h0.002c0.533,0.118,1.025,0.329,1.481,0.593c0.063,0.037,0.132,0.065,0.191,0.104
+		c0.179,0.115,0.343,0.248,0.506,0.383c0.078,0.063,0.156,0.122,0.229,0.19c0.149,0.139,0.287,0.29,0.421,0.443
+		C106.186,154.139,106.256,154.22,106.321,154.306z M110.928,157.357c0.58,0.083,1.168,0.125,1.76,0.125
+		c4.754,0,8.938-2.667,10.988-6.752l3.863,3.862c-3.22,5.069-8.797,8.166-14.851,8.166c-1.101,0-2.2-0.107-3.289-0.32
+		c0.009-0.012,0.014-0.026,0.023-0.038c0.007-0.011,0.012-0.024,0.021-0.035c0.06-0.09,0.105-0.19,0.164-0.281
+		c0.091-0.15,0.179-0.3,0.262-0.455c0.102-0.187,0.198-0.377,0.285-0.572c0.049-0.111,0.095-0.224,0.14-0.337
+		c0.052-0.131,0.104-0.263,0.15-0.399c0.088-0.255,0.152-0.517,0.216-0.781c0.032-0.13,0.074-0.253,0.099-0.385
+		c0.014-0.074,0.028-0.146,0.041-0.22c0.081-0.479,0.13-0.962,0.13-1.453C110.929,157.44,110.929,157.398,110.928,157.357z
+		 M86.306,210.247h-7.035v-21.722c0.545,0.194,1.11,0.346,1.692,0.447c0.035,0.006,0.069,0.011,0.102,0.016
+		c0.563,0.094,1.136,0.153,1.724,0.153c0.587,0,1.161-0.06,1.722-0.153c0.035-0.006,0.069-0.009,0.103-0.016
+		c0.583-0.102,1.148-0.253,1.693-0.447V210.247z M86.978,184.207c-0.003,0.002-0.007,0.005-0.011,0.006
+		c-0.287,0.215-0.589,0.398-0.898,0.562c-0.039,0.021-0.078,0.044-0.118,0.063c-0.301,0.151-0.608,0.28-0.924,0.387
+		c-0.055,0.018-0.109,0.033-0.163,0.051c-1.353,0.421-2.8,0.421-4.153,0c-0.054-0.018-0.109-0.033-0.164-0.051
+		c-0.315-0.106-0.623-0.234-0.923-0.387c-0.041-0.021-0.079-0.043-0.118-0.063c-0.31-0.163-0.61-0.349-0.898-0.562
+		c-0.003-0.001-0.007-0.004-0.011-0.006c-1.715-1.283-2.844-3.314-2.844-5.619c0-3.879,3.155-7.034,7.036-7.034
+		c0.542,0,1.064,0.075,1.573,0.192c0.012,0.003,0.024,0.01,0.037,0.014c0.48,0.111,0.943,0.275,1.382,0.486
+		c0.004,0.002,0.007,0.004,0.011,0.006c1.319,0.632,2.427,1.678,3.148,3c0.003,0.006,0.009,0.01,0.012,0.015
+		c0.539,0.995,0.873,2.115,0.873,3.322C89.823,180.893,88.694,182.922,86.978,184.207z M90.738,171.67
+		c-0.875-1.003-1.932-1.84-3.124-2.456l7.077-7.079c0.009,0.016,0.024,0.027,0.034,0.042c0.695,1.095,1.62,2.018,2.714,2.714
+		c0.016,0.011,0.027,0.025,0.042,0.033L90.738,171.67z M165.453,210.247h-61.559v-44.148c0.002,0,0.003-0.002,0.005-0.002
+		c0.031-0.007,0.06-0.021,0.092-0.027c0.506-0.11,0.996-0.265,1.465-0.456c0.167-0.068,0.32-0.16,0.481-0.239
+		c0.101-0.05,0.201-0.097,0.299-0.147c2.11,0.684,4.275,1.05,6.451,1.05c7.784,0,14.913-4.267,18.602-11.138
+		c0.367-0.684,0.244-1.526-0.306-2.075l-6.775-6.775c-0.452-0.452-1.111-0.625-1.729-0.447c-0.616,0.178-1.085,0.674-1.228,1.296
+		c-0.921,4.021-4.442,6.827-8.563,6.827c-0.939,0-1.852-0.187-2.737-0.479c-0.07-0.135-0.161-0.257-0.239-0.389
+		c-0.144-0.248-0.283-0.495-0.45-0.725c-0.113-0.155-0.245-0.296-0.366-0.443c-0.348-0.424-0.731-0.811-1.154-1.163
+		c-0.168-0.143-0.331-0.293-0.512-0.422c-0.218-0.157-0.45-0.287-0.683-0.422c-0.218-0.128-0.436-0.251-0.664-0.362
+		c-0.236-0.113-0.476-0.209-0.721-0.301c-0.099-0.036-0.188-0.088-0.289-0.119c-0.05-0.095-0.102-0.188-0.147-0.287
+		c-0.063-0.135-0.123-0.272-0.18-0.41c-0.088-0.216-0.165-0.438-0.235-0.659c-0.042-0.134-0.086-0.268-0.122-0.403
+		c-0.065-0.244-0.113-0.494-0.155-0.744c-0.02-0.118-0.048-0.233-0.063-0.352c-0.049-0.371-0.078-0.742-0.078-1.115
+		c0-4.121,2.807-7.644,6.825-8.563c0.622-0.143,1.12-0.614,1.296-1.228c0.176-0.614,0.007-1.277-0.446-1.729l-6.775-6.775
+		c-0.132-0.132-0.283-0.237-0.443-0.318c-0.053-0.026-0.11-0.037-0.166-0.06c-0.097-0.037-0.19-0.084-0.292-0.104v-5.846h7.036
+		h54.523v89.7H165.453z M174.247,203.212h-5.276v-84.424c0-0.971-0.786-1.759-1.76-1.759h-54.523v-5.277h61.559V203.212
+		L174.247,203.212z"/>
+	<path fill="#43515F" d="M144.347,196.177c0-0.97-0.786-1.759-1.759-1.759h-5.277v-7.035c0-0.971-0.785-1.759-1.759-1.759h-5.276
+		v-12.312c0-0.972-0.786-1.76-1.759-1.76h-7.035c-0.973,0-1.759,0.788-1.759,1.76v8.793h-5.276c-0.973,0-1.759,0.788-1.759,1.76
+		v17.589h-3.518v3.517h38.695v-3.517h-3.518V196.177L144.347,196.177z M116.206,185.624h3.518v15.829h-3.518V185.624z
+		 M123.241,183.864v-8.794h3.518v12.312v14.07h-3.518V183.864z M130.276,189.141h3.519v7.036v5.276h-3.519V189.141z
+		 M137.312,201.453v-3.518h3.519v3.518H137.312z"/>
+	<path fill="#43515F" d="M161.844,169.476c0.008-0.064,0.021-0.128,0.029-0.193c0.042-0.411,0.062-0.829,0.062-1.247
+		c0-6.789-5.523-12.312-12.312-12.312c-6.787,0-12.312,5.523-12.312,12.312c0,0.418,0.021,0.836,0.063,1.247
+		c0.007,0.065,0.021,0.129,0.028,0.193c0.04,0.349,0.089,0.693,0.158,1.034c0.003,0.016,0.009,0.031,0.012,0.05
+		c0.481,2.325,1.625,4.464,3.346,6.182c0.009,0.009,0.02,0.011,0.029,0.02c2.227,2.215,5.294,3.587,8.675,3.587
+		s6.448-1.372,8.675-3.587c0.01-0.009,0.021-0.011,0.03-0.02c1.719-1.719,2.862-3.856,3.346-6.182
+		c0.003-0.018,0.009-0.034,0.011-0.05C161.754,170.169,161.804,169.824,161.844,169.476z M158.256,169.635
+		c-0.024,0.137-0.06,0.271-0.09,0.406c-0.097,0.414-0.221,0.817-0.376,1.206c-0.038,0.1-0.072,0.202-0.115,0.301
+		c-0.204,0.465-0.446,0.911-0.724,1.329l-2.565-2.566c0.017-0.037,0.023-0.08,0.042-0.119c0.094-0.209,0.159-0.43,0.227-0.654
+		c0.038-0.13,0.092-0.253,0.119-0.387c0.081-0.36,0.127-0.731,0.127-1.115c0-0.332-0.039-0.654-0.099-0.969
+		c-0.018-0.099-0.046-0.191-0.071-0.288c-0.052-0.216-0.116-0.429-0.196-0.634c-0.038-0.1-0.077-0.197-0.123-0.293
+		c-0.095-0.206-0.204-0.404-0.323-0.595c-0.046-0.073-0.084-0.149-0.134-0.22c-0.177-0.254-0.37-0.49-0.586-0.709
+		c-0.036-0.039-0.081-0.069-0.12-0.105c-0.184-0.176-0.38-0.34-0.589-0.488c-0.083-0.057-0.169-0.108-0.255-0.163
+		c-0.182-0.114-0.369-0.216-0.566-0.307c-0.095-0.045-0.188-0.089-0.287-0.129c-0.058-0.023-0.111-0.055-0.17-0.075v-3.641
+		c0.001,0,0.003,0,0.005,0c0.032,0.007,0.06,0.021,0.092,0.028c0.507,0.109,0.995,0.264,1.465,0.456
+		c0.168,0.068,0.32,0.16,0.482,0.239c0.321,0.156,0.64,0.318,0.939,0.51c0.174,0.113,0.334,0.239,0.499,0.365
+		c0.262,0.197,0.517,0.401,0.757,0.624c0.155,0.146,0.302,0.301,0.448,0.459c0.218,0.236,0.422,0.484,0.612,0.742
+		c0.126,0.173,0.251,0.347,0.366,0.53c0.177,0.278,0.33,0.571,0.473,0.868c0.09,0.187,0.186,0.369,0.263,0.563
+		c0.134,0.338,0.235,0.689,0.325,1.047c0.046,0.173,0.105,0.341,0.141,0.521c0.105,0.535,0.17,1.094,0.17,1.665
+		C158.418,168.582,158.353,169.113,158.256,169.635z M148.381,169.279L148.381,169.279c-0.166-0.164-0.291-0.355-0.379-0.562
+		c-0.088-0.21-0.137-0.44-0.137-0.682c0-0.969,0.788-1.758,1.759-1.758s1.759,0.789,1.759,1.758c0,0.241-0.049,0.472-0.137,0.682
+		c-0.088,0.207-0.213,0.398-0.378,0.562l0,0c-0.318,0.318-0.759,0.516-1.243,0.516C149.14,169.795,148.698,169.597,148.381,169.279z
+		 M140.998,166.364c0.035-0.179,0.097-0.347,0.141-0.521c0.092-0.355,0.191-0.707,0.325-1.045c0.077-0.193,0.175-0.377,0.265-0.564
+		c0.144-0.297,0.297-0.592,0.47-0.869c0.114-0.18,0.239-0.355,0.368-0.529c0.192-0.259,0.395-0.506,0.612-0.742
+		c0.146-0.156,0.292-0.312,0.448-0.459c0.237-0.223,0.494-0.427,0.757-0.623c0.165-0.123,0.325-0.251,0.499-0.364
+		c0.299-0.192,0.615-0.354,0.938-0.51c0.162-0.079,0.315-0.171,0.482-0.24c0.469-0.193,0.958-0.346,1.465-0.456
+		c0.031-0.007,0.06-0.021,0.092-0.028c0.001,0,0.003,0,0.005,0v3.641c-0.06,0.021-0.112,0.053-0.171,0.076
+		c-0.098,0.038-0.191,0.082-0.286,0.126c-0.198,0.093-0.386,0.193-0.566,0.308c-0.087,0.055-0.173,0.105-0.255,0.164
+		c-0.209,0.147-0.405,0.311-0.59,0.487c-0.038,0.036-0.082,0.067-0.119,0.105c-0.217,0.218-0.409,0.456-0.586,0.709
+		c-0.05,0.071-0.088,0.146-0.134,0.22c-0.12,0.191-0.229,0.388-0.323,0.596c-0.044,0.097-0.084,0.193-0.121,0.292
+		c-0.079,0.206-0.145,0.417-0.198,0.636c-0.022,0.096-0.051,0.189-0.069,0.288c-0.061,0.317-0.099,0.64-0.099,0.972
+		c0,0.385,0.046,0.756,0.123,1.115c0.028,0.134,0.082,0.259,0.121,0.389c0.067,0.222,0.134,0.443,0.227,0.652
+		c0.018,0.039,0.023,0.082,0.043,0.119l-2.567,2.566c-0.277-0.418-0.519-0.863-0.722-1.329c-0.043-0.099-0.075-0.2-0.116-0.301
+		c-0.153-0.389-0.278-0.793-0.377-1.206c-0.031-0.136-0.064-0.27-0.089-0.406c-0.095-0.521-0.16-1.053-0.16-1.6
+		C140.83,167.464,140.893,166.906,140.998,166.364z M144.781,175.364l2.58-2.582c0.689,0.329,1.449,0.53,2.262,0.53
+		s1.572-0.2,2.261-0.53l2.58,2.582c-1.39,0.923-3.052,1.466-4.841,1.466C147.835,176.83,146.173,176.286,144.781,175.364z"/>
+	<path fill="#43515F" d="M156.658,124.064c-2.908,0-5.276,2.367-5.276,5.276c0,0.219,0.039,0.426,0.065,0.639l-8.724,4.362
+		c-0.949-0.913-2.233-1.483-3.653-1.483c-1.921,0-3.588,1.043-4.512,2.581l-7.875-1.576c-0.369-2.55-2.549-4.521-5.201-4.521
+		c-2.909,0-5.276,2.367-5.276,5.276s2.367,5.277,5.276,5.277c1.921,0,3.588-1.043,4.511-2.581l7.876,1.576
+		c0.369,2.55,2.549,4.521,5.201,4.521c2.908,0,5.276-2.367,5.276-5.276c0-0.218-0.038-0.427-0.064-0.64l8.724-4.362
+		c0.949,0.914,2.233,1.484,3.652,1.484c2.909,0,5.277-2.368,5.277-5.277S159.568,124.064,156.658,124.064z M121.482,136.376
+		c-0.971,0-1.759-0.79-1.759-1.759c0-0.969,0.788-1.759,1.759-1.759s1.759,0.79,1.759,1.759S122.453,136.376,121.482,136.376z
+		 M139.071,139.894c-0.972,0-1.76-0.79-1.76-1.759c0-0.969,0.788-1.759,1.76-1.759s1.759,0.79,1.759,1.759
+		C140.83,139.104,140.042,139.894,139.071,139.894z M156.658,131.1c-0.971,0-1.758-0.788-1.758-1.759l0,0
+		c0-0.969,0.787-1.759,1.758-1.759s1.76,0.79,1.76,1.759S157.63,131.1,156.658,131.1z"/>
+</g>
+</svg>
diff --git a/src/app/+admin/admin-registries/metadata-schema/metadata-field-form/metadata-field-form.component.spec.ts b/src/app/+admin/admin-registries/metadata-schema/metadata-field-form/metadata-field-form.component.spec.ts
index 45df65ebe55cf70e2f69aa1e1c38ced1ac75dbb4..3c4f858c5584fd594f976b9c0f61b7c410ea6e49 100644
--- a/src/app/+admin/admin-registries/metadata-schema/metadata-field-form/metadata-field-form.component.spec.ts
+++ b/src/app/+admin/admin-registries/metadata-schema/metadata-field-form/metadata-field-form.component.spec.ts
@@ -28,6 +28,7 @@ describe('MetadataFieldFormComponent', () => {
   const registryServiceStub = {
     getActiveMetadataField: () => observableOf(undefined),
     createOrUpdateMetadataField: (field: MetadataField) => observableOf(field),
+    cancelEditMetadataField: () => {},
     cancelEditMetadataSchema: () => {},
   };
   const formBuilderServiceStub = {
@@ -62,6 +63,11 @@ describe('MetadataFieldFormComponent', () => {
     registryService = s;
   }));
 
+  afterEach(() => {
+    component = null;
+    registryService = null
+  })
+
   describe('when submitting the form', () => {
     const element = 'fakeElement';
     const qualifier = 'fakeQualifier';
diff --git a/src/app/+admin/admin-sidebar/admin-sidebar.component.ts b/src/app/+admin/admin-sidebar/admin-sidebar.component.ts
index f148627297ad5765c7323e6eae207cfd23d1cc4a..3ad1bd4272dc1eabc6190b6ce664fbda083a247f 100644
--- a/src/app/+admin/admin-sidebar/admin-sidebar.component.ts
+++ b/src/app/+admin/admin-sidebar/admin-sidebar.component.ts
@@ -138,13 +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.ONCLICK,
+          type: MenuItemType.LINK,
           text: 'menu.section.new_item',
-          function: () => {
-            this.modalService.open(CreateItemParentSelectorComponent);
-          }
-        } as OnClickMenuItemModel,
+          link: '/submit'
+        } as LinkMenuItemModel,
       },
       {
         id: 'new_item_version',
@@ -154,7 +159,7 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit {
         model: {
           type: MenuItemType.LINK,
           text: 'menu.section.new_item_version',
-          link: '#'
+          link: ''
         } as LinkMenuItemModel,
       },
 
@@ -230,7 +235,7 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit {
         model: {
           type: MenuItemType.LINK,
           text: 'menu.section.import_metadata',
-          link: '#'
+          link: ''
         } as LinkMenuItemModel,
       },
       {
@@ -241,7 +246,7 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit {
         model: {
           type: MenuItemType.LINK,
           text: 'menu.section.import_batch',
-          link: '#'
+          link: ''
         } as LinkMenuItemModel,
       },
       /* Export */
@@ -264,7 +269,7 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit {
         model: {
           type: MenuItemType.LINK,
           text: 'menu.section.export_community',
-          link: '#'
+          link: ''
         } as LinkMenuItemModel,
       },
       {
@@ -275,7 +280,7 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit {
         model: {
           type: MenuItemType.LINK,
           text: 'menu.section.export_collection',
-          link: '#'
+          link: ''
         } as LinkMenuItemModel,
       },
       {
@@ -286,7 +291,7 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit {
         model: {
           type: MenuItemType.LINK,
           text: 'menu.section.export_item',
-          link: '#'
+          link: ''
         } as LinkMenuItemModel,
       }, {
         id: 'export_metadata',
@@ -296,7 +301,7 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit {
         model: {
           type: MenuItemType.LINK,
           text: 'menu.section.export_metadata',
-          link: '#'
+          link: ''
         } as LinkMenuItemModel,
       },
 
@@ -320,7 +325,7 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit {
         model: {
           type: MenuItemType.LINK,
           text: 'menu.section.access_control_people',
-          link: '#'
+          link: ''
         } as LinkMenuItemModel,
       },
       {
@@ -331,7 +336,7 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit {
         model: {
           type: MenuItemType.LINK,
           text: 'menu.section.access_control_groups',
-          link: '#'
+          link: ''
         } as LinkMenuItemModel,
       },
       {
@@ -342,7 +347,7 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit {
         model: {
           type: MenuItemType.LINK,
           text: 'menu.section.access_control_authorizations',
-          link: '#'
+          link: ''
         } as LinkMenuItemModel,
       },
 
@@ -377,7 +382,7 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit {
         model: {
           type: MenuItemType.LINK,
           text: 'menu.section.find_withdrawn_items',
-          link: '#'
+          link: ''
         } as LinkMenuItemModel,
       },
       {
@@ -388,7 +393,7 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit {
         model: {
           type: MenuItemType.LINK,
           text: 'menu.section.find_private_items',
-          link: '/admin/items'
+          link: ''
         } as LinkMenuItemModel,
       },
 
@@ -435,7 +440,7 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit {
         model: {
           type: MenuItemType.LINK,
           text: 'menu.section.curation_task',
-          link: '/curation'
+          link: ''
         } as LinkMenuItemModel,
         icon: 'filter',
         index: 7
@@ -449,7 +454,7 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit {
         model: {
           type: MenuItemType.LINK,
           text: 'menu.section.statistics_task',
-          link: '#'
+          link: ''
         } as LinkMenuItemModel,
         icon: 'chart-bar',
         index: 8
@@ -463,7 +468,7 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit {
         model: {
           type: MenuItemType.LINK,
           text: 'menu.section.control_panel',
-          link: '#'
+          link: ''
         } as LinkMenuItemModel,
         icon: 'cogs',
         index: 9
diff --git a/src/app/+admin/admin-sidebar/expandable-admin-sidebar-section/expandable-admin-sidebar-section.component.ts b/src/app/+admin/admin-sidebar/expandable-admin-sidebar-section/expandable-admin-sidebar-section.component.ts
index 4921be77e227ac808a7a8655e18dfbdda05a8fff..112560de16e3c8a9b89e0dea4221560f0eb6903a 100644
--- a/src/app/+admin/admin-sidebar/expandable-admin-sidebar-section/expandable-admin-sidebar-section.component.ts
+++ b/src/app/+admin/admin-sidebar/expandable-admin-sidebar-section/expandable-admin-sidebar-section.component.ts
@@ -18,8 +18,8 @@ import { rendersSectionForMenu } from '../../../shared/menu/menu-section.decorat
   templateUrl: './expandable-admin-sidebar-section.component.html',
   styleUrls: ['./expandable-admin-sidebar-section.component.scss'],
   animations: [rotate, slide, bgColor]
-
 })
+
 @rendersSectionForMenu(MenuID.ADMIN, true)
 export class ExpandableAdminSidebarSectionComponent extends AdminSidebarSectionComponent implements OnInit {
   /**
diff --git a/src/app/+collection-page/collection-page.component.html b/src/app/+collection-page/collection-page.component.html
index 6265b223d887dcf4244e9734b4acc6ef756fa3d6..91239de17c64de4fa8a0b4351b0503c557645f64 100644
--- a/src/app/+collection-page/collection-page.component.html
+++ b/src/app/+collection-page/collection-page.component.html
@@ -1,58 +1,62 @@
 <div class="container">
-  <div class="collection-page"
-       *ngVar="(collectionRD$ | async) as collectionRD">
-    <div *ngIf="collectionRD?.hasSucceeded" @fadeInOut>
-      <div *ngIf="collectionRD?.payload as collection">
-        <!-- Collection Name -->
-        <ds-comcol-page-header
-          [name]="collection.name">
-        </ds-comcol-page-header>
-        <!-- Browse-By Links -->
-        <ds-comcol-page-browse-by [id]="collection.id"></ds-comcol-page-browse-by>
-        <!-- Collection logo -->
-        <ds-comcol-page-logo *ngIf="logoRD$"
-                             [logo]="(logoRD$ | async)?.payload"
-                             [alternateText]="'Collection Logo'">
-        </ds-comcol-page-logo>
-        <!-- Introductionary text -->
-        <ds-comcol-page-content
-          [content]="collection.introductoryText"
-          [hasInnerHtml]="true">
-        </ds-comcol-page-content>
-        <!-- News -->
-        <ds-comcol-page-content
-          [content]="collection.sidebarText"
-          [hasInnerHtml]="true"
-          [title]="'community.page.news'">
-        </ds-comcol-page-content>
-        <!-- Copyright -->
-        <ds-comcol-page-content
-          [content]="collection.copyrightText"
-          [hasInnerHtml]="true">
-        </ds-comcol-page-content>
-        <!-- Licence -->
-        <ds-comcol-page-content
-          [content]="collection.dcLicense"
-          [title]="'collection.page.license'">
-        </ds-comcol-page-content>
-      </div>
+    <div class="collection-page"
+         *ngVar="(collectionRD$ | async) as collectionRD">
+        <div *ngIf="collectionRD?.hasSucceeded" @fadeInOut>
+            <div *ngIf="collectionRD?.payload as collection">
+                <!-- Collection Name -->
+                <ds-comcol-page-header
+                        [name]="collection.name">
+                </ds-comcol-page-header>
+                <!-- Browse-By Links -->
+                <ds-comcol-page-browse-by [id]="collection.id"></ds-comcol-page-browse-by>
+                <!-- Collection logo -->
+                <ds-comcol-page-logo *ngIf="logoRD$"
+                                     [logo]="(logoRD$ | async)?.payload"
+                                     [alternateText]="'Collection Logo'">
+                </ds-comcol-page-logo>
+                <!-- Introductionary text -->
+                <ds-comcol-page-content
+                        [content]="collection.introductoryText"
+                        [hasInnerHtml]="true">
+                </ds-comcol-page-content>
+                <!-- News -->
+                <ds-comcol-page-content
+                        [content]="collection.sidebarText"
+                        [hasInnerHtml]="true"
+                        [title]="'community.page.news'">
+                </ds-comcol-page-content>
+                <!-- Copyright -->
+                <ds-comcol-page-content
+                        [content]="collection.copyrightText"
+                        [hasInnerHtml]="true">
+                </ds-comcol-page-content>
+                <!-- Licence -->
+                <ds-comcol-page-content
+                        [content]="collection.dcLicense"
+                        [title]="'collection.page.license'">
+                </ds-comcol-page-content>
+            </div>
+            <br>
+            <ng-container *ngVar="(itemRD$ | async) as itemRD">
+                <div *ngIf="itemRD?.hasSucceeded" @fadeIn>
+                    <h2>{{'collection.page.browse.recent.head' | translate}}</h2>
+                    <ds-viewable-collection
+                            [config]="paginationConfig"
+                            [sortConfig]="sortConfig"
+                            [objects]="itemRD"
+                            [hideGear]="true"
+                            (paginationChange)="onPaginationChange($event)">
+                    </ds-viewable-collection>
+                </div>
+                <ds-error *ngIf="itemRD?.hasFailed"
+                          message="{{'error.recent-submissions' | translate}}"></ds-error>
+                <ds-loading *ngIf="!itemRD || itemRD.isLoading"
+                            message="{{'loading.recent-submissions' | translate}}"></ds-loading>
+            </ng-container>
+        </div>
+        <ds-error *ngIf="collectionRD?.hasFailed"
+                  message="{{'error.collection' | translate}}"></ds-error>
+        <ds-loading *ngIf="collectionRD?.isLoading"
+                    message="{{'loading.collection' | translate}}"></ds-loading>
     </div>
-    <ds-error *ngIf="collectionRD?.hasFailed" message="{{'error.collection' | translate}}"></ds-error>
-    <ds-loading *ngIf="collectionRD?.isLoading" message="{{'loading.collection' | translate}}"></ds-loading>
-    <br>
-    <ng-container *ngVar="(itemRD$ | async) as itemRD">
-      <div *ngIf="itemRD?.hasSucceeded" @fadeIn>
-        <h2>{{'collection.page.browse.recent.head' | translate}}</h2>
-        <ds-viewable-collection
-          [config]="paginationConfig"
-          [sortConfig]="sortConfig"
-          [objects]="itemRD"
-          [hideGear]="true"
-          (paginationChange)="onPaginationChange($event)">
-        </ds-viewable-collection>
-      </div>
-      <ds-error *ngIf="itemRD?.hasFailed" message="{{'error.recent-submissions' | translate}}"></ds-error>
-      <ds-loading *ngIf="!itemRD || itemRD.isLoading" message="{{'loading.recent-submissions' | translate}}"></ds-loading>
-    </ng-container>
-  </div>
 </div>
diff --git a/src/app/+collection-page/collection-page.component.ts b/src/app/+collection-page/collection-page.component.ts
index 7c4f2b92ac880fface1cb1552f387f2d8999cb56..41afbf2115b6211bc9fe1002b45f3832ab867247 100644
--- a/src/app/+collection-page/collection-page.component.ts
+++ b/src/app/+collection-page/collection-page.component.ts
@@ -1,6 +1,9 @@
-import { ChangeDetectionStrategy, Component, OnDestroy, OnInit } from '@angular/core';
-import { ActivatedRoute } from '@angular/router';
-import { Observable, Subscription } from 'rxjs';
+import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
+import { ActivatedRoute, Router } from '@angular/router';
+import { BehaviorSubject, of as observableOf, Observable, Subject } from 'rxjs';
+import { filter, flatMap, map, startWith, switchMap, take, tap } from 'rxjs/operators';
+import { PaginatedSearchOptions } from '../+search-page/paginated-search-options.model';
+import { SearchService } from '../+search-page/search-service/search.service';
 import { SortDirection, SortOptions } from '../core/cache/models/sort-options.model';
 import { CollectionDataService } from '../core/data/collection-data.service';
 import { PaginatedList } from '../core/data/paginated-list';
@@ -10,16 +13,17 @@ import { MetadataService } from '../core/metadata/metadata.service';
 import { Bitstream } from '../core/shared/bitstream.model';
 
 import { Collection } from '../core/shared/collection.model';
+import { DSpaceObjectType } from '../core/shared/dspace-object-type.model';
 import { Item } from '../core/shared/item.model';
+import {
+  getSucceededRemoteData,
+  redirectToPageNotFoundOn404,
+  toDSpaceObjectListRD
+} from '../core/shared/operators';
 
 import { fadeIn, fadeInOut } from '../shared/animations/fade';
-import { hasValue, isNotEmpty } from '../shared/empty.util';
+import { hasNoValue, hasValue, isNotEmpty } from '../shared/empty.util';
 import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model';
-import { filter, flatMap, map, tap } from 'rxjs/operators';
-import { SearchService } from '../+search-page/search-service/search.service';
-import { PaginatedSearchOptions } from '../+search-page/paginated-search-options.model';
-import { toDSpaceObjectListRD } from '../core/shared/operators';
-import { DSpaceObjectType } from '../core/shared/dspace-object-type.model';
 
 @Component({
   selector: 'ds-collection-page',
@@ -31,20 +35,23 @@ import { DSpaceObjectType } from '../core/shared/dspace-object-type.model';
     fadeInOut
   ]
 })
-export class CollectionPageComponent implements OnInit, OnDestroy {
+export class CollectionPageComponent implements OnInit {
   collectionRD$: Observable<RemoteData<Collection>>;
   itemRD$: Observable<RemoteData<PaginatedList<Item>>>;
   logoRD$: Observable<RemoteData<Bitstream>>;
   paginationConfig: PaginationComponentOptions;
   sortConfig: SortOptions;
-  private subs: Subscription[] = [];
-  private collectionId: string;
+  private paginationChanges$: Subject<{
+    paginationConfig: PaginationComponentOptions,
+    sortConfig: SortOptions
+  }>;
 
   constructor(
     private collectionDataService: CollectionDataService,
     private searchService: SearchService,
     private metadata: MetadataService,
-    private route: ActivatedRoute
+    private route: ActivatedRoute,
+    private router: Router
   ) {
     this.paginationConfig = new PaginationComponentOptions();
     this.paginationConfig.id = 'collection-page-pagination';
@@ -55,43 +62,43 @@ export class CollectionPageComponent implements OnInit, OnDestroy {
 
   ngOnInit(): void {
     this.collectionRD$ = this.route.data.pipe(
-      map((data) => data.collection),
-      tap((data) => this.collectionId = data.payload.id)
+      map((data) => data.collection as RemoteData<Collection>),
+      redirectToPageNotFoundOn404(this.router),
+      take(1)
     );
     this.logoRD$ = this.collectionRD$.pipe(
       map((rd: RemoteData<Collection>) => rd.payload),
       filter((collection: Collection) => hasValue(collection)),
       flatMap((collection: Collection) => collection.logo)
     );
-    this.subs.push(
-      this.route.queryParams.subscribe((params) => {
-        this.metadata.processRemoteData(this.collectionRD$);
-        const page = +params.page || this.paginationConfig.currentPage;
-        const pageSize = +params.pageSize || this.paginationConfig.pageSize;
-        const pagination = Object.assign({},
-          this.paginationConfig,
-          { currentPage: page, pageSize: pageSize }
-        );
-        this.updatePage({
-          pagination: pagination,
-          sort: this.sortConfig
-        });
-      }));
 
-  }
+    this.paginationChanges$ = new BehaviorSubject({
+      paginationConfig: this.paginationConfig,
+      sortConfig: this.sortConfig
+    });
 
-  updatePage(searchOptions) {
-    this.itemRD$ = this.searchService.search(
-      new PaginatedSearchOptions({
-        scope: this.collectionId,
-        pagination: searchOptions.pagination,
-        sort: searchOptions.sort,
-        dsoType: DSpaceObjectType.ITEM
-      })).pipe(toDSpaceObjectListRD()) as Observable<RemoteData<PaginatedList<Item>>>;
-  }
+    this.itemRD$ = this.paginationChanges$.pipe(
+      switchMap((dto) => this.collectionRD$.pipe(
+        getSucceededRemoteData(),
+        map((rd) => rd.payload.id),
+        switchMap((id: string) => {
+          return this.searchService.search(
+              new PaginatedSearchOptions({
+                scope: id,
+                pagination: dto.paginationConfig,
+                sort: dto.sortConfig,
+                dsoType: DSpaceObjectType.ITEM
+              })).pipe(toDSpaceObjectListRD()) as Observable<RemoteData<PaginatedList<Item>>>
+        }),
+        startWith(undefined) // Make sure switching pages shows loading component
+        )
+      )
+    );
 
-  ngOnDestroy(): void {
-    this.subs.filter((sub) => hasValue(sub)).forEach((sub) => sub.unsubscribe());
+    this.route.queryParams.pipe(take(1)).subscribe((params) => {
+      this.metadata.processRemoteData(this.collectionRD$);
+      this.onPaginationChange(params);
+    })
   }
 
   isNotEmpty(object: any) {
@@ -99,15 +106,14 @@ export class CollectionPageComponent implements OnInit, OnDestroy {
   }
 
   onPaginationChange(event) {
-    this.updatePage({
-      pagination: {
-        currentPage: event.page,
-        pageSize: event.pageSize
-      },
-      sort: {
-        field: event.sortField,
-        direction: event.sortDirection
-      }
-    })
+    this.paginationConfig.currentPage = +event.page || this.paginationConfig.currentPage;
+    this.paginationConfig.pageSize = +event.pageSize || this.paginationConfig.pageSize;
+    this.sortConfig.direction = event.sortDirection || this.sortConfig.direction;
+    this.sortConfig.field = event.sortField || this.sortConfig.field;
+
+    this.paginationChanges$.next({
+      paginationConfig: this.paginationConfig,
+      sortConfig: this.sortConfig
+    });
   }
 }
diff --git a/src/app/+collection-page/collection-page.module.ts b/src/app/+collection-page/collection-page.module.ts
index f0e4138d2dfd180515e8182ec4f7ac6094b4a9a0..bdeffa34f38bf23cbbc0d232559ed9148fbb1738 100644
--- a/src/app/+collection-page/collection-page.module.ts
+++ b/src/app/+collection-page/collection-page.module.ts
@@ -7,9 +7,9 @@ import { CollectionPageComponent } from './collection-page.component';
 import { CollectionPageRoutingModule } from './collection-page-routing.module';
 import { CreateCollectionPageComponent } from './create-collection-page/create-collection-page.component';
 import { CollectionFormComponent } from './collection-form/collection-form.component';
-import { SearchPageModule } from '../+search-page/search-page.module';
 import { EditCollectionPageComponent } from './edit-collection-page/edit-collection-page.component';
 import { DeleteCollectionPageComponent } from './delete-collection-page/delete-collection-page.component';
+import { SearchService } from '../+search-page/search-service/search.service';
 
 @NgModule({
   imports: [
@@ -23,6 +23,9 @@ import { DeleteCollectionPageComponent } from './delete-collection-page/delete-c
     EditCollectionPageComponent,
     DeleteCollectionPageComponent,
     CollectionFormComponent
+  ],
+  providers: [
+    SearchService
   ]
 })
 export class CollectionPageModule {
diff --git a/src/app/+collection-page/collection-page.resolver.ts b/src/app/+collection-page/collection-page.resolver.ts
index d4835e2e14359e3054158dc37e703aa3e815e3f9..8c6e3ad8a65d861304a399690488589ddc49989b 100644
--- a/src/app/+collection-page/collection-page.resolver.ts
+++ b/src/app/+collection-page/collection-page.resolver.ts
@@ -4,7 +4,8 @@ import { Collection } from '../core/shared/collection.model';
 import { Observable } from 'rxjs';
 import { CollectionDataService } from '../core/data/collection-data.service';
 import { RemoteData } from '../core/data/remote-data';
-import { getSucceededRemoteData } from '../core/shared/operators';
+import { find } from 'rxjs/operators';
+import { hasValue } from '../shared/empty.util';
 
 /**
  * This class represents a resolver that requests a specific collection before the route is activated
@@ -18,11 +19,12 @@ export class CollectionPageResolver implements Resolve<RemoteData<Collection>> {
    * Method for resolving a collection based on the parameters in the current route
    * @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot
    * @param {RouterStateSnapshot} state The current RouterStateSnapshot
-   * @returns Observable<<RemoteData<Collection>> Emits the found collection based on the parameters in the current route
+   * @returns Observable<<RemoteData<Collection>> Emits the found collection based on the parameters in the current route,
+   * or an error if something went wrong
    */
   resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<RemoteData<Collection>> {
     return this.collectionService.findById(route.params.id).pipe(
-      getSucceededRemoteData()
+      find((RD) => hasValue(RD.error) || RD.hasSucceeded),
     );
   }
 }
diff --git a/src/app/+collection-page/edit-collection-page/edit-collection-page.component.ts b/src/app/+collection-page/edit-collection-page/edit-collection-page.component.ts
index a3978a5e43ef49f41bd9ef7881277bf41f4b0c6b..ba70bd26c6ba5566dafa5114805d0dc7c8752c50 100644
--- a/src/app/+collection-page/edit-collection-page/edit-collection-page.component.ts
+++ b/src/app/+collection-page/edit-collection-page/edit-collection-page.component.ts
@@ -1,7 +1,6 @@
 import { Component } from '@angular/core';
 import { ActivatedRoute, Router } from '@angular/router';
 import { EditComColPageComponent } from '../../shared/comcol-forms/edit-comcol-page/edit-comcol-page.component';
-import { NormalizedCollection } from '../../core/cache/models/normalized-collection.model';
 import { Collection } from '../../core/shared/collection.model';
 import { CollectionDataService } from '../../core/data/collection-data.service';
 
diff --git a/src/app/+community-page/community-page.component.ts b/src/app/+community-page/community-page.component.ts
index 2035faf988b32ac7f131a493c49da427865709c9..f337d70250aca257c0693959b2e8037e1eec8991 100644
--- a/src/app/+community-page/community-page.component.ts
+++ b/src/app/+community-page/community-page.component.ts
@@ -1,6 +1,6 @@
 import { mergeMap, filter, map } from 'rxjs/operators';
 import { ChangeDetectionStrategy, Component, OnDestroy, OnInit } from '@angular/core';
-import { ActivatedRoute } from '@angular/router';
+import { ActivatedRoute, Router } from '@angular/router';
 
 import { Subscription, Observable } from 'rxjs';
 import { CommunityDataService } from '../core/data/community-data.service';
@@ -13,6 +13,7 @@ import { MetadataService } from '../core/metadata/metadata.service';
 
 import { fadeInOut } from '../shared/animations/fade';
 import { hasValue } from '../shared/empty.util';
+import { redirectToPageNotFoundOn404 } from '../core/shared/operators';
 
 @Component({
   selector: 'ds-community-page',
@@ -37,13 +38,17 @@ export class CommunityPageComponent implements OnInit {
   constructor(
     private communityDataService: CommunityDataService,
     private metadata: MetadataService,
-    private route: ActivatedRoute
+    private route: ActivatedRoute,
+    private router: Router
   ) {
 
   }
 
   ngOnInit(): void {
-    this.communityRD$ = this.route.data.pipe(map((data) => data.community));
+    this.communityRD$ = this.route.data.pipe(
+      map((data) => data.community as RemoteData<Community>),
+      redirectToPageNotFoundOn404(this.router)
+    );
     this.logoRD$ = this.communityRD$.pipe(
       map((rd: RemoteData<Community>) => rd.payload),
       filter((community: Community) => hasValue(community)),
diff --git a/src/app/+community-page/community-page.resolver.ts b/src/app/+community-page/community-page.resolver.ts
index a32fe78bc5de6c90095e2756f895f30ab6548e5b..ffa66fa1235b68a28e3fcedd9de2d4d31e250d6c 100644
--- a/src/app/+community-page/community-page.resolver.ts
+++ b/src/app/+community-page/community-page.resolver.ts
@@ -2,9 +2,10 @@ import { Injectable } from '@angular/core';
 import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
 import { Observable } from 'rxjs';
 import { RemoteData } from '../core/data/remote-data';
-import { getSucceededRemoteData } from '../core/shared/operators';
 import { Community } from '../core/shared/community.model';
 import { CommunityDataService } from '../core/data/community-data.service';
+import { find } from 'rxjs/operators';
+import { hasValue } from '../shared/empty.util';
 
 /**
  * This class represents a resolver that requests a specific community before the route is activated
@@ -18,11 +19,12 @@ export class CommunityPageResolver implements Resolve<RemoteData<Community>> {
    * Method for resolving a community based on the parameters in the current route
    * @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot
    * @param {RouterStateSnapshot} state The current RouterStateSnapshot
-   * @returns Observable<<RemoteData<Community>> Emits the found community based on the parameters in the current route
+   * @returns Observable<<RemoteData<Community>> Emits the found community based on the parameters in the current route,
+   * or an error if something went wrong
    */
   resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<RemoteData<Community>> {
     return this.communityService.findById(route.params.id).pipe(
-      getSucceededRemoteData()
+      find((RD) => hasValue(RD.error) || RD.hasSucceeded)
     );
   }
 }
diff --git a/src/app/+home-page/home-news/home-news.component.html b/src/app/+home-page/home-news/home-news.component.html
index 47ceaac90fd3d5fe022a8a793af68112fc885a4a..28e10c5804abd03eb8102e5ecc727c9967227393 100644
--- a/src/app/+home-page/home-news/home-news.component.html
+++ b/src/app/+home-page/home-news/home-news.component.html
@@ -3,7 +3,7 @@
     <div class="d-flex flex-wrap">
       <img class="mr-4 dspace-logo" src="assets/images/dspace-logo.svg" alt="" />
       <div>
-        <h1 class="display-3">Welcome to the DSpace 7 Preview Release</h1>
+        <h1 class="display-3">Welcome to the DSpace 7 Preview</h1>
         <p class="lead">DSpace is the world leading open source repository platform that enables organisations to:</p>
       </div>
     </div>
diff --git a/src/app/+home-page/home-page.component.html b/src/app/+home-page/home-page.component.html
index 6a3e20ca9dade4c04d8636063cb6507113720556..39ba4790336675753fef8ca092d85242b339da5f 100644
--- a/src/app/+home-page/home-page.component.html
+++ b/src/app/+home-page/home-page.component.html
@@ -1,5 +1,5 @@
 <ds-home-news></ds-home-news>
 <div class="container">
-  <ds-search-form></ds-search-form>
+  <ds-search-form [inPlaceSearch]="false"></ds-search-form>
   <ds-top-level-community-list></ds-top-level-community-list>
 </div>
diff --git a/src/app/+item-page/edit-item-page/edit-item-page.component.ts b/src/app/+item-page/edit-item-page/edit-item-page.component.ts
index 4ea47f08e73a48a1b05e1ab03f577374e754936f..eafc04ae0b491da4dbc070461974dfb2f80e63c7 100644
--- a/src/app/+item-page/edit-item-page/edit-item-page.component.ts
+++ b/src/app/+item-page/edit-item-page/edit-item-page.component.ts
@@ -1,6 +1,6 @@
 import { fadeIn, fadeInOut } from '../../shared/animations/fade';
 import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
-import { ActivatedRoute, Params, Router } from '@angular/router';
+import { ActivatedRoute, Router } from '@angular/router';
 import { RemoteData } from '../../core/data/remote-data';
 import { Item } from '../../core/shared/item.model';
 import { Observable } from 'rxjs';
diff --git a/src/app/+item-page/field-components/metadata-field-wrapper/metadata-field-wrapper.component.html b/src/app/+item-page/field-components/metadata-field-wrapper/metadata-field-wrapper.component.html
index bbe6d8d95b114d7c67fa70920e023f1fa866350a..c791cec600a8e4748929706693815f8b6c764852 100644
--- a/src/app/+item-page/field-components/metadata-field-wrapper/metadata-field-wrapper.component.html
+++ b/src/app/+item-page/field-components/metadata-field-wrapper/metadata-field-wrapper.component.html
@@ -1,4 +1,4 @@
-<div class="simple-view-element" [class.d-none]="content.textContent.trim().length === 0">
+<div class="simple-view-element" [class.d-none]="content.textContent.trim().length === 0 && hasNoValue(content.querySelector('img'))">
   <h5 class="simple-view-element-header" *ngIf="label">{{ label }}</h5>
   <div #content class="simple-view-element-body">
     <ng-content></ng-content>
diff --git a/src/app/+item-page/field-components/metadata-field-wrapper/metadata-field-wrapper.component.spec.ts b/src/app/+item-page/field-components/metadata-field-wrapper/metadata-field-wrapper.component.spec.ts
index cce54edf64e9944e5f760a3718de24b26296b878..d7e1b80c766064edc44366b6163d8c0a21395161 100644
--- a/src/app/+item-page/field-components/metadata-field-wrapper/metadata-field-wrapper.component.spec.ts
+++ b/src/app/+item-page/field-components/metadata-field-wrapper/metadata-field-wrapper.component.spec.ts
@@ -1,18 +1,41 @@
-import { ComponentFixture, TestBed, async } from '@angular/core/testing';
-import { By } from '@angular/platform-browser';
-import { Component, DebugElement } from '@angular/core';
+import { Component } from '@angular/core';
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 
 import { MetadataFieldWrapperComponent } from './metadata-field-wrapper.component';
 
+/* tslint:disable:max-classes-per-file */
 @Component({
-    selector: 'ds-component-with-content',
+    selector: 'ds-component-without-content',
     template: '<ds-metadata-field-wrapper [label]="\'test label\'">\n' +
-      '    <div class="my-content">\n' +
+      '</ds-metadata-field-wrapper>'
+})
+class NoContentComponent {}
+
+@Component({
+    selector: 'ds-component-with-empty-spans',
+    template: '<ds-metadata-field-wrapper [label]="\'test label\'">\n' +
+      '    <span></span>\n' +
       '    <span></span>\n' +
-      '    </div>\n' +
       '</ds-metadata-field-wrapper>'
 })
-class ContentComponent {}
+class SpanContentComponent {}
+
+@Component({
+    selector: 'ds-component-with-text',
+    template: '<ds-metadata-field-wrapper [label]="\'test label\'">\n' +
+      '    <span>The quick brown fox jumps over the lazy dog</span>\n' +
+      '</ds-metadata-field-wrapper>'
+})
+class TextContentComponent {}
+
+@Component({
+    selector: 'ds-component-with-image',
+    template: '<ds-metadata-field-wrapper [label]="\'test label\'">\n' +
+      '    <img src="https://some/image.png" alt="an alt text">\n' +
+      '</ds-metadata-field-wrapper>'
+})
+class ImgContentComponent {}
+/* tslint:enable:max-classes-per-file */
 
 describe('MetadataFieldWrapperComponent', () => {
   let component: MetadataFieldWrapperComponent;
@@ -20,7 +43,7 @@ describe('MetadataFieldWrapperComponent', () => {
 
   beforeEach(async(() => {
     TestBed.configureTestingModule({
-      declarations: [MetadataFieldWrapperComponent, ContentComponent]
+      declarations: [MetadataFieldWrapperComponent, NoContentComponent, SpanContentComponent, TextContentComponent, ImgContentComponent]
     }).compileComponents();
   }));
 
@@ -30,23 +53,21 @@ describe('MetadataFieldWrapperComponent', () => {
   });
 
   const wrapperSelector = '.simple-view-element';
-  const labelSelector = '.simple-view-element-header';
-  const contentSelector = '.my-content';
 
   it('should create', () => {
     expect(component).toBeDefined();
   });
 
   it('should not show the component when there is no content', () => {
-    component.label = 'test label';
-    fixture.detectChanges();
-    const parentNative = fixture.nativeElement;
+    const parentFixture = TestBed.createComponent(NoContentComponent);
+    parentFixture.detectChanges();
+    const parentNative = parentFixture.nativeElement;
     const nativeWrapper = parentNative.querySelector(wrapperSelector);
     expect(nativeWrapper.classList.contains('d-none')).toBe(true);
   });
 
-  it('should not show the component when there is DOM content but no text', () => {
-    const parentFixture = TestBed.createComponent(ContentComponent);
+  it('should not show the component when there is DOM content but not text or an image', () => {
+    const parentFixture = TestBed.createComponent(SpanContentComponent);
     parentFixture.detectChanges();
     const parentNative = parentFixture.nativeElement;
     const nativeWrapper = parentNative.querySelector(wrapperSelector);
@@ -54,11 +75,18 @@ describe('MetadataFieldWrapperComponent', () => {
   });
 
   it('should show the component when there is text content', () => {
-    const parentFixture = TestBed.createComponent(ContentComponent);
+    const parentFixture = TestBed.createComponent(TextContentComponent);
+    parentFixture.detectChanges();
+    const parentNative = parentFixture.nativeElement;
+    const nativeWrapper = parentNative.querySelector(wrapperSelector);
+    parentFixture.detectChanges();
+    expect(nativeWrapper.classList.contains('d-none')).toBe(false);
+  });
+
+  it('should show the component when there is img content', () => {
+    const parentFixture = TestBed.createComponent(ImgContentComponent);
     parentFixture.detectChanges();
     const parentNative = parentFixture.nativeElement;
-    const nativeContent = parentNative.querySelector(contentSelector);
-    nativeContent.textContent = 'lorem ipsum';
     const nativeWrapper = parentNative.querySelector(wrapperSelector);
     parentFixture.detectChanges();
     expect(nativeWrapper.classList.contains('d-none')).toBe(false);
diff --git a/src/app/+item-page/field-components/metadata-field-wrapper/metadata-field-wrapper.component.ts b/src/app/+item-page/field-components/metadata-field-wrapper/metadata-field-wrapper.component.ts
index 8c80384732c28816c8daa46f3e4f48f61c2fc328..8af108cceb80a4eb507c21447c61528d49fe0900 100644
--- a/src/app/+item-page/field-components/metadata-field-wrapper/metadata-field-wrapper.component.ts
+++ b/src/app/+item-page/field-components/metadata-field-wrapper/metadata-field-wrapper.component.ts
@@ -1,4 +1,5 @@
 import { Component, Input } from '@angular/core';
+import { hasNoValue } from '../../../shared/empty.util';
 
 /**
  * This component renders any content inside this wrapper.
@@ -11,6 +12,15 @@ import { Component, Input } from '@angular/core';
 })
 export class MetadataFieldWrapperComponent {
 
+  /**
+   * The label (title) for the content
+   */
   @Input() label: string;
 
+  /**
+   * Make hasNoValue() available in the template
+   */
+  hasNoValue(o: any): boolean {
+    return hasNoValue(o);
+  }
 }
diff --git a/src/app/+item-page/field-components/metadata-uri-values/metadata-uri-values.component.spec.ts b/src/app/+item-page/field-components/metadata-uri-values/metadata-uri-values.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2b32ece3c34e98178e76d4d6276cfdc40540b839
--- /dev/null
+++ b/src/app/+item-page/field-components/metadata-uri-values/metadata-uri-values.component.spec.ts
@@ -0,0 +1,97 @@
+import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
+import { ChangeDetectionStrategy, DebugElement, NO_ERRORS_SCHEMA } from '@angular/core';
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { MockTranslateLoader } from '../../../shared/mocks/mock-translate-loader';
+import { By } from '@angular/platform-browser';
+import { MetadataUriValuesComponent } from './metadata-uri-values.component';
+import { isNotEmpty } from '../../../shared/empty.util';
+import { MetadataValue } from '../../../core/shared/metadata.models';
+
+let comp: MetadataUriValuesComponent;
+let fixture: ComponentFixture<MetadataUriValuesComponent>;
+
+const mockMetadata = [
+  {
+    language: 'en_US',
+    value: 'http://fakelink.org'
+  },
+  {
+    language: 'en_US',
+    value: 'http://another.fakelink.org'
+  }
+] as MetadataValue[];
+const mockSeperator = '<br/>';
+const mockLabel = 'fake.message';
+const mockLinkText = 'fake link text';
+
+describe('MetadataUriValuesComponent', () => {
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [TranslateModule.forRoot({
+        loader: {
+          provide: TranslateLoader,
+          useClass: MockTranslateLoader
+        }
+      })],
+      declarations: [MetadataUriValuesComponent],
+      schemas: [NO_ERRORS_SCHEMA]
+    }).overrideComponent(MetadataUriValuesComponent, {
+      set: {changeDetection: ChangeDetectionStrategy.Default}
+    }).compileComponents();
+  }));
+
+  beforeEach(async(() => {
+    fixture = TestBed.createComponent(MetadataUriValuesComponent);
+    comp = fixture.componentInstance;
+    comp.mdValues = mockMetadata;
+    comp.separator = mockSeperator;
+    comp.label = mockLabel;
+    fixture.detectChanges();
+  }));
+
+  it('should display all metadata values', () => {
+    const innerHTML = fixture.nativeElement.innerHTML;
+    for (const metadatum of mockMetadata) {
+      expect(innerHTML).toContain(metadatum.value);
+    }
+  });
+
+  it('should contain the correct hrefs', () => {
+    const links = fixture.debugElement.queryAll(By.css('a'));
+    for (const metadatum of mockMetadata) {
+      expect(containsHref(links, metadatum.value)).toBeTruthy();
+    }
+  });
+
+  it('should contain separators equal to the amount of metadata values minus one', () => {
+    const separators = fixture.debugElement.queryAll(By.css('a span'));
+    expect(separators.length).toBe(mockMetadata.length - 1);
+  });
+
+  describe('when linktext is defined', () => {
+
+    beforeEach(() => {
+      comp.linktext = mockLinkText;
+      fixture.detectChanges();
+    });
+
+    it('should replace the metadata value with the linktext', () => {
+      const link = fixture.debugElement.query(By.css('a'));
+      expect(link.nativeElement.textContent).toContain(mockLinkText);
+    });
+
+  });
+
+});
+
+function containsHref(links: DebugElement[], href: string): boolean {
+  for (const link of links) {
+    const hrefAtt = link.properties.href;
+    if (isNotEmpty(hrefAtt)) {
+      if (hrefAtt === href) {
+        return true;
+      }
+    }
+  }
+  return false;
+}
diff --git a/src/app/+item-page/field-components/metadata-uri-values/metadata-uri-values.component.ts b/src/app/+item-page/field-components/metadata-uri-values/metadata-uri-values.component.ts
index 67684d44af13c8e8f905d4ef329c023d86fc8264..e070eccf2d5450af924ea673795c2b5fabf7b090 100644
--- a/src/app/+item-page/field-components/metadata-uri-values/metadata-uri-values.component.ts
+++ b/src/app/+item-page/field-components/metadata-uri-values/metadata-uri-values.component.ts
@@ -17,11 +17,24 @@ import { MetadataValue } from '../../../core/shared/metadata.models';
 })
 export class MetadataUriValuesComponent extends MetadataValuesComponent {
 
+  /**
+   * Optional text to replace the links with
+   * If undefined, the metadata value (uri) is displayed
+   */
   @Input() linktext: any;
 
+  /**
+   * The metadata values to display
+   */
   @Input() mdValues: MetadataValue[];
 
+  /**
+   * The seperator used to split the metadata values (can contain HTML)
+   */
   @Input() separator: string;
 
+  /**
+   * The label for this iteration of metadata values
+   */
   @Input() label: string;
 }
diff --git a/src/app/+item-page/field-components/metadata-values/metadata-values.component.spec.ts b/src/app/+item-page/field-components/metadata-values/metadata-values.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..cad2edb98a9baa06b7e870abca61a826b84c157f
--- /dev/null
+++ b/src/app/+item-page/field-components/metadata-values/metadata-values.component.spec.ts
@@ -0,0 +1,65 @@
+import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
+import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { MockTranslateLoader } from '../../../shared/mocks/mock-translate-loader';
+import { MetadataValuesComponent } from './metadata-values.component';
+import { By } from '@angular/platform-browser';
+import { MetadataValue } from '../../../core/shared/metadata.models';
+
+let comp: MetadataValuesComponent;
+let fixture: ComponentFixture<MetadataValuesComponent>;
+
+const mockMetadata = [
+  {
+    language: 'en_US',
+    value: '1234'
+  },
+  {
+    language: 'en_US',
+    value: 'a publisher'
+  },
+  {
+    language: 'en_US',
+    value: 'desc'
+  }] as MetadataValue[];
+const mockSeperator = '<br/>';
+const mockLabel = 'fake.message';
+
+describe('MetadataValuesComponent', () => {
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [TranslateModule.forRoot({
+        loader: {
+          provide: TranslateLoader,
+          useClass: MockTranslateLoader
+        }
+      })],
+      declarations: [MetadataValuesComponent],
+      schemas: [NO_ERRORS_SCHEMA]
+    }).overrideComponent(MetadataValuesComponent, {
+      set: {changeDetection: ChangeDetectionStrategy.Default}
+    }).compileComponents();
+  }));
+
+  beforeEach(async(() => {
+    fixture = TestBed.createComponent(MetadataValuesComponent);
+    comp = fixture.componentInstance;
+    comp.mdValues = mockMetadata;
+    comp.separator = mockSeperator;
+    comp.label = mockLabel;
+    fixture.detectChanges();
+  }));
+
+  it('should display all metadata values', () => {
+    const innerHTML = fixture.nativeElement.innerHTML;
+    for (const metadatum of mockMetadata) {
+      expect(innerHTML).toContain(metadatum.value);
+    }
+  });
+
+  it('should contain separators equal to the amount of metadata values minus one', () => {
+    const separators = fixture.debugElement.queryAll(By.css('span>span'));
+    expect(separators.length).toBe(mockMetadata.length - 1);
+  });
+
+});
diff --git a/src/app/+item-page/field-components/metadata-values/metadata-values.component.ts b/src/app/+item-page/field-components/metadata-values/metadata-values.component.ts
index abcd90848d1187315e341761ff3257d4c0c3d751..142b08b360da340065dafa5cc6031fb39f9124de 100644
--- a/src/app/+item-page/field-components/metadata-values/metadata-values.component.ts
+++ b/src/app/+item-page/field-components/metadata-values/metadata-values.component.ts
@@ -12,10 +12,19 @@ import { MetadataValue } from '../../../core/shared/metadata.models';
 })
 export class MetadataValuesComponent {
 
+  /**
+   * The metadata values to display
+   */
   @Input() mdValues: MetadataValue[];
 
+  /**
+   * The seperator used to split the metadata values (can contain HTML)
+   */
   @Input() separator: string;
 
+  /**
+   * The label for this iteration of metadata values
+   */
   @Input() label: string;
 
 }
diff --git a/src/app/+item-page/full/full-item-page.component.spec.ts b/src/app/+item-page/full/full-item-page.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..15dd0019642d67ae7029499f775ca8b45dbe240f
--- /dev/null
+++ b/src/app/+item-page/full/full-item-page.component.spec.ts
@@ -0,0 +1,78 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { ItemDataService } from '../../core/data/item-data.service';
+import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
+import { MockTranslateLoader } from '../../shared/mocks/mock-translate-loader';
+import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
+import { TruncatePipe } from '../../shared/utils/truncate.pipe';
+import { FullItemPageComponent } from './full-item-page.component';
+import { MetadataService } from '../../core/metadata/metadata.service';
+import { ActivatedRoute } from '@angular/router';
+import { ActivatedRouteStub } from '../../shared/testing/active-router-stub';
+import { VarDirective } from '../../shared/utils/var.directive';
+import { RouterTestingModule } from '@angular/router/testing';
+import { Item } from '../../core/shared/item.model';
+import { PageInfo } from '../../core/shared/page-info.model';
+import { PaginatedList } from '../../core/data/paginated-list';
+import { RemoteData } from '../../core/data/remote-data';
+import { of as observableOf } from 'rxjs';
+import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+import { By } from '@angular/platform-browser';
+
+const mockItem: Item = Object.assign(new Item(), {
+  bitstreams: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))),
+  metadata: {
+      'dc.title': [
+        {
+          language: 'en_US',
+          value: 'test item'
+        }
+      ]
+    }
+});
+const routeStub = Object.assign(new ActivatedRouteStub(), {
+  data: observableOf({ item: new RemoteData(false, false, true, null, mockItem) })
+});
+const metadataServiceStub = {
+  /* tslint:disable:no-empty */
+  processRemoteData: () => {}
+  /* tslint:enable:no-empty */
+};
+
+describe('FullItemPageComponent', () => {
+  let comp: FullItemPageComponent;
+  let fixture: ComponentFixture<FullItemPageComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [TranslateModule.forRoot({
+        loader: {
+          provide: TranslateLoader,
+          useClass: MockTranslateLoader
+        }
+      }), RouterTestingModule.withRoutes([]), BrowserAnimationsModule],
+      declarations: [FullItemPageComponent, TruncatePipe, VarDirective],
+      providers: [
+        {provide: ActivatedRoute, useValue: routeStub},
+        {provide: ItemDataService, useValue: {}},
+        {provide: MetadataService, useValue: metadataServiceStub}
+      ],
+
+      schemas: [NO_ERRORS_SCHEMA]
+    }).overrideComponent(FullItemPageComponent, {
+      set: {changeDetection: ChangeDetectionStrategy.Default}
+    }).compileComponents();
+  }));
+
+  beforeEach(async(() => {
+    fixture = TestBed.createComponent(FullItemPageComponent);
+    comp = fixture.componentInstance;
+    fixture.detectChanges();
+  }));
+
+  it('should display the item\'s metadata', () => {
+    const table = fixture.debugElement.query(By.css('table'));
+    for (const metadatum of mockItem.allMetadata([])) {
+      expect(table.nativeElement.innerHTML).toContain(metadatum.value);
+    }
+  })
+});
diff --git a/src/app/+item-page/full/full-item-page.component.ts b/src/app/+item-page/full/full-item-page.component.ts
index 6e19a5086486aa877cfed635f32ca380e54ccaa4..b2a42b7c6fe9e02deffac410ea29382e11c2f0be 100644
--- a/src/app/+item-page/full/full-item-page.component.ts
+++ b/src/app/+item-page/full/full-item-page.component.ts
@@ -1,9 +1,8 @@
-
 import {filter, map} from 'rxjs/operators';
 import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
-import { ActivatedRoute } from '@angular/router';
+import { ActivatedRoute, Router } from '@angular/router';
 
-import { Observable } from 'rxjs';
+import { Observable ,  BehaviorSubject } from 'rxjs';
 
 import { ItemPageComponent } from '../simple/item-page.component';
 import { MetadataMap } from '../../core/shared/metadata.models';
@@ -32,12 +31,12 @@ import { hasValue } from '../../shared/empty.util';
 })
 export class FullItemPageComponent extends ItemPageComponent implements OnInit {
 
-  itemRD$: Observable<RemoteData<Item>>;
+  itemRD$: BehaviorSubject<RemoteData<Item>>;
 
   metadata$: Observable<MetadataMap>;
 
-  constructor(route: ActivatedRoute, items: ItemDataService, metadataService: MetadataService) {
-    super(route, items, metadataService);
+  constructor(route: ActivatedRoute, router: Router, items: ItemDataService, metadataService: MetadataService) {
+    super(route, router, items, metadataService);
   }
 
   /*** AoT inheritance fix, will hopefully be resolved in the near future **/
diff --git a/src/app/+item-page/item-page.module.ts b/src/app/+item-page/item-page.module.ts
index c60f9d35832d4bb47ce61076821ae8d96795949f..123e3ea1432fe5de3dd4d3e98d4e43e946b1c278 100644
--- a/src/app/+item-page/item-page.module.ts
+++ b/src/app/+item-page/item-page.module.ts
@@ -1,47 +1,78 @@
 import { NgModule } from '@angular/core';
 import { CommonModule } from '@angular/common';
 
-import { SharedModule } from '../shared/shared.module';
+import { SharedModule } from './../shared/shared.module';
+import { GenericItemPageFieldComponent } from './simple/field-components/specific-field/generic/generic-item-page-field.component';
 
 import { ItemPageComponent } from './simple/item-page.component';
 import { ItemPageRoutingModule } from './item-page-routing.module';
-import { MetadataValuesComponent } from './field-components/metadata-values/metadata-values.component';
 import { MetadataUriValuesComponent } from './field-components/metadata-uri-values/metadata-uri-values.component';
-import { MetadataFieldWrapperComponent } from './field-components/metadata-field-wrapper/metadata-field-wrapper.component';
 import { ItemPageAuthorFieldComponent } from './simple/field-components/specific-field/author/item-page-author-field.component';
 import { ItemPageDateFieldComponent } from './simple/field-components/specific-field/date/item-page-date-field.component';
 import { ItemPageAbstractFieldComponent } from './simple/field-components/specific-field/abstract/item-page-abstract-field.component';
 import { ItemPageUriFieldComponent } from './simple/field-components/specific-field/uri/item-page-uri-field.component';
 import { ItemPageTitleFieldComponent } from './simple/field-components/specific-field/title/item-page-title-field.component';
-import { ItemPageSpecificFieldComponent } from './simple/field-components/specific-field/item-page-specific-field.component';
+import { ItemPageFieldComponent } from './simple/field-components/specific-field/item-page-field.component';
 import { FileSectionComponent } from './simple/field-components/file-section/file-section.component';
 import { CollectionsComponent } from './field-components/collections/collections.component';
 import { FullItemPageComponent } from './full/full-item-page.component';
 import { FullFileSectionComponent } from './full/field-components/file-section/full-file-section.component';
+import { RelatedItemsComponent } from './simple/related-items/related-items-component';
+import { SearchPageModule } from '../+search-page/search-page.module';
+import { PublicationComponent } from './simple/item-types/publication/publication.component';
+import { PersonComponent } from './simple/item-types/person/person.component';
+import { OrgunitComponent } from './simple/item-types/orgunit/orgunit.component';
+import { ProjectComponent } from './simple/item-types/project/project.component';
+import { JournalComponent } from './simple/item-types/journal/journal.component';
+import { JournalVolumeComponent } from './simple/item-types/journal-volume/journal-volume.component';
+import { JournalIssueComponent } from './simple/item-types/journal-issue/journal-issue.component';
+import { ItemComponent } from './simple/item-types/shared/item.component';
 import { EditItemPageModule } from './edit-item-page/edit-item-page.module';
+import { MetadataRepresentationListComponent } from './simple/metadata-representation-list/metadata-representation-list.component';
+import { RelatedEntitiesSearchComponent } from './simple/related-entities/related-entities-search/related-entities-search.component';
 
 @NgModule({
   imports: [
     CommonModule,
     SharedModule,
     EditItemPageModule,
-    ItemPageRoutingModule
+    ItemPageRoutingModule,
+    SearchPageModule
   ],
   declarations: [
     ItemPageComponent,
     FullItemPageComponent,
-    MetadataValuesComponent,
     MetadataUriValuesComponent,
-    MetadataFieldWrapperComponent,
     ItemPageAuthorFieldComponent,
     ItemPageDateFieldComponent,
     ItemPageAbstractFieldComponent,
     ItemPageUriFieldComponent,
     ItemPageTitleFieldComponent,
-    ItemPageSpecificFieldComponent,
+    ItemPageFieldComponent,
     FileSectionComponent,
     CollectionsComponent,
-    FullFileSectionComponent
+    FullFileSectionComponent,
+    PublicationComponent,
+    ProjectComponent,
+    OrgunitComponent,
+    PersonComponent,
+    RelatedItemsComponent,
+    ItemComponent,
+    GenericItemPageFieldComponent,
+    JournalComponent,
+    JournalIssueComponent,
+    JournalVolumeComponent,
+    MetadataRepresentationListComponent,
+    RelatedEntitiesSearchComponent
+  ],
+  entryComponents: [
+    PublicationComponent,
+    ProjectComponent,
+    OrgunitComponent,
+    PersonComponent,
+    JournalComponent,
+    JournalIssueComponent,
+    JournalVolumeComponent
   ]
 })
 export class ItemPageModule {
diff --git a/src/app/+item-page/item-page.resolver.ts b/src/app/+item-page/item-page.resolver.ts
index c0ee6a84eec9501f3917d46b95112400c340c345..4b7ef23b6991ee171095714dc49f4f1247ef17c0 100644
--- a/src/app/+item-page/item-page.resolver.ts
+++ b/src/app/+item-page/item-page.resolver.ts
@@ -2,9 +2,10 @@ import { Injectable } from '@angular/core';
 import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
 import { Observable } from 'rxjs';
 import { RemoteData } from '../core/data/remote-data';
-import { getSucceededRemoteData } from '../core/shared/operators';
 import { ItemDataService } from '../core/data/item-data.service';
 import { Item } from '../core/shared/item.model';
+import { hasValue } from '../shared/empty.util';
+import { find } from 'rxjs/operators';
 
 /**
  * This class represents a resolver that requests a specific item before the route is activated
@@ -18,11 +19,13 @@ export class ItemPageResolver implements Resolve<RemoteData<Item>> {
    * Method for resolving an item based on the parameters in the current route
    * @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot
    * @param {RouterStateSnapshot} state The current RouterStateSnapshot
-   * @returns Observable<<RemoteData<Item>> Emits the found item based on the parameters in the current route
+   * @returns Observable<<RemoteData<Item>> Emits the found item based on the parameters in the current route,
+   * or an error if something went wrong
    */
   resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<RemoteData<Item>> {
-    return this.itemService.findById(route.params.id).pipe(
-      getSucceededRemoteData()
-    );
+    return this.itemService.findById(route.params.id)
+      .pipe(
+        find((RD) => hasValue(RD.error) || RD.hasSucceeded),
+      );
   }
 }
diff --git a/src/app/+item-page/simple/field-components/specific-field/abstract/item-page-abstract-field.component.spec.ts b/src/app/+item-page/simple/field-components/specific-field/abstract/item-page-abstract-field.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9461ee0950772204df523f8b48f33e2432c6ea6a
--- /dev/null
+++ b/src/app/+item-page/simple/field-components/specific-field/abstract/item-page-abstract-field.component.spec.ts
@@ -0,0 +1,41 @@
+import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
+import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { ItemPageAbstractFieldComponent } from './item-page-abstract-field.component';
+import { MockTranslateLoader } from '../../../../../shared/testing/mock-translate-loader';
+import { MetadataValuesComponent } from '../../../../field-components/metadata-values/metadata-values.component';
+import { mockItemWithMetadataFieldAndValue } from '../item-page-field.component.spec';
+
+let comp: ItemPageAbstractFieldComponent;
+let fixture: ComponentFixture<ItemPageAbstractFieldComponent>;
+
+const mockField = 'dc.description.abstract';
+const mockValue = 'test value';
+
+describe('ItemPageAbstractFieldComponent', () => {
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [TranslateModule.forRoot({
+        loader: {
+          provide: TranslateLoader,
+          useClass: MockTranslateLoader
+        }
+      })],
+      declarations: [ItemPageAbstractFieldComponent, MetadataValuesComponent],
+      schemas: [NO_ERRORS_SCHEMA]
+    }).overrideComponent(ItemPageAbstractFieldComponent, {
+      set: {changeDetection: ChangeDetectionStrategy.Default}
+    }).compileComponents();
+  }));
+
+  beforeEach(async(() => {
+    fixture = TestBed.createComponent(ItemPageAbstractFieldComponent);
+    comp = fixture.componentInstance;
+    comp.item = mockItemWithMetadataFieldAndValue(mockField, mockValue);
+    fixture.detectChanges();
+  }));
+
+  it('should display display the correct metadata value', () => {
+    expect(fixture.nativeElement.innerHTML).toContain(mockValue);
+  });
+});
diff --git a/src/app/+item-page/simple/field-components/specific-field/abstract/item-page-abstract-field.component.ts b/src/app/+item-page/simple/field-components/specific-field/abstract/item-page-abstract-field.component.ts
index a8cc309ab69c6ca2cd86f097c69965e254e39d0e..00984d65921a647b31abb35fb4888dc8806bab30 100644
--- a/src/app/+item-page/simple/field-components/specific-field/abstract/item-page-abstract-field.component.ts
+++ b/src/app/+item-page/simple/field-components/specific-field/abstract/item-page-abstract-field.component.ts
@@ -1,22 +1,39 @@
 import { Component, Input } from '@angular/core';
 
 import { Item } from '../../../../../core/shared/item.model';
-import { ItemPageSpecificFieldComponent } from '../item-page-specific-field.component';
+import { ItemPageFieldComponent } from '../item-page-field.component';
 
 @Component({
     selector: 'ds-item-page-abstract-field',
-    templateUrl: './../item-page-specific-field.component.html'
+    templateUrl: '../item-page-field.component.html'
 })
-export class ItemPageAbstractFieldComponent extends ItemPageSpecificFieldComponent {
+/**
+ * This component is used for displaying the abstract (dc.description.abstract) of an item
+ */
+export class ItemPageAbstractFieldComponent extends ItemPageFieldComponent {
 
+    /**
+     * The item to display metadata for
+     */
     @Input() item: Item;
 
+    /**
+     * Separator string between multiple values of the metadata fields defined
+     * @type {string}
+     */
     separator: string;
 
+    /**
+     * Fields (schema.element.qualifier) used to render their values.
+     * In this component, we want to display values for metadata 'dc.description.abstract'
+     */
     fields: string[] = [
         'dc.description.abstract'
     ];
 
+    /**
+     * Label i18n key for the rendered metadata
+     */
     label = 'item.page.abstract';
 
 }
diff --git a/src/app/+item-page/simple/field-components/specific-field/author/item-page-author-field.component.spec.ts b/src/app/+item-page/simple/field-components/specific-field/author/item-page-author-field.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d865caff8af6d8d5b9ab59b6e5a075f212c6ca35
--- /dev/null
+++ b/src/app/+item-page/simple/field-components/specific-field/author/item-page-author-field.component.spec.ts
@@ -0,0 +1,45 @@
+import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
+import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { MockTranslateLoader } from '../../../../../shared/testing/mock-translate-loader';
+import { MetadataValuesComponent } from '../../../../field-components/metadata-values/metadata-values.component';
+import { mockItemWithMetadataFieldAndValue } from '../item-page-field.component.spec';
+import { ItemPageAuthorFieldComponent } from './item-page-author-field.component';
+
+let comp: ItemPageAuthorFieldComponent;
+let fixture: ComponentFixture<ItemPageAuthorFieldComponent>;
+
+const mockFields = ['dc.contributor.author', 'dc.creator', 'dc.contributor'];
+const mockValue = 'test value';
+
+describe('ItemPageAuthorFieldComponent', () => {
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [TranslateModule.forRoot({
+        loader: {
+          provide: TranslateLoader,
+          useClass: MockTranslateLoader
+        }
+      })],
+      declarations: [ItemPageAuthorFieldComponent, MetadataValuesComponent],
+      schemas: [NO_ERRORS_SCHEMA]
+    }).overrideComponent(ItemPageAuthorFieldComponent, {
+      set: {changeDetection: ChangeDetectionStrategy.Default}
+    }).compileComponents();
+  }));
+
+  for (const field of mockFields) {
+    beforeEach(async(() => {
+      fixture = TestBed.createComponent(ItemPageAuthorFieldComponent);
+      comp = fixture.componentInstance;
+      comp.item = mockItemWithMetadataFieldAndValue(field, mockValue);
+      fixture.detectChanges();
+    }));
+
+    describe(`when the item contains metadata for ${field}`, () => {
+      it('should display display the correct metadata value', () => {
+        expect(fixture.nativeElement.innerHTML).toContain(mockValue);
+      });
+    });
+  }
+});
diff --git a/src/app/+item-page/simple/field-components/specific-field/author/item-page-author-field.component.ts b/src/app/+item-page/simple/field-components/specific-field/author/item-page-author-field.component.ts
index e84a52d1b98c1175cd8b117390161424ed898a7d..51941d2cc8b4597e8a0273a7de46ebbf461270ef 100644
--- a/src/app/+item-page/simple/field-components/specific-field/author/item-page-author-field.component.ts
+++ b/src/app/+item-page/simple/field-components/specific-field/author/item-page-author-field.component.ts
@@ -1,24 +1,41 @@
 import { Component, Input } from '@angular/core';
 
 import { Item } from '../../../../../core/shared/item.model';
-import { ItemPageSpecificFieldComponent } from '../item-page-specific-field.component';
+import { ItemPageFieldComponent } from '../item-page-field.component';
 
 @Component({
   selector: 'ds-item-page-author-field',
-  templateUrl: './../item-page-specific-field.component.html'
+  templateUrl: '../item-page-field.component.html'
 })
-export class ItemPageAuthorFieldComponent extends ItemPageSpecificFieldComponent {
+/**
+ * This component is used for displaying the author (dc.contributor.author, dc.creator and dc.contributor) metadata of an item
+ */
+export class ItemPageAuthorFieldComponent extends ItemPageFieldComponent {
 
+  /**
+   * The item to display metadata for
+   */
   @Input() item: Item;
 
+  /**
+   * Separator string between multiple values of the metadata fields defined
+   * @type {string}
+   */
   separator: string;
 
+  /**
+   * Fields (schema.element.qualifier) used to render their values.
+   * In this component, we want to display values for metadata 'dc.contributor.author', 'dc.creator' and 'dc.contributor'
+   */
   fields: string[] = [
     'dc.contributor.author',
     'dc.creator',
     'dc.contributor'
   ];
 
+  /**
+   * Label i18n key for the rendered metadata
+   */
   label = 'item.page.author';
 
 }
diff --git a/src/app/+item-page/simple/field-components/specific-field/date/item-page-date-field.component.spec.ts b/src/app/+item-page/simple/field-components/specific-field/date/item-page-date-field.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2adada582b0c10b6d5200ee59c5a1ebb9ba1143d
--- /dev/null
+++ b/src/app/+item-page/simple/field-components/specific-field/date/item-page-date-field.component.spec.ts
@@ -0,0 +1,41 @@
+import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
+import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { MockTranslateLoader } from '../../../../../shared/testing/mock-translate-loader';
+import { MetadataValuesComponent } from '../../../../field-components/metadata-values/metadata-values.component';
+import { mockItemWithMetadataFieldAndValue } from '../item-page-field.component.spec';
+import { ItemPageDateFieldComponent } from './item-page-date-field.component';
+
+let comp: ItemPageDateFieldComponent;
+let fixture: ComponentFixture<ItemPageDateFieldComponent>;
+
+const mockField = 'dc.date.issued';
+const mockValue = 'test value';
+
+describe('ItemPageDateFieldComponent', () => {
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [TranslateModule.forRoot({
+        loader: {
+          provide: TranslateLoader,
+          useClass: MockTranslateLoader
+        }
+      })],
+      declarations: [ItemPageDateFieldComponent, MetadataValuesComponent],
+      schemas: [NO_ERRORS_SCHEMA]
+    }).overrideComponent(ItemPageDateFieldComponent, {
+      set: {changeDetection: ChangeDetectionStrategy.Default}
+    }).compileComponents();
+  }));
+
+  beforeEach(async(() => {
+    fixture = TestBed.createComponent(ItemPageDateFieldComponent);
+    comp = fixture.componentInstance;
+    comp.item = mockItemWithMetadataFieldAndValue(mockField, mockValue);
+    fixture.detectChanges();
+  }));
+
+  it('should display display the correct metadata value', () => {
+    expect(fixture.nativeElement.innerHTML).toContain(mockValue);
+  });
+});
diff --git a/src/app/+item-page/simple/field-components/specific-field/date/item-page-date-field.component.ts b/src/app/+item-page/simple/field-components/specific-field/date/item-page-date-field.component.ts
index 6950944f879f53ec9a806c798634fb3c6610b401..5a7d56b7dab596fa0f5a3cb8bf1f6e7c2ee79eb9 100644
--- a/src/app/+item-page/simple/field-components/specific-field/date/item-page-date-field.component.ts
+++ b/src/app/+item-page/simple/field-components/specific-field/date/item-page-date-field.component.ts
@@ -1,22 +1,39 @@
 import { Component, Input } from '@angular/core';
 
 import { Item } from '../../../../../core/shared/item.model';
-import { ItemPageSpecificFieldComponent } from '../item-page-specific-field.component';
+import { ItemPageFieldComponent } from '../item-page-field.component';
 
 @Component({
     selector: 'ds-item-page-date-field',
-    templateUrl: './../item-page-specific-field.component.html'
+    templateUrl: '../item-page-field.component.html'
 })
-export class ItemPageDateFieldComponent extends ItemPageSpecificFieldComponent {
+/**
+ * This component is used for displaying the issue date (dc.date.issued) metadata of an item
+ */
+export class ItemPageDateFieldComponent extends ItemPageFieldComponent {
 
+    /**
+     * The item to display metadata for
+     */
     @Input() item: Item;
 
+    /**
+     * Separator string between multiple values of the metadata fields defined
+     * @type {string}
+     */
     separator = ', ';
 
+    /**
+     * Fields (schema.element.qualifier) used to render their values.
+     * In this component, we want to display values for metadata 'dc.date.issued'
+     */
     fields: string[] = [
         'dc.date.issued'
     ];
 
+    /**
+     * Label i18n key for the rendered metadata
+     */
     label = 'item.page.date';
 
 }
diff --git a/src/app/+item-page/simple/field-components/specific-field/generic/generic-item-page-field.component.spec.ts b/src/app/+item-page/simple/field-components/specific-field/generic/generic-item-page-field.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d8abd39cf37d451106fe6371343b38101bc5d45e
--- /dev/null
+++ b/src/app/+item-page/simple/field-components/specific-field/generic/generic-item-page-field.component.spec.ts
@@ -0,0 +1,45 @@
+import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
+import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { MockTranslateLoader } from '../../../../../shared/testing/mock-translate-loader';
+import { MetadataValuesComponent } from '../../../../field-components/metadata-values/metadata-values.component';
+import { mockItemWithMetadataFieldAndValue } from '../item-page-field.component.spec';
+import { GenericItemPageFieldComponent } from './generic-item-page-field.component';
+
+let comp: GenericItemPageFieldComponent;
+let fixture: ComponentFixture<GenericItemPageFieldComponent>;
+
+const mockValue = 'test value';
+const mockField = 'dc.test';
+const mockLabel = 'test label';
+const mockFields = [mockField];
+
+describe('GenericItemPageFieldComponent', () => {
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [TranslateModule.forRoot({
+        loader: {
+          provide: TranslateLoader,
+          useClass: MockTranslateLoader
+        }
+      })],
+      declarations: [GenericItemPageFieldComponent, MetadataValuesComponent],
+      schemas: [NO_ERRORS_SCHEMA]
+    }).overrideComponent(GenericItemPageFieldComponent, {
+      set: {changeDetection: ChangeDetectionStrategy.Default}
+    }).compileComponents();
+  }));
+
+  beforeEach(async(() => {
+    fixture = TestBed.createComponent(GenericItemPageFieldComponent);
+    comp = fixture.componentInstance;
+    comp.item = mockItemWithMetadataFieldAndValue(mockField, mockValue);
+    comp.fields = mockFields;
+    comp.label = mockLabel;
+    fixture.detectChanges();
+  }));
+
+  it('should display display the correct metadata value', () => {
+    expect(fixture.nativeElement.innerHTML).toContain(mockValue);
+  });
+});
diff --git a/src/app/+item-page/simple/field-components/specific-field/generic/generic-item-page-field.component.ts b/src/app/+item-page/simple/field-components/specific-field/generic/generic-item-page-field.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ee7d27a11ff591516a1618ab3e092352fd4792b8
--- /dev/null
+++ b/src/app/+item-page/simple/field-components/specific-field/generic/generic-item-page-field.component.ts
@@ -0,0 +1,38 @@
+import { Component, Input } from '@angular/core';
+
+import { Item } from '../../../../../core/shared/item.model';
+import { ItemPageFieldComponent } from '../item-page-field.component';
+
+@Component({
+  selector: 'ds-generic-item-page-field',
+  templateUrl: '../item-page-field.component.html'
+})
+/**
+ * This component can be used to represent metadata on a simple item page.
+ * It is the most generic way of displaying metadata values
+ * It expects 4 parameters: The item, a seperator, the metadata keys and an i18n key
+ */
+export class GenericItemPageFieldComponent extends ItemPageFieldComponent {
+
+  /**
+   * The item to display metadata for
+   */
+  @Input() item: Item;
+
+  /**
+   * Separator string between multiple values of the metadata fields defined
+   * @type {string}
+   */
+  @Input() separator: string;
+
+  /**
+   * Fields (schema.element.qualifier) used to render their values.
+   */
+  @Input() fields: string[];
+
+  /**
+   * Label i18n key for the rendered metadata
+   */
+  @Input() label: string;
+
+}
diff --git a/src/app/+item-page/simple/field-components/specific-field/item-page-specific-field.component.html b/src/app/+item-page/simple/field-components/specific-field/item-page-field.component.html
similarity index 76%
rename from src/app/+item-page/simple/field-components/specific-field/item-page-specific-field.component.html
rename to src/app/+item-page/simple/field-components/specific-field/item-page-field.component.html
index d6a569198c8c93cd19ebf8f939664530876de7f5..fd3055d197e950b006b630717489d06804ea63c9 100644
--- a/src/app/+item-page/simple/field-components/specific-field/item-page-specific-field.component.html
+++ b/src/app/+item-page/simple/field-components/specific-field/item-page-field.component.html
@@ -1,3 +1,3 @@
-<div class="item-page-specific-field">
+<div class="item-page-field">
   <ds-metadata-values [mdValues]="item?.allMetadata(fields)" [separator]="separator" [label]="label"></ds-metadata-values>
 </div>
diff --git a/src/app/+item-page/simple/field-components/specific-field/item-page-field.component.spec.ts b/src/app/+item-page/simple/field-components/specific-field/item-page-field.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ea6e722c66853e9dfcd101fef5df2be68f7aee0c
--- /dev/null
+++ b/src/app/+item-page/simple/field-components/specific-field/item-page-field.component.spec.ts
@@ -0,0 +1,63 @@
+import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
+import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { Item } from '../../../../core/shared/item.model';
+import { PaginatedList } from '../../../../core/data/paginated-list';
+import { MockTranslateLoader } from '../../../../shared/mocks/mock-translate-loader';
+import { Observable } from 'rxjs';
+import { PageInfo } from '../../../../core/shared/page-info.model';
+import { RemoteData } from '../../../../core/data/remote-data';
+import { ItemPageFieldComponent } from './item-page-field.component';
+import { MetadataValuesComponent } from '../../../field-components/metadata-values/metadata-values.component';
+import { of as observableOf } from 'rxjs';
+import { MetadataMap, MetadataValue } from '../../../../core/shared/metadata.models';
+
+let comp: ItemPageFieldComponent;
+let fixture: ComponentFixture<ItemPageFieldComponent>;
+
+const mockValue = 'test value';
+const mockField = 'dc.test';
+const mockLabel = 'test label';
+const mockFields = [mockField];
+
+describe('ItemPageFieldComponent', () => {
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [TranslateModule.forRoot({
+        loader: {
+          provide: TranslateLoader,
+          useClass: MockTranslateLoader
+        }
+      })],
+      declarations: [ItemPageFieldComponent, MetadataValuesComponent],
+      schemas: [NO_ERRORS_SCHEMA]
+    }).overrideComponent(ItemPageFieldComponent, {
+      set: {changeDetection: ChangeDetectionStrategy.Default}
+    }).compileComponents();
+  }));
+
+  beforeEach(async(() => {
+    fixture = TestBed.createComponent(ItemPageFieldComponent);
+    comp = fixture.componentInstance;
+    comp.item = mockItemWithMetadataFieldAndValue(mockField, mockValue);
+    comp.fields = mockFields;
+    comp.label = mockLabel;
+    fixture.detectChanges();
+  }));
+
+  it('should display display the correct metadata value', () => {
+    expect(fixture.nativeElement.innerHTML).toContain(mockValue);
+  });
+});
+
+export function mockItemWithMetadataFieldAndValue(field: string, value: string): Item {
+  const item = Object.assign(new Item(), {
+    bitstreams: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))),
+    metadata: new MetadataMap()
+  });
+  item.metadata[field] = [{
+    language: 'en_US',
+    value: value
+  }] as MetadataValue[];
+  return item;
+}
diff --git a/src/app/+item-page/simple/field-components/specific-field/item-page-specific-field.component.ts b/src/app/+item-page/simple/field-components/specific-field/item-page-field.component.ts
similarity index 82%
rename from src/app/+item-page/simple/field-components/specific-field/item-page-specific-field.component.ts
rename to src/app/+item-page/simple/field-components/specific-field/item-page-field.component.ts
index f69671a5b5c23e6476e883cd87210d8a3fd4dd32..ce2b110efdd67921949a110192131ebe32963444 100644
--- a/src/app/+item-page/simple/field-components/specific-field/item-page-specific-field.component.ts
+++ b/src/app/+item-page/simple/field-components/specific-field/item-page-field.component.ts
@@ -9,10 +9,13 @@ import { Item } from '../../../../core/shared/item.model';
  */
 
 @Component({
-    templateUrl: './item-page-specific-field.component.html'
+    templateUrl: './item-page-field.component.html'
 })
-export class ItemPageSpecificFieldComponent {
+export class ItemPageFieldComponent {
 
+    /**
+     * The item to display metadata for
+     */
     @Input() item: Item;
 
     /**
diff --git a/src/app/+item-page/simple/field-components/specific-field/title/item-page-title-field.component.html b/src/app/+item-page/simple/field-components/specific-field/title/item-page-title-field.component.html
index aac85d335fd33f57a88f77fae5753e267511b1e0..43bd20d0f691fe7b4adc65c4fffc6d65456b71b0 100644
--- a/src/app/+item-page/simple/field-components/specific-field/title/item-page-title-field.component.html
+++ b/src/app/+item-page/simple/field-components/specific-field/title/item-page-title-field.component.html
@@ -1,3 +1,6 @@
 <h2 class="item-page-title-field">
+  <div *ngIf="item.firstMetadataValue('relationship.type') as type">
+    {{ type.toLowerCase() + '.page.titleprefix' | translate }}
+  </div>
   <ds-metadata-values [mdValues]="item?.allMetadata(fields)"></ds-metadata-values>
 </h2>
diff --git a/src/app/+item-page/simple/field-components/specific-field/title/item-page-title-field.component.spec.ts b/src/app/+item-page/simple/field-components/specific-field/title/item-page-title-field.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..cb1ba6a4bc2c7c6976a88f5cdc658c4c6271f525
--- /dev/null
+++ b/src/app/+item-page/simple/field-components/specific-field/title/item-page-title-field.component.spec.ts
@@ -0,0 +1,41 @@
+import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
+import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { MockTranslateLoader } from '../../../../../shared/testing/mock-translate-loader';
+import { MetadataValuesComponent } from '../../../../field-components/metadata-values/metadata-values.component';
+import { mockItemWithMetadataFieldAndValue } from '../item-page-field.component.spec';
+import { ItemPageTitleFieldComponent } from './item-page-title-field.component';
+
+let comp: ItemPageTitleFieldComponent;
+let fixture: ComponentFixture<ItemPageTitleFieldComponent>;
+
+const mockField = 'dc.title';
+const mockValue = 'test value';
+
+describe('ItemPageTitleFieldComponent', () => {
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [TranslateModule.forRoot({
+        loader: {
+          provide: TranslateLoader,
+          useClass: MockTranslateLoader
+        }
+      })],
+      declarations: [ItemPageTitleFieldComponent, MetadataValuesComponent],
+      schemas: [NO_ERRORS_SCHEMA]
+    }).overrideComponent(ItemPageTitleFieldComponent, {
+      set: {changeDetection: ChangeDetectionStrategy.Default}
+    }).compileComponents();
+  }));
+
+  beforeEach(async(() => {
+    fixture = TestBed.createComponent(ItemPageTitleFieldComponent);
+    comp = fixture.componentInstance;
+    comp.item = mockItemWithMetadataFieldAndValue(mockField, mockValue);
+    fixture.detectChanges();
+  }));
+
+  it('should display display the correct metadata value', () => {
+    expect(fixture.nativeElement.innerHTML).toContain(mockValue);
+  });
+});
diff --git a/src/app/+item-page/simple/field-components/specific-field/title/item-page-title-field.component.ts b/src/app/+item-page/simple/field-components/specific-field/title/item-page-title-field.component.ts
index be8102359a261d9a94770da684e4dd1764213567..c67d8bcf622eff7490535c28d0bf0a88fc939111 100644
--- a/src/app/+item-page/simple/field-components/specific-field/title/item-page-title-field.component.ts
+++ b/src/app/+item-page/simple/field-components/specific-field/title/item-page-title-field.component.ts
@@ -1,18 +1,32 @@
 import { Component, Input } from '@angular/core';
 
 import { Item } from '../../../../../core/shared/item.model';
-import { ItemPageSpecificFieldComponent } from '../item-page-specific-field.component';
+import { ItemPageFieldComponent } from '../item-page-field.component';
 
 @Component({
     selector: 'ds-item-page-title-field',
     templateUrl: './item-page-title-field.component.html'
 })
-export class ItemPageTitleFieldComponent extends ItemPageSpecificFieldComponent {
+/**
+ * This component is used for displaying the title (dc.title) of an item
+ */
+export class ItemPageTitleFieldComponent extends ItemPageFieldComponent {
 
+    /**
+     * The item to display metadata for
+     */
     @Input() item: Item;
 
+    /**
+     * Separator string between multiple values of the metadata fields defined
+     * @type {string}
+     */
     separator: string;
 
+    /**
+     * Fields (schema.element.qualifier) used to render their values.
+     * In this component, we want to display values for metadata 'dc.title'
+     */
     fields: string[] = [
         'dc.title'
     ];
diff --git a/src/app/+item-page/simple/field-components/specific-field/uri/item-page-uri-field.component.html b/src/app/+item-page/simple/field-components/specific-field/uri/item-page-uri-field.component.html
index a5561b22e5d262a0112ab31e89ed121acdf43095..2b197541276761e3fbc377edd9f7992b204cc896 100644
--- a/src/app/+item-page/simple/field-components/specific-field/uri/item-page-uri-field.component.html
+++ b/src/app/+item-page/simple/field-components/specific-field/uri/item-page-uri-field.component.html
@@ -1,3 +1,3 @@
-<div class="item-page-specific-field">
+<div class="item-page-field">
   <ds-metadata-uri-values [mdValues]="item?.allMetadata(fields)" [separator]="separator" [label]="label"></ds-metadata-uri-values>
 </div>
diff --git a/src/app/+item-page/simple/field-components/specific-field/uri/item-page-uri-field.component.spec.ts b/src/app/+item-page/simple/field-components/specific-field/uri/item-page-uri-field.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4511f16aae01deddcd017b37ef6a039375bad62f
--- /dev/null
+++ b/src/app/+item-page/simple/field-components/specific-field/uri/item-page-uri-field.component.spec.ts
@@ -0,0 +1,41 @@
+import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
+import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { MockTranslateLoader } from '../../../../../shared/testing/mock-translate-loader';
+import { mockItemWithMetadataFieldAndValue } from '../item-page-field.component.spec';
+import { ItemPageUriFieldComponent } from './item-page-uri-field.component';
+import { MetadataUriValuesComponent } from '../../../../field-components/metadata-uri-values/metadata-uri-values.component';
+
+let comp: ItemPageUriFieldComponent;
+let fixture: ComponentFixture<ItemPageUriFieldComponent>;
+
+const mockField = 'dc.identifier.uri';
+const mockValue = 'test value';
+
+describe('ItemPageUriFieldComponent', () => {
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [TranslateModule.forRoot({
+        loader: {
+          provide: TranslateLoader,
+          useClass: MockTranslateLoader
+        }
+      })],
+      declarations: [ItemPageUriFieldComponent, MetadataUriValuesComponent],
+      schemas: [NO_ERRORS_SCHEMA]
+    }).overrideComponent(ItemPageUriFieldComponent, {
+      set: {changeDetection: ChangeDetectionStrategy.Default}
+    }).compileComponents();
+  }));
+
+  beforeEach(async(() => {
+    fixture = TestBed.createComponent(ItemPageUriFieldComponent);
+    comp = fixture.componentInstance;
+    comp.item = mockItemWithMetadataFieldAndValue(mockField, mockValue);
+    fixture.detectChanges();
+  }));
+
+  it('should display display the correct metadata value', () => {
+    expect(fixture.nativeElement.innerHTML).toContain(mockValue);
+  });
+});
diff --git a/src/app/+item-page/simple/field-components/specific-field/uri/item-page-uri-field.component.ts b/src/app/+item-page/simple/field-components/specific-field/uri/item-page-uri-field.component.ts
index 4f0633703211d089b1fad0fd3e0db9a43eb0c1dd..c9cd5f1a00db35b687f213a50031ac68482d06ea 100644
--- a/src/app/+item-page/simple/field-components/specific-field/uri/item-page-uri-field.component.ts
+++ b/src/app/+item-page/simple/field-components/specific-field/uri/item-page-uri-field.component.ts
@@ -1,22 +1,39 @@
 import { Component, Input } from '@angular/core';
 
 import { Item } from '../../../../../core/shared/item.model';
-import { ItemPageSpecificFieldComponent } from '../item-page-specific-field.component';
+import { ItemPageFieldComponent } from '../item-page-field.component';
 
 @Component({
   selector: 'ds-item-page-uri-field',
   templateUrl: './item-page-uri-field.component.html'
 })
-export class ItemPageUriFieldComponent extends ItemPageSpecificFieldComponent {
+/**
+ * This component is used for displaying the uri (dc.identifier.uri) metadata of an item
+ */
+export class ItemPageUriFieldComponent extends ItemPageFieldComponent {
 
+  /**
+   * The item to display metadata for
+   */
   @Input() item: Item;
 
+  /**
+   * Separator string between multiple values of the metadata fields defined
+   * @type {string}
+   */
   separator: string;
 
+  /**
+   * Fields (schema.element.qualifier) used to render their values.
+   * In this component, we want to display values for metadata 'dc.identifier.uri'
+   */
   fields: string[] = [
     'dc.identifier.uri'
   ];
 
+  /**
+   * Label i18n key for the rendered metadata
+   */
   label = 'item.page.uri';
 
 }
diff --git a/src/app/+item-page/simple/item-page.component.html b/src/app/+item-page/simple/item-page.component.html
index 98b98a5e32fc9090e637980822da74d5fe55316f..b6de496dc44637e5cecb41b145d017c2c2662d65 100644
--- a/src/app/+item-page/simple/item-page.component.html
+++ b/src/app/+item-page/simple/item-page.component.html
@@ -1,27 +1,7 @@
 <div class="container" *ngVar="(itemRD$ | async) as itemRD">
   <div class="item-page" *ngIf="itemRD?.hasSucceeded" @fadeInOut>
     <div *ngIf="itemRD?.payload as item">
-      <ds-item-page-title-field [item]="item"></ds-item-page-title-field>
-      <div class="row">
-        <div class="col-xs-12 col-md-4">
-          <ds-metadata-field-wrapper>
-            <ds-thumbnail [thumbnail]="thumbnail$ | async"></ds-thumbnail>
-          </ds-metadata-field-wrapper>
-          <ds-item-page-file-section [item]="item"></ds-item-page-file-section>
-          <ds-item-page-date-field [item]="item"></ds-item-page-date-field>
-          <ds-item-page-author-field [item]="item"></ds-item-page-author-field>
-        </div>
-        <div class="col-xs-12 col-md-6">
-          <ds-item-page-abstract-field [item]="item"></ds-item-page-abstract-field>
-          <ds-item-page-uri-field [item]="item"></ds-item-page-uri-field>
-          <ds-item-page-collections [item]="item"></ds-item-page-collections>
-          <div>
-            <a class="btn btn-outline-primary" [routerLink]="['/items/' + item.id + '/full']">
-              {{"item.page.link.full" | translate}}
-            </a>
-          </div>
-        </div>
-      </div>
+      <ds-item-type-switcher [object]="item" [viewMode]="viewMode"></ds-item-type-switcher>
     </div>
   </div>
   <ds-error *ngIf="itemRD?.hasFailed" message="{{'error.item' | translate}}"></ds-error>
diff --git a/src/app/+item-page/simple/item-page.component.scss b/src/app/+item-page/simple/item-page.component.scss
index 50be6f5ad03dee5a13636a9571c7a2e4bf85181d..4c26cf08fbb5db8df87bb4c23b8e494802253ff4 100644
--- a/src/app/+item-page/simple/item-page.component.scss
+++ b/src/app/+item-page/simple/item-page.component.scss
@@ -1 +1,9 @@
 @import '../../../styles/variables.scss';
+@import '../../../styles/mixins.scss';
+
+@include media-breakpoint-down(md) {
+  .container {
+    width: 100%;
+    max-width: none;
+  }
+}
diff --git a/src/app/+item-page/simple/item-page.component.spec.ts b/src/app/+item-page/simple/item-page.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e1202ab7256ee6588f587611f6996c0bc2530497
--- /dev/null
+++ b/src/app/+item-page/simple/item-page.component.spec.ts
@@ -0,0 +1,91 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
+import { MockTranslateLoader } from '../../shared/mocks/mock-translate-loader';
+import { ItemDataService } from '../../core/data/item-data.service';
+import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
+import { ItemPageComponent } from './item-page.component';
+import { ActivatedRoute, Router } from '@angular/router';
+import { ActivatedRouteStub } from '../../shared/testing/active-router-stub';
+import { MetadataService } from '../../core/metadata/metadata.service';
+import { VarDirective } from '../../shared/utils/var.directive';
+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 { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+import { By } from '@angular/platform-browser';
+import { createRelationshipsObservable } from './item-types/shared/item.component.spec';
+import { of as observableOf } from 'rxjs';
+
+const mockItem: Item = Object.assign(new Item(), {
+  bitstreams: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))),
+  metadata: [],
+  relationships: createRelationshipsObservable()
+});
+
+describe('ItemPageComponent', () => {
+  let comp: ItemPageComponent;
+  let fixture: ComponentFixture<ItemPageComponent>;
+
+  const mockMetadataService = {
+    /* tslint:disable:no-empty */
+    processRemoteData: () => {}
+    /* tslint:enable:no-empty */
+  };
+  const mockRoute = Object.assign(new ActivatedRouteStub(), {
+    data: observableOf({ item: new RemoteData(false, false, true, null, mockItem) })
+  });
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [TranslateModule.forRoot({
+        loader: {
+          provide: TranslateLoader,
+          useClass: MockTranslateLoader
+        }
+      }), BrowserAnimationsModule],
+      declarations: [ItemPageComponent, VarDirective],
+      providers: [
+        {provide: ActivatedRoute, useValue: mockRoute},
+        {provide: ItemDataService, useValue: {}},
+        {provide: MetadataService, useValue: mockMetadataService},
+        {provide: Router, useValue: {}}
+      ],
+
+      schemas: [NO_ERRORS_SCHEMA]
+    }).overrideComponent(ItemPageComponent, {
+      set: {changeDetection: ChangeDetectionStrategy.Default}
+    }).compileComponents();
+  }));
+
+  beforeEach(async(() => {
+    fixture = TestBed.createComponent(ItemPageComponent);
+    comp = fixture.componentInstance;
+    fixture.detectChanges();
+  }));
+
+  describe('when the item is loading', () => {
+    beforeEach(() => {
+      comp.itemRD$ = observableOf(new RemoteData(true, true, true, null, undefined));
+      fixture.detectChanges();
+    });
+
+    it('should display a loading component', () => {
+      const loading = fixture.debugElement.query(By.css('ds-loading'));
+      expect(loading.nativeElement).toBeDefined();
+    });
+  });
+
+  describe('when the item failed loading', () => {
+    beforeEach(() => {
+      comp.itemRD$ = observableOf(new RemoteData(false, false, false, null, undefined));
+      fixture.detectChanges();
+    });
+
+    it('should display an error component', () => {
+      const error = fixture.debugElement.query(By.css('ds-error'));
+      expect(error.nativeElement).toBeDefined();
+    });
+  });
+
+});
diff --git a/src/app/+item-page/simple/item-page.component.ts b/src/app/+item-page/simple/item-page.component.ts
index 35162b011f19bb4c2d23043bdd48c9f1f30b45c2..89d5977583149e23eafe998295f15c82ea97c7b2 100644
--- a/src/app/+item-page/simple/item-page.component.ts
+++ b/src/app/+item-page/simple/item-page.component.ts
@@ -1,7 +1,7 @@
 
-import {mergeMap, filter, map} from 'rxjs/operators';
+import { mergeMap, filter, map, take, tap } from 'rxjs/operators';
 import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
-import { ActivatedRoute } from '@angular/router';
+import { ActivatedRoute, Router } from '@angular/router';
 
 import { Observable } from 'rxjs';
 import { ItemDataService } from '../../core/data/item-data.service';
@@ -14,6 +14,8 @@ import { MetadataService } from '../../core/metadata/metadata.service';
 
 import { fadeInOut } from '../../shared/animations/fade';
 import { hasValue } from '../../shared/empty.util';
+import { redirectToPageNotFoundOn404 } from '../../core/shared/operators';
+import { ItemViewMode } from '../../shared/items/item-type-decorator';
 
 /**
  * This component renders a simple item page.
@@ -29,28 +31,33 @@ import { hasValue } from '../../shared/empty.util';
 })
 export class ItemPageComponent implements OnInit {
 
+  /**
+   * The item's id
+   */
   id: number;
 
-  private sub: any;
-
+  /**
+   * The item wrapped in a remote-data object
+   */
   itemRD$: Observable<RemoteData<Item>>;
 
-  thumbnail$: Observable<Bitstream>;
+  /**
+   * The view-mode we're currently on
+   */
+  viewMode = ItemViewMode.Full;
 
   constructor(
     private route: ActivatedRoute,
+    private router: Router,
     private items: ItemDataService,
-    private metadataService: MetadataService
-  ) {
-
-  }
+    private metadataService: MetadataService,
+  ) { }
 
   ngOnInit(): void {
-    this.itemRD$ = this.route.data.pipe(map((data) => data.item));
+    this.itemRD$ = this.route.data.pipe(
+      map((data) => data.item as RemoteData<Item>),
+      redirectToPageNotFoundOn404(this.router)
+    );
     this.metadataService.processRemoteData(this.itemRD$);
-    this.thumbnail$ = this.itemRD$.pipe(
-      map((rd: RemoteData<Item>) => rd.payload),
-      filter((item: Item) => hasValue(item)),
-      mergeMap((item: Item) => item.getThumbnail()),);
   }
 }
diff --git a/src/app/+item-page/simple/item-types/journal-issue/journal-issue.component.html b/src/app/+item-page/simple/item-types/journal-issue/journal-issue.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..5d96abb82b5e77fe21a793eba06106f4bcd82b88
--- /dev/null
+++ b/src/app/+item-page/simple/item-types/journal-issue/journal-issue.component.html
@@ -0,0 +1,50 @@
+<h2 class="item-page-title-field">
+  {{'journalissue.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="item?.allMetadata(['dc.title'])"></ds-metadata-values>
+</h2>
+<div class="row">
+  <div class="col-xs-12 col-md-4">
+    <ds-metadata-field-wrapper>
+      <ds-thumbnail [thumbnail]="this.item.getThumbnail() | async"></ds-thumbnail>
+    </ds-metadata-field-wrapper>
+    <ds-generic-item-page-field [item]="item"
+      [fields]="['journalissue.identifier.number']"
+      [label]="'journalissue.page.number'">
+    </ds-generic-item-page-field>
+    <ds-generic-item-page-field [item]="item"
+      [fields]="['journalissue.issuedate']"
+      [label]="'journalissue.page.issuedate'">
+    </ds-generic-item-page-field>
+    <ds-generic-item-page-field [item]="item"
+      [fields]="['journal.title']"
+      [label]="'journalissue.page.journal-title'">
+    </ds-generic-item-page-field>
+    <ds-generic-item-page-field [item]="item"
+      [fields]="['journal.identifier.issn']"
+      [label]="'journalissue.page.journal-issn'">
+    </ds-generic-item-page-field>
+  </div>
+  <div class="col-xs-12 col-md-6">
+    <ds-related-items
+      [items]="volumes$ | async"
+      [label]="'relationships.isSingleVolumeOf' | translate">
+    </ds-related-items>
+    <ds-related-items
+      class="mb-1 mt-1"
+      [items]="publications$ | async"
+      [label]="'relationships.isPublicationOfJournalIssue' | translate">
+    </ds-related-items>
+    <ds-generic-item-page-field [item]="item"
+      [fields]="['journalissue.identifier.description']"
+      [label]="'journalissue.page.description'">
+    </ds-generic-item-page-field>
+    <ds-generic-item-page-field [item]="item"
+      [fields]="['journalissue.identifier.keyword']"
+      [label]="'journalissue.page.keyword'">
+    </ds-generic-item-page-field>
+    <div>
+      <a class="btn btn-outline-primary" [routerLink]="['/items/' + item.id + '/full']">
+        {{"item.page.link.full" | translate}}
+      </a>
+    </div>
+  </div>
+</div>
diff --git a/src/app/+item-page/simple/item-types/journal-issue/journal-issue.component.scss b/src/app/+item-page/simple/item-types/journal-issue/journal-issue.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..3575cae797d53c2f3bcd1dcb34f6b91b529db815
--- /dev/null
+++ b/src/app/+item-page/simple/item-types/journal-issue/journal-issue.component.scss
@@ -0,0 +1 @@
+@import '../../../../../styles/variables.scss';
diff --git a/src/app/+item-page/simple/item-types/journal-issue/journal-issue.component.spec.ts b/src/app/+item-page/simple/item-types/journal-issue/journal-issue.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..24b18af96e8cbbfdb52f0d83346a04505e6798aa
--- /dev/null
+++ b/src/app/+item-page/simple/item-types/journal-issue/journal-issue.component.spec.ts
@@ -0,0 +1,40 @@
+import { Item } from '../../../../core/shared/item.model';
+import { RemoteData } from '../../../../core/data/remote-data';
+import { PaginatedList } from '../../../../core/data/paginated-list';
+import { PageInfo } from '../../../../core/shared/page-info.model';
+import { createRelationshipsObservable, getItemPageFieldsTest } from '../shared/item.component.spec';
+import { JournalIssueComponent } from './journal-issue.component';
+import { of as observableOf } from 'rxjs';
+
+const mockItem: Item = Object.assign(new Item(), {
+  bitstreams: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))),
+  metadata: {
+    'journalissue.identifier.number': [
+      {
+        language: 'en_US',
+        value: '1234'
+      }
+    ],
+    'journalissue.issuedate': [
+      {
+        language: 'en_US',
+        value: '2018'
+      }
+    ],
+    'journalissue.identifier.description': [
+      {
+        language: 'en_US',
+        value: 'desc'
+      }
+    ],
+    'journalissue.identifier.keyword': [
+      {
+        language: 'en_US',
+        value: 'keyword'
+      }
+    ]
+  },
+  relationships: createRelationshipsObservable()
+});
+
+describe('JournalIssueComponent', getItemPageFieldsTest(mockItem, JournalIssueComponent));
diff --git a/src/app/+item-page/simple/item-types/journal-issue/journal-issue.component.ts b/src/app/+item-page/simple/item-types/journal-issue/journal-issue.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..77ed54d67fc6fa2700c2c02b61d666f9feb8fbdd
--- /dev/null
+++ b/src/app/+item-page/simple/item-types/journal-issue/journal-issue.component.ts
@@ -0,0 +1,51 @@
+import { Component, Inject } from '@angular/core';
+import { Observable } from 'rxjs';
+import { ItemDataService } from '../../../../core/data/item-data.service';
+import { Item } from '../../../../core/shared/item.model';
+import { ItemViewMode, rendersItemType } from '../../../../shared/items/item-type-decorator';
+import { ITEM } from '../../../../shared/items/switcher/item-type-switcher.component';
+import { isNotEmpty } from '../../../../shared/empty.util';
+import { ItemComponent } from '../shared/item.component';
+import { filterRelationsByTypeLabel, relationsToItems } from '../shared/item-relationships-utils';
+
+@rendersItemType('JournalIssue', ItemViewMode.Full)
+@Component({
+  selector: 'ds-journal-issue',
+  styleUrls: ['./journal-issue.component.scss'],
+  templateUrl: './journal-issue.component.html'
+})
+/**
+ * The component for displaying metadata and relations of an item of the type Journal Issue
+ */
+export class JournalIssueComponent extends ItemComponent {
+  /**
+   * The volumes related to this journal issue
+   */
+  volumes$: Observable<Item[]>;
+
+  /**
+   * The publications related to this journal issue
+   */
+  publications$: Observable<Item[]>;
+
+  constructor(
+    @Inject(ITEM) public item: Item,
+    private ids: ItemDataService
+  ) {
+    super(item);
+  }
+  ngOnInit(): void {
+    super.ngOnInit();
+
+    if (isNotEmpty(this.resolvedRelsAndTypes$)) {
+      this.volumes$ = this.resolvedRelsAndTypes$.pipe(
+        filterRelationsByTypeLabel('isJournalVolumeOfIssue'),
+        relationsToItems(this.item.id, this.ids)
+      );
+      this.publications$ = this.resolvedRelsAndTypes$.pipe(
+        filterRelationsByTypeLabel('isPublicationOfJournalIssue'),
+        relationsToItems(this.item.id, this.ids)
+      );
+    }
+  }
+}
diff --git a/src/app/+item-page/simple/item-types/journal-volume/journal-volume.component.html b/src/app/+item-page/simple/item-types/journal-volume/journal-volume.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..18bf1701fcd6cb423977db9ef59daf4eb4a54e19
--- /dev/null
+++ b/src/app/+item-page/simple/item-types/journal-volume/journal-volume.component.html
@@ -0,0 +1,37 @@
+<h2 class="item-page-title-field">
+  {{'journalvolume.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="item?.allMetadata(['dc.title'])"></ds-metadata-values>
+</h2>
+<div class="row">
+  <div class="col-xs-12 col-md-4">
+    <ds-metadata-field-wrapper>
+      <ds-thumbnail [thumbnail]="this.item.getThumbnail() | async"></ds-thumbnail>
+    </ds-metadata-field-wrapper>
+    <ds-generic-item-page-field [item]="item"
+      [fields]="['journalvolume.identifier.volume']"
+      [label]="'journalvolume.page.volume'">
+    </ds-generic-item-page-field>
+    <ds-generic-item-page-field [item]="item"
+      [fields]="['journalvolume.issuedate']"
+      [label]="'journalvolume.page.issuedate'">
+    </ds-generic-item-page-field>
+  </div>
+  <div class="col-xs-12 col-md-6">
+    <ds-related-items
+      [items]="journals$ | async"
+      [label]="'relationships.isSingleJournalOf' | translate">
+    </ds-related-items>
+    <ds-related-items
+      [items]="issues$ | async"
+      [label]="'relationships.isIssueOf' | translate">
+    </ds-related-items>
+    <ds-generic-item-page-field [item]="item"
+      [fields]="['journalvolume.identifier.description']"
+      [label]="'journalvolume.page.description'">
+    </ds-generic-item-page-field>
+    <div>
+      <a class="btn btn-outline-primary" [routerLink]="['/items/' + item.id + '/full']">
+        {{"item.page.link.full" | translate}}
+      </a>
+    </div>
+  </div>
+</div>
diff --git a/src/app/+item-page/simple/item-types/journal-volume/journal-volume.component.scss b/src/app/+item-page/simple/item-types/journal-volume/journal-volume.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..3575cae797d53c2f3bcd1dcb34f6b91b529db815
--- /dev/null
+++ b/src/app/+item-page/simple/item-types/journal-volume/journal-volume.component.scss
@@ -0,0 +1 @@
+@import '../../../../../styles/variables.scss';
diff --git a/src/app/+item-page/simple/item-types/journal-volume/journal-volume.component.spec.ts b/src/app/+item-page/simple/item-types/journal-volume/journal-volume.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a6f32e9b5f711083a22ab8d6dd2a68496666dd34
--- /dev/null
+++ b/src/app/+item-page/simple/item-types/journal-volume/journal-volume.component.spec.ts
@@ -0,0 +1,34 @@
+import { Item } from '../../../../core/shared/item.model';
+import { RemoteData } from '../../../../core/data/remote-data';
+import { PaginatedList } from '../../../../core/data/paginated-list';
+import { PageInfo } from '../../../../core/shared/page-info.model';
+import { createRelationshipsObservable, getItemPageFieldsTest } from '../shared/item.component.spec';
+import { JournalVolumeComponent } from './journal-volume.component';
+import { of as observableOf } from 'rxjs';
+
+const mockItem: Item = Object.assign(new Item(), {
+  bitstreams: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))),
+  metadata: {
+    'journalvolume.identifier.volume': [
+      {
+        language: 'en_US',
+        value: '1234'
+      }
+    ],
+    'journalvolume.issuedate': [
+      {
+        language: 'en_US',
+        value: '2018'
+      }
+    ],
+    'journalvolume.identifier.description': [
+      {
+        language: 'en_US',
+        value: 'desc'
+      }
+    ]
+  },
+  relationships: createRelationshipsObservable()
+});
+
+describe('JournalVolumeComponent', getItemPageFieldsTest(mockItem, JournalVolumeComponent));
diff --git a/src/app/+item-page/simple/item-types/journal-volume/journal-volume.component.ts b/src/app/+item-page/simple/item-types/journal-volume/journal-volume.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..616d96178a46b1faf82f60f3d94eeefe3e59d79a
--- /dev/null
+++ b/src/app/+item-page/simple/item-types/journal-volume/journal-volume.component.ts
@@ -0,0 +1,51 @@
+import { Component, Inject } from '@angular/core';
+import { Observable } from 'rxjs';
+import { ItemDataService } from '../../../../core/data/item-data.service';
+import { Item } from '../../../../core/shared/item.model';
+import { ItemViewMode, rendersItemType } from '../../../../shared/items/item-type-decorator';
+import { ITEM } from '../../../../shared/items/switcher/item-type-switcher.component';
+import { isNotEmpty } from '../../../../shared/empty.util';
+import { ItemComponent } from '../shared/item.component';
+import { filterRelationsByTypeLabel, relationsToItems } from '../shared/item-relationships-utils';
+
+@rendersItemType('JournalVolume', ItemViewMode.Full)
+@Component({
+  selector: 'ds-journal-volume',
+  styleUrls: ['./journal-volume.component.scss'],
+  templateUrl: './journal-volume.component.html'
+})
+/**
+ * The component for displaying metadata and relations of an item of the type Journal Volume
+ */
+export class JournalVolumeComponent extends ItemComponent {
+  /**
+   * The journals related to this journal volume
+   */
+  journals$: Observable<Item[]>;
+
+  /**
+   * The journal issues related to this journal volume
+   */
+  issues$: Observable<Item[]>;
+
+  constructor(
+    @Inject(ITEM) public item: Item,
+    private ids: ItemDataService
+  ) {
+    super(item);
+  }
+  ngOnInit(): void {
+    super.ngOnInit();
+
+    if (isNotEmpty(this.resolvedRelsAndTypes$)) {
+      this.journals$ = this.resolvedRelsAndTypes$.pipe(
+        filterRelationsByTypeLabel('isJournalOfVolume'),
+        relationsToItems(this.item.id, this.ids)
+      );
+      this.issues$ = this.resolvedRelsAndTypes$.pipe(
+        filterRelationsByTypeLabel('isIssueOfJournalVolume'),
+        relationsToItems(this.item.id, this.ids)
+      );
+    }
+  }
+}
diff --git a/src/app/+item-page/simple/item-types/journal/journal.component.html b/src/app/+item-page/simple/item-types/journal/journal.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..2ab34302569f406bbd84f1c027198c9a1f2ee44e
--- /dev/null
+++ b/src/app/+item-page/simple/item-types/journal/journal.component.html
@@ -0,0 +1,42 @@
+<h2 class="item-page-title-field">
+  {{'journal.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="item?.allMetadata(['dc.title'])"></ds-metadata-values>
+</h2>
+<div class="row">
+  <div class="col-xs-12 col-md-4">
+    <ds-metadata-field-wrapper>
+      <ds-thumbnail [thumbnail]="this.item.getThumbnail() | async"></ds-thumbnail>
+    </ds-metadata-field-wrapper>
+    <ds-generic-item-page-field class="item-page-fields" [item]="item"
+      [fields]="['journal.identifier.issn']"
+      [label]="'journal.page.issn'">
+    </ds-generic-item-page-field>
+    <ds-generic-item-page-field class="item-page-fields" [item]="item"
+      [fields]="['journal.publisher']"
+      [label]="'journal.page.publisher'">
+    </ds-generic-item-page-field>
+    <ds-generic-item-page-field [item]="item"
+      [fields]="['journal.contributor.editor']"
+      [label]="'journal.page.editor'">
+    </ds-generic-item-page-field>
+  </div>
+  <div class="col-xs-12 col-md-6">
+    <ds-related-items
+      [items]="volumes$ | async"
+      [label]="'relationships.isVolumeOf' | translate">
+    </ds-related-items>
+    <ds-generic-item-page-field class="item-page-fields" [item]="item"
+      [fields]="['journal.identifier.description']"
+      [label]="'journal.page.description'">
+    </ds-generic-item-page-field>
+    <div>
+      <a class="btn btn-outline-primary" [routerLink]="['/items/' + item.id + '/full']">
+        {{"item.page.link.full" | translate}}
+      </a>
+    </div>
+  </div>
+  <div class="mt-5 w-100">
+    <ds-related-entities-search [item]="item"
+      [relationType]="'isJournalOfPublication'">
+    </ds-related-entities-search>
+  </div>
+</div>
diff --git a/src/app/+item-page/simple/item-types/journal/journal.component.scss b/src/app/+item-page/simple/item-types/journal/journal.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..3575cae797d53c2f3bcd1dcb34f6b91b529db815
--- /dev/null
+++ b/src/app/+item-page/simple/item-types/journal/journal.component.scss
@@ -0,0 +1 @@
+@import '../../../../../styles/variables.scss';
diff --git a/src/app/+item-page/simple/item-types/journal/journal.component.spec.ts b/src/app/+item-page/simple/item-types/journal/journal.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..08e8859b3519de286c0b8b98b57b0918f9de1c82
--- /dev/null
+++ b/src/app/+item-page/simple/item-types/journal/journal.component.spec.ts
@@ -0,0 +1,92 @@
+import { ChangeDetectionStrategy, DebugElement, NO_ERRORS_SCHEMA } from '@angular/core';
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { TruncatableService } from '../../../../shared/truncatable/truncatable.service';
+import { ITEM } from '../../../../shared/items/switcher/item-type-switcher.component';
+import { TruncatePipe } from '../../../../shared/utils/truncate.pipe';
+import { ItemDataService } from '../../../../core/data/item-data.service';
+import { Item } from '../../../../core/shared/item.model';
+import { By } from '@angular/platform-browser';
+import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
+import { MockTranslateLoader } from '../../../../shared/mocks/mock-translate-loader';
+import { GenericItemPageFieldComponent } from '../../field-components/specific-field/generic/generic-item-page-field.component';
+import { RemoteData } from '../../../../core/data/remote-data';
+import { PaginatedList } from '../../../../core/data/paginated-list';
+import { PageInfo } from '../../../../core/shared/page-info.model';
+import { isNotEmpty } from '../../../../shared/empty.util';
+import { JournalComponent } from './journal.component';
+import { of as observableOf } from 'rxjs';
+
+let comp: JournalComponent;
+let fixture: ComponentFixture<JournalComponent>;
+
+const mockItem: Item = Object.assign(new Item(), {
+  bitstreams: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))),
+  metadata: {
+    'journal.identifier.issn': [
+      {
+        language: 'en_US',
+        value: '1234'
+      }
+    ],
+    'journal.publisher': [
+      {
+        language: 'en_US',
+        value: 'a publisher'
+      }
+    ],
+    'journal.identifier.description': [
+      {
+        language: 'en_US',
+        value: 'desc'
+      }
+    ]
+  }
+});
+
+describe('JournalComponent', () => {
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [TranslateModule.forRoot({
+        loader: {
+          provide: TranslateLoader,
+          useClass: MockTranslateLoader
+        }
+      })],
+      declarations: [JournalComponent, GenericItemPageFieldComponent, TruncatePipe],
+      providers: [
+        {provide: ITEM, useValue: mockItem},
+        {provide: ItemDataService, useValue: {}},
+        {provide: TruncatableService, useValue: {}}
+      ],
+
+      schemas: [NO_ERRORS_SCHEMA]
+    }).overrideComponent(JournalComponent, {
+      set: {changeDetection: ChangeDetectionStrategy.Default}
+    }).compileComponents();
+  }));
+
+  beforeEach(async(() => {
+    fixture = TestBed.createComponent(JournalComponent);
+    comp = fixture.componentInstance;
+    fixture.detectChanges();
+  }));
+
+  for (const key of Object.keys(mockItem.metadata)) {
+    it(`should be calling a component with metadata field ${key}`, () => {
+      const fields = fixture.debugElement.queryAll(By.css('.item-page-fields'));
+      expect(containsFieldInput(fields, key)).toBeTruthy();
+    });
+  }
+});
+
+function containsFieldInput(fields: DebugElement[], metadataKey: string): boolean {
+  for (const field of fields) {
+    const fieldComp = field.componentInstance;
+    if (isNotEmpty(fieldComp.fields)) {
+      if (fieldComp.fields.indexOf(metadataKey) > -1) {
+        return true;
+      }
+    }
+  }
+  return false;
+}
diff --git a/src/app/+item-page/simple/item-types/journal/journal.component.ts b/src/app/+item-page/simple/item-types/journal/journal.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0799f5c7365dc9af810f191b4807595039e68438
--- /dev/null
+++ b/src/app/+item-page/simple/item-types/journal/journal.component.ts
@@ -0,0 +1,42 @@
+import { Component, Inject } from '@angular/core';
+import { Observable } from 'rxjs';
+import { ItemDataService } from '../../../../core/data/item-data.service';
+import { Item } from '../../../../core/shared/item.model';
+import { ItemViewMode, rendersItemType } from '../../../../shared/items/item-type-decorator';
+import { ITEM } from '../../../../shared/items/switcher/item-type-switcher.component';
+import { isNotEmpty } from '../../../../shared/empty.util';
+import { ItemComponent } from '../shared/item.component';
+import { filterRelationsByTypeLabel, relationsToItems } from '../shared/item-relationships-utils';
+
+@rendersItemType('Journal', ItemViewMode.Full)
+@Component({
+  selector: 'ds-journal',
+  styleUrls: ['./journal.component.scss'],
+  templateUrl: './journal.component.html'
+})
+/**
+ * The component for displaying metadata and relations of an item of the type Journal
+ */
+export class JournalComponent extends ItemComponent {
+  /**
+   * The volumes related to this journal
+   */
+  volumes$: Observable<Item[]>;
+
+  constructor(
+    @Inject(ITEM) public item: Item,
+    private ids: ItemDataService
+  ) {
+    super(item);
+  }
+  ngOnInit(): void {
+    super.ngOnInit();
+
+    if (isNotEmpty(this.resolvedRelsAndTypes$)) {
+      this.volumes$ = this.resolvedRelsAndTypes$.pipe(
+        filterRelationsByTypeLabel('isVolumeOfJournal'),
+        relationsToItems(this.item.id, this.ids)
+      );
+    }
+  }
+}
diff --git a/src/app/+item-page/simple/item-types/orgunit/orgunit.component.html b/src/app/+item-page/simple/item-types/orgunit/orgunit.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..0446ac6861f88ca705d279b8e44187a921d148ef
--- /dev/null
+++ b/src/app/+item-page/simple/item-types/orgunit/orgunit.component.html
@@ -0,0 +1,49 @@
+<h2 class="item-page-title-field">
+  {{'orgunit.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="item?.allMetadata(['orgunit.identifier.name'])"></ds-metadata-values>
+</h2>
+<div class="row">
+  <div class="col-xs-12 col-md-4">
+    <ds-metadata-field-wrapper>
+      <ds-thumbnail [thumbnail]="this.item.getThumbnail() | async" [defaultImage]="'assets/images/orgunit-placeholder.svg'"></ds-thumbnail>
+    </ds-metadata-field-wrapper>
+    <ds-generic-item-page-field [item]="item"
+      [fields]="['orgunit.identifier.dateestablished']"
+      [label]="'orgunit.page.dateestablished'">
+    </ds-generic-item-page-field>
+    <ds-generic-item-page-field [item]="item"
+      [fields]="['orgunit.identifier.city']"
+      [label]="'orgunit.page.city'">
+    </ds-generic-item-page-field>
+    <ds-generic-item-page-field [item]="item"
+      [fields]="['orgunit.identifier.country']"
+      [label]="'orgunit.page.country'">
+    </ds-generic-item-page-field>
+    <ds-generic-item-page-field [item]="item"
+      [fields]="['orgunit.identifier.id']"
+      [label]="'orgunit.page.id'">
+    </ds-generic-item-page-field>
+  </div>
+  <div class="col-xs-12 col-md-6">
+    <ds-related-items
+      [items]="people$ | async"
+      [label]="'relationships.isPersonOf' | translate">
+    </ds-related-items>
+    <ds-related-items
+      [items]="projects$ | async"
+      [label]="'relationships.isProjectOf' | translate">
+    </ds-related-items>
+    <ds-related-items
+      [items]="publications$ | async"
+      [label]="'relationships.isPublicationOf' | translate">
+    </ds-related-items>
+    <ds-generic-item-page-field [item]="item"
+      [fields]="['orgunit.identifier.description']"
+      [label]="'orgunit.page.description'">
+    </ds-generic-item-page-field>
+    <div>
+      <a class="btn btn-outline-primary" [routerLink]="['/items/' + item.id + '/full']">
+        {{"item.page.link.full" | translate}}
+      </a>
+    </div>
+  </div>
+</div>
diff --git a/src/app/+item-page/simple/item-types/orgunit/orgunit.component.scss b/src/app/+item-page/simple/item-types/orgunit/orgunit.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..3575cae797d53c2f3bcd1dcb34f6b91b529db815
--- /dev/null
+++ b/src/app/+item-page/simple/item-types/orgunit/orgunit.component.scss
@@ -0,0 +1 @@
+@import '../../../../../styles/variables.scss';
diff --git a/src/app/+item-page/simple/item-types/orgunit/orgunit.component.spec.ts b/src/app/+item-page/simple/item-types/orgunit/orgunit.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fa5396fb3d653d988948713117bb16f61d9494b8
--- /dev/null
+++ b/src/app/+item-page/simple/item-types/orgunit/orgunit.component.spec.ts
@@ -0,0 +1,46 @@
+import { Item } from '../../../../core/shared/item.model';
+import { RemoteData } from '../../../../core/data/remote-data';
+import { PaginatedList } from '../../../../core/data/paginated-list';
+import { PageInfo } from '../../../../core/shared/page-info.model';
+import { createRelationshipsObservable, getItemPageFieldsTest } from '../shared/item.component.spec';
+import { OrgunitComponent } from './orgunit.component';
+import { of as observableOf } from 'rxjs';
+
+const mockItem: Item = Object.assign(new Item(), {
+  bitstreams: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))),
+  metadata: {
+    'orgunit.identifier.dateestablished': [
+      {
+        language: 'en_US',
+        value: '2018'
+      }
+    ],
+    'orgunit.identifier.city': [
+      {
+        language: 'en_US',
+        value: 'New York'
+      }
+    ],
+    'orgunit.identifier.country': [
+      {
+        language: 'en_US',
+        value: 'USA'
+      }
+    ],
+    'orgunit.identifier.id': [
+      {
+        language: 'en_US',
+        value: '1'
+      }
+    ],
+    'orgunit.identifier.description': [
+      {
+        language: 'en_US',
+        value: 'desc'
+      }
+    ]
+  },
+  relationships: createRelationshipsObservable()
+});
+
+describe('OrgUnitComponent', getItemPageFieldsTest(mockItem, OrgunitComponent));
diff --git a/src/app/+item-page/simple/item-types/orgunit/orgunit.component.ts b/src/app/+item-page/simple/item-types/orgunit/orgunit.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..96dc9a5960e90339cabdfdfe93a88591020b4ffe
--- /dev/null
+++ b/src/app/+item-page/simple/item-types/orgunit/orgunit.component.ts
@@ -0,0 +1,62 @@
+import { Component, Inject, OnInit } from '@angular/core';
+import { Observable } from 'rxjs';
+import { ItemDataService } from '../../../../core/data/item-data.service';
+import { Item } from '../../../../core/shared/item.model';
+import { ItemViewMode, rendersItemType } from '../../../../shared/items/item-type-decorator';
+import { ITEM } from '../../../../shared/items/switcher/item-type-switcher.component';
+import { isNotEmpty } from '../../../../shared/empty.util';
+import { ItemComponent } from '../shared/item.component';
+import { filterRelationsByTypeLabel, relationsToItems } from '../shared/item-relationships-utils';
+
+@rendersItemType('OrgUnit', ItemViewMode.Full)
+@Component({
+  selector: 'ds-orgunit',
+  styleUrls: ['./orgunit.component.scss'],
+  templateUrl: './orgunit.component.html'
+})
+/**
+ * The component for displaying metadata and relations of an item of the type Organisation Unit
+ */
+export class OrgunitComponent extends ItemComponent implements OnInit {
+  /**
+   * The people related to this organisation unit
+   */
+  people$: Observable<Item[]>;
+
+  /**
+   * The projects related to this organisation unit
+   */
+  projects$: Observable<Item[]>;
+
+  /**
+   * The publications related to this organisation unit
+   */
+  publications$: Observable<Item[]>;
+
+  constructor(
+    @Inject(ITEM) public item: Item,
+    private ids: ItemDataService
+  ) {
+    super(item);
+  }
+
+  ngOnInit(): void {
+    super.ngOnInit();
+
+    if (isNotEmpty(this.resolvedRelsAndTypes$)) {
+      this.people$ = this.resolvedRelsAndTypes$.pipe(
+        filterRelationsByTypeLabel('isPersonOfOrgUnit'),
+        relationsToItems(this.item.id, this.ids)
+      );
+
+      this.projects$ = this.resolvedRelsAndTypes$.pipe(
+        filterRelationsByTypeLabel('isProjectOfOrgUnit'),
+        relationsToItems(this.item.id, this.ids)
+      );
+
+      this.publications$ = this.resolvedRelsAndTypes$.pipe(
+        filterRelationsByTypeLabel('isPublicationOfOrgUnit'),
+        relationsToItems(this.item.id, this.ids)
+      );
+    }
+  }}
diff --git a/src/app/+item-page/simple/item-types/person/person.component.html b/src/app/+item-page/simple/item-types/person/person.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..88cd647645ce7a721f80dac60da5aad68efc5302
--- /dev/null
+++ b/src/app/+item-page/simple/item-types/person/person.component.html
@@ -0,0 +1,58 @@
+<h2 class="item-page-title-field">
+  {{'person.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="item?.allMetadata(['dc.contributor.author'])"></ds-metadata-values>
+</h2>
+<div class="row">
+  <div class="col-xs-12 col-md-4">
+    <ds-metadata-field-wrapper>
+      <ds-thumbnail [thumbnail]="this.item.getThumbnail() | async" [defaultImage]="'assets/images/person-placeholder.svg'"></ds-thumbnail>
+    </ds-metadata-field-wrapper>
+    <ds-generic-item-page-field [item]="item"
+      [fields]="['person.identifier.email']"
+      [label]="'person.page.email'">
+    </ds-generic-item-page-field>
+    <ds-generic-item-page-field [item]="item"
+      [fields]="['person.identifier.orcid']"
+      [label]="'person.page.orcid'">
+    </ds-generic-item-page-field>
+    <ds-generic-item-page-field [item]="item"
+      [fields]="['person.identifier.birthdate']"
+      [label]="'person.page.birthdate'">
+    </ds-generic-item-page-field>
+    <ds-generic-item-page-field [item]="item"
+      [fields]="['person.identifier.staffid']"
+      [label]="'person.page.staffid'">
+    </ds-generic-item-page-field>
+  </div>
+  <div class="col-xs-12 col-md-6">
+    <ds-related-items
+      [items]="projects$ | async"
+      [label]="'relationships.isProjectOf' | translate">
+    </ds-related-items>
+    <ds-related-items
+      [items]="orgUnits$ | async"
+      [label]="'relationships.isOrgUnitOf' | translate">
+    </ds-related-items>
+    <ds-generic-item-page-field [item]="item"
+      [fields]="['person.identifier.jobtitle']"
+      [label]="'person.page.jobtitle'">
+    </ds-generic-item-page-field>
+    <ds-generic-item-page-field [item]="item"
+      [fields]="['person.identifier.lastname']"
+      [label]="'person.page.lastname'">
+    </ds-generic-item-page-field>
+    <ds-generic-item-page-field [item]="item"
+      [fields]="['person.identifier.firstname']"
+      [label]="'person.page.firstname'">
+    </ds-generic-item-page-field>
+    <div>
+      <a class="btn btn-outline-primary" [routerLink]="['/items/' + item.id + '/full']">
+        {{"item.page.link.full" | translate}}
+      </a>
+    </div>
+  </div>
+  <div class="mt-5 w-100">
+    <ds-related-entities-search [item]="item"
+      [relationType]="'isAuthorOfPublication'">
+    </ds-related-entities-search>
+  </div>
+</div>
diff --git a/src/app/+item-page/simple/item-types/person/person.component.scss b/src/app/+item-page/simple/item-types/person/person.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..3575cae797d53c2f3bcd1dcb34f6b91b529db815
--- /dev/null
+++ b/src/app/+item-page/simple/item-types/person/person.component.scss
@@ -0,0 +1 @@
+@import '../../../../../styles/variables.scss';
diff --git a/src/app/+item-page/simple/item-types/person/person.component.spec.ts b/src/app/+item-page/simple/item-types/person/person.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..cf0d5c197db2f295d8139a00d6460587a9d4b8e3
--- /dev/null
+++ b/src/app/+item-page/simple/item-types/person/person.component.spec.ts
@@ -0,0 +1,58 @@
+import { Item } from '../../../../core/shared/item.model';
+import { RemoteData } from '../../../../core/data/remote-data';
+import { PaginatedList } from '../../../../core/data/paginated-list';
+import { PageInfo } from '../../../../core/shared/page-info.model';
+import { createRelationshipsObservable, getItemPageFieldsTest } from '../shared/item.component.spec';
+import { PersonComponent } from './person.component';
+import { of as observableOf } from 'rxjs';
+
+const mockItem: Item = Object.assign(new Item(), {
+  bitstreams: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))),
+  metadata: {
+    'person.identifier.email': [
+      {
+        language: 'en_US',
+        value: 'fake@email.com'
+      }
+    ],
+    'person.identifier.orcid': [
+      {
+        language: 'en_US',
+        value: 'ORCID-1'
+      }
+    ],
+    'person.identifier.birthdate': [
+      {
+        language: 'en_US',
+        value: '1993'
+      }
+    ],
+    'person.identifier.staffid': [
+      {
+        language: 'en_US',
+        value: '1'
+      }
+    ],
+    'person.identifier.jobtitle': [
+      {
+        language: 'en_US',
+        value: 'Developer'
+      }
+    ],
+    'person.identifier.lastname': [
+      {
+        language: 'en_US',
+        value: 'Doe'
+      }
+    ],
+    'person.identifier.firstname': [
+      {
+        language: 'en_US',
+        value: 'John'
+      }
+    ]
+  },
+  relationships: createRelationshipsObservable()
+});
+
+describe('PersonComponent', getItemPageFieldsTest(mockItem, PersonComponent));
diff --git a/src/app/+item-page/simple/item-types/person/person.component.ts b/src/app/+item-page/simple/item-types/person/person.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..67a2ae7a2e908e24265a98bebce80cd4cf7648fb
--- /dev/null
+++ b/src/app/+item-page/simple/item-types/person/person.component.ts
@@ -0,0 +1,77 @@
+import { Component, Inject } from '@angular/core';
+import { Observable ,  of as observableOf } from 'rxjs';
+import { ItemDataService } from '../../../../core/data/item-data.service';
+import { Item } from '../../../../core/shared/item.model';
+import { ItemViewMode, rendersItemType } from '../../../../shared/items/item-type-decorator';
+import { ITEM } from '../../../../shared/items/switcher/item-type-switcher.component';
+import { SearchFixedFilterService } from '../../../../+search-page/search-filters/search-filter/search-fixed-filter.service';
+import { isNotEmpty } from '../../../../shared/empty.util';
+import { ItemComponent } from '../shared/item.component';
+import { filterRelationsByTypeLabel, relationsToItems } from '../shared/item-relationships-utils';
+
+@rendersItemType('Person', ItemViewMode.Full)
+@Component({
+  selector: 'ds-person',
+  styleUrls: ['./person.component.scss'],
+  templateUrl: './person.component.html'
+})
+/**
+ * The component for displaying metadata and relations of an item of the type Person
+ */
+export class PersonComponent extends ItemComponent {
+  /**
+   * The publications related to this person
+   */
+  publications$: Observable<Item[]>;
+
+  /**
+   * The projects related to this person
+   */
+  projects$: Observable<Item[]>;
+
+  /**
+   * The organisation units related to this person
+   */
+  orgUnits$: Observable<Item[]>;
+
+  /**
+   * The applied fixed filter
+   */
+  fixedFilter$: Observable<string>;
+
+  /**
+   * The query used for applying the fixed filter
+   */
+  fixedFilterQuery: string;
+
+  constructor(
+    @Inject(ITEM) public item: Item,
+    private ids: ItemDataService,
+    private fixedFilterService: SearchFixedFilterService
+  ) {
+    super(item);
+  }
+  ngOnInit(): void {
+    super.ngOnInit();
+
+    if (isNotEmpty(this.resolvedRelsAndTypes$)) {
+      this.publications$ = this.resolvedRelsAndTypes$.pipe(
+        filterRelationsByTypeLabel('isPublicationOfAuthor'),
+        relationsToItems(this.item.id, this.ids)
+      );
+
+      this.projects$ = this.resolvedRelsAndTypes$.pipe(
+        filterRelationsByTypeLabel('isProjectOfPerson'),
+        relationsToItems(this.item.id, this.ids)
+      );
+
+      this.orgUnits$ = this.resolvedRelsAndTypes$.pipe(
+        filterRelationsByTypeLabel('isOrgUnitOfPerson'),
+        relationsToItems(this.item.id, this.ids)
+      );
+
+      this.fixedFilterQuery = this.fixedFilterService.getQueryByRelations('isAuthorOfPublication', this.item.id);
+      this.fixedFilter$ = observableOf('publication');
+    }
+  }
+}
diff --git a/src/app/+item-page/simple/item-types/project/project.component.html b/src/app/+item-page/simple/item-types/project/project.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..08e386182b22d385a1b1020e7e6a8b24d5a013bf
--- /dev/null
+++ b/src/app/+item-page/simple/item-types/project/project.component.html
@@ -0,0 +1,57 @@
+<h2 class="item-page-title-field">
+  {{'project.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="item?.allMetadata(['project.identifier.name'])"></ds-metadata-values>
+</h2>
+<div class="row">
+  <div class="col-xs-12 col-md-4">
+    <ds-metadata-field-wrapper>
+      <ds-thumbnail [thumbnail]="this.item.getThumbnail() | async" [defaultImage]="'assets/images/project-placeholder.svg'"></ds-thumbnail>
+    </ds-metadata-field-wrapper>
+    <ds-generic-item-page-field [item]="item"
+      [fields]="['project.identifier.status']"
+      [label]="'project.page.status'">
+    </ds-generic-item-page-field>
+    <ds-metadata-representation-list
+      [label]="'project.page.contributor' | translate"
+      [representations]="contributors$ | async">
+    </ds-metadata-representation-list>
+    <ds-generic-item-page-field [item]="item"
+      [fields]="['project.identifier.funder']"
+      [label]="'project.page.funder'">
+    </ds-generic-item-page-field>
+    <ds-generic-item-page-field [item]="item"
+      [fields]="['project.identifier.id']"
+      [label]="'project.page.id'">
+    </ds-generic-item-page-field>
+    <ds-generic-item-page-field [item]="item"
+      [fields]="['project.identifier.expectedcompletion']"
+      [label]="'project.page.expectedcompletion'">
+    </ds-generic-item-page-field>
+  </div>
+  <div class="col-xs-12 col-md-6">
+    <ds-related-items
+      [items]="people$ | async"
+      [label]="'relationships.isPersonOf' | translate">
+    </ds-related-items>
+    <ds-related-items
+      [items]="publications$ | async"
+      [label]="'relationships.isPublicationOf' | translate">
+    </ds-related-items>
+    <ds-related-items
+      [items]="orgUnits$ | async"
+      [label]="'relationships.isOrgUnitOf' | translate">
+    </ds-related-items>
+    <ds-generic-item-page-field [item]="item"
+      [fields]="['project.identifier.description']"
+      [label]="'project.page.description'">
+    </ds-generic-item-page-field>
+    <ds-generic-item-page-field [item]="item"
+      [fields]="['project.identifier.keyword']"
+      [label]="'project.page.keyword'">
+    </ds-generic-item-page-field>
+    <div>
+      <a class="btn btn-outline-primary" [routerLink]="['/items/' + item.id + '/full']">
+        {{"item.page.link.full" | translate}}
+      </a>
+    </div>
+  </div>
+</div>
diff --git a/src/app/+item-page/simple/item-types/project/project.component.scss b/src/app/+item-page/simple/item-types/project/project.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..3575cae797d53c2f3bcd1dcb34f6b91b529db815
--- /dev/null
+++ b/src/app/+item-page/simple/item-types/project/project.component.scss
@@ -0,0 +1 @@
+@import '../../../../../styles/variables.scss';
diff --git a/src/app/+item-page/simple/item-types/project/project.component.spec.ts b/src/app/+item-page/simple/item-types/project/project.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9b54ff9a412e6d2b177182144638bd17efb633a1
--- /dev/null
+++ b/src/app/+item-page/simple/item-types/project/project.component.spec.ts
@@ -0,0 +1,46 @@
+import { Item } from '../../../../core/shared/item.model';
+import { RemoteData } from '../../../../core/data/remote-data';
+import { PaginatedList } from '../../../../core/data/paginated-list';
+import { PageInfo } from '../../../../core/shared/page-info.model';
+import { createRelationshipsObservable, getItemPageFieldsTest } from '../shared/item.component.spec';
+import { ProjectComponent } from './project.component';
+import { of as observableOf } from 'rxjs';
+
+const mockItem: Item = Object.assign(new Item(), {
+  bitstreams: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))),
+  metadata: {
+    'project.identifier.status': [
+      {
+        language: 'en_US',
+        value: 'published'
+      }
+    ],
+    'project.identifier.id': [
+      {
+        language: 'en_US',
+        value: '1'
+      }
+    ],
+    'project.identifier.expectedcompletion': [
+      {
+        language: 'en_US',
+        value: 'exp comp'
+      }
+    ],
+    'project.identifier.description': [
+      {
+        language: 'en_US',
+        value: 'keyword'
+      }
+    ],
+    'project.identifier.keyword': [
+      {
+        language: 'en_US',
+        value: 'keyword'
+      }
+    ]
+  },
+  relationships: createRelationshipsObservable()
+});
+
+describe('ProjectComponent', getItemPageFieldsTest(mockItem, ProjectComponent));
diff --git a/src/app/+item-page/simple/item-types/project/project.component.ts b/src/app/+item-page/simple/item-types/project/project.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..eafef36307a5e99050f19a73106d1006dbeb5762
--- /dev/null
+++ b/src/app/+item-page/simple/item-types/project/project.component.ts
@@ -0,0 +1,71 @@
+import { Component, Inject, OnInit } from '@angular/core';
+import { Observable } from 'rxjs';
+import { ItemDataService } from '../../../../core/data/item-data.service';
+import { Item } from '../../../../core/shared/item.model';
+import { MetadataRepresentation } from '../../../../core/shared/metadata-representation/metadata-representation.model';
+import { ItemViewMode, rendersItemType } from '../../../../shared/items/item-type-decorator';
+import { ITEM } from '../../../../shared/items/switcher/item-type-switcher.component';
+import { isNotEmpty } from '../../../../shared/empty.util';
+import { ItemComponent } from '../shared/item.component';
+import { filterRelationsByTypeLabel, relationsToItems } from '../shared/item-relationships-utils';
+
+@rendersItemType('Project', ItemViewMode.Full)
+@Component({
+  selector: 'ds-project',
+  styleUrls: ['./project.component.scss'],
+  templateUrl: './project.component.html'
+})
+/**
+ * The component for displaying metadata and relations of an item of the type Project
+ */
+export class ProjectComponent extends ItemComponent implements OnInit {
+  /**
+   * The contributors related to this project
+   */
+  contributors$: Observable<MetadataRepresentation[]>;
+
+  /**
+   * The people related to this project
+   */
+  people$: Observable<Item[]>;
+
+  /**
+   * The publications related to this project
+   */
+  publications$: Observable<Item[]>;
+
+  /**
+   * The organisation units related to this project
+   */
+  orgUnits$: Observable<Item[]>;
+
+  constructor(
+    @Inject(ITEM) public item: Item,
+    private ids: ItemDataService
+  ) {
+    super(item);
+  }
+
+  ngOnInit(): void {
+    super.ngOnInit();
+
+    if (isNotEmpty(this.resolvedRelsAndTypes$)) {
+      this.contributors$ = this.buildRepresentations('OrgUnit', 'project.contributor.other', this.ids);
+
+      this.people$ = this.resolvedRelsAndTypes$.pipe(
+        filterRelationsByTypeLabel('isPersonOfProject'),
+        relationsToItems(this.item.id, this.ids)
+      );
+
+      this.publications$ = this.resolvedRelsAndTypes$.pipe(
+        filterRelationsByTypeLabel('isPublicationOfProject'),
+        relationsToItems(this.item.id, this.ids)
+      );
+
+      this.orgUnits$ = this.resolvedRelsAndTypes$.pipe(
+        filterRelationsByTypeLabel('isOrgUnitOfProject'),
+        relationsToItems(this.item.id, this.ids)
+      );
+    }
+  }
+}
diff --git a/src/app/+item-page/simple/item-types/publication/publication.component.html b/src/app/+item-page/simple/item-types/publication/publication.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..37135c6036b2545f999e33f42a5da524545ed62d
--- /dev/null
+++ b/src/app/+item-page/simple/item-types/publication/publication.component.html
@@ -0,0 +1,60 @@
+<h2 class="item-page-title-field">
+  {{'publication.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="item?.allMetadata(['dc.title'])"></ds-metadata-values>
+</h2>
+<div class="row">
+  <div class="col-xs-12 col-md-4">
+    <ds-metadata-field-wrapper>
+      <ds-thumbnail [thumbnail]="this.item.getThumbnail() | async"></ds-thumbnail>
+    </ds-metadata-field-wrapper>
+    <ds-item-page-file-section [item]="item"></ds-item-page-file-section>
+    <ds-item-page-date-field [item]="item"></ds-item-page-date-field>
+    <ds-item-page-author-field *ngIf="!(authors$ | async)" [item]="item"></ds-item-page-author-field>
+    <ds-generic-item-page-field [item]="item"
+      [fields]="['journal.title']"
+      [label]="'publication.page.journal-title'">
+    </ds-generic-item-page-field>
+    <ds-generic-item-page-field [item]="item"
+      [fields]="['journal.identifier.issn']"
+      [label]="'publication.page.journal-issn'">
+    </ds-generic-item-page-field>
+    <ds-generic-item-page-field [item]="item"
+      [fields]="['journalvolume.identifier.name']"
+      [label]="'publication.page.volume-title'">
+    </ds-generic-item-page-field>
+  </div>
+  <div class="col-xs-12 col-md-6">
+    <ds-metadata-representation-list
+      [label]="'relationships.isAuthorOf' | translate"
+      [representations]="authors$ | async">
+    </ds-metadata-representation-list>
+    <ds-related-items
+      [items]="projects$ | async"
+      [label]="'relationships.isProjectOf' | translate">
+    </ds-related-items>
+    <ds-related-items
+      [items]="orgUnits$ | async"
+      [label]="'relationships.isOrgUnitOf' | translate">
+    </ds-related-items>
+    <ds-related-items
+      [items]="journalIssues$ | async"
+      [label]="'relationships.isJournalIssueOf' | translate">
+    </ds-related-items>
+    <ds-item-page-abstract-field [item]="item"></ds-item-page-abstract-field>
+    <ds-generic-item-page-field [item]="item"
+      [fields]="['dc.subject']"
+      [separator]="','"
+      [label]="'item.page.subject'">
+    </ds-generic-item-page-field>
+    <ds-generic-item-page-field [item]="item"
+      [fields]="['dc.identifier.citation']"
+      [label]="'item.page.citation'">
+    </ds-generic-item-page-field>    
+    <ds-item-page-uri-field [item]="item"></ds-item-page-uri-field>
+    <ds-item-page-collections [item]="item"></ds-item-page-collections>
+    <div>
+      <a class="btn btn-outline-primary" [routerLink]="['/items/' + item.id + '/full']">
+        {{"item.page.link.full" | translate}}
+      </a>
+    </div>
+  </div>
+</div>
diff --git a/src/app/+item-page/simple/item-types/publication/publication.component.scss b/src/app/+item-page/simple/item-types/publication/publication.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..3575cae797d53c2f3bcd1dcb34f6b91b529db815
--- /dev/null
+++ b/src/app/+item-page/simple/item-types/publication/publication.component.scss
@@ -0,0 +1 @@
+@import '../../../../../styles/variables.scss';
diff --git a/src/app/+item-page/simple/item-types/publication/publication.component.spec.ts b/src/app/+item-page/simple/item-types/publication/publication.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..48a7a05f459cbef454c0829ce5fd2e457c80aad0
--- /dev/null
+++ b/src/app/+item-page/simple/item-types/publication/publication.component.spec.ts
@@ -0,0 +1,90 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
+import { MockTranslateLoader } from '../../../../shared/mocks/mock-translate-loader';
+import { GenericItemPageFieldComponent } from '../../field-components/specific-field/generic/generic-item-page-field.component';
+import { TruncatePipe } from '../../../../shared/utils/truncate.pipe';
+import { ITEM } from '../../../../shared/items/switcher/item-type-switcher.component';
+import { ItemDataService } from '../../../../core/data/item-data.service';
+import { SearchFixedFilterService } from '../../../../+search-page/search-filters/search-filter/search-fixed-filter.service';
+import { TruncatableService } from '../../../../shared/truncatable/truncatable.service';
+import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
+import { Item } from '../../../../core/shared/item.model';
+import { RemoteData } from '../../../../core/data/remote-data';
+import { PaginatedList } from '../../../../core/data/paginated-list';
+import { PageInfo } from '../../../../core/shared/page-info.model';
+import { By } from '@angular/platform-browser';
+import { createRelationshipsObservable } from '../shared/item.component.spec';
+import { PublicationComponent } from './publication.component';
+import { of as observableOf } from 'rxjs';
+import { MetadataMap } from '../../../../core/shared/metadata.models';
+
+const mockItem: Item = Object.assign(new Item(), {
+  bitstreams: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))),
+  metadata: new MetadataMap(),
+  relationships: createRelationshipsObservable()
+});
+
+describe('PublicationComponent', () => {
+  let comp: PublicationComponent;
+  let fixture: ComponentFixture<PublicationComponent>;
+
+  const searchFixedFilterServiceStub = {
+    /* tslint:disable:no-empty */
+    getQueryByRelations: () => {}
+    /* tslint:enable:no-empty */
+  };
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [TranslateModule.forRoot({
+        loader: {
+          provide: TranslateLoader,
+          useClass: MockTranslateLoader
+        }
+      })],
+      declarations: [PublicationComponent, GenericItemPageFieldComponent, TruncatePipe],
+      providers: [
+        {provide: ITEM, useValue: mockItem},
+        {provide: ItemDataService, useValue: {}},
+        {provide: SearchFixedFilterService, useValue: searchFixedFilterServiceStub},
+        {provide: TruncatableService, useValue: {}}
+      ],
+
+      schemas: [NO_ERRORS_SCHEMA]
+    }).overrideComponent(PublicationComponent, {
+      set: {changeDetection: ChangeDetectionStrategy.Default}
+    }).compileComponents();
+  }));
+
+  beforeEach(async(() => {
+    fixture = TestBed.createComponent(PublicationComponent);
+    comp = fixture.componentInstance;
+    fixture.detectChanges();
+  }));
+
+  it('should contain a component to display the date', () => {
+    const fields = fixture.debugElement.queryAll(By.css('ds-item-page-date-field'));
+    expect(fields.length).toBeGreaterThanOrEqual(1);
+  });
+
+  it('should contain a component to display the author', () => {
+    const fields = fixture.debugElement.queryAll(By.css('ds-item-page-author-field'));
+    expect(fields.length).toBeGreaterThanOrEqual(1);
+  });
+
+  it('should contain a component to display the abstract', () => {
+    const fields = fixture.debugElement.queryAll(By.css('ds-item-page-abstract-field'));
+    expect(fields.length).toBeGreaterThanOrEqual(1);
+  });
+
+  it('should contain a component to display the uri', () => {
+    const fields = fixture.debugElement.queryAll(By.css('ds-item-page-uri-field'));
+    expect(fields.length).toBeGreaterThanOrEqual(1);
+  });
+
+  it('should contain a component to display the collections', () => {
+    const fields = fixture.debugElement.queryAll(By.css('ds-item-page-collections'));
+    expect(fields.length).toBeGreaterThanOrEqual(1);
+  });
+
+});
diff --git a/src/app/+item-page/simple/item-types/publication/publication.component.ts b/src/app/+item-page/simple/item-types/publication/publication.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8798b3c1cf78bf752a46cd7474b74e18a2d4a6fd
--- /dev/null
+++ b/src/app/+item-page/simple/item-types/publication/publication.component.ts
@@ -0,0 +1,74 @@
+import { ChangeDetectionStrategy, Component, Inject, OnInit } from '@angular/core';
+import { Observable } from 'rxjs';
+import { ItemDataService } from '../../../../core/data/item-data.service';
+import { Item } from '../../../../core/shared/item.model';
+import {
+  DEFAULT_ITEM_TYPE, ItemViewMode,
+  rendersItemType
+} from '../../../../shared/items/item-type-decorator';
+import { ITEM } from '../../../../shared/items/switcher/item-type-switcher.component';
+import { ItemComponent } from '../shared/item.component';
+import { MetadataRepresentation } from '../../../../core/shared/metadata-representation/metadata-representation.model';
+import { filterRelationsByTypeLabel, relationsToItems } from '../shared/item-relationships-utils';
+
+@rendersItemType('Publication', ItemViewMode.Full)
+@rendersItemType(DEFAULT_ITEM_TYPE, ItemViewMode.Full)
+@Component({
+  selector: 'ds-publication',
+  styleUrls: ['./publication.component.scss'],
+  templateUrl: './publication.component.html',
+  changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class PublicationComponent extends ItemComponent implements OnInit {
+  /**
+   * The authors related to this publication
+   */
+  authors$: Observable<MetadataRepresentation[]>;
+
+  /**
+   * The projects related to this publication
+   */
+  projects$: Observable<Item[]>;
+
+  /**
+   * The organisation units related to this publication
+   */
+  orgUnits$: Observable<Item[]>;
+
+  /**
+   * The journal issues related to this publication
+   */
+  journalIssues$: Observable<Item[]>;
+
+  constructor(
+    @Inject(ITEM) public item: Item,
+    private ids: ItemDataService
+  ) {
+    super(item);
+  }
+
+  ngOnInit(): void {
+    super.ngOnInit();
+
+    if (this.resolvedRelsAndTypes$) {
+
+      this.authors$ = this.buildRepresentations('Person', 'dc.contributor.author', this.ids);
+
+      this.projects$ = this.resolvedRelsAndTypes$.pipe(
+        filterRelationsByTypeLabel('isProjectOfPublication'),
+        relationsToItems(this.item.id, this.ids)
+      );
+
+      this.orgUnits$ = this.resolvedRelsAndTypes$.pipe(
+        filterRelationsByTypeLabel('isOrgUnitOfPublication'),
+        relationsToItems(this.item.id, this.ids)
+      );
+
+      this.journalIssues$ = this.resolvedRelsAndTypes$.pipe(
+        filterRelationsByTypeLabel('isJournalIssueOfPublication'),
+        relationsToItems(this.item.id, this.ids)
+      );
+
+    }
+  }
+}
diff --git a/src/app/+item-page/simple/item-types/shared/item-relationships-utils.ts b/src/app/+item-page/simple/item-types/shared/item-relationships-utils.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7c632a93657f44dca3ec2624c4a0fafa802d2e89
--- /dev/null
+++ b/src/app/+item-page/simple/item-types/shared/item-relationships-utils.ts
@@ -0,0 +1,121 @@
+import { ItemMetadataRepresentation } from '../../../../core/shared/metadata-representation/item/item-metadata-representation.model';
+import { MetadataRepresentation } from '../../../../core/shared/metadata-representation/metadata-representation.model';
+import { MetadatumRepresentation } from '../../../../core/shared/metadata-representation/metadatum/metadatum-representation.model';
+import { MetadataValue } from '../../../../core/shared/metadata.models';
+import { getSucceededRemoteData } from '../../../../core/shared/operators';
+import { hasValue } from '../../../../shared/empty.util';
+import { Observable } from 'rxjs/internal/Observable';
+import { Relationship } from '../../../../core/shared/item-relationships/relationship.model';
+import { RelationshipType } from '../../../../core/shared/item-relationships/relationship-type.model';
+import { distinctUntilChanged, flatMap, map } from 'rxjs/operators';
+import { of as observableOf, zip as observableZip } from 'rxjs';
+import { ItemDataService } from '../../../../core/data/item-data.service';
+import { Item } from '../../../../core/shared/item.model';
+import { RemoteData } from '../../../../core/data/remote-data';
+
+/**
+ * Operator for comparing arrays using a mapping function
+ * The mapping function should turn the source array into an array of basic types, so that the array can
+ * be compared using these basic types.
+ * For example: "(o) => o.id" will compare the two arrays by comparing their content by id.
+ * @param mapFn   Function for mapping the arrays
+ */
+export const compareArraysUsing = <T>(mapFn: (t: T) => any) =>
+  (a: T[], b: T[]): boolean => {
+    if (!Array.isArray(a) || ! Array.isArray(b)) {
+      return false
+    }
+
+    const aIds = a.map(mapFn);
+    const bIds = b.map(mapFn);
+
+    return aIds.length === bIds.length &&
+      aIds.every((e) => bIds.includes(e)) &&
+      bIds.every((e) => aIds.includes(e));
+  };
+
+/**
+ * Operator for comparing arrays using the object's ids
+ */
+export const compareArraysUsingIds = <T extends { id: string }>() =>
+  compareArraysUsing((t: T) => hasValue(t) ? t.id : undefined);
+
+/**
+ * Fetch the relationships which match the type label given
+ * @param {string} label      Type label
+ * @returns {(source: Observable<[Relationship[] , RelationshipType[]]>) => Observable<Relationship[]>}
+ */
+export const filterRelationsByTypeLabel = (label: string) =>
+  (source: Observable<[Relationship[], RelationshipType[]]>): Observable<Relationship[]> =>
+    source.pipe(
+      map(([relsCurrentPage, relTypesCurrentPage]) =>
+        relsCurrentPage.filter((rel: Relationship, idx: number) =>
+          hasValue(relTypesCurrentPage[idx]) && (relTypesCurrentPage[idx].leftLabel === label ||
+          relTypesCurrentPage[idx].rightLabel === label)
+        )
+      ),
+      distinctUntilChanged(compareArraysUsingIds())
+    );
+
+/**
+ * Operator for turning a list of relationships into a list of the relevant items
+ * @param {string} thisId           The item's id of which the relations belong to
+ * @param {ItemDataService} ids     The ItemDataService to fetch items from the REST API
+ * @returns {(source: Observable<Relationship[]>) => Observable<Item[]>}
+ */
+export const relationsToItems = (thisId: string, ids: ItemDataService) =>
+  (source: Observable<Relationship[]>): Observable<Item[]> =>
+    source.pipe(
+      flatMap((rels: Relationship[]) =>
+        observableZip(
+          ...rels.map((rel: Relationship) => {
+            let queryId = rel.leftId;
+            if (rel.leftId === thisId) {
+              queryId = rel.rightId;
+            }
+            return ids.findById(queryId);
+          })
+        )
+      ),
+      map((arr: Array<RemoteData<Item>>) =>
+        arr
+          .filter((d: RemoteData<Item>) => d.hasSucceeded)
+          .map((d: RemoteData<Item>) => d.payload)),
+      distinctUntilChanged(compareArraysUsingIds()),
+    );
+
+/**
+ * Operator for turning a list of relationships into a list of metadatarepresentations given the original metadata
+ * @param parentId    The id of the parent item
+ * @param itemType    The type of relation this list resembles (for creating representations)
+ * @param metadata    The list of original Metadatum objects
+ * @param ids         The ItemDataService to use for fetching Items from the Rest API
+ */
+export const relationsToRepresentations = (parentId: string, itemType: string, metadata: MetadataValue[], ids: ItemDataService) =>
+  (source: Observable<Relationship[]>): Observable<MetadataRepresentation[]> =>
+    source.pipe(
+      flatMap((rels: Relationship[]) =>
+        observableZip(
+          ...metadata
+            .map((metadatum: any) => Object.assign(new MetadataValue(), metadatum))
+            .map((metadatum: MetadataValue) => {
+              if (metadatum.isVirtual) {
+                const matchingRels = rels.filter((rel: Relationship) => ('' + rel.id) === metadatum.virtualValue);
+                if (matchingRels.length > 0) {
+                  const matchingRel = matchingRels[0];
+                  let queryId = matchingRel.leftId;
+                  if (matchingRel.leftId === parentId) {
+                    queryId = matchingRel.rightId;
+                  }
+                  return ids.findById(queryId).pipe(
+                    getSucceededRemoteData(),
+                    map((d: RemoteData<Item>) => Object.assign(new ItemMetadataRepresentation(), d.payload))
+                  );
+                }
+              } else {
+                return observableOf(Object.assign(new MetadatumRepresentation(itemType), metadatum));
+              }
+            })
+        )
+      )
+    );
diff --git a/src/app/+item-page/simple/item-types/shared/item.component.spec.ts b/src/app/+item-page/simple/item-types/shared/item.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a6b4dd801df07c7921803952984471b82d55d087
--- /dev/null
+++ b/src/app/+item-page/simple/item-types/shared/item.component.spec.ts
@@ -0,0 +1,428 @@
+import { Item } from '../../../../core/shared/item.model';
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { By } from '@angular/platform-browser';
+import { GenericItemPageFieldComponent } from '../../field-components/specific-field/generic/generic-item-page-field.component';
+import { TruncatableService } from '../../../../shared/truncatable/truncatable.service';
+import { ItemDataService } from '../../../../core/data/item-data.service';
+import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
+import { MockTranslateLoader } from '../../../../shared/mocks/mock-translate-loader';
+import { ChangeDetectionStrategy, DebugElement, NO_ERRORS_SCHEMA } from '@angular/core';
+import { ITEM } from '../../../../shared/items/switcher/item-type-switcher.component';
+import { TruncatePipe } from '../../../../shared/utils/truncate.pipe';
+import { isNotEmpty } from '../../../../shared/empty.util';
+import { SearchFixedFilterService } from '../../../../+search-page/search-filters/search-filter/search-fixed-filter.service';
+import { RelationshipType } from '../../../../core/shared/item-relationships/relationship-type.model';
+import { PaginatedList } from '../../../../core/data/paginated-list';
+import { RemoteData } from '../../../../core/data/remote-data';
+import { Relationship } from '../../../../core/shared/item-relationships/relationship.model';
+import { PageInfo } from '../../../../core/shared/page-info.model';
+import { ItemComponent } from './item.component';
+import { of as observableOf } from 'rxjs';
+import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+import { VarDirective } from '../../../../shared/utils/var.directive';
+import { Observable } from 'rxjs/internal/Observable';
+import { MetadataRepresentation } from '../../../../core/shared/metadata-representation/metadata-representation.model';
+import { MetadatumRepresentation } from '../../../../core/shared/metadata-representation/metadatum/metadatum-representation.model';
+import { ItemMetadataRepresentation } from '../../../../core/shared/metadata-representation/item/item-metadata-representation.model';
+import { MetadataMap, MetadataValue } from '../../../../core/shared/metadata.models';
+import { compareArraysUsing, compareArraysUsingIds } from './item-relationships-utils';
+
+/**
+ * Create a generic test for an item-page-fields component using a mockItem and the type of component
+ * @param {Item} mockItem     The item to use for testing. The item needs to contain just the metadata necessary to
+ *                            execute the tests for it's component.
+ * @param component           The type of component to create test cases for.
+ * @returns {() => void}      Returns a specDefinition for the test.
+ */
+export function getItemPageFieldsTest(mockItem: Item, component) {
+  return () => {
+    let comp: any;
+    let fixture: ComponentFixture<any>;
+
+    const searchFixedFilterServiceStub = {
+      /* tslint:disable:no-empty */
+      getQueryByRelations: () => {}
+      /* tslint:enable:no-empty */
+    };
+
+    beforeEach(async(() => {
+      TestBed.configureTestingModule({
+        imports: [TranslateModule.forRoot({
+          loader: {
+            provide: TranslateLoader,
+            useClass: MockTranslateLoader
+          }
+        })],
+        declarations: [component, GenericItemPageFieldComponent, TruncatePipe],
+        providers: [
+          {provide: ITEM, useValue: mockItem},
+          {provide: ItemDataService, useValue: {}},
+          {provide: SearchFixedFilterService, useValue: searchFixedFilterServiceStub},
+          {provide: TruncatableService, useValue: {}}
+        ],
+
+        schemas: [NO_ERRORS_SCHEMA]
+      }).overrideComponent(component, {
+        set: {changeDetection: ChangeDetectionStrategy.Default}
+      }).compileComponents();
+    }));
+
+    beforeEach(async(() => {
+      fixture = TestBed.createComponent(component);
+      comp = fixture.componentInstance;
+      fixture.detectChanges();
+    }));
+
+    for (const key of Object.keys(mockItem.metadata)) {
+      it(`should be calling a component with metadata field ${key}`, () => {
+        const fields = fixture.debugElement.queryAll(By.css('ds-generic-item-page-field'));
+        expect(containsFieldInput(fields, key)).toBeTruthy();
+      });
+    }
+  }
+}
+
+/**
+ * Checks whether in a list of debug elements, at least one of them contains a specific metadata key in their
+ * fields property.
+ * @param {DebugElement[]} fields   List of debug elements to check
+ * @param {string} metadataKey      A metadata key to look for
+ * @returns {boolean}
+ */
+export function containsFieldInput(fields: DebugElement[], metadataKey: string): boolean {
+  for (const field of fields) {
+    const fieldComp = field.componentInstance;
+    if (isNotEmpty(fieldComp.fields)) {
+      if (fieldComp.fields.indexOf(metadataKey) > -1) {
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
+export function createRelationshipsObservable() {
+  return observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), [
+    Object.assign(new Relationship(), {
+      relationshipType: observableOf(new RemoteData(false, false, true, null, new RelationshipType()))
+    })
+  ])));
+}
+describe('ItemComponent', () => {
+  const arr1 = [
+    {
+      id: 1,
+      name: 'test'
+    },
+    {
+      id: 2,
+      name: 'another test'
+    },
+    {
+      id: 3,
+      name: 'one last test'
+    }
+  ];
+  const arrWithWrongId = [
+    {
+      id: 1,
+      name: 'test'
+    },
+    {
+      id: 5,  // Wrong id on purpose
+      name: 'another test'
+    },
+    {
+      id: 3,
+      name: 'one last test'
+    }
+  ];
+  const arrWithWrongName = [
+    {
+      id: 1,
+      name: 'test'
+    },
+    {
+      id: 2,
+      name: 'wrong test'  // Wrong name on purpose
+    },
+    {
+      id: 3,
+      name: 'one last test'
+    }
+  ];
+  const arrWithDifferentOrder = [arr1[0], arr1[2], arr1[1]];
+  const arrWithOneMore = [...arr1, {
+    id: 4,
+    name: 'fourth test'
+  }];
+  const arrWithAddedProperties = [
+    {
+      id: 1,
+      name: 'test',
+      extra: 'extra property'
+    },
+    {
+      id: 2,
+      name: 'another test',
+      extra: 'extra property'
+    },
+    {
+      id: 3,
+      name: 'one last test',
+      extra: 'extra property'
+    }
+  ];
+  const arrOfPrimitiveTypes = [1, 2, 3, 4];
+  const arrOfPrimitiveTypesWithOneWrong = [1, 5, 3, 4];
+  const arrOfPrimitiveTypesWithDifferentOrder = [1, 3, 2, 4];
+  const arrOfPrimitiveTypesWithOneMore = [1, 2, 3, 4, 5];
+
+  describe('when calling compareArraysUsing', () => {
+
+    describe('and comparing by id', () => {
+      const compare = compareArraysUsing<any>((o) => o.id);
+
+      it('should return true when comparing the same array', () => {
+        expect(compare(arr1, arr1)).toBeTruthy();
+      });
+
+      it('should return true regardless of the order', () => {
+        expect(compare(arr1, arrWithDifferentOrder)).toBeTruthy();
+      });
+
+      it('should return true regardless of other properties being different', () => {
+        expect(compare(arr1, arrWithWrongName)).toBeTruthy();
+      });
+
+      it('should return true regardless of extra properties', () => {
+        expect(compare(arr1, arrWithAddedProperties)).toBeTruthy();
+      });
+
+      it('should return false when the ids don\'t match', () => {
+        expect(compare(arr1, arrWithWrongId)).toBeFalsy();
+      });
+
+      it('should return false when the sizes don\'t match', () => {
+        expect(compare(arr1, arrWithOneMore)).toBeFalsy();
+      });
+    });
+
+    describe('and comparing by name', () => {
+      const compare = compareArraysUsing<any>((o) => o.name);
+
+      it('should return true when comparing the same array', () => {
+        expect(compare(arr1, arr1)).toBeTruthy();
+      });
+
+      it('should return true regardless of the order', () => {
+        expect(compare(arr1, arrWithDifferentOrder)).toBeTruthy();
+      });
+
+      it('should return true regardless of other properties being different', () => {
+        expect(compare(arr1, arrWithWrongId)).toBeTruthy();
+      });
+
+      it('should return true regardless of extra properties', () => {
+        expect(compare(arr1, arrWithAddedProperties)).toBeTruthy();
+      });
+
+      it('should return false when the names don\'t match', () => {
+        expect(compare(arr1, arrWithWrongName)).toBeFalsy();
+      });
+
+      it('should return false when the sizes don\'t match', () => {
+        expect(compare(arr1, arrWithOneMore)).toBeFalsy();
+      });
+    });
+
+    describe('and comparing by full objects', () => {
+      const compare = compareArraysUsing<any>((o) => o);
+
+      it('should return true when comparing the same array', () => {
+        expect(compare(arr1, arr1)).toBeTruthy();
+      });
+
+      it('should return true regardless of the order', () => {
+        expect(compare(arr1, arrWithDifferentOrder)).toBeTruthy();
+      });
+
+      it('should return false when extra properties are added', () => {
+        expect(compare(arr1, arrWithAddedProperties)).toBeFalsy();
+      });
+
+      it('should return false when the ids don\'t match', () => {
+        expect(compare(arr1, arrWithWrongId)).toBeFalsy();
+      });
+
+      it('should return false when the names don\'t match', () => {
+        expect(compare(arr1, arrWithWrongName)).toBeFalsy();
+      });
+
+      it('should return false when the sizes don\'t match', () => {
+        expect(compare(arr1, arrWithOneMore)).toBeFalsy();
+      });
+    });
+
+    describe('and comparing with primitive objects as source', () => {
+      const compare = compareArraysUsing<any>((o) => o);
+
+      it('should return true when comparing the same array', () => {
+        expect(compare(arrOfPrimitiveTypes, arrOfPrimitiveTypes)).toBeTruthy();
+      });
+
+      it('should return true regardless of the order', () => {
+        expect(compare(arrOfPrimitiveTypes, arrOfPrimitiveTypesWithDifferentOrder)).toBeTruthy();
+      });
+
+      it('should return false when at least one is wrong', () => {
+        expect(compare(arrOfPrimitiveTypes, arrOfPrimitiveTypesWithOneWrong)).toBeFalsy();
+      });
+
+      it('should return false when the sizes don\'t match', () => {
+        expect(compare(arrOfPrimitiveTypes, arrOfPrimitiveTypesWithOneMore)).toBeFalsy();
+      });
+    });
+
+  });
+
+  describe('when calling compareArraysUsingIds', () => {
+    const compare = compareArraysUsingIds();
+
+    it('should return true when comparing the same array', () => {
+      expect(compare(arr1 as any, arr1 as any)).toBeTruthy();
+    });
+
+    it('should return true regardless of the order', () => {
+      expect(compare(arr1 as any, arrWithDifferentOrder as any)).toBeTruthy();
+    });
+
+    it('should return true regardless of other properties being different', () => {
+      expect(compare(arr1 as any, arrWithWrongName as any)).toBeTruthy();
+    });
+
+    it('should return true regardless of extra properties', () => {
+      expect(compare(arr1 as any, arrWithAddedProperties as any)).toBeTruthy();
+    });
+
+    it('should return false when the ids don\'t match', () => {
+      expect(compare(arr1 as any, arrWithWrongId as any)).toBeFalsy();
+    });
+
+    it('should return false when the sizes don\'t match', () => {
+      expect(compare(arr1 as any, arrWithOneMore as any)).toBeFalsy();
+    });
+  });
+
+  describe('when calling buildRepresentations', () => {
+    let comp: ItemComponent;
+    let fixture: ComponentFixture<ItemComponent>;
+
+    const metadataField = 'dc.contributor.author';
+    const mockItem = Object.assign(new Item(), {
+      id: '1',
+      uuid: '1',
+      metadata: new MetadataMap(),
+      relationships: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), [
+        Object.assign(new Relationship(), {
+          uuid: '123',
+          id: '123',
+          leftId: '1',
+          rightId: '2',
+          relationshipType: observableOf(new RemoteData(false, false, true, null, new RelationshipType()))
+        })
+      ])))
+    });
+    mockItem.metadata[metadataField] = [
+      {
+        value: 'Second value',
+        place: 1
+      },
+      {
+        value: 'Third value',
+        place: 2,
+        authority: 'virtual::123'
+      },
+      {
+        value: 'First value',
+        place: 0
+      },
+      {
+        value: 'Fourth value',
+        place: 3,
+        authority: '123'
+      }
+    ] as MetadataValue[];
+    const relatedItem = Object.assign(new Item(), {
+      id: '2',
+      metadata: Object.assign(new MetadataMap(), {
+        'dc.title': [
+          {
+            language: 'en_US',
+            value: 'related item'
+          }
+        ]
+      })
+    });
+    const mockItemDataService = Object.assign({
+      findById: (id) => {
+        if (id === relatedItem.id) {
+          return observableOf(new RemoteData(false, false, true, null, relatedItem))
+        }
+      }
+    }) as ItemDataService;
+
+    let representations: Observable<MetadataRepresentation[]>;
+
+    beforeEach(async(() => {
+      TestBed.configureTestingModule({
+        imports: [TranslateModule.forRoot({
+          loader: {
+            provide: TranslateLoader,
+            useClass: MockTranslateLoader
+          }
+        }), BrowserAnimationsModule],
+        declarations: [ItemComponent, VarDirective],
+        providers: [
+          {provide: ITEM, useValue: mockItem}
+        ],
+
+        schemas: [NO_ERRORS_SCHEMA]
+      }).overrideComponent(ItemComponent, {
+        set: {changeDetection: ChangeDetectionStrategy.Default}
+      }).compileComponents();
+    }));
+
+    beforeEach(async(() => {
+      fixture = TestBed.createComponent(ItemComponent);
+      comp = fixture.componentInstance;
+      fixture.detectChanges();
+      representations = comp.buildRepresentations('bogus', metadataField, mockItemDataService);
+    }));
+
+    it('should contain exactly 4 metadata-representations', () => {
+      representations.subscribe((reps: MetadataRepresentation[]) => {
+        expect(reps.length).toEqual(4);
+      });
+    });
+
+    it('should have all the representations in the correct order', () => {
+      representations.subscribe((reps: MetadataRepresentation[]) => {
+        expect(reps[0].getValue()).toEqual('First value');
+        expect(reps[1].getValue()).toEqual('Second value');
+        expect(reps[2].getValue()).toEqual('related item');
+        expect(reps[3].getValue()).toEqual('Fourth value');
+      });
+    });
+
+    it('should have created the correct MetadatumRepresentation and ItemMetadataRepresentation objects for the correct Metadata', () => {
+      representations.subscribe((reps: MetadataRepresentation[]) => {
+        expect(reps[0] instanceof MetadatumRepresentation).toEqual(true);
+        expect(reps[1] instanceof MetadatumRepresentation).toEqual(true);
+        expect(reps[2] instanceof ItemMetadataRepresentation).toEqual(true);
+        expect(reps[3] instanceof MetadatumRepresentation).toEqual(true);
+      });
+    });
+  })
+
+});
diff --git a/src/app/+item-page/simple/item-types/shared/item.component.ts b/src/app/+item-page/simple/item-types/shared/item.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c6d43aa6b3a2579bd6e5064fb5b3fa5994d4503f
--- /dev/null
+++ b/src/app/+item-page/simple/item-types/shared/item.component.ts
@@ -0,0 +1,79 @@
+import { Component, Inject, OnInit } from '@angular/core';
+import { combineLatest as observableCombineLatest, Observable, zip as observableZip } from 'rxjs';
+import { distinctUntilChanged, filter, flatMap, map } from 'rxjs/operators';
+import { ItemDataService } from '../../../../core/data/item-data.service';
+import { PaginatedList } from '../../../../core/data/paginated-list';
+import { RemoteData } from '../../../../core/data/remote-data';
+import { RelationshipType } from '../../../../core/shared/item-relationships/relationship-type.model';
+import { Relationship } from '../../../../core/shared/item-relationships/relationship.model';
+import { Item } from '../../../../core/shared/item.model';
+import { MetadataRepresentation } from '../../../../core/shared/metadata-representation/metadata-representation.model';
+import { getRemoteDataPayload, getSucceededRemoteData } from '../../../../core/shared/operators';
+import { ITEM } from '../../../../shared/items/switcher/item-type-switcher.component';
+import { compareArraysUsingIds, relationsToRepresentations } from './item-relationships-utils';
+
+@Component({
+  selector: 'ds-item',
+  template: ''
+})
+/**
+ * A generic component for displaying metadata and relations of an item
+ */
+export class ItemComponent implements OnInit {
+  /**
+   * Resolved relationships and types together in one observable
+   */
+  resolvedRelsAndTypes$: Observable<[Relationship[], RelationshipType[]]>;
+
+  constructor(
+    @Inject(ITEM) public item: Item
+  ) {}
+
+  ngOnInit(): void {
+    const relationships$ = this.item.relationships;
+    if (relationships$) {
+      const relsCurrentPage$ = relationships$.pipe(
+        filter((rd: RemoteData<PaginatedList<Relationship>>) => rd.hasSucceeded),
+        getRemoteDataPayload(),
+        map((pl: PaginatedList<Relationship>) => pl.page),
+        distinctUntilChanged(compareArraysUsingIds())
+      );
+
+      const relTypesCurrentPage$ = relsCurrentPage$.pipe(
+        flatMap((rels: Relationship[]) =>
+          observableZip(...rels.map((rel: Relationship) => rel.relationshipType)).pipe(
+            map(([...arr]: Array<RemoteData<RelationshipType>>) => arr.map((d: RemoteData<RelationshipType>) => d.payload))
+          )
+        ),
+        distinctUntilChanged(compareArraysUsingIds())
+      );
+
+      this.resolvedRelsAndTypes$ = observableCombineLatest(
+        relsCurrentPage$,
+        relTypesCurrentPage$
+      );
+    }
+  }
+
+  /**
+   * Build a list of MetadataRepresentations for the current item. This combines all metadata and relationships of a
+   * certain type.
+   * @param itemType          The type of item we're building representations of. Used for matching templates.
+   * @param metadataField     The metadata field that resembles the item type.
+   * @param itemDataService   ItemDataService to turn relations into items.
+   */
+  buildRepresentations(itemType: string, metadataField: string, itemDataService: ItemDataService): Observable<MetadataRepresentation[]> {
+    const metadata = this.item.findMetadataSortedByPlace(metadataField);
+    const relsCurrentPage$ = this.item.relationships.pipe(
+      getSucceededRemoteData(),
+      getRemoteDataPayload(),
+      map((pl: PaginatedList<Relationship>) => pl.page),
+      distinctUntilChanged(compareArraysUsingIds())
+    );
+
+    return relsCurrentPage$.pipe(
+      relationsToRepresentations(this.item.id, itemType, metadata, itemDataService)
+    );
+  }
+
+}
diff --git a/src/app/+item-page/simple/metadata-representation-list/metadata-representation-list.component.html b/src/app/+item-page/simple/metadata-representation-list/metadata-representation-list.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..48eabf8451d1fc5b765c5a3cb5729e6591986ec5
--- /dev/null
+++ b/src/app/+item-page/simple/metadata-representation-list/metadata-representation-list.component.html
@@ -0,0 +1,5 @@
+<ds-metadata-field-wrapper *ngIf="representations && representations.length > 0" [label]="label">
+  <ds-item-type-switcher *ngFor="let rep of representations"
+                           [object]="rep" [viewMode]="viewMode">
+  </ds-item-type-switcher>
+</ds-metadata-field-wrapper>
diff --git a/src/app/+item-page/simple/metadata-representation-list/metadata-representation-list.component.spec.ts b/src/app/+item-page/simple/metadata-representation-list/metadata-representation-list.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f02625e8c72a9edeb3740fccbd96d1c12714c22c
--- /dev/null
+++ b/src/app/+item-page/simple/metadata-representation-list/metadata-representation-list.component.spec.ts
@@ -0,0 +1,40 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
+import { By } from '@angular/platform-browser';
+import { MetadataRepresentationListComponent } from './metadata-representation-list.component';
+import { MetadatumRepresentation } from '../../../core/shared/metadata-representation/metadatum/metadatum-representation.model';
+import { ItemMetadataRepresentation } from '../../../core/shared/metadata-representation/item/item-metadata-representation.model';
+
+const itemType = 'type';
+const metadataRepresentation1 = new MetadatumRepresentation(itemType);
+const metadataRepresentation2 = new ItemMetadataRepresentation();
+const representations = [metadataRepresentation1, metadataRepresentation2];
+
+describe('MetadataRepresentationListComponent', () => {
+  let comp: MetadataRepresentationListComponent;
+  let fixture: ComponentFixture<MetadataRepresentationListComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [],
+      declarations: [MetadataRepresentationListComponent],
+      providers: [],
+      schemas: [NO_ERRORS_SCHEMA]
+    }).overrideComponent(MetadataRepresentationListComponent, {
+      set: {changeDetection: ChangeDetectionStrategy.Default}
+    }).compileComponents();
+  }));
+
+  beforeEach(async(() => {
+    fixture = TestBed.createComponent(MetadataRepresentationListComponent);
+    comp = fixture.componentInstance;
+    comp.representations = representations;
+    fixture.detectChanges();
+  }));
+
+  it(`should load ${representations.length} item-type-switcher components`, () => {
+    const fields = fixture.debugElement.queryAll(By.css('ds-item-type-switcher'));
+    expect(fields.length).toBe(representations.length);
+  });
+
+});
diff --git a/src/app/+item-page/simple/metadata-representation-list/metadata-representation-list.component.ts b/src/app/+item-page/simple/metadata-representation-list/metadata-representation-list.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f0dc222bf12a526ce7cfac8078e8248271141bc7
--- /dev/null
+++ b/src/app/+item-page/simple/metadata-representation-list/metadata-representation-list.component.ts
@@ -0,0 +1,29 @@
+import { Component, Input } from '@angular/core';
+import { MetadataRepresentation } from '../../../core/shared/metadata-representation/metadata-representation.model';
+import { ItemViewMode } from '../../../shared/items/item-type-decorator';
+
+@Component({
+  selector: 'ds-metadata-representation-list',
+  templateUrl: './metadata-representation-list.component.html'
+})
+/**
+ * This component is used for displaying metadata
+ * It expects a list of MetadataRepresentation objects and a label to put on top of the list
+ */
+export class MetadataRepresentationListComponent {
+  /**
+   * A list of metadata-representations to display
+   */
+  @Input() representations: MetadataRepresentation[];
+
+  /**
+   * An i18n label to use as a title for the list
+   */
+  @Input() label: string;
+
+  /**
+   * The view-mode we're currently on
+   * @type {ElementViewMode}
+   */
+  viewMode = ItemViewMode.Metadata;
+}
diff --git a/src/app/+item-page/simple/related-entities/related-entities-search/related-entities-search.component.html b/src/app/+item-page/simple/related-entities/related-entities-search/related-entities-search.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..9ec082db733d9eecd468b6d44b1a18d334e96202
--- /dev/null
+++ b/src/app/+item-page/simple/related-entities/related-entities-search/related-entities-search.component.html
@@ -0,0 +1,6 @@
+<ds-filtered-search-page
+  [fixedFilterQuery]="fixedFilter"
+  [fixedFilter$]="fixedFilter$"
+  [searchEnabled]="searchEnabled"
+  [sideBarWidth]="sideBarWidth">
+</ds-filtered-search-page>
diff --git a/src/app/+item-page/simple/related-entities/related-entities-search/related-entities-search.component.spec.ts b/src/app/+item-page/simple/related-entities/related-entities-search/related-entities-search.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e76a9cf3d069325dd339de36e09748502c071fe5
--- /dev/null
+++ b/src/app/+item-page/simple/related-entities/related-entities-search/related-entities-search.component.spec.ts
@@ -0,0 +1,56 @@
+import { RelatedEntitiesSearchComponent } from './related-entities-search.component';
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { NoopAnimationsModule } from '@angular/platform-browser/animations';
+import { TranslateModule } from '@ngx-translate/core';
+import { NO_ERRORS_SCHEMA } from '@angular/core';
+import { FormsModule } from '@angular/forms';
+import { SearchFixedFilterService } from '../../../../+search-page/search-filters/search-filter/search-fixed-filter.service';
+import { Item } from '../../../../core/shared/item.model';
+
+describe('RelatedEntitiesSearchComponent', () => {
+  let comp: RelatedEntitiesSearchComponent;
+  let fixture: ComponentFixture<RelatedEntitiesSearchComponent>;
+  let fixedFilterService: SearchFixedFilterService;
+
+  const mockItem = Object.assign(new Item(), {
+    id: 'id1'
+  });
+  const mockRelationType = 'publicationsOfAuthor';
+  const mockRelationEntityType = 'publication';
+  const mockFilter= `f.${mockRelationType}=${mockItem.id}`;
+  const fixedFilterServiceStub = {
+    getFilterByRelation: () => mockFilter
+  };
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [TranslateModule.forRoot(), NoopAnimationsModule, FormsModule],
+      declarations: [RelatedEntitiesSearchComponent],
+      providers: [
+        { provide: SearchFixedFilterService, useValue: fixedFilterServiceStub }
+      ],
+      schemas: [NO_ERRORS_SCHEMA]
+    }).compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(RelatedEntitiesSearchComponent);
+    comp = fixture.componentInstance;
+    fixedFilterService = (comp as any).fixedFilterService;
+    comp.relationType = mockRelationType;
+    comp.item = mockItem;
+    comp.relationEntityType = mockRelationEntityType;
+    fixture.detectChanges();
+  });
+
+  it('should create a fixedFilter', () => {
+    expect(comp.fixedFilter).toEqual(mockFilter);
+  });
+
+  it('should create a fixedFilter$', () => {
+    comp.fixedFilter$.subscribe((fixedFilter) => {
+      expect(fixedFilter).toEqual(mockRelationEntityType);
+    })
+  });
+
+});
diff --git a/src/app/+item-page/simple/related-entities/related-entities-search/related-entities-search.component.ts b/src/app/+item-page/simple/related-entities/related-entities-search/related-entities-search.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..672655a8b83adbcb82c4482981f10e7cd5f1a96e
--- /dev/null
+++ b/src/app/+item-page/simple/related-entities/related-entities-search/related-entities-search.component.ts
@@ -0,0 +1,64 @@
+import { Component, Input, OnInit } from '@angular/core';
+import { Observable } from 'rxjs';
+import { Item } from '../../../../core/shared/item.model';
+import { SearchFixedFilterService } from '../../../../+search-page/search-filters/search-filter/search-fixed-filter.service';
+import { isNotEmpty } from '../../../../shared/empty.util';
+import { of } from 'rxjs/internal/observable/of';
+
+@Component({
+  selector: 'ds-related-entities-search',
+  templateUrl: './related-entities-search.component.html'
+})
+/**
+ * A component to show related items as search results.
+ * Related items can be facetted, or queried using an
+ * optional search box.
+ */
+export class RelatedEntitiesSearchComponent implements OnInit {
+
+  /**
+   * The type of relationship to fetch items for
+   * e.g. 'isAuthorOfPublication'
+   */
+  @Input() relationType: string;
+
+  /**
+   * The item to render relationships for
+   */
+  @Input() item: Item;
+
+  /**
+   * The entity type of the relationship items to be displayed
+   * e.g. 'publication'
+   * This determines the title of the search results (if search is enabled)
+   */
+  @Input() relationEntityType: string;
+
+  /**
+   * Whether or not the search bar and title should be displayed (defaults to true)
+   * @type {boolean}
+   */
+  @Input() searchEnabled = true;
+
+  /**
+   * The ratio of the sidebar's width compared to the search results (1-12) (defaults to 4)
+   * @type {number}
+   */
+  @Input() sideBarWidth = 4;
+
+  fixedFilter: string;
+  fixedFilter$: Observable<string>;
+
+  constructor(private fixedFilterService: SearchFixedFilterService) {
+  }
+
+  ngOnInit(): void {
+    if (isNotEmpty(this.relationType) && isNotEmpty(this.item)) {
+      this.fixedFilter = this.fixedFilterService.getFilterByRelation(this.relationType, this.item.id);
+    }
+    if (isNotEmpty(this.relationEntityType)) {
+      this.fixedFilter$ = of(this.relationEntityType);
+    }
+  }
+
+}
diff --git a/src/app/+item-page/simple/related-items/related-items-component.ts b/src/app/+item-page/simple/related-items/related-items-component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7b54d7316a9805afd2eaf09c7795682fbd7a19d3
--- /dev/null
+++ b/src/app/+item-page/simple/related-items/related-items-component.ts
@@ -0,0 +1,30 @@
+import { Component, Input } from '@angular/core';
+import { Item } from '../../../core/shared/item.model';
+import { ItemViewMode } from '../../../shared/items/item-type-decorator';
+
+@Component({
+  selector: 'ds-related-items',
+  styleUrls: ['./related-items.component.scss'],
+  templateUrl: './related-items.component.html'
+})
+/**
+ * This component is used for displaying relations between items
+ * It expects a list of items to display and a label to put on top
+ */
+export class RelatedItemsComponent {
+  /**
+   * A list of items to display
+   */
+  @Input() items: Item[];
+
+  /**
+   * An i18n label to use as a title for the list (usually describes the relation)
+   */
+  @Input() label: string;
+
+  /**
+   * The view-mode we're currently on
+   * @type {ElementViewMode}
+   */
+  viewMode = ItemViewMode.Element;
+}
diff --git a/src/app/+item-page/simple/related-items/related-items.component.html b/src/app/+item-page/simple/related-items/related-items.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..4b284ad63c59017f40e84cba1568242f89883378
--- /dev/null
+++ b/src/app/+item-page/simple/related-items/related-items.component.html
@@ -0,0 +1,5 @@
+<ds-metadata-field-wrapper *ngIf="items && items.length > 0" [label]="label">
+  <ds-item-type-switcher *ngFor="let item of items"
+                           [object]="item" [viewMode]="viewMode">
+  </ds-item-type-switcher>
+</ds-metadata-field-wrapper>
diff --git a/src/app/+item-page/simple/related-items/related-items.component.scss b/src/app/+item-page/simple/related-items/related-items.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/app/+item-page/simple/related-items/related-items.component.spec.ts b/src/app/+item-page/simple/related-items/related-items.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ef42ab1098482e8ece1cbbafda0d606a39763e92
--- /dev/null
+++ b/src/app/+item-page/simple/related-items/related-items.component.spec.ts
@@ -0,0 +1,51 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
+import { RelatedItemsComponent } from './related-items-component';
+import { Item } from '../../../core/shared/item.model';
+import { RemoteData } from '../../../core/data/remote-data';
+import { PaginatedList } from '../../../core/data/paginated-list';
+import { PageInfo } from '../../../core/shared/page-info.model';
+import { By } from '@angular/platform-browser';
+import { createRelationshipsObservable } from '../item-types/shared/item.component.spec';
+import { of as observableOf } from 'rxjs';
+
+const mockItem1: Item = Object.assign(new Item(), {
+  bitstreams: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))),
+  metadata: [],
+  relationships: createRelationshipsObservable()
+});
+const mockItem2: Item = Object.assign(new Item(), {
+  bitstreams: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))),
+  metadata: [],
+  relationships: createRelationshipsObservable()
+});
+const mockItems = [mockItem1, mockItem2];
+
+describe('RelatedItemsComponent', () => {
+  let comp: RelatedItemsComponent;
+  let fixture: ComponentFixture<RelatedItemsComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [],
+      declarations: [RelatedItemsComponent],
+      providers: [],
+      schemas: [NO_ERRORS_SCHEMA]
+    }).overrideComponent(RelatedItemsComponent, {
+      set: {changeDetection: ChangeDetectionStrategy.Default}
+    }).compileComponents();
+  }));
+
+  beforeEach(async(() => {
+    fixture = TestBed.createComponent(RelatedItemsComponent);
+    comp = fixture.componentInstance;
+    comp.items = mockItems;
+    fixture.detectChanges();
+  }));
+
+  it(`should load ${mockItems.length} item-type-switcher components`, () => {
+    const fields = fixture.debugElement.queryAll(By.css('ds-item-type-switcher'));
+    expect(fields.length).toBe(mockItems.length);
+  });
+
+});
diff --git a/src/app/+my-dspace-page/my-dspace-configuration-value-type.ts b/src/app/+my-dspace-page/my-dspace-configuration-value-type.ts
new file mode 100644
index 0000000000000000000000000000000000000000..baf2f0b9208964b6a5c7adb2df580e062eb17912
--- /dev/null
+++ b/src/app/+my-dspace-page/my-dspace-configuration-value-type.ts
@@ -0,0 +1,4 @@
+export enum MyDSpaceConfigurationValueType {
+  Workspace = 'workspace',
+  Workflow = 'workflow'
+}
diff --git a/src/app/+my-dspace-page/my-dspace-configuration.service.spec.ts b/src/app/+my-dspace-page/my-dspace-configuration.service.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..38d6769437d2f04ac7b633f1673dac1f9fdb428d
--- /dev/null
+++ b/src/app/+my-dspace-page/my-dspace-configuration.service.spec.ts
@@ -0,0 +1,259 @@
+import { of as observableOf } from 'rxjs';
+
+import { MyDSpaceConfigurationService } from './my-dspace-configuration.service';
+import { PaginatedSearchOptions } from '../+search-page/paginated-search-options.model';
+import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model';
+import { SortDirection, SortOptions } from '../core/cache/models/sort-options.model';
+import { SearchFilter } from '../+search-page/search-filter.model';
+import { ActivatedRouteStub } from '../shared/testing/active-router-stub';
+import { MockRoleService } from '../shared/mocks/mock-role-service';
+import { cold, hot } from 'jasmine-marbles';
+import { MyDSpaceConfigurationValueType } from './my-dspace-configuration-value-type';
+
+describe('MyDSpaceConfigurationService', () => {
+  let service: MyDSpaceConfigurationService;
+  const value1 = 'random value';
+  const prefixFilter = {
+    'f.namedresourcetype': ['another value'],
+    'f.dateSubmitted.min': ['2013'],
+    'f.dateSubmitted.max': ['2018']
+  };
+  const defaults = new PaginatedSearchOptions({
+    pagination: Object.assign(new PaginationComponentOptions(), { currentPage: 1, pageSize: 20 }),
+    sort: new SortOptions('score', SortDirection.DESC),
+    query: '',
+    scope: ''
+  });
+
+  const backendFilters = [new SearchFilter('f.namedresourcetype', ['another value']), new SearchFilter('f.dateSubmitted', ['[2013 TO 2018]'])];
+
+  const spy = jasmine.createSpyObj('RouteService', {
+    getQueryParameterValue: observableOf(value1),
+    getQueryParamsWithPrefix: observableOf(prefixFilter),
+    getRouteParameterValue: observableOf(''),
+    getRouteDataValue: observableOf({})
+  });
+
+  const activatedRoute: any = new ActivatedRouteStub();
+
+  const roleService: any = new MockRoleService();
+
+  const fixedFilterService = jasmine.createSpyObj('SearchFixedFilterService', {
+    getQueryByFilterName: observableOf(''),
+  });
+
+  beforeEach(() => {
+    service = new MyDSpaceConfigurationService(roleService, fixedFilterService, spy, activatedRoute);
+  });
+
+  describe('when the scope is called', () => {
+    beforeEach(() => {
+      service.getCurrentScope('');
+    });
+    it('should call getQueryParameterValue on the routeService with parameter name \'scope\'', () => {
+      expect((service as any).routeService.getQueryParameterValue).toHaveBeenCalledWith('scope');
+    });
+  });
+
+  describe('when getCurrentConfiguration is called', () => {
+    beforeEach(() => {
+      service.getCurrentConfiguration('');
+    });
+    it('should call getQueryParameterValue on the routeService with parameter name \'configuration\'', () => {
+      expect((service as any).routeService.getQueryParameterValue).toHaveBeenCalledWith('configuration');
+    });
+  });
+
+  describe('when getCurrentQuery is called', () => {
+    beforeEach(() => {
+      service.getCurrentQuery('');
+    });
+    it('should call getQueryParameterValue on the routeService with parameter name \'query\'', () => {
+      expect((service as any).routeService.getQueryParameterValue).toHaveBeenCalledWith('query');
+    });
+  });
+
+  describe('when getCurrentDSOType is called', () => {
+    beforeEach(() => {
+      service.getCurrentDSOType();
+    });
+    it('should call getQueryParameterValue on the routeService with parameter name \'dsoType\'', () => {
+      expect((service as any).routeService.getQueryParameterValue).toHaveBeenCalledWith('dsoType');
+    });
+  });
+
+  describe('when getCurrentFrontendFilters is called', () => {
+    beforeEach(() => {
+      service.getCurrentFrontendFilters();
+    });
+    it('should call getQueryParamsWithPrefix on the routeService with parameter prefix \'f.\'', () => {
+      expect((service as any).routeService.getQueryParamsWithPrefix).toHaveBeenCalledWith('f.');
+    });
+  });
+
+  describe('when getCurrentFilters is called', () => {
+    let parsedValues$;
+    beforeEach(() => {
+      parsedValues$ = service.getCurrentFilters();
+    });
+    it('should call getQueryParamsWithPrefix on the routeService with parameter prefix \'f.\'', () => {
+      expect((service as any).routeService.getQueryParamsWithPrefix).toHaveBeenCalledWith('f.');
+      parsedValues$.subscribe((values) => {
+        expect(values).toEqual(backendFilters);
+      });
+    });
+  });
+
+  describe('when getCurrentSort is called', () => {
+    beforeEach(() => {
+      service.getCurrentSort({} as any);
+    });
+    it('should call getQueryParameterValue on the routeService with parameter name \'sortDirection\'', () => {
+      expect((service as any).routeService.getQueryParameterValue).toHaveBeenCalledWith('sortDirection');
+    });
+    it('should call getQueryParameterValue on the routeService with parameter name \'sortField\'', () => {
+      expect((service as any).routeService.getQueryParameterValue).toHaveBeenCalledWith('sortField');
+    });
+  });
+
+  describe('when getCurrentPagination is called', () => {
+    beforeEach(() => {
+      service.getCurrentPagination({ currentPage: 1, pageSize: 10 } as any);
+    });
+    it('should call getQueryParameterValue on the routeService with parameter name \'page\'', () => {
+      expect((service as any).routeService.getQueryParameterValue).toHaveBeenCalledWith('page');
+    });
+    it('should call getQueryParameterValue on the routeService with parameter name \'pageSize\'', () => {
+      expect((service as any).routeService.getQueryParameterValue).toHaveBeenCalledWith('pageSize');
+    });
+  });
+
+  describe('when subscribeToSearchOptions or subscribeToPaginatedSearchOptions is called', () => {
+    beforeEach(() => {
+      spyOn(service, 'getCurrentPagination').and.callThrough();
+      spyOn(service, 'getCurrentSort').and.callThrough();
+      spyOn(service, 'getCurrentScope').and.callThrough();
+      spyOn(service, 'getCurrentConfiguration').and.callThrough();
+      spyOn(service, 'getCurrentQuery').and.callThrough();
+      spyOn(service, 'getCurrentDSOType').and.callThrough();
+      spyOn(service, 'getCurrentFilters').and.callThrough();
+    });
+
+    describe('when subscribeToSearchOptions is called', () => {
+      beforeEach(() => {
+        (service as any).subscribeToSearchOptions(defaults)
+      });
+      it('should call all getters it needs, but not call any others', () => {
+        expect(service.getCurrentPagination).not.toHaveBeenCalled();
+        expect(service.getCurrentSort).not.toHaveBeenCalled();
+        expect(service.getCurrentScope).toHaveBeenCalled();
+        expect(service.getCurrentConfiguration).toHaveBeenCalled();
+        expect(service.getCurrentQuery).toHaveBeenCalled();
+        expect(service.getCurrentDSOType).toHaveBeenCalled();
+        expect(service.getCurrentFilters).toHaveBeenCalled();
+      });
+    });
+
+    describe('when subscribeToPaginatedSearchOptions is called', () => {
+      beforeEach(() => {
+        (service as any).subscribeToPaginatedSearchOptions(defaults);
+      });
+      it('should call all getters it needs', () => {
+        expect(service.getCurrentPagination).toHaveBeenCalled();
+        expect(service.getCurrentSort).toHaveBeenCalled();
+        expect(service.getCurrentScope).toHaveBeenCalled();
+        expect(service.getCurrentConfiguration).toHaveBeenCalled();
+        expect(service.getCurrentQuery).toHaveBeenCalled();
+        expect(service.getCurrentDSOType).toHaveBeenCalled();
+        expect(service.getCurrentFilters).toHaveBeenCalled();
+      });
+    });
+  });
+
+  describe('when getAvailableConfigurationTypes is called', () => {
+
+    it('should return properly list when user is submitter', () => {
+      roleService.setSubmitter(true);
+      roleService.setController(false);
+      roleService.setAdmin(false);
+
+      const list$ = service.getAvailableConfigurationTypes();
+
+      expect(list$).toBeObservable(cold('(b|)', {
+        b: [
+          MyDSpaceConfigurationValueType.Workspace
+        ]
+      }));
+    });
+
+    it('should return properly list when user is controller', () => {
+      roleService.setSubmitter(false);
+      roleService.setController(true);
+      roleService.setAdmin(false);
+
+      const list$ = service.getAvailableConfigurationTypes();
+
+      expect(list$).toBeObservable(cold('(b|)', {
+        b: [
+          MyDSpaceConfigurationValueType.Workflow
+        ]
+      }));
+    });
+
+    it('should return properly list when user is admin', () => {
+      roleService.setSubmitter(false);
+      roleService.setController(false);
+      roleService.setAdmin(true);
+
+      const list$ = service.getAvailableConfigurationTypes();
+
+      expect(list$).toBeObservable(cold('(b|)', {
+        b: [
+          MyDSpaceConfigurationValueType.Workflow
+        ]
+      }));
+    });
+
+    it('should return properly list when user is submitter and controller', () => {
+      roleService.setSubmitter(true);
+      roleService.setController(true);
+      roleService.setAdmin(false);
+
+      const list$ = service.getAvailableConfigurationTypes();
+
+      expect(list$).toBeObservable(cold('(b|)', {
+        b: [
+          MyDSpaceConfigurationValueType.Workspace,
+          MyDSpaceConfigurationValueType.Workflow
+        ]
+      }));
+    });
+  });
+
+  describe('when getAvailableConfigurationOptions is called', () => {
+
+    it('should return properly options list', () => {
+      spyOn(service, 'getAvailableConfigurationTypes').and.returnValue(hot('a', {
+        a: [
+          MyDSpaceConfigurationValueType.Workspace,
+          MyDSpaceConfigurationValueType.Workflow
+        ]
+      }));
+
+      const list$ = service.getAvailableConfigurationOptions();
+
+      expect(list$).toBeObservable(cold('(b|)', {
+        b: [
+          {
+            value: MyDSpaceConfigurationValueType.Workspace,
+            label: `mydspace.show.${MyDSpaceConfigurationValueType.Workspace}`
+          },
+          {
+            value: MyDSpaceConfigurationValueType.Workflow,
+            label: `mydspace.show.${MyDSpaceConfigurationValueType.Workflow}`
+          }
+        ]
+      }));
+    });
+  });
+});
diff --git a/src/app/+my-dspace-page/my-dspace-configuration.service.ts b/src/app/+my-dspace-page/my-dspace-configuration.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..705ec897f89efe25d040558b55ca05c11374181d
--- /dev/null
+++ b/src/app/+my-dspace-page/my-dspace-configuration.service.ts
@@ -0,0 +1,120 @@
+import { Injectable } from '@angular/core';
+import { ActivatedRoute } from '@angular/router';
+
+import { combineLatest, Observable } from 'rxjs';
+import { first, map } from 'rxjs/operators';
+
+import { MyDSpaceConfigurationValueType } from './my-dspace-configuration-value-type';
+import { RoleService } from '../core/roles/role.service';
+import { SearchConfigurationOption } from '../+search-page/search-switch-configuration/search-configuration-option.model';
+import { SearchConfigurationService } from '../+search-page/search-service/search-configuration.service';
+import { RouteService } from '../shared/services/route.service';
+import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model';
+import { SortDirection, SortOptions } from '../core/cache/models/sort-options.model';
+import { SearchFixedFilterService } from '../+search-page/search-filters/search-filter/search-fixed-filter.service';
+
+/**
+ * Service that performs all actions that have to do with the current mydspace configuration
+ */
+@Injectable()
+export class MyDSpaceConfigurationService extends SearchConfigurationService {
+  /**
+   * Default pagination settings
+   */
+  protected defaultPagination = Object.assign(new PaginationComponentOptions(), {
+    id: 'mydspace-page',
+    pageSize: 10,
+    currentPage: 1
+  });
+
+  /**
+   * Default sort settings
+   */
+  protected defaultSort = new SortOptions('dc.date.issued', SortDirection.DESC);
+
+  /**
+   * Default configuration parameter setting
+   */
+  protected defaultConfiguration = 'workspace';
+
+  /**
+   * Default scope setting
+   */
+  protected defaultScope = '';
+
+  /**
+   * Default query setting
+   */
+  protected defaultQuery = '';
+
+  private isAdmin$: Observable<boolean>;
+  private isController$: Observable<boolean>;
+  private isSubmitter$: Observable<boolean>;
+
+  /**
+   * Initialize class
+   *
+   * @param {roleService} roleService
+   * @param {SearchFixedFilterService} fixedFilterService
+   * @param {RouteService} routeService
+   * @param {ActivatedRoute} route
+   */
+  constructor(protected roleService: RoleService,
+              protected fixedFilterService: SearchFixedFilterService,
+              protected routeService: RouteService,
+              protected route: ActivatedRoute) {
+
+    super(routeService, fixedFilterService, route);
+
+    // override parent class initialization
+    this._defaults = null;
+    this.initDefaults();
+
+    this.isSubmitter$ = this.roleService.isSubmitter();
+    this.isController$ = this.roleService.isController();
+    this.isAdmin$ = this.roleService.isAdmin();
+  }
+
+  /**
+   * Returns the list of available configuration depend on the user role
+   *
+   * @return {Observable<MyDSpaceConfigurationValueType[]>}
+   *    Emits the available configuration list
+   */
+  public getAvailableConfigurationTypes(): Observable<MyDSpaceConfigurationValueType[]> {
+    return combineLatest(this.isSubmitter$, this.isController$, this.isAdmin$).pipe(
+      first(),
+      map(([isSubmitter, isController, isAdmin]: [boolean, boolean, boolean]) => {
+        const availableConf: MyDSpaceConfigurationValueType[] = [];
+        if (isSubmitter) {
+          availableConf.push(MyDSpaceConfigurationValueType.Workspace);
+        }
+        if (isController || isAdmin) {
+          availableConf.push(MyDSpaceConfigurationValueType.Workflow);
+        }
+        return availableConf;
+      }));
+  }
+
+  /**
+   * Returns the select options for the available configuration list
+   *
+   * @return {Observable<SearchConfigurationOption[]>}
+   *    Emits the select options list
+   */
+  public getAvailableConfigurationOptions(): Observable<SearchConfigurationOption[]> {
+    return this.getAvailableConfigurationTypes().pipe(
+      first(),
+      map((availableConfigurationTypes: MyDSpaceConfigurationValueType[]) => {
+        const configurationOptions: SearchConfigurationOption[] = [];
+        availableConfigurationTypes.forEach((type) => {
+          const value = type;
+          const label = `mydspace.show.${value}`;
+          configurationOptions.push({ value, label });
+        });
+        return configurationOptions;
+      })
+    )
+  }
+
+}
diff --git a/src/app/+my-dspace-page/my-dspace-new-submission/my-dspace-new-submission.component.html b/src/app/+my-dspace-page/my-dspace-new-submission/my-dspace-new-submission.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..280d694d27260c1db727449035aa359d754920fc
--- /dev/null
+++ b/src/app/+my-dspace-page/my-dspace-new-submission/my-dspace-new-submission.component.html
@@ -0,0 +1,15 @@
+<div class="parent mb-3">
+  <div class="upload">
+    <ds-uploader *ngIf="uploadFilesOptions.url !== ''"
+                 [uploadFilesOptions]="uploadFilesOptions"
+                 (onCompleteItem)="onCompleteItem($event)"
+                 (onUploadError)="onUploadError($event)"></ds-uploader>
+
+  </div>
+  <div class="add">
+    <a class="btn btn-lg btn-primary mt-1 ml-2" [routerLink]="['/submit']" role="button">
+      <i class="fa fa-plus-circle" aria-hidden="true"></i> {{'mydspace.new-submission' | translate}}
+    </a>
+  </div>
+
+</div>
diff --git a/src/app/+my-dspace-page/my-dspace-new-submission/my-dspace-new-submission.component.scss b/src/app/+my-dspace-page/my-dspace-new-submission/my-dspace-new-submission.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..40a955b349accfb3aa3b22e5072ff07cc7cf9399
--- /dev/null
+++ b/src/app/+my-dspace-page/my-dspace-new-submission/my-dspace-new-submission.component.scss
@@ -0,0 +1,11 @@
+.parent {
+  display: flex;
+}
+
+.upload {
+  flex: auto;
+}
+
+.add {
+  flex: initial;
+}
diff --git a/src/app/+my-dspace-page/my-dspace-new-submission/my-dspace-new-submission.component.spec.ts b/src/app/+my-dspace-page/my-dspace-new-submission/my-dspace-new-submission.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..012f86f579f86b44c6db6c9c0a42aa31c58e11af
--- /dev/null
+++ b/src/app/+my-dspace-page/my-dspace-new-submission/my-dspace-new-submission.component.spec.ts
@@ -0,0 +1,101 @@
+import { ChangeDetectorRef, Component, NO_ERRORS_SCHEMA } from '@angular/core';
+import { async, ComponentFixture, inject, TestBed } from '@angular/core/testing';
+import { RouterTestingModule } from '@angular/router/testing';
+
+import { Store } from '@ngrx/store';
+import { of as observableOf } from 'rxjs';
+import { TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core';
+import { ScrollToService } from '@nicky-lenaers/ngx-scroll-to';
+
+import { HALEndpointService } from '../../core/shared/hal-endpoint.service';
+import { AuthServiceStub } from '../../shared/testing/auth-service-stub';
+import { AuthService } from '../../core/auth/auth.service';
+import { HALEndpointServiceStub } from '../../shared/testing/hal-endpoint-service-stub';
+import { createTestComponent } from '../../shared/testing/utils';
+import { MyDSpaceNewSubmissionComponent } from './my-dspace-new-submission.component';
+import { AppState } from '../../app.reducer';
+import { MockTranslateLoader } from '../../shared/mocks/mock-translate-loader';
+import { getMockTranslateService } from '../../shared/mocks/mock-translate.service';
+import { NotificationsService } from '../../shared/notifications/notifications.service';
+import { NotificationsServiceStub } from '../../shared/testing/notifications-service-stub';
+import { SharedModule } from '../../shared/shared.module';
+import { getMockScrollToService } from '../../shared/mocks/mock-scroll-to-service';
+import { UploaderService } from '../../shared/uploader/uploader.service';
+
+describe('MyDSpaceNewSubmissionComponent test', () => {
+
+  const translateService: any = getMockTranslateService();
+  const store: Store<AppState> = jasmine.createSpyObj('store', {
+    /* tslint:disable:no-empty */
+    dispatch: {},
+    /* tslint:enable:no-empty */
+    pipe: observableOf(true)
+  });
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [
+        RouterTestingModule,
+        SharedModule,
+        TranslateModule.forRoot({
+          loader: {
+            provide: TranslateLoader,
+            useClass: MockTranslateLoader
+          }
+        })
+      ],
+      declarations: [
+        MyDSpaceNewSubmissionComponent,
+        TestComponent
+      ],
+      providers: [
+        { provide: AuthService, useClass: AuthServiceStub },
+        { provide: HALEndpointService, useValue: new HALEndpointServiceStub('workspaceitems') },
+        { provide: NotificationsService, useValue: new NotificationsServiceStub() },
+        { provide: ScrollToService, useValue: getMockScrollToService() },
+        { provide: Store, useValue: store },
+        { provide: TranslateService, useValue: translateService },
+        ChangeDetectorRef,
+        MyDSpaceNewSubmissionComponent,
+        UploaderService
+      ],
+      schemas: [NO_ERRORS_SCHEMA]
+    }).compileComponents();
+  }));
+
+  describe('', () => {
+    let testComp: TestComponent;
+    let testFixture: ComponentFixture<TestComponent>;
+
+    // synchronous beforeEach
+    beforeEach(() => {
+      const html = `
+        <ds-my-dspace-new-submission (uploadEnd)="reload($event)"></ds-my-dspace-new-submission>`;
+
+      testFixture = createTestComponent(html, TestComponent) as ComponentFixture<TestComponent>;
+      testComp = testFixture.componentInstance;
+    });
+
+    afterEach(() => {
+      testFixture.destroy();
+    });
+
+    it('should create MyDSpaceNewSubmissionComponent', inject([MyDSpaceNewSubmissionComponent], (app: MyDSpaceNewSubmissionComponent) => {
+
+      expect(app).toBeDefined();
+
+    }));
+  });
+
+});
+
+// declare a test component
+@Component({
+  selector: 'ds-test-cmp',
+  template: ``
+})
+class TestComponent {
+
+  reload = (event) => {
+    return;
+  }
+}
diff --git a/src/app/+my-dspace-page/my-dspace-new-submission/my-dspace-new-submission.component.ts b/src/app/+my-dspace-page/my-dspace-new-submission/my-dspace-new-submission.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..938a1ec899ee6a2835b3a83a5b20a3a83782e393
--- /dev/null
+++ b/src/app/+my-dspace-page/my-dspace-new-submission/my-dspace-new-submission.component.ts
@@ -0,0 +1,118 @@
+import { ChangeDetectorRef, Component, EventEmitter, OnDestroy, OnInit, Output } from '@angular/core';
+
+import { Subscription } from 'rxjs';
+import { first } from 'rxjs/operators';
+import { Store } from '@ngrx/store';
+import { TranslateService } from '@ngx-translate/core';
+
+import { SubmissionState } from '../../submission/submission.reducers';
+import { AuthService } from '../../core/auth/auth.service';
+import { MyDSpaceResult } from '../my-dspace-result.model';
+import { DSpaceObject } from '../../core/shared/dspace-object.model';
+import { NotificationsService } from '../../shared/notifications/notifications.service';
+import { NotificationOptions } from '../../shared/notifications/models/notification-options.model';
+import { UploaderOptions } from '../../shared/uploader/uploader-options.model';
+import { HALEndpointService } from '../../core/shared/hal-endpoint.service';
+import { NotificationType } from '../../shared/notifications/models/notification-type';
+import { hasValue } from '../../shared/empty.util';
+
+/**
+ * This component represents the whole mydspace page header
+ */
+@Component({
+  selector: 'ds-my-dspace-new-submission',
+  styleUrls: ['./my-dspace-new-submission.component.scss'],
+  templateUrl: './my-dspace-new-submission.component.html'
+})
+export class MyDSpaceNewSubmissionComponent implements OnDestroy, OnInit {
+  @Output() uploadEnd = new EventEmitter<Array<MyDSpaceResult<DSpaceObject>>>();
+
+  /**
+   * The UploaderOptions object
+   */
+  public uploadFilesOptions: UploaderOptions = {
+    url: '',
+    authToken: null,
+    disableMultipart: false,
+    itemAlias: null
+  };
+
+  /**
+   * Subscription to unsubscribe from
+   */
+  private sub: Subscription;
+
+  /**
+   * Initialize instance variables
+   *
+   * @param {AuthService} authService
+   * @param {ChangeDetectorRef} changeDetectorRef
+   * @param {HALEndpointService} halService
+   * @param {NotificationsService} notificationsService
+   * @param {Store<SubmissionState>} store
+   * @param {TranslateService} translate
+   */
+  constructor(private authService: AuthService,
+              private changeDetectorRef: ChangeDetectorRef,
+              private halService: HALEndpointService,
+              private notificationsService: NotificationsService,
+              private store: Store<SubmissionState>,
+              private translate: TranslateService) {
+  }
+
+  /**
+   * Initialize url and Bearer token
+   */
+  ngOnInit() {
+    this.sub = this.halService.getEndpoint('workspaceitems').pipe(first()).subscribe((url) => {
+        this.uploadFilesOptions.url = url;
+        this.uploadFilesOptions.authToken = this.authService.buildAuthHeader();
+        this.changeDetectorRef.detectChanges();
+      }
+    );
+  }
+
+  /**
+   * Method called when file upload is completed to notify upload status
+   */
+  public onCompleteItem(res) {
+    if (res && res._embedded && res._embedded.workspaceitems && res._embedded.workspaceitems.length > 0) {
+      const workspaceitems = res._embedded.workspaceitems;
+      this.uploadEnd.emit(workspaceitems);
+
+      if (workspaceitems.length === 1) {
+        const options = new NotificationOptions();
+        options.timeOut = 0;
+        const link = '/workspaceitems/' + workspaceitems[0].id + '/edit';
+        this.notificationsService.notificationWithAnchor(
+          NotificationType.Success,
+          options,
+          link,
+          'mydspace.general.text-here',
+          'mydspace.upload.upload-successful',
+          'here');
+      } else if (workspaceitems.length > 1) {
+        this.notificationsService.success(null, this.translate.get('mydspace.upload.upload-multiple-successful', {qty: workspaceitems.length}));
+      }
+
+    } else {
+      this.notificationsService.error(null, this.translate.get('mydspace.upload.upload-failed'));
+    }
+  }
+
+  /**
+   * Method called on file upload error
+   */
+  public onUploadError() {
+    this.notificationsService.error(null, this.translate.get('mydspace.upload.upload-failed'));
+  }
+
+  /**
+   * Unsubscribe from the subscription
+   */
+  ngOnDestroy(): void {
+    if (hasValue(this.sub)) {
+      this.sub.unsubscribe();
+    }
+  }
+}
diff --git a/src/app/+my-dspace-page/my-dspace-page-routing.module.ts b/src/app/+my-dspace-page/my-dspace-page-routing.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d70a007e3ae2de9abf7c1bb4b5184b796d3ea035
--- /dev/null
+++ b/src/app/+my-dspace-page/my-dspace-page-routing.module.ts
@@ -0,0 +1,25 @@
+import { NgModule } from '@angular/core';
+import { RouterModule } from '@angular/router';
+
+import { MyDSpacePageComponent } from './my-dspace-page.component';
+import { MyDSpaceGuard } from './my-dspace.guard';
+
+@NgModule({
+  imports: [
+    RouterModule.forChild([
+      {
+        path: '',
+        component: MyDSpacePageComponent,
+        data: { title: 'mydspace.title' },
+        canActivate: [
+          MyDSpaceGuard
+        ]
+      }
+    ])
+  ]
+})
+/**
+ * This module defines the default component to load when navigating to the mydspace page path.
+ */
+export class MyDspacePageRoutingModule {
+}
diff --git a/src/app/+my-dspace-page/my-dspace-page.component.html b/src/app/+my-dspace-page/my-dspace-page.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..4c691028fc1af3365cec0474be9cdccbcd8fb285
--- /dev/null
+++ b/src/app/+my-dspace-page/my-dspace-page.component.html
@@ -0,0 +1,48 @@
+<div class="container">
+  <ds-my-dspace-new-submission *dsShowOnlyForRole="[roleTypeEnum.Submitter]"
+                               (uploadEnd)="reload($event)"></ds-my-dspace-new-submission>
+  <div class="search-page row">
+      <ds-search-sidebar *ngIf="!(isXsOrSm$ | async)" class="col-3 sidebar-md-sticky"
+                         id="search-sidebar"
+                         [configurationList]="(configurationList$ | async)"
+                         [resultCount]="(resultsRD$ | async)?.payload.totalElements"
+                         [viewModeList]="viewModeList"
+                         [inPlaceSearch]="inPlaceSearch"></ds-search-sidebar>
+      <div class="col-12 col-md-9">
+          <ds-search-form id="search-form"
+                          [query]="(searchOptions$ | async)?.query"
+                          [scope]="(searchOptions$ | async)?.scope"
+                          [currentUrl]="getSearchLink()"
+                          [scopes]="(scopeListRD$ | async)"
+                          [inPlaceSearch]="inPlaceSearch">
+          </ds-search-form>
+          <ds-search-labels [inPlaceSearch]="inPlaceSearch"></ds-search-labels>
+          <div class="row">
+              <div id="search-body"
+                   class="row-offcanvas row-offcanvas-left"
+                   [@pushInOut]="(isSidebarCollapsed() | async) ? 'collapsed' : 'expanded'">
+                  <ds-search-sidebar *ngIf="(isXsOrSm$ | async)" class="col-12"
+                                     id="search-sidebar-sm"
+                                     [configurationList]="(configurationList$ | async)"
+                                     [resultCount]="(resultsRD$ | async)?.payload.totalElements"
+                                     (toggleSidebar)="closeSidebar()"
+                                     [ngClass]="{'active': !(isSidebarCollapsed() | async)}"
+                                     [inPlaceSearch]="inPlaceSearch">
+                  </ds-search-sidebar>
+                  <div id="search-content" class="col-12">
+                      <div class="d-block d-md-none search-controls clearfix">
+                          <ds-view-mode-switch [viewModeList]="viewModeList" [inPlaceSearch]="inPlaceSearch"></ds-view-mode-switch>
+                          <button (click)="openSidebar()" aria-controls="#search-body"
+                                  class="btn btn-outline-primary float-right open-sidebar"><i
+                                  class="fas fa-sliders"></i> {{"search.sidebar.open"
+                              | translate}}
+                          </button>
+                      </div>
+                      <ds-my-dspace-results [searchResults]="resultsRD$ | async"
+                                            [searchConfig]="searchOptions$ | async"></ds-my-dspace-results>
+                  </div>
+              </div>
+          </div>
+      </div>
+  </div>
+</div>
diff --git a/src/app/+my-dspace-page/my-dspace-page.component.scss b/src/app/+my-dspace-page/my-dspace-page.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..86c589bf66c03127ab8d37207bfa39eadd5783bb
--- /dev/null
+++ b/src/app/+my-dspace-page/my-dspace-page.component.scss
@@ -0,0 +1 @@
+@import '../+search-page/search-page.component.scss';
diff --git a/src/app/+my-dspace-page/my-dspace-page.component.spec.ts b/src/app/+my-dspace-page/my-dspace-page.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9658814a6aca4b1bd2d67617325fe352bc385f72
--- /dev/null
+++ b/src/app/+my-dspace-page/my-dspace-page.component.spec.ts
@@ -0,0 +1,204 @@
+import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
+import { NoopAnimationsModule } from '@angular/platform-browser/animations';
+import { ActivatedRoute } from '@angular/router';
+import { By } from '@angular/platform-browser';
+import { NgbCollapseModule } from '@ng-bootstrap/ng-bootstrap';
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { RouterTestingModule } from '@angular/router/testing';
+
+import { Store } from '@ngrx/store';
+import { TranslateModule } from '@ngx-translate/core';
+import { cold } from 'jasmine-marbles';
+import { of as observableOf } from 'rxjs';
+
+import { SortDirection, SortOptions } from '../core/cache/models/sort-options.model';
+import { CommunityDataService } from '../core/data/community-data.service';
+import { HostWindowService } from '../shared/host-window.service';
+import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model';
+import { RemoteData } from '../core/data/remote-data';
+import { MyDSpacePageComponent, SEARCH_CONFIG_SERVICE } from './my-dspace-page.component';
+import { RouteService } from '../shared/services/route.service';
+import { routeServiceStub } from '../shared/testing/route-service-stub';
+import { SearchConfigurationServiceStub } from '../shared/testing/search-configuration-service-stub';
+import { SearchService } from '../+search-page/search-service/search.service';
+import { SearchConfigurationService } from '../+search-page/search-service/search-configuration.service';
+import { PaginatedSearchOptions } from '../+search-page/paginated-search-options.model';
+import { SearchSidebarService } from '../+search-page/search-sidebar/search-sidebar.service';
+import { SearchFilterService } from '../+search-page/search-filters/search-filter/search-filter.service';
+import { RoleDirective } from '../shared/roles/role.directive';
+import { RoleService } from '../core/roles/role.service';
+import { MockRoleService } from '../shared/mocks/mock-role-service';
+import { SearchFixedFilterService } from '../+search-page/search-filters/search-filter/search-fixed-filter.service';
+
+describe('MyDSpacePageComponent', () => {
+  let comp: MyDSpacePageComponent;
+  let fixture: ComponentFixture<MyDSpacePageComponent>;
+  let searchServiceObject: SearchService;
+  let searchConfigurationServiceObject: SearchConfigurationService;
+  const store: Store<MyDSpacePageComponent> = jasmine.createSpyObj('store', {
+    /* tslint:disable:no-empty */
+    dispatch: {},
+    /* tslint:enable:no-empty */
+    select: observableOf(true)
+  });
+  const pagination: PaginationComponentOptions = new PaginationComponentOptions();
+  pagination.id = 'mydspace-results-pagination';
+  pagination.currentPage = 1;
+  pagination.pageSize = 10;
+  const sort: SortOptions = new SortOptions('score', SortDirection.DESC);
+  const mockResults = observableOf(new RemoteData(false, false, true, null, ['test', 'data']));
+  const searchServiceStub = jasmine.createSpyObj('SearchService', {
+    search: mockResults,
+    getSearchLink: '/mydspace',
+    getScopes: observableOf(['test-scope']),
+    setServiceOptions: {}
+  });
+  const configurationParam = 'default';
+  const queryParam = 'test query';
+  const scopeParam = '7669c72a-3f2a-451f-a3b9-9210e7a4c02f';
+  const paginatedSearchOptions = new PaginatedSearchOptions({
+    configuration: configurationParam,
+    query: queryParam,
+    scope: scopeParam,
+    pagination,
+    sort
+  });
+  const activatedRouteStub = {
+    snapshot: {
+      queryParamMap: new Map([
+        ['query', queryParam],
+        ['scope', scopeParam]
+      ])
+    },
+    queryParams: observableOf({
+      query: queryParam,
+      scope: scopeParam
+    })
+  };
+  const sidebarService = {
+    isCollapsed: observableOf(true),
+    collapse: () => this.isCollapsed = observableOf(true),
+    expand: () => this.isCollapsed = observableOf(false)
+  };
+  const mockFixedFilterService: SearchFixedFilterService = {
+    getQueryByFilterName: (filter: string) => {
+      return observableOf(undefined)
+    }
+  } as SearchFixedFilterService;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [TranslateModule.forRoot(), RouterTestingModule.withRoutes([]), NoopAnimationsModule, NgbCollapseModule.forRoot()],
+      declarations: [MyDSpacePageComponent, RoleDirective],
+      providers: [
+        { provide: SearchService, useValue: searchServiceStub },
+        {
+          provide: CommunityDataService,
+          useValue: jasmine.createSpyObj('communityService', ['findById', 'findAll'])
+        },
+        { provide: ActivatedRoute, useValue: activatedRouteStub },
+        { provide: RouteService, useValue: routeServiceStub },
+        {
+          provide: Store, useValue: store
+        },
+        {
+          provide: HostWindowService, useValue: jasmine.createSpyObj('hostWindowService',
+            {
+              isXs: observableOf(true),
+              isSm: observableOf(false),
+              isXsOrSm: observableOf(true)
+            })
+        },
+        {
+          provide: SearchSidebarService,
+          useValue: sidebarService
+        },
+        {
+          provide: SearchFilterService,
+          useValue: {}
+        }, {
+          provide: SEARCH_CONFIG_SERVICE,
+          useValue: new SearchConfigurationServiceStub()
+        },
+        {
+          provide: RoleService,
+          useValue: new MockRoleService()
+        },
+        {
+          provide: SearchFixedFilterService,
+          useValue: mockFixedFilterService
+        }
+      ],
+      schemas: [NO_ERRORS_SCHEMA]
+    }).overrideComponent(MyDSpacePageComponent, {
+      set: { changeDetection: ChangeDetectionStrategy.Default }
+    }).compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(MyDSpacePageComponent);
+    comp = fixture.componentInstance; // SearchPageComponent test instance
+    fixture.detectChanges();
+    searchServiceObject = (comp as any).service;
+    searchConfigurationServiceObject = (comp as any).searchConfigService;
+  });
+
+  afterEach(() => {
+    comp = null;
+    searchServiceObject = null;
+    searchConfigurationServiceObject = null;
+  });
+
+  it('should get the scope and query from the route parameters', () => {
+
+    searchConfigurationServiceObject.paginatedSearchOptions.next(paginatedSearchOptions);
+    expect(comp.searchOptions$).toBeObservable(cold('b', {
+      b: paginatedSearchOptions
+    }));
+
+  });
+
+  describe('when the open sidebar button is clicked in mobile view', () => {
+
+    beforeEach(() => {
+      spyOn(comp, 'openSidebar');
+      const openSidebarButton = fixture.debugElement.query(By.css('.open-sidebar'));
+      openSidebarButton.triggerEventHandler('click', null);
+    });
+
+    it('should trigger the openSidebar function', () => {
+      expect(comp.openSidebar).toHaveBeenCalled();
+    });
+
+  });
+
+  describe('when sidebarCollapsed is true in mobile view', () => {
+    let menu: HTMLElement;
+
+    beforeEach(() => {
+      menu = fixture.debugElement.query(By.css('#search-sidebar-sm')).nativeElement;
+      comp.isSidebarCollapsed = () => observableOf(true);
+      fixture.detectChanges();
+    });
+
+    it('should close the sidebar', () => {
+      expect(menu.classList).not.toContain('active');
+    });
+
+  });
+
+  describe('when sidebarCollapsed is false in mobile view', () => {
+    let menu: HTMLElement;
+
+    beforeEach(() => {
+      menu = fixture.debugElement.query(By.css('#search-sidebar-sm')).nativeElement;
+      comp.isSidebarCollapsed = () => observableOf(false);
+      fixture.detectChanges();
+    });
+
+    it('should open the menu', () => {
+      expect(menu.classList).toContain('active');
+    });
+
+  });
+});
diff --git a/src/app/+my-dspace-page/my-dspace-page.component.ts b/src/app/+my-dspace-page/my-dspace-page.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..251bf50bd1c988884ab7d664e2bb3d8aadb1f597
--- /dev/null
+++ b/src/app/+my-dspace-page/my-dspace-page.component.ts
@@ -0,0 +1,168 @@
+import {
+  ChangeDetectionStrategy,
+  Component,
+  Inject,
+  InjectionToken,
+  Input,
+  OnInit
+} from '@angular/core';
+
+import { BehaviorSubject, Observable, Subscription } from 'rxjs';
+import { switchMap, tap, } from 'rxjs/operators';
+
+import { PaginatedList } from '../core/data/paginated-list';
+import { RemoteData } from '../core/data/remote-data';
+import { DSpaceObject } from '../core/shared/dspace-object.model';
+import { pushInOut } from '../shared/animations/push';
+import { HostWindowService } from '../shared/host-window.service';
+import { PaginatedSearchOptions } from '../+search-page/paginated-search-options.model';
+import { SearchService } from '../+search-page/search-service/search.service';
+import { SearchSidebarService } from '../+search-page/search-sidebar/search-sidebar.service';
+import { hasValue } from '../shared/empty.util';
+import { getSucceededRemoteData } from '../core/shared/operators';
+import { MyDSpaceResult } from './my-dspace-result.model';
+import { MyDSpaceResponseParsingService } from '../core/data/mydspace-response-parsing.service';
+import { SearchConfigurationOption } from '../+search-page/search-switch-configuration/search-configuration-option.model';
+import { RoleType } from '../core/roles/role-types';
+import { SearchConfigurationService } from '../+search-page/search-service/search-configuration.service';
+import { MyDSpaceConfigurationService } from './my-dspace-configuration.service';
+import { ViewMode } from '../core/shared/view-mode.model';
+import { MyDSpaceRequest } from '../core/data/request.models';
+
+export const MYDSPACE_ROUTE = '/mydspace';
+export const SEARCH_CONFIG_SERVICE: InjectionToken<SearchConfigurationService> = new InjectionToken<SearchConfigurationService>('searchConfigurationService');
+
+/**
+ * This component represents the whole mydspace page
+ */
+@Component({
+  selector: 'ds-my-dspace-page',
+  styleUrls: ['./my-dspace-page.component.scss'],
+  templateUrl: './my-dspace-page.component.html',
+  changeDetection: ChangeDetectionStrategy.OnPush,
+  animations: [pushInOut],
+  providers: [
+    {
+      provide: SEARCH_CONFIG_SERVICE,
+      useClass: MyDSpaceConfigurationService
+    }
+  ]
+})
+export class MyDSpacePageComponent implements OnInit {
+
+  /**
+   * True when the search component should show results on the current page
+   */
+  @Input() inPlaceSearch = true;
+
+  /**
+   * The list of available configuration options
+   */
+  configurationList$: Observable<SearchConfigurationOption[]>;
+
+  /**
+   * The current search results
+   */
+  resultsRD$: BehaviorSubject<RemoteData<PaginatedList<MyDSpaceResult<DSpaceObject>>>> = new BehaviorSubject(null);
+
+  /**
+   * The current paginated search options
+   */
+  searchOptions$: Observable<PaginatedSearchOptions>;
+
+  /**
+   * The current relevant scopes
+   */
+  scopeListRD$: Observable<DSpaceObject[]>;
+
+  /**
+   * Emits true if were on a small screen
+   */
+  isXsOrSm$: Observable<boolean>;
+
+  /**
+   * Subscription to unsubscribe from
+   */
+  sub: Subscription;
+
+  /**
+   * Variable for enumeration RoleType
+   */
+  roleTypeEnum = RoleType;
+
+  /**
+   * List of available view mode
+   */
+  viewModeList = [ViewMode.List, ViewMode.Detail];
+
+  constructor(private service: SearchService,
+              private sidebarService: SearchSidebarService,
+              private windowService: HostWindowService,
+              @Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: MyDSpaceConfigurationService) {
+    this.isXsOrSm$ = this.windowService.isXsOrSm();
+    this.service.setServiceOptions(MyDSpaceResponseParsingService, MyDSpaceRequest);
+  }
+
+  /**
+   * Initialize available configuration list
+   *
+   * Listening to changes in the paginated search options
+   * If something changes, update the search results
+   *
+   * Listen to changes in the scope
+   * If something changes, update the list of scopes for the dropdown
+   */
+  ngOnInit(): void {
+    this.configurationList$ = this.searchConfigService.getAvailableConfigurationOptions();
+    this.searchOptions$ = this.searchConfigService.paginatedSearchOptions;
+
+    this.sub = this.searchOptions$.pipe(
+      tap(() => this.resultsRD$.next(null)),
+      switchMap((options: PaginatedSearchOptions) => this.service.search(options).pipe(getSucceededRemoteData())))
+      .subscribe((results) => {
+        this.resultsRD$.next(results);
+      });
+    this.scopeListRD$ = this.searchConfigService.getCurrentScope('').pipe(
+      switchMap((scopeId) => this.service.getScopes(scopeId))
+    );
+
+  }
+
+  /**
+   * Set the sidebar to a collapsed state
+   */
+  public closeSidebar(): void {
+    this.sidebarService.collapse()
+  }
+
+  /**
+   * Set the sidebar to an expanded state
+   */
+  public openSidebar(): void {
+    this.sidebarService.expand();
+  }
+
+  /**
+   * Check if the sidebar is collapsed
+   * @returns {Observable<boolean>} emits true if the sidebar is currently collapsed, false if it is expanded
+   */
+  public isSidebarCollapsed(): Observable<boolean> {
+    return this.sidebarService.isCollapsed;
+  }
+
+  /**
+   * @returns {string} The base path to the search page
+   */
+  public getSearchLink(): string {
+    return this.service.getSearchLink();
+  }
+
+  /**
+   * Unsubscribe from the subscription
+   */
+  ngOnDestroy(): void {
+    if (hasValue(this.sub)) {
+      this.sub.unsubscribe();
+    }
+  }
+}
diff --git a/src/app/+my-dspace-page/my-dspace-page.module.ts b/src/app/+my-dspace-page/my-dspace-page.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4b8cf37b7af4b192670e4ea46fa5282eb776bbb9
--- /dev/null
+++ b/src/app/+my-dspace-page/my-dspace-page.module.ts
@@ -0,0 +1,69 @@
+import { CommonModule } from '@angular/common';
+import { NgModule } from '@angular/core';
+
+import { SharedModule } from '../shared/shared.module';
+
+import { MyDspacePageRoutingModule } from './my-dspace-page-routing.module';
+import { MyDSpacePageComponent } from './my-dspace-page.component';
+import { SearchPageModule } from '../+search-page/search-page.module';
+import { MyDSpaceResultsComponent } from './my-dspace-results/my-dspace-results.component';
+import { WorkspaceitemMyDSpaceResultListElementComponent } from '../shared/object-list/my-dspace-result-list-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-list-element.component';
+import { ItemMyDSpaceResultListElementComponent } from '../shared/object-list/my-dspace-result-list-element/item-my-dspace-result/item-my-dspace-result-list-element.component';
+import { WorkflowitemMyDSpaceResultListElementComponent } from '../shared/object-list/my-dspace-result-list-element/workflowitem-my-dspace-result/workflowitem-my-dspace-result-list-element.component';
+import { ClaimedMyDSpaceResultListElementComponent } from '../shared/object-list/my-dspace-result-list-element/claimed-my-dspace-result/claimed-my-dspace-result-list-element.component';
+import { PoolMyDSpaceResultListElementComponent } from '../shared/object-list/my-dspace-result-list-element/pool-my-dspace-result/pool-my-dspace-result-list-element.component';
+import { MyDSpaceNewSubmissionComponent } from './my-dspace-new-submission/my-dspace-new-submission.component';
+import { ItemMyDSpaceResultDetailElementComponent } from '../shared/object-detail/my-dspace-result-detail-element/item-my-dspace-result/item-my-dspace-result-detail-element.component';
+import { WorkspaceitemMyDSpaceResultDetailElementComponent } from '../shared/object-detail/my-dspace-result-detail-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-detail-element.component';
+import { WorkflowitemMyDSpaceResultDetailElementComponent } from '../shared/object-detail/my-dspace-result-detail-element/workflowitem-my-dspace-result/workflowitem-my-dspace-result-detail-element.component';
+import { ClaimedMyDSpaceResultDetailElementComponent } from '../shared/object-detail/my-dspace-result-detail-element/claimed-my-dspace-result/claimed-my-dspace-result-detail-element.component';
+import { PoolMyDSpaceResultDetailElementComponent } from '../shared/object-detail/my-dspace-result-detail-element/pool-my-dspace-result/pool-my-dspace-result-detail-lement.component';
+import { MyDSpaceGuard } from './my-dspace.guard';
+import { MyDSpaceConfigurationService } from './my-dspace-configuration.service';
+
+@NgModule({
+  imports: [
+    CommonModule,
+    SharedModule,
+    MyDspacePageRoutingModule,
+    SearchPageModule
+  ],
+  declarations: [
+    MyDSpacePageComponent,
+    MyDSpaceResultsComponent,
+    ItemMyDSpaceResultListElementComponent,
+    WorkspaceitemMyDSpaceResultListElementComponent,
+    WorkflowitemMyDSpaceResultListElementComponent,
+    ClaimedMyDSpaceResultListElementComponent,
+    PoolMyDSpaceResultListElementComponent,
+    ItemMyDSpaceResultDetailElementComponent,
+    WorkspaceitemMyDSpaceResultDetailElementComponent,
+    WorkflowitemMyDSpaceResultDetailElementComponent,
+    ClaimedMyDSpaceResultDetailElementComponent,
+    PoolMyDSpaceResultDetailElementComponent,
+    MyDSpaceNewSubmissionComponent
+  ],
+  providers: [
+    MyDSpaceGuard,
+    MyDSpaceConfigurationService
+  ],
+  entryComponents: [
+    ItemMyDSpaceResultListElementComponent,
+    WorkspaceitemMyDSpaceResultListElementComponent,
+    WorkflowitemMyDSpaceResultListElementComponent,
+    ClaimedMyDSpaceResultListElementComponent,
+    PoolMyDSpaceResultListElementComponent,
+    ItemMyDSpaceResultDetailElementComponent,
+    WorkspaceitemMyDSpaceResultDetailElementComponent,
+    WorkflowitemMyDSpaceResultDetailElementComponent,
+    ClaimedMyDSpaceResultDetailElementComponent,
+    PoolMyDSpaceResultDetailElementComponent
+  ]
+})
+
+/**
+ * This module handles all components that are necessary for the mydspace page
+ */
+export class MyDSpacePageModule {
+
+}
diff --git a/src/app/+my-dspace-page/my-dspace-result.model.ts b/src/app/+my-dspace-page/my-dspace-result.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d300ed0bc8a804fd63c9a425a85e0de60818a68f
--- /dev/null
+++ b/src/app/+my-dspace-page/my-dspace-result.model.ts
@@ -0,0 +1,19 @@
+import { DSpaceObject } from '../core/shared/dspace-object.model';
+import { MetadataMap } from '../core/shared/metadata.models';
+import { ListableObject } from '../shared/object-collection/shared/listable-object.model';
+
+/**
+ * Represents a search result object of a certain (<T>) DSpaceObject
+ */
+export class MyDSpaceResult<T extends DSpaceObject> implements ListableObject {
+  /**
+   * The DSpaceObject that was found
+   */
+  indexableObject: T;
+
+  /**
+   * The metadata that was used to find this item, hithighlighted
+   */
+  hitHighlights: MetadataMap;
+
+}
diff --git a/src/app/+my-dspace-page/my-dspace-results/my-dspace-results.component.html b/src/app/+my-dspace-page/my-dspace-results/my-dspace-results.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..132a0d22040141c37952b2c8ad9937ff6977691c
--- /dev/null
+++ b/src/app/+my-dspace-page/my-dspace-results/my-dspace-results.component.html
@@ -0,0 +1,12 @@
+<div *ngIf="searchResults?.hasSucceeded && !searchResults?.isLoading && searchResults?.payload?.page.length > 0" @fadeIn>
+  <ds-viewable-collection
+    [config]="searchConfig.pagination"
+    [hasBorder]="hasBorder"
+    [sortConfig]="searchConfig.sort"
+    [objects]="searchResults"
+    [hideGear]="true">
+  </ds-viewable-collection>
+</div>
+<ds-loading *ngIf="isLoading()" message="{{'loading.mydspace-results' | translate}}"></ds-loading>
+<ds-error *ngIf="searchResults?.hasFailed && (!searchResults?.error || searchResults?.error?.statusCode != 400)" message="{{'error.search-results' | translate}}"></ds-error>
+<h3 *ngIf="searchResults?.payload?.page.length == 0" class="text-center text-muted" ><span>{{'mydspace.results.no-results' | translate}}</span></h3>
diff --git a/src/app/+my-dspace-page/my-dspace-results/my-dspace-results.component.spec.ts b/src/app/+my-dspace-page/my-dspace-results/my-dspace-results.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..67625706a6d586b5f654c6c4e767829a7a2d1167
--- /dev/null
+++ b/src/app/+my-dspace-page/my-dspace-results/my-dspace-results.component.spec.ts
@@ -0,0 +1,58 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { By } from '@angular/platform-browser';
+import { NoopAnimationsModule } from '@angular/platform-browser/animations';
+import { NO_ERRORS_SCHEMA } from '@angular/core';
+
+import { TranslateModule } from '@ngx-translate/core';
+import { QueryParamsDirectiveStub } from '../../shared/testing/query-params-directive-stub';
+import { MyDSpaceResultsComponent } from './my-dspace-results.component';
+
+describe('MyDSpaceResultsComponent', () => {
+  let comp: MyDSpaceResultsComponent;
+  let fixture: ComponentFixture<MyDSpaceResultsComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [TranslateModule.forRoot(), NoopAnimationsModule],
+      declarations: [
+        MyDSpaceResultsComponent,
+        QueryParamsDirectiveStub],
+      schemas: [NO_ERRORS_SCHEMA]
+    }).compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(MyDSpaceResultsComponent);
+    comp = fixture.componentInstance; // MyDSpaceResultsComponent test instance
+  });
+
+  it('should display results when results are not empty', () => {
+    (comp as any).searchResults = { hasSucceeded: true, isLoading: false, payload: { page: { length: 2 } } };
+    (comp as any).searchConfig = {};
+    fixture.detectChanges();
+    expect(fixture.debugElement.query(By.css('ds-viewable-collection'))).not.toBeNull();
+  });
+
+  it('should not display link when results are not empty', () => {
+    (comp as any).searchResults = { hasSucceeded: true, isLoading: false, payload: { page: { length: 2 } } };
+    (comp as any).searchConfig = {};
+    fixture.detectChanges();
+    expect(fixture.debugElement.query(By.css('a'))).toBeNull();
+  });
+
+  it('should display error message if error is != 400', () => {
+    (comp as any).searchResults = { hasFailed: true, error: { statusCode: 500 } };
+    fixture.detectChanges();
+    expect(fixture.debugElement.query(By.css('ds-error'))).not.toBeNull();
+  });
+
+  it('should display a message if search result is empty', () => {
+    (comp as any).searchResults = { payload: { page: { length: 0 } } };
+    (comp as any).searchConfig = { query: 'foobar' };
+    fixture.detectChanges();
+
+    const linkDes = fixture.debugElement.queryAll(By.css('text-muted'));
+
+    expect(linkDes).toBeDefined()
+  });
+});
diff --git a/src/app/+my-dspace-page/my-dspace-results/my-dspace-results.component.ts b/src/app/+my-dspace-page/my-dspace-results/my-dspace-results.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3a16def9c19232ed03af5041948813e1614374a9
--- /dev/null
+++ b/src/app/+my-dspace-page/my-dspace-results/my-dspace-results.component.ts
@@ -0,0 +1,51 @@
+import { Component, Input } from '@angular/core';
+
+import { RemoteData } from '../../core/data/remote-data';
+import { DSpaceObject } from '../../core/shared/dspace-object.model';
+import { fadeIn, fadeInOut } from '../../shared/animations/fade';
+import { MyDSpaceResult } from '../my-dspace-result.model';
+import { SearchOptions } from '../../+search-page/search-options.model';
+import { PaginatedList } from '../../core/data/paginated-list';
+import { ViewMode } from '../../core/shared/view-mode.model';
+import { isEmpty } from '../../shared/empty.util';
+
+/**
+ * Component that represents all results for mydspace page
+ */
+@Component({
+  selector: 'ds-my-dspace-results',
+  templateUrl: './my-dspace-results.component.html',
+  animations: [
+    fadeIn,
+    fadeInOut
+  ]
+})
+export class MyDSpaceResultsComponent {
+
+  /**
+   * The actual search result objects
+   */
+  @Input() searchResults: RemoteData<PaginatedList<MyDSpaceResult<DSpaceObject>>>;
+
+  /**
+   * The current configuration of the search
+   */
+  @Input() searchConfig: SearchOptions;
+
+  /**
+   * The current view mode for the search results
+   */
+  @Input() viewMode: ViewMode;
+
+  /**
+   * A boolean representing if search results entry are separated by a line
+   */
+  hasBorder = true;
+
+  /**
+   * Check if mydspace search results are loading
+   */
+  isLoading() {
+    return !this.searchResults || isEmpty(this.searchResults) || this.searchResults.isLoading;
+  }
+}
diff --git a/src/app/+my-dspace-page/my-dspace.guard.ts b/src/app/+my-dspace-page/my-dspace.guard.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9cb9aff485d7e39405d24fde0c7352e8376db85e
--- /dev/null
+++ b/src/app/+my-dspace-page/my-dspace.guard.ts
@@ -0,0 +1,57 @@
+import { Injectable } from '@angular/core';
+import { ActivatedRouteSnapshot, CanActivate, NavigationExtras, Router, RouterStateSnapshot } from '@angular/router';
+
+import { Observable } from 'rxjs';
+import { first, map } from 'rxjs/operators';
+import { isEmpty } from '../shared/empty.util';
+import { MYDSPACE_ROUTE } from './my-dspace-page.component';
+import { MyDSpaceConfigurationValueType } from './my-dspace-configuration-value-type';
+import { MyDSpaceConfigurationService } from './my-dspace-configuration.service';
+
+/**
+ * Prevent unauthorized activating and loading of mydspace configuration
+ * @class MyDSpaceGuard
+ */
+@Injectable()
+export class MyDSpaceGuard implements CanActivate {
+
+  /**
+   * @constructor
+   */
+  constructor(private configurationService: MyDSpaceConfigurationService, private router: Router) {
+  }
+
+  /**
+   * True when configuration is valid
+   * @method canActivate
+   */
+  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
+    return this.configurationService.getAvailableConfigurationTypes().pipe(
+      first(),
+      map((configurationList) => this.validateConfigurationParam(route.queryParamMap.get('configuration'), configurationList)));
+  }
+
+  /**
+   * Check if the given configuration is present in the list of those available
+   *
+   * @param configuration
+   *    the configuration to validate
+   * @param configurationList
+   *    the list of available configuration
+   *
+   */
+  private validateConfigurationParam(configuration: string, configurationList: MyDSpaceConfigurationValueType[]): boolean {
+    const configurationDefault: string = configurationList[0];
+    if (isEmpty(configuration) || !configurationList.includes(configuration as MyDSpaceConfigurationValueType)) {
+      // If configuration param is empty or is not included in available configurations redirect to a default configuration value
+      const navigationExtras: NavigationExtras = {
+        queryParams: {configuration: configurationDefault}
+      };
+
+      this.router.navigate([MYDSPACE_ROUTE], navigationExtras);
+      return false;
+    } else {
+      return true;
+    }
+  }
+}
diff --git a/src/app/+search-page/filtered-search-page.component.spec.ts b/src/app/+search-page/filtered-search-page.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5c49767ed2143dbeeb66a98fc18821496752ff97
--- /dev/null
+++ b/src/app/+search-page/filtered-search-page.component.spec.ts
@@ -0,0 +1,37 @@
+import { FilteredSearchPageComponent } from './filtered-search-page.component';
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { configureSearchComponentTestingModule } from './search-page.component.spec';
+import { SearchConfigurationService } from './search-service/search-configuration.service';
+
+describe('FilteredSearchPageComponent', () => {
+  let comp: FilteredSearchPageComponent;
+  let fixture: ComponentFixture<FilteredSearchPageComponent>;
+  let searchConfigService: SearchConfigurationService;
+
+  beforeEach(async(() => {
+    configureSearchComponentTestingModule(FilteredSearchPageComponent);
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(FilteredSearchPageComponent);
+    comp = fixture.componentInstance;
+    searchConfigService = (comp as any).searchConfigService;
+    fixture.detectChanges();
+  });
+
+  describe('when fixedFilterQuery is defined', () => {
+    const fixedFilterQuery = 'fixedFilterQuery';
+
+    beforeEach(() => {
+      spyOn(searchConfigService, 'updateFixedFilter').and.callThrough();
+      comp.fixedFilterQuery = fixedFilterQuery;
+      comp.ngOnInit();
+      fixture.detectChanges();
+    });
+
+    it('should update the paginated search options', () => {
+      expect(searchConfigService.updateFixedFilter).toHaveBeenCalledWith(fixedFilterQuery);
+    });
+  });
+
+});
diff --git a/src/app/+search-page/filtered-search-page.component.ts b/src/app/+search-page/filtered-search-page.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d577c2c44c542eeb07bc8a2918a2f3f81416fe5d
--- /dev/null
+++ b/src/app/+search-page/filtered-search-page.component.ts
@@ -0,0 +1,58 @@
+import { HostWindowService } from '../shared/host-window.service';
+import { SearchService } from './search-service/search.service';
+import { SearchSidebarService } from './search-sidebar/search-sidebar.service';
+import { SearchPageComponent } from './search-page.component';
+import { ChangeDetectionStrategy, Component, Inject, Input } from '@angular/core';
+import { pushInOut } from '../shared/animations/push';
+import { RouteService } from '../shared/services/route.service';
+import { SearchConfigurationService } from './search-service/search-configuration.service';
+import { Observable } from 'rxjs';
+import { PaginatedSearchOptions } from './paginated-search-options.model';
+import { SEARCH_CONFIG_SERVICE } from '../+my-dspace-page/my-dspace-page.component';
+
+/**
+ * This component renders a simple item page.
+ * The route parameter 'id' is used to request the item it represents.
+ * All fields of the item that should be displayed, are defined in its template.
+ */
+@Component({selector: 'ds-filtered-search-page',
+  styleUrls: ['./search-page.component.scss'],
+  templateUrl: './search-page.component.html',
+  changeDetection: ChangeDetectionStrategy.OnPush,
+  animations: [pushInOut],
+  providers: [
+    {
+      provide: SEARCH_CONFIG_SERVICE,
+      useClass: SearchConfigurationService
+    }
+  ]
+})
+
+export class FilteredSearchPageComponent extends SearchPageComponent {
+
+  /**
+   * The actual query for the fixed filter.
+   * If empty, the query will be determined by the route parameter called 'filter'
+   */
+  @Input() fixedFilterQuery: string;
+
+  constructor(protected service: SearchService,
+              protected sidebarService: SearchSidebarService,
+              protected windowService: HostWindowService,
+              @Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: SearchConfigurationService,
+              protected routeService: RouteService) {
+    super(service, sidebarService, windowService, searchConfigService, routeService);
+  }
+
+  /**
+   * Get the current paginated search options after updating the fixed filter using the fixedFilterQuery input
+   * This is to make sure the fixed filter is included in the paginated search options, as it is not part of any
+   * query or route parameters
+   * @returns {Observable<PaginatedSearchOptions>}
+   */
+  protected getSearchOptions(): Observable<PaginatedSearchOptions> {
+    this.searchConfigService.updateFixedFilter(this.fixedFilterQuery);
+    return this.searchConfigService.paginatedSearchOptions;
+  }
+
+}
diff --git a/src/app/+search-page/filtered-search-page.guard.ts b/src/app/+search-page/filtered-search-page.guard.ts
new file mode 100644
index 0000000000000000000000000000000000000000..39fbb48c67791149ca422bdd085ca26a049599b9
--- /dev/null
+++ b/src/app/+search-page/filtered-search-page.guard.ts
@@ -0,0 +1,24 @@
+import { Injectable } from '@angular/core';
+import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
+import { Observable } from 'rxjs';
+
+@Injectable()
+/**
+ * Assemble the correct i18n key for the filtered search page's title depending on the current route's filter parameter
+ * and title data.
+ * The format of the key will be "{title}{filter}.title" with:
+ * - title: The prefix of the key stored in route.data
+ * - filter: The current filter stored in route.params
+ */
+export class FilteredSearchPageGuard implements CanActivate {
+  canActivate(
+    route: ActivatedRouteSnapshot,
+    state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
+    const filter = route.params.filter;
+
+    const newTitle = route.data.title + filter + '.title';
+
+    route.data = { title: newTitle };
+    return true;
+  }
+}
diff --git a/src/app/+search-page/normalized-search-result.model.ts b/src/app/+search-page/normalized-search-result.model.ts
index 46f14c042d3da175f66f1147df33524a5bad977e..32f3217b54b66ef397587c35f8c3d34e94d880ae 100644
--- a/src/app/+search-page/normalized-search-result.model.ts
+++ b/src/app/+search-page/normalized-search-result.model.ts
@@ -1,4 +1,4 @@
-import { autoserialize } from 'cerialize';
+import { autoserialize, autoserializeAs } from 'cerialize';
 import { MetadataMap } from '../core/shared/metadata.models';
 import { ListableObject } from '../shared/object-collection/shared/listable-object.model';
 
@@ -10,7 +10,7 @@ export class NormalizedSearchResult implements ListableObject {
    * The UUID of the DSpaceObject that was found
    */
   @autoserialize
-  dspaceObject: string;
+  indexableObject: string;
 
   /**
    * The metadata that was used to find this item, hithighlighted
diff --git a/src/app/+search-page/paginated-search-options.model.ts b/src/app/+search-page/paginated-search-options.model.ts
index 8f4d93b0df1f5ffc4d153768b269aa31106f2276..45cd0b8f09f72d28e8cd0928a0e6567cac18c38e 100644
--- a/src/app/+search-page/paginated-search-options.model.ts
+++ b/src/app/+search-page/paginated-search-options.model.ts
@@ -12,7 +12,7 @@ export class PaginatedSearchOptions extends SearchOptions {
   pagination?: PaginationComponentOptions;
   sort?: SortOptions;
 
-  constructor(options: {scope?: string, query?: string, dsoType?: DSpaceObjectType, filters?: SearchFilter[], pagination?: PaginationComponentOptions, sort?: SortOptions}) {
+  constructor(options: {configuration?: string, scope?: string, query?: string, dsoType?: DSpaceObjectType, filters?: SearchFilter[], fixedFilter?: any, pagination?: PaginationComponentOptions, sort?: SortOptions}) {
     super(options);
     this.pagination = options.pagination;
     this.sort = options.sort;
diff --git a/src/app/+search-page/search-filters/search-filter/search-authority-filter/search-authority-filter.component.html b/src/app/+search-page/search-filters/search-filter/search-authority-filter/search-authority-filter.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..76cdc6c8f57f8c789ce3344c4c2ce820480982f9
--- /dev/null
+++ b/src/app/+search-page/search-filters/search-filter/search-authority-filter/search-authority-filter.component.html
@@ -0,0 +1,27 @@
+<div>
+    <div class="filters py-2">
+        <ds-search-facet-selected-option *ngFor="let value of (selectedValues$ | async)" [selectedValue]="value" [filterConfig]="filterConfig" [selectedValues$]="selectedValues$" [inPlaceSearch]="inPlaceSearch"></ds-search-facet-selected-option>
+        <ng-container *ngFor="let page of (filterValues$ | async)?.payload">
+          <div [@facetLoad]="animationState">
+                  <ds-search-facet-option *ngFor="let value of page.page; trackBy: trackUpdate" [filterConfig]="filterConfig" [filterValue]="value" [selectedValues$]="selectedValues$" [inPlaceSearch]="inPlaceSearch"></ds-search-facet-option>
+          </div>
+        </ng-container>
+        <div class="clearfix toggle-more-filters">
+            <a class="float-left" *ngIf="!(isLastPage$ | async)"
+               (click)="showMore()">{{"search.filters.filter.show-more"
+                | translate}}</a>
+            <a class="float-right" *ngIf="(currentPage | async) > 1"
+               (click)="showFirstPageOnly()">{{"search.filters.filter.show-less"
+                | translate}}</a>
+        </div>
+    </div>
+    <ds-input-suggestions [suggestions]="(filterSearchResults | async)"
+                          [placeholder]="'search.filters.filter.' + filterConfig.name + '.placeholder'| translate"
+                          [action]="getCurrentUrl()"
+                          [name]="filterConfig.paramName"
+                          [(ngModel)]="filter"
+                          (submitSuggestion)="onSubmit($event)"
+                          (clickSuggestion)="onSubmit($event)"
+                          (findSuggestions)="findSuggestions($event)"
+                          ngDefaultControl></ds-input-suggestions>
+</div>
diff --git a/src/app/+search-page/search-filters/search-filter/search-authority-filter/search-authority-filter.component.scss b/src/app/+search-page/search-filters/search-filter/search-authority-filter/search-authority-filter.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..33e354f2d8a6b3f6ff4c841465428b9b7653e47f
--- /dev/null
+++ b/src/app/+search-page/search-filters/search-filter/search-authority-filter/search-authority-filter.component.scss
@@ -0,0 +1,23 @@
+@import '../../../../../styles/variables.scss';
+@import '../../../../../styles/mixins.scss';
+
+.filters {
+    a {
+        color: $body-color;
+        &:hover, &focus {
+          text-decoration: none;
+        }
+        span.badge {
+            vertical-align: text-top;
+        }
+    }
+    .toggle-more-filters a {
+        color: $link-color;
+        text-decoration: underline;
+        cursor: pointer;
+    }
+}
+::ng-deep em {
+    font-weight: bold;
+    font-style: normal;
+}
diff --git a/src/app/+search-page/search-filters/search-filter/search-authority-filter/search-authority-filter.component.ts b/src/app/+search-page/search-filters/search-filter/search-authority-filter/search-authority-filter.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..83131e1344bd3883b60e45aa8468927677aa0fc0
--- /dev/null
+++ b/src/app/+search-page/search-filters/search-filter/search-authority-filter/search-authority-filter.component.ts
@@ -0,0 +1,36 @@
+import { Component, OnInit } from '@angular/core';
+
+import { FilterType } from '../../../search-service/filter-type.model';
+import { facetLoad, SearchFacetFilterComponent } from '../search-facet-filter/search-facet-filter.component';
+import { renderFacetFor } from '../search-filter-type-decorator';
+import { FacetValue } from '../../../search-service/facet-value.model';
+
+@Component({
+  selector: 'ds-search-authority-filter',
+  styleUrls: ['./search-authority-filter.component.scss'],
+  templateUrl: './search-authority-filter.component.html',
+  animations: [facetLoad]
+})
+
+/**
+ * Component that represents an authority facet for a specific filter configuration
+ */
+@renderFacetFor(FilterType.authority)
+export class SearchAuthorityFilterComponent extends SearchFacetFilterComponent implements OnInit {
+
+  /**
+   * TODO to review after https://github.com/DSpace/dspace-angular/issues/368 is resolved
+   * Retrieve facet value from search link
+   */
+  protected getFacetValue(facet: FacetValue): string {
+    const search = facet.search;
+    const hashes = search.slice(search.indexOf('?') + 1).split('&');
+    const params = {};
+    hashes.map((hash) => {
+      const [key, val] = hash.split('=');
+      params[key] = decodeURIComponent(val)
+    });
+
+    return params[this.filterConfig.paramName];
+  }
+}
diff --git a/src/app/+search-page/search-filters/search-filter/search-boolean-filter/search-boolean-filter.component.html b/src/app/+search-page/search-filters/search-filter/search-boolean-filter/search-boolean-filter.component.html
index 968bf9e42098070f28f6bddddd24abb6adfa2d47..cc39b80db833efdeaf9740e47d958a1324474865 100644
--- a/src/app/+search-page/search-filters/search-filter/search-boolean-filter/search-boolean-filter.component.html
+++ b/src/app/+search-page/search-filters/search-filter/search-boolean-filter/search-boolean-filter.component.html
@@ -1,9 +1,9 @@
 <div>
     <div class="filters py-2">
-        <ds-search-facet-selected-option *ngFor="let value of (selectedValues$ | async)" [selectedValue]="value" [filterConfig]="filterConfig" [selectedValues$]="selectedValues$"></ds-search-facet-selected-option>
+        <ds-search-facet-selected-option *ngFor="let value of (selectedValues$ | async)" [selectedValue]="value" [filterConfig]="filterConfig" [selectedValues$]="selectedValues$" [inPlaceSearch]="inPlaceSearch"></ds-search-facet-selected-option>
       <ng-container *ngFor="let page of (filterValues$ | async)?.payload">
         <div [@facetLoad]="animationState">
-            <ds-search-facet-option *ngFor="let value of page.page; trackBy: trackUpdate" [filterConfig]="filterConfig" [filterValue]="value" [selectedValues$]="selectedValues$"></ds-search-facet-option>
+            <ds-search-facet-option *ngFor="let value of page.page; trackBy: trackUpdate" [filterConfig]="filterConfig" [filterValue]="value" [selectedValues$]="selectedValues$" [inPlaceSearch]="inPlaceSearch"></ds-search-facet-option>
         </div>
         </ng-container>
         <div class="clearfix toggle-more-filters">
diff --git a/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.spec.ts b/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.spec.ts
index f1dbedfb40f5878bdeee99f4cbb88b7e94dd1ae3..245c0e3ddb1da1370df4c8a8f626c667323d0381 100644
--- a/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.spec.ts
+++ b/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.spec.ts
@@ -19,10 +19,12 @@ import { By } from '@angular/platform-browser';
 describe('SearchFacetOptionComponent', () => {
   let comp: SearchFacetOptionComponent;
   let fixture: ComponentFixture<SearchFacetOptionComponent>;
-  const filterName1 = 'test name';
+  const filterName1 = 'testname';
+  const filterName2 = 'testAuthorityname';
   const value1 = 'testvalue1';
   const value2 = 'test2';
-  const value3 = 'another value3';
+  const operator = 'authority';
+
   const mockFilterConfig = Object.assign(new SearchFilterConfig(), {
     name: filterName1,
     type: FilterType.range,
@@ -32,14 +34,38 @@ describe('SearchFacetOptionComponent', () => {
     minValue: 200,
     maxValue: 3000,
   });
+
+  const mockAuthorityFilterConfig = Object.assign(new SearchFilterConfig(), {
+    name: filterName2,
+    type: FilterType.authority,
+    hasFacets: false,
+    isOpenByDefault: false,
+    pageSize: 2
+  });
+
   const value: FacetValue = {
-      value: value2,
-      count: 20,
-      search: ''
-    };
+    label: value2,
+    value: value2,
+    count: 20,
+    search: ``
+  };
+
+  const selectedValue: FacetValue = {
+    label: value1,
+    value: value1,
+    count: 20,
+    search: `http://test.org/api/discover/search/objects?f.${filterName1}=${value1},${operator}`
+  };
+
+  const authorityValue: FacetValue = {
+    label: value2,
+    value: value2,
+    count: 20,
+    search: `http://test.org/api/discover/search/objects?f.${filterName2}=${value2},${operator}`
+  };
 
   const searchLink = '/search';
-  const selectedValues = [value1];
+  const selectedValues = [selectedValue];
   const selectedValues$ = observableOf(selectedValues);
   let filterService;
   let searchService;
@@ -90,7 +116,7 @@ describe('SearchFacetOptionComponent', () => {
     fixture.detectChanges();
   });
 
-  describe('when the updateAddParams method is called wih a value', () => {
+  describe('when the updateAddParams method is called with a value', () => {
     it('should update the addQueryParams with the new parameter values', () => {
       comp.addQueryParams = {};
       (comp as any).updateAddParams(selectedValues);
@@ -101,6 +127,21 @@ describe('SearchFacetOptionComponent', () => {
     });
   });
 
+  describe('when filter type is authority and the updateAddParams method is called with a value', () => {
+    it('should update the addQueryParams with the new parameter values', () => {
+      comp.filterValue = authorityValue;
+      comp.filterConfig = mockAuthorityFilterConfig;
+      fixture.detectChanges();
+
+      comp.addQueryParams = {};
+      (comp as any).updateAddParams(selectedValues);
+      expect(comp.addQueryParams).toEqual({
+        [mockAuthorityFilterConfig.paramName]: [value1, `${value2},${operator}`],
+        page: 1
+      });
+    });
+  });
+
   describe('when isVisible emits true', () => {
     it('the facet option should be visible', () => {
       comp.isVisible = observableOf(true);
diff --git a/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.ts b/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.ts
index 016ebf62a372fda6496720ff8440bd50a395dec1..1fccee3736276e42762798f2855fd68bdc8b3c01 100644
--- a/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.ts
+++ b/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.ts
@@ -1,5 +1,5 @@
 import { combineLatest as observableCombineLatest, Observable, Subscription } from 'rxjs';
-import { map, take } from 'rxjs/operators';
+import { map } from 'rxjs/operators';
 import { Component, Input, OnDestroy, OnInit } from '@angular/core';
 import { Router } from '@angular/router';
 import { FacetValue } from '../../../../search-service/facet-value.model';
@@ -8,6 +8,7 @@ import { SearchService } from '../../../../search-service/search.service';
 import { SearchFilterService } from '../../search-filter.service';
 import { SearchConfigurationService } from '../../../../search-service/search-configuration.service';
 import { hasValue } from '../../../../../shared/empty.util';
+import { FilterType } from '../../../../search-service/filter-type.model';
 
 @Component({
   selector: 'ds-search-facet-option',
@@ -32,7 +33,12 @@ export class SearchFacetOptionComponent implements OnInit, OnDestroy {
   /**
    * Emits the active values for this filter
    */
-  @Input() selectedValues$: Observable<string[]>;
+  @Input() selectedValues$: Observable<FacetValue[]>;
+
+  /**
+   * True when the search component should show results on the current page
+   */
+  @Input() inPlaceSearch;
 
   /**
    * Emits true when this option should be visible and false when it should be invisible
@@ -71,13 +77,16 @@ export class SearchFacetOptionComponent implements OnInit, OnDestroy {
    * Checks if a value for this filter is currently active
    */
   private isChecked(): Observable<boolean> {
-    return this.filterService.isFilterActiveWithValue(this.filterConfig.paramName, this.filterValue.value);
+    return this.filterService.isFilterActiveWithValue(this.filterConfig.paramName, this.getFacetValue());
   }
 
   /**
-   * @returns {string} The base path to the search page
+   * @returns {string} The base path to the search page, or the current page when inPlaceSearch is true
    */
-  getSearchLink() {
+  public getSearchLink(): string {
+    if (this.inPlaceSearch) {
+      return './';
+    }
     return this.searchService.getSearchLink();
   }
 
@@ -85,13 +94,33 @@ export class SearchFacetOptionComponent implements OnInit, OnDestroy {
    * Calculates the parameters that should change if a given value for this filter would be added to the active filters
    * @param {string[]} selectedValues The values that are currently selected for this filter
    */
-  private updateAddParams(selectedValues: string[]): void {
+  private updateAddParams(selectedValues: FacetValue[]): void {
     this.addQueryParams = {
-      [this.filterConfig.paramName]: [...selectedValues, this.filterValue.value],
+      [this.filterConfig.paramName]: [...selectedValues.map((facetValue: FacetValue) => facetValue.label), this.getFacetValue()],
       page: 1
     };
   }
 
+  /**
+   * TODO to review after https://github.com/DSpace/dspace-angular/issues/368 is resolved
+   * Retrieve facet value related to facet type
+   */
+  private getFacetValue(): string {
+    if (this.filterConfig.type === FilterType.authority) {
+      const search = this.filterValue.search;
+      const hashes = search.slice(search.indexOf('?') + 1).split('&');
+      const params = {};
+      hashes.map((hash) => {
+        const [key, val] = hash.split('=');
+        params[key] = decodeURIComponent(val)
+      });
+
+      return params[this.filterConfig.paramName];
+    } else {
+      return this.filterValue.value;
+    }
+  }
+
   /**
    * Make sure the subscription is unsubscribed from when this component is destroyed
    */
diff --git a/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-range-option/search-facet-range-option.component.html b/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-range-option/search-facet-range-option.component.html
index b485fe0fd081156ea33a6c51ddc6ee3854c7bdb4..8e8ad9b4e3ea4e0b7df2faf3b06c442b5b97b014 100644
--- a/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-range-option/search-facet-range-option.component.html
+++ b/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-range-option/search-facet-range-option.component.html
@@ -1,8 +1,8 @@
 <a *ngIf="isVisible | async" class="d-flex flex-row"
    [routerLink]="[getSearchLink()]"
    [queryParams]="changeQueryParams" queryParamsHandling="merge">
-    <span class="filter-value px-1">{{filterValue.value}}</span>
+    <span class="filter-value px-1">{{filterValue.label}}</span>
     <span class="float-right filter-value-count ml-auto">
                         <span class="badge badge-secondary badge-pill">{{filterValue.count}}</span>
                     </span>
-</a>
\ No newline at end of file
+</a>
diff --git a/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-range-option/search-facet-range-option.component.spec.ts b/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-range-option/search-facet-range-option.component.spec.ts
index 218730263bbf125942ad254986c152049d6c76c3..d3264214edb6c589ea63dc794425350ab421c058 100644
--- a/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-range-option/search-facet-range-option.component.spec.ts
+++ b/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-range-option/search-facet-range-option.component.spec.ts
@@ -35,10 +35,11 @@ describe('SearchFacetRangeOptionComponent', () => {
     maxValue: 3000,
   });
   const value: FacetValue = {
-      value: value2,
-      count: 20,
-      search: ''
-    };
+    label: value2,
+    value: value2,
+    count: 20,
+    search: ''
+  };
 
   const searchLink = '/search';
   let filterService;
@@ -92,10 +93,11 @@ describe('SearchFacetRangeOptionComponent', () => {
     it('should update the changeQueryParams with the new parameter values', () => {
       comp.changeQueryParams = {};
       comp.filterValue = {
-          value: '50-60',
-          count: 20,
-          search: ''
-        };
+        label: '50-60',
+        value: '50-60',
+        count: 20,
+        search: ''
+      };
       (comp as any).updateChangeParams();
       expect(comp.changeQueryParams).toEqual({
         [mockFilterConfig.paramName + RANGE_FILTER_MIN_SUFFIX]: ['50'],
diff --git a/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-range-option/search-facet-range-option.component.ts b/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-range-option/search-facet-range-option.component.ts
index 67d31293b0e1bbbca05e91ec588b0665d66306b6..54d5d535df8905e2adff3f4fa795e6e24945c022 100644
--- a/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-range-option/search-facet-range-option.component.ts
+++ b/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-range-option/search-facet-range-option.component.ts
@@ -35,6 +35,11 @@ export class SearchFacetRangeOptionComponent implements OnInit, OnDestroy {
    */
   @Input() filterConfig: SearchFilterConfig;
 
+  /**
+   * True when the search component should show results on the current page
+   */
+  @Input() inPlaceSearch;
+
   /**
    * Emits true when this option should be visible and false when it should be invisible
    */
@@ -75,9 +80,12 @@ export class SearchFacetRangeOptionComponent implements OnInit, OnDestroy {
   }
 
   /**
-   * @returns {string} The base path to the search page
+   * @returns {string} The base path to the search page, or the current page when inPlaceSearch is true
    */
-  getSearchLink() {
+  public getSearchLink(): string {
+    if (this.inPlaceSearch) {
+      return './';
+    }
     return this.searchService.getSearchLink();
   }
 
diff --git a/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.html b/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.html
index ba43bae100c56aab0ded8947e4a6fdeb19248383..5657bd224e11d9fb5603d0e4000719510049545f 100644
--- a/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.html
+++ b/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.html
@@ -2,5 +2,5 @@
    [routerLink]="[getSearchLink()]"
    [queryParams]="removeQueryParams" queryParamsHandling="merge">
     <input type="checkbox" [checked]="true" class="my-1 align-self-stretch"/>
-    <span class="filter-value pl-1">{{selectedValue}}</span>
-</a>
\ No newline at end of file
+    <span class="filter-value pl-1 text-capitalize">{{selectedValue.label}}</span>
+</a>
diff --git a/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.spec.ts b/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.spec.ts
index 545ba1d66bf2b94051b16c78e62803d42ac6a2fb..01defb9893b3a6cd7c0a2f79d2848ee1fc3f55b5 100644
--- a/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.spec.ts
+++ b/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.spec.ts
@@ -13,13 +13,18 @@ import { RouterStub } from '../../../../../shared/testing/router-stub';
 import { SearchConfigurationService } from '../../../../search-service/search-configuration.service';
 import { SearchFilterService } from '../../search-filter.service';
 import { SearchFacetSelectedOptionComponent } from './search-facet-selected-option.component';
+import { FacetValue } from '../../../../search-service/facet-value.model';
 
 describe('SearchFacetSelectedOptionComponent', () => {
   let comp: SearchFacetSelectedOptionComponent;
   let fixture: ComponentFixture<SearchFacetSelectedOptionComponent>;
   const filterName1 = 'test name';
+  const filterName2 = 'testAuthorityname';
+  const label1 = 'test value 1';
   const value1 = 'testvalue1';
+  const label2 = 'test 2';
   const value2 = 'test2';
+  const operator = 'authority';
   const mockFilterConfig = Object.assign(new SearchFilterConfig(), {
     name: filterName1,
     type: FilterType.range,
@@ -29,10 +34,55 @@ describe('SearchFacetSelectedOptionComponent', () => {
     minValue: 200,
     maxValue: 3000,
   });
+  const mockAuthorityFilterConfig = Object.assign(new SearchFilterConfig(), {
+    name: filterName2,
+    type: FilterType.authority,
+    hasFacets: false,
+    isOpenByDefault: false,
+    pageSize: 2
+  });
 
   const searchLink = '/search';
-  const selectedValues = [value1, value2];
+  const selectedValue: FacetValue = {
+    label: value1,
+    value: value1,
+    count: 20,
+    search: `http://test.org/api/discover/search/objects?f.${filterName1}=${value1}`
+  };
+  const selectedValue2: FacetValue = {
+    label: value2,
+    value: value2,
+    count: 20,
+    search: `http://test.org/api/discover/search/objects?f.${filterName1}=${value2}`
+  };
+  const selectedAuthorityValue: FacetValue = {
+    label: label1,
+    value: value1,
+    count: 20,
+    search: `http://test.org/api/discover/search/objects?f.${filterName2}=${value1},${operator}`
+  };
+  const selectedAuthorityValue2: FacetValue = {
+    label: label2,
+    value: value2,
+    count: 20,
+    search: `http://test.org/api/discover/search/objects?f.${filterName2}=${value2},${operator}`
+  };
+  const selectedValues = [selectedValue, selectedValue2];
+  const selectedAuthorityValues = [selectedAuthorityValue, selectedAuthorityValue2];
+  const facetValue = {
+    label: value2,
+    value: value2,
+    count: 1,
+    search: ''
+  };
+  const authorityValue: FacetValue = {
+    label: label2,
+    value: value2,
+    count: 20,
+    search: `http://test.org/api/discover/search/objects?f.${filterName2}=${value2},${operator}`
+  };
   const selectedValues$ = observableOf(selectedValues);
+  const selectedAuthorityValues$ = observableOf(selectedAuthorityValues);
   let filterService;
   let searchService;
   let router;
@@ -76,7 +126,7 @@ describe('SearchFacetSelectedOptionComponent', () => {
     filterService = (comp as any).filterService;
     searchService = (comp as any).searchService;
     router = (comp as any).router;
-    comp.selectedValue = value2;
+    comp.selectedValue = facetValue;
     comp.selectedValues$ = selectedValues$;
     comp.filterConfig = mockFilterConfig;
     fixture.detectChanges();
@@ -92,4 +142,20 @@ describe('SearchFacetSelectedOptionComponent', () => {
       });
     });
   });
+
+  describe('when filter type is authority and the updateRemoveParams method is called with a value', () => {
+    it('should update the removeQueryParams with the new parameter values', () => {
+      spyOn(filterService, 'getSelectedValuesForFilter').and.returnValue(selectedAuthorityValues);
+      comp.selectedValue = authorityValue;
+      comp.selectedValues$ = selectedAuthorityValues$;
+      comp.filterConfig = mockAuthorityFilterConfig;
+      comp.removeQueryParams = {};
+      fixture.detectChanges();
+      (comp as any).updateRemoveParams(selectedAuthorityValues);
+      expect(comp.removeQueryParams).toEqual({
+        [mockAuthorityFilterConfig.paramName]: [`${value1},${operator}`],
+        page: 1
+      });
+    });
+  });
 });
diff --git a/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.ts b/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.ts
index 23ad3eccba43e57f7be1fb8164352851250a487b..78dde92c2b486eed484f93a77bf1d894df32b9cc 100644
--- a/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.ts
+++ b/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.ts
@@ -6,6 +6,8 @@ import { SearchService } from '../../../../search-service/search.service';
 import { SearchFilterService } from '../../search-filter.service';
 import { hasValue } from '../../../../../shared/empty.util';
 import { SearchConfigurationService } from '../../../../search-service/search-configuration.service';
+import { FacetValue } from '../../../../search-service/facet-value.model';
+import { FilterType } from '../../../../search-service/filter-type.model';
 
 @Component({
   selector: 'ds-search-facet-selected-option',
@@ -20,7 +22,7 @@ export class SearchFacetSelectedOptionComponent implements OnInit, OnDestroy {
   /**
    * The value for this component
    */
-  @Input() selectedValue: string;
+  @Input() selectedValue: FacetValue;
 
   /**
    * The filter configuration for this facet option
@@ -30,7 +32,12 @@ export class SearchFacetSelectedOptionComponent implements OnInit, OnDestroy {
   /**
    * Emits the active values for this filter
    */
-  @Input() selectedValues$: Observable<string[]>;
+  @Input() selectedValues$: Observable<FacetValue[]>;
+
+  /**
+   * True when the search component should show results on the current page
+   */
+  @Input() inPlaceSearch;
 
   /**
    * UI parameters when this filter is removed
@@ -60,9 +67,12 @@ export class SearchFacetSelectedOptionComponent implements OnInit, OnDestroy {
   }
 
   /**
-   * @returns {string} The base path to the search page
+   * @returns {string} The base path to the search page, or the current page when inPlaceSearch is true
    */
-  getSearchLink() {
+  public getSearchLink(): string {
+    if (this.inPlaceSearch) {
+      return './';
+    }
     return this.searchService.getSearchLink();
   }
 
@@ -70,13 +80,35 @@ export class SearchFacetSelectedOptionComponent implements OnInit, OnDestroy {
    * Calculates the parameters that should change if a given value for this filter would be removed from the active filters
    * @param {string[]} selectedValues The values that are currently selected for this filter
    */
-  private updateRemoveParams(selectedValues: string[]): void {
+  private updateRemoveParams(selectedValues: FacetValue[]): void {
     this.removeQueryParams = {
-      [this.filterConfig.paramName]: selectedValues.filter((v) => v !== this.selectedValue),
+      [this.filterConfig.paramName]: selectedValues
+        .filter((facetValue: FacetValue) => facetValue.label !== this.selectedValue.label)
+        .map((facetValue: FacetValue) => this.getFacetValue(facetValue)),
       page: 1
     };
   }
 
+  /**
+   * TODO to review after https://github.com/DSpace/dspace-angular/issues/368 is resolved
+   * Retrieve facet value related to facet type
+   */
+  private getFacetValue(facetValue: FacetValue): string {
+    if (this.filterConfig.type === FilterType.authority) {
+      const search = facetValue.search;
+      const hashes = search.slice(search.indexOf('?') + 1).split('&');
+      const params = {};
+      hashes.map((hash) => {
+        const [key, val] = hash.split('=');
+        params[key] = decodeURIComponent(val)
+      });
+
+      return params[this.filterConfig.paramName];
+    } else {
+      return facetValue.value;
+    }
+  }
+
   /**
    * Make sure the subscription is unsubscribed from when this component is destroyed
    */
diff --git a/src/app/+search-page/search-filters/search-filter/search-facet-filter-wrapper/search-facet-filter-wrapper.component.ts b/src/app/+search-page/search-filters/search-filter/search-facet-filter-wrapper/search-facet-filter-wrapper.component.ts
index 6369a7691edacdfb7b8f1bbadc4d8930bf317bd5..6720b306813c6e9f867e642445680affff012d0d 100644
--- a/src/app/+search-page/search-filters/search-filter/search-facet-filter-wrapper/search-facet-filter-wrapper.component.ts
+++ b/src/app/+search-page/search-filters/search-filter/search-facet-filter-wrapper/search-facet-filter-wrapper.component.ts
@@ -2,7 +2,7 @@ import { Component, Injector, Input, OnInit } from '@angular/core';
 import { renderFilterType } from '../search-filter-type-decorator';
 import { FilterType } from '../../../search-service/filter-type.model';
 import { SearchFilterConfig } from '../../../search-service/search-filter-config.model';
-import { FILTER_CONFIG } from '../search-filter.service';
+import { FILTER_CONFIG, IN_PLACE_SEARCH } from '../search-filter.service';
 import { GenericConstructor } from '../../../../core/shared/generic-constructor';
 import { SearchFacetFilterComponent } from '../search-facet-filter/search-facet-filter.component';
 
@@ -20,6 +20,11 @@ export class SearchFacetFilterWrapperComponent implements OnInit {
    */
   @Input() filterConfig: SearchFilterConfig;
 
+  /**
+   * True when the search component should show results on the current page
+   */
+  @Input() inPlaceSearch;
+
   /**
    * The constructor of the search facet filter that should be rendered, based on the filter config's type
    */
@@ -39,7 +44,8 @@ export class SearchFacetFilterWrapperComponent implements OnInit {
     this.searchFilter = this.getSearchFilter();
     this.objectInjector = Injector.create({
       providers: [
-        { provide: FILTER_CONFIG, useFactory: () => (this.filterConfig), deps: [] }
+        { provide: FILTER_CONFIG, useFactory: () => (this.filterConfig), deps: [] },
+        { provide: IN_PLACE_SEARCH, useFactory: () => (this.inPlaceSearch), deps: [] }
       ],
       parent: this.injector
     });
diff --git a/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.spec.ts b/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.spec.ts
index cb3d4730b4f17516469064c0f1330b51271a0719..5d8b51de960e02ce773a603357f8ea475075da51 100644
--- a/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.spec.ts
+++ b/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.spec.ts
@@ -2,7 +2,7 @@ import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
 import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 import { TranslateModule } from '@ngx-translate/core';
 import { NoopAnimationsModule } from '@angular/platform-browser/animations';
-import { FILTER_CONFIG, SearchFilterService } from '../search-filter.service';
+import { FILTER_CONFIG, IN_PLACE_SEARCH, SearchFilterService } from '../search-filter.service';
 import { SearchFilterConfig } from '../../../search-service/search-filter-config.model';
 import { FilterType } from '../../../search-service/filter-type.model';
 import { FacetValue } from '../../../search-service/facet-value.model';
@@ -17,7 +17,9 @@ import { Router } from '@angular/router';
 import { PageInfo } from '../../../../core/shared/page-info.model';
 import { SearchFacetFilterComponent } from './search-facet-filter.component';
 import { RemoteDataBuildService } from '../../../../core/cache/builders/remote-data-build.service';
-import { SearchConfigurationService } from '../../../search-service/search-configuration.service';
+import { SearchConfigurationServiceStub } from '../../../../shared/testing/search-configuration-service-stub';
+import { SEARCH_CONFIG_SERVICE } from '../../../../+my-dspace-page/my-dspace-page.component';
+import { tap } from 'rxjs/operators';
 
 describe('SearchFacetFilterComponent', () => {
   let comp: SearchFacetFilterComponent;
@@ -35,14 +37,17 @@ describe('SearchFacetFilterComponent', () => {
   });
   const values: FacetValue[] = [
     {
+      label: value1,
       value: value1,
       count: 52,
       search: ''
     }, {
+      label: value2,
       value: value2,
       count: 20,
       search: ''
     }, {
+      label: value3,
       value: value3,
       count: 5,
       search: ''
@@ -65,8 +70,9 @@ describe('SearchFacetFilterComponent', () => {
         { provide: SearchService, useValue: new SearchServiceStub(searchLink) },
         { provide: Router, useValue: new RouterStub() },
         { provide: FILTER_CONFIG, useValue: new SearchFilterConfig() },
-        { provide: RemoteDataBuildService, useValue: {aggregate: () => observableOf({})} },
-        { provide: SearchConfigurationService, useValue: {searchOptions: observableOf({})} },
+        { provide: RemoteDataBuildService, useValue: { aggregate: () => observableOf({}) } },
+        { provide: SEARCH_CONFIG_SERVICE, useValue: new SearchConfigurationServiceStub() },
+        { provide: IN_PLACE_SEARCH, useValue: false },
         {
           provide: SearchFilterService, useValue: {
             getSelectedValuesForFilter: () => observableOf(selectedValues),
@@ -168,13 +174,20 @@ describe('SearchFacetFilterComponent', () => {
     const searchUrl = '/search/path';
     const testValue = 'test';
     const data = testValue;
+
     beforeEach(() => {
+      comp.selectedValues$ = observableOf(selectedValues.map((value) =>
+        Object.assign(new FacetValue(), {
+          label: value,
+          value: value
+        })));
+      fixture.detectChanges();
       spyOn(comp, 'getSearchLink').and.returnValue(searchUrl);
       comp.onSubmit(data);
     });
 
     it('should call navigate on the router with the right searchlink and parameters', () => {
-      expect(router.navigate).toHaveBeenCalledWith([searchUrl], {
+      expect(router.navigate).toHaveBeenCalledWith(searchUrl.split('/'), {
         queryParams: { [mockFilterConfig.paramName]: [...selectedValues, testValue] },
         queryParamsHandling: 'merge'
       });
@@ -188,9 +201,9 @@ describe('SearchFacetFilterComponent', () => {
     });
 
     it('should call showFirstPageOnly and empty the filter', () => {
-        expect(comp.animationState).toEqual('loading');
-        expect((comp as any).collapseNextUpdate).toBeTruthy();
-        expect(comp.filter).toEqual('');
+      expect(comp.animationState).toEqual('loading');
+      expect((comp as any).collapseNextUpdate).toBeTruthy();
+      expect(comp.filter).toEqual('');
     });
   });
 
diff --git a/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts b/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts
index 367947a37775c66d569eaa49df5cce862e470444..772240eb0b597f90f507ffdadd78c35ff36a6df2 100644
--- a/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts
+++ b/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts
@@ -6,7 +6,7 @@ import {
   Subject,
   Subscription
 } from 'rxjs';
-import { switchMap, distinctUntilChanged, map, take } from 'rxjs/operators';
+import { switchMap, distinctUntilChanged, map, take, flatMap, tap } from 'rxjs/operators';
 import { animate, state, style, transition, trigger } from '@angular/animations';
 import { Component, Inject, OnDestroy, OnInit } from '@angular/core';
 import { Router } from '@angular/router';
@@ -18,11 +18,12 @@ import { EmphasizePipe } from '../../../../shared/utils/emphasize.pipe';
 import { FacetValue } from '../../../search-service/facet-value.model';
 import { SearchFilterConfig } from '../../../search-service/search-filter-config.model';
 import { SearchService } from '../../../search-service/search.service';
-import { FILTER_CONFIG, SearchFilterService } from '../search-filter.service';
+import { FILTER_CONFIG, IN_PLACE_SEARCH, SearchFilterService } from '../search-filter.service';
 import { SearchConfigurationService } from '../../../search-service/search-configuration.service';
 import { getSucceededRemoteData } from '../../../../core/shared/operators';
 import { InputSuggestion } from '../../../../shared/input-suggestions/input-suggestions.model';
 import { SearchOptions } from '../../../search-options.model';
+import { SEARCH_CONFIG_SERVICE } from '../../../../+my-dspace-page/my-dspace-page.component';
 
 @Component({
   selector: 'ds-search-facet-filter',
@@ -56,7 +57,7 @@ export class SearchFacetFilterComponent implements OnInit, OnDestroy {
   /**
    * List of subscriptions to unsubscribe from
    */
-  private subs: Subscription[] = [];
+  protected subs: Subscription[] = [];
 
   /**
    * Emits the result values for this filter found by the current filter query
@@ -66,8 +67,8 @@ export class SearchFacetFilterComponent implements OnInit, OnDestroy {
   /**
    * Emits the active values for this filter
    */
-  selectedValues$: Observable<string[]>;
-  private collapseNextUpdate = true;
+  selectedValues$: Observable<FacetValue[]>;
+  protected collapseNextUpdate = true;
 
   /**
    * State of the requested facets used to time the animation
@@ -81,9 +82,10 @@ export class SearchFacetFilterComponent implements OnInit, OnDestroy {
 
   constructor(protected searchService: SearchService,
               protected filterService: SearchFilterService,
-              protected searchConfigService: SearchConfigurationService,
               protected rdbs: RemoteDataBuildService,
               protected router: Router,
+              @Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: SearchConfigurationService,
+              @Inject(IN_PLACE_SEARCH) public inPlaceSearch: boolean,
               @Inject(FILTER_CONFIG) public filterConfig: SearchFilterConfig) {
   }
 
@@ -94,10 +96,9 @@ export class SearchFacetFilterComponent implements OnInit, OnDestroy {
     this.filterValues$ = new BehaviorSubject(new RemoteData(true, false, undefined, undefined, undefined));
     this.currentPage = this.getCurrentPage().pipe(distinctUntilChanged());
 
-    this.selectedValues$ = this.filterService.getSelectedValuesForFilter(this.filterConfig);
     this.searchOptions$ = this.searchConfigService.searchOptions;
     this.subs.push(this.searchOptions$.subscribe(() => this.updateFilterValueList()));
-    const facetValues = observableCombineLatest(this.searchOptions$, this.currentPage).pipe(
+    const facetValues$ = observableCombineLatest(this.searchOptions$, this.currentPage).pipe(
       map(([options, page]) => {
         return { options, page }
       }),
@@ -115,8 +116,9 @@ export class SearchFacetFilterComponent implements OnInit, OnDestroy {
           )
       })
     );
+
     let filterValues = [];
-    this.subs.push(facetValues.subscribe((facetOutcome) => {
+    this.subs.push(facetValues$.subscribe((facetOutcome) => {
       const newValues$ = facetOutcome.values;
 
       if (this.collapseNextUpdate) {
@@ -130,9 +132,24 @@ export class SearchFacetFilterComponent implements OnInit, OnDestroy {
 
       filterValues = [...filterValues, newValues$];
 
-      this.subs.push(this.rdbs.aggregate(filterValues).subscribe((rd: RemoteData<Array<PaginatedList<FacetValue>>>) => {
+      this.subs.push(this.rdbs.aggregate(filterValues).pipe(
+        tap((rd: RemoteData<Array<PaginatedList<FacetValue>>>) => {
+          this.selectedValues$ = this.filterService.getSelectedValuesForFilter(this.filterConfig).pipe(
+            map((selectedValues) => {
+              return selectedValues.map((value: string) => {
+                const fValue = [].concat(...rd.payload.map((page) => page.page)).find((facetValue: FacetValue) => facetValue.value === value);
+                if (hasValue(fValue)) {
+                  return fValue;
+                }
+                return Object.assign(new FacetValue(), { label: value, value: value });
+              });
+            })
+          );
+        })
+      ).subscribe((rd: RemoteData<Array<PaginatedList<FacetValue>>>) => {
         this.animationState = 'ready';
         this.filterValues$.next(rd);
+
       }));
       this.subs.push(newValues$.pipe(take(1)).subscribe((rd) => {
         this.isLastPage$.next(hasNoValue(rd.payload.next))
@@ -158,12 +175,25 @@ export class SearchFacetFilterComponent implements OnInit, OnDestroy {
   }
 
   /**
-   * @returns {string} The base path to the search page
+   * @returns {string} The base path to the search page, or the current page when inPlaceSearch is true
    */
-  getSearchLink() {
+  public getSearchLink(): string {
+    if (this.inPlaceSearch) {
+      return './';
+    }
     return this.searchService.getSearchLink();
   }
 
+  /**
+   * @returns {string[]} The base path to the search page, or the current page when inPlaceSearch is true, split in separate pieces
+   */
+  public getSearchLinkParts(): string[] {
+    if (this.inPlaceSearch) {
+      return [];
+    }
+    return this.getSearchLink().split('/');
+  }
+
   /**
    * Show the next page as well
    */
@@ -199,9 +229,14 @@ export class SearchFacetFilterComponent implements OnInit, OnDestroy {
   onSubmit(data: any) {
     this.selectedValues$.pipe(take(1)).subscribe((selectedValues) => {
         if (isNotEmpty(data)) {
-          this.router.navigate([this.getSearchLink()], {
+          this.router.navigate(this.getSearchLinkParts(), {
             queryParams:
-              { [this.filterConfig.paramName]: [...selectedValues, data] },
+              {
+                [this.filterConfig.paramName]: [
+                  ...selectedValues.map((facet) => this.getFacetValue(facet)),
+                  data
+                ]
+              },
             queryParamsHandling: 'merge'
           });
           this.filter = '';
@@ -252,7 +287,7 @@ export class SearchFacetFilterComponent implements OnInit, OnDestroy {
                   return rd.payload.page.map((facet) => {
                     return {
                       displayValue: this.getDisplayValue(facet, data),
-                      value: facet.value
+                      value: this.getFacetValue(facet)
                     }
                   })
                 }
@@ -264,6 +299,13 @@ export class SearchFacetFilterComponent implements OnInit, OnDestroy {
     }
   }
 
+  /**
+   * Retrieve facet value
+   */
+  protected getFacetValue(facet: FacetValue): string {
+    return facet.value;
+  }
+
   /**
    * Transforms the facet value string, so if the query matches part of the value, it's emphasized in the value
    * @param {FacetValue} facet The value of the facet as returned by the server
diff --git a/src/app/+search-page/search-filters/search-filter/search-filter.component.html b/src/app/+search-page/search-filters/search-filter/search-filter.component.html
index 5c4db44d245155203a3f85c1b760663b93887d9b..a1758d73393401be2febb0f980b1ed0a2cb639c5 100644
--- a/src/app/+search-page/search-filters/search-filter/search-filter.component.html
+++ b/src/app/+search-page/search-filters/search-filter/search-filter.component.html
@@ -2,6 +2,6 @@
     <div (click)="toggle()" class="filter-name"><h5 class="d-inline-block mb-0">{{'search.filters.filter.' + filter.name + '.head'| translate}}</h5> <span class="filter-toggle fas float-right"
                                                                       [ngClass]="(collapsed$ | async) ? 'fa-plus' : 'fa-minus'"></span></div>
     <div [@slide]="(collapsed$ | async) ? 'collapsed' : 'expanded'"  (@slide.start)="startSlide($event)" (@slide.done)="finishSlide($event)" class="search-filter-wrapper" [ngClass]="{'closed' : closed}">
-        <ds-search-facet-filter-wrapper [filterConfig]="filter"></ds-search-facet-filter-wrapper>
+        <ds-search-facet-filter-wrapper [filterConfig]="filter" [inPlaceSearch]="inPlaceSearch"></ds-search-facet-filter-wrapper>
     </div>
-</div>
\ No newline at end of file
+</div>
diff --git a/src/app/+search-page/search-filters/search-filter/search-filter.component.spec.ts b/src/app/+search-page/search-filters/search-filter/search-filter.component.spec.ts
index 30ef349675a88a8880708977ffee2db9166661cf..23c4ab3b53b6cd0e74240462094a6e6f60383868 100644
--- a/src/app/+search-page/search-filters/search-filter/search-filter.component.spec.ts
+++ b/src/app/+search-page/search-filters/search-filter/search-filter.component.spec.ts
@@ -11,6 +11,8 @@ import { SearchFilterComponent } from './search-filter.component';
 import { SearchFilterConfig } from '../../search-service/search-filter-config.model';
 import { FilterType } from '../../search-service/filter-type.model';
 import { SearchConfigurationService } from '../../search-service/search-configuration.service';
+import { SearchConfigurationServiceStub } from '../../../shared/testing/search-configuration-service-stub';
+import { SEARCH_CONFIG_SERVICE } from '../../../+my-dspace-page/my-dspace-page.component';
 
 describe('SearchFilterComponent', () => {
   let comp: SearchFilterComponent;
@@ -54,8 +56,6 @@ describe('SearchFilterComponent', () => {
     getFacetValuesFor: (filter) => mockResults
   };
 
-  const searchConfigServiceStub = {};
-
   beforeEach(async(() => {
     TestBed.configureTestingModule({
       imports: [TranslateModule.forRoot(), RouterTestingModule.withRoutes([]), NoopAnimationsModule],
@@ -66,7 +66,7 @@ describe('SearchFilterComponent', () => {
           provide: SearchFilterService,
           useValue: mockFilterService
         },
-        { provide: SearchConfigurationService, useValue: searchConfigServiceStub },
+        { provide: SEARCH_CONFIG_SERVICE, useValue: new SearchConfigurationServiceStub() }
       ],
       schemas: [NO_ERRORS_SCHEMA]
     }).overrideComponent(SearchFilterComponent, {
diff --git a/src/app/+search-page/search-filters/search-filter/search-filter.component.ts b/src/app/+search-page/search-filters/search-filter/search-filter.component.ts
index 14ba8f0b76c0ed934e274674172c1b9c8405c73e..bfe9f3be63406e4ed9724ec0cedc1212aa3423ea 100644
--- a/src/app/+search-page/search-filters/search-filter/search-filter.component.ts
+++ b/src/app/+search-page/search-filters/search-filter/search-filter.component.ts
@@ -1,12 +1,15 @@
+import { Component, Inject, Input, OnInit } from '@angular/core';
+
+import { Observable, of as observableOf } from 'rxjs';
 import { filter, first, map, startWith, switchMap, take } from 'rxjs/operators';
-import { Component, Input, OnInit } from '@angular/core';
+
 import { SearchFilterConfig } from '../../search-service/search-filter-config.model';
 import { SearchFilterService } from './search-filter.service';
-import { BehaviorSubject, Observable, of as observableOf } from 'rxjs';
 import { slide } from '../../../shared/animations/slide';
 import { isNotEmpty } from '../../../shared/empty.util';
 import { SearchService } from '../../search-service/search.service';
 import { SearchConfigurationService } from '../../search-service/search-configuration.service';
+import { SEARCH_CONFIG_SERVICE } from '../../../+my-dspace-page/my-dspace-page.component';
 
 @Component({
   selector: 'ds-search-filter',
@@ -24,6 +27,11 @@ export class SearchFilterComponent implements OnInit {
    */
   @Input() filter: SearchFilterConfig;
 
+  /**
+   * True when the search component should show results on the current page
+   */
+  @Input() inPlaceSearch;
+
   /**
    * True when the filter is 100% collapsed in the UI
    */
@@ -44,7 +52,10 @@ export class SearchFilterComponent implements OnInit {
    */
   active$: Observable<boolean>;
 
-  constructor(private filterService: SearchFilterService, private searchService: SearchService, private searchConfigService: SearchConfigurationService) {
+  constructor(
+    private filterService: SearchFilterService,
+    private searchService: SearchService,
+    @Inject(SEARCH_CONFIG_SERVICE) private searchConfigService: SearchConfigurationService) {
   }
 
   /**
diff --git a/src/app/+search-page/search-filters/search-filter/search-filter.reducer.ts b/src/app/+search-page/search-filters/search-filter/search-filter.reducer.ts
index 187bcd50d050878142c2112b2108702895d68674..7102c8c9bc667ae06ad3ce543ef98e7b9a29d40e 100644
--- a/src/app/+search-page/search-filters/search-filter/search-filter.reducer.ts
+++ b/src/app/+search-page/search-filters/search-filter/search-filter.reducer.ts
@@ -1,9 +1,4 @@
-import {
-  SearchFilterAction,
-  SearchFilterActionTypes,
-  SearchFilterInitializeAction
-} from './search-filter.actions';
-import { isEmpty, isNotUndefined } from '../../../shared/empty.util';
+import { SearchFilterAction, SearchFilterActionTypes, SearchFilterInitializeAction } from './search-filter.actions';
 
 /**
  * Interface that represents the state for a single filters
diff --git a/src/app/+search-page/search-filters/search-filter/search-filter.service.spec.ts b/src/app/+search-page/search-filters/search-filter/search-filter.service.spec.ts
index 19239d899cf14506a4cb37ff3ca3ca179fac39b5..e317a276983ea184fe3655735b6aabb9745524c7 100644
--- a/src/app/+search-page/search-filters/search-filter/search-filter.service.spec.ts
+++ b/src/app/+search-page/search-filters/search-filter/search-filter.service.spec.ts
@@ -12,8 +12,10 @@ import {
 import { SearchFiltersState } from './search-filter.reducer';
 import { SearchFilterConfig } from '../../search-service/search-filter-config.model';
 import { FilterType } from '../../search-service/filter-type.model';
+import { SearchFixedFilterService } from './search-fixed-filter.service';
 import { ActivatedRouteStub } from '../../../shared/testing/active-router-stub';
 import { of as observableOf } from 'rxjs';
+import { SortDirection, SortOptions } from '../../../core/cache/models/sort-options.model';
 
 describe('SearchFilterService', () => {
   let service: SearchFilterService;
@@ -25,6 +27,12 @@ describe('SearchFilterService', () => {
     isOpenByDefault: false,
     pageSize: 2
   });
+
+  const mockFixedFilterService: SearchFixedFilterService = {
+    getQueryByFilterName: (filter: string) => {
+      return observableOf(undefined)
+    }
+  } as SearchFixedFilterService
   const value1 = 'random value';
   // const value2 = 'another value';
   const store: Store<SearchFiltersState> = jasmine.createSpyObj('store', {
@@ -44,11 +52,15 @@ describe('SearchFilterService', () => {
     },
     addQueryParameterValue: (param: string, value: string) => {
     },
+    getQueryParameterValue: (param: string) => {
+    },
     getQueryParameterValues: (param: string) => {
       return observableOf({});
     },
     getQueryParamsWithPrefix: (param: string) => {
       return observableOf({});
+    },
+    getRouteParameterValue: (param: string) => {
     }
     /* tslint:enable:no-empty */
   };
@@ -58,7 +70,7 @@ describe('SearchFilterService', () => {
   };
 
   beforeEach(() => {
-    service = new SearchFilterService(store, routeServiceStub);
+    service = new SearchFilterService(store, routeServiceStub, mockFixedFilterService);
   });
 
   describe('when the initializeFilter method is triggered', () => {
@@ -168,4 +180,113 @@ describe('SearchFilterService', () => {
     });
   });
 
+  describe('when the getCurrentScope method is called', () => {
+    beforeEach(() => {
+      spyOn(routeServiceStub, 'getQueryParameterValue');
+      service.getCurrentScope();
+    });
+
+    it('should call getQueryParameterValue on the route service with scope', () => {
+      expect(routeServiceStub.getQueryParameterValue).toHaveBeenCalledWith('scope');
+    });
+  });
+
+  describe('when the getCurrentQuery method is called', () => {
+    beforeEach(() => {
+      spyOn(routeServiceStub, 'getQueryParameterValue');
+      service.getCurrentQuery();
+    });
+
+    it('should call getQueryParameterValue on the route service with query', () => {
+      expect(routeServiceStub.getQueryParameterValue).toHaveBeenCalledWith('query');
+    });
+  });
+
+  describe('when the getCurrentPagination method is called', () => {
+    let result;
+    const mockReturn = 5;
+
+    beforeEach(() => {
+      spyOn(routeServiceStub, 'getQueryParameterValue').and.returnValue(observableOf(mockReturn));
+      result = service.getCurrentPagination();
+    });
+
+    it('should call getQueryParameterValue on the route service with page', () => {
+      expect(routeServiceStub.getQueryParameterValue).toHaveBeenCalledWith('page');
+    });
+
+    it('should call getQueryParameterValue on the route service with pageSize', () => {
+      expect(routeServiceStub.getQueryParameterValue).toHaveBeenCalledWith('pageSize');
+    });
+
+    it('should return an observable containing the correct pagination', () => {
+      result.subscribe((pagination) => {
+        expect(pagination.currentPage).toBe(mockReturn);
+        expect(pagination.pageSize).toBe(mockReturn);
+      });
+    });
+  });
+
+  describe('when the getCurrentSort method is called', () => {
+    let result;
+    const field = 'author';
+    const direction = SortDirection.ASC;
+
+    beforeEach(() => {
+      spyOn(routeServiceStub, 'getQueryParameterValue').and.returnValue(observableOf(undefined));
+      result = service.getCurrentSort(new SortOptions(field, direction));
+    });
+
+    it('should call getQueryParameterValue on the route service with sortDirection', () => {
+      expect(routeServiceStub.getQueryParameterValue).toHaveBeenCalledWith('sortDirection');
+    });
+
+    it('should call getQueryParameterValue on the route service with sortField', () => {
+      expect(routeServiceStub.getQueryParameterValue).toHaveBeenCalledWith('sortField');
+    });
+
+    it('should return an observable containing the correct sortOptions', () => {
+      result.subscribe((sort) => {
+        expect(sort.field).toBe(field);
+        expect(sort.direction).toBe(direction);
+      });
+    });
+  });
+
+  describe('when the getCurrentFilters method is called', () => {
+    beforeEach(() => {
+      spyOn(routeServiceStub, 'getQueryParamsWithPrefix');
+      service.getCurrentFilters();
+    });
+
+    it('should call getQueryParamsWithPrefix on the route service with prefix \'f.\'', () => {
+      expect(routeServiceStub.getQueryParamsWithPrefix).toHaveBeenCalledWith('f.');
+    });
+  });
+
+  describe('when the getCurrentFixedFilter method is called', () => {
+    const filter = 'filter';
+
+    beforeEach(() => {
+      spyOn(routeServiceStub, 'getRouteParameterValue').and.returnValue(observableOf(filter));
+      spyOn(mockFixedFilterService, 'getQueryByFilterName').and.returnValue(observableOf(filter));
+      service.getCurrentFixedFilter().subscribe();
+    });
+
+    it('should call getQueryByFilterName on the fixed-filter service with the correct filter', () => {
+      expect(mockFixedFilterService.getQueryByFilterName).toHaveBeenCalledWith(filter);
+    });
+  });
+
+  describe('when the getCurrentView method is called', () => {
+    beforeEach(() => {
+      spyOn(routeServiceStub, 'getQueryParameterValue');
+      service.getCurrentView();
+    });
+
+    it('should call getQueryParameterValue on the route service with view', () => {
+      expect(routeServiceStub.getQueryParameterValue).toHaveBeenCalledWith('view');
+    });
+  });
+
 });
diff --git a/src/app/+search-page/search-filters/search-filter/search-filter.service.ts b/src/app/+search-page/search-filters/search-filter/search-filter.service.ts
index bed4b1777f66f837c69a1efbb28b96736df76856..4b12417084518443fd52001f713071cc7be27500 100644
--- a/src/app/+search-page/search-filters/search-filter/search-filter.service.ts
+++ b/src/app/+search-page/search-filters/search-filter/search-filter.service.ts
@@ -1,6 +1,6 @@
 import { combineLatest as observableCombineLatest, Observable } from 'rxjs';
+import { mergeMap, map, distinctUntilChanged } from 'rxjs/operators';
 import { Injectable, InjectionToken } from '@angular/core';
-import { distinctUntilChanged, map } from 'rxjs/operators';
 import { SearchFiltersState, SearchFilterState } from './search-filter.reducer';
 import { createSelector, MemoizedSelector, select, Store } from '@ngrx/store';
 import {
@@ -15,12 +15,19 @@ import {
 import { hasValue, isNotEmpty, } from '../../../shared/empty.util';
 import { SearchFilterConfig } from '../../search-service/search-filter-config.model';
 import { RouteService } from '../../../shared/services/route.service';
-import { Params } from '@angular/router';
+import { SortDirection, SortOptions } from '../../../core/cache/models/sort-options.model';
+import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
 import { SearchOptions } from '../../search-options.model';
+import { PaginatedSearchOptions } from '../../paginated-search-options.model';
+import { SearchFixedFilterService } from './search-fixed-filter.service';
+import { Params } from '@angular/router';
+import * as postcss from 'postcss';
+import prefix = postcss.vendor.prefix;
 // const spy = create();
 const filterStateSelector = (state: SearchFiltersState) => state.searchFilter;
 
 export const FILTER_CONFIG: InjectionToken<SearchFilterConfig> = new InjectionToken<SearchFilterConfig>('filterConfig');
+export const IN_PLACE_SEARCH: InjectionToken<boolean> = new InjectionToken<boolean>('inPlaceSearch');
 
 /**
  * Service that performs all actions that have to do with search filters and facets
@@ -29,8 +36,8 @@ export const FILTER_CONFIG: InjectionToken<SearchFilterConfig> = new InjectionTo
 export class SearchFilterService {
 
   constructor(private store: Store<SearchFiltersState>,
-              private routeService: RouteService
-  ) {
+              private routeService: RouteService,
+              private fixedFilterService: SearchFixedFilterService) {
   }
 
   /**
@@ -52,6 +59,81 @@ export class SearchFilterService {
     return this.routeService.hasQueryParam(paramName);
   }
 
+  /**
+   * Fetch the current active scope from the query parameters
+   * @returns {Observable<string>}
+   */
+  getCurrentScope() {
+    return this.routeService.getQueryParameterValue('scope');
+  }
+
+  /**
+   * Fetch the current query from the query parameters
+   * @returns {Observable<string>}
+   */
+  getCurrentQuery() {
+    return this.routeService.getQueryParameterValue('query');
+  }
+
+  /**
+   * Fetch the current pagination from query parameters 'page' and 'pageSize'
+   * and combine them with a given pagination
+   * @param pagination      Pagination options to combine the query parameters with
+   * @returns {Observable<PaginationComponentOptions>}
+   */
+  getCurrentPagination(pagination: any = {}): Observable<PaginationComponentOptions> {
+    const page$ = this.routeService.getQueryParameterValue('page');
+    const size$ = this.routeService.getQueryParameterValue('pageSize');
+    return observableCombineLatest(page$, size$).pipe(map(([page, size]) => {
+      return Object.assign(new PaginationComponentOptions(), pagination, {
+        currentPage: page || 1,
+        pageSize: size || pagination.pageSize
+      });
+    }))
+  }
+
+  /**
+   * Fetch the current sorting options from query parameters 'sortDirection' and 'sortField'
+   * and combine them with given sorting options
+   * @param {SortOptions} defaultSort       Sorting options to combine the query parameters with
+   * @returns {Observable<SortOptions>}
+   */
+  getCurrentSort(defaultSort: SortOptions): Observable<SortOptions> {
+    const sortDirection$ = this.routeService.getQueryParameterValue('sortDirection');
+    const sortField$ = this.routeService.getQueryParameterValue('sortField');
+    return observableCombineLatest(sortDirection$, sortField$).pipe(map(([sortDirection, sortField]) => {
+        const field = sortField || defaultSort.field;
+        const direction = SortDirection[sortDirection] || defaultSort.direction;
+        return new SortOptions(field, direction)
+      }
+    ))
+  }
+
+  /**
+   * Fetch the current active filters from the query parameters
+   * @returns {Observable<Params>}
+   */
+  getCurrentFilters() {
+    return this.routeService.getQueryParamsWithPrefix('f.');
+  }
+
+  /**
+   * Fetch the current active fixed filter from the route parameters and return the query by filter name
+   * @returns {Observable<string>}
+   */
+  getCurrentFixedFilter(): Observable<string> {
+    const filter: Observable<string> = this.routeService.getRouteParameterValue('filter');
+    return filter.pipe(mergeMap((f) => this.fixedFilterService.getQueryByFilterName(f)));
+  }
+
+  /**
+   * Fetch the current view from the query parameters
+   * @returns {Observable<string>}
+   */
+  getCurrentView() {
+    return this.routeService.getQueryParameterValue('view');
+  }
+
   /**
    * Requests the active filter values set for a given filter
    * @param {SearchFilterConfig} filterConfig The configuration for which the filters are active
@@ -62,7 +144,6 @@ export class SearchFilterService {
     const prefixValues$ = this.routeService.getQueryParamsWithPrefix(filterConfig.paramName + '.').pipe(
       map((params: Params) => [].concat(...Object.values(params))),
     );
-
     return observableCombineLatest(values$, prefixValues$).pipe(
       map(([values, prefixValues]) => {
           if (isNotEmpty(values)) {
diff --git a/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.spec.ts b/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..32073455642581d2e449d62268e9c810f3906dbc
--- /dev/null
+++ b/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.spec.ts
@@ -0,0 +1,60 @@
+import { SearchFixedFilterService } from './search-fixed-filter.service';
+import { RouteService } from '../../../shared/services/route.service';
+import { RequestService } from '../../../core/data/request.service';
+import { HALEndpointService } from '../../../core/shared/hal-endpoint.service';
+import { of as observableOf } from 'rxjs';
+import { RequestEntry } from '../../../core/data/request.reducer';
+import { FilteredDiscoveryQueryResponse, RestResponse } from '../../../core/cache/response.models';
+
+describe('SearchFixedFilterService', () => {
+  let service: SearchFixedFilterService;
+
+  const filterQuery = 'filter:query';
+
+  const routeServiceStub = {} as RouteService;
+  const requestServiceStub = Object.assign({
+    /* tslint:disable:no-empty */
+    configure: () => {},
+    /* tslint:enable:no-empty */
+    generateRequestId: () => 'fake-id',
+    getByUUID: () => observableOf(Object.assign(new RequestEntry(), {
+      response: new FilteredDiscoveryQueryResponse(filterQuery, 200, 'OK')
+    }))
+  }) as RequestService;
+  const halServiceStub = Object.assign(new HALEndpointService(requestServiceStub, undefined), {
+    getEndpoint: () => observableOf('fake-url')
+  });
+
+  beforeEach(() => {
+    service = new SearchFixedFilterService(routeServiceStub, requestServiceStub, halServiceStub);
+  });
+
+  describe('when getQueryByFilterName is called with a filterName', () => {
+    it('should return the filter query', () => {
+      service.getQueryByFilterName('filter').subscribe((query) => {
+        expect(query).toBe(filterQuery);
+      });
+    });
+  });
+
+  describe('when getQueryByFilterName is called without a filterName', () => {
+    it('should return undefined', () => {
+      service.getQueryByFilterName(undefined).subscribe((query) => {
+        expect(query).toBeUndefined();
+      });
+    });
+  });
+
+  describe('when getQueryByRelations is called', () => {
+    const relationType = 'isRelationOf';
+    const itemUUID = 'c5b277e6-2477-48bb-8993-356710c285f3';
+
+    it('should contain the relationType and itemUUID', () => {
+      const query = service.getQueryByRelations(relationType, itemUUID);
+      expect(query.length).toBeGreaterThan(relationType.length + itemUUID.length);
+      expect(query).toContain(relationType);
+      expect(query).toContain(itemUUID);
+    });
+  });
+
+});
diff --git a/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.ts b/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7d59e5a4461b00998ccbda83e9a76108f18b9882
--- /dev/null
+++ b/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.ts
@@ -0,0 +1,79 @@
+import { Injectable } from '@angular/core';
+import { flatMap, map } from 'rxjs/operators';
+import { Observable ,  of as observableOf } from 'rxjs';
+import { HALEndpointService } from '../../../core/shared/hal-endpoint.service';
+import { GetRequest, RestRequest } from '../../../core/data/request.models';
+import { RequestService } from '../../../core/data/request.service';
+import { ResponseParsingService } from '../../../core/data/parsing.service';
+import { GenericConstructor } from '../../../core/shared/generic-constructor';
+import { FilteredDiscoveryPageResponseParsingService } from '../../../core/data/filtered-discovery-page-response-parsing.service';
+import { hasValue } from '../../../shared/empty.util';
+import { configureRequest, getResponseFromEntry } from '../../../core/shared/operators';
+import { RouteService } from '../../../shared/services/route.service';
+import { FilteredDiscoveryQueryResponse } from '../../../core/cache/response.models';
+
+/**
+ * Service for performing actions on the filtered-discovery-pages REST endpoint
+ */
+@Injectable()
+export class SearchFixedFilterService {
+  private queryByFilterPath = 'filtered-discovery-pages';
+
+  constructor(private routeService: RouteService,
+              protected requestService: RequestService,
+              private halService: HALEndpointService) {
+
+  }
+
+  /**
+   * Get the filter query for a certain filter by name
+   * @param {string} filterName     Name of the filter
+   * @returns {Observable<string>}  Filter query
+   */
+  getQueryByFilterName(filterName: string): Observable<string> {
+    if (hasValue(filterName)) {
+      const requestUuid = this.requestService.generateRequestId();
+      this.halService.getEndpoint(this.queryByFilterPath).pipe(
+        map((url: string) => {
+          url += ('/' + filterName);
+          const request = new GetRequest(requestUuid, url);
+          return Object.assign(request, {
+            getResponseParser(): GenericConstructor<ResponseParsingService> {
+              return FilteredDiscoveryPageResponseParsingService;
+            }
+          });
+        }),
+        configureRequest(this.requestService)
+      ).subscribe();
+
+      // get search results from response cache
+      const filterQuery: Observable<string> = this.requestService.getByUUID(requestUuid).pipe(
+        getResponseFromEntry(),
+        map((response: FilteredDiscoveryQueryResponse) =>
+          response.filterQuery
+        ));
+      return filterQuery;
+    }
+    return observableOf(undefined);
+  }
+
+  /**
+   * Get the query for looking up items by relation type
+   * @param {string} relationType   Relation type
+   * @param {string} itemUUID       Item UUID
+   * @returns {string}              Query
+   */
+  getQueryByRelations(relationType: string, itemUUID: string): string {
+    return `query=relation.${relationType}:${itemUUID}`;
+  }
+
+  /**
+   * Get the filter for a relation with the item's UUID
+   * @param relationType    The type of relation e.g. 'isAuthorOfPublication'
+   * @param itemUUID        The item's UUID
+   */
+  getFilterByRelation(relationType: string, itemUUID: string): string {
+    return `f.${relationType}=${itemUUID}`;
+  }
+
+}
diff --git a/src/app/+search-page/search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component.html b/src/app/+search-page/search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component.html
index b6ae0ada639fb68aa4dc6fae684ff92279817005..ac2a72f4b6356df472bdccf455247271b52c70f7 100644
--- a/src/app/+search-page/search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component.html
+++ b/src/app/+search-page/search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component.html
@@ -1,9 +1,9 @@
 <div>
     <div class="filters py-2">
-        <ds-search-facet-selected-option *ngFor="let value of (selectedValues$ | async)" [selectedValue]="value" [filterConfig]="filterConfig" [selectedValues$]="selectedValues$"></ds-search-facet-selected-option>
+        <ds-search-facet-selected-option *ngFor="let value of (selectedValues$ | async)" [selectedValue]="value" [filterConfig]="filterConfig" [selectedValues$]="selectedValues$" [inPlaceSearch]="inPlaceSearch"></ds-search-facet-selected-option>
         <ng-container *ngFor="let page of (filterValues$ | async)?.payload">
           <div [@facetLoad]="animationState">
-            <ds-search-facet-option *ngFor="let value of page.page; trackBy: trackUpdate" [filterConfig]="filterConfig" [filterValue]="value" [selectedValues$]="selectedValues$"></ds-search-facet-option>
+            <ds-search-facet-option *ngFor="let value of page.page; trackBy: trackUpdate" [filterConfig]="filterConfig" [filterValue]="value" [selectedValues$]="selectedValues$" [inPlaceSearch]="inPlaceSearch"></ds-search-facet-option>
           </div>
         </ng-container>
         <div class="clearfix toggle-more-filters">
diff --git a/src/app/+search-page/search-filters/search-filter/search-range-filter/search-range-filter.component.html b/src/app/+search-page/search-filters/search-filter/search-range-filter/search-range-filter.component.html
index 9d35cc518af31ed519ba90ab74e2a23cd4b6026f..cad31e7f0ff58633c86e94ccc63c1b5f98ac5c2d 100644
--- a/src/app/+search-page/search-filters/search-filter/search-range-filter/search-range-filter.component.html
+++ b/src/app/+search-page/search-filters/search-filter/search-range-filter/search-range-filter.component.html
@@ -24,7 +24,7 @@
         </ng-container>
       <ng-container *ngFor="let page of (filterValues$ | async)?.payload">
         <div [@facetLoad]="animationState">
-          <ds-search-facet-range-option *ngFor="let value of page.page; trackBy: trackUpdate" [filterConfig]="filterConfig" [filterValue]="value"></ds-search-facet-range-option>
+          <ds-search-facet-range-option *ngFor="let value of page.page; trackBy: trackUpdate" [filterConfig]="filterConfig" [filterValue]="value" [inPlaceSearch]="inPlaceSearch"></ds-search-facet-range-option>
           </div>
         </ng-container>
     </div>
diff --git a/src/app/+search-page/search-filters/search-filter/search-range-filter/search-range-filter.component.spec.ts b/src/app/+search-page/search-filters/search-filter/search-range-filter/search-range-filter.component.spec.ts
index 930ea8c9fb2d099a4cc59afc2b24172ee60de44a..119f3f92a9400772f06dc6e98b8a2a9ea603f871 100644
--- a/src/app/+search-page/search-filters/search-filter/search-range-filter/search-range-filter.component.spec.ts
+++ b/src/app/+search-page/search-filters/search-filter/search-range-filter/search-range-filter.component.spec.ts
@@ -2,7 +2,7 @@ import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
 import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 import { TranslateModule } from '@ngx-translate/core';
 import { NoopAnimationsModule } from '@angular/platform-browser/animations';
-import { FILTER_CONFIG, SearchFilterService } from '../search-filter.service';
+import { FILTER_CONFIG, IN_PLACE_SEARCH, SearchFilterService } from '../search-filter.service';
 import { SearchFilterConfig } from '../../../search-service/search-filter-config.model';
 import { FilterType } from '../../../search-service/filter-type.model';
 import { FacetValue } from '../../../search-service/facet-value.model';
@@ -18,7 +18,8 @@ import { PageInfo } from '../../../../core/shared/page-info.model';
 import { SearchRangeFilterComponent } from './search-range-filter.component';
 import { RouteService } from '../../../../shared/services/route.service';
 import { RemoteDataBuildService } from '../../../../core/cache/builders/remote-data-build.service';
-import { SearchConfigurationService } from '../../../search-service/search-configuration.service';
+import { SEARCH_CONFIG_SERVICE } from '../../../../+my-dspace-page/my-dspace-page.component';
+import { SearchConfigurationServiceStub } from '../../../../shared/testing/search-configuration-service-stub';
 
 describe('SearchRangeFilterComponent', () => {
   let comp: SearchRangeFilterComponent;
@@ -41,14 +42,17 @@ describe('SearchRangeFilterComponent', () => {
   });
   const values: FacetValue[] = [
     {
+      label: value1,
       value: value1,
       count: 52,
       search: ''
     }, {
+      label: value2,
       value: value2,
       count: 20,
       search: ''
     }, {
+      label: value3,
       value: value3,
       count: 5,
       search: ''
@@ -73,9 +77,8 @@ describe('SearchRangeFilterComponent', () => {
         { provide: FILTER_CONFIG, useValue: mockFilterConfig },
         { provide: RemoteDataBuildService, useValue: {aggregate: () => observableOf({})} },
         { provide: RouteService, useValue: {getQueryParameterValue: () => observableOf({})} },
-        { provide: SearchConfigurationService, useValue: {
-            searchOptions: observableOf({}) }
-        },
+        { provide: SEARCH_CONFIG_SERVICE, useValue: new SearchConfigurationServiceStub() },
+        { provide: IN_PLACE_SEARCH, useValue: false },
         {
           provide: SearchFilterService, useValue: {
             getSelectedValuesForFilter: () => selectedValues,
@@ -116,7 +119,7 @@ describe('SearchRangeFilterComponent', () => {
     });
 
     it('should call navigate on the router with the right searchlink and parameters', () => {
-      expect(router.navigate).toHaveBeenCalledWith([searchUrl], {
+      expect(router.navigate).toHaveBeenCalledWith(searchUrl.split('/'), {
         queryParams: {
           [mockFilterConfig.paramName + minSuffix]: [1900],
           [mockFilterConfig.paramName + maxSuffix]: [1950]
diff --git a/src/app/+search-page/search-filters/search-filter/search-range-filter/search-range-filter.component.ts b/src/app/+search-page/search-filters/search-filter/search-range-filter/search-range-filter.component.ts
index ebdb79750036548496ed598d293b264c47002a52..95d7441184e391759fac4be871e7f2ef7450d20b 100644
--- a/src/app/+search-page/search-filters/search-filter/search-range-filter/search-range-filter.component.ts
+++ b/src/app/+search-page/search-filters/search-filter/search-range-filter/search-range-filter.component.ts
@@ -10,13 +10,14 @@ import {
   SearchFacetFilterComponent
 } from '../search-facet-filter/search-facet-filter.component';
 import { SearchFilterConfig } from '../../../search-service/search-filter-config.model';
-import { FILTER_CONFIG, SearchFilterService } from '../search-filter.service';
+import { FILTER_CONFIG, IN_PLACE_SEARCH, SearchFilterService } from '../search-filter.service';
 import { SearchService } from '../../../search-service/search.service';
 import { Router } from '@angular/router';
 import * as moment from 'moment';
 import { RouteService } from '../../../../shared/services/route.service';
 import { hasValue } from '../../../../shared/empty.util';
 import { SearchConfigurationService } from '../../../search-service/search-configuration.service';
+import { SEARCH_CONFIG_SERVICE } from '../../../../+my-dspace-page/my-dspace-page.component';
 
 /**
  * The suffix for a range filters' minimum in the frontend URL
@@ -72,13 +73,14 @@ export class SearchRangeFilterComponent extends SearchFacetFilterComponent imple
 
   constructor(protected searchService: SearchService,
               protected filterService: SearchFilterService,
-              protected searchConfigService: SearchConfigurationService,
               protected router: Router,
               protected rdbs: RemoteDataBuildService,
+              @Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: SearchConfigurationService,
+              @Inject(IN_PLACE_SEARCH) public inPlaceSearch: boolean,
               @Inject(FILTER_CONFIG) public filterConfig: SearchFilterConfig,
               @Inject(PLATFORM_ID) private platformId: any,
               private route: RouteService) {
-    super(searchService, filterService, searchConfigService, rdbs, router, filterConfig);
+    super(searchService, filterService, rdbs, router, searchConfigService, inPlaceSearch, filterConfig);
 
   }
 
@@ -107,7 +109,7 @@ export class SearchRangeFilterComponent extends SearchFacetFilterComponent imple
   onSubmit() {
     const newMin = this.range[0] !== this.min ? [this.range[0]] : null;
     const newMax = this.range[1] !== this.max ? [this.range[1]] : null;
-    this.router.navigate([this.getSearchLink()], {
+    this.router.navigate(this.getSearchLinkParts(), {
       queryParams:
         {
           [this.filterConfig.paramName + RANGE_FILTER_MIN_SUFFIX]: newMin,
diff --git a/src/app/+search-page/search-filters/search-filter/search-text-filter/search-text-filter.component.html b/src/app/+search-page/search-filters/search-filter/search-text-filter/search-text-filter.component.html
index 25ff8e46d34003a148e93362849f64ca40f125f2..a4f4fb5ee8f3475c83a90cb55f8bc4fabfc4017a 100644
--- a/src/app/+search-page/search-filters/search-filter/search-text-filter/search-text-filter.component.html
+++ b/src/app/+search-page/search-filters/search-filter/search-text-filter/search-text-filter.component.html
@@ -1,9 +1,9 @@
 <div>
     <div class="filters py-2">
-        <ds-search-facet-selected-option *ngFor="let value of (selectedValues$ | async)" [selectedValue]="value" [filterConfig]="filterConfig" [selectedValues$]="selectedValues$"></ds-search-facet-selected-option>
+        <ds-search-facet-selected-option *ngFor="let value of (selectedValues$ | async)" [selectedValue]="value" [filterConfig]="filterConfig" [selectedValues$]="selectedValues$" [inPlaceSearch]="inPlaceSearch"></ds-search-facet-selected-option>
         <ng-container *ngFor="let page of (filterValues$ | async)?.payload">
           <div [@facetLoad]="animationState">
-                  <ds-search-facet-option *ngFor="let value of page.page; trackBy: trackUpdate" [filterConfig]="filterConfig" [filterValue]="value" [selectedValues$]="selectedValues$"></ds-search-facet-option>
+                  <ds-search-facet-option *ngFor="let value of page.page; trackBy: trackUpdate" [filterConfig]="filterConfig" [filterValue]="value" [selectedValues$]="selectedValues$" [inPlaceSearch]="inPlaceSearch"></ds-search-facet-option>
           </div>
         </ng-container>
         <div class="clearfix toggle-more-filters">
diff --git a/src/app/+search-page/search-filters/search-filters.component.html b/src/app/+search-page/search-filters/search-filters.component.html
index 895765f6ac587a4a090b5607dbb91297f86ca6cf..05f4a693c294a2d236727267242d8005748578fd 100644
--- a/src/app/+search-page/search-filters/search-filters.component.html
+++ b/src/app/+search-page/search-filters/search-filters.component.html
@@ -1,7 +1,7 @@
 <h3>{{"search.filters.head" | translate}}</h3>
 <div *ngIf="(filters | async)?.hasSucceeded">
     <div *ngFor="let filter of (filters | async)?.payload; trackBy: trackUpdate">
-        <ds-search-filter [filter]="filter"></ds-search-filter>
+        <ds-search-filter [filter]="filter" [inPlaceSearch]="inPlaceSearch"></ds-search-filter>
     </div>
 </div>
-<a class="btn btn-primary" [routerLink]="[getSearchLink()]" [queryParams]="clearParams | async" queryParamsHandling="merge" role="button">{{"search.filters.reset" | translate}}</a>
\ No newline at end of file
+<a class="btn btn-primary" [routerLink]="[getSearchLink()]" [queryParams]="clearParams | async" queryParamsHandling="merge" role="button">{{"search.filters.reset" | translate}}</a>
diff --git a/src/app/+search-page/search-filters/search-filters.component.spec.ts b/src/app/+search-page/search-filters/search-filters.component.spec.ts
index db21fc8a69d837149725e710a0d3ade95f9e8549..dc883cd29002fc872839dca447c18784cc28d95d 100644
--- a/src/app/+search-page/search-filters/search-filters.component.spec.ts
+++ b/src/app/+search-page/search-filters/search-filters.component.spec.ts
@@ -7,13 +7,15 @@ import { NoopAnimationsModule } from '@angular/platform-browser/animations';
 import { SearchFilterService } from './search-filter/search-filter.service';
 import { SearchFiltersComponent } from './search-filters.component';
 import { SearchService } from '../search-service/search.service';
-import { SearchConfigurationService } from '../search-service/search-configuration.service';
 import { of as observableOf } from 'rxjs';
+import { SEARCH_CONFIG_SERVICE } from '../../+my-dspace-page/my-dspace-page.component';
+import { SearchConfigurationServiceStub } from '../../shared/testing/search-configuration-service-stub';
 
 describe('SearchFiltersComponent', () => {
   let comp: SearchFiltersComponent;
   let fixture: ComponentFixture<SearchFiltersComponent>;
   let searchService: SearchService;
+
   const searchServiceStub = {
     /* tslint:disable:no-empty */
     getConfig: () =>
@@ -30,17 +32,13 @@ describe('SearchFiltersComponent', () => {
       []
   };
 
-  const searchConfigServiceStub = jasmine.createSpyObj('SearchConfigurationService', {
-    getCurrentFrontendFilters: observableOf({})
-  });
-
   beforeEach(async(() => {
     TestBed.configureTestingModule({
       imports: [TranslateModule.forRoot(), RouterTestingModule.withRoutes([]), NoopAnimationsModule],
       declarations: [SearchFiltersComponent],
       providers: [
         { provide: SearchService, useValue: searchServiceStub },
-        { provide: SearchConfigurationService, useValue: searchConfigServiceStub },
+        { provide: SEARCH_CONFIG_SERVICE, useValue: new SearchConfigurationServiceStub() },
         { provide: SearchFilterService, useValue: searchFiltersStub },
 
       ],
diff --git a/src/app/+search-page/search-filters/search-filters.component.ts b/src/app/+search-page/search-filters/search-filters.component.ts
index 1dd747e908de700a1f4a4f21dfcb04f07332625e..e970647747f659f0eb1eecfbb1dc11e88896ce32 100644
--- a/src/app/+search-page/search-filters/search-filters.component.ts
+++ b/src/app/+search-page/search-filters/search-filters.component.ts
@@ -1,13 +1,15 @@
+import { Component, Inject, Input, OnInit } from '@angular/core';
+
 import { Observable } from 'rxjs';
+import { map, switchMap } from 'rxjs/operators';
 
-import { map } from 'rxjs/operators';
-import { Component } from '@angular/core';
 import { SearchService } from '../search-service/search.service';
 import { RemoteData } from '../../core/data/remote-data';
 import { SearchFilterConfig } from '../search-service/search-filter-config.model';
 import { SearchConfigurationService } from '../search-service/search-configuration.service';
 import { SearchFilterService } from './search-filter/search-filter.service';
 import { getSucceededRemoteData } from '../../core/shared/operators';
+import { SEARCH_CONFIG_SERVICE } from '../../+my-dspace-page/my-dspace-page.component';
 
 @Component({
   selector: 'ds-search-filters',
@@ -18,7 +20,7 @@ import { getSucceededRemoteData } from '../../core/shared/operators';
 /**
  * This component represents the part of the search sidebar that contains filters.
  */
-export class SearchFiltersComponent {
+export class SearchFiltersComponent implements OnInit {
   /**
    * An observable containing configuration about which filters are shown and how they are shown
    */
@@ -30,24 +32,43 @@ export class SearchFiltersComponent {
    */
   clearParams;
 
+  /**
+   * True when the search component should show results on the current page
+   */
+  @Input() inPlaceSearch;
+
   /**
    * Initialize instance variables
    * @param {SearchService} searchService
    * @param {SearchConfigurationService} searchConfigService
    * @param {SearchFilterService} filterService
    */
-  constructor(private searchService: SearchService, private searchConfigService: SearchConfigurationService, private filterService: SearchFilterService) {
-    this.filters = searchService.getConfig().pipe(getSucceededRemoteData());
-    this.clearParams = searchConfigService.getCurrentFrontendFilters().pipe(map((filters) => {
+  constructor(
+    private searchService: SearchService,
+    private filterService: SearchFilterService,
+    @Inject(SEARCH_CONFIG_SERVICE) private searchConfigService: SearchConfigurationService) {
+
+  }
+
+  ngOnInit(): void {
+
+    this.filters = this.searchConfigService.searchOptions.pipe(
+      switchMap((options) => this.searchService.getConfig(options.scope, options.configuration).pipe(getSucceededRemoteData()))
+    );
+
+    this.clearParams = this.searchConfigService.getCurrentFrontendFilters().pipe(map((filters) => {
       Object.keys(filters).forEach((f) => filters[f] = null);
       return filters;
     }));
   }
 
   /**
-   * @returns {string} The base path to the search page
+   * @returns {string} The base path to the search page, or the current page when inPlaceSearch is true
    */
-  getSearchLink() {
+  public getSearchLink(): string {
+    if (this.inPlaceSearch) {
+      return './';
+    }
     return this.searchService.getSearchLink();
   }
 
@@ -57,4 +78,5 @@ export class SearchFiltersComponent {
   trackUpdate(index, config: SearchFilterConfig) {
     return config ? config.name : undefined;
   }
+
 }
diff --git a/src/app/+search-page/search-labels/search-labels.component.html b/src/app/+search-page/search-labels/search-labels.component.html
index 61a5618dada80fb3f33fe5a90ebee2c9a421b00b..cac81e871762ee20efa7e60a677d8b3727e1d93a 100644
--- a/src/app/+search-page/search-labels/search-labels.component.html
+++ b/src/app/+search-page/search-labels/search-labels.component.html
@@ -2,11 +2,11 @@
     <div class="labels col-sm-9 offset-sm-3">
         <ng-container *ngFor="let key of ((appliedFilters | async) | dsObjectKeys)"><!--Do not remove this to prevent uneven spacing
         --><a *ngFor="let values of (appliedFilters | async)[key]"
-              class="badge badge-primary mr-1 mb-1"
+              class="badge badge-primary mr-1 mb-1 text-capitalize"
               [routerLink]="getSearchLink()"
               [queryParams]="(getRemoveParams(key, values) | async)" queryParamsHandling="merge">
-            {{('search.filters.applied.' + key) | translate}}: {{values}}
-            <span> ×</span>
+          {{('search.filters.applied.' + key) | translate}}: {{normalizeFilterValue(values)}}
+          <span> ×</span>
         </a><!--Do not remove this to prevent uneven spacing
         --></ng-container>
     </div>
diff --git a/src/app/+search-page/search-labels/search-labels.component.spec.ts b/src/app/+search-page/search-labels/search-labels.component.spec.ts
index 81fa5b5df80d0b20260098a2a3ebfe4bb6bbd4a7..d28698764c60e7a47a09e42af3966492aee8f236 100644
--- a/src/app/+search-page/search-labels/search-labels.component.spec.ts
+++ b/src/app/+search-page/search-labels/search-labels.component.spec.ts
@@ -9,7 +9,8 @@ import { SearchServiceStub } from '../../shared/testing/search-service-stub';
 import { Observable, of as observableOf } from 'rxjs';
 import { Params } from '@angular/router';
 import { ObjectKeysPipe } from '../../shared/utils/object-keys-pipe';
-import { SearchConfigurationService } from '../search-service/search-configuration.service';
+import { SEARCH_CONFIG_SERVICE } from '../../+my-dspace-page/my-dspace-page.component';
+import { SearchConfigurationServiceStub } from '../../shared/testing/search-configuration-service-stub';
 
 describe('SearchLabelsComponent', () => {
   let comp: SearchLabelsComponent;
@@ -20,8 +21,11 @@ describe('SearchLabelsComponent', () => {
 
   const field1 = 'author';
   const field2 = 'subject';
-  const value1 = 'TestAuthor';
+  const value1 = 'Test, Author';
+  const normValue1 = 'Test, Author';
   const value2 = 'TestSubject';
+  const value3 = 'Test, Authority,authority';
+  const normValue3 = 'Test, Authority';
   const filter1 = [field1, value1];
   const filter2 = [field2, value2];
   const mockFilters = [
@@ -35,7 +39,8 @@ describe('SearchLabelsComponent', () => {
       declarations: [SearchLabelsComponent, ObjectKeysPipe],
       providers: [
         { provide: SearchService, useValue: new SearchServiceStub(searchLink) },
-        { provide: SearchConfigurationService, useValue: {getCurrentFrontendFilters : () =>  observableOf({})} }
+        { provide: SEARCH_CONFIG_SERVICE, useValue: new SearchConfigurationServiceStub() }
+        // { provide: SearchConfigurationService, useValue: {getCurrentFrontendFilters : () =>  observableOf({})} }
       ],
       schemas: [NO_ERRORS_SCHEMA]
     }).overrideComponent(SearchLabelsComponent, {
@@ -65,4 +70,16 @@ describe('SearchLabelsComponent', () => {
       });
     })
   });
+
+  describe('when normalizeFilterValue is called', () => {
+    it('should return properly filter value', () => {
+      let result: string;
+
+      result = comp.normalizeFilterValue(value1);
+      expect(result).toBe(normValue1);
+
+      result = comp.normalizeFilterValue(value3);
+      expect(result).toBe(normValue3);
+    })
+  });
 });
diff --git a/src/app/+search-page/search-labels/search-labels.component.ts b/src/app/+search-page/search-labels/search-labels.component.ts
index 08e07cce3d673277dc976077ae50d7b3f6598615..104ed5b08bcaba2f4697443c7930af558f66b9ba 100644
--- a/src/app/+search-page/search-labels/search-labels.component.ts
+++ b/src/app/+search-page/search-labels/search-labels.component.ts
@@ -1,10 +1,11 @@
-import { Component } from '@angular/core';
+import { Component, Inject, Input } from '@angular/core';
 import { SearchService } from '../search-service/search.service';
 import { Observable } from 'rxjs';
 import { Params } from '@angular/router';
 import { map } from 'rxjs/operators';
 import { hasValue, isNotEmpty } from '../../shared/empty.util';
 import { SearchConfigurationService } from '../search-service/search-configuration.service';
+import { SEARCH_CONFIG_SERVICE } from '../../+my-dspace-page/my-dspace-page.component';
 
 @Component({
   selector: 'ds-search-labels',
@@ -21,10 +22,17 @@ export class SearchLabelsComponent {
    */
   appliedFilters: Observable<Params>;
 
+  /**
+   * True when the search component should show results on the current page
+   */
+  @Input() inPlaceSearch;
+
   /**
    * Initialize the instance variable
    */
-  constructor(private searchService: SearchService, private searchConfigService: SearchConfigurationService) {
+  constructor(
+    private searchService: SearchService,
+    @Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: SearchConfigurationService) {
     this.appliedFilters = this.searchConfigService.getCurrentFrontendFilters();
   }
 
@@ -48,9 +56,25 @@ export class SearchLabelsComponent {
   }
 
   /**
-   * @returns {string} The base path to the search page
+   * @returns {string} The base path to the search page, or the current page when inPlaceSearch is true
    */
-  getSearchLink() {
+  public getSearchLink(): string {
+    if (this.inPlaceSearch) {
+      return './';
+    }
     return this.searchService.getSearchLink();
   }
+
+  /**
+   * TODO to review after https://github.com/DSpace/dspace-angular/issues/368 is resolved
+   * Strips authority operator from filter value
+   * e.g. 'test ,authority' => 'test'
+   *
+   * @param value
+   */
+  normalizeFilterValue(value: string) {
+    // const pattern = /,[^,]*$/g;
+    const pattern = /,authority*$/g;
+    return value.replace(pattern, '');
+  }
 }
diff --git a/src/app/+search-page/search-options.model.ts b/src/app/+search-page/search-options.model.ts
index 123cf950f80b7a1cb26c246489c76d19ba50e929..2b18854e1eb8109357017d29d2d6877af797bdb5 100644
--- a/src/app/+search-page/search-options.model.ts
+++ b/src/app/+search-page/search-options.model.ts
@@ -3,21 +3,27 @@ import { URLCombiner } from '../core/url-combiner/url-combiner';
 import 'core-js/library/fn/object/entries';
 import { SearchFilter } from './search-filter.model';
 import { DSpaceObjectType } from '../core/shared/dspace-object-type.model';
+import { SetViewMode } from '../shared/view-mode';
 
 /**
  * This model class represents all parameters needed to request information about a certain search request
  */
 export class SearchOptions {
+  configuration?: string;
+  view?: SetViewMode = SetViewMode.List;
   scope?: string;
   query?: string;
   dsoType?: DSpaceObjectType;
-  filters?: SearchFilter[];
+  filters?: any;
+  fixedFilter?: any;
 
-  constructor(options: {scope?: string, query?: string, dsoType?: DSpaceObjectType, filters?: SearchFilter[]}) {
+  constructor(options: {configuration?: string, scope?: string, query?: string, dsoType?: DSpaceObjectType, filters?: SearchFilter[], fixedFilter?: any}) {
+      this.configuration = options.configuration;
       this.scope = options.scope;
       this.query = options.query;
       this.dsoType = options.dsoType;
       this.filters = options.filters;
+      this.fixedFilter = options.fixedFilter;
   }
 
   /**
@@ -27,7 +33,12 @@ export class SearchOptions {
    * @returns {string} URL with all search options and passed arguments as query parameters
    */
   toRestUrl(url: string, args: string[] = []): string {
-
+    if (isNotEmpty(this.configuration)) {
+      args.push(`configuration=${this.configuration}`);
+    }
+    if (isNotEmpty(this.fixedFilter)) {
+      args.push(this.fixedFilter);
+    }
     if (isNotEmpty(this.query)) {
       args.push(`query=${this.query}`);
     }
@@ -39,7 +50,10 @@ export class SearchOptions {
     }
     if (isNotEmpty(this.filters)) {
       this.filters.forEach((filter: SearchFilter) => {
-        filter.values.forEach((value) => args.push(`${filter.key}=${value},${filter.operator}`));
+        filter.values.forEach((value) => {
+          const filterValue = value.includes(',') ? `${value}` : `${value},${filter.operator}`;
+          args.push(`${filter.key}=${filterValue}`)
+        });
       });
     }
     if (isNotEmpty(args)) {
diff --git a/src/app/+search-page/search-page-routing.module.ts b/src/app/+search-page/search-page-routing.module.ts
index 65cca99a343c80bb3f670a7a1d859a559765a4f3..8c138c0d527bb1138288f949d2691f08e0c9f3ef 100644
--- a/src/app/+search-page/search-page-routing.module.ts
+++ b/src/app/+search-page/search-page-routing.module.ts
@@ -2,11 +2,14 @@ import { NgModule } from '@angular/core';
 import { RouterModule } from '@angular/router';
 
 import { SearchPageComponent } from './search-page.component';
+import { FilteredSearchPageComponent } from './filtered-search-page.component';
+import { FilteredSearchPageGuard } from './filtered-search-page.guard';
 
 @NgModule({
   imports: [
     RouterModule.forChild([
-      { path: '', component: SearchPageComponent, data: { title: 'search.title' } }
+      { path: '', component: SearchPageComponent, data: { title: 'search.title' } },
+      { path: ':filter', component: FilteredSearchPageComponent, canActivate: [FilteredSearchPageGuard], data: { title: 'search.' }}
     ])
   ]
 })
diff --git a/src/app/+search-page/search-page.component.html b/src/app/+search-page/search-page.component.html
index 6476f8bd6864855a5a8cd52f9d2a4fedf1399e1f..c11e863429f6250e76f79e8a922b249a4eb14060 100644
--- a/src/app/+search-page/search-page.component.html
+++ b/src/app/+search-page/search-page.component.html
@@ -1,16 +1,17 @@
 <div class="container">
     <div class="search-page row">
-        <ds-search-sidebar *ngIf="!(isXsOrSm$ | async)" class="col-3 sidebar-md-sticky"
+        <ds-search-sidebar *ngIf="!(isXsOrSm$ | async)" class="col-{{sideBarWidth}} sidebar-md-sticky"
                            id="search-sidebar"
-                           [resultCount]="(resultsRD$ | async)?.payload.totalElements"></ds-search-sidebar>
-        <div class="col-12 col-md-9">
-            <ds-search-form id="search-form"
+                           [resultCount]="(resultsRD$ | async)?.payload.totalElements" [inPlaceSearch]="inPlaceSearch"></ds-search-sidebar>
+        <div class="col-12 col-md-{{12 - sideBarWidth}}">
+            <ds-search-form *ngIf="searchEnabled" id="search-form"
                             [query]="(searchOptions$ | async)?.query"
                             [scope]="(searchOptions$ | async)?.scope"
                             [currentUrl]="getSearchLink()"
-                            [scopes]="(scopeListRD$ | async)">
+                            [scopes]="(scopeListRD$ | async)"
+                            [inPlaceSearch]="inPlaceSearch">
             </ds-search-form>
-            <ds-search-labels></ds-search-labels>
+            <ds-search-labels *ngIf="searchEnabled" [inPlaceSearch]="inPlaceSearch"></ds-search-labels>
             <div class="row">
                 <div id="search-body"
                      class="row-offcanvas row-offcanvas-left"
@@ -23,7 +24,7 @@
                     </ds-search-sidebar>
                     <div id="search-content" class="col-12">
                         <div class="d-block d-md-none search-controls clearfix">
-                            <ds-view-mode-switch></ds-view-mode-switch>
+                            <ds-view-mode-switch [inPlaceSearch]="inPlaceSearch"></ds-view-mode-switch>
                             <button (click)="openSidebar()" aria-controls="#search-body"
                                     class="btn btn-outline-primary float-right open-sidebar"><i
                                     class="fas fa-sliders"></i> {{"search.sidebar.open"
@@ -31,7 +32,9 @@
                             </button>
                         </div>
                         <ds-search-results [searchResults]="resultsRD$ | async"
-                                           [searchConfig]="searchOptions$ | async"></ds-search-results>
+                                           [searchConfig]="searchOptions$ | async"
+                                           [fixedFilter]="fixedFilter$ | async"
+                                           [disableHeader]="!searchEnabled"></ds-search-results>
                     </div>
                 </div>
             </div>
diff --git a/src/app/+search-page/search-page.component.spec.ts b/src/app/+search-page/search-page.component.spec.ts
index 1991cf8f1b64c36c9cd292d10d2aa2b8c1dff57a..88c7c693d39a84069c4098bcd8c74f76f12bca7a 100644
--- a/src/app/+search-page/search-page.component.spec.ts
+++ b/src/app/+search-page/search-page.component.spec.ts
@@ -20,91 +20,142 @@ import { SearchSidebarService } from './search-sidebar/search-sidebar.service';
 import { SearchFilterService } from './search-filters/search-filter/search-filter.service';
 import { SearchConfigurationService } from './search-service/search-configuration.service';
 import { RemoteData } from '../core/data/remote-data';
-
-describe('SearchPageComponent', () => {
-  let comp: SearchPageComponent;
-  let fixture: ComponentFixture<SearchPageComponent>;
-  let searchServiceObject: SearchService;
-  const store: Store<SearchPageComponent> = jasmine.createSpyObj('store', {
-    /* tslint:disable:no-empty */
-    dispatch: {},
-    /* tslint:enable:no-empty */
-    select: observableOf(true)
-  });
-  const pagination: PaginationComponentOptions = new PaginationComponentOptions();
-  pagination.id = 'search-results-pagination';
-  pagination.currentPage = 1;
-  pagination.pageSize = 10;
-  const sort: SortOptions = new SortOptions('score', SortDirection.DESC);
-  const mockResults = observableOf(new RemoteData(false, false, true, null, ['test', 'data']));
-  const searchServiceStub = jasmine.createSpyObj('SearchService', {
-    search: mockResults,
-    getSearchLink: '/search',
-    getScopes: observableOf(['test-scope'])
-  });
-  const queryParam = 'test query';
-  const scopeParam = '7669c72a-3f2a-451f-a3b9-9210e7a4c02f';
-  const paginatedSearchOptions = {
+import { SEARCH_CONFIG_SERVICE } from '../+my-dspace-page/my-dspace-page.component';
+import { RouteService } from '../shared/services/route.service';
+import { SearchConfigurationServiceStub } from '../shared/testing/search-configuration-service-stub';
+import { PaginatedSearchOptions } from './paginated-search-options.model';
+import { SearchFixedFilterService } from './search-filters/search-filter/search-fixed-filter.service';
+
+let comp: SearchPageComponent;
+let fixture: ComponentFixture<SearchPageComponent>;
+let searchServiceObject: SearchService;
+let searchConfigurationServiceObject: SearchConfigurationService;
+const store: Store<SearchPageComponent> = jasmine.createSpyObj('store', {
+  /* tslint:disable:no-empty */
+  dispatch: {},
+  /* tslint:enable:no-empty */
+  select: observableOf(true)
+});
+const pagination: PaginationComponentOptions = new PaginationComponentOptions();
+pagination.id = 'search-results-pagination';
+pagination.currentPage = 1;
+pagination.pageSize = 10;
+const sort: SortOptions = new SortOptions('score', SortDirection.DESC);
+const mockResults = observableOf(new RemoteData(false, false, true, null, ['test', 'data']));
+const searchServiceStub = jasmine.createSpyObj('SearchService', {
+  search: mockResults,
+  getSearchLink: '/search',
+  getScopes: observableOf(['test-scope'])
+});
+const configurationParam = 'default';
+const queryParam = 'test query';
+const scopeParam = '7669c72a-3f2a-451f-a3b9-9210e7a4c02f';
+const fixedFilter = 'fixed filter';
+const paginatedSearchOptions = new PaginatedSearchOptions({
+  configuration: configurationParam,
+  query: queryParam,
+  scope: scopeParam,
+  fixedFilter: fixedFilter,
+  pagination,
+  sort
+});
+const activatedRouteStub = {
+  snapshot: {
+    queryParamMap: new Map([
+      ['query', queryParam],
+      ['scope', scopeParam]
+    ])
+  },
+  queryParams: observableOf({
     query: queryParam,
-    scope: scopeParam,
-    pagination,
-    sort
-  };
-  const activatedRouteStub = {
-    queryParams: observableOf({
-      query: queryParam,
-      scope: scopeParam
-    })
-  };
-  const sidebarService = {
-    isCollapsed: observableOf(true),
-    collapse: () => this.isCollapsed = observableOf(true),
-    expand: () => this.isCollapsed = observableOf(false)
-  };
+    scope: scopeParam
+  })
+};
+const sidebarService = {
+  isCollapsed: observableOf(true),
+  collapse: () => this.isCollapsed = observableOf(true),
+  expand: () => this.isCollapsed = observableOf(false)
+};
+
+const routeServiceStub = {
+  getRouteParameterValue: () => {
+    return observableOf('');
+  },
+  getQueryParameterValue: () => {
+    return observableOf('')
+  },
+  getQueryParamsWithPrefix: () => {
+    return observableOf('')
+  }
+};
+const mockFixedFilterService: SearchFixedFilterService = {
+  getQueryByFilterName: (filter: string) => {
+    return observableOf(undefined)
+  }
+} as SearchFixedFilterService;
+
+export function configureSearchComponentTestingModule(compType) {
+  TestBed.configureTestingModule({
+    imports: [TranslateModule.forRoot(), RouterTestingModule.withRoutes([]), NoopAnimationsModule, NgbCollapseModule.forRoot()],
+    declarations: [compType],
+    providers: [
+      { provide: SearchService, useValue: searchServiceStub },
+      {
+        provide: CommunityDataService,
+        useValue: jasmine.createSpyObj('communityService', ['findById', 'findAll'])
+      },
+      { provide: ActivatedRoute, useValue: activatedRouteStub },
+      { provide: RouteService, useValue: routeServiceStub },
+      {
+        provide: Store, useValue: store
+      },
+      {
+        provide: HostWindowService, useValue: jasmine.createSpyObj('hostWindowService',
+          {
+            isXs: observableOf(true),
+            isSm: observableOf(false),
+            isXsOrSm: observableOf(true)
+          })
+      },
+      {
+        provide: SearchSidebarService,
+        useValue: sidebarService
+      },
+      {
+        provide: SearchFilterService,
+        useValue: {}
+      },
+      {
+        provide: SearchFixedFilterService,
+        useValue: mockFixedFilterService
+      },
+      {
+        provide: SearchConfigurationService,
+        useValue: {
+          paginatedSearchOptions: hot('a', {
+            a: paginatedSearchOptions
+          }),
+          getCurrentScope: (a) => observableOf('test-id'),
+          /* tslint:disable:no-empty */
+          updateFixedFilter: (newFilter) => {
+          }
+          /* tslint:enable:no-empty */
+        }
+      },
+      {
+        provide: SEARCH_CONFIG_SERVICE,
+        useValue: new SearchConfigurationServiceStub()
+      },
+    ],
+    schemas: [NO_ERRORS_SCHEMA]
+  }).overrideComponent(compType, {
+    set: { changeDetection: ChangeDetectionStrategy.Default }
+  }).compileComponents();
+}
 
+describe('SearchPageComponent', () => {
   beforeEach(async(() => {
-    TestBed.configureTestingModule({
-      imports: [TranslateModule.forRoot(), RouterTestingModule.withRoutes([]), NoopAnimationsModule, NgbCollapseModule.forRoot()],
-      declarations: [SearchPageComponent],
-      providers: [
-        { provide: SearchService, useValue: searchServiceStub },
-        {
-          provide: CommunityDataService,
-          useValue: jasmine.createSpyObj('communityService', ['findById', 'findAll'])
-        },
-        { provide: ActivatedRoute, useValue: activatedRouteStub },
-        {
-          provide: Store, useValue: store
-        },
-        {
-          provide: HostWindowService, useValue: jasmine.createSpyObj('hostWindowService',
-            {
-              isXs: observableOf(true),
-              isSm: observableOf(false),
-              isXsOrSm: observableOf(true)
-            })
-        },
-        {
-          provide: SearchSidebarService,
-          useValue: sidebarService
-        },
-        {
-          provide: SearchFilterService,
-          useValue: {}
-        }, {
-          provide: SearchConfigurationService,
-          useValue: {
-            paginatedSearchOptions: hot('a', {
-              a: paginatedSearchOptions
-            }),
-            getCurrentScope: (a) => observableOf('test-id')
-          }
-        },
-      ],
-      schemas: [NO_ERRORS_SCHEMA]
-    }).overrideComponent(SearchPageComponent, {
-      set: { changeDetection: ChangeDetectionStrategy.Default }
-    }).compileComponents();
+    configureSearchComponentTestingModule(SearchPageComponent);
   }));
 
   beforeEach(() => {
@@ -112,25 +163,21 @@ describe('SearchPageComponent', () => {
     comp = fixture.componentInstance; // SearchPageComponent test instance
     fixture.detectChanges();
     searchServiceObject = (comp as any).service;
+    searchConfigurationServiceObject = (comp as any).searchConfigService;
+  });
+
+  afterEach(() => {
+    comp = null;
+    searchServiceObject = null;
+    searchConfigurationServiceObject = null;
   });
 
   it('should get the scope and query from the route parameters', () => {
+
+    searchConfigurationServiceObject.paginatedSearchOptions.next(paginatedSearchOptions);
     expect(comp.searchOptions$).toBeObservable(cold('b', {
       b: paginatedSearchOptions
     }));
-  });
-
-  describe('when the closeSidebar event is emitted clicked in mobile view', () => {
-
-    beforeEach(() => {
-      spyOn(comp, 'closeSidebar');
-      const closeSidebarButton = fixture.debugElement.query(By.css('#search-sidebar-sm'));
-      closeSidebarButton.triggerEventHandler('toggleSidebar', null);
-    });
-
-    it('should trigger the closeSidebar function', () => {
-      expect(comp.closeSidebar).toHaveBeenCalled();
-    });
 
   });
 
@@ -177,4 +224,4 @@ describe('SearchPageComponent', () => {
     });
 
   });
-})
+});
diff --git a/src/app/+search-page/search-page.component.ts b/src/app/+search-page/search-page.component.ts
index 0c572a3a843775813f55df4fc688b668d520bd97..2b343ac5848911869952fc2b681bef9279ce9749 100644
--- a/src/app/+search-page/search-page.component.ts
+++ b/src/app/+search-page/search-page.component.ts
@@ -1,5 +1,5 @@
-import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
-import { BehaviorSubject, Observable, Subscription } from 'rxjs';
+import { ChangeDetectionStrategy, Component, Inject, Input, OnInit } from '@angular/core';
+import { Observable ,  Subscription ,  BehaviorSubject } from 'rxjs';
 import { switchMap, } from 'rxjs/operators';
 import { PaginatedList } from '../core/data/paginated-list';
 import { RemoteData } from '../core/data/remote-data';
@@ -7,13 +7,16 @@ import { DSpaceObject } from '../core/shared/dspace-object.model';
 import { pushInOut } from '../shared/animations/push';
 import { HostWindowService } from '../shared/host-window.service';
 import { PaginatedSearchOptions } from './paginated-search-options.model';
-import { SearchFilterService } from './search-filters/search-filter/search-filter.service';
 import { SearchResult } from './search-result.model';
 import { SearchService } from './search-service/search.service';
 import { SearchSidebarService } from './search-sidebar/search-sidebar.service';
-import { hasValue } from '../shared/empty.util';
+import { hasValue, isNotEmpty } from '../shared/empty.util';
 import { SearchConfigurationService } from './search-service/search-configuration.service';
 import { getSucceededRemoteData } from '../core/shared/operators';
+import { RouteService } from '../shared/services/route.service';
+import { SEARCH_CONFIG_SERVICE } from '../+my-dspace-page/my-dspace-page.component';
+
+export const SEARCH_ROUTE = '/search';
 
 /**
  * This component renders a simple item page.
@@ -26,11 +29,18 @@ import { getSucceededRemoteData } from '../core/shared/operators';
   styleUrls: ['./search-page.component.scss'],
   templateUrl: './search-page.component.html',
   changeDetection: ChangeDetectionStrategy.OnPush,
-  animations: [pushInOut]
+  animations: [pushInOut],
+  providers: [
+    {
+      provide: SEARCH_CONFIG_SERVICE,
+      useClass: SearchConfigurationService
+    }
+  ]
 })
 
 /**
  * This component represents the whole search page
+ * It renders search results depending on the current search options
  */
 export class SearchPageComponent implements OnInit {
 
@@ -59,10 +69,34 @@ export class SearchPageComponent implements OnInit {
    */
   sub: Subscription;
 
-  constructor(private service: SearchService,
-              private sidebarService: SearchSidebarService,
-              private windowService: HostWindowService,
-              private searchConfigService: SearchConfigurationService) {
+  /**
+   * True when the search component should show results on the current page
+   */
+  @Input() inPlaceSearch = true;
+
+  /**
+   * Whether or not the search bar should be visible
+   */
+  @Input()
+  searchEnabled = true;
+
+  /**
+   * The width of the sidebar (bootstrap columns)
+   */
+  @Input()
+  sideBarWidth = 3;
+
+  /**
+   * The currently applied filter (determines title of search)
+   */
+  @Input()
+  fixedFilter$: Observable<string>;
+
+  constructor(protected service: SearchService,
+              protected sidebarService: SearchSidebarService,
+              protected windowService: HostWindowService,
+              @Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: SearchConfigurationService,
+              protected routeService: RouteService) {
     this.isXsOrSm$ = this.windowService.isXsOrSm();
   }
 
@@ -74,7 +108,7 @@ export class SearchPageComponent implements OnInit {
    * If something changes, update the list of scopes for the dropdown
    */
   ngOnInit(): void {
-    this.searchOptions$ = this.searchConfigService.paginatedSearchOptions;
+    this.searchOptions$ = this.getSearchOptions();
     this.sub = this.searchOptions$.pipe(
       switchMap((options) => this.service.search(options).pipe(getSucceededRemoteData())))
       .subscribe((results) => {
@@ -83,6 +117,17 @@ export class SearchPageComponent implements OnInit {
     this.scopeListRD$ = this.searchConfigService.getCurrentScope('').pipe(
       switchMap((scopeId) => this.service.getScopes(scopeId))
     );
+    if (!isNotEmpty(this.fixedFilter$)) {
+      this.fixedFilter$ = this.routeService.getRouteParameterValue('filter');
+    }
+  }
+
+  /**
+   * Get the current paginated search options
+   * @returns {Observable<PaginatedSearchOptions>}
+   */
+  protected getSearchOptions(): Observable<PaginatedSearchOptions> {
+    return this.searchConfigService.paginatedSearchOptions;
   }
 
   /**
@@ -108,9 +153,12 @@ export class SearchPageComponent implements OnInit {
   }
 
   /**
-   * @returns {string} The base path to the search page
+   * @returns {string} The base path to the search page, or the current page when inPlaceSearch is true
    */
   public getSearchLink(): string {
+    if (this.inPlaceSearch) {
+      return './';
+    }
     return this.service.getSearchLink();
   }
 
diff --git a/src/app/+search-page/search-page.module.ts b/src/app/+search-page/search-page.module.ts
index dc895e5d703f4f40d7daa79da208019c947935f9..65558eae175e1f934ff2eaa0ef7bab0dc54ac0c7 100644
--- a/src/app/+search-page/search-page.module.ts
+++ b/src/app/+search-page/search-page.module.ts
@@ -5,9 +5,6 @@ import { SharedModule } from '../shared/shared.module';
 import { SearchPageRoutingModule } from './search-page-routing.module';
 import { SearchPageComponent } from './search-page.component';
 import { SearchResultsComponent } from './search-results/search-results.component';
-import { ItemSearchResultListElementComponent } from '../shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component';
-import { CollectionSearchResultListElementComponent } from '../shared/object-list/search-result-list-element/collection-search-result/collection-search-result-list-element.component';
-import { CommunitySearchResultListElementComponent } from '../shared/object-list/search-result-list-element/community-search-result/community-search-result-list-element.component';
 import { ItemSearchResultGridElementComponent } from '../shared/object-grid/search-result-grid-element/item-search-result/item-search-result-grid-element.component';
 import { CommunitySearchResultGridElementComponent } from '../shared/object-grid/search-result-grid-element/community-search-result/community-search-result-grid-element.component'
 import { CollectionSearchResultGridElementComponent } from '../shared/object-grid/search-result-grid-element/collection-search-result/collection-search-result-grid-element.component';
@@ -20,6 +17,9 @@ import { SearchFiltersComponent } from './search-filters/search-filters.componen
 import { SearchFilterComponent } from './search-filters/search-filter/search-filter.component';
 import { SearchFacetFilterComponent } from './search-filters/search-filter/search-facet-filter/search-facet-filter.component';
 import { SearchFilterService } from './search-filters/search-filter/search-filter.service';
+import { FilteredSearchPageComponent } from './filtered-search-page.component';
+import { SearchFixedFilterService } from './search-filters/search-filter/search-fixed-filter.service';
+import { FilteredSearchPageGuard } from './filtered-search-page.guard';
 import { SearchLabelsComponent } from './search-labels/search-labels.component';
 import { SearchRangeFilterComponent } from './search-filters/search-filter/search-range-filter/search-range-filter.component';
 import { SearchTextFilterComponent } from './search-filters/search-filter/search-text-filter/search-text-filter.component';
@@ -30,11 +30,39 @@ import { SearchConfigurationService } from './search-service/search-configuratio
 import { SearchFacetOptionComponent } from './search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component';
 import { SearchFacetSelectedOptionComponent } from './search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component';
 import { SearchFacetRangeOptionComponent } from './search-filters/search-filter/search-facet-filter-options/search-facet-range-option/search-facet-range-option.component';
+import { SearchSwitchConfigurationComponent } from './search-switch-configuration/search-switch-configuration.component';
+import { SearchAuthorityFilterComponent } from './search-filters/search-filter/search-authority-filter/search-authority-filter.component';
 
 const effects = [
   SearchSidebarEffects
 ];
 
+const components = [
+  SearchPageComponent,
+  SearchResultsComponent,
+  SearchSidebarComponent,
+  SearchSettingsComponent,
+  ItemSearchResultGridElementComponent,
+  CollectionSearchResultGridElementComponent,
+  CommunitySearchResultGridElementComponent,
+  SearchFiltersComponent,
+  SearchFilterComponent,
+  SearchFacetFilterComponent,
+  SearchLabelsComponent,
+  SearchFacetFilterComponent,
+  SearchFacetFilterWrapperComponent,
+  SearchRangeFilterComponent,
+  SearchTextFilterComponent,
+  SearchHierarchyFilterComponent,
+  SearchBooleanFilterComponent,
+  SearchFacetOptionComponent,
+  SearchFacetSelectedOptionComponent,
+  SearchFacetRangeOptionComponent,
+  SearchSwitchConfigurationComponent,
+  SearchAuthorityFilterComponent,
+  FilteredSearchPageComponent
+];
+
 @NgModule({
   imports: [
     SearchPageRoutingModule,
@@ -43,37 +71,16 @@ const effects = [
     EffectsModule.forFeature(effects),
     CoreModule.forRoot()
   ],
-  declarations: [
-    SearchPageComponent,
-    SearchResultsComponent,
-    SearchSidebarComponent,
-    SearchSettingsComponent,
-    ItemSearchResultGridElementComponent,
-    CollectionSearchResultGridElementComponent,
-    CommunitySearchResultGridElementComponent,
-    SearchFiltersComponent,
-    SearchFilterComponent,
-    SearchFacetFilterComponent,
-    SearchLabelsComponent,
-    SearchFacetFilterComponent,
-    SearchFacetFilterWrapperComponent,
-    SearchRangeFilterComponent,
-    SearchTextFilterComponent,
-    SearchHierarchyFilterComponent,
-    SearchBooleanFilterComponent,
-    SearchFacetOptionComponent,
-    SearchFacetSelectedOptionComponent,
-    SearchFacetRangeOptionComponent
-  ],
+  declarations: components,
   providers: [
     SearchSidebarService,
     SearchFilterService,
+    SearchFixedFilterService,
+    FilteredSearchPageGuard,
+    SearchFilterService,
     SearchConfigurationService
   ],
   entryComponents: [
-    ItemSearchResultListElementComponent,
-    CollectionSearchResultListElementComponent,
-    CommunitySearchResultListElementComponent,
     ItemSearchResultGridElementComponent,
     CollectionSearchResultGridElementComponent,
     CommunitySearchResultGridElementComponent,
@@ -84,8 +91,10 @@ const effects = [
     SearchBooleanFilterComponent,
     SearchFacetOptionComponent,
     SearchFacetSelectedOptionComponent,
-    SearchFacetRangeOptionComponent
-  ]
+    SearchFacetRangeOptionComponent,
+    SearchAuthorityFilterComponent
+  ],
+  exports: components
 })
 
 /**
diff --git a/src/app/+search-page/search-result.model.ts b/src/app/+search-page/search-result.model.ts
index ff865610c6c0ddbd6feab86be0ed1d236e3597b3..0354edbc6b23692f00b39d785867da106a8d248c 100644
--- a/src/app/+search-page/search-result.model.ts
+++ b/src/app/+search-page/search-result.model.ts
@@ -9,7 +9,7 @@ export class SearchResult<T extends DSpaceObject> implements ListableObject {
   /**
    * The DSpaceObject that was found
    */
-  dspaceObject: T;
+  indexableObject: T;
 
   /**
    * The metadata that was used to find this item, hithighlighted
diff --git a/src/app/+search-page/search-results/search-results.component.html b/src/app/+search-page/search-results/search-results.component.html
index 4915b552c37285c5a964c933956c5651e796294c..d7ecb0357ee5b92e84794916f56013f602089336 100644
--- a/src/app/+search-page/search-results/search-results.component.html
+++ b/src/app/+search-page/search-results/search-results.component.html
@@ -1,4 +1,4 @@
-<h2>{{ 'search.results.head' | translate }}</h2>
+<h2 *ngIf="!disableHeader">{{ getTitleKey() | translate }}</h2>
 <div *ngIf="searchResults?.hasSucceeded && !searchResults?.isLoading && searchResults?.payload?.page.length > 0" @fadeIn>
 <ds-viewable-collection
   [config]="searchConfig.pagination"
diff --git a/src/app/+search-page/search-results/search-results.component.spec.ts b/src/app/+search-page/search-results/search-results.component.spec.ts
index 8d0566d1df2c1ac1ada6dc3a3acff140a17a14e4..518829e69f831c30e3d4a4f79665c93877b31aea 100644
--- a/src/app/+search-page/search-results/search-results.component.spec.ts
+++ b/src/app/+search-page/search-results/search-results.component.spec.ts
@@ -1,7 +1,7 @@
 import { ComponentFixture, TestBed, async, tick, fakeAsync } from '@angular/core/testing';
 import { By } from '@angular/platform-browser';
 import { NoopAnimationsModule } from '@angular/platform-browser/animations';
-import { NO_ERRORS_SCHEMA } from '@angular/core';
+import { DebugElement, NO_ERRORS_SCHEMA } from '@angular/core';
 import { ResourceType } from '../../core/shared/resource-type';
 import { Community } from '../../core/shared/community.model';
 import { TranslateModule } from '@ngx-translate/core';
@@ -11,6 +11,8 @@ import { QueryParamsDirectiveStub } from '../../shared/testing/query-params-dire
 describe('SearchResultsComponent', () => {
   let comp: SearchResultsComponent;
   let fixture: ComponentFixture<SearchResultsComponent>;
+  let heading: DebugElement;
+  let title: DebugElement;
 
   beforeEach(async(() => {
     TestBed.configureTestingModule({
@@ -24,7 +26,9 @@ describe('SearchResultsComponent', () => {
 
   beforeEach(() => {
     fixture = TestBed.createComponent(SearchResultsComponent);
-    comp = fixture.componentInstance; // SearchResultsComponent test instance
+    comp = fixture.componentInstance; // SearchFormComponent test instance
+    heading = fixture.debugElement.query(By.css('heading'));
+    title = fixture.debugElement.query(By.css('h2'));
   });
 
   it('should display results when results are not empty', () => {
diff --git a/src/app/+search-page/search-results/search-results.component.ts b/src/app/+search-page/search-results/search-results.component.ts
index ae0abfcd276610b290fdd52c7509ed23dc597896..9656ba95748d392ac261ffc198dc948ced8d9ab3 100644
--- a/src/app/+search-page/search-results/search-results.component.ts
+++ b/src/app/+search-page/search-results/search-results.component.ts
@@ -2,11 +2,12 @@ import { Component, Input } from '@angular/core';
 import { RemoteData } from '../../core/data/remote-data';
 import { DSpaceObject } from '../../core/shared/dspace-object.model';
 import { fadeIn, fadeInOut } from '../../shared/animations/fade';
+import { SetViewMode } from '../../shared/view-mode';
 import { SearchOptions } from '../search-options.model';
 import { SearchResult } from '../search-result.model';
 import { PaginatedList } from '../../core/data/paginated-list';
-import { ViewMode } from '../../core/shared/view-mode.model';
 import { isNotEmpty } from '../../shared/empty.util';
+import { SortOptions } from '../../core/cache/models/sort-options.model';
 
 @Component({
   selector: 'ds-search-results',
@@ -32,9 +33,38 @@ export class SearchResultsComponent {
   @Input() searchConfig: SearchOptions;
 
   /**
-   * The current view mode for the search results
+   * The current sorting configuration of the search
    */
-  @Input() viewMode: ViewMode;
+  @Input() sortConfig: SortOptions;
+
+  /**
+   * The current view-mode of the list
+   */
+  @Input() viewMode: SetViewMode;
+
+  /**
+   * An optional fixed filter to filter the result on one type
+   */
+  @Input() fixedFilter: string;
+
+  /**
+   * Whether or not to hide the header of the results
+   * Defaults to a visible header
+   */
+  @Input() disableHeader = false;
+
+  /**
+   * Get the i18n key for the title depending on the fixed filter
+   * Defaults to 'search.results.head' if there's no fixed filter found
+   * @returns {string}
+   */
+  getTitleKey() {
+    if (isNotEmpty(this.fixedFilter)) {
+      return 'search.' + this.fixedFilter + '.results.head'
+    } else {
+      return 'search.results.head';
+    }
+  }
 
   /**
    * Method to change the given string by surrounding it by quotes if not already present.
diff --git a/src/app/+search-page/search-service/facet-value.model.ts b/src/app/+search-page/search-service/facet-value.model.ts
index 0f673f34850bda41531781193ba42da94c2faaed..d2cc521356fa5be8ba9465f5fec75581a0a63cce 100644
--- a/src/app/+search-page/search-service/facet-value.model.ts
+++ b/src/app/+search-page/search-service/facet-value.model.ts
@@ -5,7 +5,13 @@ import { autoserialize, autoserializeAs } from 'cerialize';
  */
 export class FacetValue {
   /**
-   * The display value of the facet value
+   * The display label of the facet value
+   */
+  @autoserialize
+  label: string;
+
+  /**
+   * The value of the facet value
    */
   @autoserializeAs(String, 'label')
   value: string;
diff --git a/src/app/+search-page/search-service/filter-type.model.ts b/src/app/+search-page/search-service/filter-type.model.ts
index d9b96293479b1f1bab897b9f31792bc8b4f58e18..d5a338de6da8ad933a540a0a7bcc526c2a5dc4b7 100644
--- a/src/app/+search-page/search-service/filter-type.model.ts
+++ b/src/app/+search-page/search-service/filter-type.model.ts
@@ -2,6 +2,11 @@
  * Enumeration containing all possible types for filters
  */
 export enum FilterType {
+  /**
+   * Represents authority facets
+   */
+  authority = 'authority',
+
   /**
    * Represents simple text facets
    */
diff --git a/src/app/+search-page/search-service/search-configuration.service.spec.ts b/src/app/+search-page/search-service/search-configuration.service.spec.ts
index f1f4ef8bdce8950dae78dce3feb78972ebfdc162..79932805c1013a330e13cd1a8acc8d8f2b3a8bfe 100644
--- a/src/app/+search-page/search-service/search-configuration.service.spec.ts
+++ b/src/app/+search-page/search-service/search-configuration.service.spec.ts
@@ -17,23 +17,28 @@ describe('SearchConfigurationService', () => {
   const defaults = new PaginatedSearchOptions({
     pagination: Object.assign(new PaginationComponentOptions(), { currentPage: 1, pageSize: 20 }),
     sort: new SortOptions('score', SortDirection.DESC),
+    configuration: 'default',
     query: '',
     scope: ''
   });
 
   const backendFilters = [new SearchFilter('f.author', ['another value']), new SearchFilter('f.date', ['[2013 TO 2018]'])];
 
-  const spy = jasmine.createSpyObj('RouteService', {
+  const routeService = jasmine.createSpyObj('RouteService', {
     getQueryParameterValue: observableOf(value1),
-    getQueryParamsWithPrefix: observableOf(prefixFilter)
+    getQueryParamsWithPrefix: observableOf(prefixFilter),
+    getRouteParameterValue: observableOf('')
+  });
+
+  const fixedFilterService = jasmine.createSpyObj('SearchFixedFilterService', {
+    getQueryByFilterName: observableOf(''),
   });
 
   const activatedRoute: any = new ActivatedRouteStub();
 
   beforeEach(() => {
-    service = new SearchConfigurationService(spy, activatedRoute);
+    service = new SearchConfigurationService(routeService, fixedFilterService, activatedRoute);
   });
-
   describe('when the scope is called', () => {
     beforeEach(() => {
       service.getCurrentScope('');
@@ -43,6 +48,15 @@ describe('SearchConfigurationService', () => {
     });
   });
 
+  describe('when getCurrentConfiguration is called', () => {
+    beforeEach(() => {
+      service.getCurrentConfiguration('');
+    });
+    it('should call getQueryParameterValue on the routeService with parameter name \'configuration\'', () => {
+      expect((service as any).routeService.getQueryParameterValue).toHaveBeenCalledWith('configuration');
+    });
+  });
+
   describe('when getCurrentQuery is called', () => {
     beforeEach(() => {
       service.getCurrentQuery('');
@@ -94,6 +108,7 @@ describe('SearchConfigurationService', () => {
       expect((service as any).routeService.getQueryParameterValue).toHaveBeenCalledWith('sortField');
     });
   });
+
   describe('when getCurrentPagination is called', () => {
     beforeEach(() => {
       service.getCurrentPagination({ currentPage: 1, pageSize: 10 } as any);
@@ -105,11 +120,13 @@ describe('SearchConfigurationService', () => {
       expect((service as any).routeService.getQueryParameterValue).toHaveBeenCalledWith('pageSize');
     });
   });
+
   describe('when subscribeToSearchOptions or subscribeToPaginatedSearchOptions is called', () => {
     beforeEach(() => {
       spyOn(service, 'getCurrentPagination').and.callThrough();
       spyOn(service, 'getCurrentSort').and.callThrough();
       spyOn(service, 'getCurrentScope').and.callThrough();
+      spyOn(service, 'getCurrentConfiguration').and.callThrough();
       spyOn(service, 'getCurrentQuery').and.callThrough();
       spyOn(service, 'getCurrentDSOType').and.callThrough();
       spyOn(service, 'getCurrentFilters').and.callThrough();
@@ -123,6 +140,7 @@ describe('SearchConfigurationService', () => {
         expect(service.getCurrentPagination).not.toHaveBeenCalled();
         expect(service.getCurrentSort).not.toHaveBeenCalled();
         expect(service.getCurrentScope).toHaveBeenCalled();
+        expect(service.getCurrentConfiguration).toHaveBeenCalled();
         expect(service.getCurrentQuery).toHaveBeenCalled();
         expect(service.getCurrentDSOType).toHaveBeenCalled();
         expect(service.getCurrentFilters).toHaveBeenCalled();
@@ -137,10 +155,36 @@ describe('SearchConfigurationService', () => {
         expect(service.getCurrentPagination).toHaveBeenCalled();
         expect(service.getCurrentSort).toHaveBeenCalled();
         expect(service.getCurrentScope).toHaveBeenCalled();
+        expect(service.getCurrentConfiguration).toHaveBeenCalled();
         expect(service.getCurrentQuery).toHaveBeenCalled();
         expect(service.getCurrentDSOType).toHaveBeenCalled();
         expect(service.getCurrentFilters).toHaveBeenCalled();
       });
     });
   });
+
+  describe('when getCurrentFixedFilter is called', () => {
+    beforeEach(() => {
+      service.getCurrentFixedFilter();
+    });
+    it('should call getRouteParameterValue on the routeService with parameter name \'filter\'', () => {
+      expect((service as any).routeService.getRouteParameterValue).toHaveBeenCalledWith('filter');
+    });
+  });
+
+  describe('when updateFixedFilter is called', () => {
+    const filter = 'filter';
+
+    beforeEach(() => {
+      service.updateFixedFilter(filter);
+    });
+
+    it('should update the paginated search options with the correct fixed filter', () => {
+      expect(service.paginatedSearchOptions.getValue().fixedFilter).toEqual(filter);
+    });
+
+    it('should update the search options with the correct fixed filter', () => {
+      expect(service.searchOptions.getValue().fixedFilter).toEqual(filter);
+    });
+  });
 });
diff --git a/src/app/+search-page/search-service/search-configuration.service.ts b/src/app/+search-page/search-service/search-configuration.service.ts
index 7ba1ebd75f9b5db62f1f068fabda07b0fb7462a2..39acd19ccda8780b8972c1331827ae2a595e3586 100644
--- a/src/app/+search-page/search-service/search-configuration.service.ts
+++ b/src/app/+search-page/search-service/search-configuration.service.ts
@@ -1,3 +1,6 @@
+import { Injectable, OnDestroy } from '@angular/core';
+import { ActivatedRoute, Params } from '@angular/router';
+
 import {
   BehaviorSubject,
   combineLatest as observableCombineLatest,
@@ -6,19 +9,18 @@ import {
   of as observableOf,
   Subscription
 } from 'rxjs';
-import { filter, map } from 'rxjs/operators';
+import { filter, flatMap, map } from 'rxjs/operators';
 import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model';
 import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
 import { SearchOptions } from '../search-options.model';
-import { ActivatedRoute, Params } from '@angular/router';
 import { PaginatedSearchOptions } from '../paginated-search-options.model';
-import { Injectable, OnDestroy } from '@angular/core';
 import { RouteService } from '../../shared/services/route.service';
-import { hasNoValue, hasValue, isNotEmpty } from '../../shared/empty.util';
+import { hasNoValue, hasValue, isNotEmpty, isNotEmptyOperator } from '../../shared/empty.util';
 import { RemoteData } from '../../core/data/remote-data';
 import { getSucceededRemoteData } from '../../core/shared/operators';
 import { SearchFilter } from '../search-filter.model';
 import { DSpaceObjectType } from '../../core/shared/dspace-object-type.model';
+import { SearchFixedFilterService } from '../search-filters/search-filter/search-fixed-filter.service';
 
 /**
  * Service that performs all actions that have to do with the current search configuration
@@ -28,7 +30,7 @@ export class SearchConfigurationService implements OnDestroy {
   /**
    * Default pagination settings
    */
-  private defaultPagination = Object.assign(new PaginationComponentOptions(), {
+  protected defaultPagination = Object.assign(new PaginationComponentOptions(), {
     id: 'search-page-configuration',
     pageSize: 10,
     currentPage: 1
@@ -37,22 +39,27 @@ export class SearchConfigurationService implements OnDestroy {
   /**
    * Default sort settings
    */
-  private defaultSort = new SortOptions('score', SortDirection.DESC);
+  protected defaultSort = new SortOptions('score', SortDirection.DESC);
+
+  /**
+   * Default configuration parameter setting
+   */
+  protected defaultConfiguration = 'default';
 
   /**
    * Default scope setting
    */
-  private defaultScope = '';
+  protected defaultScope = '';
 
   /**
    * Default query setting
    */
-  private defaultQuery = '';
+  protected defaultQuery = '';
 
   /**
    * Emits the current default values
    */
-  private _defaults: Observable<RemoteData<PaginatedSearchOptions>>;
+  protected _defaults: Observable<RemoteData<PaginatedSearchOptions>>;
 
   /**
    * Emits the current search options
@@ -67,28 +74,48 @@ export class SearchConfigurationService implements OnDestroy {
   /**
    * List of subscriptions to unsubscribe from on destroy
    */
-  private subs: Subscription[] = new Array();
+  protected subs: Subscription[] = new Array();
 
   /**
    * Initialize the search options
    * @param {RouteService} routeService
+   * @param {SearchFixedFilterService} fixedFilterService
    * @param {ActivatedRoute} route
    */
-  constructor(private routeService: RouteService,
-              private route: ActivatedRoute) {
+  constructor(protected routeService: RouteService,
+              protected fixedFilterService: SearchFixedFilterService,
+              protected route: ActivatedRoute) {
+
+    this.initDefaults();
+  }
+
+  /**
+   * Initialize the search options
+   */
+  protected initDefaults() {
     this.defaults
       .pipe(getSucceededRemoteData())
       .subscribe((defRD) => {
           const defs = defRD.payload;
-          this.paginatedSearchOptions = new BehaviorSubject<SearchOptions>(defs);
-          this.searchOptions = new BehaviorSubject<PaginatedSearchOptions>(defs);
+          this.paginatedSearchOptions = new BehaviorSubject<PaginatedSearchOptions>(defs);
+          this.searchOptions = new BehaviorSubject<SearchOptions>(defs);
 
           this.subs.push(this.subscribeToSearchOptions(defs));
           this.subs.push(this.subscribeToPaginatedSearchOptions(defs));
+
         }
       )
   }
 
+  /**
+   * @returns {Observable<string>} Emits the current configuration string
+   */
+  getCurrentConfiguration(defaultConfiguration: string) {
+    return this.routeService.getQueryParameterValue('configuration').pipe(map((configuration) => {
+      return configuration || defaultConfiguration;
+    }));
+  }
+
   /**
    * @returns {Observable<string>} Emits the current scope's identifier
    */
@@ -112,7 +139,7 @@ export class SearchConfigurationService implements OnDestroy {
    */
   getCurrentDSOType(): Observable<DSpaceObjectType> {
     return this.routeService.getQueryParameterValue('dsoType').pipe(
-      filter((type) => hasValue(type) && hasValue(DSpaceObjectType[type.toUpperCase()])),
+      filter((type) => isNotEmpty(type) && hasValue(DSpaceObjectType[type.toUpperCase()])),
       map((type) => DSpaceObjectType[type.toUpperCase()]),);
   }
 
@@ -174,6 +201,15 @@ export class SearchConfigurationService implements OnDestroy {
     }));
   }
 
+  /**
+   * @returns {Observable<string>} Emits the current fixed filter as a string
+   */
+  getCurrentFixedFilter(): Observable<string> {
+    return this.routeService.getRouteParameterValue('filter').pipe(
+      flatMap((f) => this.fixedFilterService.getQueryByFilterName(f))
+    );
+  }
+
   /**
    * @returns {Observable<Params>} Emits the current active filters with their values as they are displayed in the frontend URL
    */
@@ -188,10 +224,12 @@ export class SearchConfigurationService implements OnDestroy {
    */
   private subscribeToSearchOptions(defaults: SearchOptions): Subscription {
     return observableMerge(
+      this.getConfigurationPart(defaults.configuration),
       this.getScopePart(defaults.scope),
       this.getQueryPart(defaults.query),
       this.getDSOTypePart(),
-      this.getFiltersPart()
+      this.getFiltersPart(),
+      this.getFixedFilterPart()
     ).subscribe((update) => {
       const currentValue: SearchOptions = this.searchOptions.getValue();
       const updatedValue: SearchOptions = Object.assign(currentValue, update);
@@ -208,10 +246,12 @@ export class SearchConfigurationService implements OnDestroy {
     return observableMerge(
       this.getPaginationPart(defaults.pagination),
       this.getSortPart(defaults.sort),
+      this.getConfigurationPart(defaults.configuration),
       this.getScopePart(defaults.scope),
       this.getQueryPart(defaults.query),
       this.getDSOTypePart(),
-      this.getFiltersPart()
+      this.getFiltersPart(),
+      this.getFixedFilterPart()
     ).subscribe((update) => {
       const currentValue: PaginatedSearchOptions = this.paginatedSearchOptions.getValue();
       const updatedValue: PaginatedSearchOptions = Object.assign(currentValue, update);
@@ -226,6 +266,7 @@ export class SearchConfigurationService implements OnDestroy {
     if (hasNoValue(this._defaults)) {
       const options = new PaginatedSearchOptions({
         pagination: this.defaultPagination,
+        configuration: this.defaultConfiguration,
         sort: this.defaultSort,
         scope: this.defaultScope,
         query: this.defaultQuery
@@ -242,6 +283,16 @@ export class SearchConfigurationService implements OnDestroy {
     this.subs.forEach((sub) => {
       sub.unsubscribe();
     });
+    this.subs = [];
+  }
+
+  /**
+   * @returns {Observable<string>} Emits the current configuration settings as a partial SearchOptions object
+   */
+  private getConfigurationPart(defaultConfiguration: string): Observable<any> {
+    return this.getCurrentConfiguration(defaultConfiguration).pipe(map((configuration) => {
+      return { configuration }
+    }));
   }
 
   /**
@@ -297,4 +348,30 @@ export class SearchConfigurationService implements OnDestroy {
       return { filters }
     }));
   }
+
+  /**
+   * @returns {Observable<string>} Emits the current fixed filter as a partial SearchOptions object
+   */
+  private getFixedFilterPart(): Observable<any> {
+    return this.getCurrentFixedFilter().pipe(
+      isNotEmptyOperator(),
+      map((fixedFilter) => {
+        return { fixedFilter }
+      })
+    );
+  }
+
+  /**
+   * Update the fixed filter in paginated and non-paginated search options with a given value
+   * @param {string} fixedFilter
+   */
+  public updateFixedFilter(fixedFilter: string) {
+    const currentPaginatedValue: PaginatedSearchOptions = this.paginatedSearchOptions.getValue();
+    const updatedPaginatedValue: PaginatedSearchOptions = Object.assign(currentPaginatedValue, { fixedFilter: fixedFilter });
+    this.paginatedSearchOptions.next(updatedPaginatedValue);
+
+    const currentValue: SearchOptions = this.searchOptions.getValue();
+    const updatedValue: SearchOptions = Object.assign(currentValue, { fixedFilter: fixedFilter });
+    this.searchOptions.next(updatedValue);
+  }
 }
diff --git a/src/app/+search-page/search-service/search-query-response.model.ts b/src/app/+search-page/search-service/search-query-response.model.ts
index ac1d8b7df339f22a549dedc57afaae29d07a68e8..bca6e644fc4a54b82339acfadccc231e52f38e8b 100644
--- a/src/app/+search-page/search-service/search-query-response.model.ts
+++ b/src/app/+search-page/search-service/search-query-response.model.ts
@@ -34,7 +34,7 @@ export class SearchQueryResponse {
    * The sort parameters used in the search request
    */
   @autoserialize
-  configurationName: string;
+  configuration: string;
 
   /**
    * The sort parameters used in the search request
diff --git a/src/app/+search-page/search-service/search-result-element-decorator.ts b/src/app/+search-page/search-service/search-result-element-decorator.ts
index 348cf7f592ce00ec37c392230bd00decd9d310d2..59446480a3af0577aa61ced3b87de71f46842bfc 100644
--- a/src/app/+search-page/search-service/search-result-element-decorator.ts
+++ b/src/app/+search-page/search-service/search-result-element-decorator.ts
@@ -1,5 +1,6 @@
 import { GenericConstructor } from '../../core/shared/generic-constructor';
 import { ListableObject } from '../../shared/object-collection/shared/listable-object.model';
+import { isNull } from '../../shared/empty.util';
 
 /**
  * Contains the mapping between a search result component and a DSpaceObject
@@ -11,12 +12,19 @@ const searchResultMap = new Map();
  * @param {GenericConstructor<ListableObject>} domainConstructor The constructor of the DSpaceObject
  * @returns Decorator function that performs the actual mapping on initialization of the component
  */
-export function searchResultFor(domainConstructor: GenericConstructor<ListableObject>) {
+export function searchResultFor(domainConstructor: GenericConstructor<ListableObject>, configuration: string = null) {
   return function decorator(searchResult: any) {
     if (!searchResult) {
       return;
     }
-    searchResultMap.set(domainConstructor, searchResult);
+    if (isNull(configuration)) {
+      searchResultMap.set(domainConstructor, searchResult);
+    } else {
+      if (!searchResultMap.get(configuration)) {
+        searchResultMap.set(configuration, new Map());
+      }
+      searchResultMap.get(configuration).set(domainConstructor, searchResult);
+    }
   };
 }
 
@@ -25,6 +33,10 @@ export function searchResultFor(domainConstructor: GenericConstructor<ListableOb
  * @param {GenericConstructor<ListableObject>} domainConstructor The DSpaceObject's constructor for which the search result component is requested
  * @returns The component's constructor that matches the given DSpaceObject
  */
-export function getSearchResultFor(domainConstructor: GenericConstructor<ListableObject>) {
-  return searchResultMap.get(domainConstructor);
+export function getSearchResultFor(domainConstructor: GenericConstructor<ListableObject>, configuration: string = null) {
+  if (isNull(configuration) || configuration === 'default') {
+    return searchResultMap.get(domainConstructor);
+  } else {
+    return searchResultMap.get(configuration).get(domainConstructor);
+  }
 }
diff --git a/src/app/+search-page/search-service/search.service.spec.ts b/src/app/+search-page/search-service/search.service.spec.ts
index ca48b02aa72e54111dc903104b7f91a2749c3588..9ec5bc35f212fedc72ca371d22396df6907c4cd5 100644
--- a/src/app/+search-page/search-service/search.service.spec.ts
+++ b/src/app/+search-page/search-service/search.service.spec.ts
@@ -5,28 +5,29 @@ import { CommonModule } from '@angular/common';
 import { Component } from '@angular/core';
 
 import { SearchService } from './search.service';
+import { ItemDataService } from './../../core/data/item-data.service';
+import { SetViewMode } from '../../shared/view-mode';
+import { GLOBAL_CONFIG } from '../../../config';
 import { RemoteDataBuildService } from '../../core/cache/builders/remote-data-build.service';
-import { ActivatedRoute, Router, UrlTree } from '@angular/router';
+import { Router, UrlTree } from '@angular/router';
 import { RequestService } from '../../core/data/request.service';
 import { ActivatedRouteStub } from '../../shared/testing/active-router-stub';
 import { RouterStub } from '../../shared/testing/router-stub';
 import { HALEndpointService } from '../../core/shared/hal-endpoint.service';
-import { Observable, combineLatest as observableCombineLatest } from 'rxjs';
+import { combineLatest as observableCombineLatest, Observable, of as observableOf } from 'rxjs';
 import { PaginatedSearchOptions } from '../paginated-search-options.model';
 import { RemoteData } from '../../core/data/remote-data';
 import { RequestEntry } from '../../core/data/request.reducer';
 import { getMockRequestService } from '../../shared/mocks/mock-request.service';
-import {
-  FacetConfigSuccessResponse,
-  SearchSuccessResponse
-} from '../../core/cache/response.models';
+import { FacetConfigSuccessResponse, SearchSuccessResponse } from '../../core/cache/response.models';
 import { SearchQueryResponse } from './search-query-response.model';
 import { SearchFilterConfig } from './search-filter-config.model';
 import { CommunityDataService } from '../../core/data/community-data.service';
 import { ViewMode } from '../../core/shared/view-mode.model';
 import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service';
-import { of as observableOf } from 'rxjs';
 import { map } from 'rxjs/operators';
+import { RouteService } from '../../shared/services/route.service';
+import { routeServiceStub } from '../../shared/testing/route-service-stub';
 
 @Component({ template: '' })
 class DummyComponent {
@@ -50,7 +51,7 @@ describe('SearchService', () => {
         ],
         providers: [
           { provide: Router, useValue: router },
-          { provide: ActivatedRoute, useValue: route },
+          { provide: RouteService, useValue: routeServiceStub },
           { provide: RequestService, useValue: getMockRequestService() },
           { provide: RemoteDataBuildService, useValue: {} },
           { provide: HALEndpointService, useValue: {} },
@@ -71,7 +72,7 @@ describe('SearchService', () => {
   describe('', () => {
     let searchService: SearchService;
     const router = new RouterStub();
-    const route = new ActivatedRouteStub();
+    let routeService;
 
     const halService = {
       /* tslint:disable:no-empty */
@@ -107,7 +108,7 @@ describe('SearchService', () => {
         ],
         providers: [
           { provide: Router, useValue: router },
-          { provide: ActivatedRoute, useValue: route },
+          { provide: RouteService, useValue: routeServiceStub },
           { provide: RequestService, useValue: getMockRequestService() },
           { provide: RemoteDataBuildService, useValue: remoteDataBuildService },
           { provide: HALEndpointService, useValue: halService },
@@ -117,6 +118,7 @@ describe('SearchService', () => {
         ],
       });
       searchService = TestBed.get(SearchService);
+      routeService = TestBed.get(RouteService);
       const urlTree = Object.assign(new UrlTree(), { root: { children: { primary: 'search' } } });
       router.parseUrl.and.returnValue(urlTree);
     });
@@ -124,7 +126,7 @@ describe('SearchService', () => {
     it('should call the navigate method on the Router with view mode list parameter as a parameter when setViewMode is called', () => {
       searchService.setViewMode(ViewMode.List);
       expect(router.navigate).toHaveBeenCalledWith(['/search'], {
-        queryParams: { view: ViewMode.List },
+        queryParams: { view: ViewMode.List, page: 1 },
         queryParamsHandling: 'merge'
       });
     });
@@ -132,21 +134,26 @@ describe('SearchService', () => {
     it('should call the navigate method on the Router with view mode grid parameter as a parameter when setViewMode is called', () => {
       searchService.setViewMode(ViewMode.Grid);
       expect(router.navigate).toHaveBeenCalledWith(['/search'], {
-        queryParams: { view: ViewMode.Grid },
+        queryParams: { view: ViewMode.Grid, page: 1 },
         queryParamsHandling: 'merge'
       });
     });
 
     it('should return ViewMode.List when the viewMode is set to ViewMode.List in the ActivatedRoute', () => {
       let viewMode = ViewMode.Grid;
-      route.testParams = { view: ViewMode.List };
+      spyOn(routeService, 'getQueryParamMap').and.returnValue(observableOf(new Map([
+        [ 'view', ViewMode.List ],
+      ])));
+
       searchService.getViewMode().subscribe((mode) => viewMode = mode);
       expect(viewMode).toEqual(ViewMode.List);
     });
 
     it('should return ViewMode.Grid when the viewMode is set to ViewMode.Grid in the ActivatedRoute', () => {
       let viewMode = ViewMode.List;
-      route.testParams = { view: ViewMode.Grid };
+      spyOn(routeService, 'getQueryParamMap').and.returnValue(observableOf(new Map([
+        [ 'view', ViewMode.Grid ],
+      ])));
       searchService.getViewMode().subscribe((mode) => viewMode = mode);
       expect(viewMode).toEqual(ViewMode.Grid);
     });
diff --git a/src/app/+search-page/search-service/search.service.ts b/src/app/+search-page/search-service/search.service.ts
index 2c0f1f4e55d407ea045b493e52da5d779741894b..598657a1b288cc03a97c96bea3b0bd50002a45d0 100644
--- a/src/app/+search-page/search-service/search.service.ts
+++ b/src/app/+search-page/search-service/search.service.ts
@@ -1,13 +1,7 @@
 import { combineLatest as observableCombineLatest, Observable, of as observableOf } from 'rxjs';
 import { Injectable, OnDestroy } from '@angular/core';
-import {
-  ActivatedRoute,
-  NavigationExtras,
-  PRIMARY_OUTLET,
-  Router,
-  UrlSegmentGroup
-} from '@angular/router';
-import { map, switchMap, tap } from 'rxjs/operators';
+import { NavigationExtras, PRIMARY_OUTLET, Router, UrlSegmentGroup } from '@angular/router';
+import { first, map, switchMap } from 'rxjs/operators';
 import { RemoteDataBuildService } from '../../core/cache/builders/remote-data-build.service';
 import {
   FacetConfigSuccessResponse,
@@ -23,12 +17,13 @@ import { DSpaceObject } from '../../core/shared/dspace-object.model';
 import { GenericConstructor } from '../../core/shared/generic-constructor';
 import { HALEndpointService } from '../../core/shared/hal-endpoint.service';
 import {
-  configureRequest, filterSuccessfulResponses,
+  configureRequest,
+  filterSuccessfulResponses,
   getResponseFromEntry,
   getSucceededRemoteData
 } from '../../core/shared/operators';
 import { URLCombiner } from '../../core/url-combiner/url-combiner';
-import { hasValue, isEmpty, isNotEmpty } from '../../shared/empty.util';
+import { hasValue, isEmpty, isNotEmpty, isNotUndefined } from '../../shared/empty.util';
 import { NormalizedSearchResult } from '../normalized-search-result.model';
 import { SearchOptions } from '../search-options.model';
 import { SearchResult } from '../search-result.model';
@@ -47,6 +42,7 @@ import { CommunityDataService } from '../../core/data/community-data.service';
 import { ViewMode } from '../../core/shared/view-mode.model';
 import { ResourceType } from '../../core/shared/resource-type';
 import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service';
+import { RouteService } from '../../shared/services/route.service';
 
 /**
  * Service that performs all general actions that have to do with the search page
@@ -63,13 +59,23 @@ export class SearchService implements OnDestroy {
    */
   private facetLinkPathPrefix = 'discover/facets/';
 
+  /**
+   * The ResponseParsingService constructor name
+   */
+  private parser: GenericConstructor<ResponseParsingService> = SearchResponseParsingService;
+
+  /**
+   * The RestRequest constructor name
+   */
+  private request: GenericConstructor<RestRequest> = GetRequest;
+
   /**
    * Subscription to unsubscribe from
    */
   private sub;
 
   constructor(private router: Router,
-              private route: ActivatedRoute,
+              private routeService: RouteService,
               protected requestService: RequestService,
               private rdb: RemoteDataBuildService,
               private halService: HALEndpointService,
@@ -78,6 +84,20 @@ export class SearchService implements OnDestroy {
   ) {
   }
 
+  /**
+   * Method to set service options
+   * @param {GenericConstructor<ResponseParsingService>} parser The ResponseParsingService constructor name
+   * @param {boolean} request The RestRequest constructor name
+   */
+  setServiceOptions(parser: GenericConstructor<ResponseParsingService>, request: GenericConstructor<RestRequest>) {
+    if (parser) {
+      this.parser = parser;
+    }
+    if (request) {
+      this.request = request;
+    }
+  }
+
   /**
    * Method to retrieve a paginated list of search results from the server
    * @param {PaginatedSearchOptions} searchOptions The configuration necessary to perform this search
@@ -89,14 +109,17 @@ export class SearchService implements OnDestroy {
         if (hasValue(searchOptions)) {
           url = (searchOptions as PaginatedSearchOptions).toRestUrl(url);
         }
-        const request = new GetRequest(this.requestService.generateRequestId(), url);
+        const request = new this.request(this.requestService.generateRequestId(), url);
+
+        const getResponseParserFn: () => GenericConstructor<ResponseParsingService> = () => {
+          return this.parser;
+        };
+
         return Object.assign(request, {
-          getResponseParser(): GenericConstructor<ResponseParsingService> {
-            return SearchResponseParsingService;
-          }
+          getResponseParser: getResponseParserFn
         });
       }),
-      configureRequest(this.requestService)
+      configureRequest(this.requestService),
     );
     const requestEntryObs = requestObs.pipe(
       switchMap((request: RestRequest) => this.requestService.getByHref(request.href))
@@ -112,8 +135,10 @@ export class SearchService implements OnDestroy {
     // Turn list of observable remote data DSO's into observable remote data object with list of DSO
     const dsoObs: Observable<RemoteData<DSpaceObject[]>> = sqrObs.pipe(
       map((sqr: SearchQueryResponse) => {
-        return sqr.objects.map((nsr: NormalizedSearchResult) => {
-          return this.rdb.buildSingle(nsr.dspaceObject);
+        return sqr.objects
+          .filter((nsr: NormalizedSearchResult) => isNotUndefined(nsr.indexableObject))
+          .map((nsr: NormalizedSearchResult) => {
+          return this.rdb.buildSingle(nsr.indexableObject);
         })
       }),
       switchMap((input: Array<Observable<RemoteData<DSpaceObject>>>) => this.rdb.aggregate(input)),
@@ -126,9 +151,9 @@ export class SearchService implements OnDestroy {
           let co = DSpaceObject;
           if (dsos.payload[index]) {
             const constructor: GenericConstructor<ListableObject> = dsos.payload[index].constructor as GenericConstructor<ListableObject>;
-            co = getSearchResultFor(constructor);
+            co = getSearchResultFor(constructor, searchOptions.configuration);
             return Object.assign(new co(), object, {
-              dspaceObject: dsos.payload[index]
+              indexableObject: dsos.payload[index]
             });
           } else {
             return undefined;
@@ -154,9 +179,10 @@ export class SearchService implements OnDestroy {
   /**
    * Request the filter configuration for a given scope or the whole repository
    * @param {string} scope UUID of the object for which config the filter config is requested, when no scope is provided the configuration for the whole repository is loaded
+   * @param {string} configurationName the name of the configuration
    * @returns {Observable<RemoteData<SearchFilterConfig[]>>} The found filter configuration
    */
-  getConfig(scope?: string): Observable<RemoteData<SearchFilterConfig[]>> {
+  getConfig(scope?: string, configurationName?: string): Observable<RemoteData<SearchFilterConfig[]>> {
     const requestObs = this.halService.getEndpoint(this.facetLinkPathPrefix).pipe(
       map((url: string) => {
         const args: string[] = [];
@@ -165,11 +191,15 @@ export class SearchService implements OnDestroy {
           args.push(`scope=${scope}`);
         }
 
+        if (isNotEmpty(configurationName)) {
+          args.push(`configuration=${configurationName}`);
+        }
+
         if (isNotEmpty(args)) {
           url = new URLCombiner(url, `?${args.join('&')}`).toString();
         }
 
-        const request = new GetRequest(this.requestService.generateRequestId(), url);
+        const request = new this.request(this.requestService.generateRequestId(), url);
         return Object.assign(request, {
           getResponseParser(): GenericConstructor<ResponseParsingService> {
             return FacetConfigResponseParsingService;
@@ -212,14 +242,15 @@ export class SearchService implements OnDestroy {
           url = searchOptions.toRestUrl(url, args);
         }
 
-        const request = new GetRequest(this.requestService.generateRequestId(), url);
+        const request = new this.request(this.requestService.generateRequestId(), url);
         return Object.assign(request, {
           getResponseParser(): GenericConstructor<ResponseParsingService> {
             return FacetValueResponseParsingService;
           }
         });
       }),
-      configureRequest(this.requestService)
+      configureRequest(this.requestService),
+      first()
     );
 
     const requestEntryObs = requestObs.pipe(
@@ -288,9 +319,9 @@ export class SearchService implements OnDestroy {
    * @returns {Observable<ViewMode>} The current view mode
    */
   getViewMode(): Observable<ViewMode> {
-    return this.route.queryParams.pipe(map((params) => {
-      if (isNotEmpty(params.view) && hasValue(params.view)) {
-        return params.view;
+    return this.routeService.getQueryParamMap().pipe(map((params) => {
+      if (isNotEmpty(params.get('view')) && hasValue(params.get('view'))) {
+        return params.get('view');
       } else {
         return ViewMode.List;
       }
@@ -301,22 +332,29 @@ export class SearchService implements OnDestroy {
    * Changes the current view mode in the current URL
    * @param {ViewMode} viewMode Mode to switch to
    */
-  setViewMode(viewMode: ViewMode) {
-    const navigationExtras: NavigationExtras = {
-      queryParams: { view: viewMode },
-      queryParamsHandling: 'merge'
-    };
+  setViewMode(viewMode: ViewMode, searchLinkParts?: string[]) {
+    this.routeService.getQueryParameterValue('pageSize').pipe(first())
+      .subscribe((pageSize) => {
+        let queryParams = { view: viewMode, page: 1 };
+        if (viewMode === ViewMode.Detail) {
+          queryParams = Object.assign(queryParams, {pageSize: '1'});
+        } else if (pageSize === '1') {
+          queryParams = Object.assign(queryParams, {pageSize: '10'});
+        }
+        const navigationExtras: NavigationExtras = {
+          queryParams: queryParams,
+          queryParamsHandling: 'merge'
+        };
 
-    this.router.navigate([this.getSearchLink()], navigationExtras);
+        this.router.navigate(hasValue(searchLinkParts) ? searchLinkParts : [this.getSearchLink()], navigationExtras);
+      })
   }
 
   /**
    * @returns {string} The base path to the search page
    */
   getSearchLink(): string {
-    const urlTree = this.router.parseUrl(this.router.url);
-    const g: UrlSegmentGroup = urlTree.root.children[PRIMARY_OUTLET];
-    return '/' + g.toString();
+    return '/search';
   }
 
   /**
diff --git a/src/app/+search-page/search-settings/search-settings.component.spec.ts b/src/app/+search-page/search-settings/search-settings.component.spec.ts
index b1585c4347f3b385a67cf10f1478f0c63ae21c6b..b9b5c5a5ebd0a14a4ab35f989e45cce11fd03508 100644
--- a/src/app/+search-page/search-settings/search-settings.component.spec.ts
+++ b/src/app/+search-page/search-settings/search-settings.component.spec.ts
@@ -14,8 +14,8 @@ import { By } from '@angular/platform-browser';
 import { SearchFilterService } from '../search-filters/search-filter/search-filter.service';
 import { hot } from 'jasmine-marbles';
 import { VarDirective } from '../../shared/utils/var.directive';
-import { SearchConfigurationService } from '../search-service/search-configuration.service';
 import { first } from 'rxjs/operators';
+import { SEARCH_CONFIG_SERVICE } from '../../+my-dspace-page/my-dspace-page.component';
 
 describe('SearchSettingsComponent', () => {
 
@@ -73,7 +73,7 @@ describe('SearchSettingsComponent', () => {
           useValue: {}
         },
         {
-          provide: SearchConfigurationService,
+          provide: SEARCH_CONFIG_SERVICE,
           useValue: {
             paginatedSearchOptions: hot('a', {
               a: paginatedSearchOptions
diff --git a/src/app/+search-page/search-settings/search-settings.component.ts b/src/app/+search-page/search-settings/search-settings.component.ts
index 7fc5645fcc59ed427508dc2d84856e973010b9be..aac861c64f409ada56385e9297ec4902b6ee9a52 100644
--- a/src/app/+search-page/search-settings/search-settings.component.ts
+++ b/src/app/+search-page/search-settings/search-settings.component.ts
@@ -1,10 +1,11 @@
-import { Component, OnInit } from '@angular/core';
+import { Component, Inject, Input, OnInit } from '@angular/core';
 import { SearchService } from '../search-service/search.service';
 import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model';
 import { ActivatedRoute, NavigationExtras, Router } from '@angular/router';
 import { PaginatedSearchOptions } from '../paginated-search-options.model';
 import { Observable } from 'rxjs';
 import { SearchConfigurationService } from '../search-service/search-configuration.service';
+import { SEARCH_CONFIG_SERVICE } from '../../+my-dspace-page/my-dspace-page.component';
 
 @Component({
   selector: 'ds-search-settings',
@@ -17,6 +18,11 @@ import { SearchConfigurationService } from '../search-service/search-configurati
  */
 export class SearchSettingsComponent implements OnInit {
 
+  /**
+   * True when the search component should show results on the current page
+   */
+  @Input() inPlaceSearch;
+
   /**
    * The configuration for the current paginated search results
    */
@@ -30,7 +36,7 @@ export class SearchSettingsComponent implements OnInit {
   constructor(private service: SearchService,
               private route: ActivatedRoute,
               private router: Router,
-              private searchConfigurationService: SearchConfigurationService) {
+              @Inject(SEARCH_CONFIG_SERVICE) public searchConfigurationService: SearchConfigurationService) {
   }
 
   /**
@@ -53,7 +59,7 @@ export class SearchSettingsComponent implements OnInit {
       },
       queryParamsHandling: 'merge'
     };
-    this.router.navigate([ '/search' ], navigationExtras);
+    this.router.navigate(this.getSearchLinkParts(), navigationExtras);
   }
 
   /**
@@ -70,6 +76,26 @@ export class SearchSettingsComponent implements OnInit {
       },
       queryParamsHandling: 'merge'
     };
-    this.router.navigate([ '/search' ], navigationExtras);
+    this.router.navigate(this.getSearchLinkParts(), navigationExtras);
+  }
+
+  /**
+   * @returns {string} The base path to the search page, or the current page when inPlaceSearch is true
+   */
+  public getSearchLink(): string {
+    if (this.inPlaceSearch) {
+      return './';
+    }
+    return this.service.getSearchLink();
+  }
+
+  /**
+   * @returns {string[]} The base path to the search page, or the current page when inPlaceSearch is true, split in separate pieces
+   */
+  public getSearchLinkParts(): string[] {
+    if (this.service) {
+      return [];
+    }
+    return this.getSearchLink().split('/');
   }
 }
diff --git a/src/app/+search-page/search-sidebar/search-sidebar.component.html b/src/app/+search-page/search-sidebar/search-sidebar.component.html
index 5ff1e3c8faf90ab448df34e922e649d82b1c2ee6..50877052ec808f697e96bac46eaef56e4c68fd7f 100644
--- a/src/app/+search-page/search-sidebar/search-sidebar.component.html
+++ b/src/app/+search-page/search-sidebar/search-sidebar.component.html
@@ -8,10 +8,11 @@
         </button>
     </div>
     <div id="search-sidebar-content">
-        <ds-view-mode-switch class="d-none d-md-block"></ds-view-mode-switch>
+        <ds-view-mode-switch [viewModeList]="viewModeList" class="d-none d-md-block"></ds-view-mode-switch>
         <div class="sidebar-content">
-            <ds-search-filters></ds-search-filters>
-            <ds-search-settings></ds-search-settings>
+            <ds-search-switch-configuration *ngIf="configurationList" [configurationList]="configurationList"></ds-search-switch-configuration>
+            <ds-search-filters [inPlaceSearch]="inPlaceSearch"></ds-search-filters>
+            <ds-search-settings [inPlaceSearch]="inPlaceSearch"></ds-search-settings>
         </div>
     </div>
 </div>
diff --git a/src/app/+search-page/search-sidebar/search-sidebar.component.scss b/src/app/+search-page/search-sidebar/search-sidebar.component.scss
index b5bd6dd30d440cdc3ecdd389089cd329fa5e5f03..35ce5eebce06d2e00a011efae6d492ae660e7319 100644
--- a/src/app/+search-page/search-sidebar/search-sidebar.component.scss
+++ b/src/app/+search-page/search-sidebar/search-sidebar.component.scss
@@ -8,8 +8,12 @@
     ds-view-mode-switch {
         margin-bottom: $spacer;
     }
-    .sidebar-content > *:not(:last-child) {
+    .sidebar-content > *:not(:last-child):not(ds-search-switch-configuration) {
         margin-bottom: 4*$spacer;
         display: block;
     }
+    ds-search-switch-configuration {
+      margin-bottom: 2*$spacer;
+      display: block;
+    }
 }
diff --git a/src/app/+search-page/search-sidebar/search-sidebar.component.ts b/src/app/+search-page/search-sidebar/search-sidebar.component.ts
index 8b68cda793fe1c29475747eedebdb0c2bdaf55c0..9ee0a74942a5474a62e553a2046dbcfcd0712141 100644
--- a/src/app/+search-page/search-sidebar/search-sidebar.component.ts
+++ b/src/app/+search-page/search-sidebar/search-sidebar.component.ts
@@ -1,5 +1,7 @@
 import { Component, EventEmitter, Input, Output } from '@angular/core';
 
+import { SearchConfigurationOption } from '../search-switch-configuration/search-configuration-option.model';
+
 /**
  * This component renders a simple item page.
  * The route parameter 'id' is used to request the item it represents.
@@ -17,13 +19,29 @@ import { Component, EventEmitter, Input, Output } from '@angular/core';
  */
 export class SearchSidebarComponent {
 
+  /**
+   * The list of available configuration options
+   */
+  @Input() configurationList: SearchConfigurationOption[];
+
   /**
    * The total amount of results
    */
   @Input() resultCount;
 
+  /**
+   * The list of available view mode options
+   */
+  @Input() viewModeList;
+
+  /**
+   * True when the search component should show results on the current page
+   */
+  @Input() inPlaceSearch;
+
   /**
    * Emits event when the user clicks a button to open or close the sidebar
    */
   @Output() toggleSidebar = new EventEmitter<boolean>();
+
 }
diff --git a/src/app/+search-page/search-switch-configuration/search-configuration-option.model.ts b/src/app/+search-page/search-switch-configuration/search-configuration-option.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6f9a72da4856fcc3d319235281069bdd1780e217
--- /dev/null
+++ b/src/app/+search-page/search-switch-configuration/search-configuration-option.model.ts
@@ -0,0 +1,15 @@
+/**
+ * Represents a search configuration select option
+ */
+export interface SearchConfigurationOption {
+
+  /**
+   * The select option value
+   */
+  value: string;
+
+  /**
+   * The select option label
+   */
+  label: string;
+}
diff --git a/src/app/+search-page/search-switch-configuration/search-switch-configuration.component.html b/src/app/+search-page/search-switch-configuration/search-switch-configuration.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..8df37214d1d4b8c2ed1fed2ba7f5a7fec0427293
--- /dev/null
+++ b/src/app/+search-page/search-switch-configuration/search-switch-configuration.component.html
@@ -0,0 +1,13 @@
+<div *ngIf="configurationList?.length > 1" class="search-switch-configuration">
+  <h5>{{ 'search.switch-configuration.title' | translate}}</h5>
+
+  <select class="form-control"
+          [compareWith]="compare"
+          [(ngModel)]="selectedOption"
+          (change)="onSelect()">
+    <option *ngFor="let option of configurationList;" [ngValue]="option.value">
+      {{option.label | translate}}
+    </option>
+  </select>
+
+</div>
diff --git a/src/app/+search-page/search-switch-configuration/search-switch-configuration.component.scss b/src/app/+search-page/search-switch-configuration/search-switch-configuration.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/app/+search-page/search-switch-configuration/search-switch-configuration.component.spec.ts b/src/app/+search-page/search-switch-configuration/search-switch-configuration.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b3efc240e1232c4acb0ef7b61a6a5d62b7dc98fd
--- /dev/null
+++ b/src/app/+search-page/search-switch-configuration/search-switch-configuration.component.spec.ts
@@ -0,0 +1,109 @@
+import { NO_ERRORS_SCHEMA } from '@angular/core';
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { By } from '@angular/platform-browser';
+
+import { of as observableOf } from 'rxjs';
+import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
+
+import { SearchSwitchConfigurationComponent } from './search-switch-configuration.component';
+import { MYDSPACE_ROUTE, SEARCH_CONFIG_SERVICE } from '../../+my-dspace-page/my-dspace-page.component';
+import { SearchConfigurationServiceStub } from '../../shared/testing/search-configuration-service-stub';
+import { MockTranslateLoader } from '../../shared/mocks/mock-translate-loader';
+import { NavigationExtras, Router } from '@angular/router';
+import { RouterStub } from '../../shared/testing/router-stub';
+import { MyDSpaceConfigurationValueType } from '../../+my-dspace-page/my-dspace-configuration-value-type';
+import { SearchService } from '../search-service/search.service';
+
+describe('SearchSwitchConfigurationComponent', () => {
+
+  let comp: SearchSwitchConfigurationComponent;
+  let fixture: ComponentFixture<SearchSwitchConfigurationComponent>;
+  let searchConfService: SearchConfigurationServiceStub;
+  let select: any;
+
+  const searchServiceStub = jasmine.createSpyObj('SearchService', {
+    getSearchLink:  jasmine.createSpy('getSearchLink')
+  });
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [
+        TranslateModule.forRoot({
+          loader: {
+            provide: TranslateLoader,
+            useClass: MockTranslateLoader
+          }
+        })
+      ],
+      declarations: [ SearchSwitchConfigurationComponent ],
+      providers: [
+        { provide: Router, useValue: new RouterStub() },
+        { provide: SearchService, useValue: searchServiceStub },
+        { provide: SEARCH_CONFIG_SERVICE, useValue: new SearchConfigurationServiceStub() },
+      ],
+      schemas: [ NO_ERRORS_SCHEMA ]
+    }).compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(SearchSwitchConfigurationComponent);
+    comp = fixture.componentInstance;
+    searchConfService = TestBed.get(SEARCH_CONFIG_SERVICE);
+
+    spyOn(searchConfService, 'getCurrentConfiguration').and.returnValue(observableOf(MyDSpaceConfigurationValueType.Workspace));
+
+    comp.configurationList = [
+      {
+        value: MyDSpaceConfigurationValueType.Workspace,
+        label: 'workspace'
+      },
+      {
+        value: MyDSpaceConfigurationValueType.Workflow,
+        label: 'workflow'
+      },
+    ];
+
+    // SearchSwitchConfigurationComponent test instance
+    fixture.detectChanges();
+
+  });
+
+  it('should init the current configuration name', () => {
+    expect(comp.selectedOption).toBe(MyDSpaceConfigurationValueType.Workspace);
+  });
+
+  it('should display select field properly', () => {
+    const selectField = fixture.debugElement.query(By.css('.form-control'));
+    expect(selectField).toBeDefined();
+
+    const childElements = selectField.children;
+    expect(childElements.length).toEqual(comp.configurationList.length);
+  });
+
+  it('should call onSelect method when selecting an option', () => {
+    fixture.whenStable().then(() => {
+      spyOn(comp, 'onSelect');
+      select = fixture.debugElement.query(By.css('select'));
+      const selectEl = select.nativeElement;
+      selectEl.value = selectEl.options[1].value;  // <-- select a new value
+      selectEl.dispatchEvent(new Event('change'));
+      fixture.detectChanges();
+      expect(comp.onSelect).toHaveBeenCalled();
+    });
+
+  });
+
+  it('should navigate to the route when selecting an option', () => {
+    (comp as any).searchService.getSearchLink.and.returnValue(MYDSPACE_ROUTE);
+    comp.selectedOption = MyDSpaceConfigurationValueType.Workflow;
+    const navigationExtras: NavigationExtras = {
+      queryParams: {configuration: MyDSpaceConfigurationValueType.Workflow},
+    };
+
+    fixture.detectChanges();
+
+    comp.onSelect();
+
+    expect((comp as any).router.navigate).toHaveBeenCalledWith([MYDSPACE_ROUTE], navigationExtras);
+  });
+});
diff --git a/src/app/+search-page/search-switch-configuration/search-switch-configuration.component.ts b/src/app/+search-page/search-switch-configuration/search-switch-configuration.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c34fe2030358b431f78c90eaf5c8b8c0ac1f5ee2
--- /dev/null
+++ b/src/app/+search-page/search-switch-configuration/search-switch-configuration.component.ts
@@ -0,0 +1,80 @@
+import { Component, Inject, Input, OnDestroy, OnInit } from '@angular/core';
+import { NavigationExtras, Router } from '@angular/router';
+
+import { Subscription } from 'rxjs';
+
+import { hasValue } from '../../shared/empty.util';
+import { SEARCH_CONFIG_SERVICE } from '../../+my-dspace-page/my-dspace-page.component';
+import { SearchConfigurationService } from '../search-service/search-configuration.service';
+import { MyDSpaceConfigurationValueType } from '../../+my-dspace-page/my-dspace-configuration-value-type';
+import { SearchConfigurationOption } from './search-configuration-option.model';
+import { SearchService } from '../search-service/search.service';
+
+@Component({
+  selector: 'ds-search-switch-configuration',
+  styleUrls: ['./search-switch-configuration.component.scss'],
+  templateUrl: './search-switch-configuration.component.html',
+})
+/**
+ * Represents a select that allow to switch over available search configurations
+ */
+export class SearchSwitchConfigurationComponent implements OnDestroy, OnInit {
+
+  /**
+   * The list of available configuration options
+   */
+  @Input() configurationList: SearchConfigurationOption[] = [];
+
+  /**
+   * The selected option
+   */
+  public selectedOption: string;
+
+  /**
+   * Subscription to unsubscribe from
+   */
+  private sub: Subscription;
+
+  constructor(private router: Router,
+              private searchService: SearchService,
+              @Inject(SEARCH_CONFIG_SERVICE) private searchConfigService: SearchConfigurationService) {
+  }
+
+  /**
+   * Init current configuration
+   */
+  ngOnInit() {
+    this.searchConfigService.getCurrentConfiguration('default')
+      .subscribe((currentConfiguration) => this.selectedOption = currentConfiguration);
+  }
+
+  /**
+   * Init current configuration
+   */
+  onSelect() {
+    const navigationExtras: NavigationExtras = {
+      queryParams: {configuration: this.selectedOption},
+    };
+
+    this.router.navigate([this.searchService.getSearchLink()], navigationExtras);
+  }
+
+  /**
+   * Define the select 'compareWith' method to tell Angular how to compare the values
+   *
+   * @param item1
+   * @param item2
+   */
+  compare(item1: MyDSpaceConfigurationValueType, item2: MyDSpaceConfigurationValueType) {
+    return item1 === item2;
+  }
+
+  /**
+   * Make sure the subscription is unsubscribed from when this component is destroyed
+   */
+  ngOnDestroy() {
+    if (hasValue(this.sub)) {
+      this.sub.unsubscribe();
+    }
+  }
+}
diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts
index 266839f64babbe2c466ed024e87410ddee0579dc..86364aca895d831432b60da2d21137da9e20180a 100644
--- a/src/app/app-routing.module.ts
+++ b/src/app/app-routing.module.ts
@@ -24,6 +24,7 @@ export function getCommunityModulePath() {
       { path: COMMUNITY_MODULE_PATH, loadChildren: './+community-page/community-page.module#CommunityPageModule' },
       { path: COLLECTION_MODULE_PATH, loadChildren: './+collection-page/collection-page.module#CollectionPageModule' },
       { path: ITEM_MODULE_PATH, loadChildren: './+item-page/item-page.module#ItemPageModule' },
+      { path: 'mydspace', loadChildren: './+my-dspace-page/my-dspace-page.module#MyDSpacePageModule', canActivate: [AuthenticatedGuard] },
       { path: 'search', loadChildren: './+search-page/search-page.module#SearchPageModule' },
       { path: 'browse', loadChildren: './+browse-by/browse-by.module#BrowseByModule' },
       { path: 'admin', loadChildren: './+admin/admin.module#AdminModule', canActivate: [AuthenticatedGuard] },
diff --git a/src/app/core/auth/auth-request.service.ts b/src/app/core/auth/auth-request.service.ts
index 14784d1b55f496dd181e1e9b4dec9301ea278c6d..cbabe5c3fd406063b07365bc761d1c35781ffb23 100644
--- a/src/app/core/auth/auth-request.service.ts
+++ b/src/app/core/auth/auth-request.service.ts
@@ -6,18 +6,10 @@ import { RequestService } from '../data/request.service';
 import { GLOBAL_CONFIG } from '../../../config';
 import { GlobalConfig } from '../../../config/global-config.interface';
 import { isNotEmpty } from '../../shared/empty.util';
-import {
-  AuthGetRequest,
-  AuthPostRequest,
-  GetRequest,
-  PostRequest,
-  RestRequest
-} from '../data/request.models';
+import { AuthGetRequest, AuthPostRequest, GetRequest, PostRequest, RestRequest } from '../data/request.models';
 import { AuthStatusResponse, ErrorResponse } from '../cache/response.models';
 import { HttpOptions } from '../dspace-rest-v2/dspace-rest-v2.service';
-import { RequestEntry } from '../data/request.reducer';
 import { getResponseFromEntry } from '../shared/operators';
-import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
 
 @Injectable()
 export class AuthRequestService {
diff --git a/src/app/core/auth/auth.service.ts b/src/app/core/auth/auth.service.ts
index 7874eebea892b581f6f7ca364c0b5cf184852a0e..a01768e687cd7719b53faf7885a313903085b293 100644
--- a/src/app/core/auth/auth.service.ts
+++ b/src/app/core/auth/auth.service.ts
@@ -1,43 +1,27 @@
-import {Observable, of, of as observableOf} from 'rxjs';
-import {
-  distinctUntilChanged,
-  filter,
-  first,
-  map,
-  startWith,
-  switchMap,
-  take,
-  withLatestFrom
-} from 'rxjs/operators';
 import { Inject, Injectable, Optional } from '@angular/core';
 import { PRIMARY_OUTLET, Router, UrlSegmentGroup, UrlTree } from '@angular/router';
 import { HttpHeaders } from '@angular/common/http';
 import { REQUEST, RESPONSE } from '@nguniversal/express-engine/tokens';
 
+import { Observable, of as observableOf } from 'rxjs';
+import { distinctUntilChanged, filter, map, startWith, switchMap, take, withLatestFrom } from 'rxjs/operators';
 import { RouterReducerState } from '@ngrx/router-store';
 import { select, Store } from '@ngrx/store';
 import { CookieAttributes } from 'js-cookie';
 
 import { EPerson } from '../eperson/models/eperson.model';
 import { AuthRequestService } from './auth-request.service';
-
 import { HttpOptions } from '../dspace-rest-v2/dspace-rest-v2.service';
 import { AuthStatus } from './models/auth-status.model';
 import { AuthTokenInfo, TOKENITEM } from './models/auth-token-info.model';
 import { isEmpty, isNotEmpty, isNotNull, isNotUndefined } from '../../shared/empty.util';
 import { CookieService } from '../../shared/services/cookie.service';
-import {
-  getAuthenticationToken,
-  getRedirectUrl,
-  isAuthenticated,
-  isTokenRefreshing
-} from './selectors';
+import { getAuthenticationToken, getRedirectUrl, isAuthenticated, isTokenRefreshing } from './selectors';
 import { AppState, routerStateSelector } from '../../app.reducer';
 import { ResetAuthenticationMessagesAction, SetRedirectUrlAction } from './auth.actions';
 import { NativeWindowRef, NativeWindowService } from '../../shared/services/window.service';
 import { Base64EncodeUrl } from '../../shared/utils/encode-decode.util';
 import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
-import { NormalizedEPerson } from '../eperson/models/normalized-eperson.model';
 
 export const LOGIN_ROUTE = '/login';
 export const LOGOUT_ROUTE = '/logout';
diff --git a/src/app/core/auth/selectors.ts b/src/app/core/auth/selectors.ts
index fa637981ae585d30e3aadd262e1dd80c3f04b0f8..8c88e0fce55c98278971436baa28ec678472b0d6 100644
--- a/src/app/core/auth/selectors.ts
+++ b/src/app/core/auth/selectors.ts
@@ -8,6 +8,7 @@ import { createSelector } from '@ngrx/store';
  */
 import { AuthState } from './auth.reducer';
 import { AppState } from '../../app.reducer';
+import { EPerson } from '../eperson/models/eperson.model';
 
 /**
  * Returns the user state.
@@ -35,11 +36,12 @@ const _isAuthenticatedLoaded = (state: AuthState) => state.loaded;
 
 /**
  * Return the users state
+ * NOTE: when state is REHYDRATED user object lose prototype so return always a new EPerson object
  * @function _getAuthenticatedUser
  * @param {State} state
- * @returns {User}
+ * @returns {EPerson}
  */
-const _getAuthenticatedUser = (state: AuthState) => state.user;
+const _getAuthenticatedUser = (state: AuthState) => Object.assign(new EPerson(), state.user);
 
 /**
  * Returns the authentication error.
diff --git a/src/app/core/cache/builders/remote-data-build.service.ts b/src/app/core/cache/builders/remote-data-build.service.ts
index 6b8034bafd1996a0505206456b501a46dbe61949..8c706d46cdd1de5cecd695564f4b65552a04b7c8 100644
--- a/src/app/core/cache/builders/remote-data-build.service.ts
+++ b/src/app/core/cache/builders/remote-data-build.service.ts
@@ -1,7 +1,7 @@
 import { Injectable } from '@angular/core';
 
 import { combineLatest as observableCombineLatest, Observable, of as observableOf, race as observableRace } from 'rxjs';
-import { distinctUntilChanged, flatMap, map, startWith, switchMap } from 'rxjs/operators';
+import { distinctUntilChanged, flatMap, map, startWith, switchMap, tap } from 'rxjs/operators';
 
 import { hasValue, hasValueOperator, isEmpty, isNotEmpty, isNotUndefined } from '../../../shared/empty.util';
 import { PaginatedList } from '../../data/paginated-list';
@@ -205,17 +205,28 @@ export class RemoteDataBuildService {
 
     return observableCombineLatest(...input).pipe(
       map((arr) => {
+        // The request of an aggregate RD should be pending if at least one
+        // of the RDs it's based on is still in the state RequestPending
         const requestPending: boolean = arr
           .map((d: RemoteData<T>) => d.isRequestPending)
-          .every((b: boolean) => b === true);
+          .find((b: boolean) => b === true);
 
-        const responsePending: boolean = arr
+        // The response of an aggregate RD should be pending if no requests
+        // are still pending and at least one of the RDs it's based
+        // on is still in the state ResponsePending
+        const responsePending: boolean = !requestPending && arr
           .map((d: RemoteData<T>) => d.isResponsePending)
-          .every((b: boolean) => b === true);
+          .find((b: boolean) => b === true);
 
-        const isSuccessful: boolean = arr
-          .map((d: RemoteData<T>) => d.hasSucceeded)
-          .every((b: boolean) => b === true);
+        let isSuccessful: boolean;
+        // isSuccessful should be undefined until all responses have come in.
+        // We can't know its state beforehand. We also can't say it's false
+        // because that would imply a request failed.
+        if (!(requestPending || responsePending)) {
+          isSuccessful = arr
+            .map((d: RemoteData<T>) => d.hasSucceeded)
+            .every((b: boolean) => b === true);
+        }
 
         const errorMessage: string = arr
           .map((d: RemoteData<T>) => d.error)
diff --git a/src/app/core/cache/models/items/normalized-item-type.model.ts b/src/app/core/cache/models/items/normalized-item-type.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ed38d80a4baf8332743baa8e797119d968cfd254
--- /dev/null
+++ b/src/app/core/cache/models/items/normalized-item-type.model.ts
@@ -0,0 +1,32 @@
+import { autoserialize, autoserializeAs, inheritSerialization } from 'cerialize';
+import { ItemType } from '../../../shared/item-relationships/item-type.model';
+import { ResourceType } from '../../../shared/resource-type';
+import { mapsTo } from '../../builders/build-decorators';
+import { NormalizedObject } from '../normalized-object.model';
+import { IDToUUIDSerializer } from '../../id-to-uuid-serializer';
+
+/**
+ * Normalized model class for a DSpace ItemType
+ */
+@mapsTo(ItemType)
+@inheritSerialization(NormalizedObject)
+export class NormalizedItemType extends NormalizedObject<ItemType> {
+
+  /**
+   * The label that describes the ResourceType of the Item
+   */
+  @autoserialize
+  label: string;
+
+  /**
+   * The identifier of this ItemType
+   */
+  @autoserialize
+  id: string;
+
+  /**
+   * The universally unique identifier of this ItemType
+   */
+  @autoserializeAs(new IDToUUIDSerializer(ResourceType.ItemType), 'id')
+  uuid: string;
+}
diff --git a/src/app/core/cache/models/items/normalized-relationship-type.model.ts b/src/app/core/cache/models/items/normalized-relationship-type.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d201fb27462d861db0eee1ec793c2b6ff27ed6dc
--- /dev/null
+++ b/src/app/core/cache/models/items/normalized-relationship-type.model.ts
@@ -0,0 +1,77 @@
+import { autoserialize, autoserializeAs, inheritSerialization } from 'cerialize';
+import { RelationshipType } from '../../../shared/item-relationships/relationship-type.model';
+import { ResourceType } from '../../../shared/resource-type';
+import { mapsTo, relationship } from '../../builders/build-decorators';
+import { NormalizedDSpaceObject } from '../normalized-dspace-object.model';
+import { NormalizedObject } from '../normalized-object.model';
+import { IDToUUIDSerializer } from '../../id-to-uuid-serializer';
+
+/**
+ * Normalized model class for a DSpace RelationshipType
+ */
+@mapsTo(RelationshipType)
+@inheritSerialization(NormalizedDSpaceObject)
+export class NormalizedRelationshipType extends NormalizedObject<RelationshipType> {
+
+  /**
+   * The identifier of this RelationshipType
+   */
+  @autoserialize
+  id: string;
+
+  /**
+   * The label that describes the Relation to the left of this RelationshipType
+   */
+  @autoserialize
+  leftLabel: string;
+
+  /**
+   * The maximum amount of Relationships allowed to the left of this RelationshipType
+   */
+  @autoserialize
+  leftMaxCardinality: number;
+
+  /**
+   * The minimum amount of Relationships allowed to the left of this RelationshipType
+   */
+  @autoserialize
+  leftMinCardinality: number;
+
+  /**
+   * The label that describes the Relation to the right of this RelationshipType
+   */
+  @autoserialize
+  rightLabel: string;
+
+  /**
+   * The maximum amount of Relationships allowed to the right of this RelationshipType
+   */
+  @autoserialize
+  rightMaxCardinality: number;
+
+  /**
+   * The minimum amount of Relationships allowed to the right of this RelationshipType
+   */
+  @autoserialize
+  rightMinCardinality: number;
+
+  /**
+   * The type of Item found to the left of this RelationshipType
+   */
+  @autoserialize
+  @relationship(ResourceType.ItemType, false)
+  leftType: string;
+
+  /**
+   * The type of Item found to the right of this RelationshipType
+   */
+  @autoserialize
+  @relationship(ResourceType.ItemType, false)
+  rightType: string;
+
+  /**
+   * The universally unique identifier of this RelationshipType
+   */
+  @autoserializeAs(new IDToUUIDSerializer(ResourceType.RelationshipType), 'id')
+  uuid: string;
+}
diff --git a/src/app/core/cache/models/items/normalized-relationship.model.ts b/src/app/core/cache/models/items/normalized-relationship.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b9084263612b0805bbf1f3763e8dd0f62f7e9968
--- /dev/null
+++ b/src/app/core/cache/models/items/normalized-relationship.model.ts
@@ -0,0 +1,57 @@
+import { autoserialize, autoserializeAs, inheritSerialization } from 'cerialize';
+import { Relationship } from '../../../shared/item-relationships/relationship.model';
+import { ResourceType } from '../../../shared/resource-type';
+import { mapsTo, relationship } from '../../builders/build-decorators';
+import { NormalizedObject } from '../normalized-object.model';
+import { IDToUUIDSerializer } from '../../id-to-uuid-serializer';
+
+/**
+ * Normalized model class for a DSpace Relationship
+ */
+@mapsTo(Relationship)
+@inheritSerialization(NormalizedObject)
+export class NormalizedRelationship extends NormalizedObject<Relationship> {
+
+  /**
+   * The identifier of this Relationship
+   */
+  @autoserialize
+  id: string;
+
+  /**
+   * The identifier of the Item to the left side of this Relationship
+   */
+  @autoserialize
+  leftId: string;
+
+  /**
+   * The identifier of the Item to the right side of this Relationship
+   */
+  @autoserialize
+  rightId: string;
+
+  /**
+   * The place of the Item to the left side of this Relationship
+   */
+  @autoserialize
+  leftPlace: number;
+
+  /**
+   * The place of the Item to the right side of this Relationship
+   */
+  @autoserialize
+  rightPlace: number;
+
+  /**
+   * The type of Relationship
+   */
+  @autoserialize
+  @relationship(ResourceType.RelationshipType, false)
+  relationshipType: string;
+
+  /**
+   * The universally unique identifier of this Relationship
+   */
+  @autoserializeAs(new IDToUUIDSerializer(ResourceType.Relationship), 'id')
+  uuid: string;
+}
diff --git a/src/app/core/cache/models/normalized-item.model.ts b/src/app/core/cache/models/normalized-item.model.ts
index c5be6d8e2eb11876c223b5dbc30b73837799fa19..0ce2874f0bc3307c99e26e874caf1e91aad341d5 100644
--- a/src/app/core/cache/models/normalized-item.model.ts
+++ b/src/app/core/cache/models/normalized-item.model.ts
@@ -65,4 +65,8 @@ export class NormalizedItem extends NormalizedDSpaceObject<Item> {
   @relationship(ResourceType.Bitstream, true)
   bitstreams: string[];
 
+  @autoserialize
+  @relationship(ResourceType.Relationship, true)
+  relationships: string[];
+
 }
diff --git a/src/app/core/cache/object-cache.service.spec.ts b/src/app/core/cache/object-cache.service.spec.ts
index eae7c06be7e20ba528eff53f8dc5b457ae8e25bd..20e12108ad91bf394da0393d2e29caa856272739 100644
--- a/src/app/core/cache/object-cache.service.spec.ts
+++ b/src/app/core/cache/object-cache.service.spec.ts
@@ -1,18 +1,19 @@
+import * as ngrx from '@ngrx/store';
 import { Store } from '@ngrx/store';
 import { of as observableOf } from 'rxjs';
 
 import { ObjectCacheService } from './object-cache.service';
 import {
   AddPatchObjectCacheAction,
-  AddToObjectCacheAction, ApplyPatchObjectCacheAction,
+  AddToObjectCacheAction,
+  ApplyPatchObjectCacheAction,
   RemoveFromObjectCacheAction
 } from './object-cache.actions';
 import { CoreState } from '../core.reducers';
 import { ResourceType } from '../shared/resource-type';
 import { NormalizedItem } from './models/normalized-item.model';
 import { first } from 'rxjs/operators';
-import * as ngrx from '@ngrx/store';
-import { Operation } from '../../../../node_modules/fast-json-patch';
+import { Operation } from 'fast-json-patch';
 import { RestRequestMethod } from '../data/rest-request-method';
 import { AddToSSBAction } from './server-sync-buffer.actions';
 import { Patch } from './object-cache.reducer';
diff --git a/src/app/core/cache/response.models.ts b/src/app/core/cache/response.models.ts
index 753f9dab1ea17763252c04560f5bcf86e06d70d0..2f652d91a2114645a418e81f53e8ab42859950d5 100644
--- a/src/app/core/cache/response.models.ts
+++ b/src/app/core/cache/response.models.ts
@@ -8,6 +8,8 @@ import { IntegrationModel } from '../integration/models/integration.model';
 import { RegistryMetadataschemasResponse } from '../registry/registry-metadataschemas-response.model';
 import { RegistryMetadatafieldsResponse } from '../registry/registry-metadatafields-response.model';
 import { RegistryBitstreamformatsResponse } from '../registry/registry-bitstreamformats-response.model';
+import { MetadataSchema } from '../metadata/metadataschema.model';
+import { MetadataField } from '../metadata/metadatafield.model';
 import { PaginatedList } from '../data/paginated-list';
 import { SubmissionObject } from '../submission/models/submission-object.model';
 import { DSpaceObject } from '../shared/dspace-object.model';
@@ -254,4 +256,38 @@ export class EpersonSuccessResponse extends RestResponse {
   }
 }
 
+export class MessageResponse extends RestResponse {
+  public toCache = false;
+
+  constructor(
+    public statusCode: number,
+    public statusText: string,
+    public pageInfo?: PageInfo
+  ) {
+    super(true, statusCode, statusText);
+  }
+}
+
+export class TaskResponse extends RestResponse {
+  public toCache = false;
+
+  constructor(
+    public statusCode: number,
+    public statusText: string,
+    public pageInfo?: PageInfo
+  ) {
+    super(true, statusCode, statusText);
+  }
+}
+
+export class FilteredDiscoveryQueryResponse extends RestResponse {
+  constructor(
+    public filterQuery: string,
+    public statusCode: number,
+    public statusText: string,
+    public pageInfo?: PageInfo
+  ) {
+    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 ef6f508226bfb7301f47eb1847abfb8032024096..d4bda00a349be5bd656e6c89117bb74d366ed4a8 100644
--- a/src/app/core/core.module.ts
+++ b/src/app/core/core.module.ts
@@ -71,6 +71,7 @@ import { SubmissionRestService } from './submission/submission-rest.service';
 import { BrowseItemsResponseParsingService } from './data/browse-items-response-parsing-service';
 import { DSpaceObjectDataService } from './data/dspace-object-data.service';
 import { MetadataschemaParsingService } from './data/metadataschema-parsing.service';
+import { FilteredDiscoveryPageResponseParsingService } from './data/filtered-discovery-page-response-parsing.service';
 import { CSSVariableService } from '../shared/sass-helper/sass-helper.service';
 import { MenuService } from '../shared/menu/menu.service';
 import { SubmissionJsonPatchOperationsService } from './submission/submission-json-patch-operations.service';
@@ -100,6 +101,12 @@ import { NormalizedSubmissionSectionModel } from './config/models/normalized-con
 import { NormalizedAuthStatus } from './auth/models/normalized-auth-status.model';
 import { NormalizedAuthorityValue } from './integration/models/normalized-authority-value.model';
 import { BrowseEntry } from './shared/browse-entry.model';
+import { RoleService } from './roles/role.service';
+import { MyDSpaceGuard } from '../+my-dspace-page/my-dspace.guard';
+import { MyDSpaceResponseParsingService } from './data/mydspace-response-parsing.service';
+import { ClaimedTaskDataService } from './tasks/claimed-task-data.service';
+import { PoolTaskDataService } from './tasks/pool-task-data.service';
+import { TaskResponseParsingService } from './tasks/task-response-parsing.service';
 
 const IMPORTS = [
   CommonModule,
@@ -147,6 +154,7 @@ const PROVIDERS = [
   RegistryBitstreamformatsResponseParsingService,
   DebugResponseParsingService,
   SearchResponseParsingService,
+  MyDSpaceResponseParsingService,
   ServerResponseService,
   BrowseResponseParsingService,
   BrowseEntriesResponseParsingService,
@@ -178,6 +186,11 @@ const PROVIDERS = [
   MenuService,
   ObjectUpdatesService,
   SearchService,
+  MyDSpaceGuard,
+  RoleService,
+  TaskResponseParsingService,
+  ClaimedTaskDataService,
+  PoolTaskDataService,
   // register AuthInterceptor as HttpInterceptor
   {
     provide: HTTP_INTERCEPTORS,
@@ -185,7 +198,8 @@ const PROVIDERS = [
     multi: true
   },
   NotificationsService,
-  { provide: NativeWindowService, useFactory: NativeWindowFactory },
+  FilteredDiscoveryPageResponseParsingService,
+  { provide: NativeWindowService, useFactory: NativeWindowFactory }
 ];
 
 /**
diff --git a/src/app/core/data/base-response-parsing.service.ts b/src/app/core/data/base-response-parsing.service.ts
index 15f52e0939403ee3b7757f281e6805496a7feee5..cae59994b5992637f5bf9399b7cd5e460d30ffe2 100644
--- a/src/app/core/data/base-response-parsing.service.ts
+++ b/src/app/core/data/base-response-parsing.service.ts
@@ -6,8 +6,6 @@ import { ObjectCacheService } from '../cache/object-cache.service';
 import { GlobalConfig } from '../../../config/global-config.interface';
 import { GenericConstructor } from '../shared/generic-constructor';
 import { PaginatedList } from './paginated-list';
-import { ResourceType } from '../shared/resource-type';
-import { RESTURLCombiner } from '../url-combiner/rest-url-combiner';
 import { isRestDataObject, isRestPaginatedList } from '../cache/builders/normalized-object-build.service';
 import { getNormalizedConstructorByType } from '../shared/resource-type.decorator';
 /* tslint:disable:max-classes-per-file */
@@ -66,7 +64,9 @@ export abstract class BaseResponseParsingService {
     let list = data._embedded;
 
     // Workaround for inconsistency in rest response. Issue: https://github.com/DSpace/dspace-angular/issues/238
-    if (!Array.isArray(list)) {
+    if (hasNoValue(list)) {
+      list = [];
+    } else if (!Array.isArray(list)) {
       list = this.flattenSingleKeyObject(list);
     }
     const page: ObjectDomain[] = this.processArray(list, requestUUID);
@@ -140,4 +140,8 @@ export abstract class BaseResponseParsingService {
   protected retrieveObjectOrUrl(obj: any): any {
     return this.toCache ? obj.self : obj;
   }
+
+  protected isSuccessStatus(statusCode: number) {
+    return statusCode >= 200 && statusCode < 300;
+  }
 }
diff --git a/src/app/core/data/collection-data.service.ts b/src/app/core/data/collection-data.service.ts
index 3d03b9397df9c28d3d9f7598463569a0df472eef..993954a360ae6d4c265722e99229dc518f34b77e 100644
--- a/src/app/core/data/collection-data.service.ts
+++ b/src/app/core/data/collection-data.service.ts
@@ -1,7 +1,9 @@
 import { Injectable } from '@angular/core';
+
+import { filter, map, take } from 'rxjs/operators';
 import { Store } from '@ngrx/store';
+
 import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
-import { NormalizedCollection } from '../cache/models/normalized-collection.model';
 import { ObjectCacheService } from '../cache/object-cache.service';
 import { CoreState } from '../core.reducers';
 import { Collection } from '../shared/collection.model';
@@ -13,6 +15,10 @@ import { NotificationsService } from '../../shared/notifications/notifications.s
 import { HttpClient } 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 { FindAllOptions } from './request.models';
+import { RemoteData } from './remote-data';
+import { PaginatedList } from './paginated-list';
 
 @Injectable()
 export class CollectionDataService extends ComColDataService<Collection> {
@@ -34,4 +40,21 @@ export class CollectionDataService extends ComColDataService<Collection> {
     super();
   }
 
+  /**
+   * Find whether there is a collection whom user has authorization to submit to
+   *
+   * @return boolean
+   *    true if the user has at least one collection to submit to
+   */
+  hasAuthorizedCollection(): Observable<boolean> {
+    const searchHref = 'findAuthorized';
+    const options = new FindAllOptions();
+    options.elementsPerPage = 1;
+
+    return this.searchBy(searchHref, options).pipe(
+      filter((collections: RemoteData<PaginatedList<Collection>>) => !collections.isResponsePending),
+      take(1),
+      map((collections: RemoteData<PaginatedList<Collection>>) => collections.payload.totalElements > 0)
+    );
+  }
 }
diff --git a/src/app/core/data/community-data.service.ts b/src/app/core/data/community-data.service.ts
index 75ef58b06bb8c7c47a461813dd1cd3b64fbc52e7..8db4d762eb69fa104ff85d3a5e5839893d688aeb 100644
--- a/src/app/core/data/community-data.service.ts
+++ b/src/app/core/data/community-data.service.ts
@@ -1,9 +1,8 @@
-import { filter, mergeMap, take } from 'rxjs/operators';
+import { filter, take } from 'rxjs/operators';
 import { Injectable } from '@angular/core';
 
 import { Store } from '@ngrx/store';
 import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
-import { NormalizedCommunity } from '../cache/models/normalized-community.model';
 import { ObjectCacheService } from '../cache/object-cache.service';
 import { CoreState } from '../core.reducers';
 import { Community } from '../shared/community.model';
@@ -12,7 +11,7 @@ import { RequestService } from './request.service';
 import { HALEndpointService } from '../shared/hal-endpoint.service';
 import { FindAllOptions, FindAllRequest } from './request.models';
 import { RemoteData } from './remote-data';
-import { hasValue, isNotEmpty } from '../../shared/empty.util';
+import { hasValue } from '../../shared/empty.util';
 import { Observable } from 'rxjs';
 import { PaginatedList } from './paginated-list';
 import { NotificationsService } from '../../shared/notifications/notifications.service';
diff --git a/src/app/core/data/data.service.spec.ts b/src/app/core/data/data.service.spec.ts
index 4a244db24f2d666fd0553be25780ac8124febc41..dede6f8ae2975bc7dea177e7e45134dc578e0c98 100644
--- a/src/app/core/data/data.service.spec.ts
+++ b/src/app/core/data/data.service.spec.ts
@@ -9,13 +9,12 @@ import { Observable, of as observableOf } from 'rxjs';
 import { FindAllOptions } from './request.models';
 import { SortDirection, SortOptions } from '../cache/models/sort-options.model';
 import { ObjectCacheService } from '../cache/object-cache.service';
-import { Operation } from '../../../../node_modules/fast-json-patch';
+import { compare, Operation } from 'fast-json-patch';
 import { DSpaceObject } from '../shared/dspace-object.model';
 import { ChangeAnalyzer } from './change-analyzer';
 import { HttpClient } from '@angular/common/http';
 import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service';
 import { NotificationsService } from '../../shared/notifications/notifications.service';
-import { compare } from 'fast-json-patch';
 import { Item } from '../shared/item.model';
 
 const endpoint = 'https://rest.api/core';
diff --git a/src/app/core/data/default-change-analyzer.service.ts b/src/app/core/data/default-change-analyzer.service.ts
index 1fd207d2bffa0a16741bb049d8ed0876942eee72..cd30479f6df00ea59e537d967a3259fc638b1f24 100644
--- a/src/app/core/data/default-change-analyzer.service.ts
+++ b/src/app/core/data/default-change-analyzer.service.ts
@@ -1,9 +1,7 @@
 import { Operation } from 'fast-json-patch/lib/core';
 import { compare } from 'fast-json-patch';
 import { ChangeAnalyzer } from './change-analyzer';
-import { NormalizedDSpaceObject } from '../cache/models/normalized-dspace-object.model';
 import { Injectable } from '@angular/core';
-import { DSpaceObject } from '../shared/dspace-object.model';
 import { CacheableObject } from '../cache/object-cache.reducer';
 import { NormalizedObject } from '../cache/models/normalized-object.model';
 
diff --git a/src/app/core/data/filtered-discovery-page-response-parsing.service.spec.ts b/src/app/core/data/filtered-discovery-page-response-parsing.service.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d81ce4b6bd2bd335065c9071ae29a2fbf1dc0753
--- /dev/null
+++ b/src/app/core/data/filtered-discovery-page-response-parsing.service.spec.ts
@@ -0,0 +1,36 @@
+import { FilteredDiscoveryPageResponseParsingService } from './filtered-discovery-page-response-parsing.service';
+import { getMockObjectCacheService } from '../../shared/mocks/mock-object-cache.service';
+import { GenericConstructor } from '../shared/generic-constructor';
+import { ResponseParsingService } from './parsing.service';
+import { GetRequest } from './request.models';
+import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
+import { FilteredDiscoveryQueryResponse } from '../cache/response.models';
+
+describe('FilteredDiscoveryPageResponseParsingService', () => {
+  let service: FilteredDiscoveryPageResponseParsingService;
+
+  beforeEach(() => {
+    service = new FilteredDiscoveryPageResponseParsingService(undefined, getMockObjectCacheService());
+  });
+
+  describe('parse', () => {
+    const request = Object.assign(new GetRequest('client/f5b4ccb8-fbb0-4548-b558-f234d9fdfad6', 'https://rest.api/path'), {
+      getResponseParser(): GenericConstructor<ResponseParsingService> {
+        return FilteredDiscoveryPageResponseParsingService;
+      }
+    });
+
+    const mockResponse = {
+      payload: {
+        'discovery-query': 'query'
+      },
+      statusCode: 200,
+      statusText: 'OK'
+    } as DSpaceRESTV2Response;
+
+    it('should return a FilteredDiscoveryQueryResponse containing the correct query', () => {
+      const response = service.parse(request, mockResponse);
+      expect((response as FilteredDiscoveryQueryResponse).filterQuery).toBe(mockResponse.payload['discovery-query']);
+    })
+  });
+});
diff --git a/src/app/core/data/filtered-discovery-page-response-parsing.service.ts b/src/app/core/data/filtered-discovery-page-response-parsing.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..166a915b162fe6a173c95bd80636b6752fc80f5c
--- /dev/null
+++ b/src/app/core/data/filtered-discovery-page-response-parsing.service.ts
@@ -0,0 +1,35 @@
+import { Inject, 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 { BaseResponseParsingService } from './base-response-parsing.service';
+import { ObjectCacheService } from '../cache/object-cache.service';
+import { GlobalConfig } from '../../../config/global-config.interface';
+import { GLOBAL_CONFIG } from '../../../config';
+import { FilteredDiscoveryQueryResponse, RestResponse } from '../cache/response.models';
+
+/**
+ * A ResponseParsingService used to parse DSpaceRESTV2Response coming from the REST API to a discovery query (string)
+ * wrapped in a FilteredDiscoveryQueryResponse
+ */
+@Injectable()
+export class FilteredDiscoveryPageResponseParsingService extends BaseResponseParsingService implements ResponseParsingService {
+  objectFactory = {};
+  toCache = false;
+  constructor(
+    @Inject(GLOBAL_CONFIG) protected EnvConfig: GlobalConfig,
+    protected objectCache: ObjectCacheService,
+  ) { super();
+  }
+
+  /**
+   * Parses data from the REST API to a discovery query wrapped in a FilteredDiscoveryQueryResponse
+   * @param {RestRequest} request
+   * @param {DSpaceRESTV2Response} data
+   * @returns {RestResponse}
+   */
+  parse(request: RestRequest, data: DSpaceRESTV2Response): RestResponse {
+    const query = data.payload['discovery-query'];
+    return new FilteredDiscoveryQueryResponse(query, data.statusCode, data.statusText);
+  }
+}
diff --git a/src/app/core/data/mydspace-response-parsing.service.ts b/src/app/core/data/mydspace-response-parsing.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a6945e27b40aa2b8d711848e75ee720f2e992d75
--- /dev/null
+++ b/src/app/core/data/mydspace-response-parsing.service.ts
@@ -0,0 +1,82 @@
+import { Injectable } from '@angular/core';
+import { RestResponse, SearchSuccessResponse } from '../cache/response.models';
+import { DSOResponseParsingService } from './dso-response-parsing.service';
+import { ResponseParsingService } from './parsing.service';
+import { RestRequest } from './request.models';
+import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
+import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer';
+import { hasValue } from '../../shared/empty.util';
+import { SearchQueryResponse } from '../../+search-page/search-service/search-query-response.model';
+import { MetadataMap, MetadataValue } from '../shared/metadata.models';
+
+@Injectable()
+export class MyDSpaceResponseParsingService implements ResponseParsingService {
+  constructor(private dsoParser: DSOResponseParsingService) {
+  }
+
+  parse(request: RestRequest, data: DSpaceRESTV2Response): RestResponse {
+    // fallback for unexpected empty response
+    const emptyPayload = {
+      _embedded: {
+        objects: []
+      }
+    };
+    const payload = data.payload._embedded.searchResult || emptyPayload;
+    const hitHighlights: MetadataMap[] = payload._embedded.objects
+      .map((object) => object.hitHighlights)
+      .map((hhObject) => {
+        const mdMap: MetadataMap = {};
+        if (hhObject) {
+          for (const key of Object.keys(hhObject)) {
+            const value: MetadataValue = Object.assign(new MetadataValue(), {
+              value: hhObject[key].join('...'),
+              language: null
+            });
+            mdMap[key] = [value];
+          }
+        }
+        return mdMap;
+      });
+
+    const dsoSelfLinks = payload._embedded.objects
+      .filter((object) => hasValue(object._embedded))
+      .map((object) => object._embedded.indexableObject)
+      .map((dso) => this.dsoParser.parse(request, {
+        payload: dso,
+        statusCode: data.statusCode,
+        statusText: data.statusText
+      }))
+      .map((obj) => obj.resourceSelfLinks)
+      .reduce((combined, thisElement) => [...combined, ...thisElement], []);
+
+    const objects = payload._embedded.objects
+      .filter((object) => hasValue(object._embedded))
+      .map((object, index) => Object.assign({}, object, {
+        indexableObject: dsoSelfLinks[index],
+        hitHighlights: hitHighlights[index],
+        _embedded: this.filterEmbeddedObjects(object)
+      }));
+    payload.objects = objects;
+    const deserialized = new DSpaceRESTv2Serializer(SearchQueryResponse).deserialize(payload);
+    return new SearchSuccessResponse(deserialized, data.statusCode, data.statusText, this.dsoParser.processPageInfo(payload));
+  }
+
+  protected filterEmbeddedObjects(object) {
+    const allowedEmbeddedKeys = ['submitter', 'item', 'workspaceitem', 'workflowitem'];
+    if (object._embedded.indexableObject && object._embedded.indexableObject._embedded) {
+      return Object.assign({}, object._embedded, {
+        indexableObject: Object.assign({}, object._embedded.indexableObject, {
+          _embedded: Object.keys(object._embedded.indexableObject._embedded)
+            .filter((key) => allowedEmbeddedKeys.includes(key))
+            .reduce((obj, key) => {
+              obj[key] = object._embedded.indexableObject._embedded[key];
+              return obj;
+            }, {})
+        })
+      });
+    } else {
+      return object;
+    }
+
+  }
+}
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 a13fb9487b2020a3bca756180323f8696751230b..22d5fd3e774a3047827e285ceb0053049cf356ce 100644
--- a/src/app/core/data/object-updates/object-updates.service.ts
+++ b/src/app/core/data/object-updates/object-updates.service.ts
@@ -5,7 +5,8 @@ import { coreSelector } from '../../core.selectors';
 import {
   FieldState,
   FieldUpdates,
-  Identifiable, OBJECT_UPDATES_TRASH_PATH,
+  Identifiable,
+  OBJECT_UPDATES_TRASH_PATH,
   ObjectUpdatesEntry,
   ObjectUpdatesState
 } from './object-updates.reducer';
@@ -17,9 +18,10 @@ import {
   InitializeFieldsAction,
   ReinstateObjectUpdatesAction,
   RemoveFieldUpdateAction,
-  SetEditableFieldUpdateAction, SetValidFieldUpdateAction
+  SetEditableFieldUpdateAction,
+  SetValidFieldUpdateAction
 } from './object-updates.actions';
-import { distinctUntilChanged, filter, map, tap } from 'rxjs/operators';
+import { distinctUntilChanged, filter, map } from 'rxjs/operators';
 import { hasNoValue, hasValue, isEmpty, isNotEmpty } from '../../../shared/empty.util';
 import { INotification } from '../../../shared/notifications/models/notification.model';
 
@@ -212,6 +214,7 @@ export class ObjectUpdatesService {
   /**
    * Method to dispatch an RemoveFieldUpdateAction to the store
    * @param url The page's URL for which the changes should be removed
+   * @param uuid The UUID of the field that should be set
    */
   removeSingleFieldUpdate(url: string, uuid) {
     this.store.dispatch(new RemoveFieldUpdateAction(url, uuid));
diff --git a/src/app/core/data/paginated-list.ts b/src/app/core/data/paginated-list.ts
index faecd231bc99f1e112ec2c6b0a91a3e6f9011d1a..e1c1b22569b752593030e4f28a45f25f6323ac7a 100644
--- a/src/app/core/data/paginated-list.ts
+++ b/src/app/core/data/paginated-list.ts
@@ -1,5 +1,5 @@
 import { PageInfo } from '../shared/page-info.model';
-import { hasNoValue, hasValue } from '../../shared/empty.util';
+import { hasValue } from '../../shared/empty.util';
 
 export class PaginatedList<T> {
 
@@ -11,7 +11,7 @@ export class PaginatedList<T> {
     if (hasValue(this.pageInfo) && hasValue(this.pageInfo.elementsPerPage)) {
       return this.pageInfo.elementsPerPage;
     }
-    return this.page.length;
+    return this.getPageLength();
   }
 
   set elementsPerPage(value: number) {
@@ -22,10 +22,7 @@ export class PaginatedList<T> {
     if (hasValue(this.pageInfo) && hasValue(this.pageInfo.totalElements)) {
       return this.pageInfo.totalElements;
     }
-    if (hasNoValue(this.page)) {
-      return 0;
-    }
-    return this.page.length;
+    return this.getPageLength();
   }
 
   set totalElements(value: number) {
@@ -92,4 +89,8 @@ export class PaginatedList<T> {
   set self(self: string) {
     this.pageInfo.self = self;
   }
+
+  protected getPageLength() {
+    return (Array.isArray(this.page)) ? this.page.length : 0;
+  }
 }
diff --git a/src/app/core/data/request.models.ts b/src/app/core/data/request.models.ts
index d2cdd45a0a66767a654ebd50ea7470609b9a68f5..a2b34239609ee15ea33d0cdc00a485357047b2af 100644
--- a/src/app/core/data/request.models.ts
+++ b/src/app/core/data/request.models.ts
@@ -14,10 +14,10 @@ import { RestRequestMethod } from './rest-request-method';
 import { SearchParam } from '../cache/models/search-param.model';
 import { EpersonResponseParsingService } from '../eperson/eperson-response-parsing.service';
 import { BrowseItemsResponseParsingService } from './browse-items-response-parsing-service';
-import { RegistryMetadataschemasResponseParsingService } from './registry-metadataschemas-response-parsing.service';
 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';
 
 /* tslint:disable:max-classes-per-file */
 
@@ -371,6 +371,30 @@ export class DeleteByIDRequest extends DeleteRequest {
   }
 }
 
+export class TaskPostRequest extends PostRequest {
+  constructor(uuid: string, href: string, public body?: any, public options?: HttpOptions) {
+    super(uuid, href, body, options);
+  }
+
+  getResponseParser(): GenericConstructor<ResponseParsingService> {
+    return TaskResponseParsingService;
+  }
+}
+
+export class TaskDeleteRequest extends DeleteRequest {
+  constructor(uuid: string, href: string, public body?: any, public options?: HttpOptions) {
+    super(uuid, href, body, options);
+  }
+
+  getResponseParser(): GenericConstructor<ResponseParsingService> {
+    return TaskResponseParsingService;
+  }
+}
+
+export class MyDSpaceRequest extends GetRequest {
+  public responseMsToLive = 0;
+}
+
 export class RequestError extends Error {
   statusCode: number;
   statusText: string;
diff --git a/src/app/core/data/request.service.ts b/src/app/core/data/request.service.ts
index 9f79d7539bff8bbeb3c91eb2dee26f175c7f7897..83071382ed91981366b46b429b5d8dd9f8d05950 100644
--- a/src/app/core/data/request.service.ts
+++ b/src/app/core/data/request.service.ts
@@ -1,11 +1,13 @@
 import { Injectable } from '@angular/core';
+import { HttpHeaders } from '@angular/common/http';
 
 import { createSelector, MemoizedSelector, select, Store } from '@ngrx/store';
 import { Observable, race as observableRace } from 'rxjs';
-import { filter, map, mergeMap, take } from 'rxjs/operators';
+import { filter, find, map, mergeMap, take } from 'rxjs/operators';
+import { cloneDeep, remove } from 'lodash';
 
 import { AppState } from '../../app.reducer';
-import { hasValue, isNotEmpty } from '../../shared/empty.util';
+import { hasValue, isEmpty, isNotEmpty } from '../../shared/empty.util';
 import { CacheableObject } from '../cache/object-cache.reducer';
 import { ObjectCacheService } from '../cache/object-cache.service';
 import { CoreState } from '../core.reducers';
@@ -23,8 +25,6 @@ import { CommitSSBAction } from '../cache/server-sync-buffer.actions';
 import { RestRequestMethod } from './rest-request-method';
 import { AddToIndexAction, RemoveFromIndexBySubstringAction } from '../index/index.actions';
 import { coreSelector } from '../core.selectors';
-import { HttpHeaders } from '@angular/common/http';
-import { cloneDeep } from 'lodash';
 
 /**
  * The base selector function to select the request state in the store
@@ -49,7 +49,6 @@ const entryFromUUIDSelector = (uuid: string): MemoizedSelector<CoreState, Reques
  * Create a selector that fetches a list of request UUIDs from a given index substate of which the request href
  * contains a given substring
  * @param selector    MemoizedSelector to start from
- * @param name        The name of the index substate we're fetching request UUIDs from
  * @param href        Substring that the request's href should contain
  */
 const uuidsFromHrefSubstringSelector =
@@ -149,12 +148,14 @@ export class RequestService {
    * @param {RestRequest} request The request to send out
    * @param {boolean} forceBypassCache When true, a new request is always dispatched
    */
-  // TODO to review "forceBypassCache" param when https://github.com/DSpace/dspace-angular/issues/217 will be fixed
   configure<T extends CacheableObject>(request: RestRequest, forceBypassCache: boolean = false): void {
     const isGetRequest = request.method === RestRequestMethod.GET;
-    if (!isGetRequest || !this.isCachedOrPending(request) || forceBypassCache) {
+    if (forceBypassCache) {
+      this.clearRequestsOnTheirWayToTheStore(request);
+    }
+    if (!isGetRequest || (forceBypassCache && !this.isPending(request)) || !this.isCachedOrPending(request)) {
       this.dispatchRequest(request);
-      if (isGetRequest && !forceBypassCache) {
+      if (isGetRequest) {
         this.trackRequestsOnTheirWayToTheStore(request);
       }
     } else {
@@ -168,6 +169,29 @@ export class RequestService {
     }
   }
 
+  /**
+   * Convert request Payload to a URL-encoded string
+   *
+   * e.g.  uriEncodeBody({param: value, param1: value1})
+   * returns: param=value&param1=value1
+   *
+   * @param body
+   *    The request Payload to convert
+   * @return string
+   *    URL-encoded string
+   */
+  public uriEncodeBody(body: any) {
+    let queryParams = '';
+    if (isNotEmpty(body) && typeof body === 'object') {
+      Object.keys(body)
+        .forEach((param) => {
+          const paramValue = `${param}=${body[param]}`;
+          queryParams = isEmpty(queryParams) ? queryParams.concat(paramValue) : queryParams.concat('&', paramValue);
+        })
+    }
+    return encodeURI(queryParams);
+  }
+
   /**
    * Remove all request cache providing (part of) the href
    * This also includes href-to-uuid index cache
@@ -234,6 +258,19 @@ export class RequestService {
     });
   }
 
+  /**
+   * This method remove requests that are on their way to the store.
+   */
+  private clearRequestsOnTheirWayToTheStore(request: GetRequest) {
+    this.getByHref(request.href).pipe(
+      find((re: RequestEntry) => hasValue(re)))
+      .subscribe((re: RequestEntry) => {
+        if (!re.responsePending) {
+          remove(this.requestsOnTheirWayToTheStore, (item) => item === request.href);
+        }
+      });
+  }
+
   /**
    * Dispatch commit action to send all changes (for a certain method) to the server (buffer)
    * @param {RestRequestMethod} method RestRequestMethod for which the changes should be committed
diff --git a/src/app/core/data/search-response-parsing.service.ts b/src/app/core/data/search-response-parsing.service.ts
index 0ca793c5aed9ac7fed8dcdb9498c2363e84bdf4b..9ab01043930ccedf09cf14e9be2926391b9e9349 100644
--- a/src/app/core/data/search-response-parsing.service.ts
+++ b/src/app/core/data/search-response-parsing.service.ts
@@ -15,7 +15,13 @@ export class SearchResponseParsingService implements ResponseParsingService {
   }
 
   parse(request: RestRequest, data: DSpaceRESTV2Response): RestResponse {
-    const payload = data.payload._embedded.searchResult;
+    // fallback for unexpected empty response
+    const emptyPayload = {
+      _embedded : {
+        objects: []
+      }
+    };
+    const payload = data.payload._embedded.searchResult || emptyPayload;
     const hitHighlights: MetadataMap[] = payload._embedded.objects
       .map((object) => object.hitHighlights)
       .map((hhObject) => {
@@ -31,7 +37,7 @@ export class SearchResponseParsingService implements ResponseParsingService {
 
     const dsoSelfLinks = payload._embedded.objects
       .filter((object) => hasValue(object._embedded))
-      .map((object) => object._embedded.dspaceObject)
+      .map((object) => object._embedded.indexableObject)
       // we don't need embedded collections, bitstreamformats, etc for search results.
       // And parsing them all takes up a lot of time. Throw them away to improve performance
       // until objs until partial results are supported by the rest api
@@ -47,7 +53,7 @@ export class SearchResponseParsingService implements ResponseParsingService {
     const objects = payload._embedded.objects
       .filter((object) => hasValue(object._embedded))
       .map((object, index) => Object.assign({}, object, {
-        dspaceObject: dsoSelfLinks[index],
+        indexableObject: dsoSelfLinks[index],
         hitHighlights: hitHighlights[index],
         // we don't need embedded collections, bitstreamformats, etc for search results.
         // And parsing them all takes up a lot of time. Throw them away to improve performance
diff --git a/src/app/core/dspace-rest-v2/dspace-rest-v2.service.ts b/src/app/core/dspace-rest-v2/dspace-rest-v2.service.ts
index 204c782e796756b877bc8e48559bca7274dcf965..290f4be8a2324efad4486d7ad1923edd62c1cf1f 100644
--- a/src/app/core/dspace-rest-v2/dspace-rest-v2.service.ts
+++ b/src/app/core/dspace-rest-v2/dspace-rest-v2.service.ts
@@ -68,6 +68,8 @@ export class DSpaceRESTv2Service {
    *    the URL for the request
    * @param body
    *    an optional body for the request
+   * @param options
+   *    the HttpOptions object
    * @return {Observable<string>}
    *      An Observable<string> containing the response from the server
    */
diff --git a/src/app/core/eperson/models/eperson.model.ts b/src/app/core/eperson/models/eperson.model.ts
index 32286929ee3eff207f8478e6792d503985a37b15..f8c11c120157489911cd8492a29e455f63dd808d 100644
--- a/src/app/core/eperson/models/eperson.model.ts
+++ b/src/app/core/eperson/models/eperson.model.ts
@@ -47,7 +47,9 @@ export class EPerson extends DSpaceObject {
    */
   public selfRegistered: boolean;
 
-  /** Getter to retrieve the EPerson's full name as a string */
+  /**
+   * Getter to retrieve the EPerson's full name as a string
+   */
   get name(): string {
     return this.firstMetadataValue('eperson.firstname') + ' ' + this.firstMetadataValue('eperson.lastname');
   }
diff --git a/src/app/core/metadata/metadata.service.ts b/src/app/core/metadata/metadata.service.ts
index a95fc73d33fa56b5a7bcfca7f774043031b32273..309dfd88907bcde3651d84210f1f3b9c74c1a8b7 100644
--- a/src/app/core/metadata/metadata.service.ts
+++ b/src/app/core/metadata/metadata.service.ts
@@ -59,7 +59,7 @@ export class MetadataService {
       map((route: ActivatedRoute) => {
         route = this.getCurrentRoute(route);
         return { params: route.params, data: route.data };
-      }),).subscribe((routeInfo: any) => {
+      })).subscribe((routeInfo: any) => {
       this.processRouteChange(routeInfo);
     });
   }
diff --git a/src/app/core/roles/role-types.ts b/src/app/core/roles/role-types.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b39d1205a6d8536917e445ec361cdb86fa22ff89
--- /dev/null
+++ b/src/app/core/roles/role-types.ts
@@ -0,0 +1,5 @@
+export enum RoleType {
+  Submitter = 'submitter',
+  Controller = 'controller',
+  Admin = 'admin'
+}
diff --git a/src/app/core/roles/role.service.ts b/src/app/core/roles/role.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7a4b6e6ccf91c43f3b3c8101f97f839146b3b5c9
--- /dev/null
+++ b/src/app/core/roles/role.service.ts
@@ -0,0 +1,70 @@
+import { Injectable } from '@angular/core';
+
+import { Observable, of as observableOf } from 'rxjs';
+import { distinctUntilChanged } from 'rxjs/operators';
+
+import { RoleType } from './role-types';
+import { CollectionDataService } from '../data/collection-data.service';
+
+/**
+ * A service that provides methods to identify user role.
+ */
+@Injectable()
+export class RoleService {
+
+  /**
+   * Initialize instance variables
+   *
+   * @param {CollectionDataService} collectionService
+   */
+  constructor(private collectionService: CollectionDataService) {
+  }
+
+  /**
+   * Check if current user is a submitter
+   */
+  isSubmitter(): Observable<boolean> {
+    return this.collectionService.hasAuthorizedCollection().pipe(
+      distinctUntilChanged()
+    );
+  }
+
+  /**
+   * Check if current user is a controller
+   */
+  isController(): Observable<boolean> {
+    // TODO find a way to check if user is a controller
+    return observableOf(true);
+  }
+
+  /**
+   * Check if current user is an admin
+   */
+  isAdmin(): Observable<boolean> {
+    // TODO find a way to check if user is an admin
+    return observableOf(false);
+  }
+
+  /**
+   * Check if current user by role type
+   *
+   * @param {RoleType} role
+   *    the role type
+   */
+  checkRole(role: RoleType): Observable<boolean> {
+    let check: Observable<boolean>;
+    switch (role) {
+      case RoleType.Submitter:
+        check = this.isSubmitter();
+        break;
+      case RoleType.Controller:
+        check = this.isController();
+        break;
+      case RoleType.Admin:
+        check = this.isAdmin();
+        break;
+    }
+
+    return check;
+  }
+}
diff --git a/src/app/core/shared/dspace-object.model.ts b/src/app/core/shared/dspace-object.model.ts
index 4aa0ba9695b94925a1b0a9e5474440407036cb14..fb42edd1260ae3fcbbc818a0566bfc117f32e626 100644
--- a/src/app/core/shared/dspace-object.model.ts
+++ b/src/app/core/shared/dspace-object.model.ts
@@ -7,6 +7,7 @@ import { CacheableObject, TypedObject } from '../cache/object-cache.reducer';
 import { RemoteData } from '../data/remote-data';
 import { ResourceType } from './resource-type';
 import { ListableObject } from '../../shared/object-collection/shared/listable-object.model';
+import { hasNoValue } from '../../shared/empty.util';
 
 /**
  * An abstract model class for a DSpaceObject.
@@ -123,4 +124,23 @@ export class DSpaceObject implements CacheableObject, ListableObject {
     return Metadata.has(this.metadata, keyOrKeys, valueFilter);
   }
 
+  /**
+   * Find metadata on a specific field and order all of them using their "place" property.
+   * @param key
+   */
+  findMetadataSortedByPlace(key: string): MetadataValue[] {
+    return this.allMetadata([key]).sort((a: MetadataValue, b: MetadataValue) => {
+      if (hasNoValue(a.place) && hasNoValue(b.place)) {
+        return 0;
+      }
+      if (hasNoValue(a.place)) {
+        return -1;
+      }
+      if (hasNoValue(b.place)) {
+        return 1;
+      }
+      return a.place - b.place;
+    });
+  }
+
 }
diff --git a/src/app/core/shared/item-relationships/item-type.model.ts b/src/app/core/shared/item-relationships/item-type.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e4f98ab653071f3f774c769bb6ce4d8214ded0df
--- /dev/null
+++ b/src/app/core/shared/item-relationships/item-type.model.ts
@@ -0,0 +1,27 @@
+import { CacheableObject } from '../../cache/object-cache.reducer';
+import { ResourceType } from '../resource-type';
+
+/**
+ * Describes a type of Item
+ */
+export class ItemType implements CacheableObject {
+  /**
+   * The identifier of this ItemType
+   */
+  id: string;
+
+  /**
+   * The link to the rest endpoint where this object can be found
+   */
+  self: string;
+
+  /**
+   * The type of Resource this is
+   */
+  type: ResourceType;
+
+  /**
+   * The universally unique identifier of this ItemType
+   */
+  uuid: string;
+}
diff --git a/src/app/core/shared/item-relationships/relationship-type.model.ts b/src/app/core/shared/item-relationships/relationship-type.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..404d8cdb4b50e727efd17c065cd29b8c9dc4381c
--- /dev/null
+++ b/src/app/core/shared/item-relationships/relationship-type.model.ts
@@ -0,0 +1,75 @@
+import { Observable } from 'rxjs';
+import { CacheableObject } from '../../cache/object-cache.reducer';
+import { RemoteData } from '../../data/remote-data';
+import { ResourceType } from '../resource-type';
+import { ItemType } from './item-type.model';
+
+/**
+ * Describes a type of Relationship between multiple possible Items
+ */
+export class RelationshipType implements CacheableObject {
+  /**
+   * The link to the rest endpoint where this object can be found
+   */
+  self: string;
+
+  /**
+   * The type of Resource this is
+   */
+  type: ResourceType;
+
+  /**
+   * The label that describes this RelationshipType
+   */
+  label: string;
+
+  /**
+   * The identifier of this RelationshipType
+   */
+  id: string;
+
+  /**
+   * The universally unique identifier of this RelationshipType
+   */
+  uuid: string;
+
+  /**
+   * The label that describes the Relation to the left of this RelationshipType
+   */
+  leftLabel: string;
+
+  /**
+   * The maximum amount of Relationships allowed to the left of this RelationshipType
+   */
+  leftMaxCardinality: number;
+
+  /**
+   * The minimum amount of Relationships allowed to the left of this RelationshipType
+   */
+  leftMinCardinality: number;
+
+  /**
+   * The label that describes the Relation to the right of this RelationshipType
+   */
+  rightLabel: string;
+
+  /**
+   * The maximum amount of Relationships allowed to the right of this RelationshipType
+   */
+  rightMaxCardinality: number;
+
+  /**
+   * The minimum amount of Relationships allowed to the right of this RelationshipType
+   */
+  rightMinCardinality: number;
+
+  /**
+   * The type of Item found to the left of this RelationshipType
+   */
+  leftType: Observable<RemoteData<ItemType>>;
+
+  /**
+   * The type of Item found to the right of this RelationshipType
+   */
+  rightType: Observable<RemoteData<ItemType>>;
+}
diff --git a/src/app/core/shared/item-relationships/relationship.model.ts b/src/app/core/shared/item-relationships/relationship.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..df8f04cd8a8afee39a78646ff366f0800af88705
--- /dev/null
+++ b/src/app/core/shared/item-relationships/relationship.model.ts
@@ -0,0 +1,55 @@
+import { Observable } from 'rxjs';
+import { CacheableObject } from '../../cache/object-cache.reducer';
+import { RemoteData } from '../../data/remote-data';
+import { ResourceType } from '../resource-type';
+import { RelationshipType } from './relationship-type.model';
+
+/**
+ * Describes a Relationship between two Items
+ */
+export class Relationship implements CacheableObject {
+  /**
+   * The link to the rest endpoint where this object can be found
+   */
+  self: string;
+
+  /**
+   * The type of Resource this is
+   */
+  type: ResourceType;
+
+  /**
+   * The universally unique identifier of this Relationship
+   */
+  uuid: string;
+
+  /**
+   * The identifier of this Relationship
+   */
+  id: string;
+
+  /**
+   * The identifier of the Item to the left side of this Relationship
+   */
+  leftId: string;
+
+  /**
+   * The identifier of the Item to the right side of this Relationship
+   */
+  rightId: string;
+
+  /**
+   * The place of the Item to the left side of this Relationship
+   */
+  leftPlace: number;
+
+  /**
+   * The place of the Item to the right side of this Relationship
+   */
+  rightPlace: number;
+
+  /**
+   * The type of Relationship
+   */
+  relationshipType: Observable<RemoteData<RelationshipType>>;
+}
diff --git a/src/app/core/shared/item.model.ts b/src/app/core/shared/item.model.ts
index 6cd5634fd053d06cc6808121694cebdadcd7aebf..839103b9f599d8d3bf2d25077c393bedf871dca3 100644
--- a/src/app/core/shared/item.model.ts
+++ b/src/app/core/shared/item.model.ts
@@ -5,8 +5,9 @@ import { DSpaceObject } from './dspace-object.model';
 import { Collection } from './collection.model';
 import { RemoteData } from '../data/remote-data';
 import { Bitstream } from './bitstream.model';
-import { hasValue, isNotEmpty } from '../../shared/empty.util';
+import { hasValue, isNotEmpty, isNotUndefined } from '../../shared/empty.util';
 import { PaginatedList } from '../data/paginated-list';
+import { Relationship } from './item-relationships/relationship.model';
 
 export class Item extends DSpaceObject {
 
@@ -51,6 +52,8 @@ export class Item extends DSpaceObject {
 
   bitstreams: Observable<RemoteData<PaginatedList<Bitstream>>>;
 
+  relationships: Observable<RemoteData<PaginatedList<Relationship>>>;
+
   /**
    * Retrieves the thumbnail of this item
    * @returns {Observable<Bitstream>} the primaryBitstream of the 'THUMBNAIL' bundle
@@ -87,10 +90,12 @@ export class Item extends DSpaceObject {
    * Retrieves bitstreams by bundle name
    * @param bundleName The name of the Bundle that should be returned
    * @returns {Observable<Bitstream[]>} the bitstreams with the given bundleName
+   * TODO now that bitstreams can be paginated this should move to the server
+   * see https://github.com/DSpace/dspace-angular/issues/332
    */
   getBitstreamsByBundleName(bundleName: string): Observable<Bitstream[]> {
     return this.bitstreams.pipe(
-      filter((rd: RemoteData<PaginatedList<Bitstream>>) => !rd.isResponsePending),
+      filter((rd: RemoteData<PaginatedList<Bitstream>>) => !rd.isResponsePending && isNotUndefined(rd.payload)),
       map((rd: RemoteData<PaginatedList<Bitstream>>) => rd.payload.page),
       filter((bitstreams: Bitstream[]) => hasValue(bitstreams)),
       take(1),
diff --git a/src/app/core/shared/metadata-representation/item/item-metadata-representation.model.spec.ts b/src/app/core/shared/metadata-representation/item/item-metadata-representation.model.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f31f8617adff4a81bf8728f38874407f8c8f0a04
--- /dev/null
+++ b/src/app/core/shared/metadata-representation/item/item-metadata-representation.model.spec.ts
@@ -0,0 +1,43 @@
+import { MetadataRepresentationType } from '../metadata-representation.model';
+import { ItemMetadataRepresentation, ItemTypeToValue } from './item-metadata-representation.model';
+import { Item } from '../../item.model';
+import { MetadataMap, MetadataValue } from '../../metadata.models';
+
+describe('ItemMetadataRepresentation', () => {
+  const valuePrefix = 'Test value for ';
+  const item = new Item();
+  let itemMetadataRepresentation: ItemMetadataRepresentation;
+  const metadataMap = new MetadataMap();
+  for (const key of Object.keys(ItemTypeToValue)) {
+    metadataMap[ItemTypeToValue[key]] = [Object.assign(new MetadataValue(), {
+      value: `${valuePrefix}${ItemTypeToValue[key]}`
+    })];
+  }
+  item.metadata = metadataMap;
+
+  for (const itemType of Object.keys(ItemTypeToValue)) {
+    describe(`when creating an ItemMetadataRepresentation`, () => {
+      beforeEach(() => {
+        item.metadata['relationship.type'] = [
+          Object.assign(new MetadataValue(), {
+            value: itemType
+          })
+        ];
+
+        itemMetadataRepresentation = Object.assign(new ItemMetadataRepresentation(), item);
+      });
+
+      it('should have a representation type of item', () => {
+        expect(itemMetadataRepresentation.representationType).toEqual(MetadataRepresentationType.Item);
+      });
+
+      it('should return the correct value when calling getValue', () => {
+        expect(itemMetadataRepresentation.getValue()).toEqual(`${valuePrefix}${ItemTypeToValue[itemType]}`);
+      });
+
+      it('should return the correct item type', () => {
+        expect(itemMetadataRepresentation.itemType).toEqual(item.firstMetadataValue('relationship.type'));
+      });
+    });
+  }
+});
diff --git a/src/app/core/shared/metadata-representation/item/item-metadata-representation.model.ts b/src/app/core/shared/metadata-representation/item/item-metadata-representation.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7ec1445613b07127f1def2b24b2bba83581f6ff1
--- /dev/null
+++ b/src/app/core/shared/metadata-representation/item/item-metadata-representation.model.ts
@@ -0,0 +1,46 @@
+import { Item } from '../../item.model';
+import { MetadataRepresentation, MetadataRepresentationType } from '../metadata-representation.model';
+import { hasValue } from '../../../../shared/empty.util';
+
+/**
+ * An object to convert item types into the metadata field it should render for the item's value
+ */
+export const ItemTypeToValue = {
+  Default: 'dc.title',
+  Person: 'dc.contributor.author',
+  OrgUnit: 'dc.title'
+};
+
+/**
+ * This class determines which fields to use when rendering an Item as a metadata value.
+ */
+export class ItemMetadataRepresentation extends Item implements MetadataRepresentation {
+
+  /**
+   * The type of item this item can be represented as
+   */
+  get itemType(): string {
+    return this.firstMetadataValue('relationship.type');
+  }
+
+  /**
+   * Fetch the way this item should be rendered as in a list
+   */
+  get representationType(): MetadataRepresentationType {
+    return MetadataRepresentationType.Item;
+  }
+
+  /**
+   * Get the value to display, depending on the itemType
+   */
+  getValue(): string {
+    let metadata;
+    if (hasValue(ItemTypeToValue[this.itemType])) {
+      metadata = ItemTypeToValue[this.itemType];
+    } else {
+      metadata = ItemTypeToValue.Default;
+    }
+    return this.firstMetadataValue(metadata);
+  }
+
+}
diff --git a/src/app/core/shared/metadata-representation/metadata-representation.model.ts b/src/app/core/shared/metadata-representation/metadata-representation.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..58e5bf906ffa213d5ecce6f644790dc005e704f4
--- /dev/null
+++ b/src/app/core/shared/metadata-representation/metadata-representation.model.ts
@@ -0,0 +1,31 @@
+/**
+ * An Enum defining the representation type of metadata
+ */
+export enum MetadataRepresentationType {
+  None = 'none',
+  Item = 'item',
+  AuthorityControlled = 'authority_controlled',
+  PlainText = 'plain_text'
+}
+
+/**
+ * An interface containing information about how we should represent certain metadata
+ */
+export interface MetadataRepresentation {
+  /**
+   * The type of item this metadata is representing
+   * e.g. 'Person'
+   * This can be used for template matching
+   */
+  itemType: string;
+
+  /**
+   * How we should render the metadata in a list
+   */
+  representationType: MetadataRepresentationType,
+
+  /**
+   * Fetches the value to be displayed
+   */
+  getValue(): string
+}
diff --git a/src/app/core/shared/metadata-representation/metadatum/metadatum-representation.model.spec.ts b/src/app/core/shared/metadata-representation/metadatum/metadatum-representation.model.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ea48d345c5d19af613227b13b1675f0c98b71a02
--- /dev/null
+++ b/src/app/core/shared/metadata-representation/metadatum/metadatum-representation.model.spec.ts
@@ -0,0 +1,54 @@
+import { MetadatumRepresentation } from './metadatum-representation.model';
+import { MetadataRepresentationType } from '../metadata-representation.model';
+import { MetadataValue } from '../../metadata.models';
+
+describe('MetadatumRepresentation', () => {
+  const itemType = 'Person';
+  const normalMetadatum = Object.assign(new MetadataValue(), {
+    key: 'dc.contributor.author',
+    value: 'Test Author'
+  });
+  const authorityMetadatum = Object.assign(new MetadataValue(), {
+    key: 'dc.contributor.author',
+    value: 'Test Authority Author',
+    authority: '1234'
+  });
+
+  let metadatumRepresentation: MetadatumRepresentation;
+
+  describe('when creating a MetadatumRepresentation based on a standard Metadatum object', () => {
+    beforeEach(() => {
+      metadatumRepresentation = Object.assign(new MetadatumRepresentation(itemType), normalMetadatum);
+    });
+
+    it('should have a representation type of plain text', () => {
+      expect(metadatumRepresentation.representationType).toEqual(MetadataRepresentationType.PlainText);
+    });
+
+    it('should return the correct value when calling getPrimaryValue', () => {
+      expect(metadatumRepresentation.getValue()).toEqual(normalMetadatum.value);
+    });
+
+    it('should return the correct item type', () => {
+      expect(metadatumRepresentation.itemType).toEqual(itemType);
+    });
+  });
+
+  describe('when creating a MetadatumRepresentation based on an authority controlled Metadatum object', () => {
+    beforeEach(() => {
+      metadatumRepresentation = Object.assign(new MetadatumRepresentation(itemType), authorityMetadatum);
+    });
+
+    it('should have a representation type of plain text', () => {
+      expect(metadatumRepresentation.representationType).toEqual(MetadataRepresentationType.AuthorityControlled);
+    });
+
+    it('should return the correct value when calling getValue', () => {
+      expect(metadatumRepresentation.getValue()).toEqual(authorityMetadatum.value);
+    });
+
+    it('should return the correct item type', () => {
+      expect(metadatumRepresentation.itemType).toEqual(itemType);
+    });
+  });
+});
diff --git a/src/app/core/shared/metadata-representation/metadatum/metadatum-representation.model.ts b/src/app/core/shared/metadata-representation/metadatum/metadatum-representation.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..595147f3e6f1a2d3ff4ce7ceed7c0ea6dd773195
--- /dev/null
+++ b/src/app/core/shared/metadata-representation/metadatum/metadatum-representation.model.ts
@@ -0,0 +1,38 @@
+import { MetadataRepresentation, MetadataRepresentationType } from '../metadata-representation.model';
+import { hasValue } from '../../../../shared/empty.util';
+import { MetadataValue } from '../../metadata.models';
+
+/**
+ * This class defines the way the metadatum it extends should be represented
+ */
+export class MetadatumRepresentation extends MetadataValue implements MetadataRepresentation {
+
+  /**
+   * The type of item this metadatum can be represented as
+   */
+  itemType: string;
+
+  constructor(itemType: string) {
+    super();
+    this.itemType = itemType;
+  }
+
+  /**
+   * Fetch the way this metadatum should be rendered as in a list
+   */
+  get representationType(): MetadataRepresentationType {
+    if (hasValue(this.authority)) {
+      return MetadataRepresentationType.AuthorityControlled;
+    } else {
+      return MetadataRepresentationType.PlainText;
+    }
+  }
+
+  /**
+   * Get the value to display
+   */
+  getValue(): string {
+    return this.value;
+  }
+
+}
diff --git a/src/app/core/shared/metadata.models.ts b/src/app/core/shared/metadata.models.ts
index ab007c15f6efb952a0563682a89693611ce93ec0..9c7e30dcb4a1a780c5e6eb3413072d9c5adccaae 100644
--- a/src/app/core/shared/metadata.models.ts
+++ b/src/app/core/shared/metadata.models.ts
@@ -1,7 +1,10 @@
 import * as uuidv4 from 'uuid/v4';
 import { autoserialize, Serialize, Deserialize } from 'cerialize';
+import { hasValue } from '../../shared/empty.util';
 /* tslint:disable:max-classes-per-file */
 
+const VIRTUAL_METADATA_PREFIX = 'virtual::';
+
 /** A single metadata value and its properties. */
 export interface MetadataValueInterface {
 
@@ -34,6 +37,40 @@ export class MetadataValue implements MetadataValueInterface {
   /** The string value. */
   @autoserialize
   value: string;
+
+  /**
+   * The place of this MetadataValue within his list of metadata
+   * This is used to render metadata in a specific custom order
+   */
+  @autoserialize
+  place: number;
+
+  /** The authority key used for authority-controlled metadata */
+  @autoserialize
+  authority: string;
+
+  /** The authority confidence value */
+  @autoserialize
+  confidence: number;
+
+  /**
+   * Returns true if this Metadatum's authority key starts with 'virtual::'
+   */
+  get isVirtual(): boolean {
+    return hasValue(this.authority) && this.authority.startsWith(VIRTUAL_METADATA_PREFIX);
+  }
+
+  /**
+   * If this is a virtual Metadatum, it returns everything in the authority key after 'virtual::'.
+   * Returns undefined otherwise.
+   */
+  get virtualValue(): string {
+    if (this.isVirtual) {
+      return this.authority.substring(this.authority.indexOf(VIRTUAL_METADATA_PREFIX) + VIRTUAL_METADATA_PREFIX.length);
+    } else {
+      return undefined;
+    }
+  }
 }
 
 /** Constraints for matching metadata values. */
@@ -64,8 +101,17 @@ export class MetadatumViewModel {
   /** The string value. */
   value: string;
 
-  /** The order. */
-  order: number;
+  /**
+   * The place of this MetadataValue within his list of metadata
+   * This is used to render metadata in a specific custom order
+   */
+  place: number;
+
+  /** The authority key used for authority-controlled metadata */
+  authority: string;
+
+  /** The authority confidence value */
+  confidence: number;
 }
 
 /** Serializer used for MetadataMaps.
diff --git a/src/app/core/shared/metadata.utils.spec.ts b/src/app/core/shared/metadata.utils.spec.ts
index 7fbea14b1369956394ce3f2c9d12e4dec0b9eff6..1e1d7f86d51becfd3f1d09e613549af13caa1ae6 100644
--- a/src/app/core/shared/metadata.utils.spec.ts
+++ b/src/app/core/shared/metadata.utils.spec.ts
@@ -9,7 +9,7 @@ import {
 import { Metadata } from './metadata.utils';
 
 const mdValue = (value: string, language?: string): MetadataValue => {
-  return { uuid: uuidv4(), value: value, language: isUndefined(language) ? null : language };
+  return Object.assign(new MetadataValue(), { uuid: uuidv4(), value: value, language: isUndefined(language) ? null : language, place: 0, authority: undefined, confidence: undefined });
 };
 
 const dcDescription = mdValue('Some description');
diff --git a/src/app/core/shared/metadata.utils.ts b/src/app/core/shared/metadata.utils.ts
index 938d646a82282fa07635e60b8b71178399406f2e..62a1957e22f0569b7711e59a5cf6d929d230c4a8 100644
--- a/src/app/core/shared/metadata.utils.ts
+++ b/src/app/core/shared/metadata.utils.ts
@@ -205,8 +205,8 @@ export class Metadata {
       .sort()
       .forEach((key: string) => {
         const orderedValues = sortBy(groupedList[key], ['order']);
-        metadataMap[key] = orderedValues.map((value: MetadataValue) => {
-            const val = Object.assign({}, value);
+        metadataMap[key] = orderedValues.map((value: MetadatumViewModel) => {
+            const val = Object.assign(new MetadataValue(), value);
             delete (val as any).order;
             delete (val as any).key;
             return val;
diff --git a/src/app/core/shared/operators.spec.ts b/src/app/core/shared/operators.spec.ts
index 2eb47507b27416144598db8123975637d368a708..564b0ff31999fe15b54a5d57a2aca79dbe729407 100644
--- a/src/app/core/shared/operators.spec.ts
+++ b/src/app/core/shared/operators.spec.ts
@@ -13,9 +13,11 @@ import {
   getRequestFromRequestUUID,
   getResourceLinksFromResponse,
   getResponseFromEntry,
-  getSucceededRemoteData
+  getSucceededRemoteData, redirectToPageNotFoundOn404
 } from './operators';
 import { RemoteData } from '../data/remote-data';
+import { RemoteDataError } from '../data/remote-data-error';
+import { of as observableOf } from 'rxjs';
 
 describe('Core Module - RxJS Operators', () => {
   let scheduler: TestScheduler;
@@ -193,6 +195,34 @@ describe('Core Module - RxJS Operators', () => {
     });
   });
 
+  describe('redirectToPageNotFoundOn404', () => {
+    let router;
+    beforeEach(() => {
+      router = jasmine.createSpyObj('router', ['navigateByUrl']);
+    });
+
+    it('should call navigateByUrl to a 404 page, when the remote data contains a 404 error', () => {
+      const testRD = new RemoteData(false, false, false, new RemoteDataError(404, 'Not Found', 'Object was not found'), undefined);
+
+      observableOf(testRD).pipe(redirectToPageNotFoundOn404(router)).subscribe();
+      expect(router.navigateByUrl).toHaveBeenCalledWith('/404', { skipLocationChange: true });
+    });
+
+    it('should not call navigateByUrl to a 404 page, when the remote data contains another error than a 404', () => {
+      const testRD = new RemoteData(false, false, false, new RemoteDataError(500, 'Server Error', 'Something went wrong'), undefined);
+
+      observableOf(testRD).pipe(redirectToPageNotFoundOn404(router)).subscribe();
+      expect(router.navigateByUrl).not.toHaveBeenCalled();
+    });
+
+    it('should not call navigateByUrl to a 404 page, when the remote data contains no error', () => {
+      const testRD = new RemoteData(false, false, true, null, undefined);
+
+      observableOf(testRD).pipe(redirectToPageNotFoundOn404(router)).subscribe();
+      expect(router.navigateByUrl).not.toHaveBeenCalled();
+    });
+  });
+
   describe('getResponseFromEntry', () => {
     it('should return the response for all not empty request entries, when they have a value', () => {
       const source = hot('abcdefg', testRCEs);
diff --git a/src/app/core/shared/operators.ts b/src/app/core/shared/operators.ts
index ce9740a0fcc19f581a394fce2ce5c0c07ca9e0e4..ae46691e390574f17b5a12906de1db2bcb61678b 100644
--- a/src/app/core/shared/operators.ts
+++ b/src/app/core/shared/operators.ts
@@ -1,5 +1,5 @@
 import { Observable } from 'rxjs';
-import { filter, find, flatMap, map, tap } from 'rxjs/operators';
+import { filter, find, flatMap, map, take, tap } from 'rxjs/operators';
 import { hasValue, hasValueOperator, isNotEmpty } from '../../shared/empty.util';
 import { DSOSuccessResponse, RestResponse } from '../cache/response.models';
 import { RemoteData } from '../data/remote-data';
@@ -10,6 +10,8 @@ import { BrowseDefinition } from './browse-definition.model';
 import { DSpaceObject } from './dspace-object.model';
 import { PaginatedList } from '../data/paginated-list';
 import { SearchResult } from '../../+search-page/search-result.model';
+import { Item } from './item.model';
+import { Router } from '@angular/router';
 
 /**
  * This file contains custom RxJS operators that can be used in multiple places
@@ -62,6 +64,20 @@ export const getSucceededRemoteData = () =>
   <T>(source: Observable<RemoteData<T>>): Observable<RemoteData<T>> =>
     source.pipe(find((rd: RemoteData<T>) => rd.hasSucceeded));
 
+/**
+ * Operator that checks if a remote data object contains a page not found error
+ * When it does contain such an error, it will redirect the user to a page not found, without altering the current URL
+ * @param router The router used to navigate to a new page
+ */
+export const redirectToPageNotFoundOn404 = (router: Router) =>
+  <T>(source: Observable<RemoteData<T>>): Observable<RemoteData<T>> =>
+    source.pipe(
+      tap((rd: RemoteData<T>) => {
+        if (rd.hasFailed && rd.error.statusCode === 404) {
+          router.navigateByUrl('/404', { skipLocationChange: true });
+        }
+      }));
+
 export const getFinishedRemoteData = () =>
   <T>(source: Observable<RemoteData<T>>): Observable<RemoteData<T>> =>
     source.pipe(find((rd: RemoteData<T>) => !rd.isLoading));
@@ -75,7 +91,7 @@ export const toDSpaceObjectListRD = () =>
     source.pipe(
       filter((rd: RemoteData<PaginatedList<SearchResult<T>>>) => rd.hasSucceeded),
       map((rd: RemoteData<PaginatedList<SearchResult<T>>>) => {
-        const dsoPage: T[] = rd.payload.page.map((searchResult: SearchResult<T>) => searchResult.dspaceObject);
+        const dsoPage: T[] = rd.payload.page.map((searchResult: SearchResult<T>) => searchResult.indexableObject);
         const payload = Object.assign(rd.payload, { page: dsoPage }) as PaginatedList<T>;
         return Object.assign(rd, { payload: payload });
       })
diff --git a/src/app/core/shared/resource-type.ts b/src/app/core/shared/resource-type.ts
index 47c59b95dcc45698f20dce2a80c8a05be6b4427d..5839e140845c13580dd4342f713fbb6c75eb1830 100644
--- a/src/app/core/shared/resource-type.ts
+++ b/src/app/core/shared/resource-type.ts
@@ -11,6 +11,9 @@ export enum ResourceType {
   ResourcePolicy = 'resourcePolicy',
   MetadataSchema = 'metadataschema',
   MetadataField = 'metadatafield',
+  Relationship = 'relationship',
+  RelationshipType = 'relationshiptype',
+  ItemType = 'entitytype',
   License = 'license',
   WorkflowItem = 'workflowitem',
   WorkspaceItem = 'workspaceitem',
@@ -23,4 +26,6 @@ export enum ResourceType {
   AuthStatus = 'status',
   Authority = 'authority',
   BrowseEntry = 'browseEntry',
+  ClaimedTask = 'claimedtask',
+  PoolTask = 'pooltask'
 }
diff --git a/src/app/core/shared/view-mode.model.ts b/src/app/core/shared/view-mode.model.ts
index b026d6843132dc1c72ebaa301d5ad87b0d1fbb5f..9c8d08609718d375c16f15d3a49fa8df849f0f67 100644
--- a/src/app/core/shared/view-mode.model.ts
+++ b/src/app/core/shared/view-mode.model.ts
@@ -4,5 +4,6 @@
 
 export enum ViewMode {
   List = 'list',
-  Grid = 'grid'
+  Grid = 'grid',
+  Detail = 'detail'
 }
diff --git a/src/app/core/submission/models/normalized-submission-object.model.ts b/src/app/core/submission/models/normalized-submission-object.model.ts
index 809178176080953d5afc92ee42db9c6d2fe584e8..f674ebdf72dd96dfe15275c415b179461b30c69d 100644
--- a/src/app/core/submission/models/normalized-submission-object.model.ts
+++ b/src/app/core/submission/models/normalized-submission-object.model.ts
@@ -1,4 +1,4 @@
-import { autoserialize, inheritSerialization } from 'cerialize';
+import { autoserialize, autoserializeAs, inheritSerialization } from 'cerialize';
 
 import { NormalizedDSpaceObject } from '../../cache/models/normalized-dspace-object.model';
 import { WorkspaceitemSectionsObject } from './workspaceitem-sections.model';
@@ -17,6 +17,12 @@ export class NormalizedSubmissionObject<T extends DSpaceObject> extends Normaliz
   @autoserialize
   id: string;
 
+  /**
+   * The workspaceitem/workflowitem identifier
+   */
+  @autoserializeAs(String, 'id')
+  uuid: string;
+
   /**
    * The workspaceitem/workflowitem last modified date
    */
diff --git a/src/app/core/submission/models/submission-object.model.ts b/src/app/core/submission/models/submission-object.model.ts
index 6b2d9a03b955d85d13ff1db82681cb5bd142bb62..23f75553c5f4a3883ba8f554334a5c787b6f21c3 100644
--- a/src/app/core/submission/models/submission-object.model.ts
+++ b/src/app/core/submission/models/submission-object.model.ts
@@ -25,6 +25,11 @@ export abstract class SubmissionObject extends DSpaceObject implements Cacheable
    */
   id: string;
 
+  /**
+   * The workspaceitem/workflowitem identifier
+   */
+  uuid: string;
+
   /**
    * The workspaceitem/workflowitem last modified date
    */
diff --git a/src/app/core/submission/submission-response-parsing.service.ts b/src/app/core/submission/submission-response-parsing.service.ts
index deb8a814ce0f1c6ddab52029a9f9057a3795d624..a0811c8f2d247e689ec076f7536cae44567fd1ae 100644
--- a/src/app/core/submission/submission-response-parsing.service.ts
+++ b/src/app/core/submission/submission-response-parsing.service.ts
@@ -25,16 +25,16 @@ export function isServerFormValue(obj: any): boolean {
     && obj.hasOwnProperty('value')
     && obj.hasOwnProperty('language')
     && obj.hasOwnProperty('authority')
-    && obj.hasOwnProperty('confidence')
-    && obj.hasOwnProperty('place'))
+    && obj.hasOwnProperty('confidence'))
 }
 
 /**
  * Export a function to normalize sections object of the server response
  *
  * @param obj
+ * @param objIndex
  */
-export function normalizeSectionData(obj: any) {
+export function normalizeSectionData(obj: any, objIndex?: number) {
   let result: any = obj;
   if (isNotNull(obj)) {
     // If is an Instance of FormFieldMetadataValueObject normalize it
@@ -47,14 +47,14 @@ export function normalizeSectionData(obj: any) {
         obj.language,
         obj.authority,
         (obj.display || obj.value),
-        obj.place,
+        obj.place || objIndex,
         obj.confidence,
         obj.otherInformation
       );
     } else if (Array.isArray(obj)) {
       result = [];
       obj.forEach((item, index) => {
-        result[index] = normalizeSectionData(item);
+        result[index] = normalizeSectionData(item, index);
       });
     } else if (typeof obj === 'object') {
       result = Object.create({});
@@ -90,11 +90,10 @@ export class SubmissionResponseParsingService extends BaseResponseParsingService
   parse(request: RestRequest, data: DSpaceRESTV2Response): RestResponse {
     if (isNotEmpty(data.payload)
       && isNotEmpty(data.payload._links)
-      && (data.statusCode === 201 || data.statusCode === 200)) {
+      && this.isSuccessStatus(data.statusCode)) {
       const dataDefinition = this.processResponse<SubmissionObject | ConfigObject>(data.payload, request.href);
       return new SubmissionSuccessResponse(dataDefinition, data.statusCode, data.statusText, this.processPageInfo(data.payload));
-    } else if (isEmpty(data.payload) && data.statusCode === 204) {
-      // Response from a DELETE request
+    } else if (isEmpty(data.payload) && this.isSuccessStatus(data.statusCode)) {
       return new SubmissionSuccessResponse(null, data.statusCode, data.statusText);
     } else {
       return new ErrorResponse(
@@ -138,9 +137,9 @@ export class SubmissionResponseParsingService extends BaseResponseParsingService
                     // If entry is not an array, for sure is not a section of type form
                     if (Array.isArray(entry)) {
                       normalizedSectionData[metdadataId] = [];
-                      entry.forEach((valueItem) => {
+                      entry.forEach((valueItem, index) => {
                         // Parse value and normalize it
-                        const normValue = normalizeSectionData(valueItem);
+                        const normValue = normalizeSectionData(valueItem, index);
                         if (isNotEmpty(normValue)) {
                           normalizedSectionData[metdadataId].push(normValue);
                         }
diff --git a/src/app/core/tasks/claimed-task-data.service.spec.ts b/src/app/core/tasks/claimed-task-data.service.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a7be0830ec17a890c7edb0d229a5043c5751126e
--- /dev/null
+++ b/src/app/core/tasks/claimed-task-data.service.spec.ts
@@ -0,0 +1,108 @@
+import { HttpClient, HttpHeaders } from '@angular/common/http';
+
+import { Store } from '@ngrx/store';
+
+import { getMockRequestService } from '../../shared/mocks/mock-request.service';
+import { HALEndpointServiceStub } from '../../shared/testing/hal-endpoint-service-stub';
+import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
+import { NotificationsService } from '../../shared/notifications/notifications.service';
+import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service';
+import { CoreState } from '../core.reducers';
+import { ClaimedTaskDataService } from './claimed-task-data.service';
+import { HttpOptions } from '../dspace-rest-v2/dspace-rest-v2.service';
+
+describe('ClaimedTaskDataService', () => {
+  let service: ClaimedTaskDataService;
+  let options: HttpOptions;
+  const taskEndpoint = 'https://rest.api/task';
+  const linkPath = 'claimedtasks';
+  const requestService: any = getMockRequestService();
+  const halService: any = new HALEndpointServiceStub(taskEndpoint);
+  const rdbService = {} as RemoteDataBuildService;
+  const notificationsService = {} as NotificationsService;
+  const http = {} as HttpClient;
+  const comparator = {} as any;
+  const dataBuildService = {
+    normalize: (object) => object
+  } as NormalizedObjectBuildService;
+  const objectCache = {
+    addPatch: () => {
+      /* empty */
+    },
+    getObjectBySelfLink: () => {
+      /* empty */
+    }
+  } as any;
+  const store = {} as Store<CoreState>;
+
+  function initTestService(): ClaimedTaskDataService {
+    return new ClaimedTaskDataService(
+      requestService,
+      rdbService,
+      dataBuildService,
+      store,
+      objectCache,
+      halService,
+      notificationsService,
+      http,
+      comparator
+    );
+  }
+
+  beforeEach(() => {
+    service = initTestService();
+    options = Object.create({});
+    let headers = new HttpHeaders();
+    headers = headers.append('Content-Type', 'application/x-www-form-urlencoded');
+    options.headers = headers;
+  });
+
+  describe('approveTask', () => {
+
+    it('should call postToEndpoint method', () => {
+      const scopeId = '1234';
+      const body = {
+        submit_approve: 'true'
+      };
+
+      spyOn(service, 'postToEndpoint');
+      requestService.uriEncodeBody.and.returnValue(body);
+
+      service.approveTask(scopeId);
+
+      expect(service.postToEndpoint).toHaveBeenCalledWith(linkPath, body, scopeId, options);
+    });
+  });
+
+  describe('rejectTask', () => {
+
+    it('should call postToEndpoint method', () => {
+      const scopeId = '1234';
+      const reason = 'test reject';
+      const body = {
+        submit_reject: 'true',
+        reason
+      };
+
+      spyOn(service, 'postToEndpoint');
+      requestService.uriEncodeBody.and.returnValue(body);
+
+      service.rejectTask(reason, scopeId);
+
+      expect(service.postToEndpoint).toHaveBeenCalledWith(linkPath, body, scopeId, options);
+    });
+  });
+
+  describe('returnToPoolTask', () => {
+
+    it('should call deleteById method', () => {
+      const scopeId = '1234';
+
+      spyOn(service, 'deleteById');
+
+      service.returnToPoolTask(scopeId);
+
+      expect(service.deleteById).toHaveBeenCalledWith(linkPath, scopeId, options);
+    });
+  });
+});
diff --git a/src/app/core/tasks/claimed-task-data.service.ts b/src/app/core/tasks/claimed-task-data.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..dda42e1c6726476ed18305ee8b0a66e7b45b90ef
--- /dev/null
+++ b/src/app/core/tasks/claimed-task-data.service.ts
@@ -0,0 +1,106 @@
+import { Injectable } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+
+import { Store } from '@ngrx/store';
+import { Observable } from 'rxjs';
+
+import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
+import { CoreState } from '../core.reducers';
+import { RequestService } from '../data/request.service';
+import { ClaimedTask } from './models/claimed-task-object.model';
+import { TasksService } from './tasks.service';
+import { HALEndpointService } from '../shared/hal-endpoint.service';
+import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service';
+import { ObjectCacheService } from '../cache/object-cache.service';
+import { NotificationsService } from '../../shared/notifications/notifications.service';
+import { DSOChangeAnalyzer } from '../data/dso-change-analyzer.service';
+import { ProcessTaskResponse } from './models/process-task-response';
+
+/**
+ * The service handling all REST requests for ClaimedTask
+ */
+@Injectable()
+export class ClaimedTaskDataService extends TasksService<ClaimedTask> {
+
+  /**
+   * The endpoint link name
+   */
+  protected linkPath = 'claimedtasks';
+
+  /**
+   * When true, a new request is always dispatched
+   */
+  protected forceBypassCache = true;
+
+  /**
+   * Initialize instance variables
+   *
+   * @param {RequestService} requestService
+   * @param {RemoteDataBuildService} rdbService
+   * @param {NormalizedObjectBuildService} dataBuildService
+   * @param {Store<CoreState>} store
+   * @param {ObjectCacheService} objectCache
+   * @param {HALEndpointService} halService
+   * @param {NotificationsService} notificationsService
+   * @param {HttpClient} http
+   * @param {DSOChangeAnalyzer<ClaimedTask} comparator
+   */
+  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<ClaimedTask>) {
+    super();
+  }
+
+  /**
+   * Make a request to approve the given task
+   *
+   * @param scopeId
+   *    The task id
+   * @return {Observable<ProcessTaskResponse>}
+   *    Emit the server response
+   */
+  public approveTask(scopeId: string): Observable<ProcessTaskResponse> {
+    const body = {
+      submit_approve: 'true'
+    };
+    return this.postToEndpoint(this.linkPath, this.requestService.uriEncodeBody(body), scopeId, this.makeHttpOptions());
+  }
+
+  /**
+   * Make a request to reject the given task
+   *
+   * @param reason
+   *    The reason of reject
+   * @param scopeId
+   *    The task id
+   * @return {Observable<ProcessTaskResponse>}
+   *    Emit the server response
+   */
+  public rejectTask(reason: string, scopeId: string): Observable<ProcessTaskResponse> {
+    const body = {
+      submit_reject: 'true',
+      reason
+    };
+    return this.postToEndpoint(this.linkPath, this.requestService.uriEncodeBody(body), scopeId, this.makeHttpOptions());
+  }
+
+  /**
+   * Make a request to return the given task to the pool
+   *
+   * @param scopeId
+   *    The task id
+   * @return {Observable<ProcessTaskResponse>}
+   *    Emit the server response
+   */
+  public returnToPoolTask(scopeId: string): Observable<ProcessTaskResponse> {
+    return this.deleteById(this.linkPath, scopeId, this.makeHttpOptions());
+  }
+
+}
diff --git a/src/app/core/tasks/models/claimed-task-object.model.ts b/src/app/core/tasks/models/claimed-task-object.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..212e75ed95126495cb84cd450b957a00553011ec
--- /dev/null
+++ b/src/app/core/tasks/models/claimed-task-object.model.ts
@@ -0,0 +1,8 @@
+import { TaskObject } from './task-object.model';
+
+/**
+ * A model class for a ClaimedTask.
+ */
+export class ClaimedTask extends TaskObject {
+
+}
diff --git a/src/app/core/tasks/models/normalized-claimed-task-object.model.ts b/src/app/core/tasks/models/normalized-claimed-task-object.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c2c3f12bc48554e766e899721fed08d16e7af034
--- /dev/null
+++ b/src/app/core/tasks/models/normalized-claimed-task-object.model.ts
@@ -0,0 +1,39 @@
+import { NormalizedTaskObject } from './normalized-task-object.model';
+import { mapsTo, relationship } from '../../cache/builders/build-decorators';
+import { autoserialize, inheritSerialization } from 'cerialize';
+import { ClaimedTask } from './claimed-task-object.model';
+import { ResourceType } from '../../shared/resource-type';
+
+/**
+ * A normalized model class for a ClaimedTask.
+ */
+@mapsTo(ClaimedTask)
+@inheritSerialization(NormalizedTaskObject)
+export class NormalizedClaimedTask extends NormalizedTaskObject<ClaimedTask> {
+
+  /**
+   * The task identifier
+   */
+  @autoserialize
+  id: string;
+
+  /**
+   * The workflow step
+   */
+  @autoserialize
+  step: string;
+
+  /**
+   * The task action type
+   */
+  @autoserialize
+  action: string;
+
+  /**
+   * The workflowitem object whom this task is related
+   */
+  @autoserialize
+  @relationship(ResourceType.Workflowitem, false)
+  workflowitem: string;
+
+}
diff --git a/src/app/core/tasks/models/normalized-pool-task-object.model.ts b/src/app/core/tasks/models/normalized-pool-task-object.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..22cda6ff9cb6e77b0e60faea71c5919168864bb8
--- /dev/null
+++ b/src/app/core/tasks/models/normalized-pool-task-object.model.ts
@@ -0,0 +1,38 @@
+import { NormalizedTaskObject } from './normalized-task-object.model';
+import { PoolTask } from './pool-task-object.model';
+import { autoserialize, inheritSerialization } from 'cerialize';
+import { mapsTo, relationship } from '../../cache/builders/build-decorators';
+import { ResourceType } from '../../shared/resource-type';
+
+/**
+ * A normalized model class for a PoolTask.
+ */
+@mapsTo(PoolTask)
+@inheritSerialization(NormalizedTaskObject)
+export class NormalizedPoolTask extends NormalizedTaskObject<PoolTask> {
+
+  /**
+   * The task identifier
+   */
+  @autoserialize
+  id: string;
+
+  /**
+   * The workflow step
+   */
+  @autoserialize
+  step: string;
+
+  /**
+   * The task action type
+   */
+  @autoserialize
+  action: string;
+
+  /**
+   * The workflowitem object whom this task is related
+   */
+  @autoserialize
+  @relationship(ResourceType.Workflowitem, false)
+  workflowitem: string;
+}
diff --git a/src/app/core/tasks/models/normalized-task-object.model.ts b/src/app/core/tasks/models/normalized-task-object.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..52c274e3a8b14d5ccb46975456319dec5633e21a
--- /dev/null
+++ b/src/app/core/tasks/models/normalized-task-object.model.ts
@@ -0,0 +1,39 @@
+import { autoserialize, inheritSerialization } from 'cerialize';
+import { mapsTo, relationship } from '../../cache/builders/build-decorators';
+import { ResourceType } from '../../shared/resource-type';
+import { NormalizedDSpaceObject } from '../../cache/models/normalized-dspace-object.model';
+import { TaskObject } from './task-object.model';
+import { DSpaceObject } from '../../shared/dspace-object.model';
+
+/**
+ * An abstract normalized model class for a TaskObject.
+ */
+@mapsTo(TaskObject)
+@inheritSerialization(NormalizedDSpaceObject)
+export abstract class NormalizedTaskObject<T extends DSpaceObject> extends NormalizedDSpaceObject<T> {
+
+  /**
+   * The task identifier
+   */
+  @autoserialize
+  id: string;
+
+  /**
+   * The workflow step
+   */
+  @autoserialize
+  step: string;
+
+  /**
+   * The task action type
+   */
+  @autoserialize
+  action: string;
+
+  /**
+   * The workflowitem object whom this task is related
+   */
+  @autoserialize
+  @relationship(ResourceType.Workflowitem, false)
+  workflowitem: string;
+}
diff --git a/src/app/core/tasks/models/pool-task-object.model.ts b/src/app/core/tasks/models/pool-task-object.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8d98d3e1a5776e6ffce820d029d16e29476854d2
--- /dev/null
+++ b/src/app/core/tasks/models/pool-task-object.model.ts
@@ -0,0 +1,8 @@
+import { TaskObject } from './task-object.model';
+
+/**
+ * A model class for a PoolTask.
+ */
+export class PoolTask extends TaskObject {
+
+}
diff --git a/src/app/core/tasks/models/process-task-response.ts b/src/app/core/tasks/models/process-task-response.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ca4bc9a0682dc6e20b7ec316cce154c300749885
--- /dev/null
+++ b/src/app/core/tasks/models/process-task-response.ts
@@ -0,0 +1,17 @@
+import { RemoteDataError } from '../../data/remote-data-error';
+
+/**
+ * A class to represent the data retrieved by after processing a task
+ */
+export class ProcessTaskResponse {
+  constructor(
+    private isSuccessful: boolean,
+    public error?: RemoteDataError,
+    public payload?: any
+  ) {
+  }
+
+  get hasSucceeded(): boolean {
+    return this.isSuccessful;
+  }
+}
diff --git a/src/app/core/tasks/models/task-object.model.ts b/src/app/core/tasks/models/task-object.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..97a1c9f59e57ce456cc1fb89198222bca92b0ff8
--- /dev/null
+++ b/src/app/core/tasks/models/task-object.model.ts
@@ -0,0 +1,33 @@
+import { Observable } from 'rxjs';
+
+import { CacheableObject } from '../../cache/object-cache.reducer';
+import { DSpaceObject } from '../../shared/dspace-object.model';
+import { ListableObject } from '../../../shared/object-collection/shared/listable-object.model';
+import { RemoteData } from '../../data/remote-data';
+import { Workflowitem } from '../../submission/models/workflowitem.model';
+
+/**
+ * An abstract model class for a TaskObject.
+ */
+export class TaskObject extends DSpaceObject implements CacheableObject, ListableObject {
+
+  /**
+   * The task identifier
+   */
+  id: string;
+
+  /**
+   * The workflow step
+   */
+  step: string;
+
+  /**
+   * The task action type
+   */
+  action: string;
+
+  /**
+   * The workflowitem object whom this task is related
+   */
+  workflowitem: Observable<RemoteData<Workflowitem>> | Workflowitem;
+}
diff --git a/src/app/core/tasks/pool-task-data.service.spec.ts b/src/app/core/tasks/pool-task-data.service.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7f40c6e89c17be4d3b596db4cda75962ca965743
--- /dev/null
+++ b/src/app/core/tasks/pool-task-data.service.spec.ts
@@ -0,0 +1,70 @@
+import { HttpClient, HttpHeaders } from '@angular/common/http';
+
+import { Store } from '@ngrx/store';
+
+import { getMockRequestService } from '../../shared/mocks/mock-request.service';
+import { HALEndpointServiceStub } from '../../shared/testing/hal-endpoint-service-stub';
+import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
+import { NotificationsService } from '../../shared/notifications/notifications.service';
+import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service';
+import { CoreState } from '../core.reducers';
+import { PoolTaskDataService } from './pool-task-data.service';
+import { HttpOptions } from '../dspace-rest-v2/dspace-rest-v2.service';
+
+describe('PoolTaskDataService', () => {
+  let service: PoolTaskDataService;
+  let options: HttpOptions;
+  const taskEndpoint = 'https://rest.api/task';
+  const linkPath = 'pooltasks';
+  const requestService = getMockRequestService();
+  const halService: any = new HALEndpointServiceStub(taskEndpoint);
+  const rdbService = {} as RemoteDataBuildService;
+  const notificationsService = {} as NotificationsService;
+  const http = {} as HttpClient;
+  const comparator = {} as any;
+  const dataBuildService = {
+    normalize: (object) => object
+  } as NormalizedObjectBuildService;
+  const objectCache = {
+    addPatch: () => {
+      /* empty */
+    },
+    getObjectBySelfLink: () => {
+      /* empty */
+    }
+  } as any;
+  const store = {} as Store<CoreState>;
+
+  function initTestService(): PoolTaskDataService {
+    return new PoolTaskDataService(
+      requestService,
+      rdbService,
+      dataBuildService,
+      store,
+      objectCache,
+      halService,
+      notificationsService,
+      http,
+      comparator
+    );
+  }
+
+  beforeEach(() => {
+    service = initTestService();
+    options = Object.create({});
+    let headers = new HttpHeaders();
+    headers = headers.append('Content-Type', 'application/x-www-form-urlencoded');
+    options.headers = headers;
+  });
+
+  describe('claimTask', () => {
+
+    it('should call postToEndpoint method', () => {
+      spyOn(service, 'postToEndpoint');
+      const scopeId = '1234';
+      service.claimTask(scopeId);
+
+      expect(service.postToEndpoint).toHaveBeenCalledWith(linkPath, {}, scopeId, options);
+    });
+  });
+});
diff --git a/src/app/core/tasks/pool-task-data.service.ts b/src/app/core/tasks/pool-task-data.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1a93450d4d6cc4aaff19d5611a20984335e8da8d
--- /dev/null
+++ b/src/app/core/tasks/pool-task-data.service.ts
@@ -0,0 +1,72 @@
+import { Injectable } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+
+import { Observable } from 'rxjs';
+import { Store } from '@ngrx/store';
+
+import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
+import { CoreState } from '../core.reducers';
+import { RequestService } from '../data/request.service';
+import { PoolTask } from './models/pool-task-object.model';
+import { TasksService } from './tasks.service';
+import { HALEndpointService } from '../shared/hal-endpoint.service';
+import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service';
+import { ObjectCacheService } from '../cache/object-cache.service';
+import { NotificationsService } from '../../shared/notifications/notifications.service';
+import { DSOChangeAnalyzer } from '../data/dso-change-analyzer.service';
+import { ProcessTaskResponse } from './models/process-task-response';
+
+/**
+ * The service handling all REST requests for PoolTask
+ */
+@Injectable()
+export class PoolTaskDataService extends TasksService<PoolTask> {
+
+  /**
+   * The endpoint link name
+   */
+  protected linkPath = 'pooltasks';
+
+  /**
+   * When true, a new request is always dispatched
+   */
+  protected forceBypassCache = true;
+
+  /**
+   * Initialize instance variables
+   *
+   * @param {RequestService} requestService
+   * @param {RemoteDataBuildService} rdbService
+   * @param {NormalizedObjectBuildService} dataBuildService
+   * @param {Store<CoreState>} store
+   * @param {ObjectCacheService} objectCache
+   * @param {HALEndpointService} halService
+   * @param {NotificationsService} notificationsService
+   * @param {HttpClient} http
+   * @param {DSOChangeAnalyzer<ClaimedTask} comparator
+   */
+  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<PoolTask>) {
+    super();
+  }
+
+  /**
+   * Make a request to claim the given task
+   *
+   * @param scopeId
+   *    The task id
+   * @return {Observable<ProcessTaskResponse>}
+   *    Emit the server response
+   */
+  public claimTask(scopeId: string): Observable<ProcessTaskResponse> {
+    return this.postToEndpoint(this.linkPath, {}, scopeId, this.makeHttpOptions());
+  }
+}
diff --git a/src/app/core/tasks/task-response-parsing.service.ts b/src/app/core/tasks/task-response-parsing.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7445f9d26729556cfa9bd47f6fd263124918239a
--- /dev/null
+++ b/src/app/core/tasks/task-response-parsing.service.ts
@@ -0,0 +1,54 @@
+import { Inject, Injectable } from '@angular/core';
+
+import { ResponseParsingService } from '../data/parsing.service';
+import { RestRequest } from '../data/request.models';
+import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
+
+import { BaseResponseParsingService } from '../data/base-response-parsing.service';
+import { GLOBAL_CONFIG } from '../../../config';
+import { GlobalConfig } from '../../../config/global-config.interface';
+import { ObjectCacheService } from '../cache/object-cache.service';
+import { NormalizedObjectFactory } from '../cache/models/normalized-object-factory';
+import { ErrorResponse, RestResponse, TaskResponse } from '../cache/response.models';
+
+/**
+ * Provides methods to parse response for a task request.
+ */
+@Injectable()
+export class TaskResponseParsingService extends BaseResponseParsingService implements ResponseParsingService {
+
+  protected objectFactory = NormalizedObjectFactory;
+  protected toCache = false;
+
+  /**
+   * Initialize instance variables
+   *
+   * @param {GlobalConfig} EnvConfig
+   * @param {ObjectCacheService} objectCache
+   */
+  constructor(@Inject(GLOBAL_CONFIG) protected EnvConfig: GlobalConfig,
+              protected objectCache: ObjectCacheService,) {
+    super();
+  }
+
+  /**
+   * Parses data from the tasks endpoints
+   *
+   * @param {RestRequest} request
+   * @param {DSpaceRESTV2Response} data
+   * @returns {RestResponse}
+   */
+  parse(request: RestRequest, data: DSpaceRESTV2Response): RestResponse {
+    if (this.isSuccessStatus(data.statusCode)) {
+      return new TaskResponse( data.statusCode, data.statusText);
+    } else {
+      return new ErrorResponse(
+        Object.assign(
+          new Error('Unexpected response from server'),
+          { statusCode: data.statusCode, statusText: data.statusText }
+        )
+      );
+    }
+  }
+
+}
diff --git a/src/app/core/tasks/tasks.service.spec.ts b/src/app/core/tasks/tasks.service.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..753ce2ddd5c53f1e730f29eeceb6b9d615fc4cdb
--- /dev/null
+++ b/src/app/core/tasks/tasks.service.spec.ts
@@ -0,0 +1,130 @@
+import { getTestScheduler } from 'jasmine-marbles';
+import { TestScheduler } from 'rxjs/testing';
+
+import { getMockRequestService } from '../../shared/mocks/mock-request.service';
+import { TasksService } from './tasks.service';
+import { RequestService } from '../data/request.service';
+import { TaskDeleteRequest, TaskPostRequest } from '../data/request.models';
+import { HALEndpointService } from '../shared/hal-endpoint.service';
+import { HALEndpointServiceStub } from '../../shared/testing/hal-endpoint-service-stub';
+import { TaskObject } from './models/task-object.model';
+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 { NotificationsService } from '../../shared/notifications/notifications.service';
+import { HttpClient, HttpHeaders } from '@angular/common/http';
+import { DSOChangeAnalyzer } from '../data/dso-change-analyzer.service';
+import { ChangeAnalyzer } from '../data/change-analyzer';
+import { compare, Operation } from 'fast-json-patch';
+import { NormalizedTaskObject } from './models/normalized-task-object.model';
+import { HttpOptions } from '../dspace-rest-v2/dspace-rest-v2.service';
+
+const LINK_NAME = 'test';
+
+/* tslint:disable:max-classes-per-file */
+class TestTask extends TaskObject {
+}
+
+class TestService extends TasksService<TestTask> {
+  protected linkPath = LINK_NAME;
+  protected forceBypassCache = true;
+
+  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<TestTask>) {
+    super();
+  }
+}
+
+class NormalizedTestTaskObject extends NormalizedTaskObject<TestTask> {
+}
+
+class DummyChangeAnalyzer implements ChangeAnalyzer<NormalizedTestTaskObject> {
+  diff(object1: NormalizedTestTaskObject, object2: NormalizedTestTaskObject): Operation[] {
+    return compare((object1 as any).metadata, (object2 as any).metadata);
+  }
+
+}
+/* tslint:enable:max-classes-per-file */
+
+describe('TasksService', () => {
+  let scheduler: TestScheduler;
+  let service: TestService;
+  const taskEndpoint = 'https://rest.api/task';
+  const linkPath = 'testTask';
+  const requestService = getMockRequestService();
+  const halService: any = new HALEndpointServiceStub(taskEndpoint);
+  const rdbService = {} as RemoteDataBuildService;
+  const notificationsService = {} as NotificationsService;
+  const http = {} as HttpClient;
+  const comparator = new DummyChangeAnalyzer() as any;
+  const dataBuildService = {
+    normalize: (object) => object
+  } as NormalizedObjectBuildService;
+  const objectCache = {
+    addPatch: () => {
+      /* empty */
+    },
+    getObjectBySelfLink: () => {
+      /* empty */
+    }
+  } as any;
+  const store = {} as Store<CoreState>;
+
+  function initTestService(): TestService {
+    return new TestService(
+      requestService,
+      rdbService,
+      dataBuildService,
+      store,
+      objectCache,
+      halService,
+      notificationsService,
+      http,
+      comparator
+    );
+  }
+
+  beforeEach(() => {
+    scheduler = getTestScheduler();
+    service = initTestService();
+    const options: HttpOptions = Object.create({});
+    let headers = new HttpHeaders();
+    headers = headers.append('Content-Type', 'application/x-www-form-urlencoded');
+    options.headers = headers;
+
+  });
+
+  describe('postToEndpoint', () => {
+
+    it('should configure a new TaskPostRequest', () => {
+      const expected = new TaskPostRequest(requestService.generateRequestId(), `${taskEndpoint}/${linkPath}`, {});
+      scheduler.schedule(() => service.postToEndpoint('testTask', {}).subscribe());
+      scheduler.flush();
+
+      expect(requestService.configure).toHaveBeenCalledWith(expected);
+    });
+  });
+
+  describe('deleteById', () => {
+
+    it('should configure a new TaskDeleteRequest', () => {
+      const scopeId = '1234';
+      const expected = new TaskDeleteRequest(requestService.generateRequestId(), `${taskEndpoint}/${linkPath}/${scopeId}`, null);
+      scheduler.schedule(() => service.deleteById('testTask', scopeId).subscribe());
+      scheduler.flush();
+
+      expect(requestService.configure).toHaveBeenCalledWith(expected);
+    });
+  });
+
+});
diff --git a/src/app/core/tasks/tasks.service.ts b/src/app/core/tasks/tasks.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f39b144c6aab45a9c550d2c30a4d72a661021989
--- /dev/null
+++ b/src/app/core/tasks/tasks.service.ts
@@ -0,0 +1,125 @@
+import { HttpHeaders } from '@angular/common/http';
+
+import { merge as observableMerge, Observable, of as observableOf } from 'rxjs';
+import { distinctUntilChanged, filter, flatMap, map, mergeMap, tap } from 'rxjs/operators';
+
+import { DataService } from '../data/data.service';
+import { DeleteRequest, FindAllOptions, PostRequest, TaskDeleteRequest, TaskPostRequest } from '../data/request.models';
+import { isNotEmpty } from '../../shared/empty.util';
+import { HttpOptions } from '../dspace-rest-v2/dspace-rest-v2.service';
+import { ProcessTaskResponse } from './models/process-task-response';
+import { RemoteDataError } from '../data/remote-data-error';
+import { getResponseFromEntry } from '../shared/operators';
+import { ErrorResponse, MessageResponse, RestResponse } from '../cache/response.models';
+import { CacheableObject } from '../cache/object-cache.reducer';
+
+/**
+ * An abstract class that provides methods to handle task requests.
+ */
+export abstract class TasksService<T extends CacheableObject> extends DataService<T> {
+
+  public getBrowseEndpoint(options: FindAllOptions): Observable<string> {
+    return this.halService.getEndpoint(this.linkPath);
+  }
+
+  /**
+   * Fetch a RestRequest
+   *
+   * @param requestId
+   *    The base endpoint for the type of object
+   * @return Observable<ProcessTaskResponse>
+   *     server response
+   */
+  protected fetchRequest(requestId: string): Observable<ProcessTaskResponse> {
+    const responses = this.requestService.getByUUID(requestId).pipe(
+      getResponseFromEntry()
+    );
+    const errorResponses = responses.pipe(
+      filter((response: RestResponse) => !response.isSuccessful),
+      mergeMap((response: ErrorResponse) => observableOf(
+        new ProcessTaskResponse(
+          response.isSuccessful,
+          new RemoteDataError(response.statusCode, response.statusText, response.errorMessage)
+        ))
+      ));
+    const successResponses = responses.pipe(
+      filter((response: RestResponse) => response.isSuccessful),
+      map((response: MessageResponse) => new ProcessTaskResponse(response.isSuccessful)),
+      distinctUntilChanged()
+    );
+    return observableMerge(errorResponses, successResponses);
+  }
+
+  /**
+   * Create the HREF for a specific submission object based on its identifier
+   *
+   * @param endpoint
+   *    The base endpoint for the type of object
+   * @param resourceID
+   *    The identifier for the object
+   */
+  protected getEndpointByIDHref(endpoint, resourceID): string {
+    return isNotEmpty(resourceID) ? `${endpoint}/${resourceID}` : `${endpoint}`;
+  }
+
+  /**
+   * Make a new post request
+   *
+   * @param linkPath
+   *    The endpoint link name
+   * @param body
+   *    The request body
+   * @param scopeId
+   *    The task id to be removed
+   * @param options
+   *    The HttpOptions object
+   * @return Observable<SubmitDataResponseDefinitionObject>
+   *     server response
+   */
+  public postToEndpoint(linkPath: string, body: any, scopeId?: string, options?: HttpOptions): Observable<ProcessTaskResponse> {
+    const requestId = this.requestService.generateRequestId();
+    return this.halService.getEndpoint(linkPath).pipe(
+      filter((href: string) => isNotEmpty(href)),
+      map((endpointURL: string) => this.getEndpointByIDHref(endpointURL, scopeId)),
+      distinctUntilChanged(),
+      map((endpointURL: string) => new TaskPostRequest(requestId, endpointURL, body, options)),
+      tap((request: PostRequest) => this.requestService.configure(request)),
+      flatMap((request: PostRequest) => this.fetchRequest(requestId)),
+      distinctUntilChanged());
+  }
+
+  /**
+   * Delete an existing task on the server
+   *
+   * @param linkPath
+   *    The endpoint link name
+   * @param scopeId
+   *    The task id to be removed
+   * @param options
+   *    The HttpOptions object
+   * @return Observable<SubmitDataResponseDefinitionObject>
+   *     server response
+   */
+  public deleteById(linkPath: string, scopeId: string, options?: HttpOptions): Observable<ProcessTaskResponse> {
+    const requestId = this.requestService.generateRequestId();
+    return this.halService.getEndpoint(linkPath || this.linkPath).pipe(
+      filter((href: string) => isNotEmpty(href)),
+      distinctUntilChanged(),
+      map((endpointURL: string) => this.getEndpointByIDHref(endpointURL, scopeId)),
+      map((endpointURL: string) => new TaskDeleteRequest(requestId, endpointURL, null, options)),
+      tap((request: DeleteRequest) => this.requestService.configure(request)),
+      flatMap((request: DeleteRequest) => this.fetchRequest(requestId)),
+      distinctUntilChanged());
+  }
+
+  /**
+   * Create a new HttpOptions
+   */
+  protected makeHttpOptions() {
+    const options: HttpOptions = Object.create({});
+    let headers = new HttpHeaders();
+    headers = headers.append('Content-Type', 'application/x-www-form-urlencoded');
+    options.headers = headers;
+    return options;
+  }
+}
diff --git a/src/app/footer/footer.component.html b/src/app/footer/footer.component.html
index c7f41a07a3c4940447c12a9fa250b77083970568..fec75b2fd35a52a502405995c27eca8eff391fba 100644
--- a/src/app/footer/footer.component.html
+++ b/src/app/footer/footer.component.html
@@ -1,5 +1,6 @@
 <footer class="footer">
   <div class="container-fluid content-container-fluid">
+    <img src="assets/images/dspace-logo.png" />
     <p>
       <a href="http://www.dspace.org/">{{ 'footer.link.dspace' | translate}}</a>
       {{ 'footer.copyright' | translate:{year : dateObj | date:'y'} }}
diff --git a/src/app/footer/footer.component.scss b/src/app/footer/footer.component.scss
index fec6473f687d68ebdd6e80a5cf805d4d43ba9b3a..bd141706da1a6e8ce9128818009d375f908182a0 100644
--- a/src/app/footer/footer.component.scss
+++ b/src/app/footer/footer.component.scss
@@ -2,6 +2,7 @@
 $footer-bg: $gray-100;
 $footer-border: 1px solid darken($footer-bg, 10%);
 $footer-padding: $spacer * 1.5;
+$footer-logo-height: 55px;
 
 .footer {
   background-color: $footer-bg;
@@ -12,4 +13,7 @@ $footer-padding: $spacer * 1.5;
   p {
     margin: 0;
   }
+  img {
+    height: $footer-logo-height;
+  }
 }
diff --git a/src/app/header/header.component.html b/src/app/header/header.component.html
index 402eb7a44dcfacee6280840b9c9441e3556c3fe3..a03fd01c533919d34b63804290e639060fd2b0bb 100644
--- a/src/app/header/header.component.html
+++ b/src/app/header/header.component.html
@@ -5,7 +5,7 @@
         </a>
 
         <nav class="navbar navbar-light navbar-expand-md float-right px-0">
-            <a href="#" class="px-1"><i class="fas fa-search fa-lg fa-fw" [title]="'nav.search' | translate"></i></a>
+            <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">
diff --git a/src/app/navbar/navbar.component.ts b/src/app/navbar/navbar.component.ts
index 008a86599d8042e8d6394e31942923240f9212da..48b316af4bc04135a8fa7abdd38792296df0055a 100644
--- a/src/app/navbar/navbar.component.ts
+++ b/src/app/navbar/navbar.component.ts
@@ -115,7 +115,7 @@ export class NavbarComponent extends MenuComponent implements OnInit {
         model: {
           type: MenuItemType.LINK,
           text: 'menu.section.statistics',
-          link: '#'
+          link: ''
         } as LinkMenuItemModel,
         index: 2
       },
diff --git a/src/app/shared/auth-nav-menu/auth-nav-menu.component.html b/src/app/shared/auth-nav-menu/auth-nav-menu.component.html
index 97c3e393533c83f22e7af61711e6e6791ca6aff9..b560283ad50efcdfc49d90a925ae588119bd5d5d 100644
--- a/src/app/shared/auth-nav-menu/auth-nav-menu.component.html
+++ b/src/app/shared/auth-nav-menu/auth-nav-menu.component.html
@@ -13,10 +13,9 @@
   <li *ngIf="(isAuthenticated | async) && !(isXsOrSm$ | async) && (showAuth | async)" class="nav-item">
     <div ngbDropdown placement="bottom-right" class="d-inline-block" @fadeInOut>
       <a href="#" id="dropdownUser" (click)="$event.preventDefault()" class="px-1" ngbDropdownToggle><i class="fas fa-user-circle fa-lg fa-fw" [title]="'nav.logout' | translate"></i></a>
-      <ul id="logoutDropdownMenu" ngbDropdownMenu aria-labelledby="dropdownUser">
-        <li class="dropdown-item">{{(user | async).name}} ({{(user | async).email}})</li>
-        <li class="dropdown-item"><ds-log-out></ds-log-out></li>
-      </ul>
+      <div id="logoutDropdownMenu" ngbDropdownMenu aria-labelledby="dropdownUser">
+        <ds-user-menu></ds-user-menu>
+      </div>
     </div>
   </li>
   <li *ngIf="(isAuthenticated | async) && (isXsOrSm$ | async)" class="nav-item">
diff --git a/src/app/shared/auth-nav-menu/auth-nav-menu.component.spec.ts b/src/app/shared/auth-nav-menu/auth-nav-menu.component.spec.ts
index ff4948caa00fbc485ca1fd3103df845962e3fcac..5e01494674556eb6155e80a1fe67a4a365be3ca4 100644
--- a/src/app/shared/auth-nav-menu/auth-nav-menu.component.spec.ts
+++ b/src/app/shared/auth-nav-menu/auth-nav-menu.component.spec.ts
@@ -228,8 +228,8 @@ describe('AuthNavMenuComponent', () => {
           fixture.destroy();
           component = null;
         });
-        it('should render logout dropdown menu', () => {
-          const logoutDropdownMenu = deNavMenuItem.query(By.css('ul[id=logoutDropdownMenu]'));
+        it('should render UserMenuComponent component', () => {
+          const logoutDropdownMenu = deNavMenuItem.query(By.css('ds-user-menu'));
           expect(logoutDropdownMenu.nativeElement).toBeDefined();
         });
       })
diff --git a/src/app/shared/auth-nav-menu/user-menu/user-menu.component.html b/src/app/shared/auth-nav-menu/user-menu/user-menu.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..fef47b395b94652910bbfbaccffe2d873eb32a14
--- /dev/null
+++ b/src/app/shared/auth-nav-menu/user-menu/user-menu.component.html
@@ -0,0 +1,9 @@
+<ds-loading *ngIf="(loading$ | async)"></ds-loading>
+<div *ngIf="!(loading$ | async)">
+  <span class="dropdown-item-text">{{(user$ | async)?.name}} ({{(user$ | async)?.email}})</span>
+  <a class="dropdown-item" [routerLink]="[mydspaceRoute]" routerLinkActive="active">{{'nav.mydspace' | translate}}</a>
+  <div class="dropdown-divider"></div>
+  <ds-log-out></ds-log-out>
+</div>
+
+
diff --git a/src/app/shared/auth-nav-menu/user-menu/user-menu.component.scss b/src/app/shared/auth-nav-menu/user-menu/user-menu.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/app/shared/auth-nav-menu/user-menu/user-menu.component.spec.ts b/src/app/shared/auth-nav-menu/user-menu/user-menu.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..512d9e0917295534b5e0ac0d398aca877b90ab0b
--- /dev/null
+++ b/src/app/shared/auth-nav-menu/user-menu/user-menu.component.spec.ts
@@ -0,0 +1,151 @@
+import { DebugElement, NO_ERRORS_SCHEMA } from '@angular/core';
+import { async, ComponentFixture, inject, TestBed } from '@angular/core/testing';
+
+import { Store, StoreModule } from '@ngrx/store';
+import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
+
+import { UserMenuComponent } from './user-menu.component';
+import { authReducer, AuthState } from '../../../core/auth/auth.reducer';
+import { AuthTokenInfo } from '../../../core/auth/models/auth-token-info.model';
+import { EPersonMock } from '../../testing/eperson-mock';
+import { AppState } from '../../../app.reducer';
+import { MockTranslateLoader } from '../../mocks/mock-translate-loader';
+import { cold } from 'jasmine-marbles';
+import { By } from '@angular/platform-browser';
+
+describe('UserMenuComponent', () => {
+
+  let component: UserMenuComponent;
+  let fixture: ComponentFixture<UserMenuComponent>;
+  let deUserMenu: DebugElement;
+  let authState: AuthState;
+  let authStateLoading: AuthState;
+
+  function init() {
+    authState = {
+      authenticated: true,
+      loaded: true,
+      loading: false,
+      authToken: new AuthTokenInfo('test_token'),
+      user: EPersonMock
+    };
+    authStateLoading = {
+      authenticated: true,
+      loaded: true,
+      loading: true,
+      authToken: null,
+      user: EPersonMock
+    };
+  }
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [
+        StoreModule.forRoot(authReducer),
+        TranslateModule.forRoot({
+          loader: {
+            provide: TranslateLoader,
+            useClass: MockTranslateLoader
+          }
+        })
+      ],
+      declarations: [
+        UserMenuComponent
+      ],
+      schemas: [
+        NO_ERRORS_SCHEMA
+      ]
+    }).compileComponents();
+
+  }));
+
+  beforeEach(() => {
+    init();
+  });
+
+  describe('when auth state is loading', () => {
+    beforeEach(inject([Store], (store: Store<AppState>) => {
+      store
+        .subscribe((state) => {
+          (state as any).core = Object.create({});
+          (state as any).core.auth = authStateLoading;
+        });
+
+      // create component and test fixture
+      fixture = TestBed.createComponent(UserMenuComponent);
+
+      // get test component from the fixture
+      component = fixture.componentInstance;
+
+      fixture.detectChanges();
+
+      deUserMenu = fixture.debugElement.query(By.css('div'));
+    }));
+
+    afterEach(() => {
+      fixture.destroy();
+    });
+
+    it('should init component properly', () => {
+      expect(component).toBeDefined();
+
+      expect(component.loading$).toBeObservable(cold('b', {
+        b: true
+      }));
+
+      expect(component.user$).toBeObservable(cold('c', {
+        c: EPersonMock
+      }));
+
+      expect(deUserMenu).toBeNull();
+    });
+
+  });
+
+  describe('when auth state is not loading', () => {
+    beforeEach(inject([Store], (store: Store<AppState>) => {
+      store
+        .subscribe((state) => {
+          (state as any).core = Object.create({});
+          (state as any).core.auth = authState;
+        });
+
+      // create component and test fixture
+      fixture = TestBed.createComponent(UserMenuComponent);
+
+      // get test component from the fixture
+      component = fixture.componentInstance;
+
+      fixture.detectChanges();
+
+      deUserMenu = fixture.debugElement.query(By.css('div'));
+    }));
+
+    afterEach(() => {
+      fixture.destroy();
+    });
+
+    it('should init component properly', () => {
+      expect(component).toBeDefined();
+
+      expect(component.loading$).toBeObservable(cold('b', {
+        b: false
+      }));
+
+      expect(component.user$).toBeObservable(cold('c', {
+        c: EPersonMock
+      }));
+
+      expect(deUserMenu).toBeDefined();
+    });
+
+    it('should display user name and email', () =>  {
+      const user = 'User Test (test@test.com)';
+      const span = deUserMenu.query(By.css('.dropdown-item-text'));
+      expect(span).toBeDefined();
+      expect(span.nativeElement.innerHTML).toBe(user);
+    })
+
+  });
+
+});
diff --git a/src/app/shared/auth-nav-menu/user-menu/user-menu.component.ts b/src/app/shared/auth-nav-menu/user-menu/user-menu.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e3c21b4e2487ad290a080e785bd17ddab0e8c7b4
--- /dev/null
+++ b/src/app/shared/auth-nav-menu/user-menu/user-menu.component.ts
@@ -0,0 +1,54 @@
+import { Component, OnInit } from '@angular/core';
+
+import { Observable } from 'rxjs';
+import { select, Store } from '@ngrx/store';
+
+import { EPerson } from '../../../core/eperson/models/eperson.model';
+import { AppState } from '../../../app.reducer';
+import { getAuthenticatedUser, isAuthenticationLoading } from '../../../core/auth/selectors';
+import { MYDSPACE_ROUTE } from '../../../+my-dspace-page/my-dspace-page.component';
+
+/**
+ * This component represents the user nav menu.
+ */
+@Component({
+  selector: 'ds-user-menu',
+  templateUrl: './user-menu.component.html',
+  styleUrls: ['./user-menu.component.scss']
+})
+export class UserMenuComponent implements OnInit {
+
+  /**
+   * True if the authentication is loading.
+   * @type {Observable<boolean>}
+   */
+  public loading$: Observable<boolean>;
+
+  /**
+   * The authenticated user.
+   * @type {Observable<EPerson>}
+   */
+  public user$: Observable<EPerson>;
+
+  /**
+   * The mydspace page route.
+   * @type {string}
+   */
+  public mydspaceRoute = MYDSPACE_ROUTE;
+
+  constructor(private store: Store<AppState>) {
+  }
+
+  /**
+   * Initialize all instance variables
+   */
+  ngOnInit(): void {
+
+    // set loading
+    this.loading$ = this.store.pipe(select(isAuthenticationLoading));
+
+    // set user
+    this.user$ = this.store.pipe(select(getAuthenticatedUser));
+
+  }
+}
diff --git a/src/app/shared/dso-selector/dso-selector/dso-selector.component.html b/src/app/shared/dso-selector/dso-selector/dso-selector.component.html
index 1e0deed4b908d54f5fd75e1d661c59bf64796731..662144823d104a3c3ce2912f550bb18685397d6c 100644
--- a/src/app/shared/dso-selector/dso-selector/dso-selector.component.html
+++ b/src/app/shared/dso-selector/dso-selector/dso-selector.component.html
@@ -13,8 +13,8 @@
     </button>
     <button *ngFor="let listEntry of (listEntries$ | async)?.payload.page"
             class="list-group-item list-group-item-action border-0 list-entry"
-            title="{{ listEntry.dspaceObject.name }}"
-            (click)="onSelect.emit(listEntry.dspaceObject)" #listEntryElement>
+            title="{{ listEntry.indexableObject.name }}"
+            (click)="onSelect.emit(listEntry.indexableObject)" #listEntryElement>
         <ds-wrapper-list-element [object]="listEntry"></ds-wrapper-list-element>
     </button>
-</div>
\ No newline at end of file
+</div>
diff --git a/src/app/shared/dso-selector/dso-selector/dso-selector.component.spec.ts b/src/app/shared/dso-selector/dso-selector/dso-selector.component.spec.ts
index 04111a4ea6e9aec980efa873430e5c6e0b8a739c..5ec553222b1c47bf01664828991ea67f3b24c75b 100644
--- a/src/app/shared/dso-selector/dso-selector/dso-selector.component.spec.ts
+++ b/src/app/shared/dso-selector/dso-selector/dso-selector.component.spec.ts
@@ -1,4 +1,4 @@
-import { async, ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 import { TranslateModule } from '@ngx-translate/core';
 import { DebugElement, NO_ERRORS_SCHEMA } from '@angular/core';
 import { DSOSelectorComponent } from './dso-selector.component';
@@ -27,7 +27,7 @@ describe('DSOSelectorComponent', () => {
       language: undefined
     })]
   };
-  searchResult.dspaceObject = item;
+  searchResult.indexableObject = item;
   searchResult.hitHighlights = {};
   const searchService = jasmine.createSpyObj('searchService', {
     search: observableOf(new RemoteData(false, false, true, undefined, new PaginatedList(undefined, [searchResult])))
diff --git a/src/app/shared/dso-selector/modal-wrappers/create-collection-parent-selector/create-collection-parent-selector.component.ts b/src/app/shared/dso-selector/modal-wrappers/create-collection-parent-selector/create-collection-parent-selector.component.ts
index 1e129c0dbedd363ef2fa9d5a2eff047d1470521c..0533addb0128242200c3890d0d109785b36e58f3 100644
--- a/src/app/shared/dso-selector/modal-wrappers/create-collection-parent-selector/create-collection-parent-selector.component.ts
+++ b/src/app/shared/dso-selector/modal-wrappers/create-collection-parent-selector/create-collection-parent-selector.component.ts
@@ -1,7 +1,5 @@
-import { Component, Input, OnInit } from '@angular/core';
+import { Component, OnInit } from '@angular/core';
 import { ActivatedRoute, NavigationExtras, Router } from '@angular/router';
-import { Community } from '../../../../core/shared/community.model';
-import { RemoteData } from '../../../../core/data/remote-data';
 import { DSpaceObjectType } from '../../../../core/shared/dspace-object-type.model';
 import { DSpaceObject } from '../../../../core/shared/dspace-object.model';
 import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
@@ -9,10 +7,7 @@ import {
   COLLECTION_PARENT_PARAMETER,
   getCollectionCreatePath
 } from '../../../../+collection-page/collection-page-routing.module';
-import {
-  DSOSelectorModalWrapperComponent,
-  SelectorActionType
-} from '../dso-selector-modal-wrapper.component';
+import { DSOSelectorModalWrapperComponent, SelectorActionType } from '../dso-selector-modal-wrapper.component';
 
 /**
  * Component to wrap a list of existing communities inside a modal
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 dac5888bf7ff83de029dfc21a1980d9fb573ddc4..29af9f624e0a306976fc4d4347182e5889b3ca7e 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,18 +1,9 @@
-import { Component, Input, OnInit } from '@angular/core';
+import { Component, OnInit } from '@angular/core';
 import { ActivatedRoute, Router } from '@angular/router';
-import { Community } from '../../../../core/shared/community.model';
-import { RemoteData } from '../../../../core/data/remote-data';
-import { Collection } from '../../../../core/shared/collection.model';
 import { DSpaceObjectType } from '../../../../core/shared/dspace-object-type.model';
 import { DSpaceObject } from '../../../../core/shared/dspace-object.model';
-import { hasValue } from '../../../empty.util';
 import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
-import { map } from 'rxjs/operators';
-import { Observable } from 'rxjs';
-import {
-  DSOSelectorModalWrapperComponent,
-  SelectorActionType
-} from '../dso-selector-modal-wrapper.component';
+import { DSOSelectorModalWrapperComponent, SelectorActionType } from '../dso-selector-modal-wrapper.component';
 
 /**
  * Component to wrap a list of existing collections inside a modal
diff --git a/src/app/shared/dso-selector/modal-wrappers/dso-selector-modal-wrapper.component.spec.ts b/src/app/shared/dso-selector/modal-wrappers/dso-selector-modal-wrapper.component.spec.ts
index ea857f7d62adf91d19599f7c67d199fc38f93a66..4ceaeccb3a3053558a77077708dc9bfbee3d82e0 100644
--- a/src/app/shared/dso-selector/modal-wrappers/dso-selector-modal-wrapper.component.spec.ts
+++ b/src/app/shared/dso-selector/modal-wrappers/dso-selector-modal-wrapper.component.spec.ts
@@ -5,10 +5,7 @@ import { DSpaceObjectType } from '../../../core/shared/dspace-object-type.model'
 import { RemoteData } from '../../../core/data/remote-data';
 import { Item } from '../../../core/shared/item.model';
 import { of as observableOf } from 'rxjs';
-import {
-  DSOSelectorModalWrapperComponent,
-  SelectorActionType
-} from './dso-selector-modal-wrapper.component';
+import { DSOSelectorModalWrapperComponent, SelectorActionType } from './dso-selector-modal-wrapper.component';
 import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
 import { ActivatedRoute } from '@angular/router';
 import { DSpaceObject } from '../../../core/shared/dspace-object.model';
@@ -16,7 +13,7 @@ import { first } from 'rxjs/operators';
 import { By } from '@angular/platform-browser';
 import { DSOSelectorComponent } from '../dso-selector/dso-selector.component';
 import { MockComponent } from 'ng-mocks';
-import { MetadataMap, MetadataValue } from '../../../core/shared/metadata.models';
+import { MetadataValue } from '../../../core/shared/metadata.models';
 
 describe('DSOSelectorModalWrapperComponent', () => {
   let component: DSOSelectorModalWrapperComponent;
diff --git a/src/app/shared/dso-selector/modal-wrappers/dso-selector-modal-wrapper.component.ts b/src/app/shared/dso-selector/modal-wrappers/dso-selector-modal-wrapper.component.ts
index 351a92302c308e9ca3125c4b11721703194c3fcd..881476cac6e088be2728952064db318c8d1d6ac5 100644
--- a/src/app/shared/dso-selector/modal-wrappers/dso-selector-modal-wrapper.component.ts
+++ b/src/app/shared/dso-selector/modal-wrappers/dso-selector-modal-wrapper.component.ts
@@ -1,4 +1,4 @@
-import { Component, Injectable, Input, OnInit } from '@angular/core';
+import { Injectable, Input, OnInit } from '@angular/core';
 import { ActivatedRoute } from '@angular/router';
 import { Observable } from 'rxjs';
 import { DSpaceObject } from '../../../core/shared/dspace-object.model';
diff --git a/src/app/shared/dso-selector/modal-wrappers/edit-item-selector/edit-item-selector.component.ts b/src/app/shared/dso-selector/modal-wrappers/edit-item-selector/edit-item-selector.component.ts
index 9182df80452ffe118d37ccb0c22c8825a8d1e21d..dae36d3017d705937ea20eb19d1e4f1a271b44c8 100644
--- a/src/app/shared/dso-selector/modal-wrappers/edit-item-selector/edit-item-selector.component.ts
+++ b/src/app/shared/dso-selector/modal-wrappers/edit-item-selector/edit-item-selector.component.ts
@@ -1,19 +1,10 @@
-import { Component, Input, OnInit } from '@angular/core';
-import { ActivatedRoute, NavigationExtras, Router } from '@angular/router';
-import { Community } from '../../../../core/shared/community.model';
-import { RemoteData } from '../../../../core/data/remote-data';
+import { Component, OnInit } from '@angular/core';
+import { ActivatedRoute, 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';
-import { Collection } from '../../../../core/shared/collection.model';
-import { Observable } from 'rxjs';
-import { map } from 'rxjs/operators';
-import { Item } from '../../../../core/shared/item.model';
 import { getItemEditPath } from '../../../../+item-page/item-page-routing.module';
-import {
-  DSOSelectorModalWrapperComponent,
-  SelectorActionType
-} from '../dso-selector-modal-wrapper.component';
+import { DSOSelectorModalWrapperComponent, SelectorActionType } from '../dso-selector-modal-wrapper.component';
 
 /**
  * Component to wrap a list of existing items inside a modal
diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/tag/dynamic-tag.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/tag/dynamic-tag.component.ts
index d61134347ac7ef83971a5e2098c3c8c705607a2e..a44a20d4bd6fad264b6db0a0266d474e41fe2415 100644
--- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/tag/dynamic-tag.component.ts
+++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/tag/dynamic-tag.component.ts
@@ -168,7 +168,7 @@ export class DsDynamicTagComponent extends DynamicFormControlComponent implement
   }
 
   private addTagsToChips() {
-    if (!this.hasAuthority || !this.model.authorityOptions.closed) {
+    if (hasValue(this.currentValue) && (!this.hasAuthority || !this.model.authorityOptions.closed)) {
       let res: string[] = [];
       res = this.currentValue.split(',');
 
diff --git a/src/app/shared/form/form.component.spec.ts b/src/app/shared/form/form.component.spec.ts
index 4107586e7bff5eaf7ec693d6c76d481ad3328c77..669c416eb901a3017342265a2ed73a038aa9f449 100644
--- a/src/app/shared/form/form.component.spec.ts
+++ b/src/app/shared/form/form.component.spec.ts
@@ -23,7 +23,7 @@ import { MockStore } from '../testing/mock-store';
 import { FormFieldMetadataValueObject } from './builder/models/form-field-metadata-value.model';
 import { GLOBAL_CONFIG } from '../../../config';
 import { createTestComponent } from '../testing/utils';
-import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject';
+import { BehaviorSubject } from 'rxjs';
 
 let TEST_FORM_MODEL;
 
diff --git a/src/app/shared/input-suggestions/input-suggestions.component.ts b/src/app/shared/input-suggestions/input-suggestions.component.ts
index 727421c83ec0e5fe3f1c962c9a9b2e430db7672b..9f59f42cc411731f5fd3d6597700d765feb40d31 100644
--- a/src/app/shared/input-suggestions/input-suggestions.component.ts
+++ b/src/app/shared/input-suggestions/input-suggestions.component.ts
@@ -12,7 +12,7 @@ import {
   ViewChildren
 } from '@angular/core';
 import { BehaviorSubject } from 'rxjs';
-import { hasValue, isNotEmpty, isNotUndefined } from '../empty.util';
+import { hasValue, isNotEmpty } from '../empty.util';
 import { InputSuggestion } from './input-suggestions.model';
 import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
 
diff --git a/src/app/shared/items/item-type-decorator.ts b/src/app/shared/items/item-type-decorator.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2420e719087a7b876bb0745a7de3f2143f9e548c
--- /dev/null
+++ b/src/app/shared/items/item-type-decorator.ts
@@ -0,0 +1,65 @@
+import { hasNoValue, hasValue } from '../empty.util';
+import { MetadataRepresentationType } from '../../core/shared/metadata-representation/metadata-representation.model';
+
+export enum ItemViewMode {
+  Element = 'element',
+  Full = 'full',
+  Metadata = 'metadata'
+}
+
+export const DEFAULT_ITEM_TYPE = 'Default';
+export const DEFAULT_VIEW_MODE = ItemViewMode.Element;
+export const NO_REPRESENTATION_TYPE = MetadataRepresentationType.None;
+export const DEFAULT_REPRESENTATION_TYPE = MetadataRepresentationType.PlainText;
+
+const map = new Map();
+
+/**
+ * Decorator used for rendering simple item pages by type and viewMode (and optionally a representationType)
+ * @param type
+ * @param viewMode
+ * @param representationType
+ */
+export function rendersItemType(type: string, viewMode: string, representationType?: MetadataRepresentationType) {
+  return function decorator(component: any) {
+    if (hasNoValue(map.get(viewMode))) {
+      map.set(viewMode, new Map());
+    }
+    if (hasNoValue(map.get(viewMode).get(type))) {
+      map.get(viewMode).set(type, new Map());
+    }
+    if (hasNoValue(representationType)) {
+      representationType = NO_REPRESENTATION_TYPE;
+    }
+    if (hasValue(map.get(viewMode).get(type).get(representationType))) {
+      throw new Error(`There can't be more than one component to render Metadata of type "${type}" in view mode "${viewMode}" with representation type "${representationType}"`);
+    }
+    map.get(viewMode).get(type).set(representationType, component);
+  };
+}
+
+/**
+ * Get the component used for rendering an item by type and viewMode (and optionally a representationType)
+ * @param type
+ * @param viewMode
+ * @param representationType
+ */
+export function getComponentByItemType(type: string, viewMode: string, representationType?: MetadataRepresentationType) {
+  if (hasNoValue(representationType)) {
+    representationType = NO_REPRESENTATION_TYPE;
+  }
+  if (hasNoValue(map.get(viewMode))) {
+    viewMode = DEFAULT_VIEW_MODE;
+  }
+  if (hasNoValue(map.get(viewMode).get(type))) {
+    type = DEFAULT_ITEM_TYPE;
+  }
+  let representationComponent = map.get(viewMode).get(type).get(representationType);
+  if (hasNoValue(representationComponent)) {
+    representationComponent = map.get(viewMode).get(type).get(DEFAULT_REPRESENTATION_TYPE);
+  }
+  if (hasNoValue(representationComponent)) {
+    representationComponent = map.get(viewMode).get(type).get(NO_REPRESENTATION_TYPE);
+  }
+  return representationComponent;
+}
diff --git a/src/app/shared/items/switcher/item-type-switcher.component.html b/src/app/shared/items/switcher/item-type-switcher.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..496535949583487659ecad5381b548de2514657e
--- /dev/null
+++ b/src/app/shared/items/switcher/item-type-switcher.component.html
@@ -0,0 +1 @@
+<ng-container *ngComponentOutlet="getComponent(); injector: objectInjector;"></ng-container>
diff --git a/src/app/shared/items/switcher/item-type-switcher.component.scss b/src/app/shared/items/switcher/item-type-switcher.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..45a533cd01d15e25526c4e8a9b39d083b00936a8
--- /dev/null
+++ b/src/app/shared/items/switcher/item-type-switcher.component.scss
@@ -0,0 +1 @@
+@import '../../../../styles/variables';
diff --git a/src/app/shared/items/switcher/item-type-switcher.component.spec.ts b/src/app/shared/items/switcher/item-type-switcher.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3b13abf2efca8bc1e787af1de5e3c3e06c2678e6
--- /dev/null
+++ b/src/app/shared/items/switcher/item-type-switcher.component.spec.ts
@@ -0,0 +1,90 @@
+import { ItemTypeSwitcherComponent } from './item-type-switcher.component';
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { NO_ERRORS_SCHEMA } from '@angular/core';
+import { of as observableOf } from 'rxjs';
+import { PageInfo } from '../../../core/shared/page-info.model';
+import { Item } from '../../../core/shared/item.model';
+import { PaginatedList } from '../../../core/data/paginated-list';
+import { RemoteData } from '../../../core/data/remote-data';
+import * as decorator from '../item-type-decorator';
+import { getComponentByItemType, ItemViewMode } from '../item-type-decorator';
+import { ItemMetadataRepresentation } from '../../../core/shared/metadata-representation/item/item-metadata-representation.model';
+import createSpy = jasmine.createSpy;
+
+const relationType = 'type';
+const mockItem: Item = Object.assign(new Item(), {
+  bitstreams: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))),
+  metadata: {
+    'dc.title': [
+      {
+        language: 'en_US',
+        value: 'test item'
+      }
+    ],
+    'relationship.type': [
+      {
+        language: 'en_US',
+        value: relationType
+      }
+    ]
+  }
+});
+const mockItemMetadataRepresentation = Object.assign(new ItemMetadataRepresentation(), mockItem);
+let viewMode = ItemViewMode.Full;
+
+describe('ItemTypeSwitcherComponent', () => {
+  let comp: ItemTypeSwitcherComponent;
+  let fixture: ComponentFixture<ItemTypeSwitcherComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ ItemTypeSwitcherComponent ],
+      schemas: [ NO_ERRORS_SCHEMA ]
+    }).compileComponents();  // compile template and css
+  }));
+
+  beforeEach(async(() => {
+    fixture = TestBed.createComponent(ItemTypeSwitcherComponent);
+    comp = fixture.componentInstance;
+    comp.object = mockItem;
+    comp.viewMode = viewMode;
+    spyOnProperty(decorator, 'getComponentByItemType').and.returnValue(createSpy('getComponentByItemType'))
+  }));
+
+  describe('when the injected object is of type Item', () => {
+    beforeEach(() => {
+      viewMode = ItemViewMode.Full;
+      comp.object = mockItem;
+      comp.viewMode = viewMode;
+    });
+
+    describe('when calling getComponent', () => {
+      beforeEach(() => {
+        comp.getComponent();
+      });
+
+      it('should call getComponentByItemType with parameters type and viewMode', () => {
+        expect(decorator.getComponentByItemType).toHaveBeenCalledWith(relationType, viewMode);
+      });
+    });
+  });
+
+  describe('when the injected object is of type MetadataRepresentation', () => {
+    beforeEach(() => {
+      viewMode = ItemViewMode.Metadata;
+      comp.object = mockItemMetadataRepresentation;
+      comp.viewMode = viewMode;
+    });
+
+    describe('when calling getComponent', () => {
+      beforeEach(() => {
+        comp.getComponent();
+      });
+
+      it('should call getComponentByItemType with parameters type, viewMode and representationType', () => {
+        expect(decorator.getComponentByItemType).toHaveBeenCalledWith(relationType, viewMode, mockItemMetadataRepresentation.representationType);
+      });
+    });
+  });
+
+});
diff --git a/src/app/shared/items/switcher/item-type-switcher.component.ts b/src/app/shared/items/switcher/item-type-switcher.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..21a045b8f460ac83f53678696e0c1f0956edb86e
--- /dev/null
+++ b/src/app/shared/items/switcher/item-type-switcher.component.ts
@@ -0,0 +1,67 @@
+import { Component, InjectionToken, Injector, Input, OnInit } from '@angular/core';
+import { SearchResult } from '../../../+search-page/search-result.model';
+import { Item } from '../../../core/shared/item.model';
+import { hasValue } from '../../empty.util';
+import { ItemSearchResult } from '../../object-collection/shared/item-search-result.model';
+import { getComponentByItemType } from '../item-type-decorator';
+import { MetadataRepresentation } from '../../../core/shared/metadata-representation/metadata-representation.model';
+
+export const ITEM: InjectionToken<string> = new InjectionToken<string>('item');
+
+@Component({
+  selector: 'ds-item-type-switcher',
+  styleUrls: ['./item-type-switcher.component.scss'],
+  templateUrl: './item-type-switcher.component.html'
+})
+/**
+ * Component for determining what component to use depending on the item's relationship type (relationship.type)
+ */
+export class ItemTypeSwitcherComponent implements OnInit {
+  /**
+   * The item or metadata to determine the component for
+   */
+  @Input() object: Item | SearchResult<Item> | MetadataRepresentation;
+
+  /**
+   * The preferred view-mode to display
+   */
+  @Input() viewMode: string;
+
+  /**
+   * The object injector used to inject the item into the child component
+   */
+  objectInjector: Injector;
+
+  constructor(private injector: Injector) {
+  }
+
+  ngOnInit(): void {
+    this.objectInjector = Injector.create({
+      providers: [{ provide: ITEM, useFactory: () => this.object, deps:[] }],
+      parent: this.injector
+    });
+
+  }
+
+  /**
+   * Fetch the component depending on the item's relationship type
+   * @returns {string}
+   */
+  getComponent(): string {
+    if (hasValue((this.object as any).representationType)) {
+      const metadataRepresentation = this.object as MetadataRepresentation;
+      return getComponentByItemType(metadataRepresentation.itemType, this.viewMode, metadataRepresentation.representationType);
+    }
+
+    let item: Item;
+    if (hasValue((this.object as any).indexableObject)) {
+      const searchResult = this.object as ItemSearchResult;
+      item = searchResult.indexableObject;
+    } else {
+      item = this.object as Item;
+    }
+
+    const type = item.firstMetadataValue('relationship.type');
+    return getComponentByItemType(type, this.viewMode);
+  }
+}
diff --git a/src/app/shared/log-out/log-out.component.html b/src/app/shared/log-out/log-out.component.html
index f3ceae00877f3453b60de4d7676ab80fee96b81e..d522fc6fb94bccf2ebda267fc96f3fc6919249c9 100644
--- a/src/app/shared/log-out/log-out.component.html
+++ b/src/app/shared/log-out/log-out.component.html
@@ -1,7 +1,6 @@
-<ds-loading *ngIf="(loading | async)"></ds-loading>
-<div *ngIf="!(loading | async)" class="form-login px-4 py-3">
+<div class="form-login px-4">
 
-  <div *ngIf="(error | async) && hasError" class="alert alert-danger" role="alert" @fadeOut>{{ error | async }}</div>
+  <div *ngIf="(error | async)" class="alert alert-danger" role="alert" @fadeOut>{{ error | async }}</div>
 
   <button class="btn btn-lg btn-primary btn-block mt-3" (click)="logOut()">{{"logout.form.submit" | translate}}</button>
 </div>
diff --git a/src/app/shared/log-out/log-out.component.ts b/src/app/shared/log-out/log-out.component.ts
index 9e8e7f78652a49039dde090461369f85603db202..6fa71caa32832b379855ba5d21fc876cee2602ce 100644
--- a/src/app/shared/log-out/log-out.component.ts
+++ b/src/app/shared/log-out/log-out.component.ts
@@ -1,21 +1,12 @@
-import { Component, OnDestroy, OnInit } from '@angular/core';
+import { Component, OnInit } from '@angular/core';
 import { Router } from '@angular/router';
 
-// @ngrx
+import { Observable } from 'rxjs';
 import { select, Store } from '@ngrx/store';
 
-// actions
 import { LogOutAction } from '../../core/auth/auth.actions';
-
-// reducers
-import {
-  getLogOutError,
-  isAuthenticated,
-  isAuthenticationLoading,
-} from '../../core/auth/selectors';
-
+import { getLogOutError, } from '../../core/auth/selectors';
 import { AppState } from '../../app.reducer';
-import { Observable } from 'rxjs';
 import { fadeOut } from '../animations/fade';
 
 @Component({
@@ -24,49 +15,28 @@ import { fadeOut } from '../animations/fade';
   styleUrls: ['./log-out.component.scss'],
   animations: [fadeOut]
 })
-export class LogOutComponent implements OnDestroy, OnInit {
+export class LogOutComponent implements OnInit {
   /**
    * The error if authentication fails.
    * @type {Observable<string>}
    */
   public error: Observable<string>;
 
-  /**
-   * True if the logout is loading.
-   * @type {boolean}
-   */
-  public loading: Observable<boolean>;
-
-  /**
-   * Component state.
-   * @type {boolean}
-   */
-  private alive = true;
-
   /**
    * @constructor
    * @param {Store<State>} store
+   * @param {Router} router
    */
   constructor(private router: Router,
               private store: Store<AppState>) {
   }
 
-  /**
-   *  Lifecycle hook that is called when a directive, pipe or service is destroyed.
-   */
-  public ngOnDestroy() {
-    this.alive = false;
-  }
-
   /**
    * Lifecycle hook that is called after data-bound properties of a directive are initialized.
    */
   ngOnInit() {
     // set error
     this.error = this.store.pipe(select(getLogOutError));
-
-    // set loading
-    this.loading = this.store.pipe(select(isAuthenticationLoading));
   }
 
   /**
diff --git a/src/app/shared/menu/menu-item/link-menu-item.component.html b/src/app/shared/menu/menu-item/link-menu-item.component.html
index 76856e57efc09ce2aaf2accacfa0e54c28624645..74e918c07b4806e300990f66d28b71754630125f 100644
--- a/src/app/shared/menu/menu-item/link-menu-item.component.html
+++ b/src/app/shared/menu/menu-item/link-menu-item.component.html
@@ -1 +1 @@
-<a class="nav-item nav-link" [routerLink]="getRouterLink()">{{item.text | translate}}</a>
\ No newline at end of file
+<a class="nav-item nav-link"  [ngClass]="{'disabled': !hasLink}" [routerLink]="getRouterLink()">{{item.text | translate}}</a>
\ No newline at end of file
diff --git a/src/app/shared/menu/menu-item/link-menu-item.component.ts b/src/app/shared/menu/menu-item/link-menu-item.component.ts
index 02ce31843c377c230292498a8cf121348687ae23..4e1e70236bc286a46fc73141d3cb06eba9f879c5 100644
--- a/src/app/shared/menu/menu-item/link-menu-item.component.ts
+++ b/src/app/shared/menu/menu-item/link-menu-item.component.ts
@@ -1,8 +1,9 @@
-import { Component, Inject, Input } from '@angular/core';
+import { Component, Inject, Input, OnInit } from '@angular/core';
 import { LinkMenuItemModel } from './models/link.model';
 import { MenuItemType } from '../initial-menus-state';
 import { rendersMenuItemForType } from '../menu-item.decorator';
 import { GLOBAL_CONFIG, GlobalConfig } from '../../../../config';
+import { isNotEmpty } from '../../empty.util';
 
 /**
  * Component that renders a menu section of type LINK
@@ -12,13 +13,22 @@ import { GLOBAL_CONFIG, GlobalConfig } from '../../../../config';
   templateUrl: './link-menu-item.component.html'
 })
 @rendersMenuItemForType(MenuItemType.LINK)
-export class LinkMenuItemComponent {
+export class LinkMenuItemComponent implements OnInit {
   item: LinkMenuItemModel;
+  hasLink: boolean;
   constructor(@Inject('itemModelProvider') item: LinkMenuItemModel, @Inject(GLOBAL_CONFIG) private EnvConfig: GlobalConfig) {
     this.item = item;
   }
 
+  ngOnInit(): void {
+    this.hasLink = isNotEmpty(this.item.link);
+  }
+
   getRouterLink() {
-    return this.EnvConfig.ui.nameSpace + this.item.link;
+    if (this.hasLink) {
+      return this.EnvConfig.ui.nameSpace + this.item.link;
+    }
+    return undefined;
   }
+
 }
diff --git a/src/app/shared/menu/menu-item/onclick-menu-item.component.spec.ts b/src/app/shared/menu/menu-item/onclick-menu-item.component.spec.ts
index dd031a96e0b086c0610e96d4bc3861d3fd921b73..dbe6fdab6a6788273d5446c4f78c60280ea74dbd 100644
--- a/src/app/shared/menu/menu-item/onclick-menu-item.component.spec.ts
+++ b/src/app/shared/menu/menu-item/onclick-menu-item.component.spec.ts
@@ -1,5 +1,4 @@
 import { async, ComponentFixture, TestBed } from '@angular/core/testing';
-import { TextMenuItemComponent } from './text-menu-item.component';
 import { TranslateModule } from '@ngx-translate/core';
 import { DebugElement, NO_ERRORS_SCHEMA } from '@angular/core';
 import { By } from '@angular/platform-browser';
diff --git a/src/app/shared/menu/menu.module.ts b/src/app/shared/menu/menu.module.ts
index 7e900d18e662120fa5d274d169205b5570fa7fda..12d9b4001bbe64bc363f9a01f497d37016a6855a 100644
--- a/src/app/shared/menu/menu.module.ts
+++ b/src/app/shared/menu/menu.module.ts
@@ -6,6 +6,7 @@ import { RouterModule } from '@angular/router';
 import { LinkMenuItemComponent } from './menu-item/link-menu-item.component';
 import { TextMenuItemComponent } from './menu-item/text-menu-item.component';
 import { OnClickMenuItemComponent } from './menu-item/onclick-menu-item.component';
+import { CommonModule } from '@angular/common';
 
 const COMPONENTS = [
   MenuSectionComponent,
@@ -23,7 +24,8 @@ const ENTRY_COMPONENTS = [
 
 const MODULES = [
   TranslateModule,
-  RouterModule
+  RouterModule,
+  CommonModule
 ];
 const PROVIDERS = [
 
diff --git a/src/app/shared/mocks/mock-remote-data-build.service.ts b/src/app/shared/mocks/mock-remote-data-build.service.ts
index 3cab439581f437ed71ef708f30dfc1d6b4bb23a6..6a05c9bf3688cdd1ffb22410ad65993747f83696 100644
--- a/src/app/shared/mocks/mock-remote-data-build.service.ts
+++ b/src/app/shared/mocks/mock-remote-data-build.service.ts
@@ -1,5 +1,5 @@
-import {of as observableOf,  Observable } from 'rxjs';
-import { map, take } from 'rxjs/operators';
+import { Observable, of as observableOf } from 'rxjs';
+import { map } from 'rxjs/operators';
 import { RemoteDataBuildService } from '../../core/cache/builders/remote-data-build.service';
 import { RemoteData } from '../../core/data/remote-data';
 import { RequestEntry } from '../../core/data/request.reducer';
diff --git a/src/app/shared/mocks/mock-request.service.ts b/src/app/shared/mocks/mock-request.service.ts
index ce09f6d85e35e1c3b32afd89a85c0dfd8713ff72..9a320b749c1d98ce4cb70fdbb99ed1edb95f85ff 100644
--- a/src/app/shared/mocks/mock-request.service.ts
+++ b/src/app/shared/mocks/mock-request.service.ts
@@ -8,6 +8,7 @@ export function getMockRequestService(requestEntry$: Observable<RequestEntry> =
     generateRequestId: 'clients/b186e8ce-e99c-4183-bc9a-42b4821bdb78',
     getByHref: requestEntry$,
     getByUUID: requestEntry$,
+    uriEncodeBody: jasmine.createSpy('uriEncodeBody'),
     /* tslint:disable:no-empty */
     removeByHrefSubstring: () => {}
     /* tslint:enable:no-empty */
diff --git a/src/app/shared/mocks/mock-role-service.ts b/src/app/shared/mocks/mock-role-service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..dad296f986bcdcd932638bb93fb2c310c9cd219d
--- /dev/null
+++ b/src/app/shared/mocks/mock-role-service.ts
@@ -0,0 +1,51 @@
+import { Observable } from 'rxjs/internal/Observable';
+import { RoleType } from '../../core/roles/role-types';
+import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject';
+
+export class MockRoleService {
+
+  _isSubmitter = new BehaviorSubject(true);
+  _isController = new BehaviorSubject(true);
+  _isAdmin = new BehaviorSubject(true);
+
+  setSubmitter(isSubmitter: boolean) {
+    this._isSubmitter.next(isSubmitter);
+  }
+
+  setController(isController: boolean) {
+    this._isController.next(isController);
+  }
+
+  setAdmin(isAdmin: boolean) {
+    this._isAdmin.next(isAdmin);
+  }
+
+  isSubmitter(): Observable<boolean> {
+    return this._isSubmitter;
+  }
+
+  isController(): Observable<boolean> {
+    return this._isController;
+  }
+
+  isAdmin(): Observable<boolean> {
+    return this._isAdmin;
+  }
+
+  checkRole(role: RoleType): Observable<boolean> {
+    let check: Observable<boolean>;
+    switch (role) {
+      case RoleType.Submitter:
+        check = this.isSubmitter();
+        break;
+      case RoleType.Controller:
+        check = this.isController();
+        break;
+      case RoleType.Admin:
+        check = this.isAdmin();
+        break;
+    }
+
+    return check;
+  }
+}
diff --git a/src/app/shared/mocks/mock-router.ts b/src/app/shared/mocks/mock-router.ts
index 929e2644e830a6651cba2cdd1a3d4de72daf6efe..fb475a04671aa6f83a16ad9a64ad35ff58d6d8c7 100644
--- a/src/app/shared/mocks/mock-router.ts
+++ b/src/app/shared/mocks/mock-router.ts
@@ -7,7 +7,10 @@ export class MockRouter {
   public events = observableOf({});
   public routerState = {
     snapshot: {
-      url: ''
+      url: '',
+      root: {
+        queryParamMap: null
+      }
     }
   };
 
@@ -18,4 +21,12 @@ export class MockRouter {
   setRoute(route) {
     this.routerState.snapshot.url = route;
   }
+
+  setParams(paramsMap) {
+    this.routerState.snapshot.root.queryParamMap = paramsMap;
+  }
+
+  createUrlTree(commands, navExtras = {}) {
+    return {};
+  }
 }
diff --git a/src/app/shared/mydspace-actions/claimed-task/approve/claimed-task-actions-approve.component.html b/src/app/shared/mydspace-actions/claimed-task/approve/claimed-task-actions-approve.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..3c41fdbb07cb458d63468c2bd4545f362cc7db8f
--- /dev/null
+++ b/src/app/shared/mydspace-actions/claimed-task/approve/claimed-task-actions-approve.component.html
@@ -0,0 +1,8 @@
+<button type="button"
+        [className]="'btn btn-success ' + wrapperClass"
+        ngbTooltip="{{'submission.workflow.tasks.claimed.approve_help' | translate}}"
+        [disabled]="processingApprove"
+        (click)="confirmApprove()">
+  <span *ngIf="processingApprove"><i class='fas fa-circle-notch fa-spin'></i> {{'submission.workflow.tasks.generic.processing' | translate}}</span>
+  <span *ngIf="!processingApprove"><i class="fa fa-thumbs-up"></i> {{'submission.workflow.tasks.claimed.approve' | translate}}</span>
+</button>
diff --git a/src/app/shared/mydspace-actions/claimed-task/approve/claimed-task-actions-approve.component.scss b/src/app/shared/mydspace-actions/claimed-task/approve/claimed-task-actions-approve.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/app/shared/mydspace-actions/claimed-task/approve/claimed-task-actions-approve.component.spec.ts b/src/app/shared/mydspace-actions/claimed-task/approve/claimed-task-actions-approve.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..552d31675e97710ff4193bf59945da36dfec9269
--- /dev/null
+++ b/src/app/shared/mydspace-actions/claimed-task/approve/claimed-task-actions-approve.component.spec.ts
@@ -0,0 +1,65 @@
+import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { By } from '@angular/platform-browser';
+import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
+
+import { ClaimedTaskActionsApproveComponent } from './claimed-task-actions-approve.component';
+import { MockTranslateLoader } from '../../../mocks/mock-translate-loader';
+
+let component: ClaimedTaskActionsApproveComponent;
+let fixture: ComponentFixture<ClaimedTaskActionsApproveComponent>;
+
+describe('ClaimedTaskActionsApproveComponent', () => {
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [
+        TranslateModule.forRoot({
+          loader: {
+            provide: TranslateLoader,
+            useClass: MockTranslateLoader
+          }
+        })
+      ],
+      declarations: [ClaimedTaskActionsApproveComponent],
+      schemas: [NO_ERRORS_SCHEMA]
+    }).overrideComponent(ClaimedTaskActionsApproveComponent, {
+      set: { changeDetection: ChangeDetectionStrategy.Default }
+    }).compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(ClaimedTaskActionsApproveComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  afterEach(() => {
+    fixture = null;
+    component = null;
+  });
+
+  it('should display approve button', () => {
+    const btn = fixture.debugElement.query(By.css('.btn-success'));
+
+    expect(btn).toBeDefined();
+  });
+
+  it('should display spin icon when approve is pending', () => {
+    component.processingApprove = true;
+    fixture.detectChanges();
+
+    const span = fixture.debugElement.query(By.css('.btn-success .fa-spin'));
+
+    expect(span).toBeDefined();
+  });
+
+  it('should emit approve event', () => {
+    spyOn(component.approve, 'emit');
+
+    component.confirmApprove();
+    fixture.detectChanges();
+
+    expect(component.approve.emit).toHaveBeenCalled();
+  });
+
+});
diff --git a/src/app/shared/mydspace-actions/claimed-task/approve/claimed-task-actions-approve.component.ts b/src/app/shared/mydspace-actions/claimed-task/approve/claimed-task-actions-approve.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8e7c0dab606edfd8542902a5cef155bff5c4ef4b
--- /dev/null
+++ b/src/app/shared/mydspace-actions/claimed-task/approve/claimed-task-actions-approve.component.ts
@@ -0,0 +1,32 @@
+import { Component, EventEmitter, Input, Output } from '@angular/core';
+
+@Component({
+  selector: 'ds-claimed-task-actions-approve',
+  styleUrls: ['./claimed-task-actions-approve.component.scss'],
+  templateUrl: './claimed-task-actions-approve.component.html',
+})
+
+export class ClaimedTaskActionsApproveComponent {
+
+  /**
+   * A boolean representing if a reject operation is pending
+   */
+  @Input() processingApprove: boolean;
+
+  /**
+   * CSS classes to append to reject button
+   */
+  @Input() wrapperClass: string;
+
+  /**
+   * An event fired when a approve action is confirmed.
+   */
+  @Output() approve: EventEmitter<any> = new EventEmitter<any>();
+
+  /**
+   * Emit approve event
+   */
+  confirmApprove() {
+    this.approve.emit();
+  }
+}
diff --git a/src/app/shared/mydspace-actions/claimed-task/claimed-task-actions.component.html b/src/app/shared/mydspace-actions/claimed-task/claimed-task-actions.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..4b9b93e7e3469ccf25b15bf5210f0745c31c041c
--- /dev/null
+++ b/src/app/shared/mydspace-actions/claimed-task/claimed-task-actions.component.html
@@ -0,0 +1,20 @@
+
+  <a [class.disabled]="!(object.workflowitem | async)?.hasSucceeded"
+     class="btn btn-primary mt-1 mb-3"
+     ngbTooltip="{{'submission.workflow.tasks.claimed.edit_help' | translate}}"
+     [routerLink]="['/workflowitems/' + (object.workflowitem | async)?.payload.id + '/' + object.id + '/edit']"
+     role="button">
+    <i class="fa fa-edit"></i> {{'submission.workflow.tasks.claimed.edit' | translate}}
+  </a>
+
+  <ds-claimed-task-actions-approve [processingApprove]="(processingApprove$ | async)"
+                                   [wrapperClass]="'mt-1 mb-3'"
+                                   (approve)="approve()"></ds-claimed-task-actions-approve>
+
+  <ds-claimed-task-actions-reject [processingReject]="(processingReject$ | async)"
+                                  [wrapperClass]="'mt-1 mb-3'"
+                                  (reject)="reject($event)"></ds-claimed-task-actions-reject>
+
+  <ds-claimed-task-actions-return-to-pool [processingReturnToPool]="(processingReturnToPool$ | async)"
+                                          [wrapperClass]="'mt-1 mb-3'"
+                                          (returnToPool)="returnToPool()"></ds-claimed-task-actions-return-to-pool>
diff --git a/src/app/shared/mydspace-actions/claimed-task/claimed-task-actions.component.scss b/src/app/shared/mydspace-actions/claimed-task/claimed-task-actions.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/app/shared/mydspace-actions/claimed-task/claimed-task-actions.component.spec.ts b/src/app/shared/mydspace-actions/claimed-task/claimed-task-actions.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4144cb3760c4420285bc7d3e49152e83de7b48a9
--- /dev/null
+++ b/src/app/shared/mydspace-actions/claimed-task/claimed-task-actions.component.spec.ts
@@ -0,0 +1,269 @@
+import { ChangeDetectionStrategy, Injector, NO_ERRORS_SCHEMA } from '@angular/core';
+import { async, ComponentFixture, fakeAsync, TestBed } from '@angular/core/testing';
+import { Router } from '@angular/router';
+import { By } from '@angular/platform-browser';
+
+import { of as observableOf } from 'rxjs';
+import { cold } from 'jasmine-marbles';
+import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
+
+import { MockTranslateLoader } from '../../mocks/mock-translate-loader';
+import { NotificationsService } from '../../notifications/notifications.service';
+import { NotificationsServiceStub } from '../../testing/notifications-service-stub';
+import { RouterStub } from '../../testing/router-stub';
+import { RemoteData } from '../../../core/data/remote-data';
+import { Item } from '../../../core/shared/item.model';
+import { ClaimedTaskDataService } from '../../../core/tasks/claimed-task-data.service';
+import { ClaimedTaskActionsComponent } from './claimed-task-actions.component';
+import { ClaimedTask } from '../../../core/tasks/models/claimed-task-object.model';
+import { Workflowitem } from '../../../core/submission/models/workflowitem.model';
+
+let component: ClaimedTaskActionsComponent;
+let fixture: ComponentFixture<ClaimedTaskActionsComponent>;
+
+let mockObject: ClaimedTask;
+let notificationsServiceStub: NotificationsServiceStub;
+let router: RouterStub;
+
+const mockDataService = jasmine.createSpyObj('PoolTaskDataService', {
+  approveTask: jasmine.createSpy('approveTask'),
+  rejectTask: jasmine.createSpy('rejectTask'),
+  returnToPoolTask: jasmine.createSpy('returnToPoolTask'),
+});
+
+const item = Object.assign(new Item(), {
+  bitstreams: observableOf({}),
+  metadata: {
+    'dc.title': [
+      {
+        language: 'en_US',
+        value: 'This is just another title'
+      }
+    ],
+    'dc.type': [
+      {
+        language: null,
+        value: 'Article'
+      }
+    ],
+    'dc.contributor.author': [
+      {
+        language: 'en_US',
+        value: 'Smith, Donald'
+      }
+    ],
+    'dc.date.issued': [
+      {
+        language: null,
+        value: '2015-06-26'
+      }
+    ]
+  }
+});
+const rdItem = new RemoteData(false, false, true, null, item);
+const workflowitem = Object.assign(new Workflowitem(), { item: observableOf(rdItem) });
+const rdWorkflowitem = new RemoteData(false, false, true, null, workflowitem);
+mockObject = Object.assign(new ClaimedTask(), { workflowitem: observableOf(rdWorkflowitem), id: '1234' });
+
+describe('ClaimedTaskActionsComponent', () => {
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [
+        TranslateModule.forRoot({
+          loader: {
+            provide: TranslateLoader,
+            useClass: MockTranslateLoader
+          }
+        })
+      ],
+      declarations: [ClaimedTaskActionsComponent],
+      providers: [
+        { provide: Injector, useValue: {} },
+        { provide: NotificationsService, useValue: new NotificationsServiceStub() },
+        { provide: Router, useValue: new RouterStub() },
+        { provide: ClaimedTaskDataService, useValue: mockDataService },
+      ],
+      schemas: [NO_ERRORS_SCHEMA]
+    }).overrideComponent(ClaimedTaskActionsComponent, {
+      set: { changeDetection: ChangeDetectionStrategy.Default }
+    }).compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(ClaimedTaskActionsComponent);
+    component = fixture.componentInstance;
+    component.object = mockObject;
+    notificationsServiceStub = TestBed.get(NotificationsService);
+    router = TestBed.get(Router);
+    fixture.detectChanges();
+  });
+
+  afterEach(() => {
+    fixture = null;
+    component = null;
+  });
+
+  it('should init objects properly', () => {
+    component.object = null;
+    component.initObjects(mockObject);
+
+    expect(component.object).toEqual(mockObject);
+
+    expect(component.workflowitem$).toBeObservable(cold('(b|)', {
+      b: rdWorkflowitem.payload
+    }))
+  });
+
+  it('should display edit task button', () => {
+    const btn = fixture.debugElement.query(By.css('.btn-info'));
+
+    expect(btn).toBeDefined();
+  });
+
+  it('should call approveTask method when approving a task', fakeAsync(() => {
+    spyOn(component, 'reload');
+    mockDataService.approveTask.and.returnValue(observableOf({hasSucceeded: true}));
+
+    component.approve();
+    fixture.detectChanges();
+
+    fixture.whenStable().then(() => {
+      expect(mockDataService.approveTask).toHaveBeenCalledWith(mockObject.id);
+    });
+
+  }));
+
+  it('should display a success notification on approve success', async(() => {
+    spyOn(component, 'reload');
+    mockDataService.approveTask.and.returnValue(observableOf({hasSucceeded: true}));
+
+    component.approve();
+    fixture.detectChanges();
+
+    fixture.whenStable().then(() => {
+      expect(notificationsServiceStub.success).toHaveBeenCalled();
+    });
+  }));
+
+  it('should reload page on approve success', async(() => {
+    spyOn(router, 'navigateByUrl');
+    router.url = 'test.url/test';
+    mockDataService.approveTask.and.returnValue(observableOf({hasSucceeded: true}));
+
+    component.approve();
+    fixture.detectChanges();
+
+    fixture.whenStable().then(() => {
+      expect(router.navigateByUrl).toHaveBeenCalledWith('test.url/test');
+    });
+  }));
+
+  it('should display an error notification on approve failure', async(() => {
+    mockDataService.approveTask.and.returnValue(observableOf({hasSucceeded: false}));
+
+    component.approve();
+    fixture.detectChanges();
+
+    fixture.whenStable().then(() => {
+      expect(notificationsServiceStub.error).toHaveBeenCalled();
+    });
+  }));
+
+  it('should call rejectTask method when rejecting a task', fakeAsync(() => {
+    spyOn(component, 'reload');
+    mockDataService.rejectTask.and.returnValue(observableOf({hasSucceeded: true}));
+
+    component.reject('test reject');
+    fixture.detectChanges();
+
+    fixture.whenStable().then(() => {
+      expect(mockDataService.rejectTask).toHaveBeenCalledWith('test reject', mockObject.id);
+    });
+
+  }));
+
+  it('should display a success notification on reject success', async(() => {
+    spyOn(component, 'reload');
+    mockDataService.rejectTask.and.returnValue(observableOf({hasSucceeded: true}));
+
+    component.reject('test reject');
+    fixture.detectChanges();
+
+    fixture.whenStable().then(() => {
+      expect(notificationsServiceStub.success).toHaveBeenCalled();
+    });
+  }));
+
+  it('should reload page on reject success', async(() => {
+    spyOn(router, 'navigateByUrl');
+    router.url = 'test.url/test';
+    mockDataService.rejectTask.and.returnValue(observableOf({hasSucceeded: true}));
+
+    component.reject('test reject');
+    fixture.detectChanges();
+
+    fixture.whenStable().then(() => {
+      expect(router.navigateByUrl).toHaveBeenCalledWith('test.url/test');
+    });
+  }));
+
+  it('should display an error notification on reject failure', async(() => {
+    mockDataService.rejectTask.and.returnValue(observableOf({hasSucceeded: false}));
+
+    component.reject('test reject');
+    fixture.detectChanges();
+
+    fixture.whenStable().then(() => {
+      expect(notificationsServiceStub.error).toHaveBeenCalled();
+    });
+  }));
+
+  it('should call returnToPoolTask method when returning a task to pool', fakeAsync(() => {
+    spyOn(component, 'reload');
+    mockDataService.returnToPoolTask.and.returnValue(observableOf({hasSucceeded: true}));
+
+    component.returnToPool();
+    fixture.detectChanges();
+
+    fixture.whenStable().then(() => {
+      expect(mockDataService.returnToPoolTask).toHaveBeenCalledWith( mockObject.id);
+    });
+
+  }));
+
+  it('should display a success notification on return to pool success', async(() => {
+    spyOn(component, 'reload');
+    mockDataService.returnToPoolTask.and.returnValue(observableOf({hasSucceeded: true}));
+
+    component.returnToPool();
+    fixture.detectChanges();
+
+    fixture.whenStable().then(() => {
+      expect(notificationsServiceStub.success).toHaveBeenCalled();
+    });
+  }));
+
+  it('should reload page on return to pool success', async(() => {
+    spyOn(router, 'navigateByUrl');
+    router.url = 'test.url/test';
+    mockDataService.returnToPoolTask.and.returnValue(observableOf({hasSucceeded: true}));
+
+    component.returnToPool();
+    fixture.detectChanges();
+
+    fixture.whenStable().then(() => {
+      expect(router.navigateByUrl).toHaveBeenCalledWith('test.url/test');
+    });
+  }));
+
+  it('should display an error notification on return to pool failure', async(() => {
+    mockDataService.returnToPoolTask.and.returnValue(observableOf({hasSucceeded: false}));
+
+    component.returnToPool();
+    fixture.detectChanges();
+
+    fixture.whenStable().then(() => {
+      expect(notificationsServiceStub.error).toHaveBeenCalled();
+    });
+  }));
+});
diff --git a/src/app/shared/mydspace-actions/claimed-task/claimed-task-actions.component.ts b/src/app/shared/mydspace-actions/claimed-task/claimed-task-actions.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2a7669f5b11301682ceb9581119e5b334bc7fca4
--- /dev/null
+++ b/src/app/shared/mydspace-actions/claimed-task/claimed-task-actions.component.ts
@@ -0,0 +1,123 @@
+import { Component, Injector, Input, OnInit } from '@angular/core';
+import { Router } from '@angular/router';
+
+import { BehaviorSubject, Observable } from 'rxjs';
+import { filter, map } from 'rxjs/operators';
+import { TranslateService } from '@ngx-translate/core';
+
+import { ClaimedTaskDataService } from '../../../core/tasks/claimed-task-data.service';
+import { ClaimedTask } from '../../../core/tasks/models/claimed-task-object.model';
+import { ProcessTaskResponse } from '../../../core/tasks/models/process-task-response';
+import { isNotUndefined } from '../../empty.util';
+import { Workflowitem } from '../../../core/submission/models/workflowitem.model';
+import { RemoteData } from '../../../core/data/remote-data';
+import { MyDSpaceActionsComponent } from '../mydspace-actions';
+import { ResourceType } from '../../../core/shared/resource-type';
+import { NotificationsService } from '../../notifications/notifications.service';
+
+/**
+ * This component represents mydspace actions related to ClaimedTask object.
+ */
+@Component({
+  selector: 'ds-claimed-task-actions',
+  styleUrls: ['./claimed-task-actions.component.scss'],
+  templateUrl: './claimed-task-actions.component.html',
+})
+export class ClaimedTaskActionsComponent extends MyDSpaceActionsComponent<ClaimedTask, ClaimedTaskDataService> implements OnInit {
+
+  /**
+   * The ClaimedTask object
+   */
+  @Input() object: ClaimedTask;
+
+  /**
+   * The workflowitem object that belonging to the ClaimedTask object
+   */
+  public workflowitem$: Observable<Workflowitem>;
+
+  /**
+   * A boolean representing if an approve operation is pending
+   */
+  public processingApprove$ = new BehaviorSubject<boolean>(false);
+
+  /**
+   * A boolean representing if a reject operation is pending
+   */
+  public processingReject$ = new BehaviorSubject<boolean>(false);
+
+  /**
+   * A boolean representing if a return to pool operation is pending
+   */
+  public processingReturnToPool$ = new BehaviorSubject<boolean>(false);
+
+  /**
+   * Initialize instance variables
+   *
+   * @param {Injector} injector
+   * @param {Router} router
+   * @param {NotificationsService} notificationsService
+   * @param {TranslateService} translate
+   */
+  constructor(protected injector: Injector,
+              protected router: Router,
+              protected notificationsService: NotificationsService,
+              protected translate: TranslateService) {
+    super(ResourceType.ClaimedTask, injector, router, notificationsService, translate);
+  }
+
+  /**
+   * Initialize objects
+   */
+  ngOnInit() {
+    this.initObjects(this.object);
+  }
+
+  /**
+   * Init the ClaimedTask and Workflowitem objects
+   *
+   * @param {PoolTask} object
+   */
+  initObjects(object: ClaimedTask) {
+    this.object = object;
+    this.workflowitem$ = (this.object.workflowitem as Observable<RemoteData<Workflowitem>>).pipe(
+      filter((rd: RemoteData<Workflowitem>) => ((!rd.isRequestPending) && isNotUndefined(rd.payload))),
+      map((rd: RemoteData<Workflowitem>) => rd.payload));
+  }
+
+  /**
+   * Approve the task.
+   */
+  approve() {
+    this.processingApprove$.next(true);
+    this.objectDataService.approveTask(this.object.id)
+      .subscribe((res: ProcessTaskResponse) => {
+        this.processingApprove$.next(false);
+        this.handleActionResponse(res.hasSucceeded);
+      });
+  }
+
+  /**
+   * Reject the task.
+   */
+  reject(reason) {
+    this.processingReject$.next(true);
+    this.objectDataService.rejectTask(reason, this.object.id)
+      .subscribe((res: ProcessTaskResponse) => {
+        this.processingReject$.next(false);
+        this.handleActionResponse(res.hasSucceeded);
+      });
+  }
+
+  /**
+   * Return task to the pool.
+   */
+  returnToPool() {
+    this.processingReturnToPool$.next(true);
+    this.objectDataService.returnToPoolTask(this.object.id)
+      .subscribe((res: ProcessTaskResponse) => {
+        this.processingReturnToPool$.next(false);
+        this.handleActionResponse(res.hasSucceeded);
+      });
+  }
+
+}
diff --git a/src/app/shared/mydspace-actions/claimed-task/reject/claimed-task-actions-reject.component.html b/src/app/shared/mydspace-actions/claimed-task/reject/claimed-task-actions-reject.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..91edee66bd5654032d60c5cd036ca4bcc86e2d0e
--- /dev/null
+++ b/src/app/shared/mydspace-actions/claimed-task/reject/claimed-task-actions-reject.component.html
@@ -0,0 +1,38 @@
+<ng-template #rejectTipContent><p [innerHTML]="'submission.workflow.tasks.claimed.reject_help' | translate"></p></ng-template>
+<button [className]="'btn btn-danger ' + wrapperClass"
+        [ngbTooltip]="rejectTipContent"
+        [disabled]="processingReject"
+        (click)="openRejectModal(rejectModal)" >
+  <span *ngIf="processingReject"><i class='fas fa-circle-notch fa-spin'></i> {{'submission.workflow.tasks.generic.processing' | translate}}</span>
+  <span *ngIf="!processingReject"><i class="fa fa-trash"></i> {{'submission.workflow.tasks.claimed.reject.submit' | translate}}</span>
+</button>
+
+<ng-template #rejectModal let-c="close" let-d="dismiss">
+  <div class="modal-header">
+    <h4 class="modal-title">{{'submission.workflow.tasks.claimed.reject.reason.title' | translate}}</h4>
+    <button type="button"
+            class="close"
+            aria-label="Close"
+            (click)="d('Cross click')">
+      <span aria-hidden="true">&times;</span>
+    </button>
+  </div>
+  <div class="modal-body">
+    <div class="alert alert-info" role="alert">
+      {{'submission.workflow.tasks.claimed.reject.reason.info' | translate}}
+    </div>
+    <form (ngSubmit)="confirmReject(rejectModal);" [formGroup]="rejectForm" >
+      <textarea style="width: 100%"
+                formControlName="reason"
+                rows="4"
+                placeholder="{{'submission.workflow.tasks.claimed.reject.reason.placeholder' | translate}}"></textarea>
+      <button id="btn-chat"
+              class="btn btn-danger btn-lg btn-block mt-3"
+              [disabled]="!rejectForm.valid || processingReject"
+              type="submit">
+        <span *ngIf="processingReject"><i class='fas fa-circle-notch fa-spin'></i> {{'submission.workflow.tasks.generic.processing' | translate}}</span>
+        <span *ngIf="!processingReject">{{'submission.workflow.tasks.claimed.reject.reason.submit' | translate}}</span>
+      </button>
+    </form>
+  </div>
+</ng-template>
diff --git a/src/app/shared/mydspace-actions/claimed-task/reject/claimed-task-actions-reject.component.scss b/src/app/shared/mydspace-actions/claimed-task/reject/claimed-task-actions-reject.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/app/shared/mydspace-actions/claimed-task/reject/claimed-task-actions-reject.component.spec.ts b/src/app/shared/mydspace-actions/claimed-task/reject/claimed-task-actions-reject.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d7e0b53748bd3ff5ce6e3dd3ccc77b54e9522c89
--- /dev/null
+++ b/src/app/shared/mydspace-actions/claimed-task/reject/claimed-task-actions-reject.component.spec.ts
@@ -0,0 +1,108 @@
+import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
+import { async, ComponentFixture, fakeAsync, TestBed } from '@angular/core/testing';
+import { FormBuilder, FormGroup, ReactiveFormsModule } from '@angular/forms';
+import { By } from '@angular/platform-browser';
+
+import { NgbModal, NgbModule } from '@ng-bootstrap/ng-bootstrap';
+import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
+
+import { ClaimedTaskActionsRejectComponent } from './claimed-task-actions-reject.component';
+import { MockTranslateLoader } from '../../../mocks/mock-translate-loader';
+
+let component: ClaimedTaskActionsRejectComponent;
+let fixture: ComponentFixture<ClaimedTaskActionsRejectComponent>;
+let formBuilder: FormBuilder;
+let modalService: NgbModal;
+
+describe('ClaimedTaskActionsRejectComponent', () => {
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [
+        NgbModule.forRoot(),
+        ReactiveFormsModule,
+        TranslateModule.forRoot({
+          loader: {
+            provide: TranslateLoader,
+            useClass: MockTranslateLoader
+          }
+        })
+      ],
+      declarations: [ClaimedTaskActionsRejectComponent],
+      providers: [
+        FormBuilder,
+        NgbModal
+      ],
+      schemas: [NO_ERRORS_SCHEMA]
+    }).overrideComponent(ClaimedTaskActionsRejectComponent, {
+      set: { changeDetection: ChangeDetectionStrategy.Default }
+    }).compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(ClaimedTaskActionsRejectComponent);
+    component = fixture.componentInstance;
+    formBuilder = TestBed.get(FormBuilder);
+    modalService = TestBed.get(NgbModal);
+    component.modalRef = modalService.open('ok');
+    fixture.detectChanges();
+  });
+
+  afterEach(() => {
+    fixture = null;
+    component = null;
+    modalService = null;
+    formBuilder = null;
+  });
+
+  it('should init reject form properly', () => {
+    expect(component.rejectForm).toBeDefined();
+    expect(component.rejectForm instanceof FormGroup).toBeTruthy();
+    expect(component.rejectForm.controls.reason).toBeDefined();
+  });
+
+  it('should display reject button', () => {
+    const btn = fixture.debugElement.query(By.css('.btn-danger'));
+
+    expect(btn).toBeDefined();
+  });
+
+  it('should display spin icon when reject is pending', () => {
+    component.processingReject = true;
+    fixture.detectChanges();
+
+    const span = fixture.debugElement.query(By.css('.btn-danger .fa-spin'));
+
+    expect(span).toBeDefined();
+  });
+
+  it('should call openRejectModal on reject button click', fakeAsync(() => {
+    spyOn(component.rejectForm, 'reset');
+    const btn = fixture.debugElement.query(By.css('.btn-danger'));
+    btn.nativeElement.click();
+    fixture.detectChanges();
+
+    expect(component.rejectForm.reset).toHaveBeenCalled();
+    expect(component.modalRef).toBeDefined();
+
+    component.modalRef.close()
+  }));
+
+  it('should call confirmReject on form submit', fakeAsync(() => {
+    spyOn(component.reject, 'emit');
+
+    const btn = fixture.debugElement.query(By.css('.btn-danger'));
+    btn.nativeElement.click();
+    fixture.detectChanges();
+
+    expect(component.modalRef).toBeDefined();
+
+    const form = ((document as any).querySelector('form'));
+    form.dispatchEvent(new Event('ngSubmit'));
+    fixture.detectChanges();
+
+    fixture.whenStable().then(() => {
+      expect(component.reject.emit).toHaveBeenCalled();
+    });
+
+  }));
+});
diff --git a/src/app/shared/mydspace-actions/claimed-task/reject/claimed-task-actions-reject.component.ts b/src/app/shared/mydspace-actions/claimed-task/reject/claimed-task-actions-reject.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b66c10469523a755f0fd2484a8a235e7e6de7f75
--- /dev/null
+++ b/src/app/shared/mydspace-actions/claimed-task/reject/claimed-task-actions-reject.component.ts
@@ -0,0 +1,78 @@
+import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
+import { FormBuilder, FormGroup, Validators } from '@angular/forms';
+
+import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
+
+@Component({
+  selector: 'ds-claimed-task-actions-reject',
+  styleUrls: ['./claimed-task-actions-reject.component.scss'],
+  templateUrl: './claimed-task-actions-reject.component.html',
+})
+
+export class ClaimedTaskActionsRejectComponent implements OnInit {
+
+  /**
+   * A boolean representing if a reject operation is pending
+   */
+  @Input() processingReject: boolean;
+
+  /**
+   * CSS classes to append to reject button
+   */
+  @Input() wrapperClass: string;
+
+  /**
+   * An event fired when a reject action is confirmed.
+   * Event's payload equals to reject reason.
+   */
+  @Output() reject: EventEmitter<string> = new EventEmitter<string>();
+
+  /**
+   * The reject form group
+   */
+  public rejectForm: FormGroup;
+
+  /**
+   * Reference to NgbModal
+   */
+  public modalRef: NgbModalRef;
+
+  /**
+   * Initialize instance variables
+   *
+   * @param {FormBuilder} formBuilder
+   * @param {NgbModal} modalService
+   */
+  constructor(private formBuilder: FormBuilder, private modalService: NgbModal) {
+  }
+
+  /**
+   * Initialize form
+   */
+  ngOnInit() {
+    this.rejectForm = this.formBuilder.group({
+      reason: ['', Validators.required]
+    });
+
+  }
+
+  /**
+   * Close modal and emit reject event
+   */
+  confirmReject() {
+    this.processingReject = true;
+    this.modalRef.close('Send Button');
+    const reason = this.rejectForm.get('reason').value;
+    this.reject.emit(reason);
+  }
+
+  /**
+   * Open modal
+   *
+   * @param content
+   */
+  openRejectModal(content: any) {
+    this.rejectForm.reset();
+    this.modalRef = this.modalService.open(content);
+  }
+}
diff --git a/src/app/shared/mydspace-actions/claimed-task/return-to-pool/claimed-task-actions-return-to-pool.component.html b/src/app/shared/mydspace-actions/claimed-task/return-to-pool/claimed-task-actions-return-to-pool.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..702ce75e7f4b49fc1abf7c2f1ac19469c88f01ca
--- /dev/null
+++ b/src/app/shared/mydspace-actions/claimed-task/return-to-pool/claimed-task-actions-return-to-pool.component.html
@@ -0,0 +1,8 @@
+<button type="button"
+        [className]="'btn btn-secondary ' + wrapperClass"
+        ngbTooltip="{{'submission.workflow.tasks.claimed.return_help' | translate}}"
+        [disabled]="processingReturnToPool"
+        (click)="confirmReturnToPool()">
+  <span *ngIf="processingReturnToPool"><i class='fas fa-circle-notch fa-spin'></i> {{'submission.workflow.tasks.generic.processing' | translate}}</span>
+  <span *ngIf="!processingReturnToPool"><i class="fa fa-undo"></i> {{'submission.workflow.tasks.claimed.return' | translate}}</span>
+</button>
diff --git a/src/app/shared/mydspace-actions/claimed-task/return-to-pool/claimed-task-actions-return-to-pool.component.scss b/src/app/shared/mydspace-actions/claimed-task/return-to-pool/claimed-task-actions-return-to-pool.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/app/shared/mydspace-actions/claimed-task/return-to-pool/claimed-task-actions-return-to-pool.component.spec.ts b/src/app/shared/mydspace-actions/claimed-task/return-to-pool/claimed-task-actions-return-to-pool.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d461d9e055c4a9c32db12b01224b2a199b16a914
--- /dev/null
+++ b/src/app/shared/mydspace-actions/claimed-task/return-to-pool/claimed-task-actions-return-to-pool.component.spec.ts
@@ -0,0 +1,65 @@
+import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { By } from '@angular/platform-browser';
+import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
+
+import { ClaimedTaskActionsReturnToPoolComponent } from './claimed-task-actions-return-to-pool.component';
+import { MockTranslateLoader } from '../../../mocks/mock-translate-loader';
+
+let component: ClaimedTaskActionsReturnToPoolComponent;
+let fixture: ComponentFixture<ClaimedTaskActionsReturnToPoolComponent>;
+
+describe('ClaimedTaskActionsReturnToPoolComponent', () => {
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [
+        TranslateModule.forRoot({
+          loader: {
+            provide: TranslateLoader,
+            useClass: MockTranslateLoader
+          }
+        })
+      ],
+      declarations: [ClaimedTaskActionsReturnToPoolComponent],
+      schemas: [NO_ERRORS_SCHEMA]
+    }).overrideComponent(ClaimedTaskActionsReturnToPoolComponent, {
+      set: { changeDetection: ChangeDetectionStrategy.Default }
+    }).compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(ClaimedTaskActionsReturnToPoolComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  afterEach(() => {
+    fixture = null;
+    component = null;
+  });
+
+  it('should display return to pool button', () => {
+    const btn = fixture.debugElement.query(By.css('.btn-secondary'));
+
+    expect(btn).toBeDefined();
+  });
+
+  it('should display spin icon when return to pool action is pending', () => {
+    component.processingReturnToPool = true;
+    fixture.detectChanges();
+
+    const span = fixture.debugElement.query(By.css('.btn-secondary .fa-spin'));
+
+    expect(span).toBeDefined();
+  });
+
+  it('should emit return to pool event', () => {
+    spyOn(component.returnToPool, 'emit');
+
+    component.confirmReturnToPool();
+    fixture.detectChanges();
+
+    expect(component.returnToPool.emit).toHaveBeenCalled();
+  });
+
+});
diff --git a/src/app/shared/mydspace-actions/claimed-task/return-to-pool/claimed-task-actions-return-to-pool.component.ts b/src/app/shared/mydspace-actions/claimed-task/return-to-pool/claimed-task-actions-return-to-pool.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1dfe91eb5b88dfd5a14ec16c874fbb1fa653da23
--- /dev/null
+++ b/src/app/shared/mydspace-actions/claimed-task/return-to-pool/claimed-task-actions-return-to-pool.component.ts
@@ -0,0 +1,32 @@
+import { Component, EventEmitter, Input, Output } from '@angular/core';
+
+@Component({
+  selector: 'ds-claimed-task-actions-return-to-pool',
+  styleUrls: ['./claimed-task-actions-return-to-pool.component.scss'],
+  templateUrl: './claimed-task-actions-return-to-pool.component.html',
+})
+
+export class ClaimedTaskActionsReturnToPoolComponent {
+
+  /**
+   * A boolean representing if a return to pool operation is pending
+   */
+  @Input() processingReturnToPool: boolean;
+
+  /**
+   * CSS classes to append to return to pool button
+   */
+  @Input() wrapperClass: string;
+
+  /**
+   * An event fired when a return to pool action is confirmed.
+   */
+  @Output() returnToPool: EventEmitter<any> = new EventEmitter<any>();
+
+  /**
+   * Emit returnToPool event
+   */
+  confirmReturnToPool() {
+    this.returnToPool.emit();
+  }
+}
diff --git a/src/app/shared/mydspace-actions/item/item-actions.component.html b/src/app/shared/mydspace-actions/item/item-actions.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..91993466b61e1ca3ba690f40192159e4253606f1
--- /dev/null
+++ b/src/app/shared/mydspace-actions/item/item-actions.component.html
@@ -0,0 +1,5 @@
+<button class="btn btn-primary mt-1 mb-3"
+            ngbTooltip="{{'submission.workflow.generic.view-help' | translate}}"
+            [routerLink]="['/items/' + object.id]">
+  <i class="fa fa-info-circle"></i> {{"submission.workflow.generic.view" | translate}}
+</button>
diff --git a/src/app/shared/mydspace-actions/item/item-actions.component.scss b/src/app/shared/mydspace-actions/item/item-actions.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/app/shared/mydspace-actions/item/item-actions.component.spec.ts b/src/app/shared/mydspace-actions/item/item-actions.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..72be122c8f084ff035dd5e1f1dea576cbf77877a
--- /dev/null
+++ b/src/app/shared/mydspace-actions/item/item-actions.component.spec.ts
@@ -0,0 +1,96 @@
+import { ChangeDetectionStrategy, Injector, NO_ERRORS_SCHEMA } from '@angular/core';
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { Router } from '@angular/router';
+
+import { of as observableOf } from 'rxjs';
+import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
+
+import { MockTranslateLoader } from '../../mocks/mock-translate-loader';
+import { RouterStub } from '../../testing/router-stub';
+import { Item } from '../../../core/shared/item.model';
+import { ItemActionsComponent } from './item-actions.component';
+import { ItemDataService } from '../../../core/data/item-data.service';
+import { NotificationsService } from '../../notifications/notifications.service';
+import { NotificationsServiceStub } from '../../testing/notifications-service-stub';
+
+let component: ItemActionsComponent;
+let fixture: ComponentFixture<ItemActionsComponent>;
+
+let mockObject: Item;
+
+const mockDataService = {};
+
+mockObject = Object.assign(new Item(), {
+  bitstreams: observableOf({}),
+  metadata: {
+    'dc.title': [
+      {
+        language: 'en_US',
+        value: 'This is just another title'
+      }
+    ],
+    'dc.type': [
+      {
+        language: null,
+        value: 'Article'
+      }
+    ],
+    'dc.contributor.author': [
+      {
+        language: 'en_US',
+        value: 'Smith, Donald'
+      }
+    ],
+    'dc.date.issued': [
+      {
+        language: null,
+        value: '2015-06-26'
+      }
+    ]
+  }
+});
+
+describe('ItemActionsComponent', () => {
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [
+        TranslateModule.forRoot({
+          loader: {
+            provide: TranslateLoader,
+            useClass: MockTranslateLoader
+          }
+        })
+      ],
+      declarations: [ItemActionsComponent],
+      providers: [
+        { provide: Injector, useValue: {} },
+        { provide: Router, useValue: new RouterStub() },
+        { provide: ItemDataService, useValue: mockDataService },
+        { provide: NotificationsService, useValue: new NotificationsServiceStub() },
+      ],
+      schemas: [NO_ERRORS_SCHEMA]
+    }).overrideComponent(ItemActionsComponent, {
+      set: { changeDetection: ChangeDetectionStrategy.Default }
+    }).compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(ItemActionsComponent);
+    component = fixture.componentInstance;
+    component.object = mockObject;
+    fixture.detectChanges();
+  });
+
+  afterEach(() => {
+    fixture = null;
+    component = null;
+  });
+
+  it('should init object properly', () => {
+    component.object = null;
+    component.initObjects(mockObject);
+
+    expect(component.object).toEqual(mockObject);
+  });
+
+});
diff --git a/src/app/shared/mydspace-actions/item/item-actions.component.ts b/src/app/shared/mydspace-actions/item/item-actions.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0760fe54e03d600543dca7e1de589fc1300ee706
--- /dev/null
+++ b/src/app/shared/mydspace-actions/item/item-actions.component.ts
@@ -0,0 +1,52 @@
+import { Component, Injector, Input } from '@angular/core';
+import { Router } from '@angular/router';
+
+import { TranslateService } from '@ngx-translate/core';
+
+import { MyDSpaceActionsComponent } from '../mydspace-actions';
+import { ItemDataService } from '../../../core/data/item-data.service';
+import { Item } from '../../../core/shared/item.model';
+import { ResourceType } from '../../../core/shared/resource-type';
+import { NotificationsService } from '../../notifications/notifications.service';
+
+/**
+ * This component represents mydspace actions related to Item object.
+ */
+@Component({
+  selector: 'ds-item-actions',
+  styleUrls: ['./item-actions.component.scss'],
+  templateUrl: './item-actions.component.html',
+})
+
+export class ItemActionsComponent extends MyDSpaceActionsComponent<Item, ItemDataService> {
+
+  /**
+   * The Item object
+   */
+  @Input() object: Item;
+
+  /**
+   * Initialize instance variables
+   *
+   * @param {Injector} injector
+   * @param {Router} router
+   * @param {NotificationsService} notificationsService
+   * @param {TranslateService} translate
+   */
+  constructor(protected injector: Injector,
+              protected router: Router,
+              protected notificationsService: NotificationsService,
+              protected translate: TranslateService) {
+    super(ResourceType.Item, injector, router, notificationsService, translate);
+  }
+
+  /**
+   * Init the target object
+   *
+   * @param {Item} object
+   */
+  initObjects(object: Item) {
+    this.object = object;
+  }
+
+}
diff --git a/src/app/shared/mydspace-actions/mydspace-actions-service.factory.ts b/src/app/shared/mydspace-actions/mydspace-actions-service.factory.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7aa948f68905a84b1b7df59473239e92bad91128
--- /dev/null
+++ b/src/app/shared/mydspace-actions/mydspace-actions-service.factory.ts
@@ -0,0 +1,36 @@
+import { DataService } from '../../core/data/data.service';
+import { ResourceType } from '../../core/shared/resource-type';
+import { WorkspaceitemDataService } from '../../core/submission/workspaceitem-data.service';
+import { ClaimedTaskDataService } from '../../core/tasks/claimed-task-data.service';
+import { PoolTaskDataService } from '../../core/tasks/pool-task-data.service';
+import { WorkflowitemDataService } from '../../core/submission/workflowitem-data.service';
+import { CacheableObject } from '../../core/cache/object-cache.reducer';
+import { ItemDataService } from '../../core/data/item-data.service';
+
+/**
+ * Class to return DataService for given ResourceType
+ */
+export class MydspaceActionsServiceFactory<T extends CacheableObject, TService extends DataService<T>> {
+  public getConstructor(type: ResourceType): TService {
+    switch (type) {
+      case ResourceType.Item: {
+        return ItemDataService as any;
+      }
+      case ResourceType.Workspaceitem: {
+        return WorkspaceitemDataService as any;
+      }
+      case ResourceType.Workflowitem: {
+        return WorkflowitemDataService as any;
+      }
+      case ResourceType.ClaimedTask: {
+        return ClaimedTaskDataService as any;
+      }
+      case ResourceType.PoolTask: {
+        return PoolTaskDataService as any;
+      }
+      default: {
+        return undefined;
+      }
+    }
+  }
+}
diff --git a/src/app/shared/mydspace-actions/mydspace-actions.ts b/src/app/shared/mydspace-actions/mydspace-actions.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8e465644c36fac4c348614f741968a9dd95aa6f2
--- /dev/null
+++ b/src/app/shared/mydspace-actions/mydspace-actions.ts
@@ -0,0 +1,99 @@
+import { Router } from '@angular/router';
+import { Injector, Input } from '@angular/core';
+
+import { find } from 'rxjs/operators';
+
+import { MydspaceActionsServiceFactory } from './mydspace-actions-service.factory';
+import { RemoteData } from '../../core/data/remote-data';
+import { DataService } from '../../core/data/data.service';
+import { DSpaceObject } from '../../core/shared/dspace-object.model';
+import { ResourceType } from '../../core/shared/resource-type';
+import { NotificationOptions } from '../notifications/models/notification-options.model';
+import { NotificationsService } from '../notifications/notifications.service';
+import { TranslateService } from '@ngx-translate/core';
+
+/**
+ * Abstract class for all different representations of mydspace actions
+ */
+export abstract class MyDSpaceActionsComponent<T extends DSpaceObject, TService extends DataService<T>> {
+
+  /**
+   * The target mydspace object
+   */
+  @Input() abstract object: T;
+
+  /**
+   * Instance of DataService realted to mydspace object
+   */
+  protected objectDataService: TService;
+
+  /**
+   * Initialize instance variables
+   *
+   * @param {ResourceType} objectType
+   * @param {Injector} injector
+   * @param {Router} router
+   * @param {NotificationsService} notificationsService
+   * @param {TranslateService} translate
+   */
+  constructor(
+    protected objectType: ResourceType,
+    protected injector: Injector,
+    protected router: Router,
+    protected notificationsService: NotificationsService,
+    protected translate: TranslateService) {
+    const factory = new MydspaceActionsServiceFactory<T, TService>();
+    this.objectDataService = injector.get(factory.getConstructor(objectType));
+  }
+
+  /**
+   * Abstract method called to init the target object
+   *
+   * @param {T} object
+   */
+  abstract initObjects(object: T): void;
+
+  /**
+   * Refresh current page
+   */
+  reload(): void {
+    // override the route reuse strategy
+    this.router.routeReuseStrategy.shouldReuseRoute = () => {
+      return false;
+    };
+    this.router.navigated = false;
+    const url = decodeURIComponent(this.router.url);
+    this.router.navigateByUrl(url);
+  }
+
+  /**
+   * Override the target object with a refreshed one
+   */
+  refresh(): void {
+    // find object by id
+    this.objectDataService.findById(this.object.id).pipe(
+      find((rd: RemoteData<T>) => rd.hasSucceeded)
+    ).subscribe((rd: RemoteData<T>) => {
+      this.initObjects(rd.payload as T);
+    });
+  }
+
+  /**
+   * Handle action response and show properly notification
+   *
+   * @param result
+   *    true on success, false otherwise
+   */
+  handleActionResponse(result: boolean): void {
+    if (result) {
+      this.reload();
+      this.notificationsService.success(null,
+        this.translate.get('submission.workflow.tasks.generic.success'),
+        new NotificationOptions(5000, false));
+    } else {
+      this.notificationsService.error(null,
+        this.translate.get('submission.workflow.tasks.generic.error'),
+        new NotificationOptions(20000, true));
+    }
+  }
+}
diff --git a/src/app/shared/mydspace-actions/pool-task/pool-task-actions.component.html b/src/app/shared/mydspace-actions/pool-task/pool-task-actions.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..6f4ffffad362addeb91ae0000f4fbccb4ad0458f
--- /dev/null
+++ b/src/app/shared/mydspace-actions/pool-task/pool-task-actions.component.html
@@ -0,0 +1,8 @@
+<button type="button"
+        class="btn btn-info mt-1 mb-3"
+        ngbTooltip="{{'submission.workflow.tasks.pool.claim_help' | translate}}"
+        [disabled]="(processingClaim$ | async)"
+        (click)="claim()">
+  <span *ngIf="(processingClaim$ | async)"><i class='fas fa-circle-notch fa-spin'></i> {{'submission.workflow.tasks.generic.processing' | translate}}</span>
+  <span *ngIf="!(processingClaim$ | async)"><i class="fas fa-hand-paper"></i> {{'submission.workflow.tasks.pool.claim' | translate}}</span>
+</button>
diff --git a/src/app/shared/mydspace-actions/pool-task/pool-task-actions.component.scss b/src/app/shared/mydspace-actions/pool-task/pool-task-actions.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/app/shared/mydspace-actions/pool-task/pool-task-actions.component.spec.ts b/src/app/shared/mydspace-actions/pool-task/pool-task-actions.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1c0e8e71facba6c0836b1561deb1cf13446f3dc2
--- /dev/null
+++ b/src/app/shared/mydspace-actions/pool-task/pool-task-actions.component.spec.ts
@@ -0,0 +1,170 @@
+import { ChangeDetectionStrategy, Injector, NO_ERRORS_SCHEMA } from '@angular/core';
+import { async, ComponentFixture, fakeAsync, TestBed } from '@angular/core/testing';
+import { Router } from '@angular/router';
+import { By } from '@angular/platform-browser';
+
+import { of as observableOf } from 'rxjs';
+import { cold } from 'jasmine-marbles';
+import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
+
+import { MockTranslateLoader } from '../../mocks/mock-translate-loader';
+import { NotificationsService } from '../../notifications/notifications.service';
+import { NotificationsServiceStub } from '../../testing/notifications-service-stub';
+import { RouterStub } from '../../testing/router-stub';
+import { RemoteData } from '../../../core/data/remote-data';
+import { Item } from '../../../core/shared/item.model';
+import { PoolTaskDataService } from '../../../core/tasks/pool-task-data.service';
+import { PoolTaskActionsComponent } from './pool-task-actions.component';
+import { PoolTask } from '../../../core/tasks/models/pool-task-object.model';
+import { Workflowitem } from '../../../core/submission/models/workflowitem.model';
+
+let component: PoolTaskActionsComponent;
+let fixture: ComponentFixture<PoolTaskActionsComponent>;
+
+let mockObject: PoolTask;
+let notificationsServiceStub: NotificationsServiceStub;
+let router: RouterStub;
+
+const mockDataService = jasmine.createSpyObj('PoolTaskDataService', {
+  claimTask: jasmine.createSpy('claimTask')
+});
+
+const item = Object.assign(new Item(), {
+  bitstreams: observableOf({}),
+  metadata: {
+    'dc.title': [
+      {
+        language: 'en_US',
+        value: 'This is just another title'
+      }
+    ],
+    'dc.type': [
+      {
+        language: null,
+        value: 'Article'
+      }
+    ],
+    'dc.contributor.author': [
+      {
+        language: 'en_US',
+        value: 'Smith, Donald'
+      }
+    ],
+    'dc.date.issued': [
+      {
+        language: null,
+        value: '2015-06-26'
+      }
+    ]
+  }
+});
+const rdItem = new RemoteData(false, false, true, null, item);
+const workflowitem = Object.assign(new Workflowitem(), { item: observableOf(rdItem) });
+const rdWorkflowitem = new RemoteData(false, false, true, null, workflowitem);
+mockObject = Object.assign(new PoolTask(), { workflowitem: observableOf(rdWorkflowitem), id: '1234' });
+
+describe('PoolTaskActionsComponent', () => {
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [
+        TranslateModule.forRoot({
+          loader: {
+            provide: TranslateLoader,
+            useClass: MockTranslateLoader
+          }
+        })
+      ],
+      declarations: [PoolTaskActionsComponent],
+      providers: [
+        { provide: Injector, useValue: {} },
+        { provide: NotificationsService, useValue: new NotificationsServiceStub() },
+        { provide: Router, useValue: new RouterStub() },
+        { provide: PoolTaskDataService, useValue: mockDataService },
+      ],
+      schemas: [NO_ERRORS_SCHEMA]
+    }).overrideComponent(PoolTaskActionsComponent, {
+      set: { changeDetection: ChangeDetectionStrategy.Default }
+    }).compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(PoolTaskActionsComponent);
+    component = fixture.componentInstance;
+    component.object = mockObject;
+    notificationsServiceStub = TestBed.get(NotificationsService);
+    router = TestBed.get(Router);
+    fixture.detectChanges();
+  });
+
+  afterEach(() => {
+    fixture = null;
+    component = null;
+  });
+
+  it('should init objects properly', () => {
+    component.object = null;
+    component.initObjects(mockObject);
+
+    expect(component.object).toEqual(mockObject);
+
+    expect(component.workflowitem$).toBeObservable(cold('(b|)', {
+      b: rdWorkflowitem.payload
+    }))
+  });
+
+  it('should display claim task button', () => {
+    const btn = fixture.debugElement.query(By.css('.btn-info'));
+
+    expect(btn).toBeDefined();
+  });
+
+  it('should call claimTask method on claim', fakeAsync(() => {
+    spyOn(component, 'reload');
+    mockDataService.claimTask.and.returnValue(observableOf({hasSucceeded: true}));
+
+    component.claim();
+    fixture.detectChanges();
+
+    fixture.whenStable().then(() => {
+      expect(mockDataService.claimTask).toHaveBeenCalledWith(mockObject.id);
+    });
+
+  }));
+
+  it('should display a success notification on claim success', async(() => {
+    spyOn(component, 'reload');
+    mockDataService.claimTask.and.returnValue(observableOf({hasSucceeded: true}));
+
+    component.claim();
+    fixture.detectChanges();
+
+    fixture.whenStable().then(() => {
+      expect(notificationsServiceStub.success).toHaveBeenCalled();
+    });
+  }));
+
+  it('should reload page on claim success', async(() => {
+    spyOn(router, 'navigateByUrl');
+    router.url = 'test.url/test';
+    mockDataService.claimTask.and.returnValue(observableOf({hasSucceeded: true}));
+
+    component.claim();
+    fixture.detectChanges();
+
+    fixture.whenStable().then(() => {
+      expect(router.navigateByUrl).toHaveBeenCalledWith('test.url/test');
+    });
+  }));
+
+  it('should display an error notification on claim failure', async(() => {
+    mockDataService.claimTask.and.returnValue(observableOf({hasSucceeded: false}));
+
+    component.claim();
+    fixture.detectChanges();
+
+    fixture.whenStable().then(() => {
+      expect(notificationsServiceStub.error).toHaveBeenCalled();
+    });
+  }));
+
+});
diff --git a/src/app/shared/mydspace-actions/pool-task/pool-task-actions.component.ts b/src/app/shared/mydspace-actions/pool-task/pool-task-actions.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..bd8f3f1a373a2b3e995add3ccf4b2948ceec2cc2
--- /dev/null
+++ b/src/app/shared/mydspace-actions/pool-task/pool-task-actions.component.ts
@@ -0,0 +1,89 @@
+import { Component, Injector, Input } from '@angular/core';
+import { Router } from '@angular/router';
+
+import { BehaviorSubject, Observable } from 'rxjs';
+import { filter, map } from 'rxjs/operators';
+import { TranslateService } from '@ngx-translate/core';
+
+import { Workflowitem } from '../../../core/submission/models/workflowitem.model';
+import { ProcessTaskResponse } from '../../../core/tasks/models/process-task-response';
+import { RemoteData } from '../../../core/data/remote-data';
+import { PoolTask } from '../../../core/tasks/models/pool-task-object.model';
+import { PoolTaskDataService } from '../../../core/tasks/pool-task-data.service';
+import { isNotUndefined } from '../../empty.util';
+import { MyDSpaceActionsComponent } from '../mydspace-actions';
+import { ResourceType } from '../../../core/shared/resource-type';
+import { NotificationsService } from '../../notifications/notifications.service';
+
+/**
+ * This component represents mydspace actions related to PoolTask object.
+ */
+@Component({
+  selector: 'ds-pool-task-actions',
+  styleUrls: ['./pool-task-actions.component.scss'],
+  templateUrl: './pool-task-actions.component.html',
+})
+export class PoolTaskActionsComponent extends MyDSpaceActionsComponent<PoolTask, PoolTaskDataService> {
+
+  /**
+   * The PoolTask object
+   */
+  @Input() object: PoolTask;
+
+  /**
+   * A boolean representing if a claim operation is pending
+   * @type {BehaviorSubject<boolean>}
+   */
+  public processingClaim$ = new BehaviorSubject<boolean>(false);
+
+  /**
+   * The workflowitem object that belonging to the PoolTask object
+   */
+  public workflowitem$: Observable<Workflowitem>;
+
+  /**
+   * Initialize instance variables
+   *
+   * @param {Injector} injector
+   * @param {Router} router
+   * @param {NotificationsService} notificationsService
+   * @param {TranslateService} translate
+   */
+  constructor(protected injector: Injector,
+              protected router: Router,
+              protected notificationsService: NotificationsService,
+              protected translate: TranslateService) {
+    super(ResourceType.PoolTask, injector, router, notificationsService, translate);
+  }
+
+  /**
+   * Initialize objects
+   */
+  ngOnInit() {
+    this.initObjects(this.object);
+  }
+
+  /**
+   * Init the PoolTask and Workflowitem objects
+   *
+   * @param {PoolTask} object
+   */
+  initObjects(object: PoolTask) {
+    this.object = object;
+    this.workflowitem$ = (this.object.workflowitem as Observable<RemoteData<Workflowitem>>).pipe(
+      filter((rd: RemoteData<Workflowitem>) => ((!rd.isRequestPending) && isNotUndefined(rd.payload))),
+      map((rd: RemoteData<Workflowitem>) => rd.payload));
+  }
+
+  /**
+   * Claim the task.
+   */
+  claim() {
+    this.processingClaim$.next(true);
+    this.objectDataService.claimTask(this.object.id)
+      .subscribe((res: ProcessTaskResponse) => {
+        this.handleActionResponse(res.hasSucceeded);
+        this.processingClaim$.next(false);
+      });
+  }
+}
diff --git a/src/app/shared/mydspace-actions/workflowitem/workflowitem-actions.component.html b/src/app/shared/mydspace-actions/workflowitem/workflowitem-actions.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/app/shared/mydspace-actions/workflowitem/workflowitem-actions.component.scss b/src/app/shared/mydspace-actions/workflowitem/workflowitem-actions.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/app/shared/mydspace-actions/workflowitem/workflowitem-actions.component.spec.ts b/src/app/shared/mydspace-actions/workflowitem/workflowitem-actions.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7533565afeb470cd513a80d93cd318e4e3152065
--- /dev/null
+++ b/src/app/shared/mydspace-actions/workflowitem/workflowitem-actions.component.spec.ts
@@ -0,0 +1,98 @@
+import { ChangeDetectionStrategy, Injector, NO_ERRORS_SCHEMA } from '@angular/core';
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { Router } from '@angular/router';
+
+import { of as observableOf } from 'rxjs';
+import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
+
+import { MockTranslateLoader } from '../../mocks/mock-translate-loader';
+import { RouterStub } from '../../testing/router-stub';
+import { RemoteData } from '../../../core/data/remote-data';
+import { Item } from '../../../core/shared/item.model';
+import { Workflowitem } from '../../../core/submission/models/workflowitem.model';
+import { WorkflowitemActionsComponent } from './workflowitem-actions.component';
+import { WorkflowitemDataService } from '../../../core/submission/workflowitem-data.service';
+import { NotificationsService } from '../../notifications/notifications.service';
+import { NotificationsServiceStub } from '../../testing/notifications-service-stub';
+
+let component: WorkflowitemActionsComponent;
+let fixture: ComponentFixture<WorkflowitemActionsComponent>;
+
+let mockObject: Workflowitem;
+
+const mockDataService = {};
+
+const item = Object.assign(new Item(), {
+  bitstreams: observableOf({}),
+  metadata: {
+    'dc.title': [
+      {
+        language: 'en_US',
+        value: 'This is just another title'
+      }
+    ],
+    'dc.type': [
+      {
+        language: null,
+        value: 'Article'
+      }
+    ],
+    'dc.contributor.author': [
+      {
+        language: 'en_US',
+        value: 'Smith, Donald'
+      }
+    ],
+    'dc.date.issued': [
+      {
+        language: null,
+        value: '2015-06-26'
+      }
+    ]
+  }
+});
+const rd = new RemoteData(false, false, true, null, item);
+mockObject = Object.assign(new Workflowitem(), { item: observableOf(rd), id: '1234', uuid: '1234' });
+
+describe('WorkflowitemActionsComponent', () => {
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [
+        TranslateModule.forRoot({
+          loader: {
+            provide: TranslateLoader,
+            useClass: MockTranslateLoader
+          }
+        })
+      ],
+      declarations: [WorkflowitemActionsComponent],
+      providers: [
+        { provide: Injector, useValue: {} },
+        { provide: Router, useValue: new RouterStub() },
+        { provide: WorkflowitemDataService, useValue: mockDataService },
+        { provide: NotificationsService, useValue: new NotificationsServiceStub() },
+      ],
+      schemas: [NO_ERRORS_SCHEMA]
+    }).overrideComponent(WorkflowitemActionsComponent, {
+      set: { changeDetection: ChangeDetectionStrategy.Default }
+    }).compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(WorkflowitemActionsComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  afterEach(() => {
+    fixture = null;
+    component = null;
+  });
+
+  it('should init object properly', () => {
+    component.initObjects(mockObject);
+
+    expect(component.object).toEqual(mockObject);
+  });
+
+});
diff --git a/src/app/shared/mydspace-actions/workflowitem/workflowitem-actions.component.ts b/src/app/shared/mydspace-actions/workflowitem/workflowitem-actions.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a6304bf5d400a736cb5ac87b1046c231c1caf566
--- /dev/null
+++ b/src/app/shared/mydspace-actions/workflowitem/workflowitem-actions.component.ts
@@ -0,0 +1,51 @@
+import { Component, Injector, Input } from '@angular/core';
+import { Router } from '@angular/router';
+
+import { TranslateService } from '@ngx-translate/core';
+
+import { MyDSpaceActionsComponent } from '../mydspace-actions';
+import { Workflowitem } from '../../../core/submission/models/workflowitem.model';
+import { WorkflowitemDataService } from '../../../core/submission/workflowitem-data.service';
+import { ResourceType } from '../../../core/shared/resource-type';
+import { NotificationsService } from '../../notifications/notifications.service';
+
+/**
+ * This component represents mydspace actions related to Workflowitem object.
+ */
+@Component({
+  selector: 'ds-workflowitem-actions',
+  styleUrls: ['./workflowitem-actions.component.scss'],
+  templateUrl: './workflowitem-actions.component.html',
+})
+export class WorkflowitemActionsComponent extends MyDSpaceActionsComponent<Workflowitem, WorkflowitemDataService> {
+
+  /**
+   * The Workflowitem object
+   */
+  @Input() object: Workflowitem;
+
+  /**
+   * Initialize instance variables
+   *
+   * @param {Injector} injector
+   * @param {Router} router
+   * @param {NotificationsService} notificationsService
+   * @param {TranslateService} translate
+   */
+  constructor(protected injector: Injector,
+              protected router: Router,
+              protected notificationsService: NotificationsService,
+              protected translate: TranslateService) {
+    super(ResourceType.Workflowitem, injector, router, notificationsService, translate);
+  }
+
+  /**
+   * Init the target object
+   *
+   * @param {Workflowitem} object
+   */
+  initObjects(object: Workflowitem) {
+    this.object = object;
+  }
+
+}
diff --git a/src/app/shared/mydspace-actions/workspaceitem/workspaceitem-actions.component.html b/src/app/shared/mydspace-actions/workspaceitem/workspaceitem-actions.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..6c3a047f73897cda0c521708987fc5c17b396bc9
--- /dev/null
+++ b/src/app/shared/mydspace-actions/workspaceitem/workspaceitem-actions.component.html
@@ -0,0 +1,30 @@
+<a class="btn btn-primary mt-1 mb-3"
+   ngbTooltip="{{'submission.workflow.generic.edit-help' | translate}}"
+   [routerLink]="['/workspaceitems/' + object.id + '/edit']"
+   role="button">
+  <i class="fa fa-edit"></i> {{'submission.workflow.generic.edit' | translate}}
+</a>
+
+<button type="button"
+        class="btn btn-danger mt-1 mb-3"
+        ngbTooltip="{{'submission.workflow.generic.delete-help' | translate}}"
+        (click)="$event.preventDefault();confirmDiscard(content)">
+  <span *ngIf="(processingDelete$ | async)"><i class='fas fa-circle-notch fa-spin'></i> {{'submission.workflow.tasks.generic.processing' | translate}}</span>
+  <span *ngIf="!(processingDelete$ | async)"><i class="fa fa-trash"></i> {{'submission.workflow.generic.delete' | translate}}</span>
+</button>
+
+<ng-template #content let-c="close" let-d="dismiss">
+  <div class="modal-header">
+    <h4 class="modal-title text-danger">{{'submission.general.discard.confirm.title' | translate}}</h4>
+    <button type="button" class="close" aria-label="Close" (click)="d('cancel')">
+      <span aria-hidden="true">&times;</span>
+    </button>
+  </div>
+  <div class="modal-body">
+    <p>{{'submission.general.discard.confirm.info' | translate}}</p>
+  </div>
+  <div class="modal-footer">
+    <button type="button" class="btn btn-secondary" (click)="c('cancel')">{{'submission.general.discard.confirm.cancel' | translate}}</button>
+    <button type="button" class="btn btn-danger" (click)="c('ok')">{{'submission.general.discard.confirm.submit' | translate}}</button>
+  </div>
+</ng-template>
diff --git a/src/app/shared/mydspace-actions/workspaceitem/workspaceitem-actions.component.scss b/src/app/shared/mydspace-actions/workspaceitem/workspaceitem-actions.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/app/shared/mydspace-actions/workspaceitem/workspaceitem-actions.component.spec.ts b/src/app/shared/mydspace-actions/workspaceitem/workspaceitem-actions.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ec8bc4a11c2cf8483b85d26e9b5794bd6f4f665c
--- /dev/null
+++ b/src/app/shared/mydspace-actions/workspaceitem/workspaceitem-actions.component.spec.ts
@@ -0,0 +1,163 @@
+import { ChangeDetectionStrategy, Injector, NO_ERRORS_SCHEMA } from '@angular/core';
+import { async, ComponentFixture, fakeAsync, TestBed } from '@angular/core/testing';
+import { Router } from '@angular/router';
+import { By } from '@angular/platform-browser';
+
+import { of as observableOf } from 'rxjs';
+import { NgbModal, NgbModule } from '@ng-bootstrap/ng-bootstrap';
+import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
+
+import { MockTranslateLoader } from '../../mocks/mock-translate-loader';
+import { NotificationsService } from '../../notifications/notifications.service';
+import { NotificationsServiceStub } from '../../testing/notifications-service-stub';
+import { RouterStub } from '../../testing/router-stub';
+import { RemoteData } from '../../../core/data/remote-data';
+import { Item } from '../../../core/shared/item.model';
+import { Workspaceitem } from '../../../core/submission/models/workspaceitem.model';
+import { WorkspaceitemActionsComponent } from './workspaceitem-actions.component';
+import { WorkspaceitemDataService } from '../../../core/submission/workspaceitem-data.service';
+
+let component: WorkspaceitemActionsComponent;
+let fixture: ComponentFixture<WorkspaceitemActionsComponent>;
+
+let mockObject: Workspaceitem;
+let notificationsServiceStub: NotificationsServiceStub;
+
+const mockDataService = jasmine.createSpyObj('WorkspaceitemDataService', {
+  delete: jasmine.createSpy('delete')
+});
+
+const item = Object.assign(new Item(), {
+  bitstreams: observableOf({}),
+  metadata: {
+    'dc.title': [
+      {
+        language: 'en_US',
+        value: 'This is just another title'
+      }
+    ],
+    'dc.type': [
+      {
+        language: null,
+        value: 'Article'
+      }
+    ],
+    'dc.contributor.author': [
+      {
+        language: 'en_US',
+        value: 'Smith, Donald'
+      }
+    ],
+    'dc.date.issued': [
+      {
+        language: null,
+        value: '2015-06-26'
+      }
+    ]
+  }
+});
+const rd = new RemoteData(false, false, true, null, item);
+mockObject = Object.assign(new Workspaceitem(), { item: observableOf(rd), id: '1234', uuid: '1234' });
+
+describe('WorkspaceitemActionsComponent', () => {
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [
+        NgbModule.forRoot(),
+        TranslateModule.forRoot({
+          loader: {
+            provide: TranslateLoader,
+            useClass: MockTranslateLoader
+          }
+        })
+      ],
+      declarations: [WorkspaceitemActionsComponent],
+      providers: [
+        { provide: Injector, useValue: {} },
+        { provide: NotificationsService, useValue: new NotificationsServiceStub() },
+        { provide: Router, useValue: new RouterStub() },
+        { provide: WorkspaceitemDataService, useValue: mockDataService },
+        NgbModal
+      ],
+      schemas: [NO_ERRORS_SCHEMA]
+    }).overrideComponent(WorkspaceitemActionsComponent, {
+      set: { changeDetection: ChangeDetectionStrategy.Default }
+    }).compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(WorkspaceitemActionsComponent);
+    component = fixture.componentInstance;
+    component.object = mockObject;
+    notificationsServiceStub = TestBed.get(NotificationsService);
+    fixture.detectChanges();
+  });
+
+  afterEach(() => {
+    fixture = null;
+    component = null;
+  });
+
+  it('should init object properly', () => {
+    component.object = null;
+    component.initObjects(mockObject);
+
+    expect(component.object).toEqual(mockObject);
+  });
+
+  it('should display edit button', () => {
+    const btn = fixture.debugElement.query(By.css('.btn-primary'));
+
+    expect(btn).toBeDefined();
+  });
+
+  it('should display delete button', () => {
+    const btn = fixture.debugElement.query(By.css('.btn-danger'));
+
+    expect(btn).toBeDefined();
+  });
+
+  it('should call confirmDiscard on discard confirmation', fakeAsync(() => {
+    mockDataService.delete.and.returnValue(observableOf(true));
+    spyOn(component, 'reload');
+    const btn = fixture.debugElement.query(By.css('.btn-danger'));
+    btn.nativeElement.click();
+    fixture.detectChanges();
+
+    const confirmBtn: any = ((document as any).querySelector('.modal-footer .btn-danger'));
+    confirmBtn.click();
+
+    fixture.detectChanges();
+
+    fixture.whenStable().then(() => {
+      expect(mockDataService.delete).toHaveBeenCalledWith(mockObject);
+    });
+
+  }));
+
+  it('should display a success notification on delete success', async(() => {
+    spyOn((component as any).modalService, 'open').and.returnValue({result: Promise.resolve('ok')});
+    mockDataService.delete.and.returnValue(observableOf(true));
+    spyOn(component, 'reload');
+
+    component.confirmDiscard('ok');
+    fixture.detectChanges();
+
+    fixture.whenStable().then(() => {
+      expect(notificationsServiceStub.success).toHaveBeenCalled();
+    });
+  }));
+
+  it('should display an error notification on delete failure', async(() => {
+    spyOn((component as any).modalService, 'open').and.returnValue({result: Promise.resolve('ok')});
+    mockDataService.delete.and.returnValue(observableOf(false));
+    spyOn(component, 'reload');
+
+    component.confirmDiscard('ok');
+    fixture.detectChanges();
+
+    fixture.whenStable().then(() => {
+      expect(notificationsServiceStub.error).toHaveBeenCalled();
+    });
+  }));
+});
diff --git a/src/app/shared/mydspace-actions/workspaceitem/workspaceitem-actions.component.ts b/src/app/shared/mydspace-actions/workspaceitem/workspaceitem-actions.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..cea4c3746e321a96a833a945c1ab066a1f15f748
--- /dev/null
+++ b/src/app/shared/mydspace-actions/workspaceitem/workspaceitem-actions.component.ts
@@ -0,0 +1,79 @@
+import { Component, Injector, Input } from '@angular/core';
+import { Router } from '@angular/router';
+
+import { BehaviorSubject } from 'rxjs';
+import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
+import { TranslateService } from '@ngx-translate/core';
+
+import { Workspaceitem } from '../../../core/submission/models/workspaceitem.model';
+import { MyDSpaceActionsComponent } from '../mydspace-actions';
+import { WorkspaceitemDataService } from '../../../core/submission/workspaceitem-data.service';
+import { ResourceType } from '../../../core/shared/resource-type';
+import { NotificationsService } from '../../notifications/notifications.service';
+
+/**
+ * This component represents mydspace actions related to Workspaceitem object.
+ */
+@Component({
+  selector: 'ds-workspaceitem-actions',
+  styleUrls: ['./workspaceitem-actions.component.scss'],
+  templateUrl: './workspaceitem-actions.component.html',
+})
+export class WorkspaceitemActionsComponent extends MyDSpaceActionsComponent<Workspaceitem, WorkspaceitemDataService> {
+
+  /**
+   * The workspaceitem object
+   */
+  @Input() object: Workspaceitem;
+
+  /**
+   * A boolean representing if a delete operation is pending
+   * @type {BehaviorSubject<boolean>}
+   */
+  public processingDelete$ = new BehaviorSubject<boolean>(false);
+
+  /**
+   * Initialize instance variables
+   *
+   * @param {Injector} injector
+   * @param {Router} router
+   * @param {NgbModal} modalService
+   * @param {NotificationsService} notificationsService
+   * @param {TranslateService} translate
+   */
+  constructor(protected injector: Injector,
+              protected router: Router,
+              protected modalService: NgbModal,
+              protected notificationsService: NotificationsService,
+              protected translate: TranslateService) {
+    super(ResourceType.Workspaceitem, injector, router, notificationsService, translate);
+  }
+
+  /**
+   * Delete the target workspaceitem object
+   */
+  public confirmDiscard(content) {
+    this.modalService.open(content).result.then(
+      (result) => {
+        if (result === 'ok') {
+          this.processingDelete$.next(true);
+          this.objectDataService.delete(this.object)
+            .subscribe((response: boolean) => {
+              this.processingDelete$.next(false);
+              this.handleActionResponse(response);
+            })
+        }
+      }
+    );
+  }
+
+  /**
+   * Init the target object
+   *
+   * @param {Workspaceitem} object
+   */
+  initObjects(object: Workspaceitem) {
+    this.object = object;
+  }
+
+}
diff --git a/src/app/shared/object-collection/object-collection.component.html b/src/app/shared/object-collection/object-collection.component.html
index a81ee5a88274427e649ef1fed5bec9eb21441de0..5ba889892aa5ff435dba37cfde66f966b401a39e 100644
--- a/src/app/shared/object-collection/object-collection.component.html
+++ b/src/app/shared/object-collection/object-collection.component.html
@@ -1,6 +1,7 @@
 <ds-object-list [config]="config"
                 [sortConfig]="sortConfig"
                 [objects]="objects"
+                [hasBorder]="hasBorder"
                 [hideGear]="hideGear"
                 (paginationChange)="onPaginationChange($event)"
                 (pageChange)="onPageChange($event)"
@@ -22,4 +23,11 @@
                 *ngIf="getViewMode()===viewModeEnum.Grid">
 </ds-object-grid>
 
+<ds-object-detail [config]="config"
+                  [sortConfig]="sortConfig"
+                  [objects]="objects"
+                  [hideGear]="hideGear"
+                  *ngIf="getViewMode()===viewModeEnum.Detail">
+</ds-object-detail>
+
 
diff --git a/src/app/shared/object-collection/object-collection.component.spec.ts b/src/app/shared/object-collection/object-collection.component.spec.ts
index 66767ff78e54ed02d46cdbe22fe9cc711c6b62f3..aed2b2598d8034aabdc55d9496fd52f014450ec2 100644
--- a/src/app/shared/object-collection/object-collection.component.spec.ts
+++ b/src/app/shared/object-collection/object-collection.component.spec.ts
@@ -1,11 +1,13 @@
 import { ObjectCollectionComponent } from './object-collection.component';
+import { SetViewMode } from '../view-mode';
+import { element } from 'protractor';
 import { By } from '@angular/platform-browser';
 import { async, ComponentFixture, TestBed } from '@angular/core/testing';
-import { NO_ERRORS_SCHEMA } from '@angular/core';
+import { Config } from '../../../config/config.interface';
+import { DebugElement, NO_ERRORS_SCHEMA } from '@angular/core';
 import { ActivatedRoute, Router } from '@angular/router';
 import { of as observableOf } from 'rxjs';
 import { RouterStub } from '../testing/router-stub';
-import { ViewMode } from '../../core/shared/view-mode.model';
 
 describe('ObjectCollectionComponent', () => {
   let fixture: ComponentFixture<ObjectCollectionComponent>;
@@ -36,14 +38,14 @@ describe('ObjectCollectionComponent', () => {
 
   }));
   it('should only show the grid component when the viewmode is set to grid', () => {
-    objectCollectionComponent.currentMode = ViewMode.Grid;
+    objectCollectionComponent.currentMode = SetViewMode.Grid;
 
     expect(fixture.debugElement.query(By.css('ds-object-grid'))).toBeDefined();
     expect(fixture.debugElement.query(By.css('ds-object-list'))).toBeNull();
   });
 
   it('should only show the list component when the viewmode is set to list', () => {
-    objectCollectionComponent.currentMode = ViewMode.List;
+    objectCollectionComponent.currentMode = SetViewMode.List;
 
     expect(fixture.debugElement.query(By.css('ds-object-list'))).toBeDefined();
     expect(fixture.debugElement.query(By.css('ds-object-grid'))).toBeNull();
diff --git a/src/app/shared/object-collection/object-collection.component.ts b/src/app/shared/object-collection/object-collection.component.ts
index 0018c55c7fc0215be32eac0344294d744d549c1b..ccc1de1f2fbdc8fb6bfbef471526e30dbba3e534 100644
--- a/src/app/shared/object-collection/object-collection.component.ts
+++ b/src/app/shared/object-collection/object-collection.component.ts
@@ -1,23 +1,25 @@
-
-import {map} from 'rxjs/operators';
-import { Component, EventEmitter,
+import {
+  ChangeDetectorRef,
+  Component,
+  EventEmitter,
   Input,
+  OnChanges,
   OnInit,
-  Output, SimpleChanges, OnChanges, ChangeDetectorRef } from '@angular/core';
+  Output,
+  SimpleChanges
+} from '@angular/core';
 import { ActivatedRoute, Router } from '@angular/router';
 
 import { Observable } from 'rxjs';
+import { map } from 'rxjs/operators';
 
 import { RemoteData } from '../../core/data/remote-data';
 import { PageInfo } from '../../core/shared/page-info.model';
-
 import { PaginationComponentOptions } from '../pagination/pagination-component-options.model';
-
 import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model';
-
 import { ListableObject } from './shared/listable-object.model';
+import { SetViewMode } from '../view-mode';
 import { hasValue, isNotEmpty } from '../empty.util';
-import { ViewMode } from '../../core/shared/view-mode.model';
 
 @Component({
   selector: 'ds-viewable-collection',
@@ -29,6 +31,7 @@ export class ObjectCollectionComponent implements OnChanges, OnInit {
   @Input() objects: RemoteData<ListableObject[]>;
   @Input() config?: PaginationComponentOptions;
   @Input() sortConfig: SortOptions;
+  @Input() hasBorder = false;
   @Input() hideGear = false;
   pageInfo: Observable<PageInfo>;
   private sub;
@@ -58,8 +61,8 @@ export class ObjectCollectionComponent implements OnChanges, OnInit {
    */
   @Output() sortFieldChange: EventEmitter<string> = new EventEmitter<string>();
   data: any = {};
-  currentMode: ViewMode = ViewMode.List;
-  viewModeEnum = ViewMode;
+  currentMode: SetViewMode = SetViewMode.List;
+  viewModeEnum = SetViewMode;
 
   ngOnChanges(changes: SimpleChanges) {
     if (changes.objects && !changes.objects.isFirstChange()) {
@@ -80,16 +83,20 @@ export class ObjectCollectionComponent implements OnChanges, OnInit {
   }
 
   /**
+   * @param cdRef
+   *    ChangeDetectorRef service provided by Angular.
    * @param route
    *    Route is a singleton service provided by Angular.
    * @param router
    *    Router is a singleton service provided by Angular.
    */
-  constructor(private cdRef: ChangeDetectorRef, private route: ActivatedRoute,
-              private router: Router) {
+  constructor(
+    private cdRef: ChangeDetectorRef,
+    private route: ActivatedRoute,
+    private router: Router) {
   }
 
-  getViewMode(): ViewMode {
+  getViewMode(): SetViewMode {
     this.route.queryParams.pipe(map((params) => {
       if (isNotEmpty(params.view) && hasValue(params.view)) {
         this.currentMode = params.view;
diff --git a/src/app/shared/object-collection/shared/claimed-task-my-dspace-result.model.ts b/src/app/shared/object-collection/shared/claimed-task-my-dspace-result.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..051bfe6d9f9ef85049b47dde62fdb0bec5569789
--- /dev/null
+++ b/src/app/shared/object-collection/shared/claimed-task-my-dspace-result.model.ts
@@ -0,0 +1,11 @@
+import { ClaimedTask } from '../../../core/tasks/models/claimed-task-object.model';
+import { SearchResult } from '../../../+search-page/search-result.model';
+import { MyDSpaceConfigurationValueType } from '../../../+my-dspace-page/my-dspace-configuration-value-type';
+import { searchResultFor } from '../../../+search-page/search-service/search-result-element-decorator';
+
+/**
+ * Represents a search result object of a ClaimedTask object
+ */
+@searchResultFor(ClaimedTask, MyDSpaceConfigurationValueType.Workflow)
+export class ClaimedTaskMyDSpaceResult extends SearchResult<ClaimedTask> {
+}
diff --git a/src/app/shared/object-collection/shared/dso-element-decorator.spec.ts b/src/app/shared/object-collection/shared/dso-element-decorator.spec.ts
index 9243d13b8ec40f38d224bc4631e701d7e7a8d38f..12da0f5b362d2630f34d8031fc2189556f043d86 100644
--- a/src/app/shared/object-collection/shared/dso-element-decorator.spec.ts
+++ b/src/app/shared/object-collection/shared/dso-element-decorator.spec.ts
@@ -1,10 +1,10 @@
+import { SetViewMode } from '../../view-mode';
 import { renderElementsFor } from './dso-element-decorator';
 import { Item } from '../../../core/shared/item.model';
-import { ViewMode } from '../../../core/shared/view-mode.model';
 
 describe('ElementDecorator', () => {
-  const gridDecorator = renderElementsFor(Item, ViewMode.Grid);
-  const listDecorator = renderElementsFor(Item, ViewMode.List);
+  const gridDecorator = renderElementsFor(Item, SetViewMode.Grid);
+  const listDecorator = renderElementsFor(Item, SetViewMode.List);
   it('should have a decorator for both list and grid', () => {
     expect(listDecorator.length).not.toBeNull();
     expect(gridDecorator.length).not.toBeNull();
diff --git a/src/app/shared/object-collection/shared/dso-element-decorator.ts b/src/app/shared/object-collection/shared/dso-element-decorator.ts
index 98650fd25bb1699f555b15222a1ba6d6dab691f1..4e4d37579a2c022bbef6445d15ad8b51c0af6aa7 100644
--- a/src/app/shared/object-collection/shared/dso-element-decorator.ts
+++ b/src/app/shared/object-collection/shared/dso-element-decorator.ts
@@ -1,9 +1,9 @@
 import { GenericConstructor } from '../../../core/shared/generic-constructor';
 import { ListableObject } from './listable-object.model';
-import { ViewMode } from '../../../core/shared/view-mode.model';
+import { SetViewMode } from '../../view-mode';
 
 const dsoElementMap = new Map();
-export function renderElementsFor(listable: GenericConstructor<ListableObject>, viewMode: ViewMode) {
+export function renderElementsFor(listable: GenericConstructor<ListableObject>, viewMode: SetViewMode) {
   return function decorator(objectElement: any) {
     if (!objectElement) {
       return;
@@ -15,6 +15,6 @@ export function renderElementsFor(listable: GenericConstructor<ListableObject>,
   };
 }
 
-export function rendersDSOType(listable: GenericConstructor<ListableObject>, viewMode: ViewMode) {
+export function rendersDSOType(listable: GenericConstructor<ListableObject>, viewMode: SetViewMode) {
   return dsoElementMap.get(viewMode).get(listable);
 }
diff --git a/src/app/shared/object-collection/shared/item-my-dspace-result.model.ts b/src/app/shared/object-collection/shared/item-my-dspace-result.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..92724a762f8b84b5be1529658265acd15dfb063a
--- /dev/null
+++ b/src/app/shared/object-collection/shared/item-my-dspace-result.model.ts
@@ -0,0 +1,11 @@
+import { Item } from '../../../core/shared/item.model';
+import { SearchResult } from '../../../+search-page/search-result.model';
+import { searchResultFor } from '../../../+search-page/search-service/search-result-element-decorator';
+import { MyDSpaceConfigurationValueType } from '../../../+my-dspace-page/my-dspace-configuration-value-type';
+
+/**
+ * Represents a search result object of a Item object
+ */
+@searchResultFor(Item, MyDSpaceConfigurationValueType.Workspace)
+export class ItemMyDSpaceResult extends SearchResult<Item> {
+}
diff --git a/src/app/shared/object-collection/shared/mydspace-item-status/my-dspace-item-status-type.ts b/src/app/shared/object-collection/shared/mydspace-item-status/my-dspace-item-status-type.ts
new file mode 100644
index 0000000000000000000000000000000000000000..48a0a6f4a39789cb24759594e945bb17d1204c61
--- /dev/null
+++ b/src/app/shared/object-collection/shared/mydspace-item-status/my-dspace-item-status-type.ts
@@ -0,0 +1,7 @@
+export enum MyDspaceItemStatusType {
+  WORKFLOW = 'mydspace.status.workflow',
+  VALIDATION = 'mydspace.status.validation',
+  WAITING_CONTROLLER = 'mydspace.status.waiting-for-controller',
+  WORKSPACE = 'mydspace.status.workspace',
+  ARCHIVED = 'mydspace.status.archived'
+}
diff --git a/src/app/shared/object-collection/shared/mydspace-item-status/my-dspace-item-status.component.html b/src/app/shared/object-collection/shared/mydspace-item-status/my-dspace-item-status.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..848dd502a48eb0f9e13b9f33ed390f6ff54c666b
--- /dev/null
+++ b/src/app/shared/object-collection/shared/mydspace-item-status/my-dspace-item-status.component.html
@@ -0,0 +1,5 @@
+<div>
+  <span [className]="badgeClass">
+    {{badgeContent | translate}}
+  </span>
+</div>
diff --git a/src/app/shared/object-collection/shared/mydspace-item-status/my-dspace-item-status.component.scss b/src/app/shared/object-collection/shared/mydspace-item-status/my-dspace-item-status.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/app/shared/object-collection/shared/mydspace-item-status/my-dspace-item-status.component.spec.ts b/src/app/shared/object-collection/shared/mydspace-item-status/my-dspace-item-status.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9c23ab3603b0d8dfbe85a557155660a63141dcb5
--- /dev/null
+++ b/src/app/shared/object-collection/shared/mydspace-item-status/my-dspace-item-status.component.spec.ts
@@ -0,0 +1,89 @@
+import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { of as observableOf } from 'rxjs';
+import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
+
+import { RemoteData } from '../../../../core/data/remote-data';
+
+import { Workflowitem } from '../../../../core/submission/models/workflowitem.model';
+import { PoolTask } from '../../../../core/tasks/models/pool-task-object.model';
+import { EPersonMock } from '../../../testing/eperson-mock';
+import { MyDSpaceItemStatusComponent } from './my-dspace-item-status.component';
+import { MyDspaceItemStatusType } from './my-dspace-item-status-type';
+import { MockTranslateLoader } from '../../../mocks/mock-translate-loader';
+import { By } from '@angular/platform-browser';
+
+let component: MyDSpaceItemStatusComponent;
+let fixture: ComponentFixture<MyDSpaceItemStatusComponent>;
+
+let mockResultObject: PoolTask;
+
+const rdSumbitter = new RemoteData(false, false, true, null, EPersonMock);
+const workflowitem = Object.assign(new Workflowitem(), { submitter: observableOf(rdSumbitter) });
+const rdWorkflowitem = new RemoteData(false, false, true, null, workflowitem);
+mockResultObject = Object.assign(new PoolTask(), { workflowitem: observableOf(rdWorkflowitem) });
+
+describe('MyDSpaceItemStatusComponent', () => {
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [
+        TranslateModule.forRoot({
+          loader: {
+            provide: TranslateLoader,
+            useClass: MockTranslateLoader
+          }
+        })
+      ],
+      declarations: [MyDSpaceItemStatusComponent],
+      schemas: [NO_ERRORS_SCHEMA]
+    }).overrideComponent(MyDSpaceItemStatusComponent, {
+      set: { changeDetection: ChangeDetectionStrategy.Default }
+    }).compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(MyDSpaceItemStatusComponent);
+    component = fixture.componentInstance;
+  });
+
+  it('should display badge', () => {
+    const badge = fixture.debugElement.query(By.css('span'));
+    expect(badge).toBeDefined();
+  });
+
+  it('should init badge content and class', () => {
+    component.status = MyDspaceItemStatusType.VALIDATION;
+    fixture.detectChanges();
+    expect(component.badgeContent).toBe(MyDspaceItemStatusType.VALIDATION);
+    expect(component.badgeClass).toBe('text-light badge badge-warning');
+  });
+
+  it('should init badge content and class', () => {
+    component.status = MyDspaceItemStatusType.WAITING_CONTROLLER;
+    fixture.detectChanges();
+    expect(component.badgeContent).toBe(MyDspaceItemStatusType.WAITING_CONTROLLER);
+    expect(component.badgeClass).toBe('text-light badge badge-info');
+  });
+
+  it('should init badge content and class', () => {
+    component.status = MyDspaceItemStatusType.WORKSPACE;
+    fixture.detectChanges();
+    expect(component.badgeContent).toBe(MyDspaceItemStatusType.WORKSPACE);
+    expect(component.badgeClass).toBe('text-light badge badge-primary');
+  });
+
+  it('should init badge content and class', () => {
+    component.status = MyDspaceItemStatusType.ARCHIVED;
+    fixture.detectChanges();
+    expect(component.badgeContent).toBe(MyDspaceItemStatusType.ARCHIVED);
+    expect(component.badgeClass).toBe('text-light badge badge-success');
+  });
+
+  it('should init badge content and class', () => {
+    component.status = MyDspaceItemStatusType.WORKFLOW;
+    fixture.detectChanges();
+    expect(component.badgeContent).toBe(MyDspaceItemStatusType.WORKFLOW);
+    expect(component.badgeClass).toBe('text-light badge badge-info');
+  });
+});
diff --git a/src/app/shared/object-collection/shared/mydspace-item-status/my-dspace-item-status.component.ts b/src/app/shared/object-collection/shared/mydspace-item-status/my-dspace-item-status.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..917dd45accc587f5ee7d50c304b70dd639461a3a
--- /dev/null
+++ b/src/app/shared/object-collection/shared/mydspace-item-status/my-dspace-item-status.component.ts
@@ -0,0 +1,54 @@
+import { Component, Input, OnInit } from '@angular/core';
+import { MyDspaceItemStatusType } from './my-dspace-item-status-type';
+
+/**
+ * This component represents a badge with mydspace item status
+ */
+@Component({
+  selector: 'ds-mydspace-item-status',
+  styleUrls: ['./my-dspace-item-status.component.scss'],
+  templateUrl: './my-dspace-item-status.component.html'
+})
+export class MyDSpaceItemStatusComponent implements OnInit {
+
+  /**
+   * This mydspace item status
+   */
+  @Input() status: MyDspaceItemStatusType;
+
+  /**
+   * This badge class
+   */
+  public badgeClass: string;
+
+  /**
+   * This badge content
+   */
+  public badgeContent: string;
+
+  /**
+   * Initialize badge content and class
+   */
+  ngOnInit() {
+    this.badgeContent = this.status;
+    this.badgeClass = 'text-light badge ';
+    switch (this.status) {
+      case MyDspaceItemStatusType.VALIDATION:
+        this.badgeClass += 'badge-warning';
+        break;
+      case MyDspaceItemStatusType.WAITING_CONTROLLER:
+        this.badgeClass += 'badge-info';
+        break;
+      case MyDspaceItemStatusType.WORKSPACE:
+        this.badgeClass += 'badge-primary';
+        break;
+      case MyDspaceItemStatusType.ARCHIVED:
+        this.badgeClass += 'badge-success';
+        break;
+      case MyDspaceItemStatusType.WORKFLOW:
+        this.badgeClass += 'badge-info';
+        break;
+    }
+  }
+
+}
diff --git a/src/app/shared/object-collection/shared/mydspace-item-submitter/item-submitter.component.html b/src/app/shared/object-collection/shared/mydspace-item-submitter/item-submitter.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..47674025ca89395fa0d3bfab2f18ad45fdbd1647
--- /dev/null
+++ b/src/app/shared/object-collection/shared/mydspace-item-submitter/item-submitter.component.html
@@ -0,0 +1,3 @@
+<div class="mt-2 mb-2">
+  <span class="text-muted">{{'submission.workflow.tasks.generic.submitter' | translate}} : <span class="badge badge-light">{{(submitter$ | async)?.name}}</span></span>
+</div>
diff --git a/src/app/shared/object-collection/shared/mydspace-item-submitter/item-submitter.component.scss b/src/app/shared/object-collection/shared/mydspace-item-submitter/item-submitter.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/app/shared/object-collection/shared/mydspace-item-submitter/item-submitter.component.spec.ts b/src/app/shared/object-collection/shared/mydspace-item-submitter/item-submitter.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..77460a3674eaa71cdc03c86a4111cdb64374b97a
--- /dev/null
+++ b/src/app/shared/object-collection/shared/mydspace-item-submitter/item-submitter.component.spec.ts
@@ -0,0 +1,68 @@
+import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { of as observableOf } from 'rxjs';
+import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
+import { cold } from 'jasmine-marbles';
+
+import { RemoteData } from '../../../../core/data/remote-data';
+import { ItemSubmitterComponent } from './item-submitter.component';
+import { Workflowitem } from '../../../../core/submission/models/workflowitem.model';
+import { PoolTask } from '../../../../core/tasks/models/pool-task-object.model';
+import { EPersonMock } from '../../../testing/eperson-mock';
+import { MockTranslateLoader } from '../../../mocks/mock-translate-loader';
+import { By } from '@angular/platform-browser';
+
+let component: ItemSubmitterComponent;
+let fixture: ComponentFixture<ItemSubmitterComponent>;
+
+const compIndex = 1;
+
+let mockResultObject: PoolTask;
+
+const rdSumbitter = new RemoteData(false, false, true, null, EPersonMock);
+const workflowitem = Object.assign(new Workflowitem(), { submitter: observableOf(rdSumbitter) });
+const rdWorkflowitem = new RemoteData(false, false, true, null, workflowitem);
+mockResultObject = Object.assign(new PoolTask(), { workflowitem: observableOf(rdWorkflowitem) });
+
+describe('ItemSubmitterComponent', () => {
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [
+        TranslateModule.forRoot({
+          loader: {
+            provide: TranslateLoader,
+            useClass: MockTranslateLoader
+          }
+        })
+      ],
+      declarations: [ItemSubmitterComponent],
+      schemas: [NO_ERRORS_SCHEMA]
+    }).overrideComponent(ItemSubmitterComponent, {
+      set: { changeDetection: ChangeDetectionStrategy.Default }
+    }).compileComponents();
+  }));
+
+  beforeEach(async(() => {
+    fixture = TestBed.createComponent(ItemSubmitterComponent);
+    component = fixture.componentInstance;
+  }));
+
+  beforeEach(() => {
+    component.object = mockResultObject;
+    fixture.detectChanges();
+  });
+
+  it('should init submitter properly', () => {
+    expect(component.submitter$).toBeObservable(cold('(b|)', {
+      b: EPersonMock
+    }));
+  });
+
+  it('should show a badge with submitter name', () => {
+    const badge = fixture.debugElement.query(By.css('.badge'));
+
+    expect(badge).toBeDefined();
+    expect(badge.nativeElement.innerHTML).toBe(EPersonMock.name);
+  });
+});
diff --git a/src/app/shared/object-collection/shared/mydspace-item-submitter/item-submitter.component.ts b/src/app/shared/object-collection/shared/mydspace-item-submitter/item-submitter.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f752fa6f04d6955db49c9c18045bdd75bbff68b5
--- /dev/null
+++ b/src/app/shared/object-collection/shared/mydspace-item-submitter/item-submitter.component.ts
@@ -0,0 +1,41 @@
+import { Component, Input, OnInit } from '@angular/core';
+
+import { Observable } from 'rxjs';
+import { filter, find, flatMap, map } from 'rxjs/operators';
+
+import { EPerson } from '../../../../core/eperson/models/eperson.model';
+import { RemoteData } from '../../../../core/data/remote-data';
+import { isNotEmpty, isNotUndefined } from '../../../empty.util';
+import { Workflowitem } from '../../../../core/submission/models/workflowitem.model';
+
+/**
+ * This component represents a badge with submitter information.
+ */
+@Component({
+  selector: 'ds-item-submitter',
+  styleUrls: ['./item-submitter.component.scss'],
+  templateUrl: './item-submitter.component.html'
+})
+export class ItemSubmitterComponent implements OnInit {
+
+  /**
+   * The target object
+   */
+  @Input() object: any;
+
+  /**
+   * The Eperson object
+   */
+  submitter$: Observable<EPerson>;
+
+  /**
+   * Initialize submitter object
+   */
+  ngOnInit() {
+    this.submitter$ = (this.object.workflowitem as Observable<RemoteData<Workflowitem>>).pipe(
+      filter((rd: RemoteData<Workflowitem>) => (rd.hasSucceeded && isNotUndefined(rd.payload))),
+      flatMap((rd: RemoteData<Workflowitem>) => rd.payload.submitter as Observable<RemoteData<EPerson>>),
+      find((rd: RemoteData<EPerson>) => rd.hasSucceeded && isNotEmpty(rd.payload)),
+      map((rd: RemoteData<EPerson>) => rd.payload));
+  }
+}
diff --git a/src/app/shared/object-collection/shared/pool-task-my-dspace-result.model.ts b/src/app/shared/object-collection/shared/pool-task-my-dspace-result.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1bc2c4c4c2d72d9f03ac6f998064b7672ae36c52
--- /dev/null
+++ b/src/app/shared/object-collection/shared/pool-task-my-dspace-result.model.ts
@@ -0,0 +1,11 @@
+import { PoolTask } from '../../../core/tasks/models/pool-task-object.model';
+import { SearchResult } from '../../../+search-page/search-result.model';
+import { MyDSpaceConfigurationValueType } from '../../../+my-dspace-page/my-dspace-configuration-value-type';
+import { searchResultFor } from '../../../+search-page/search-service/search-result-element-decorator';
+
+/**
+ * Represents a search result object of a PoolTask object
+ */
+@searchResultFor(PoolTask, MyDSpaceConfigurationValueType.Workflow)
+export class PoolTaskMyDSpaceResult extends SearchResult<PoolTask> {
+}
diff --git a/src/app/shared/object-collection/shared/workflowitem-my-dspace-result.model.ts b/src/app/shared/object-collection/shared/workflowitem-my-dspace-result.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..89f6f351adacfff6f588248eeb6bc508af60fd65
--- /dev/null
+++ b/src/app/shared/object-collection/shared/workflowitem-my-dspace-result.model.ts
@@ -0,0 +1,11 @@
+import { Workflowitem } from '../../../core/submission/models/workflowitem.model';
+import { SearchResult } from '../../../+search-page/search-result.model';
+import { MyDSpaceConfigurationValueType } from '../../../+my-dspace-page/my-dspace-configuration-value-type';
+import { searchResultFor } from '../../../+search-page/search-service/search-result-element-decorator';
+
+/**
+ * Represents a search result object of a Workflowitem object
+ */
+@searchResultFor(Workflowitem, MyDSpaceConfigurationValueType.Workspace)
+export class WorkflowitemMyDSpaceResult extends SearchResult<Workflowitem> {
+}
diff --git a/src/app/shared/object-collection/shared/workspaceitem-my-dspace-result.model.ts b/src/app/shared/object-collection/shared/workspaceitem-my-dspace-result.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..20474c9ad3fa2ed8e085b80858740e280b1123c3
--- /dev/null
+++ b/src/app/shared/object-collection/shared/workspaceitem-my-dspace-result.model.ts
@@ -0,0 +1,11 @@
+import { Workspaceitem } from '../../../core/submission/models/workspaceitem.model';
+import { MyDSpaceConfigurationValueType } from '../../../+my-dspace-page/my-dspace-configuration-value-type';
+import { searchResultFor } from '../../../+search-page/search-service/search-result-element-decorator';
+import { SearchResult } from '../../../+search-page/search-result.model';
+
+/**
+ * Represents a search result object of a Workspaceitem object
+ */
+@searchResultFor(Workspaceitem, MyDSpaceConfigurationValueType.Workspace)
+export class WorkspaceitemMyDSpaceResult extends SearchResult<Workspaceitem> {
+}
diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/claimed-my-dspace-result/claimed-my-dspace-result-detail-element.component.html b/src/app/shared/object-detail/my-dspace-result-detail-element/claimed-my-dspace-result/claimed-my-dspace-result-detail-element.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..d4ecaaa33296ef5d648788ac8a9646aabf827cc1
--- /dev/null
+++ b/src/app/shared/object-detail/my-dspace-result-detail-element/claimed-my-dspace-result/claimed-my-dspace-result-detail-element.component.html
@@ -0,0 +1,8 @@
+<ds-item-detail-preview *ngIf="workflowitem"
+                        [item]="(workflowitem.item | async)?.payload"
+                        [object]="object"
+                        [showSubmitter]="showSubmitter"
+                        [status]="status">
+</ds-item-detail-preview>
+
+<ds-claimed-task-actions *ngIf="workflowitem" [object]="dso"></ds-claimed-task-actions>
diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/claimed-my-dspace-result/claimed-my-dspace-result-detail-element.component.spec.ts b/src/app/shared/object-detail/my-dspace-result-detail-element/claimed-my-dspace-result/claimed-my-dspace-result-detail-element.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d3307721ff29e6cde777bb56d01f81a22eef0ca7
--- /dev/null
+++ b/src/app/shared/object-detail/my-dspace-result-detail-element/claimed-my-dspace-result/claimed-my-dspace-result-detail-element.component.spec.ts
@@ -0,0 +1,89 @@
+import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { NoopAnimationsModule } from '@angular/platform-browser/animations';
+
+import { of as observableOf } from 'rxjs';
+
+import { Item } from '../../../../core/shared/item.model';
+import { ClaimedMyDSpaceResultDetailElementComponent } from './claimed-my-dspace-result-detail-element.component';
+import { ClaimedTaskMyDSpaceResult } from '../../../object-collection/shared/claimed-task-my-dspace-result.model';
+import { ClaimedTask } from '../../../../core/tasks/models/claimed-task-object.model';
+import { RemoteData } from '../../../../core/data/remote-data';
+import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type';
+import { Workflowitem } from '../../../../core/submission/models/workflowitem.model';
+
+let component: ClaimedMyDSpaceResultDetailElementComponent;
+let fixture: ComponentFixture<ClaimedMyDSpaceResultDetailElementComponent>;
+
+const compIndex = 1;
+
+const mockResultObject: ClaimedTaskMyDSpaceResult = new ClaimedTaskMyDSpaceResult();
+mockResultObject.hitHighlights = {};
+
+const item = Object.assign(new Item(), {
+  bitstreams: observableOf({}),
+  metadata: {
+    'dc.title': [
+      {
+        language: 'en_US',
+        value: 'This is just another title'
+      }
+    ],
+    'dc.type': [
+      {
+        language: null,
+        value: 'Article'
+      }
+    ],
+    'dc.contributor.author': [
+      {
+        language: 'en_US',
+        value: 'Smith, Donald'
+      }
+    ],
+    'dc.date.issued': [
+      {
+        language: null,
+        value: '2015-06-26'
+      }
+    ]
+  }
+});
+const rdItem = new RemoteData(false, false, true, null, item);
+const workflowitem = Object.assign(new Workflowitem(), { item: observableOf(rdItem) });
+const rdWorkflowitem = new RemoteData(false, false, true, null, workflowitem);
+mockResultObject.indexableObject = Object.assign(new ClaimedTask(), { workflowitem: observableOf(rdWorkflowitem) });
+
+describe('ClaimedMyDSpaceResultDetailElementComponent', () => {
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [NoopAnimationsModule],
+      declarations: [ClaimedMyDSpaceResultDetailElementComponent],
+      providers: [
+        { provide: 'objectElementProvider', useValue: (mockResultObject) },
+        { provide: 'indexElementProvider', useValue: (compIndex) }
+      ],
+      schemas: [NO_ERRORS_SCHEMA]
+    }).overrideComponent(ClaimedMyDSpaceResultDetailElementComponent, {
+      set: { changeDetection: ChangeDetectionStrategy.Default }
+    }).compileComponents();
+  }));
+
+  beforeEach(async(() => {
+    fixture = TestBed.createComponent(ClaimedMyDSpaceResultDetailElementComponent);
+    component = fixture.componentInstance;
+  }));
+
+  beforeEach(() => {
+    component.dso = mockResultObject.indexableObject;
+    fixture.detectChanges();
+  });
+
+  it('should init item properly', () => {
+    expect(component.workflowitem).toEqual(workflowitem);
+  });
+
+  it('should have properly status', () => {
+    expect(component.status).toEqual(MyDspaceItemStatusType.VALIDATION);
+  });
+});
diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/claimed-my-dspace-result/claimed-my-dspace-result-detail-element.component.ts b/src/app/shared/object-detail/my-dspace-result-detail-element/claimed-my-dspace-result/claimed-my-dspace-result-detail-element.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5f7b0aab5509167498cfbe4449628833c1802789
--- /dev/null
+++ b/src/app/shared/object-detail/my-dspace-result-detail-element/claimed-my-dspace-result/claimed-my-dspace-result-detail-element.component.ts
@@ -0,0 +1,68 @@
+import { Component, Inject } from '@angular/core';
+
+import { Observable } from 'rxjs';
+import { find } from 'rxjs/operators';
+
+import { renderElementsFor } from '../../../object-collection/shared/dso-element-decorator';
+import { RemoteData } from '../../../../core/data/remote-data';
+import { ViewMode } from '../../../../core/shared/view-mode.model';
+import { isNotUndefined } from '../../../empty.util';
+import { ListableObject } from '../../../object-collection/shared/listable-object.model';
+import { Workflowitem } from '../../../../core/submission/models/workflowitem.model';
+import { ClaimedTask } from '../../../../core/tasks/models/claimed-task-object.model';
+import { ClaimedTaskMyDSpaceResult } from '../../../object-collection/shared/claimed-task-my-dspace-result.model';
+import { MyDSpaceResultDetailElementComponent } from '../my-dspace-result-detail-element.component';
+import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type';
+import { SetViewMode } from '../../../view-mode';
+
+/**
+ * This component renders claimed task object for the mydspace result in the detail view.
+ */
+@Component({
+  selector: 'ds-claimed-my-dspace-result-detail-element',
+  styleUrls: ['../my-dspace-result-detail-element.component.scss'],
+  templateUrl: './claimed-my-dspace-result-detail-element.component.html'
+})
+
+@renderElementsFor(ClaimedTaskMyDSpaceResult, SetViewMode.Detail)
+@renderElementsFor(ClaimedTask, SetViewMode.Detail)
+export class ClaimedMyDSpaceResultDetailElementComponent extends MyDSpaceResultDetailElementComponent<ClaimedTaskMyDSpaceResult, ClaimedTask> {
+
+  /**
+   * A boolean representing if to show submitter information
+   */
+  public showSubmitter = true;
+
+  /**
+   * Represent item's status
+   */
+  public status = MyDspaceItemStatusType.VALIDATION;
+
+  /**
+   * The workflowitem object that belonging to the result object
+   */
+  public workflowitem: Workflowitem;
+
+  constructor(@Inject('objectElementProvider') public listable: ListableObject) {
+    super(listable);
+  }
+
+  /**
+   * Initialize all instance variables
+   */
+  ngOnInit() {
+    this.initWorkflowItem(this.dso.workflowitem as Observable<RemoteData<Workflowitem>>);
+  }
+
+  /**
+   * Retrieve workflowitem from result object
+   */
+  initWorkflowItem(wfi$: Observable<RemoteData<Workflowitem>>) {
+    wfi$.pipe(
+      find((rd: RemoteData<Workflowitem>) => (rd.hasSucceeded && isNotUndefined(rd.payload)))
+    ).subscribe((rd: RemoteData<Workflowitem>) => {
+      this.workflowitem = rd.payload;
+    });
+  }
+
+}
diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview-field/item-detail-preview-field.component.html b/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview-field/item-detail-preview-field.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..2b7e7f8f6e52b8c492d8bd3a76e21e093a170ace
--- /dev/null
+++ b/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview-field/item-detail-preview-field.component.html
@@ -0,0 +1,10 @@
+<ds-metadata-field-wrapper [label]="label | translate">
+  <ng-container *ngIf="item.hasMetadata(metadata)">
+    <span *ngFor="let mdValue of allMetadataValues(metadata); let last=last;">
+      {{mdValue}}<span *ngIf="!last" [innerHTML]="separator"></span>
+    </span>
+  </ng-container>
+  <ng-container *ngIf="!item.hasMetadata(metadata)">
+    <span class="text-muted">{{(placeholder | translate)}}</span>
+  </ng-container>
+</ds-metadata-field-wrapper>
diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview-field/item-detail-preview-field.component.spec.ts b/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview-field/item-detail-preview-field.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ff5224a1a20b14d2856a5d6676ab316024f04f5b
--- /dev/null
+++ b/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview-field/item-detail-preview-field.component.spec.ts
@@ -0,0 +1,98 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
+import { NoopAnimationsModule } from '@angular/platform-browser/animations';
+
+import { of as observableOf } from 'rxjs';
+import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
+
+import { ItemDetailPreviewFieldComponent } from './item-detail-preview-field.component';
+import { Item } from '../../../../../core/shared/item.model';
+import { TruncatePipe } from '../../../../utils/truncate.pipe';
+import { MockTranslateLoader } from '../../../../mocks/mock-translate-loader';
+import { By } from '@angular/platform-browser';
+
+let component: ItemDetailPreviewFieldComponent;
+let fixture: ComponentFixture<ItemDetailPreviewFieldComponent>;
+
+const mockItemWithAuthorAndDate: Item = Object.assign(new Item(), {
+  bitstreams: observableOf({}),
+  metadata: {
+    'dc.contributor.author': [
+      {
+        language: 'en_US',
+        value: 'Smith, Donald'
+      }
+    ],
+    'dc.date.issued': [
+      {
+        language: null,
+        value: '2015-06-26'
+      }
+    ],
+    'dc.title': [
+      {
+        language: 'en_US',
+        value: 'This is just another title'
+      }
+    ],
+    'dc.type': [
+      {
+        language: null,
+        value: 'Article'
+      }
+    ]
+  }
+});
+
+describe('ItemDetailPreviewFieldComponent', () => {
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [
+        NoopAnimationsModule,
+        TranslateModule.forRoot({
+          loader: {
+            provide: TranslateLoader,
+            useClass: MockTranslateLoader
+          }
+        }),
+      ],
+      declarations: [ItemDetailPreviewFieldComponent, TruncatePipe],
+      providers: [
+        { provide: 'objectElementProvider', useValue: { mockItemWithAuthorAndDate } }
+
+      ],
+
+      schemas: [NO_ERRORS_SCHEMA]
+    }).overrideComponent(ItemDetailPreviewFieldComponent, {
+      set: { changeDetection: ChangeDetectionStrategy.Default }
+    }).compileComponents();
+  }));
+
+  beforeEach(async(() => {
+    fixture = TestBed.createComponent(ItemDetailPreviewFieldComponent);
+    component = fixture.componentInstance;
+
+  }));
+
+  beforeEach(() => {
+    component.object = { hitHighlights: {} } as any;
+    component.item = mockItemWithAuthorAndDate;
+    component.label = 'test label';
+    component.metadata = 'dc.title';
+    component.placeholder = 'No title';
+    fixture.detectChanges();
+  });
+
+  it('should display dc.title value', () => {
+    const span = fixture.debugElement.query(By.css('span'));
+    expect(span.nativeElement.innerHTML).toContain('This is just another title');
+  });
+
+  it('should display placeholder when metadata has no value', () => {
+    component.metadata = 'dc.abstract';
+    component.placeholder = 'No abstract';
+    fixture.detectChanges();
+    const span = fixture.debugElement.query(By.css('.text-muted'));
+    expect(span.nativeElement.innerHTML).toContain('No abstract');
+  });
+});
diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview-field/item-detail-preview-field.component.ts b/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview-field/item-detail-preview-field.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b53d0a6b2dd6d7078063511905de244bdb685189
--- /dev/null
+++ b/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview-field/item-detail-preview-field.component.ts
@@ -0,0 +1,55 @@
+import { Component, Input } from '@angular/core';
+
+import { Metadata } from '../../../../../core/shared/metadata.utils';
+import { MyDSpaceResult } from '../../../../../+my-dspace-page/my-dspace-result.model';
+import { Item } from '../../../../../core/shared/item.model';
+
+/**
+ * This component show values for the given item metadata
+ */
+@Component({
+  selector: 'ds-item-detail-preview-field',
+  templateUrl: './item-detail-preview-field.component.html'
+})
+export class ItemDetailPreviewFieldComponent {
+
+  /**
+   * The item to display
+   */
+  @Input() item: Item;
+
+  /**
+   * The mydspace result object
+   */
+  @Input() object: MyDSpaceResult<any>;
+
+  /**
+   * The metadata label
+   */
+  @Input() label: string;
+
+  /**
+   * The metadata to show
+   */
+  @Input() metadata: string | string[];
+
+  /**
+   * The placeholder if there are no value to show
+   */
+  @Input() placeholder: string;
+
+  /**
+   * The value's separator
+   */
+  @Input() separator: string;
+
+  /**
+   * Gets all matching metadata string values from hitHighlights or dso metadata, preferring hitHighlights.
+   *
+   * @param {string|string[]} keyOrKeys The metadata key(s) in scope. Wildcards are supported; see [[Metadata]].
+   * @returns {string[]} the matching string values or an empty array.
+   */
+  allMetadataValues(keyOrKeys: string | string[]): string[] {
+    return Metadata.allValues([this.object.hitHighlights, this.item.metadata], keyOrKeys);
+  }
+}
diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.html b/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..04e128c49a5f97faa6c562768b1ecec6233cb2ed
--- /dev/null
+++ b/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.html
@@ -0,0 +1,62 @@
+<div *ngIf="item" class="item-page" @fadeInOut>
+  <ng-container *ngIf="status">
+    <ds-mydspace-item-status [status]="status"></ds-mydspace-item-status>
+  </ng-container>
+  <div *ngIf="item">
+    <h2 class="item-page-title-field">
+      <ds-metadata-values *ngIf="item.hasMetadata('dc.title')" [mdValues]="item?.allMetadata('dc.title')"></ds-metadata-values>
+      <span class="text-muted" *ngIf="!item.hasMetadata('dc.title')">{{('mydspace.results.no-title' | translate)}}</span>
+    </h2>
+    <div class="row mb-1">
+      <div class="col-xs-12 col-md-4">
+        <ds-metadata-field-wrapper>
+          <ds-thumbnail [thumbnail]="thumbnail$ | async"></ds-thumbnail>
+        </ds-metadata-field-wrapper>
+        <ng-container *ngVar="(bitstreams$ | async) as bitstreams">
+          <ds-metadata-field-wrapper [label]="('item.page.files' | translate)">
+            <div *ngIf="bitstreams?.length > 0" class="file-section">
+              <a *ngFor="let file of bitstreams; let last=last;" [href]="file?.content" target="_blank" [download]="file?.name">
+                <span>{{file?.name}}</span>
+                <span>({{(file?.sizeBytes) | dsFileSize }})</span>
+                <span *ngIf="!last" innerHTML="{{separator}}"></span>
+              </a>
+            </div>
+            <ng-container *ngIf="bitstreams?.length === 0">
+              <span class="text-muted">{{('mydspace.results.no-files' | translate)}}</span>
+            </ng-container>
+          </ds-metadata-field-wrapper>
+        </ng-container>
+        <ds-item-detail-preview-field [item]="item"
+                                      [object]="object"
+                                      [label]="('item.page.date' | translate)"
+                                      [metadata]="'dc.date.issued'"
+                                      [separator]="separator"
+                                      [placeholder]="('mydspace.results.no-date' | translate)"></ds-item-detail-preview-field>
+        <ds-item-detail-preview-field [item]="item"
+                                      [object]="object"
+                                      [label]="('item.page.author' | translate)"
+                                      [metadata]="['dc.contributor', 'dc.creator', 'dc.contributor.*']"
+                                      [separator]="separator"
+                                      [placeholder]="('mydspace.results.no-authors' | translate)"></ds-item-detail-preview-field>
+      </div>
+      <div class="col-xs-12 col-md-6">
+        <ds-item-detail-preview-field [item]="item"
+                                      [object]="object"
+                                      [label]="('item.page.abstract' | translate)"
+                                      [metadata]="'dc.description.abstract'"
+                                      [separator]="separator"
+                                      [placeholder]="('mydspace.results.no-abstract' | translate)"></ds-item-detail-preview-field>
+        <ds-item-detail-preview-field [item]="item"
+                                      [object]="object"
+                                      [label]="('item.page.uri' | translate)"
+                                      [metadata]="'dc.identifier.uri'"
+                                      [separator]="separator"
+                                      [placeholder]="('mydspace.results.no-uri' | translate)"></ds-item-detail-preview-field>
+        <div>
+          <ng-content></ng-content>
+        </div>
+      </div>
+    </div>
+  </div>
+</div>
+<ds-item-submitter *ngIf="showSubmitter" [object]="object.indexableObject"></ds-item-submitter>
diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.scss b/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.spec.ts b/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d0af614a6cd91ffda72d3f7ce9d1362efcc2d754
--- /dev/null
+++ b/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.spec.ts
@@ -0,0 +1,83 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
+import { NoopAnimationsModule } from '@angular/platform-browser/animations';
+
+import { of as observableOf } from 'rxjs';
+import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
+
+import { TruncatePipe } from '../../../utils/truncate.pipe';
+import { Item } from '../../../../core/shared/item.model';
+import { ItemDetailPreviewComponent } from './item-detail-preview.component';
+import { MockTranslateLoader } from '../../../mocks/mock-translate-loader';
+import { ItemDetailPreviewFieldComponent } from './item-detail-preview-field/item-detail-preview-field.component';
+import { FileSizePipe } from '../../../utils/file-size-pipe';
+import { VarDirective } from '../../../utils/var.directive';
+
+let component: ItemDetailPreviewComponent;
+let fixture: ComponentFixture<ItemDetailPreviewComponent>;
+
+const mockItem: Item = Object.assign(new Item(), {
+  bitstreams: observableOf([]),
+  metadata: {
+    'dc.contributor.author': [
+      {
+        language: 'en_US',
+        value: 'Smith, Donald'
+      }
+    ],
+    'dc.date.issued': [
+      {
+        language: null,
+        value: '2015-06-26'
+      }
+    ],
+    'dc.title': [
+      {
+        language: 'en_US',
+        value: 'This is just another title'
+      }
+    ],
+    'dc.type': [
+      {
+        language: null,
+        value: 'Article'
+      }
+    ]
+  }
+});
+
+describe('ItemDetailPreviewComponent', () => {
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [
+        NoopAnimationsModule,
+        TranslateModule.forRoot({
+          loader: {
+            provide: TranslateLoader,
+            useClass: MockTranslateLoader
+          }
+        }),
+      ],
+      declarations: [ItemDetailPreviewComponent, ItemDetailPreviewFieldComponent, TruncatePipe, FileSizePipe, VarDirective],
+      schemas: [NO_ERRORS_SCHEMA]
+    }).overrideComponent(ItemDetailPreviewComponent, {
+      set: { changeDetection: ChangeDetectionStrategy.Default }
+    }).compileComponents();
+  }));
+
+  beforeEach(async(() => {
+    fixture = TestBed.createComponent(ItemDetailPreviewComponent);
+    component = fixture.componentInstance;
+    component.object = { hitHighlights: {} } as any;
+    component.item = mockItem;
+    component.separator = ', ';
+    spyOn(component.item, 'getFiles').and.returnValue(mockItem.bitstreams);
+    fixture.detectChanges();
+
+  }));
+
+  it('should init thumbnail and bitstreams on init', () => {
+    expect(component.thumbnail$).toBeDefined();
+    expect(component.bitstreams$).toBeDefined();
+  });
+});
diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.ts b/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d26bfc45896602c9262043622175efb533a662d7
--- /dev/null
+++ b/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.ts
@@ -0,0 +1,65 @@
+import { Component, Input } from '@angular/core';
+
+import { Observable } from 'rxjs';
+
+import { Item } from '../../../../core/shared/item.model';
+import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type';
+import { fadeInOut } from '../../../animations/fade';
+import { Bitstream } from '../../../../core/shared/bitstream.model';
+import { MyDSpaceResult } from '../../../../+my-dspace-page/my-dspace-result.model';
+
+/**
+ * This component show metadata for the given item object in the detail view.
+ */
+@Component({
+  selector: 'ds-item-detail-preview',
+  styleUrls: ['./item-detail-preview.component.scss'],
+  templateUrl: './item-detail-preview.component.html',
+  animations: [fadeInOut]
+})
+export class ItemDetailPreviewComponent {
+
+  /**
+   * The item to display
+   */
+  @Input() item: Item;
+
+  /**
+   * The mydspace result object
+   */
+  @Input() object: MyDSpaceResult<any>;
+
+  /**
+   * Represent item's status
+   */
+  @Input() status: MyDspaceItemStatusType;
+
+  /**
+   * A boolean representing if to show submitter information
+   */
+  @Input() showSubmitter = false;
+
+  /**
+   * The item's thumbnail
+   */
+  public bitstreams$: Observable<Bitstream[]>;
+
+  /**
+   * The value's separator
+   */
+  public separator = ', ';
+
+  /**
+   * The item's thumbnail
+   */
+  public thumbnail$: Observable<Bitstream>;
+
+  /**
+   * Initialize all instance variables
+   */
+  ngOnInit() {
+    this.thumbnail$ = this.item.getThumbnail();
+    this.bitstreams$ = this.item.getFiles();
+  }
+
+}
diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/item-my-dspace-result/item-my-dspace-result-detail-element.component.html b/src/app/shared/object-detail/my-dspace-result-detail-element/item-my-dspace-result/item-my-dspace-result-detail-element.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..2b687960ab11cf2cd37303ba9cba657cc2824ed1
--- /dev/null
+++ b/src/app/shared/object-detail/my-dspace-result-detail-element/item-my-dspace-result/item-my-dspace-result-detail-element.component.html
@@ -0,0 +1,8 @@
+<ds-item-detail-preview [item]="dso"
+                        [object]="object"
+                        [status]="status">
+</ds-item-detail-preview>
+
+<ds-item-actions [object]="dso"></ds-item-actions>
+
+
diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/item-my-dspace-result/item-my-dspace-result-detail-element.component.scss b/src/app/shared/object-detail/my-dspace-result-detail-element/item-my-dspace-result/item-my-dspace-result-detail-element.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..1d0786105ca4dc61051610513a3fdbe21424a6d5
--- /dev/null
+++ b/src/app/shared/object-detail/my-dspace-result-detail-element/item-my-dspace-result/item-my-dspace-result-detail-element.component.scss
@@ -0,0 +1 @@
+@import '../../../../../styles/variables';
diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/item-my-dspace-result/item-my-dspace-result-detail-element.component.spec.ts b/src/app/shared/object-detail/my-dspace-result-detail-element/item-my-dspace-result/item-my-dspace-result-detail-element.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c99f44cbae02f55d2028d6191ede7cadca14671d
--- /dev/null
+++ b/src/app/shared/object-detail/my-dspace-result-detail-element/item-my-dspace-result/item-my-dspace-result-detail-element.component.spec.ts
@@ -0,0 +1,78 @@
+import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { NoopAnimationsModule } from '@angular/platform-browser/animations';
+
+import { of as observableOf } from 'rxjs';
+
+import { Item } from '../../../../core/shared/item.model';
+import { ItemMyDSpaceResultDetailElementComponent } from './item-my-dspace-result-detail-element.component';
+import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type';
+import { ItemMyDSpaceResult } from '../../../object-collection/shared/item-my-dspace-result.model';
+
+let component: ItemMyDSpaceResultDetailElementComponent;
+let fixture: ComponentFixture<ItemMyDSpaceResultDetailElementComponent>;
+
+const compIndex = 1;
+
+const mockResultObject: ItemMyDSpaceResult = new ItemMyDSpaceResult();
+mockResultObject.hitHighlights = {};
+
+mockResultObject.indexableObject = Object.assign(new Item(), {
+  bitstreams: observableOf({}),
+  metadata: {
+    'dc.title': [
+      {
+        language: 'en_US',
+        value: 'This is just another title'
+      }
+    ],
+    'dc.type': [
+      {
+        language: null,
+        value: 'Article'
+      }
+    ],
+    'dc.contributor.author': [
+      {
+        language: 'en_US',
+        value: 'Smith, Donald'
+      }
+    ],
+    'dc.date.issued': [
+      {
+        language: null,
+        value: '2015-06-26'
+      }
+    ]
+  }
+});
+
+describe('ItemMyDSpaceResultDetailElementComponent', () => {
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [NoopAnimationsModule],
+      declarations: [ItemMyDSpaceResultDetailElementComponent],
+      providers: [
+        { provide: 'objectElementProvider', useValue: (mockResultObject) },
+        { provide: 'indexElementProvider', useValue: (compIndex) }
+      ],
+      schemas: [NO_ERRORS_SCHEMA]
+    }).overrideComponent(ItemMyDSpaceResultDetailElementComponent, {
+      set: { changeDetection: ChangeDetectionStrategy.Default }
+    }).compileComponents();
+  }));
+
+  beforeEach(async(() => {
+    fixture = TestBed.createComponent(ItemMyDSpaceResultDetailElementComponent);
+    component = fixture.componentInstance;
+  }));
+
+  beforeEach(() => {
+    component.dso = mockResultObject.indexableObject;
+    fixture.detectChanges();
+  });
+
+  it('should have properly status', () => {
+    expect(component.status).toEqual(MyDspaceItemStatusType.ARCHIVED);
+  });
+});
diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/item-my-dspace-result/item-my-dspace-result-detail-element.component.ts b/src/app/shared/object-detail/my-dspace-result-detail-element/item-my-dspace-result/item-my-dspace-result-detail-element.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..26f63deb83d4f5786d58b0a80bcc1662d509eec2
--- /dev/null
+++ b/src/app/shared/object-detail/my-dspace-result-detail-element/item-my-dspace-result/item-my-dspace-result-detail-element.component.ts
@@ -0,0 +1,28 @@
+import { Component } from '@angular/core';
+
+import { renderElementsFor } from '../../../object-collection/shared/dso-element-decorator';
+import { ViewMode } from '../../../../core/shared/view-mode.model';
+import { Item } from '../../../../core/shared/item.model';
+import { ItemMyDSpaceResult } from '../../../object-collection/shared/item-my-dspace-result.model';
+import { MyDSpaceResultDetailElementComponent } from '../my-dspace-result-detail-element.component';
+import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type';
+import { SetViewMode } from '../../../view-mode';
+
+/**
+ * This component renders item object for the mydspace result in the detail view.
+ */
+@Component({
+  selector: 'ds-workspaceitem-my-dspace-result-detail-element',
+  styleUrls: ['../my-dspace-result-detail-element.component.scss', './item-my-dspace-result-detail-element.component.scss'],
+  templateUrl: './item-my-dspace-result-detail-element.component.html'
+})
+
+@renderElementsFor(ItemMyDSpaceResult, SetViewMode.Detail)
+export class ItemMyDSpaceResultDetailElementComponent extends MyDSpaceResultDetailElementComponent<ItemMyDSpaceResult, Item> {
+
+  /**
+   * Represent item's status
+   */
+  public status = MyDspaceItemStatusType.ARCHIVED;
+
+}
diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/my-dspace-result-detail-element.component.scss b/src/app/shared/object-detail/my-dspace-result-detail-element/my-dspace-result-detail-element.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..9a442f84e172313856eb7e18639ff4806d317646
--- /dev/null
+++ b/src/app/shared/object-detail/my-dspace-result-detail-element/my-dspace-result-detail-element.component.scss
@@ -0,0 +1 @@
+@import '../../object-grid/search-result-grid-element/search-result-grid-element.component';
diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/my-dspace-result-detail-element.component.ts b/src/app/shared/object-detail/my-dspace-result-detail-element/my-dspace-result-detail-element.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..47a44d31324bfbea9eea36ec0ada738b44eea2b5
--- /dev/null
+++ b/src/app/shared/object-detail/my-dspace-result-detail-element/my-dspace-result-detail-element.component.ts
@@ -0,0 +1,49 @@
+import { Component, Inject } from '@angular/core';
+
+import { MyDSpaceResult } from '../../../+my-dspace-page/my-dspace-result.model';
+import { AbstractListableElementComponent } from '../../object-collection/shared/object-collection-element/abstract-listable-element.component';
+import { ListableObject } from '../../object-collection/shared/listable-object.model';
+import { DSpaceObject } from '../../../core/shared/dspace-object.model';
+import { Metadata } from '../../../core/shared/metadata.utils';
+
+@Component({
+  selector: 'ds-my-dspace-result-detail-element',
+  template: ``
+})
+export class MyDSpaceResultDetailElementComponent<T extends MyDSpaceResult<K>, K extends DSpaceObject> extends AbstractListableElementComponent<T> {
+
+  /**
+   * The result element object
+   */
+  dso: K;
+
+  /**
+   * Initialize instance variables
+   *
+   * @param {ListableObject} detailable
+   */
+  public constructor(@Inject('objectElementProvider') public detailable: ListableObject) {
+    super(detailable);
+    this.dso = this.object.indexableObject;
+  }
+
+  /**
+   * Gets all matching metadata string values from hitHighlights or dso metadata, preferring hitHighlights.
+   *
+   * @param {string|string[]} keyOrKeys The metadata key(s) in scope. Wildcards are supported; see [[Metadata]].
+   * @returns {string[]} the matching string values or an empty array.
+   */
+  allMetadataValues(keyOrKeys: string | string[]): string[] {
+    return Metadata.allValues([this.object.hitHighlights, this.dso.metadata], keyOrKeys);
+  }
+
+  /**
+   * Gets the first matching metadata string value from hitHighlights or dso metadata, preferring hitHighlights.
+   *
+   * @param {string|string[]} keyOrKeys The metadata key(s) in scope. Wildcards are supported; see [[Metadata]].
+   * @returns {string} the first matching string value, or `undefined`.
+   */
+  firstMetadataValue(keyOrKeys: string | string[]): string {
+    return Metadata.firstValue([this.object.hitHighlights, this.dso.metadata], keyOrKeys);
+  }
+}
diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/pool-my-dspace-result/pool-my-dspace-result-detail-element.component.html b/src/app/shared/object-detail/my-dspace-result-detail-element/pool-my-dspace-result/pool-my-dspace-result-detail-element.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..96ef68e3f87fa1bfbf37e679100663e0c0b2fda7
--- /dev/null
+++ b/src/app/shared/object-detail/my-dspace-result-detail-element/pool-my-dspace-result/pool-my-dspace-result-detail-element.component.html
@@ -0,0 +1,7 @@
+<ds-item-detail-preview *ngIf="workflowitem"
+                        [item]="(workflowitem.item | async)?.payload"
+                        [object]="object"
+                        [showSubmitter]="showSubmitter"
+                        [status]="status"></ds-item-detail-preview>
+
+<ds-pool-task-actions *ngIf="workflowitem" [object]="dso"></ds-pool-task-actions>
diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/pool-my-dspace-result/pool-my-dspace-result-detail-element.component.spec.ts b/src/app/shared/object-detail/my-dspace-result-detail-element/pool-my-dspace-result/pool-my-dspace-result-detail-element.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0eea01aa1cb3146601389fb736b7ca0bd74f57ac
--- /dev/null
+++ b/src/app/shared/object-detail/my-dspace-result-detail-element/pool-my-dspace-result/pool-my-dspace-result-detail-element.component.spec.ts
@@ -0,0 +1,89 @@
+import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { NoopAnimationsModule } from '@angular/platform-browser/animations';
+
+import { of as observableOf } from 'rxjs';
+
+import { Item } from '../../../../core/shared/item.model';
+import { PoolMyDSpaceResultDetailElementComponent } from './pool-my-dspace-result-detail-lement.component';
+import { PoolTaskMyDSpaceResult } from '../../../object-collection/shared/pool-task-my-dspace-result.model';
+import { PoolTask } from '../../../../core/tasks/models/pool-task-object.model';
+import { RemoteData } from '../../../../core/data/remote-data';
+import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type';
+import { Workflowitem } from '../../../../core/submission/models/workflowitem.model';
+
+let component: PoolMyDSpaceResultDetailElementComponent;
+let fixture: ComponentFixture<PoolMyDSpaceResultDetailElementComponent>;
+
+const compIndex = 1;
+
+const mockResultObject: PoolTaskMyDSpaceResult = new PoolTaskMyDSpaceResult();
+mockResultObject.hitHighlights = {};
+
+const item = Object.assign(new Item(), {
+  bitstreams: observableOf({}),
+  metadata: {
+    'dc.title': [
+      {
+        language: 'en_US',
+        value: 'This is just another title'
+      }
+    ],
+    'dc.type': [
+      {
+        language: null,
+        value: 'Article'
+      }
+    ],
+    'dc.contributor.author': [
+      {
+        language: 'en_US',
+        value: 'Smith, Donald'
+      }
+    ],
+    'dc.date.issued': [
+      {
+        language: null,
+        value: '2015-06-26'
+      }
+    ]
+  }
+});
+const rdItem = new RemoteData(false, false, true, null, item);
+const workflowitem = Object.assign(new Workflowitem(), { item: observableOf(rdItem) });
+const rdWorkflowitem = new RemoteData(false, false, true, null, workflowitem);
+mockResultObject.indexableObject = Object.assign(new PoolTask(), { workflowitem: observableOf(rdWorkflowitem) });
+
+describe('PoolMyDSpaceResultDetailElementComponent', () => {
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [NoopAnimationsModule],
+      declarations: [PoolMyDSpaceResultDetailElementComponent],
+      providers: [
+        { provide: 'objectElementProvider', useValue: (mockResultObject) },
+        { provide: 'indexElementProvider', useValue: (compIndex) }
+      ],
+      schemas: [NO_ERRORS_SCHEMA]
+    }).overrideComponent(PoolMyDSpaceResultDetailElementComponent, {
+      set: { changeDetection: ChangeDetectionStrategy.Default }
+    }).compileComponents();
+  }));
+
+  beforeEach(async(() => {
+    fixture = TestBed.createComponent(PoolMyDSpaceResultDetailElementComponent);
+    component = fixture.componentInstance;
+  }));
+
+  beforeEach(() => {
+    component.dso = mockResultObject.indexableObject;
+    fixture.detectChanges();
+  });
+
+  it('should init item properly', () => {
+    expect(component.workflowitem).toEqual(workflowitem);
+  });
+
+  it('should have properly status', () => {
+    expect(component.status).toEqual(MyDspaceItemStatusType.WAITING_CONTROLLER);
+  });
+});
diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/pool-my-dspace-result/pool-my-dspace-result-detail-lement.component.ts b/src/app/shared/object-detail/my-dspace-result-detail-element/pool-my-dspace-result/pool-my-dspace-result-detail-lement.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ef7bcadebfd61d5fc935c6b54d3ff1094e317ddd
--- /dev/null
+++ b/src/app/shared/object-detail/my-dspace-result-detail-element/pool-my-dspace-result/pool-my-dspace-result-detail-lement.component.ts
@@ -0,0 +1,68 @@
+import { Component, Inject } from '@angular/core';
+
+import { Observable } from 'rxjs';
+import { find } from 'rxjs/operators';
+
+import { ViewMode } from '../../../../core/shared/view-mode.model';
+import { renderElementsFor } from '../../../object-collection/shared/dso-element-decorator';
+import { RemoteData } from '../../../../core/data/remote-data';
+import { isNotUndefined } from '../../../empty.util';
+import { ListableObject } from '../../../object-collection/shared/listable-object.model';
+import { Workflowitem } from '../../../../core/submission/models/workflowitem.model';
+import { PoolTask } from '../../../../core/tasks/models/pool-task-object.model';
+import { PoolTaskMyDSpaceResult } from '../../../object-collection/shared/pool-task-my-dspace-result.model';
+import { MyDSpaceResultDetailElementComponent } from '../my-dspace-result-detail-element.component';
+import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type';
+import { SetViewMode } from '../../../view-mode';
+
+/**
+ * This component renders pool task object for the mydspace result in the detail view.
+ */
+@Component({
+  selector: 'ds-pool-my-dspace-result-detail-element',
+  styleUrls: ['../my-dspace-result-detail-element.component.scss'],
+  templateUrl: './pool-my-dspace-result-detail-element.component.html',
+})
+
+@renderElementsFor(PoolTaskMyDSpaceResult, SetViewMode.Detail)
+@renderElementsFor(PoolTask, SetViewMode.Detail)
+export class PoolMyDSpaceResultDetailElementComponent extends MyDSpaceResultDetailElementComponent<PoolTaskMyDSpaceResult, PoolTask> {
+
+  /**
+   * A boolean representing if to show submitter information
+   */
+  public showSubmitter = true;
+
+  /**
+   * Represent item's status
+   */
+  public status = MyDspaceItemStatusType.WAITING_CONTROLLER;
+
+  /**
+   * The workflowitem object that belonging to the result object
+   */
+  public workflowitem: Workflowitem;
+
+  constructor(@Inject('objectElementProvider') public listable: ListableObject) {
+    super(listable);
+  }
+
+  /**
+   * Initialize all instance variables
+   */
+  ngOnInit() {
+    this.initWorkflowItem(this.dso.workflowitem as Observable<RemoteData<Workflowitem>>);
+  }
+
+  /**
+   * Retrieve workflowitem from result object
+   */
+  initWorkflowItem(wfi$: Observable<RemoteData<Workflowitem>>) {
+    wfi$.pipe(
+      find((rd: RemoteData<Workflowitem>) => (rd.hasSucceeded && isNotUndefined(rd.payload)))
+    ).subscribe((rd: RemoteData<Workflowitem>) => {
+      this.workflowitem = rd.payload;
+    });
+  }
+
+}
diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/workflowitem-my-dspace-result/workflowitem-my-dspace-result-detail-element.component.html b/src/app/shared/object-detail/my-dspace-result-detail-element/workflowitem-my-dspace-result/workflowitem-my-dspace-result-detail-element.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..7ff1a9bf728e4903abb622aa8965707270619854
--- /dev/null
+++ b/src/app/shared/object-detail/my-dspace-result-detail-element/workflowitem-my-dspace-result/workflowitem-my-dspace-result-detail-element.component.html
@@ -0,0 +1,6 @@
+<ds-item-detail-preview [item]="item"
+                        [object]="object"
+                        [status]="status"></ds-item-detail-preview>
+
+<ds-workflowitem-actions [object]="dso"></ds-workflowitem-actions>
+
diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/workflowitem-my-dspace-result/workflowitem-my-dspace-result-detail-element.component.spec.ts b/src/app/shared/object-detail/my-dspace-result-detail-element/workflowitem-my-dspace-result/workflowitem-my-dspace-result-detail-element.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f8bdbf9fd6b81e02df26013b9660287b55b38967
--- /dev/null
+++ b/src/app/shared/object-detail/my-dspace-result-detail-element/workflowitem-my-dspace-result/workflowitem-my-dspace-result-detail-element.component.spec.ts
@@ -0,0 +1,86 @@
+import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { NoopAnimationsModule } from '@angular/platform-browser/animations';
+
+import { of as observableOf } from 'rxjs';
+
+import { Item } from '../../../../core/shared/item.model';
+import { WorkflowitemMyDSpaceResultDetailElementComponent } from './workflowitem-my-dspace-result-detail-element.component';
+import { WorkflowitemMyDSpaceResult } from '../../../object-collection/shared/workflowitem-my-dspace-result.model';
+import { Workflowitem } from '../../../../core/submission/models/workflowitem.model';
+import { RemoteData } from '../../../../core/data/remote-data';
+import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type';
+
+let component: WorkflowitemMyDSpaceResultDetailElementComponent;
+let fixture: ComponentFixture<WorkflowitemMyDSpaceResultDetailElementComponent>;
+
+const compIndex = 1;
+
+const mockResultObject: WorkflowitemMyDSpaceResult = new WorkflowitemMyDSpaceResult();
+mockResultObject.hitHighlights = {};
+
+const item = Object.assign(new Item(), {
+  bitstreams: observableOf({}),
+  metadata: {
+    'dc.title': [
+      {
+        language: 'en_US',
+        value: 'This is just another title'
+      }
+    ],
+    'dc.type': [
+      {
+        language: null,
+        value: 'Article'
+      }
+    ],
+    'dc.contributor.author': [
+      {
+        language: 'en_US',
+        value: 'Smith, Donald'
+      }
+    ],
+    'dc.date.issued': [
+      {
+        language: null,
+        value: '2015-06-26'
+      }
+    ]
+  }
+});
+const rd = new RemoteData(false, false, true, null, item);
+mockResultObject.indexableObject = Object.assign(new Workflowitem(), { item: observableOf(rd) });
+
+describe('WorkflowitemMyDSpaceResultDetailElementComponent', () => {
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [NoopAnimationsModule],
+      declarations: [WorkflowitemMyDSpaceResultDetailElementComponent],
+      providers: [
+        { provide: 'objectElementProvider', useValue: (mockResultObject) },
+        { provide: 'indexElementProvider', useValue: (compIndex) }
+      ],
+      schemas: [NO_ERRORS_SCHEMA]
+    }).overrideComponent(WorkflowitemMyDSpaceResultDetailElementComponent, {
+      set: { changeDetection: ChangeDetectionStrategy.Default }
+    }).compileComponents();
+  }));
+
+  beforeEach(async(() => {
+    fixture = TestBed.createComponent(WorkflowitemMyDSpaceResultDetailElementComponent);
+    component = fixture.componentInstance;
+  }));
+
+  beforeEach(() => {
+    component.dso = mockResultObject.indexableObject;
+    fixture.detectChanges();
+  });
+
+  it('should init item properly', () => {
+    expect(component.item).toEqual(item);
+  });
+
+  it('should have properly status', () => {
+    expect(component.status).toEqual(MyDspaceItemStatusType.WORKFLOW);
+  });
+});
diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/workflowitem-my-dspace-result/workflowitem-my-dspace-result-detail-element.component.ts b/src/app/shared/object-detail/my-dspace-result-detail-element/workflowitem-my-dspace-result/workflowitem-my-dspace-result-detail-element.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1c4b0c668f59538f48949ccf14d17a4876a251d6
--- /dev/null
+++ b/src/app/shared/object-detail/my-dspace-result-detail-element/workflowitem-my-dspace-result/workflowitem-my-dspace-result-detail-element.component.ts
@@ -0,0 +1,62 @@
+import { Component, Inject } from '@angular/core';
+
+import { renderElementsFor } from '../../../object-collection/shared/dso-element-decorator';
+import { ViewMode } from '../../../../core/shared/view-mode.model';
+import { Item } from '../../../../core/shared/item.model';
+import { ListableObject } from '../../../object-collection/shared/listable-object.model';
+import { WorkflowitemMyDSpaceResult } from '../../../object-collection/shared/workflowitem-my-dspace-result.model';
+import { Workflowitem } from '../../../../core/submission/models/workflowitem.model';
+import { MyDSpaceResultDetailElementComponent } from '../my-dspace-result-detail-element.component';
+import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type';
+import { Observable } from 'rxjs/internal/Observable';
+import { RemoteData } from '../../../../core/data/remote-data';
+import { find } from 'rxjs/operators';
+import { isNotUndefined } from '../../../empty.util';
+import { SetViewMode } from '../../../view-mode';
+
+/**
+ * This component renders workflowitem object for the mydspace result in the detail view.
+ */
+@Component({
+  selector: 'ds-workflowitem-my-dspace-result-detail-element',
+  styleUrls: ['../my-dspace-result-detail-element.component.scss'],
+  templateUrl: './workflowitem-my-dspace-result-detail-element.component.html',
+})
+
+@renderElementsFor(WorkflowitemMyDSpaceResult, SetViewMode.Detail)
+@renderElementsFor(Workflowitem, SetViewMode.Detail)
+export class WorkflowitemMyDSpaceResultDetailElementComponent extends MyDSpaceResultDetailElementComponent<WorkflowitemMyDSpaceResult, Workflowitem> {
+
+  /**
+   * The item object that belonging to the result object
+   */
+  public item: Item;
+
+  /**
+   * Represent item's status
+   */
+  public status = MyDspaceItemStatusType.WORKFLOW;
+
+  constructor(@Inject('objectElementProvider') public listable: ListableObject) {
+    super(listable);
+  }
+
+  /**
+   * Initialize all instance variables
+   */
+  ngOnInit() {
+    this.initItem(this.dso.item as Observable<RemoteData<Item>>);
+  }
+
+  /**
+   * Retrieve item from result object
+   */
+  initItem(item$: Observable<RemoteData<Item>>) {
+    item$.pipe(
+      find((rd: RemoteData<Item>) => rd.hasSucceeded && isNotUndefined(rd.payload))
+    ).subscribe((rd: RemoteData<Item>) => {
+      this.item = rd.payload;
+    });
+  }
+
+}
diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-detail-element.component.html b/src/app/shared/object-detail/my-dspace-result-detail-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-detail-element.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..8d4eee6ba92685fb80641fcaf1a28ed13fa3a5f3
--- /dev/null
+++ b/src/app/shared/object-detail/my-dspace-result-detail-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-detail-element.component.html
@@ -0,0 +1,5 @@
+<ds-item-detail-preview [item]="item"
+                        [object]="object"
+                        [status]="status"></ds-item-detail-preview>
+
+<ds-workspaceitem-actions [object]="dso"></ds-workspaceitem-actions>
diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-detail-element.component.scss b/src/app/shared/object-detail/my-dspace-result-detail-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-detail-element.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-detail-element.component.spec.ts b/src/app/shared/object-detail/my-dspace-result-detail-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-detail-element.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..af67bf53ea1bc2c4cda908b3b61980d4a68b9816
--- /dev/null
+++ b/src/app/shared/object-detail/my-dspace-result-detail-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-detail-element.component.spec.ts
@@ -0,0 +1,86 @@
+import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { NoopAnimationsModule } from '@angular/platform-browser/animations';
+
+import { of as observableOf } from 'rxjs';
+
+import { Item } from '../../../../core/shared/item.model';
+import { WorkspaceitemMyDSpaceResultDetailElementComponent } from './workspaceitem-my-dspace-result-detail-element.component';
+import { WorkspaceitemMyDSpaceResult } from '../../../object-collection/shared/workspaceitem-my-dspace-result.model';
+import { Workspaceitem } from '../../../../core/submission/models/workspaceitem.model';
+import { RemoteData } from '../../../../core/data/remote-data';
+import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type';
+
+let component: WorkspaceitemMyDSpaceResultDetailElementComponent;
+let fixture: ComponentFixture<WorkspaceitemMyDSpaceResultDetailElementComponent>;
+
+const compIndex = 1;
+
+const mockResultObject: WorkspaceitemMyDSpaceResult = new WorkspaceitemMyDSpaceResult();
+mockResultObject.hitHighlights = {};
+
+const item = Object.assign(new Item(), {
+  bitstreams: observableOf({}),
+  metadata: {
+    'dc.title': [
+      {
+        language: 'en_US',
+        value: 'This is just another title'
+      }
+    ],
+    'dc.type': [
+      {
+        language: null,
+        value: 'Article'
+      }
+    ],
+    'dc.contributor.author': [
+      {
+        language: 'en_US',
+        value: 'Smith, Donald'
+      }
+    ],
+    'dc.date.issued': [
+      {
+        language: null,
+        value: '2015-06-26'
+      }
+    ]
+  }
+});
+const rd = new RemoteData(false, false, true, null, item);
+mockResultObject.indexableObject = Object.assign(new Workspaceitem(), { item: observableOf(rd) });
+
+describe('WorkspaceitemMyDSpaceResultDetailElementComponent', () => {
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [NoopAnimationsModule],
+      declarations: [WorkspaceitemMyDSpaceResultDetailElementComponent],
+      providers: [
+        { provide: 'objectElementProvider', useValue: (mockResultObject) },
+        { provide: 'indexElementProvider', useValue: (compIndex) }
+      ],
+      schemas: [NO_ERRORS_SCHEMA]
+    }).overrideComponent(WorkspaceitemMyDSpaceResultDetailElementComponent, {
+      set: { changeDetection: ChangeDetectionStrategy.Default }
+    }).compileComponents();
+  }));
+
+  beforeEach(async(() => {
+    fixture = TestBed.createComponent(WorkspaceitemMyDSpaceResultDetailElementComponent);
+    component = fixture.componentInstance;
+  }));
+
+  beforeEach(() => {
+    component.dso = mockResultObject.indexableObject;
+    fixture.detectChanges();
+  });
+
+  it('should init item properly', () => {
+    expect(component.item).toEqual(item);
+  });
+
+  it('should have properly status', () => {
+    expect(component.status).toEqual(MyDspaceItemStatusType.WORKSPACE);
+  });
+});
diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-detail-element.component.ts b/src/app/shared/object-detail/my-dspace-result-detail-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-detail-element.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..390db7ec284147ca19d0649e8a425061e8312d1f
--- /dev/null
+++ b/src/app/shared/object-detail/my-dspace-result-detail-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-detail-element.component.ts
@@ -0,0 +1,62 @@
+import { Component, Inject } from '@angular/core';
+
+import { Observable } from 'rxjs';
+import { find } from 'rxjs/operators';
+
+import { renderElementsFor } from '../../../object-collection/shared/dso-element-decorator';
+import { ViewMode } from '../../../../core/shared/view-mode.model';
+import { Workspaceitem } from '../../../../core/submission/models/workspaceitem.model';
+import { WorkspaceitemMyDSpaceResult } from '../../../object-collection/shared/workspaceitem-my-dspace-result.model';
+import { Item } from '../../../../core/shared/item.model';
+import { RemoteData } from '../../../../core/data/remote-data';
+import { isNotUndefined } from '../../../empty.util';
+import { ListableObject } from '../../../object-collection/shared/listable-object.model';
+import { MyDSpaceResultDetailElementComponent } from '../my-dspace-result-detail-element.component';
+import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type';
+import { SetViewMode } from '../../../view-mode';
+
+/**
+ * This component renders workspaceitem object for the mydspace result in the detail view.
+ */
+@Component({
+  selector: 'ds-workspaceitem-my-dspace-result-detail-element',
+  styleUrls: ['../my-dspace-result-detail-element.component.scss', './workspaceitem-my-dspace-result-detail-element.component.scss'],
+  templateUrl: './workspaceitem-my-dspace-result-detail-element.component.html',
+})
+
+@renderElementsFor(WorkspaceitemMyDSpaceResult, SetViewMode.Detail)
+@renderElementsFor(Workspaceitem, SetViewMode.Detail)
+export class WorkspaceitemMyDSpaceResultDetailElementComponent extends MyDSpaceResultDetailElementComponent<WorkspaceitemMyDSpaceResult, Workspaceitem> {
+
+  /**
+   * The item object that belonging to the result object
+   */
+  public item: Item;
+
+  /**
+   * Represent item's status
+   */
+  status = MyDspaceItemStatusType.WORKSPACE;
+
+  constructor(@Inject('objectElementProvider') public listable: ListableObject) {
+    super(listable);
+  }
+
+  /**
+   * Initialize all instance variables
+   */
+  ngOnInit() {
+    this.initItem(this.dso.item as Observable<RemoteData<Item>>);
+  }
+
+  /**
+   * Retrieve item from result object
+   */
+  initItem(item$: Observable<RemoteData<Item>>) {
+    item$.pipe(
+      find((rd: RemoteData<Item>) => rd.hasSucceeded && isNotUndefined(rd.payload))
+    ).subscribe((rd: RemoteData<Item>) => {
+      this.item = rd.payload;
+    });
+  }
+}
diff --git a/src/app/shared/object-detail/object-detail.component.html b/src/app/shared/object-detail/object-detail.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..a57a23027e3c948d2ce40c3d21894bdd2c275abe
--- /dev/null
+++ b/src/app/shared/object-detail/object-detail.component.html
@@ -0,0 +1,22 @@
+<ds-pagination
+  [paginationOptions]="config"
+  [pageInfoState]="objects?.payload"
+  [collectionSize]="objects?.payload?.totalElements"
+  [sortOptions]="sortConfig"
+  [hideGear]="hideGear"
+  [hidePaginationDetail]="hidePaginationDetail"
+  [hidePagerWhenSinglePage]="hidePagerWhenSinglePage"
+  (pageChange)="onPageChange($event)"
+  (pageSizeChange)="onPageSizeChange($event)"
+  (sortDirectionChange)="onSortDirectionChange($event)"
+  (sortFieldChange)="onSortFieldChange($event)"
+  (paginationChange)="onPaginationChange($event)">
+    <div class="row mt-2" *ngIf="objects?.hasSucceeded" @fadeIn>
+      <div class="col"
+           *ngFor="let object of objects?.payload?.page">
+        <ds-wrapper-detail-element [object]="object"></ds-wrapper-detail-element>
+      </div>
+    </div>
+  <ds-error *ngIf="objects.hasFailed | async" message="{{'error.objects' | translate}}"></ds-error>
+  <ds-loading *ngIf="objects.isLoading | async" message="{{'loading.objects' | translate}}"></ds-loading>
+</ds-pagination>
diff --git a/src/app/shared/object-detail/object-detail.component.scss b/src/app/shared/object-detail/object-detail.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..9a077e3e08ecb6e8fa8362cf04e4f8f8bc6fbec8
--- /dev/null
+++ b/src/app/shared/object-detail/object-detail.component.scss
@@ -0,0 +1,9 @@
+@import '../../../styles/variables';
+@import '../../../styles/mixins';
+
+ds-wrapper-detail-element ::ng-deep {
+  div.thumbnail > img {
+    height: $card-thumbnail-height;
+    width: 100%;
+  }
+}
diff --git a/src/app/shared/object-detail/object-detail.component.spec.ts b/src/app/shared/object-detail/object-detail.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9b81f1019f2bec0cb2410f6249ed9b2360ed5554
--- /dev/null
+++ b/src/app/shared/object-detail/object-detail.component.spec.ts
@@ -0,0 +1,181 @@
+import { async, ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
+import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
+import { By } from '@angular/platform-browser';
+import { ObjectDetailComponent } from './object-detail.component';
+import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
+import { MockTranslateLoader } from '../mocks/mock-translate-loader';
+import { RemoteData } from '../../core/data/remote-data';
+import { PaginatedList } from '../../core/data/paginated-list';
+import { PageInfo } from '../../core/shared/page-info.model';
+import { NoopAnimationsModule } from '@angular/platform-browser/animations';
+
+describe('ObjectDetailComponent', () => {
+  let comp: ObjectDetailComponent;
+  let fixture: ComponentFixture<ObjectDetailComponent>;
+  const testEvent = {test: 'test'};
+
+  const testObjects = [
+    { one: 1 },
+    { two: 2 },
+    { three: 3 },
+    { four: 4 },
+    { five: 5 },
+    { six: 6 },
+    { seven: 7 },
+    { eight: 8 },
+    { nine: 9 },
+    { ten: 10 }
+  ];
+  const pageInfo = Object.assign(new PageInfo(), {elementsPerPage: 1, totalElements: 10, totalPages: 10, currentPage: 1})
+  const mockRD = new RemoteData(false, false, true, null, new PaginatedList(pageInfo, testObjects));
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [
+        NoopAnimationsModule,
+        TranslateModule.forRoot({
+          loader: {
+            provide: TranslateLoader,
+            useClass: MockTranslateLoader
+          }
+        })
+      ],
+      declarations: [ObjectDetailComponent],
+      schemas: [NO_ERRORS_SCHEMA]
+    }).overrideComponent(ObjectDetailComponent, {
+      set: { changeDetection: ChangeDetectionStrategy.Default }
+    }).compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(ObjectDetailComponent);
+    comp = fixture.componentInstance; // SearchPageComponent test instance
+    comp.objects = mockRD;
+    fixture.detectChanges();
+  });
+
+  describe('when the pageChange output on the pagination is triggered', () => {
+    beforeEach(() => {
+      spyOn(comp, 'onPageChange');
+      const paginationEl = fixture.debugElement.query(By.css('ds-pagination'));
+      paginationEl.triggerEventHandler('pageChange', testEvent);
+    });
+
+    it('should call onPageChange on the component', () => {
+      expect(comp.onPageChange).toHaveBeenCalledWith(testEvent);
+    });
+  });
+
+  describe('when the pageSizeChange output on the pagination is triggered', () => {
+    beforeEach(() => {
+      spyOn(comp, 'onPageSizeChange');
+      const paginationEl = fixture.debugElement.query(By.css('ds-pagination'));
+      paginationEl.triggerEventHandler('pageSizeChange', testEvent);
+    });
+
+    it('should call onPageSizeChange on the component', () => {
+      expect(comp.onPageSizeChange).toHaveBeenCalledWith(testEvent);
+    });
+  });
+
+  describe('when the sortDirectionChange output on the pagination is triggered', () => {
+    beforeEach(() => {
+      spyOn(comp, 'onSortDirectionChange');
+      const paginationEl = fixture.debugElement.query(By.css('ds-pagination'));
+      paginationEl.triggerEventHandler('sortDirectionChange', testEvent);
+    });
+
+    it('should call onSortDirectionChange on the component', () => {
+      expect(comp.onSortDirectionChange).toHaveBeenCalledWith(testEvent);
+    });
+  });
+
+  describe('when the sortFieldChange output on the pagination is triggered', () => {
+    beforeEach(() => {
+      spyOn(comp, 'onSortFieldChange');
+      const paginationEl = fixture.debugElement.query(By.css('ds-pagination'));
+      paginationEl.triggerEventHandler('sortFieldChange', testEvent);
+    });
+
+    it('should call onSortFieldChange on the component', () => {
+      expect(comp.onSortFieldChange).toHaveBeenCalledWith(testEvent);
+    });
+  });
+
+  describe('when the paginationChange output on the pagination is triggered', () => {
+    beforeEach(() => {
+      spyOn(comp, 'onPaginationChange');
+      const paginationEl = fixture.debugElement.query(By.css('ds-pagination'));
+      paginationEl.triggerEventHandler('paginationChange', testEvent);
+    });
+
+    it('should call onPaginationChange on the component', () => {
+      expect(comp.onPaginationChange).toHaveBeenCalledWith(testEvent);
+    });
+  });
+
+  describe('when onPageChange is triggered with an event', () => {
+    beforeEach(() => {
+      spyOn(comp.pageChange, 'emit');
+      comp.onPageChange(testEvent);
+    });
+
+    it('should emit the value from the pageChange EventEmitter', fakeAsync(() => {
+      tick(1);
+      expect(comp.pageChange.emit).toHaveBeenCalled();
+      expect(comp.pageChange.emit).toHaveBeenCalledWith(testEvent);
+    }));
+  });
+
+  describe('when onPageSizeChange is triggered with an event', () => {
+    beforeEach(() => {
+      spyOn(comp.pageSizeChange, 'emit');
+      comp.onPageSizeChange(testEvent);
+    });
+
+    it('should emit the value from the pageSizeChange EventEmitter', fakeAsync(() => {
+      tick(1);
+      expect(comp.pageSizeChange.emit).toHaveBeenCalled();
+      expect(comp.pageSizeChange.emit).toHaveBeenCalledWith(testEvent);
+    }));
+  });
+
+  describe('when onSortDirectionChange is triggered with an event', () => {
+    beforeEach(() => {
+      spyOn(comp.sortDirectionChange, 'emit');
+      comp.onSortDirectionChange(testEvent);
+    });
+
+    it('should emit the value from the sortDirectionChange EventEmitter', fakeAsync(() => {
+      tick(1);
+      expect(comp.sortDirectionChange.emit).toHaveBeenCalled();
+      expect(comp.sortDirectionChange.emit).toHaveBeenCalledWith(testEvent);
+    }));
+  });
+
+  describe('when onSortFieldChange is triggered with an event', () => {
+    beforeEach(() => {
+      spyOn(comp.sortFieldChange, 'emit');
+      comp.onSortFieldChange(testEvent);
+    });
+
+    it('should emit the value from the sortFieldChange EventEmitter', fakeAsync(() => {
+      tick(1);
+      expect(comp.sortFieldChange.emit).toHaveBeenCalled();
+      expect(comp.sortFieldChange.emit).toHaveBeenCalledWith(testEvent);
+    }));
+  });
+
+  describe('when onPaginationChange is triggered with an event', () => {
+    beforeEach(() => {
+      spyOn(comp.paginationChange, 'emit');
+      comp.onPaginationChange(testEvent);
+    });
+
+    it('should emit the value from the paginationChange EventEmitter', fakeAsync(() => {
+      tick(1);
+      expect(comp.paginationChange.emit).toHaveBeenCalled();
+      expect(comp.paginationChange.emit).toHaveBeenCalledWith(testEvent);
+    }));
+  });
+});
diff --git a/src/app/shared/object-detail/object-detail.component.ts b/src/app/shared/object-detail/object-detail.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3187a2cd1b8e56eeddfcd3f089f83367c6ed5a98
--- /dev/null
+++ b/src/app/shared/object-detail/object-detail.component.ts
@@ -0,0 +1,154 @@
+import {
+  ChangeDetectionStrategy,
+  Component,
+  EventEmitter,
+  Input,
+  Output,
+  ViewEncapsulation
+} from '@angular/core';
+
+import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model';
+import { PaginatedList } from '../../core/data/paginated-list';
+
+import { RemoteData } from '../../core/data/remote-data';
+import { fadeIn } from '../animations/fade';
+import { ListableObject } from '../object-collection/shared/listable-object.model';
+
+import { PaginationComponentOptions } from '../pagination/pagination-component-options.model';
+
+/**
+ * This component renders a paginated set of results in the detail view.
+ */
+@Component({
+  changeDetection: ChangeDetectionStrategy.Default,
+  encapsulation: ViewEncapsulation.Emulated,
+  selector: 'ds-object-detail',
+  styleUrls: [ './object-detail.component.scss' ],
+  templateUrl: './object-detail.component.html',
+  animations: [fadeIn]
+})
+export class ObjectDetailComponent {
+
+  /**
+   * Pagination options object
+   */
+  @Input() config: PaginationComponentOptions;
+
+  /**
+   * Sort options object
+   */
+  @Input() sortConfig: SortOptions;
+
+  /**
+   * A boolean representing if to hide gear pagination icon
+   */
+  @Input() hideGear = true;
+
+  /**
+   * A boolean representing if to hide pagination when there is only a page
+   */
+  @Input() hidePagerWhenSinglePage = true;
+
+  /**
+   * The list of objects to paginate
+   */
+  private _objects: RemoteData<PaginatedList<ListableObject>>;
+
+  /**
+   * Setter for _objects property
+   * @param objects
+   */
+  @Input() set objects(objects: RemoteData<PaginatedList<ListableObject>>) {
+    this._objects = objects;
+  }
+
+  /**
+   * Getter for _objects property
+   */
+  get objects() {
+    return this._objects;
+  }
+
+  /**
+   * Option for hiding the pagination detail
+   */
+  public hidePaginationDetail = true;
+
+  /**
+   * An event fired when the page is changed.
+   * Event's payload equals to the newly selected page.
+   */
+  @Output() change: EventEmitter<{
+    pagination: PaginationComponentOptions,
+    sort: SortOptions
+  }> = new EventEmitter<{
+    pagination: PaginationComponentOptions,
+    sort: SortOptions
+  }>();
+
+  /**
+   * An event fired when the page is changed.
+   * Event's payload equals to the newly selected page.
+   */
+  @Output() pageChange: EventEmitter<number> = new EventEmitter<number>();
+
+  /**
+   * An event fired when the page wsize is changed.
+   * Event's payload equals to the newly selected page size.
+   */
+  @Output() pageSizeChange: EventEmitter<number> = new EventEmitter<number>();
+
+  /**
+   * An event fired when the sort direction is changed.
+   * Event's payload equals to the newly selected sort direction.
+   */
+  @Output() sortDirectionChange: EventEmitter<SortDirection> = new EventEmitter<SortDirection>();
+
+  /**
+   * An event fired when the pagination is changed.
+   * Event's payload equals to the newly selected sort direction.
+   */
+  @Output() paginationChange: EventEmitter<SortDirection> = new EventEmitter<any>();
+
+  /**
+   * An event fired when the sort field is changed.
+   * Event's payload equals to the newly selected sort field.
+   */
+  @Output() sortFieldChange: EventEmitter<string> = new EventEmitter<string>();
+
+  /**
+   * Emit pageChange event
+   */
+  onPageChange(event) {
+    this.pageChange.emit(event);
+  }
+
+  /**
+   * Emit pageSizeChange event
+   */
+  onPageSizeChange(event) {
+    this.pageSizeChange.emit(event);
+  }
+
+  /**
+   * Emit sortDirectionChange event
+   */
+  onSortDirectionChange(event) {
+    this.sortDirectionChange.emit(event);
+  }
+
+  /**
+   * Emit sortFieldChange event
+   */
+  onSortFieldChange(event) {
+    this.sortFieldChange.emit(event);
+  }
+
+  /**
+   * Emit paginationChange event
+   */
+  onPaginationChange(event) {
+    this.paginationChange.emit(event);
+  }
+
+}
diff --git a/src/app/shared/object-detail/wrapper-detail-element/wrapper-detail-element.component.html b/src/app/shared/object-detail/wrapper-detail-element/wrapper-detail-element.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..00a8ed2dc8bd1cbaab3bf1168204c6a531961bb2
--- /dev/null
+++ b/src/app/shared/object-detail/wrapper-detail-element/wrapper-detail-element.component.html
@@ -0,0 +1 @@
+<ng-container *ngComponentOutlet="getDetailElement(); injector: objectInjector;"></ng-container>
diff --git a/src/app/shared/object-detail/wrapper-detail-element/wrapper-detail-element.component.scss b/src/app/shared/object-detail/wrapper-detail-element/wrapper-detail-element.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..51a7fc6a558e9c28a56325c7f82ff3204956ab0e
--- /dev/null
+++ b/src/app/shared/object-detail/wrapper-detail-element/wrapper-detail-element.component.scss
@@ -0,0 +1,2 @@
+@import '../../../../styles/variables';
+
diff --git a/src/app/shared/object-detail/wrapper-detail-element/wrapper-detail-element.component.spec.ts b/src/app/shared/object-detail/wrapper-detail-element/wrapper-detail-element.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e54ae58398d8ef28ebfd860a35b71e3f673a1e4f
--- /dev/null
+++ b/src/app/shared/object-detail/wrapper-detail-element/wrapper-detail-element.component.spec.ts
@@ -0,0 +1,44 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { NO_ERRORS_SCHEMA } from '@angular/core';
+import { By } from '@angular/platform-browser';
+import { ActivatedRoute, Router } from '@angular/router';
+
+import { of as observableOf } from 'rxjs';
+
+import { RouterStub } from '../../testing/router-stub';
+import { WrapperDetailElementComponent } from './wrapper-detail-element.component';
+
+let wrapperDetailElementComponent: WrapperDetailElementComponent;
+let fixture: ComponentFixture<WrapperDetailElementComponent>;
+const queryParam = 'test query';
+const scopeParam = '7669c72a-3f2a-451f-a3b9-9210e7a4c02f';
+const activatedRouteStub = {
+  queryParams: observableOf({
+    query: queryParam,
+    scope: scopeParam
+  })
+};
+
+describe('WrapperDetailElementComponent', () => {
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ WrapperDetailElementComponent ],
+      providers: [
+        { provide: ActivatedRoute, useValue: activatedRouteStub },
+        { provide: Router, useClass: RouterStub },
+        { provide: 'objectElementProvider', useFactory: (WrapperDetailElementComponent)}
+      ],
+
+      schemas: [ NO_ERRORS_SCHEMA ]
+    }).compileComponents();  // compile template and css
+  }));
+
+  beforeEach(async(() => {
+    fixture = TestBed.createComponent(WrapperDetailElementComponent);
+    wrapperDetailElementComponent = fixture.componentInstance;
+  }));
+
+  it('should show the wrapper element containing the detail object',() => {
+    expect(fixture.debugElement.query(By.css('ds-workspaceitem-my-dspace-result-detail-element'))).toBeDefined();
+  })
+});
diff --git a/src/app/shared/object-detail/wrapper-detail-element/wrapper-detail-element.component.ts b/src/app/shared/object-detail/wrapper-detail-element/wrapper-detail-element.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..92b30f9ce72dcbd7dd7bc54619e0ec00a2a7bd4a
--- /dev/null
+++ b/src/app/shared/object-detail/wrapper-detail-element/wrapper-detail-element.component.ts
@@ -0,0 +1,55 @@
+import { Component, Injector, Input, OnInit } from '@angular/core';
+
+import { ViewMode } from '../../../core/shared/view-mode.model';
+import { GenericConstructor } from '../../../core/shared/generic-constructor';
+import { rendersDSOType } from '../../object-collection/shared/dso-element-decorator';
+import { ListableObject } from '../../object-collection/shared/listable-object.model';
+import { SetViewMode } from '../../view-mode';
+
+/**
+ * This component renders a wrapper for an object in the detail view.
+ */
+@Component({
+  selector: 'ds-wrapper-detail-element',
+  styleUrls: ['./wrapper-detail-element.component.scss'],
+  templateUrl: './wrapper-detail-element.component.html'
+})
+export class WrapperDetailElementComponent implements OnInit {
+
+  /**
+   * The listable object.
+   */
+  @Input() object: ListableObject;
+
+  /**
+   * The instance of the injector.
+   */
+  objectInjector: Injector;
+
+  /**
+   * Initialize instance variables
+   *
+   * @param {Injector} injector
+   */
+  constructor(private injector: Injector) {
+  }
+
+  /**
+   * Initialize injector
+   */
+  ngOnInit(): void {
+    this.objectInjector = Injector.create({
+      providers: [{ provide: 'objectElementProvider', useFactory: () => (this.object), deps:[] }],
+      parent: this.injector
+    });
+
+  }
+
+  /**
+   * Return class name for the object to inject
+   */
+  getDetailElement(): string {
+    const f: GenericConstructor<ListableObject> = this.object.constructor as GenericConstructor<ListableObject>;
+    return rendersDSOType(f, SetViewMode.Detail);
+  }
+}
diff --git a/src/app/shared/object-grid/collection-grid-element/collection-grid-element.component.ts b/src/app/shared/object-grid/collection-grid-element/collection-grid-element.component.ts
index a690037619c52c889d9271be468d9da2aed7dfe2..af6027aa1a1f6acb717cecd0f0d57873f85fda35 100644
--- a/src/app/shared/object-grid/collection-grid-element/collection-grid-element.component.ts
+++ b/src/app/shared/object-grid/collection-grid-element/collection-grid-element.component.ts
@@ -2,8 +2,8 @@ import { Component, Inject } from '@angular/core';
 
 import { Collection } from '../../../core/shared/collection.model';
 import { renderElementsFor} from '../../object-collection/shared/dso-element-decorator';
+import { SetViewMode } from '../../view-mode';
 import { AbstractListableElementComponent } from '../../object-collection/shared/object-collection-element/abstract-listable-element.component';
-import { ViewMode } from '../../../core/shared/view-mode.model';
 
 @Component({
   selector: 'ds-collection-grid-element',
@@ -11,5 +11,5 @@ import { ViewMode } from '../../../core/shared/view-mode.model';
   templateUrl: './collection-grid-element.component.html'
 })
 
-@renderElementsFor(Collection, ViewMode.Grid)
+@renderElementsFor(Collection, SetViewMode.Grid)
 export class CollectionGridElementComponent extends AbstractListableElementComponent<Collection> {}
diff --git a/src/app/shared/object-grid/community-grid-element/community-grid-element.component.ts b/src/app/shared/object-grid/community-grid-element/community-grid-element.component.ts
index 4df9ab9555e5a1d8f0e0202862348f2808034e84..4f69e3e2c7d90fad3766b5c8095ca03da40a98a8 100644
--- a/src/app/shared/object-grid/community-grid-element/community-grid-element.component.ts
+++ b/src/app/shared/object-grid/community-grid-element/community-grid-element.component.ts
@@ -3,7 +3,7 @@ import { Component, Input, Inject } from '@angular/core';
 import { Community } from '../../../core/shared/community.model';
 import { AbstractListableElementComponent } from '../../object-collection/shared/object-collection-element/abstract-listable-element.component';
 import { renderElementsFor} from '../../object-collection/shared/dso-element-decorator';
-import { ViewMode } from '../../../core/shared/view-mode.model';
+import { SetViewMode } from '../../view-mode';
 
 @Component({
   selector: 'ds-community-grid-element',
@@ -11,5 +11,5 @@ import { ViewMode } from '../../../core/shared/view-mode.model';
   templateUrl: './community-grid-element.component.html'
 })
 
-@renderElementsFor(Community, ViewMode.Grid)
+@renderElementsFor(Community, SetViewMode.Grid)
 export class CommunityGridElementComponent extends AbstractListableElementComponent<Community> {}
diff --git a/src/app/shared/object-grid/item-grid-element/item-grid-element.component.ts b/src/app/shared/object-grid/item-grid-element/item-grid-element.component.ts
index 7a679fef25ed20f0c660b4263a49e90983ea32c0..a4137b3c26a169c2901f95155c05fdd4799edb2b 100644
--- a/src/app/shared/object-grid/item-grid-element/item-grid-element.component.ts
+++ b/src/app/shared/object-grid/item-grid-element/item-grid-element.component.ts
@@ -3,7 +3,7 @@ import { Component, Input, Inject } from '@angular/core';
 import { Item } from '../../../core/shared/item.model';
 import { renderElementsFor} from '../../object-collection/shared/dso-element-decorator';
 import { AbstractListableElementComponent } from '../../object-collection/shared/object-collection-element/abstract-listable-element.component';
-import { ViewMode } from '../../../core/shared/view-mode.model';
+import { SetViewMode } from '../../view-mode';
 
 @Component({
   selector: 'ds-item-grid-element',
@@ -11,5 +11,5 @@ import { ViewMode } from '../../../core/shared/view-mode.model';
   templateUrl: './item-grid-element.component.html'
 })
 
-@renderElementsFor(Item, ViewMode.Grid)
+@renderElementsFor(Item, SetViewMode.Grid)
 export class ItemGridElementComponent extends AbstractListableElementComponent<Item> {}
diff --git a/src/app/shared/object-grid/search-result-grid-element/collection-search-result/collection-search-result-grid-element.component.spec.ts b/src/app/shared/object-grid/search-result-grid-element/collection-search-result/collection-search-result-grid-element.component.spec.ts
index e8f8b1330e330ad5d79ee1af3e2b3bf60f6dc9a2..07f3960d556d07013b4ae00efcb19ab872dcfd3e 100644
--- a/src/app/shared/object-grid/search-result-grid-element/collection-search-result/collection-search-result-grid-element.component.spec.ts
+++ b/src/app/shared/object-grid/search-result-grid-element/collection-search-result/collection-search-result-grid-element.component.spec.ts
@@ -17,7 +17,7 @@ const truncatableServiceStub: any = {
 
 const mockCollectionWithAbstract: CollectionSearchResult = new CollectionSearchResult();
 mockCollectionWithAbstract.hitHighlights = {};
-mockCollectionWithAbstract.dspaceObject = Object.assign(new Collection(), {
+mockCollectionWithAbstract.indexableObject = Object.assign(new Collection(), {
   metadata: {
     'dc.description.abstract': [
       {
@@ -30,7 +30,7 @@ mockCollectionWithAbstract.dspaceObject = Object.assign(new Collection(), {
 
 const mockCollectionWithoutAbstract: CollectionSearchResult = new CollectionSearchResult();
 mockCollectionWithoutAbstract.hitHighlights = {};
-mockCollectionWithoutAbstract.dspaceObject = Object.assign(new Collection(), {
+mockCollectionWithoutAbstract.indexableObject = Object.assign(new Collection(), {
   metadata: {
     'dc.title': [
       {
@@ -63,7 +63,7 @@ describe('CollectionSearchResultGridElementComponent', () => {
 
   describe('When the collection has an abstract', () => {
     beforeEach(() => {
-      collectionSearchResultGridElementComponent.dso = mockCollectionWithAbstract.dspaceObject;
+      collectionSearchResultGridElementComponent.dso = mockCollectionWithAbstract.indexableObject;
       fixture.detectChanges();
     });
 
@@ -75,7 +75,7 @@ describe('CollectionSearchResultGridElementComponent', () => {
 
   describe('When the collection has no abstract', () => {
     beforeEach(() => {
-      collectionSearchResultGridElementComponent.dso = mockCollectionWithoutAbstract.dspaceObject;
+      collectionSearchResultGridElementComponent.dso = mockCollectionWithoutAbstract.indexableObject;
       fixture.detectChanges();
     });
 
diff --git a/src/app/shared/object-grid/search-result-grid-element/collection-search-result/collection-search-result-grid-element.component.ts b/src/app/shared/object-grid/search-result-grid-element/collection-search-result/collection-search-result-grid-element.component.ts
index c1f27bee4f11cb02f2c3e7f5c735273f03fd9c36..61cc5dca1ce5a4ef0db6732bc9681d72276fb7b1 100644
--- a/src/app/shared/object-grid/search-result-grid-element/collection-search-result/collection-search-result-grid-element.component.ts
+++ b/src/app/shared/object-grid/search-result-grid-element/collection-search-result/collection-search-result-grid-element.component.ts
@@ -3,8 +3,8 @@ import { Component } from '@angular/core';
 import { renderElementsFor} from '../../../object-collection/shared/dso-element-decorator';
 import { SearchResultGridElementComponent } from '../search-result-grid-element.component';
 import { Collection } from '../../../../core/shared/collection.model';
+import { SetViewMode } from '../../../view-mode';
 import { CollectionSearchResult } from '../../../object-collection/shared/collection-search-result.model';
-import { ViewMode } from '../../../../core/shared/view-mode.model';
 
 @Component({
   selector: 'ds-collection-search-result-grid-element',
@@ -12,5 +12,5 @@ import { ViewMode } from '../../../../core/shared/view-mode.model';
   templateUrl: 'collection-search-result-grid-element.component.html'
 })
 
-@renderElementsFor(CollectionSearchResult, ViewMode.Grid)
+@renderElementsFor(CollectionSearchResult, SetViewMode.Grid)
 export class CollectionSearchResultGridElementComponent extends SearchResultGridElementComponent<CollectionSearchResult, Collection> {}
diff --git a/src/app/shared/object-grid/search-result-grid-element/community-search-result/community-search-result-grid-element.component.spec.ts b/src/app/shared/object-grid/search-result-grid-element/community-search-result/community-search-result-grid-element.component.spec.ts
index e111e624c5927d008b11a1d38258280fc214ce7a..567b2e1d0e045d9aa5b3b3d68e2b171b8bc088e8 100644
--- a/src/app/shared/object-grid/search-result-grid-element/community-search-result/community-search-result-grid-element.component.spec.ts
+++ b/src/app/shared/object-grid/search-result-grid-element/community-search-result/community-search-result-grid-element.component.spec.ts
@@ -17,7 +17,7 @@ const truncatableServiceStub: any = {
 
 const mockCommunityWithAbstract: CommunitySearchResult = new CommunitySearchResult();
 mockCommunityWithAbstract.hitHighlights = {};
-mockCommunityWithAbstract.dspaceObject = Object.assign(new Community(), {
+mockCommunityWithAbstract.indexableObject = Object.assign(new Community(), {
   metadata: {
     'dc.description.abstract': [
       {
@@ -30,7 +30,7 @@ mockCommunityWithAbstract.dspaceObject = Object.assign(new Community(), {
 
 const mockCommunityWithoutAbstract: CommunitySearchResult = new CommunitySearchResult();
 mockCommunityWithoutAbstract.hitHighlights = {};
-mockCommunityWithoutAbstract.dspaceObject = Object.assign(new Community(), {
+mockCommunityWithoutAbstract.indexableObject = Object.assign(new Community(), {
   metadata: {
     'dc.title': [
       {
@@ -63,7 +63,7 @@ describe('CommunitySearchResultGridElementComponent', () => {
 
   describe('When the community has an abstract', () => {
     beforeEach(() => {
-      communitySearchResultGridElementComponent.dso = mockCommunityWithAbstract.dspaceObject;
+      communitySearchResultGridElementComponent.dso = mockCommunityWithAbstract.indexableObject;
       fixture.detectChanges();
     });
 
@@ -75,7 +75,7 @@ describe('CommunitySearchResultGridElementComponent', () => {
 
   describe('When the community has no abstract', () => {
     beforeEach(() => {
-      communitySearchResultGridElementComponent.dso = mockCommunityWithoutAbstract.dspaceObject;
+      communitySearchResultGridElementComponent.dso = mockCommunityWithoutAbstract.indexableObject;
       fixture.detectChanges();
     });
 
diff --git a/src/app/shared/object-grid/search-result-grid-element/community-search-result/community-search-result-grid-element.component.ts b/src/app/shared/object-grid/search-result-grid-element/community-search-result/community-search-result-grid-element.component.ts
index 8fac11a6a4ca18c35fd32eee4d40f373c1a1b9ba..d445e27ed6570c39916a7f1f8075e75eff7d54e7 100644
--- a/src/app/shared/object-grid/search-result-grid-element/community-search-result/community-search-result-grid-element.component.ts
+++ b/src/app/shared/object-grid/search-result-grid-element/community-search-result/community-search-result-grid-element.component.ts
@@ -2,8 +2,8 @@ import { Component } from '@angular/core';
 import { Community } from '../../../../core/shared/community.model';
 import { renderElementsFor } from '../../../object-collection/shared/dso-element-decorator';
 import { SearchResultGridElementComponent } from '../search-result-grid-element.component';
+import { SetViewMode } from '../../../view-mode';
 import { CommunitySearchResult } from '../../../object-collection/shared/community-search-result.model';
-import { ViewMode } from '../../../../core/shared/view-mode.model';
 
 @Component({
   selector: 'ds-community-search-result-grid-element',
@@ -11,7 +11,7 @@ import { ViewMode } from '../../../../core/shared/view-mode.model';
   templateUrl: 'community-search-result-grid-element.component.html'
 })
 
-@renderElementsFor(CommunitySearchResult, ViewMode.Grid)
+@renderElementsFor(CommunitySearchResult, SetViewMode.Grid)
 export class CommunitySearchResultGridElementComponent extends SearchResultGridElementComponent<CommunitySearchResult, Community> {
 
 }
diff --git a/src/app/shared/object-grid/search-result-grid-element/item-search-result/item-search-result-grid-element.component.spec.ts b/src/app/shared/object-grid/search-result-grid-element/item-search-result/item-search-result-grid-element.component.spec.ts
index 0103fa5c49b929ccea2de8620b223dfba1570a1f..655fd268a75d786d09a751e2841fb5286ab47c67 100644
--- a/src/app/shared/object-grid/search-result-grid-element/item-search-result/item-search-result-grid-element.component.spec.ts
+++ b/src/app/shared/object-grid/search-result-grid-element/item-search-result/item-search-result-grid-element.component.spec.ts
@@ -18,7 +18,7 @@ const truncatableServiceStub: any = {
 
 const mockItemWithAuthorAndDate: ItemSearchResult = new ItemSearchResult();
 mockItemWithAuthorAndDate.hitHighlights = {};
-mockItemWithAuthorAndDate.dspaceObject = Object.assign(new Item(), {
+mockItemWithAuthorAndDate.indexableObject = Object.assign(new Item(), {
   bitstreams: observableOf({}),
   metadata: {
     'dc.contributor.author': [
@@ -38,7 +38,7 @@ mockItemWithAuthorAndDate.dspaceObject = Object.assign(new Item(), {
 
 const mockItemWithoutAuthorAndDate: ItemSearchResult = new ItemSearchResult();
 mockItemWithoutAuthorAndDate.hitHighlights = {};
-mockItemWithoutAuthorAndDate.dspaceObject = Object.assign(new Item(), {
+mockItemWithoutAuthorAndDate.indexableObject = Object.assign(new Item(), {
   bitstreams: observableOf({}),
   metadata: {
     'dc.title': [
@@ -78,7 +78,7 @@ describe('ItemSearchResultGridElementComponent', () => {
 
   describe('When the item has an author', () => {
     beforeEach(() => {
-      itemSearchResultGridElementComponent.dso = mockItemWithAuthorAndDate.dspaceObject;
+      itemSearchResultGridElementComponent.dso = mockItemWithAuthorAndDate.indexableObject;
       fixture.detectChanges();
     });
 
@@ -90,7 +90,7 @@ describe('ItemSearchResultGridElementComponent', () => {
 
   describe('When the item has no author', () => {
     beforeEach(() => {
-      itemSearchResultGridElementComponent.dso = mockItemWithoutAuthorAndDate.dspaceObject;
+      itemSearchResultGridElementComponent.dso = mockItemWithoutAuthorAndDate.indexableObject;
       fixture.detectChanges();
     });
 
@@ -102,7 +102,7 @@ describe('ItemSearchResultGridElementComponent', () => {
 
   describe('When the item has an issuedate', () => {
     beforeEach(() => {
-      itemSearchResultGridElementComponent.dso = mockItemWithAuthorAndDate.dspaceObject;
+      itemSearchResultGridElementComponent.dso = mockItemWithAuthorAndDate.indexableObject;
       fixture.detectChanges();
     });
 
@@ -114,7 +114,7 @@ describe('ItemSearchResultGridElementComponent', () => {
 
   describe('When the item has no issuedate', () => {
     beforeEach(() => {
-      itemSearchResultGridElementComponent.dso = mockItemWithoutAuthorAndDate.dspaceObject;
+      itemSearchResultGridElementComponent.dso = mockItemWithoutAuthorAndDate.indexableObject;
       fixture.detectChanges();
     });
 
diff --git a/src/app/shared/object-grid/search-result-grid-element/item-search-result/item-search-result-grid-element.component.ts b/src/app/shared/object-grid/search-result-grid-element/item-search-result/item-search-result-grid-element.component.ts
index 232c1617797af5dfd784e0885243c1173612d7d3..30c36b3af9f38b04cb35be09ff3399ede565fa4f 100644
--- a/src/app/shared/object-grid/search-result-grid-element/item-search-result/item-search-result-grid-element.component.ts
+++ b/src/app/shared/object-grid/search-result-grid-element/item-search-result/item-search-result-grid-element.component.ts
@@ -4,8 +4,8 @@ import { renderElementsFor } from '../../../object-collection/shared/dso-element
 import { SearchResultGridElementComponent } from '../search-result-grid-element.component';
 import { Item } from '../../../../core/shared/item.model';
 import { ItemSearchResult } from '../../../object-collection/shared/item-search-result.model';
+import { SetViewMode } from '../../../view-mode';
 import { focusShadow } from '../../../../shared/animations/focus';
-import { ViewMode } from '../../../../core/shared/view-mode.model';
 
 @Component({
   selector: 'ds-item-search-result-grid-element',
@@ -14,5 +14,5 @@ import { ViewMode } from '../../../../core/shared/view-mode.model';
   animations: [focusShadow],
 })
 
-@renderElementsFor(ItemSearchResult, ViewMode.Grid)
+@renderElementsFor(ItemSearchResult, SetViewMode.Grid)
 export class ItemSearchResultGridElementComponent extends SearchResultGridElementComponent<ItemSearchResult, Item> {}
diff --git a/src/app/shared/object-grid/search-result-grid-element/search-result-grid-element.component.ts b/src/app/shared/object-grid/search-result-grid-element/search-result-grid-element.component.ts
index 844d0bc165170ff5ad43110974c944e4207aa5d6..0961dc96ee3c1fd39f0dd8e78c3390c5d1f16480 100644
--- a/src/app/shared/object-grid/search-result-grid-element/search-result-grid-element.component.ts
+++ b/src/app/shared/object-grid/search-result-grid-element/search-result-grid-element.component.ts
@@ -18,7 +18,7 @@ export class SearchResultGridElementComponent<T extends SearchResult<K>, K exten
 
   public constructor(@Inject('objectElementProvider') public listableObject: ListableObject, private truncatableService: TruncatableService) {
     super(listableObject);
-    this.dso = this.object.dspaceObject;
+    this.dso = this.object.indexableObject;
   }
 
   /**
diff --git a/src/app/shared/object-grid/wrapper-grid-element/wrapper-grid-element.component.ts b/src/app/shared/object-grid/wrapper-grid-element/wrapper-grid-element.component.ts
index e16aac6799c565773d73b8a74ed7cf02a6e3417a..84f9357b2d1ba921d80f7338113e2014bccbb5bd 100644
--- a/src/app/shared/object-grid/wrapper-grid-element/wrapper-grid-element.component.ts
+++ b/src/app/shared/object-grid/wrapper-grid-element/wrapper-grid-element.component.ts
@@ -1,8 +1,8 @@
 import { Component, Injector, Input, OnInit } from '@angular/core';
+import { SetViewMode } from '../../view-mode';
 import { GenericConstructor } from '../../../core/shared/generic-constructor';
 import { rendersDSOType } from '../../object-collection/shared/dso-element-decorator';
 import { ListableObject } from '../../object-collection/shared/listable-object.model';
-import { ViewMode } from '../../../core/shared/view-mode.model';
 
 @Component({
   selector: 'ds-wrapper-grid-element',
@@ -26,6 +26,6 @@ export class WrapperGridElementComponent implements OnInit {
 
   getGridElement(): string {
     const f: GenericConstructor<ListableObject> = this.object.constructor as GenericConstructor<ListableObject>;
-    return rendersDSOType(f, ViewMode.Grid);
+    return rendersDSOType(f, SetViewMode.Grid);
   }
 }
diff --git a/src/app/shared/object-list/browse-entry-list-element/browse-entry-list-element.component.ts b/src/app/shared/object-list/browse-entry-list-element/browse-entry-list-element.component.ts
index 44f6a8d0517400359b050635527c11c26cef9074..a32cfb333e2c39a54c35c192b152a88b151ef088 100644
--- a/src/app/shared/object-list/browse-entry-list-element/browse-entry-list-element.component.ts
+++ b/src/app/shared/object-list/browse-entry-list-element/browse-entry-list-element.component.ts
@@ -4,6 +4,7 @@ import { AbstractListableElementComponent } from '../../object-collection/shared
 import { renderElementsFor } from '../../object-collection/shared/dso-element-decorator';
 import { BrowseEntry } from '../../../core/shared/browse-entry.model';
 import { ViewMode } from '../../../core/shared/view-mode.model';
+import { SetViewMode } from '../../view-mode';
 
 @Component({
   selector: 'ds-browse-entry-list-element',
@@ -14,5 +15,5 @@ import { ViewMode } from '../../../core/shared/view-mode.model';
 /**
  * This component is automatically used to create a list view for BrowseEntry objects when used in ObjectCollectionComponent
  */
-@renderElementsFor(BrowseEntry, ViewMode.List)
+@renderElementsFor(BrowseEntry, SetViewMode.List)
 export class BrowseEntryListElementComponent extends AbstractListableElementComponent<BrowseEntry> {}
diff --git a/src/app/shared/object-list/collection-list-element/collection-list-element.component.ts b/src/app/shared/object-list/collection-list-element/collection-list-element.component.ts
index b4e9de16b79dab4cb97d728c88f9fe58e95be696..3fe64f5bb9329bcc133adae90bbb20391f8305fd 100644
--- a/src/app/shared/object-list/collection-list-element/collection-list-element.component.ts
+++ b/src/app/shared/object-list/collection-list-element/collection-list-element.component.ts
@@ -2,8 +2,8 @@ import { Component, Inject } from '@angular/core';
 
 import { Collection } from '../../../core/shared/collection.model';
 import { renderElementsFor } from '../../object-collection/shared/dso-element-decorator';
+import { SetViewMode } from '../../view-mode';
 import { AbstractListableElementComponent } from '../../object-collection/shared/object-collection-element/abstract-listable-element.component';
-import { ViewMode } from '../../../core/shared/view-mode.model';
 
 @Component({
   selector: 'ds-collection-list-element',
@@ -11,5 +11,5 @@ import { ViewMode } from '../../../core/shared/view-mode.model';
   templateUrl: './collection-list-element.component.html'
 })
 
-@renderElementsFor(Collection, ViewMode.List)
+@renderElementsFor(Collection, SetViewMode.List)
 export class CollectionListElementComponent extends AbstractListableElementComponent<Collection> {}
diff --git a/src/app/shared/object-list/community-list-element/community-list-element.component.ts b/src/app/shared/object-list/community-list-element/community-list-element.component.ts
index 8f6870db7ed8400c65af22c0d3aa2a739e0c55dd..5e254cb1ac4d3742f14f63ae3fe50706353ac4c0 100644
--- a/src/app/shared/object-list/community-list-element/community-list-element.component.ts
+++ b/src/app/shared/object-list/community-list-element/community-list-element.component.ts
@@ -1,9 +1,9 @@
-import { Component } from '@angular/core';
+import { Component, Input, Inject } from '@angular/core';
 
 import { Community } from '../../../core/shared/community.model';
 import { AbstractListableElementComponent } from '../../object-collection/shared/object-collection-element/abstract-listable-element.component';
 import { renderElementsFor } from '../../object-collection/shared/dso-element-decorator';
-import { ViewMode } from '../../../core/shared/view-mode.model';
+import { SetViewMode } from '../../view-mode';
 
 @Component({
   selector: 'ds-community-list-element',
@@ -11,5 +11,5 @@ import { ViewMode } from '../../../core/shared/view-mode.model';
   templateUrl: './community-list-element.component.html'
 })
 
-@renderElementsFor(Community, ViewMode.List)
+@renderElementsFor(Community, SetViewMode.List)
 export class CommunityListElementComponent extends AbstractListableElementComponent<Community> {}
diff --git a/src/app/shared/object-list/item-list-element/item-list-element.component.html b/src/app/shared/object-list/item-list-element/item-list-element.component.html
index 8179b776297a600609fdda0c36ef13335be34783..d433c7acf24ce47b1cc4c820e700795566529541 100644
--- a/src/app/shared/object-list/item-list-element/item-list-element.component.html
+++ b/src/app/shared/object-list/item-list-element/item-list-element.component.html
@@ -1,24 +1 @@
-<ds-truncatable [id]="object.id">
-  <a [routerLink]="['/items/' + object.id]" class="lead">
-    {{object.firstMetadataValue("dc.title")}}
-  </a>
-  <div>
-    <ds-truncatable-part [id]="object.id" [minLines]="1">
-      <span class="text-muted">
-        <span *ngIf="object.hasMetadata(['dc.contributor.author', 'dc.creator', 'dc.contributor.*'])"
-              class="item-list-authors">
-            <span *ngFor="let author of object.allMetadataValues(['dc.contributor.author', 'dc.creator', 'dc.contributor.*']); let last=last;">{{author}}
-                <span *ngIf="!last">; </span>
-            </span>
-        </span>
-        (<span *ngIf="object.hasMetadata('dc.publisher')" class="item-list-publisher">{{object.firstMetadataValue("dc.publisher")}}, </span><span
-        *ngIf="object.hasMetadata('dc.date.issued')" class="item-list-date">{{object.firstMetadataValue("dc.date.issued")}}</span>)
-      </span>
-    </ds-truncatable-part>
-    <ds-truncatable-part [id]="object.id" [minLines]="3">
-      <div *ngIf="object.hasMetadata('dc.description.abstract')" class="item-list-abstract">
-        {{object.firstMetadataValue("dc.description.abstract")}}
-      </div>
-    </ds-truncatable-part>
-  </div>
-</ds-truncatable>
+<ds-item-type-switcher [object]="object" [viewMode]="viewMode"></ds-item-type-switcher>
diff --git a/src/app/shared/object-list/item-list-element/item-list-element.component.spec.ts b/src/app/shared/object-list/item-list-element/item-list-element.component.spec.ts
index 392d81bee440b95c7ee04b25ad1230177dae8a9b..11fdae7e6dd3957e891012767fec1cf02a312050 100644
--- a/src/app/shared/object-list/item-list-element/item-list-element.component.spec.ts
+++ b/src/app/shared/object-list/item-list-element/item-list-element.component.spec.ts
@@ -1,114 +1,46 @@
-import { ItemListElementComponent } from './item-list-element.component';
 import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
-import { By } from '@angular/platform-browser';
-import { TruncatePipe } from '../../utils/truncate.pipe';
+import { ItemListElementComponent } from './item-list-element.component';
 import { Item } from '../../../core/shared/item.model';
+import { RemoteData } from '../../../core/data/remote-data';
+import { PaginatedList } from '../../../core/data/paginated-list';
+import { PageInfo } from '../../../core/shared/page-info.model';
+import { By } from '@angular/platform-browser';
+import { createRelationshipsObservable } from '../../../+item-page/simple/item-types/shared/item.component.spec';
 import { of as observableOf } from 'rxjs';
 
-let itemListElementComponent: ItemListElementComponent;
-let fixture: ComponentFixture<ItemListElementComponent>;
-
-const mockItemWithAuthorAndDate: Item = Object.assign(new Item(), {
-  bitstreams: observableOf({}),
-  metadata: {
-    'dc.contributor.author': [
-      {
-        language: 'en_US',
-        value: 'Smith, Donald'
-      }
-    ],
-    'dc.date.issued': [
-      {
-        language: null,
-        value: '2015-06-26'
-      }
-    ]
-  }
-});
-const mockItemWithoutAuthorAndDate: Item = Object.assign(new Item(), {
-  bitstreams: observableOf({}),
-  metadata: {
-    'dc.title': [
-      {
-        language: 'en_US',
-        value: 'This is just another title'
-      }
-    ],
-    'dc.type': [
-      {
-        language: null,
-        value: 'Article'
-      }
-    ]
-  }
+const mockItem: Item = Object.assign(new Item(), {
+  bitstreams: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))),
+  metadata: [],
+  relationships: createRelationshipsObservable()
 });
 
 describe('ItemListElementComponent', () => {
+  let comp: ItemListElementComponent;
+  let fixture: ComponentFixture<ItemListElementComponent>;
+
   beforeEach(async(() => {
     TestBed.configureTestingModule({
-      declarations: [ ItemListElementComponent , TruncatePipe],
+      imports: [],
+      declarations: [ItemListElementComponent],
       providers: [
-        { provide: 'objectElementProvider', useValue: {mockItemWithAuthorAndDate}}
+        { provide: 'objectElementProvider', useValue: mockItem }
       ],
-
-      schemas: [ NO_ERRORS_SCHEMA ]
+      schemas: [NO_ERRORS_SCHEMA]
     }).overrideComponent(ItemListElementComponent, {
-      set: { changeDetection: ChangeDetectionStrategy.Default }
+      set: {changeDetection: ChangeDetectionStrategy.Default}
     }).compileComponents();
   }));
 
   beforeEach(async(() => {
     fixture = TestBed.createComponent(ItemListElementComponent);
-    itemListElementComponent = fixture.componentInstance;
-
+    comp = fixture.componentInstance;
+    fixture.detectChanges();
   }));
 
-  describe('When the item has an author', () => {
-    beforeEach(() => {
-      itemListElementComponent.object = mockItemWithAuthorAndDate;
-      fixture.detectChanges();
-    });
-
-    it('should show the author paragraph', () => {
-      const itemAuthorField = fixture.debugElement.query(By.css('span.item-list-authors'));
-      expect(itemAuthorField).not.toBeNull();
-    });
+  it('should call an item-type-switcher component and pass the item', () => {
+    const itemTypeSwitcher = fixture.debugElement.query(By.css('ds-item-type-switcher')).componentInstance;
+    expect(itemTypeSwitcher.object).toBe(mockItem);
   });
 
-  describe('When the item has no author', () => {
-    beforeEach(() => {
-      itemListElementComponent.object = mockItemWithoutAuthorAndDate;
-      fixture.detectChanges();
-    });
-
-    it('should not show the author paragraph', () => {
-      const itemAuthorField = fixture.debugElement.query(By.css('span.item-list-authors'));
-      expect(itemAuthorField).toBeNull();
-    });
-  });
-
-  describe('When the item has an issuedate', () => {
-    beforeEach(() => {
-      itemListElementComponent.object = mockItemWithAuthorAndDate;
-      fixture.detectChanges();
-    });
-
-    it('should show the issuedate span', () => {
-      const itemAuthorField = fixture.debugElement.query(By.css('span.item-list-date'));
-      expect(itemAuthorField).not.toBeNull();
-    });
-  });
-
-  describe('When the item has no issuedate', () => {
-    beforeEach(() => {
-      itemListElementComponent.object = mockItemWithoutAuthorAndDate;
-      fixture.detectChanges();
-    });
-
-    it('should not show the issuedate span', () => {
-      const dateField = fixture.debugElement.query(By.css('span.item-list-date'));
-      expect(dateField).toBeNull();
-    });
-  });
 });
diff --git a/src/app/shared/object-list/item-list-element/item-list-element.component.ts b/src/app/shared/object-list/item-list-element/item-list-element.component.ts
index bc77b513fbc7fb92d5bfb43116a3a2fef652bb1f..67a6256d438f045488c6dffcb45284619e296665 100644
--- a/src/app/shared/object-list/item-list-element/item-list-element.component.ts
+++ b/src/app/shared/object-list/item-list-element/item-list-element.component.ts
@@ -1,9 +1,10 @@
 import { Component } from '@angular/core';
 
 import { Item } from '../../../core/shared/item.model';
-import { AbstractListableElementComponent } from '../../object-collection/shared/object-collection-element/abstract-listable-element.component';
 import { renderElementsFor } from '../../object-collection/shared/dso-element-decorator';
-import { ViewMode } from '../../../core/shared/view-mode.model';
+import { AbstractListableElementComponent } from '../../object-collection/shared/object-collection-element/abstract-listable-element.component';
+import { SetViewMode } from '../../view-mode';
+import { ItemViewMode } from '../../items/item-type-decorator';
 
 @Component({
   selector: 'ds-item-list-element',
@@ -11,5 +12,11 @@ import { ViewMode } from '../../../core/shared/view-mode.model';
   templateUrl: './item-list-element.component.html'
 })
 
-@renderElementsFor(Item, ViewMode.List)
-export class ItemListElementComponent extends AbstractListableElementComponent<Item> {}
+/**
+ * The component used to list items depending on type
+ * Uses item-type-switcher to determine which components to use for displaying the list
+ */
+@renderElementsFor(Item, SetViewMode.List)
+export class ItemListElementComponent extends AbstractListableElementComponent<Item> {
+  viewMode = ItemViewMode.Element;
+}
diff --git a/src/app/shared/object-list/item-list-element/item-types/journal-issue/journal-issue-list-element.component.html b/src/app/shared/object-list/item-list-element/item-types/journal-issue/journal-issue-list-element.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..65a10ec1b7e328e52d52aa03bc2702c26bf8a6e8
--- /dev/null
+++ b/src/app/shared/object-list/item-list-element/item-types/journal-issue/journal-issue-list-element.component.html
@@ -0,0 +1,21 @@
+<ds-truncatable [id]="item.id">
+  <a
+    [routerLink]="['/items/' + item.id]" class="lead"
+    [innerHTML]="firstMetadataValue('dc.title')"></a>
+  <span class="text-muted">
+    <ds-truncatable-part [id]="item.id" [minLines]="1">
+            <span *ngIf="item.allMetadata(['journalvolume.identifier.volume']).length > 0"
+                  class="item-list-journal-issues">
+                    <span *ngFor="let value of allMetadataValues(['journalvolume.identifier.volume']); let last=last;">
+                        <span [innerHTML]="value"><span [innerHTML]="value"></span></span>
+                    </span>
+                <span *ngIf="item.allMetadata(['journalissue.identifier.number']).length > 0"
+                      class="item-list-journal-issue-numbers">
+                    <span *ngFor="let value of allMetadataValues(['journalissue.identifier.number']); let last=last;">
+                        <span> - </span><span [innerHTML]="value"><span [innerHTML]="value"></span></span>
+                    </span>
+                </span>
+            </span>
+        </ds-truncatable-part>
+  </span>
+</ds-truncatable>
diff --git a/src/app/shared/object-list/item-list-element/item-types/journal-issue/journal-issue-list-element.component.scss b/src/app/shared/object-list/item-list-element/item-types/journal-issue/journal-issue-list-element.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/app/shared/object-list/item-list-element/item-types/journal-issue/journal-issue-list-element.component.spec.ts b/src/app/shared/object-list/item-list-element/item-types/journal-issue/journal-issue-list-element.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..05de6c814bce9d83e3b18cab6a24960a7126f196
--- /dev/null
+++ b/src/app/shared/object-list/item-list-element/item-types/journal-issue/journal-issue-list-element.component.spec.ts
@@ -0,0 +1,117 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
+import { By } from '@angular/platform-browser';
+import { Item } from '../../../../../core/shared/item.model';
+import { TruncatePipe } from '../../../../utils/truncate.pipe';
+import { TruncatableService } from '../../../../truncatable/truncatable.service';
+import { ITEM } from '../../../../items/switcher/item-type-switcher.component';
+import { JournalIssueListElementComponent } from './journal-issue-list-element.component';
+import { of as observableOf } from 'rxjs';
+
+let journalIssueListElementComponent: JournalIssueListElementComponent;
+let fixture: ComponentFixture<JournalIssueListElementComponent>;
+
+const mockItemWithMetadata: Item = Object.assign(new Item(), {
+  bitstreams: observableOf({}),
+  metadata: {
+    'dc.title': [
+      {
+        language: 'en_US',
+        value: 'This is just another title'
+      }
+    ],
+    'journalvolume.identifier.volume': [
+      {
+        language: 'en_US',
+        value: '1234'
+      }
+    ],
+    'journalissue.identifier.number': [
+      {
+        language: 'en_US',
+        value: '5678'
+      }
+    ]
+  }
+});
+const mockItemWithoutMetadata: Item = Object.assign(new Item(), {
+  bitstreams: observableOf({}),
+  metadata: {
+    'dc.title': [
+      {
+        language: 'en_US',
+        value: 'This is just another title'
+      }
+    ]
+  }
+});
+
+describe('JournalIssueListElementComponent', () => {
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ JournalIssueListElementComponent , TruncatePipe],
+      providers: [
+        { provide: ITEM, useValue: mockItemWithMetadata},
+        { provide: TruncatableService, useValue: {} }
+      ],
+
+      schemas: [ NO_ERRORS_SCHEMA ]
+    }).overrideComponent(JournalIssueListElementComponent, {
+      set: { changeDetection: ChangeDetectionStrategy.Default }
+    }).compileComponents();
+  }));
+
+  beforeEach(async(() => {
+    fixture = TestBed.createComponent(JournalIssueListElementComponent);
+    journalIssueListElementComponent = fixture.componentInstance;
+
+  }));
+
+  describe('When the item has a journal identifier', () => {
+    beforeEach(() => {
+      journalIssueListElementComponent.item = mockItemWithMetadata;
+      fixture.detectChanges();
+    });
+
+    it('should show the journal issues span', () => {
+      const journalIdentifierField = fixture.debugElement.query(By.css('span.item-list-journal-issues'));
+      expect(journalIdentifierField).not.toBeNull();
+    });
+  });
+
+  describe('When the item has no journal identifier', () => {
+    beforeEach(() => {
+      journalIssueListElementComponent.item = mockItemWithoutMetadata;
+      fixture.detectChanges();
+    });
+
+    it('should not show the journal issues span', () => {
+      const journalIdentifierField = fixture.debugElement.query(By.css('span.item-list-journal-issues'));
+      expect(journalIdentifierField).toBeNull();
+    });
+  });
+
+  describe('When the item has a journal number', () => {
+    beforeEach(() => {
+      journalIssueListElementComponent.item = mockItemWithMetadata;
+      fixture.detectChanges();
+    });
+
+    it('should show the journal issue numbers span', () => {
+      const journalNumberField = fixture.debugElement.query(By.css('span.item-list-journal-issue-numbers'));
+      expect(journalNumberField).not.toBeNull();
+    });
+  });
+
+  describe('When the item has no journal number', () => {
+    beforeEach(() => {
+      journalIssueListElementComponent.item = mockItemWithoutMetadata;
+      fixture.detectChanges();
+    });
+
+    it('should not show the journal issue numbers span', () => {
+      const journalNumberField = fixture.debugElement.query(By.css('span.item-list-journal-issue-numbers'));
+      expect(journalNumberField).toBeNull();
+    });
+  });
+});
diff --git a/src/app/shared/object-list/item-list-element/item-types/journal-issue/journal-issue-list-element.component.ts b/src/app/shared/object-list/item-list-element/item-types/journal-issue/journal-issue-list-element.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..92a8dd99309b50e631cd0d933dcb6e5dd35259d8
--- /dev/null
+++ b/src/app/shared/object-list/item-list-element/item-types/journal-issue/journal-issue-list-element.component.ts
@@ -0,0 +1,15 @@
+import { Component } from '@angular/core';
+import { ItemViewMode, rendersItemType } from '../../../../items/item-type-decorator';
+import { TypedItemSearchResultListElementComponent } from '../typed-item-search-result-list-element.component';
+
+@rendersItemType('JournalIssue', ItemViewMode.Element)
+@Component({
+  selector: 'ds-journal-issue-list-element',
+  styleUrls: ['./journal-issue-list-element.component.scss'],
+  templateUrl: './journal-issue-list-element.component.html'
+})
+/**
+ * The component for displaying a list element for an item of the type Journal Issue
+ */
+export class JournalIssueListElementComponent extends TypedItemSearchResultListElementComponent {
+}
diff --git a/src/app/shared/object-list/item-list-element/item-types/journal-volume/journal-volume-list-element.component.html b/src/app/shared/object-list/item-list-element/item-types/journal-volume/journal-volume-list-element.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..7d7f0cf731f6b5c2bf1b5b084f3ff6490a5c7773
--- /dev/null
+++ b/src/app/shared/object-list/item-list-element/item-types/journal-volume/journal-volume-list-element.component.html
@@ -0,0 +1,21 @@
+<ds-truncatable [id]="item.id">
+  <a
+    [routerLink]="['/items/' + item.id]" class="lead"
+    [innerHTML]="firstMetadataValue('dc.title')"></a>
+  <span class="text-muted">
+    <ds-truncatable-part [id]="item.id" [minLines]="1">
+            <span *ngIf="item.allMetadata(['journal.title']).length > 0"
+                  class="item-list-journal-volumes">
+                    <span *ngFor="let value of allMetadataValues(['journal.title']); let last=last;">
+                        <span [innerHTML]="value"><span [innerHTML]="value"></span></span>
+                    </span>
+            </span>
+            <span *ngIf="item.allMetadata(['journalvolume.identifier.volume']).length > 0"
+                  class="item-list-journal-volume-identifiers">
+                    <span *ngFor="let value of allMetadataValues(['journalvolume.identifier.volume']); let last=last;">
+                        <span> (</span><span [innerHTML]="value"><span [innerHTML]="value"></span></span><span>)</span>
+                    </span>
+            </span>
+        </ds-truncatable-part>
+  </span>
+</ds-truncatable>
diff --git a/src/app/shared/object-list/item-list-element/item-types/journal-volume/journal-volume-list-element.component.scss b/src/app/shared/object-list/item-list-element/item-types/journal-volume/journal-volume-list-element.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/app/shared/object-list/item-list-element/item-types/journal-volume/journal-volume-list-element.component.spec.ts b/src/app/shared/object-list/item-list-element/item-types/journal-volume/journal-volume-list-element.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4cdfb0d732fa3e004e11a5dbed9508f796ecb542
--- /dev/null
+++ b/src/app/shared/object-list/item-list-element/item-types/journal-volume/journal-volume-list-element.component.spec.ts
@@ -0,0 +1,117 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
+import { By } from '@angular/platform-browser';
+import { Item } from '../../../../../core/shared/item.model';
+import { TruncatePipe } from '../../../../utils/truncate.pipe';
+import { TruncatableService } from '../../../../truncatable/truncatable.service';
+import { ITEM } from '../../../../items/switcher/item-type-switcher.component';
+import { JournalVolumeListElementComponent } from './journal-volume-list-element.component';
+import { of as observableOf } from 'rxjs';
+
+let journalVolumeListElementComponent: JournalVolumeListElementComponent;
+let fixture: ComponentFixture<JournalVolumeListElementComponent>;
+
+const mockItemWithMetadata: Item = Object.assign(new Item(), {
+  bitstreams: observableOf({}),
+  metadata: {
+    'dc.title': [
+      {
+        language: 'en_US',
+        value: 'This is just another title'
+      }
+    ],
+    'journal.title': [
+      {
+        language: 'en_US',
+        value: 'This is just another journal title'
+      }
+    ],
+    'journalvolume.identifier.volume': [
+      {
+        language: 'en_US',
+        value: '1234'
+      }
+    ]
+  }
+});
+const mockItemWithoutMetadata: Item = Object.assign(new Item(), {
+  bitstreams: observableOf({}),
+  metadata: {
+    'dc.title': [
+      {
+        language: 'en_US',
+        value: 'This is just another title'
+      }
+    ]
+  }
+});
+
+describe('JournalVolumeListElementComponent', () => {
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ JournalVolumeListElementComponent , TruncatePipe],
+      providers: [
+        { provide: ITEM, useValue: mockItemWithMetadata},
+        { provide: TruncatableService, useValue: {} }
+      ],
+
+      schemas: [ NO_ERRORS_SCHEMA ]
+    }).overrideComponent(JournalVolumeListElementComponent, {
+      set: { changeDetection: ChangeDetectionStrategy.Default }
+    }).compileComponents();
+  }));
+
+  beforeEach(async(() => {
+    fixture = TestBed.createComponent(JournalVolumeListElementComponent);
+    journalVolumeListElementComponent = fixture.componentInstance;
+
+  }));
+
+  describe('When the item has a journal title', () => {
+    beforeEach(() => {
+      journalVolumeListElementComponent.item = mockItemWithMetadata;
+      fixture.detectChanges();
+    });
+
+    it('should show the journal title span', () => {
+      const journalTitleField = fixture.debugElement.query(By.css('span.item-list-journal-volumes'));
+      expect(journalTitleField).not.toBeNull();
+    });
+  });
+
+  describe('When the item has no journal title', () => {
+    beforeEach(() => {
+      journalVolumeListElementComponent.item = mockItemWithoutMetadata;
+      fixture.detectChanges();
+    });
+
+    it('should not show the journal title span', () => {
+      const journalTitleField = fixture.debugElement.query(By.css('span.item-list-journal-volumes'));
+      expect(journalTitleField).toBeNull();
+    });
+  });
+
+  describe('When the item has a journal identifier', () => {
+    beforeEach(() => {
+      journalVolumeListElementComponent.item = mockItemWithMetadata;
+      fixture.detectChanges();
+    });
+
+    it('should show the journal identifiers span', () => {
+      const journalIdentifierField = fixture.debugElement.query(By.css('span.item-list-journal-volume-identifiers'));
+      expect(journalIdentifierField).not.toBeNull();
+    });
+  });
+
+  describe('When the item has no journal identifier', () => {
+    beforeEach(() => {
+      journalVolumeListElementComponent.item = mockItemWithoutMetadata;
+      fixture.detectChanges();
+    });
+
+    it('should not show the journal identifiers span', () => {
+      const journalIdentifierField = fixture.debugElement.query(By.css('span.item-list-journal-volume-identifiers'));
+      expect(journalIdentifierField).toBeNull();
+    });
+  });
+});
diff --git a/src/app/shared/object-list/item-list-element/item-types/journal-volume/journal-volume-list-element.component.ts b/src/app/shared/object-list/item-list-element/item-types/journal-volume/journal-volume-list-element.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..01acf3ca344a602b76660711ab12ad28460e5664
--- /dev/null
+++ b/src/app/shared/object-list/item-list-element/item-types/journal-volume/journal-volume-list-element.component.ts
@@ -0,0 +1,15 @@
+import { Component } from '@angular/core';
+import { ItemViewMode, rendersItemType } from '../../../../items/item-type-decorator';
+import { TypedItemSearchResultListElementComponent } from '../typed-item-search-result-list-element.component';
+
+@rendersItemType('JournalVolume', ItemViewMode.Element)
+@Component({
+  selector: 'ds-journal-volume-list-element',
+  styleUrls: ['./journal-volume-list-element.component.scss'],
+  templateUrl: './journal-volume-list-element.component.html'
+})
+/**
+ * The component for displaying a list element for an item of the type Journal Volume
+ */
+export class JournalVolumeListElementComponent extends TypedItemSearchResultListElementComponent {
+}
diff --git a/src/app/shared/object-list/item-list-element/item-types/journal/journal-list-element.component.html b/src/app/shared/object-list/item-list-element/item-types/journal/journal-list-element.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..32c8074503da60b966766a6631175c86d4c09f3e
--- /dev/null
+++ b/src/app/shared/object-list/item-list-element/item-types/journal/journal-list-element.component.html
@@ -0,0 +1,15 @@
+<ds-truncatable [id]="item.id">
+  <a
+    [routerLink]="['/items/' + item.id]" class="lead"
+    [innerHTML]="firstMetadataValue('dc.title')"></a>
+  <span class="text-muted">
+    <ds-truncatable-part [id]="item.id" [minLines]="1">
+            <span *ngIf="item.allMetadata(['journal.identifier.issn']).length > 0"
+                  class="item-list-journals">
+                    <span *ngFor="let value of allMetadataValues(['journal.identifier.issn']); let last=last;">
+                        <span [innerHTML]="value"><span [innerHTML]="value"></span></span>
+                    </span>
+            </span>
+        </ds-truncatable-part>
+  </span>
+</ds-truncatable>
diff --git a/src/app/shared/object-list/item-list-element/item-types/journal/journal-list-element.component.scss b/src/app/shared/object-list/item-list-element/item-types/journal/journal-list-element.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/app/shared/object-list/item-list-element/item-types/journal/journal-list-element.component.spec.ts b/src/app/shared/object-list/item-list-element/item-types/journal/journal-list-element.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fc7ef06fa044a0c993531df2a4e9d1689abe67bd
--- /dev/null
+++ b/src/app/shared/object-list/item-list-element/item-types/journal/journal-list-element.component.spec.ts
@@ -0,0 +1,87 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
+import { By } from '@angular/platform-browser';
+import { Item } from '../../../../../core/shared/item.model';
+import { TruncatePipe } from '../../../../utils/truncate.pipe';
+import { TruncatableService } from '../../../../truncatable/truncatable.service';
+import { ITEM } from '../../../../items/switcher/item-type-switcher.component';
+import { JournalListElementComponent } from './journal-list-element.component';
+import { of as observableOf } from 'rxjs';
+
+let journalListElementComponent: JournalListElementComponent;
+let fixture: ComponentFixture<JournalListElementComponent>;
+
+const mockItemWithMetadata: Item = Object.assign(new Item(), {
+  bitstreams: observableOf({}),
+  metadata: {
+    'dc.title': [
+      {
+        language: 'en_US',
+        value: 'This is just another title'
+      }
+    ],
+    'journal.identifier.issn': [
+      {
+        language: 'en_US',
+        value: '1234'
+      }
+    ]
+  }
+});
+const mockItemWithoutMetadata: Item = Object.assign(new Item(), {
+  bitstreams: observableOf({}),
+  metadata: {
+    'dc.title': [
+      {
+        language: 'en_US',
+        value: 'This is just another title'
+      }
+    ]
+  }
+});
+
+describe('JournalListElementComponent', () => {
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ JournalListElementComponent , TruncatePipe],
+      providers: [
+        { provide: ITEM, useValue: mockItemWithMetadata},
+        { provide: TruncatableService, useValue: {} }
+      ],
+
+      schemas: [ NO_ERRORS_SCHEMA ]
+    }).overrideComponent(JournalListElementComponent, {
+      set: { changeDetection: ChangeDetectionStrategy.Default }
+    }).compileComponents();
+  }));
+
+  beforeEach(async(() => {
+    fixture = TestBed.createComponent(JournalListElementComponent);
+    journalListElementComponent = fixture.componentInstance;
+
+  }));
+
+  describe('When the item has an issn', () => {
+    beforeEach(() => {
+      journalListElementComponent.item = mockItemWithMetadata;
+      fixture.detectChanges();
+    });
+
+    it('should show the journals span', () => {
+      const issnField = fixture.debugElement.query(By.css('span.item-list-journals'));
+      expect(issnField).not.toBeNull();
+    });
+  });
+
+  describe('When the item has no issn', () => {
+    beforeEach(() => {
+      journalListElementComponent.item = mockItemWithoutMetadata;
+      fixture.detectChanges();
+    });
+
+    it('should not show the journals span', () => {
+      const issnField = fixture.debugElement.query(By.css('span.item-list-journals'));
+      expect(issnField).toBeNull();
+    });
+  });
+});
diff --git a/src/app/shared/object-list/item-list-element/item-types/journal/journal-list-element.component.ts b/src/app/shared/object-list/item-list-element/item-types/journal/journal-list-element.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a40891b4526958d84c021b2dc2484cc44b1f20ad
--- /dev/null
+++ b/src/app/shared/object-list/item-list-element/item-types/journal/journal-list-element.component.ts
@@ -0,0 +1,15 @@
+import { Component } from '@angular/core';
+import { ItemViewMode, rendersItemType } from '../../../../items/item-type-decorator';
+import { TypedItemSearchResultListElementComponent } from '../typed-item-search-result-list-element.component';
+
+@rendersItemType('Journal', ItemViewMode.Element)
+@Component({
+  selector: 'ds-journal-list-element',
+  styleUrls: ['./journal-list-element.component.scss'],
+  templateUrl: './journal-list-element.component.html'
+})
+/**
+ * The component for displaying a list element for an item of the type Journal
+ */
+export class JournalListElementComponent extends TypedItemSearchResultListElementComponent {
+}
diff --git a/src/app/shared/object-list/item-list-element/item-types/orgunit/orgunit-list-element.component.html b/src/app/shared/object-list/item-list-element/item-types/orgunit/orgunit-list-element.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..a809c0f65585a5c57d168ffd13dd34d8a1251fa3
--- /dev/null
+++ b/src/app/shared/object-list/item-list-element/item-types/orgunit/orgunit-list-element.component.html
@@ -0,0 +1,16 @@
+<ds-truncatable [id]="item.id">
+  <a
+    [routerLink]="['/items/' + item.id]" class="lead"
+    [innerHTML]="firstMetadataValue('orgunit.identifier.name')"></a>
+  <span class="text-muted">
+    <ds-truncatable-part [id]="item.id" [minLines]="3">
+            <span *ngIf="item.allMetadata(['orgunit.identifier.description']).length > 0"
+                  class="item-list-orgunit-description">
+                <ds-truncatable-part [id]="item.id" [minLines]="3"><span
+                  [innerHTML]="firstMetadataValue('orgunit.identifier.description')"></span>
+                </ds-truncatable-part>
+            </span>
+        </ds-truncatable-part>
+  </span>
+</ds-truncatable>
+
diff --git a/src/app/shared/object-list/item-list-element/item-types/orgunit/orgunit-list-element.component.scss b/src/app/shared/object-list/item-list-element/item-types/orgunit/orgunit-list-element.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..5ab410dcb0f75817ae9ea534160253c7039e8ae0
--- /dev/null
+++ b/src/app/shared/object-list/item-list-element/item-types/orgunit/orgunit-list-element.component.scss
@@ -0,0 +1 @@
+@import '../../../../../../styles/variables';
diff --git a/src/app/shared/object-list/item-list-element/item-types/orgunit/orgunit-list-element.component.spec.ts b/src/app/shared/object-list/item-list-element/item-types/orgunit/orgunit-list-element.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8e74c389e9a0a29365b30f2533702e57450bd653
--- /dev/null
+++ b/src/app/shared/object-list/item-list-element/item-types/orgunit/orgunit-list-element.component.spec.ts
@@ -0,0 +1,87 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
+import { By } from '@angular/platform-browser';
+import { Item } from '../../../../../core/shared/item.model';
+import { TruncatePipe } from '../../../../utils/truncate.pipe';
+import { TruncatableService } from '../../../../truncatable/truncatable.service';
+import { ITEM } from '../../../../items/switcher/item-type-switcher.component';
+import { OrgUnitListElementComponent } from './orgunit-list-element.component';
+import { of as observableOf } from 'rxjs';
+
+let orgUnitListElementComponent: OrgUnitListElementComponent;
+let fixture: ComponentFixture<OrgUnitListElementComponent>;
+
+const mockItemWithMetadata: Item = Object.assign(new Item(), {
+  bitstreams: observableOf({}),
+  metadata: {
+    'dc.title': [
+      {
+        language: 'en_US',
+        value: 'This is just another title'
+      }
+    ],
+    'orgunit.identifier.description': [
+      {
+        language: 'en_US',
+        value: 'A description about the OrgUnit'
+      }
+    ]
+  }
+});
+const mockItemWithoutMetadata: Item = Object.assign(new Item(), {
+  bitstreams: observableOf({}),
+  metadata: {
+    'dc.title': [
+      {
+        language: 'en_US',
+        value: 'This is just another title'
+      }
+    ]
+  }
+});
+
+describe('OrgUnitListElementComponent', () => {
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ OrgUnitListElementComponent , TruncatePipe],
+      providers: [
+        { provide: ITEM, useValue: mockItemWithMetadata},
+        { provide: TruncatableService, useValue: {} }
+      ],
+
+      schemas: [ NO_ERRORS_SCHEMA ]
+    }).overrideComponent(OrgUnitListElementComponent, {
+      set: { changeDetection: ChangeDetectionStrategy.Default }
+    }).compileComponents();
+  }));
+
+  beforeEach(async(() => {
+    fixture = TestBed.createComponent(OrgUnitListElementComponent);
+    orgUnitListElementComponent = fixture.componentInstance;
+
+  }));
+
+  describe('When the item has an orgunit description', () => {
+    beforeEach(() => {
+      orgUnitListElementComponent.item = mockItemWithMetadata;
+      fixture.detectChanges();
+    });
+
+    it('should show the description span', () => {
+      const orgunitDescriptionField = fixture.debugElement.query(By.css('span.item-list-orgunit-description'));
+      expect(orgunitDescriptionField).not.toBeNull();
+    });
+  });
+
+  describe('When the item has no orgunit description', () => {
+    beforeEach(() => {
+      orgUnitListElementComponent.item = mockItemWithoutMetadata;
+      fixture.detectChanges();
+    });
+
+    it('should not show the description span', () => {
+      const orgunitDescriptionField = fixture.debugElement.query(By.css('span.item-list-orgunit-description'));
+      expect(orgunitDescriptionField).toBeNull();
+    });
+  });
+});
diff --git a/src/app/shared/object-list/item-list-element/item-types/orgunit/orgunit-list-element.component.ts b/src/app/shared/object-list/item-list-element/item-types/orgunit/orgunit-list-element.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f047aac586d1c590f660ad7e1594e153c8975bda
--- /dev/null
+++ b/src/app/shared/object-list/item-list-element/item-types/orgunit/orgunit-list-element.component.ts
@@ -0,0 +1,15 @@
+import { Component } from '@angular/core';
+import { ItemViewMode, rendersItemType } from '../../../../items/item-type-decorator';
+import { TypedItemSearchResultListElementComponent } from '../typed-item-search-result-list-element.component';
+
+@rendersItemType('OrgUnit', ItemViewMode.Element)
+@Component({
+  selector: 'ds-orgunit-list-element',
+  styleUrls: ['./orgunit-list-element.component.scss'],
+  templateUrl: './orgunit-list-element.component.html'
+})
+/**
+ * The component for displaying a list element for an item of the type Organisation Unit
+ */
+export class OrgUnitListElementComponent extends TypedItemSearchResultListElementComponent {
+}
diff --git a/src/app/shared/object-list/item-list-element/item-types/orgunit/orgunit-metadata-list-element.component.html b/src/app/shared/object-list/item-list-element/item-types/orgunit/orgunit-metadata-list-element.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..463770c0ae2516fb73415b996502b914620fb1eb
--- /dev/null
+++ b/src/app/shared/object-list/item-list-element/item-types/orgunit/orgunit-metadata-list-element.component.html
@@ -0,0 +1,13 @@
+<ng-template #descTemplate>
+  <span class="text-muted">
+      <span *ngIf="item.allMetadata(['orgunit.identifier.description']).length > 0"
+            class="item-list-job-title">
+        <span [innerHTML]="firstMetadataValue(['orgunit.identifier.description'])"></span>
+      </span>
+  </span>
+</ng-template>
+<ds-truncatable [id]="item.id">
+  <a [routerLink]="['/items/' + item.id]"
+     [innerHTML]="firstMetadataValue('orgunit.identifier.name')"
+     [tooltip]="descTemplate"></a>
+</ds-truncatable>
diff --git a/src/app/shared/object-list/item-list-element/item-types/orgunit/orgunit-metadata-list-element.component.ts b/src/app/shared/object-list/item-list-element/item-types/orgunit/orgunit-metadata-list-element.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..42c6c6f6a290122f32c86df5d497112d0b32c251
--- /dev/null
+++ b/src/app/shared/object-list/item-list-element/item-types/orgunit/orgunit-metadata-list-element.component.ts
@@ -0,0 +1,15 @@
+import { ItemViewMode, rendersItemType } from '../../../../items/item-type-decorator';
+import { Component } from '@angular/core';
+import { TypedItemSearchResultListElementComponent } from '../typed-item-search-result-list-element.component';
+import { MetadataRepresentationType } from '../../../../../core/shared/metadata-representation/metadata-representation.model';
+
+@rendersItemType('OrgUnit', ItemViewMode.Element, MetadataRepresentationType.Item)
+@Component({
+  selector: 'ds-orgunit-metadata-list-element',
+  templateUrl: './orgunit-metadata-list-element.component.html'
+})
+/**
+ * The component for displaying a list element for an item of the type OrgUnit
+ */
+export class OrgUnitMetadataListElementComponent extends TypedItemSearchResultListElementComponent {
+}
diff --git a/src/app/shared/object-list/item-list-element/item-types/person/person-list-element.component.html b/src/app/shared/object-list/item-list-element/item-types/person/person-list-element.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..52b69453cea78ea5974423b1a4c8200061186533
--- /dev/null
+++ b/src/app/shared/object-list/item-list-element/item-types/person/person-list-element.component.html
@@ -0,0 +1,15 @@
+<ds-truncatable [id]="item.id">
+  <a
+    [routerLink]="['/items/' + item.id]" class="lead"
+    [innerHTML]="firstMetadataValue('dc.contributor.author')"></a>
+  <span class="text-muted">
+    <ds-truncatable-part [id]="item.id" [minLines]="1">
+            <span *ngIf="item.allMetadata(['person.identifier.jobtitle']).length > 0"
+                  class="item-list-job-title">
+                    <span *ngFor="let value of allMetadataValues(['person.identifier.jobtitle']); let last=last;">
+                        <span [innerHTML]="value"><span [innerHTML]="value"></span></span>
+                    </span>
+            </span>
+        </ds-truncatable-part>
+  </span>
+</ds-truncatable>
diff --git a/src/app/shared/object-list/item-list-element/item-types/person/person-list-element.component.scss b/src/app/shared/object-list/item-list-element/item-types/person/person-list-element.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..5ab410dcb0f75817ae9ea534160253c7039e8ae0
--- /dev/null
+++ b/src/app/shared/object-list/item-list-element/item-types/person/person-list-element.component.scss
@@ -0,0 +1 @@
+@import '../../../../../../styles/variables';
diff --git a/src/app/shared/object-list/item-list-element/item-types/person/person-list-element.component.spec.ts b/src/app/shared/object-list/item-list-element/item-types/person/person-list-element.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..67dc4e92ac551357acb6d6ecd7faa4ff346a5346
--- /dev/null
+++ b/src/app/shared/object-list/item-list-element/item-types/person/person-list-element.component.spec.ts
@@ -0,0 +1,87 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
+import { By } from '@angular/platform-browser';
+import { Item } from '../../../../../core/shared/item.model';
+import { TruncatePipe } from '../../../../utils/truncate.pipe';
+import { TruncatableService } from '../../../../truncatable/truncatable.service';
+import { ITEM } from '../../../../items/switcher/item-type-switcher.component';
+import { PersonListElementComponent } from './person-list-element.component';
+import { of as observableOf } from 'rxjs';
+
+let personListElementComponent: PersonListElementComponent;
+let fixture: ComponentFixture<PersonListElementComponent>;
+
+const mockItemWithMetadata: Item = Object.assign(new Item(), {
+  bitstreams: observableOf({}),
+  metadata: {
+    'dc.title': [
+      {
+        language: 'en_US',
+        value: 'This is just another title'
+      }
+    ],
+    'person.identifier.jobtitle': [
+      {
+        language: 'en_US',
+        value: 'Developer'
+      }
+    ]
+  }
+});
+const mockItemWithoutMetadata: Item = Object.assign(new Item(), {
+  bitstreams: observableOf({}),
+  metadata: {
+    'dc.title': [
+      {
+        language: 'en_US',
+        value: 'This is just another title'
+      }
+    ]
+  }
+});
+
+describe('PersonListElementComponent', () => {
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ PersonListElementComponent , TruncatePipe],
+      providers: [
+        { provide: ITEM, useValue: mockItemWithMetadata},
+        { provide: TruncatableService, useValue: {} }
+      ],
+
+      schemas: [ NO_ERRORS_SCHEMA ]
+    }).overrideComponent(PersonListElementComponent, {
+      set: { changeDetection: ChangeDetectionStrategy.Default }
+    }).compileComponents();
+  }));
+
+  beforeEach(async(() => {
+    fixture = TestBed.createComponent(PersonListElementComponent);
+    personListElementComponent = fixture.componentInstance;
+
+  }));
+
+  describe('When the item has a job title', () => {
+    beforeEach(() => {
+      personListElementComponent.item = mockItemWithMetadata;
+      fixture.detectChanges();
+    });
+
+    it('should show the job title span', () => {
+      const jobTitleField = fixture.debugElement.query(By.css('span.item-list-job-title'));
+      expect(jobTitleField).not.toBeNull();
+    });
+  });
+
+  describe('When the item has no job title', () => {
+    beforeEach(() => {
+      personListElementComponent.item = mockItemWithoutMetadata;
+      fixture.detectChanges();
+    });
+
+    it('should not show the job title span', () => {
+      const jobTitleField = fixture.debugElement.query(By.css('span.item-list-job-title'));
+      expect(jobTitleField).toBeNull();
+    });
+  });
+});
diff --git a/src/app/shared/object-list/item-list-element/item-types/person/person-list-element.component.ts b/src/app/shared/object-list/item-list-element/item-types/person/person-list-element.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b3834a8aab96043d15498eef8036ea5be081e121
--- /dev/null
+++ b/src/app/shared/object-list/item-list-element/item-types/person/person-list-element.component.ts
@@ -0,0 +1,15 @@
+import { Component } from '@angular/core';
+import { ItemViewMode, rendersItemType } from '../../../../items/item-type-decorator';
+import { TypedItemSearchResultListElementComponent } from '../typed-item-search-result-list-element.component';
+
+@rendersItemType('Person', ItemViewMode.Element)
+@Component({
+  selector: 'ds-person-list-element',
+  styleUrls: ['./person-list-element.component.scss'],
+  templateUrl: './person-list-element.component.html'
+})
+/**
+ * The component for displaying a list element for an item of the type Person
+ */
+export class PersonListElementComponent extends TypedItemSearchResultListElementComponent {
+}
diff --git a/src/app/shared/object-list/item-list-element/item-types/person/person-metadata-list-element.component.html b/src/app/shared/object-list/item-list-element/item-types/person/person-metadata-list-element.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..3dfe17debc53e65a4dd438747223d47f549e0e34
--- /dev/null
+++ b/src/app/shared/object-list/item-list-element/item-types/person/person-metadata-list-element.component.html
@@ -0,0 +1,15 @@
+<ng-template #descTemplate>
+  <span class="text-muted">
+      <span *ngIf="item.allMetadata(['person.identifier.jobtitle']).length > 0"
+            class="item-list-job-title">
+              <span *ngFor="let value of allMetadataValues(['person.identifier.jobtitle']); let last=last;">
+                  <span [innerHTML]="value"><span [innerHTML]="value"></span></span>
+              </span>
+      </span>
+  </span>
+</ng-template>
+<ds-truncatable [id]="item.id">
+  <a [routerLink]="['/items/' + item.id]"
+     [innerHTML]="firstMetadataValue('dc.contributor.author')"
+     [tooltip]="descTemplate"></a>
+</ds-truncatable>
diff --git a/src/app/shared/object-list/item-list-element/item-types/person/person-metadata-list-element.component.ts b/src/app/shared/object-list/item-list-element/item-types/person/person-metadata-list-element.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..959673242de4a9d223e7f4be1445dcff653f75d3
--- /dev/null
+++ b/src/app/shared/object-list/item-list-element/item-types/person/person-metadata-list-element.component.ts
@@ -0,0 +1,15 @@
+import { ItemViewMode, rendersItemType } from '../../../../items/item-type-decorator';
+import { Component } from '@angular/core';
+import { TypedItemSearchResultListElementComponent } from '../typed-item-search-result-list-element.component';
+import { MetadataRepresentationType } from '../../../../../core/shared/metadata-representation/metadata-representation.model';
+
+@rendersItemType('Person', ItemViewMode.Element, MetadataRepresentationType.Item)
+@Component({
+  selector: 'ds-person-metadata-list-element',
+  templateUrl: './person-metadata-list-element.component.html'
+})
+/**
+ * The component for displaying a list element for an item of the type Person
+ */
+export class PersonMetadataListElementComponent extends TypedItemSearchResultListElementComponent {
+}
diff --git a/src/app/shared/object-list/item-list-element/item-types/project/project-list-element.component.html b/src/app/shared/object-list/item-list-element/item-types/project/project-list-element.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..6f0faa90ef13cc6241760130d87d7196041ce119
--- /dev/null
+++ b/src/app/shared/object-list/item-list-element/item-types/project/project-list-element.component.html
@@ -0,0 +1,16 @@
+<ds-truncatable [id]="item.id">
+  <a
+    [routerLink]="['/items/' + item.id]" class="lead"
+    [innerHTML]="firstMetadataValue('project.identifier.name')"></a>
+  <span class="text-muted">
+    <ds-truncatable-part [id]="item.id" [minLines]="1">
+            <span *ngIf="item.allMetadata(['project.identifier.status']).length > 0"
+                  class="item-list-status">
+                    <span *ngFor="let value of allMetadataValues(['project.identifier.status']); let last=last;">
+                        <span [innerHTML]="value"><span [innerHTML]="value"></span></span>
+                    </span>
+            </span>
+        </ds-truncatable-part>
+  </span>
+</ds-truncatable>
+
diff --git a/src/app/shared/object-list/item-list-element/item-types/project/project-list-element.component.scss b/src/app/shared/object-list/item-list-element/item-types/project/project-list-element.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..5ab410dcb0f75817ae9ea534160253c7039e8ae0
--- /dev/null
+++ b/src/app/shared/object-list/item-list-element/item-types/project/project-list-element.component.scss
@@ -0,0 +1 @@
+@import '../../../../../../styles/variables';
diff --git a/src/app/shared/object-list/item-list-element/item-types/project/project-list-element.component.spec.ts b/src/app/shared/object-list/item-list-element/item-types/project/project-list-element.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1dd3c42042a6957b1d2550ec9eee03e9a446fcb1
--- /dev/null
+++ b/src/app/shared/object-list/item-list-element/item-types/project/project-list-element.component.spec.ts
@@ -0,0 +1,87 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
+import { By } from '@angular/platform-browser';
+import { Item } from '../../../../../core/shared/item.model';
+import { TruncatePipe } from '../../../../utils/truncate.pipe';
+import { TruncatableService } from '../../../../truncatable/truncatable.service';
+import { ITEM } from '../../../../items/switcher/item-type-switcher.component';
+import { ProjectListElementComponent } from './project-list-element.component';
+import { of as observableOf } from 'rxjs';
+
+let projectListElementComponent: ProjectListElementComponent;
+let fixture: ComponentFixture<ProjectListElementComponent>;
+
+const mockItemWithMetadata: Item = Object.assign(new Item(), {
+  bitstreams: observableOf({}),
+  metadata: {
+    'dc.title': [
+      {
+        language: 'en_US',
+        value: 'This is just another title'
+      }
+    ],
+    'project.identifier.status': [
+      {
+        language: 'en_US',
+        value: 'A status about the project'
+      }
+    ]
+  }
+});
+const mockItemWithoutMetadata: Item = Object.assign(new Item(), {
+  bitstreams: observableOf({}),
+  metadata: {
+    'dc.title': [
+      {
+        language: 'en_US',
+        value: 'This is just another title'
+      }
+    ]
+  }
+});
+
+describe('ProjectListElementComponent', () => {
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ ProjectListElementComponent , TruncatePipe],
+      providers: [
+        { provide: ITEM, useValue: mockItemWithMetadata},
+        { provide: TruncatableService, useValue: {} }
+      ],
+
+      schemas: [ NO_ERRORS_SCHEMA ]
+    }).overrideComponent(ProjectListElementComponent, {
+      set: { changeDetection: ChangeDetectionStrategy.Default }
+    }).compileComponents();
+  }));
+
+  beforeEach(async(() => {
+    fixture = TestBed.createComponent(ProjectListElementComponent);
+    projectListElementComponent = fixture.componentInstance;
+
+  }));
+
+  describe('When the item has a status', () => {
+    beforeEach(() => {
+      projectListElementComponent.item = mockItemWithMetadata;
+      fixture.detectChanges();
+    });
+
+    it('should show the status span', () => {
+      const statusField = fixture.debugElement.query(By.css('span.item-list-status'));
+      expect(statusField).not.toBeNull();
+    });
+  });
+
+  describe('When the item has no status', () => {
+    beforeEach(() => {
+      projectListElementComponent.item = mockItemWithoutMetadata;
+      fixture.detectChanges();
+    });
+
+    it('should not show the status span', () => {
+      const statusField = fixture.debugElement.query(By.css('span.item-list-status'));
+      expect(statusField).toBeNull();
+    });
+  });
+});
diff --git a/src/app/shared/object-list/item-list-element/item-types/project/project-list-element.component.ts b/src/app/shared/object-list/item-list-element/item-types/project/project-list-element.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..86b58c94fdffe25c985ed8e7308b50a6025bfabd
--- /dev/null
+++ b/src/app/shared/object-list/item-list-element/item-types/project/project-list-element.component.ts
@@ -0,0 +1,15 @@
+import { Component } from '@angular/core';
+import { ItemViewMode, rendersItemType } from '../../../../items/item-type-decorator';
+import { TypedItemSearchResultListElementComponent } from '../typed-item-search-result-list-element.component';
+
+@rendersItemType('Project', ItemViewMode.Element)
+@Component({
+  selector: 'ds-project-list-element',
+  styleUrls: ['./project-list-element.component.scss'],
+  templateUrl: './project-list-element.component.html'
+})
+/**
+ * The component for displaying a list element for an item of the type Project
+ */
+export class ProjectListElementComponent extends TypedItemSearchResultListElementComponent {
+}
diff --git a/src/app/shared/object-list/item-list-element/item-types/publication/publication-list-element.component.html b/src/app/shared/object-list/item-list-element/item-types/publication/publication-list-element.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..aff19aec1d565f44ab5f198a31c1b83d3a4acdd7
--- /dev/null
+++ b/src/app/shared/object-list/item-list-element/item-types/publication/publication-list-element.component.html
@@ -0,0 +1,24 @@
+<ds-truncatable [id]="item.id" *ngIf="item !== undefined && item !== null">
+  <a
+    [routerLink]="['/items/' + item.id]" class="lead"
+    [innerHTML]="firstMetadataValue('dc.title')"></a>
+  <span class="text-muted">
+    <ds-truncatable-part [id]="item.id" [minLines]="1">
+            <ng-container *ngIf="item.firstMetadataValue('dc.publisher') || item.firstMetadataValue('dc.date.issued')">(<span class="item-list-publisher"
+                   [innerHTML]="firstMetadataValue('dc.publisher')">, </span><span
+      *ngIf="item.firstMetadataValue('dc.date.issued')" class="item-list-date"
+      [innerHTML]="firstMetadataValue('dc.date.issued')"></span>)</ng-container>
+            <span *ngIf="item.allMetadata(['dc.contributor.author', 'dc.creator', 'dc.contributor.*']).length > 0"
+                  class="item-list-authors">
+                    <span *ngFor="let author of allMetadataValues(['dc.contributor.author', 'dc.creator', 'dc.contributor.*']); let last=last;">
+                        <span [innerHTML]="author"><span [innerHTML]="author"></span></span>
+                    </span>
+            </span>
+        </ds-truncatable-part>
+  </span>
+  <div *ngIf="item.firstMetadataValue('dc.description.abstract')" class="item-list-abstract">
+    <ds-truncatable-part [id]="item.id" [minLines]="3"><span
+      [innerHTML]="firstMetadataValue('dc.description.abstract')"></span>
+    </ds-truncatable-part>
+  </div>
+</ds-truncatable>
diff --git a/src/app/shared/object-list/item-list-element/item-types/publication/publication-list-element.component.scss b/src/app/shared/object-list/item-list-element/item-types/publication/publication-list-element.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..5ab410dcb0f75817ae9ea534160253c7039e8ae0
--- /dev/null
+++ b/src/app/shared/object-list/item-list-element/item-types/publication/publication-list-element.component.scss
@@ -0,0 +1 @@
+@import '../../../../../../styles/variables';
diff --git a/src/app/shared/object-list/item-list-element/item-types/publication/publication-list-element.component.spec.ts b/src/app/shared/object-list/item-list-element/item-types/publication/publication-list-element.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..732fd0d4e40f3824ec19326002375286dadffd07
--- /dev/null
+++ b/src/app/shared/object-list/item-list-element/item-types/publication/publication-list-element.component.spec.ts
@@ -0,0 +1,177 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
+import { By } from '@angular/platform-browser';
+import { PublicationListElementComponent } from './publication-list-element.component';
+import { Item } from '../../../../../core/shared/item.model';
+import { TruncatePipe } from '../../../../utils/truncate.pipe';
+import { TruncatableService } from '../../../../truncatable/truncatable.service';
+import { ITEM } from '../../../../items/switcher/item-type-switcher.component';
+import { of as observableOf } from 'rxjs';
+
+let publicationListElementComponent: PublicationListElementComponent;
+let fixture: ComponentFixture<PublicationListElementComponent>;
+
+const mockItemWithMetadata: Item = Object.assign(new Item(), {
+  bitstreams: observableOf({}),
+  metadata: {
+    'dc.title': [
+      {
+        language: 'en_US',
+        value: 'This is just another title'
+      }
+    ],
+    'dc.contributor.author': [
+      {
+        language: 'en_US',
+        value: 'Smith, Donald'
+      }
+    ],
+    'dc.publisher': [
+      {
+        language: 'en_US',
+        value: 'a publisher'
+      }
+    ],
+    'dc.date.issued': [
+      {
+        language: 'en_US',
+        value: '2015-06-26'
+      }
+    ],
+    'dc.description.abstract': [
+      {
+        language: 'en_US',
+        value: 'This is the abstract'
+      }
+    ]
+  }
+});
+const mockItemWithoutMetadata: Item = Object.assign(new Item(), {
+  bitstreams: observableOf({}),
+  metadata: {
+    'dc.title': [
+      {
+        language: 'en_US',
+        value: 'This is just another title'
+      }
+    ]
+  }
+});
+
+describe('PublicationListElementComponent', () => {
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ PublicationListElementComponent , TruncatePipe],
+      providers: [
+        { provide: ITEM, useValue: mockItemWithMetadata},
+        { provide: TruncatableService, useValue: {} }
+      ],
+
+      schemas: [ NO_ERRORS_SCHEMA ]
+    }).overrideComponent(PublicationListElementComponent, {
+      set: { changeDetection: ChangeDetectionStrategy.Default }
+    }).compileComponents();
+  }));
+
+  beforeEach(async(() => {
+    fixture = TestBed.createComponent(PublicationListElementComponent);
+    publicationListElementComponent = fixture.componentInstance;
+
+  }));
+
+  describe('When the item has an author', () => {
+    beforeEach(() => {
+      publicationListElementComponent.item = mockItemWithMetadata;
+      fixture.detectChanges();
+    });
+
+    it('should show the author paragraph', () => {
+      const itemAuthorField = fixture.debugElement.query(By.css('span.item-list-authors'));
+      expect(itemAuthorField).not.toBeNull();
+    });
+  });
+
+  describe('When the item has no author', () => {
+    beforeEach(() => {
+      publicationListElementComponent.item = mockItemWithoutMetadata;
+      fixture.detectChanges();
+    });
+
+    it('should not show the author paragraph', () => {
+      const itemAuthorField = fixture.debugElement.query(By.css('span.item-list-authors'));
+      expect(itemAuthorField).toBeNull();
+    });
+  });
+
+  describe('When the item has a publisher', () => {
+    beforeEach(() => {
+      publicationListElementComponent.item = mockItemWithMetadata;
+      fixture.detectChanges();
+    });
+
+    it('should show the publisher span', () => {
+      const publisherField = fixture.debugElement.query(By.css('span.item-list-publisher'));
+      expect(publisherField).not.toBeNull();
+    });
+  });
+
+  describe('When the item has no publisher', () => {
+    beforeEach(() => {
+      publicationListElementComponent.item = mockItemWithoutMetadata;
+      fixture.detectChanges();
+    });
+
+    it('should not show the publisher span', () => {
+      const publisherField = fixture.debugElement.query(By.css('span.item-list-publisher'));
+      expect(publisherField).toBeNull();
+    });
+  });
+
+  describe('When the item has an issuedate', () => {
+    beforeEach(() => {
+      publicationListElementComponent.item = mockItemWithMetadata;
+      fixture.detectChanges();
+    });
+
+    it('should show the issuedate span', () => {
+      const dateField = fixture.debugElement.query(By.css('span.item-list-date'));
+      expect(dateField).not.toBeNull();
+    });
+  });
+
+  describe('When the item has no issuedate', () => {
+    beforeEach(() => {
+      publicationListElementComponent.item = mockItemWithoutMetadata;
+      fixture.detectChanges();
+    });
+
+    it('should not show the issuedate span', () => {
+      const dateField = fixture.debugElement.query(By.css('span.item-list-date'));
+      expect(dateField).toBeNull();
+    });
+  });
+
+  describe('When the item has an abstract', () => {
+    beforeEach(() => {
+      publicationListElementComponent.item = mockItemWithMetadata;
+      fixture.detectChanges();
+    });
+
+    it('should show the abstract span', () => {
+      const abstractField = fixture.debugElement.query(By.css('div.item-list-abstract'));
+      expect(abstractField).not.toBeNull();
+    });
+  });
+
+  describe('When the item has no abstract', () => {
+    beforeEach(() => {
+      publicationListElementComponent.item = mockItemWithoutMetadata;
+      fixture.detectChanges();
+    });
+
+    it('should not show the abstract span', () => {
+      const abstractField = fixture.debugElement.query(By.css('div.item-list-abstract'));
+      expect(abstractField).toBeNull();
+    });
+  });
+});
diff --git a/src/app/shared/object-list/item-list-element/item-types/publication/publication-list-element.component.ts b/src/app/shared/object-list/item-list-element/item-types/publication/publication-list-element.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4717ff1df20a2b8cbb79dcb5b3f96f15387aa167
--- /dev/null
+++ b/src/app/shared/object-list/item-list-element/item-types/publication/publication-list-element.component.ts
@@ -0,0 +1,16 @@
+import { Component } from '@angular/core';
+import { DEFAULT_ITEM_TYPE, ItemViewMode, rendersItemType } from '../../../../items/item-type-decorator';
+import { TypedItemSearchResultListElementComponent } from '../typed-item-search-result-list-element.component';
+
+@rendersItemType('Publication', ItemViewMode.Element)
+@rendersItemType(DEFAULT_ITEM_TYPE, ItemViewMode.Element)
+@Component({
+  selector: 'ds-publication-list-element',
+  styleUrls: ['./publication-list-element.component.scss'],
+  templateUrl: './publication-list-element.component.html'
+})
+/**
+ * The component for displaying a list element for an item of the type Publication
+ */
+export class PublicationListElementComponent extends TypedItemSearchResultListElementComponent {
+}
diff --git a/src/app/shared/object-list/item-list-element/item-types/typed-item-search-result-list-element.component.spec.ts b/src/app/shared/object-list/item-list-element/item-types/typed-item-search-result-list-element.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f320ff2efc9ee656640168be5905777434aedc4f
--- /dev/null
+++ b/src/app/shared/object-list/item-list-element/item-types/typed-item-search-result-list-element.component.spec.ts
@@ -0,0 +1,83 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { TruncatePipe } from '../../../utils/truncate.pipe';
+import { TruncatableService } from '../../../truncatable/truncatable.service';
+import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
+import { TypedItemSearchResultListElementComponent } from './typed-item-search-result-list-element.component';
+import { Item } from '../../../../core/shared/item.model';
+import { RemoteData } from '../../../../core/data/remote-data';
+import { PaginatedList } from '../../../../core/data/paginated-list';
+import { PageInfo } from '../../../../core/shared/page-info.model';
+import { ITEM } from '../../../items/switcher/item-type-switcher.component';
+import { ItemSearchResult } from '../../../object-collection/shared/item-search-result.model';
+import { createRelationshipsObservable } from '../../../../+item-page/simple/item-types/shared/item.component.spec';
+import { of as observableOf } from 'rxjs';
+import { MetadataMap } from '../../../../core/shared/metadata.models';
+
+const mockItem: Item = Object.assign(new Item(), {
+  bitstreams: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))),
+  metadata: [],
+  relationships: createRelationshipsObservable()
+});
+const mockSearchResult = {
+  indexableObject: mockItem as Item,
+  hitHighlights: new MetadataMap()
+} as ItemSearchResult;
+
+describe('ItemSearchResultComponent', () => {
+  let comp: TypedItemSearchResultListElementComponent;
+  let fixture: ComponentFixture<TypedItemSearchResultListElementComponent>;
+
+  describe('when injecting an Item', () => {
+    beforeEach(async(() => {
+      TestBed.configureTestingModule({
+        declarations: [TypedItemSearchResultListElementComponent, TruncatePipe],
+        providers: [
+          {provide: TruncatableService, useValue: {}},
+          {provide: ITEM, useValue: mockItem}
+        ],
+
+        schemas: [NO_ERRORS_SCHEMA]
+      }).overrideComponent(TypedItemSearchResultListElementComponent, {
+        set: {changeDetection: ChangeDetectionStrategy.Default}
+      }).compileComponents();
+    }));
+
+    beforeEach(async(() => {
+      fixture = TestBed.createComponent(TypedItemSearchResultListElementComponent);
+      comp = fixture.componentInstance;
+    }));
+
+    it('should initiate item, object and dso correctly', () => {
+      expect(comp.item).toBe(mockItem);
+      expect(comp.dso).toBe(mockItem);
+      expect(comp.object.indexableObject).toBe(mockItem);
+    })
+  });
+
+  describe('when injecting an ItemSearchResult', () => {
+    beforeEach(async(() => {
+      TestBed.configureTestingModule({
+        declarations: [TypedItemSearchResultListElementComponent, TruncatePipe],
+        providers: [
+          {provide: TruncatableService, useValue: {}},
+          {provide: ITEM, useValue: mockSearchResult}
+        ],
+
+        schemas: [NO_ERRORS_SCHEMA]
+      }).overrideComponent(TypedItemSearchResultListElementComponent, {
+        set: {changeDetection: ChangeDetectionStrategy.Default}
+      }).compileComponents();
+    }));
+
+    beforeEach(async(() => {
+      fixture = TestBed.createComponent(TypedItemSearchResultListElementComponent);
+      comp = fixture.componentInstance;
+    }));
+
+    it('should initiate item, object and dso correctly', () => {
+      expect(comp.item).toBe(mockItem);
+      expect(comp.dso).toBe(mockItem);
+      expect(comp.object.indexableObject).toBe(mockItem);
+    })
+  });
+});
diff --git a/src/app/shared/object-list/item-list-element/item-types/typed-item-search-result-list-element.component.ts b/src/app/shared/object-list/item-list-element/item-types/typed-item-search-result-list-element.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7df3ab5681f065cf9d26029ce11fb18a34dca54a
--- /dev/null
+++ b/src/app/shared/object-list/item-list-element/item-types/typed-item-search-result-list-element.component.ts
@@ -0,0 +1,37 @@
+import { Component, Inject } from '@angular/core';
+import { Item } from '../../../../core/shared/item.model';
+import { hasValue } from '../../../empty.util';
+import { ITEM } from '../../../items/switcher/item-type-switcher.component';
+import { ItemSearchResult } from '../../../object-collection/shared/item-search-result.model';
+import { TruncatableService } from '../../../truncatable/truncatable.service';
+import { SearchResultListElementComponent } from '../../search-result-list-element/search-result-list-element.component';
+import { MetadataMap } from '../../../../core/shared/metadata.models';
+
+/**
+ * A generic component for displaying item list elements
+ */
+@Component({
+  selector: 'ds-item-search-result',
+  template: ''
+})
+export class TypedItemSearchResultListElementComponent extends SearchResultListElementComponent<ItemSearchResult, Item> {
+  item: Item;
+
+  constructor(
+    protected truncatableService: TruncatableService,
+    @Inject(ITEM) public obj: Item | ItemSearchResult,
+  ) {
+    super(undefined, truncatableService);
+    if (hasValue((obj as any).indexableObject)) {
+      this.object = obj as ItemSearchResult;
+      this.dso = this.object.indexableObject;
+    } else {
+      this.object = {
+        indexableObject: obj as Item,
+        hitHighlights: new MetadataMap()
+      };
+      this.dso = obj as Item;
+    }
+    this.item = this.dso;
+  }
+}
diff --git a/src/app/shared/object-list/metadata-representation-list-element/item/item-metadata-list-element.component.html b/src/app/shared/object-list/metadata-representation-list-element/item/item-metadata-list-element.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..764fdc1064884d507ab35ec6d8e554a7c16ac605
--- /dev/null
+++ b/src/app/shared/object-list/metadata-representation-list-element/item/item-metadata-list-element.component.html
@@ -0,0 +1,2 @@
+<ds-item-type-switcher [object]="metadataRepresentation" [viewMode]="viewMode">
+</ds-item-type-switcher>
diff --git a/src/app/shared/object-list/metadata-representation-list-element/item/item-metadata-list-element.component.spec.ts b/src/app/shared/object-list/metadata-representation-list-element/item/item-metadata-list-element.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5ffa0689511b50ad06b1af93e25ffa8ac4e07936
--- /dev/null
+++ b/src/app/shared/object-list/metadata-representation-list-element/item/item-metadata-list-element.component.spec.ts
@@ -0,0 +1,38 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { ITEM } from '../../../items/switcher/item-type-switcher.component';
+import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
+import { ItemMetadataListElementComponent } from './item-metadata-list-element.component';
+import { By } from '@angular/platform-browser';
+import { ItemMetadataRepresentation } from '../../../../core/shared/metadata-representation/item/item-metadata-representation.model';
+
+const mockItemMetadataRepresentation = new ItemMetadataRepresentation();
+
+describe('ItemMetadataListElementComponent', () => {
+  let comp: ItemMetadataListElementComponent;
+  let fixture: ComponentFixture<ItemMetadataListElementComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [],
+      declarations: [ItemMetadataListElementComponent],
+      providers: [
+        { provide: ITEM, useValue: mockItemMetadataRepresentation }
+      ],
+      schemas: [NO_ERRORS_SCHEMA]
+    }).overrideComponent(ItemMetadataListElementComponent, {
+      set: {changeDetection: ChangeDetectionStrategy.Default}
+    }).compileComponents();
+  }));
+
+  beforeEach(async(() => {
+    fixture = TestBed.createComponent(ItemMetadataListElementComponent);
+    comp = fixture.componentInstance;
+    fixture.detectChanges();
+  }));
+
+  it('should call an item-type-switcher component and pass the item-metadata-representation', () => {
+    const itemTypeSwitcher = fixture.debugElement.query(By.css('ds-item-type-switcher')).nativeElement;
+    expect(itemTypeSwitcher.object).toBe(mockItemMetadataRepresentation);
+  });
+
+});
diff --git a/src/app/shared/object-list/metadata-representation-list-element/item/item-metadata-list-element.component.ts b/src/app/shared/object-list/metadata-representation-list-element/item/item-metadata-list-element.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..084567a885aa31fffe84f4e555c6482a3711c19b
--- /dev/null
+++ b/src/app/shared/object-list/metadata-representation-list-element/item/item-metadata-list-element.component.ts
@@ -0,0 +1,22 @@
+import { MetadataRepresentationType } from '../../../../core/shared/metadata-representation/metadata-representation.model';
+import { Component } from '@angular/core';
+import { MetadataRepresentationListElementComponent } from '../metadata-representation-list-element.component';
+import { DEFAULT_ITEM_TYPE, ItemViewMode, rendersItemType } from '../../../items/item-type-decorator';
+
+@rendersItemType(DEFAULT_ITEM_TYPE, ItemViewMode.Metadata, MetadataRepresentationType.Item)
+@Component({
+  selector: 'ds-item-metadata-list-element',
+  templateUrl: './item-metadata-list-element.component.html'
+})
+/**
+ * A component for displaying MetadataRepresentation objects in the form of items
+ * It will send the MetadataRepresentation object along with ElementViewMode.SetElement to the ItemTypeSwitcherComponent,
+ * which will in his turn decide how to render the item as metadata.
+ */
+export class ItemMetadataListElementComponent extends MetadataRepresentationListElementComponent {
+  /**
+   * The view-mode we're currently on
+   * @type {ElementViewMode}
+   */
+  viewMode = ItemViewMode.Element;
+}
diff --git a/src/app/shared/object-list/metadata-representation-list-element/metadata-representation-list-element.component.ts b/src/app/shared/object-list/metadata-representation-list-element/metadata-representation-list-element.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2488db50b17ac9b79fc63ff522b842c4b6a6dcd1
--- /dev/null
+++ b/src/app/shared/object-list/metadata-representation-list-element/metadata-representation-list-element.component.ts
@@ -0,0 +1,15 @@
+import { Component, Inject } from '@angular/core';
+import { MetadataRepresentation } from '../../../core/shared/metadata-representation/metadata-representation.model';
+import { ITEM } from '../../items/switcher/item-type-switcher.component';
+
+@Component({
+  selector: 'ds-metadata-representation-list-element',
+  template: ''
+})
+/**
+ * An abstract class for displaying a single MetadataRepresentation
+ */
+export class MetadataRepresentationListElementComponent {
+  constructor(@Inject(ITEM) public metadataRepresentation: MetadataRepresentation) {
+  }
+}
diff --git a/src/app/shared/object-list/metadata-representation-list-element/plain-text/plain-text-metadata-list-element.component.html b/src/app/shared/object-list/metadata-representation-list-element/plain-text/plain-text-metadata-list-element.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..3e017e1ae8654e2da856cf478923edfd3b4a6c36
--- /dev/null
+++ b/src/app/shared/object-list/metadata-representation-list-element/plain-text/plain-text-metadata-list-element.component.html
@@ -0,0 +1,3 @@
+<div>
+  <span>{{metadataRepresentation.getValue()}}</span>
+</div>
diff --git a/src/app/shared/object-list/metadata-representation-list-element/plain-text/plain-text-metadata-list-element.component.spec.ts b/src/app/shared/object-list/metadata-representation-list-element/plain-text/plain-text-metadata-list-element.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..42b9abde16bccd63cb5fe737197a0fc754762419
--- /dev/null
+++ b/src/app/shared/object-list/metadata-representation-list-element/plain-text/plain-text-metadata-list-element.component.spec.ts
@@ -0,0 +1,39 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
+import { PlainTextMetadataListElementComponent } from './plain-text-metadata-list-element.component';
+import { MetadatumRepresentation } from '../../../../core/shared/metadata-representation/metadatum/metadatum-representation.model';
+import { ITEM } from '../../../items/switcher/item-type-switcher.component';
+
+const mockMetadataRepresentation = Object.assign(new MetadatumRepresentation('type'), {
+  key: 'dc.contributor.author',
+  value: 'Test Author'
+});
+
+describe('PlainTextMetadataListElementComponent', () => {
+  let comp: PlainTextMetadataListElementComponent;
+  let fixture: ComponentFixture<PlainTextMetadataListElementComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [],
+      declarations: [PlainTextMetadataListElementComponent],
+      providers: [
+        { provide: ITEM, useValue: mockMetadataRepresentation }
+      ],
+      schemas: [NO_ERRORS_SCHEMA]
+    }).overrideComponent(PlainTextMetadataListElementComponent, {
+      set: {changeDetection: ChangeDetectionStrategy.Default}
+    }).compileComponents();
+  }));
+
+  beforeEach(async(() => {
+    fixture = TestBed.createComponent(PlainTextMetadataListElementComponent);
+    comp = fixture.componentInstance;
+    fixture.detectChanges();
+  }));
+
+  it('should contain the value as plain text', () => {
+    expect(fixture.debugElement.nativeElement.textContent).toContain(mockMetadataRepresentation.value);
+  });
+
+});
diff --git a/src/app/shared/object-list/metadata-representation-list-element/plain-text/plain-text-metadata-list-element.component.ts b/src/app/shared/object-list/metadata-representation-list-element/plain-text/plain-text-metadata-list-element.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7c4785e37c9f04fcb37f2fb44058c4edb9bf60ae
--- /dev/null
+++ b/src/app/shared/object-list/metadata-representation-list-element/plain-text/plain-text-metadata-list-element.component.ts
@@ -0,0 +1,18 @@
+import { MetadataRepresentationType } from '../../../../core/shared/metadata-representation/metadata-representation.model';
+import { Component } from '@angular/core';
+import { MetadataRepresentationListElementComponent } from '../metadata-representation-list-element.component';
+import { DEFAULT_ITEM_TYPE, ItemViewMode, rendersItemType } from '../../../items/item-type-decorator';
+
+@rendersItemType(DEFAULT_ITEM_TYPE, ItemViewMode.Metadata, MetadataRepresentationType.PlainText)
+// For now, authority controlled fields are rendered the same way as plain text fields
+@rendersItemType(DEFAULT_ITEM_TYPE, ItemViewMode.Metadata, MetadataRepresentationType.AuthorityControlled)
+@Component({
+  selector: 'ds-plain-text-metadata-list-element',
+  templateUrl: './plain-text-metadata-list-element.component.html'
+})
+/**
+ * A component for displaying MetadataRepresentation objects in the form of plain text
+ * It will simply use the value retrieved from MetadataRepresentation.getValue() to display as plain text
+ */
+export class PlainTextMetadataListElementComponent extends MetadataRepresentationListElementComponent {
+}
diff --git a/src/app/shared/object-list/my-dspace-result-list-element/claimed-my-dspace-result/claimed-my-dspace-result-list-element.component.html b/src/app/shared/object-list/my-dspace-result-list-element/claimed-my-dspace-result/claimed-my-dspace-result-list-element.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..5ec4c9a5b5f6ff20ffbd6bc25cd1a58c1d25df7e
--- /dev/null
+++ b/src/app/shared/object-list/my-dspace-result-list-element/claimed-my-dspace-result/claimed-my-dspace-result-list-element.component.html
@@ -0,0 +1,7 @@
+<ds-item-list-preview *ngIf="workflowitem"
+                      [item]="(workflowitem.item | async)?.payload"
+                      [object]="object"
+                      [showSubmitter]="showSubmitter"
+                      [status]="status"></ds-item-list-preview>
+
+<ds-claimed-task-actions *ngIf="workflowitem" [object]="dso"></ds-claimed-task-actions>
diff --git a/src/app/shared/object-list/my-dspace-result-list-element/claimed-my-dspace-result/claimed-my-dspace-result-list-element.component.spec.ts b/src/app/shared/object-list/my-dspace-result-list-element/claimed-my-dspace-result/claimed-my-dspace-result-list-element.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7c30b2ef8c8d475ed528f7826a2afd0924f7af80
--- /dev/null
+++ b/src/app/shared/object-list/my-dspace-result-list-element/claimed-my-dspace-result/claimed-my-dspace-result-list-element.component.spec.ts
@@ -0,0 +1,89 @@
+import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { NoopAnimationsModule } from '@angular/platform-browser/animations';
+
+import { of as observableOf } from 'rxjs';
+
+import { Item } from '../../../../core/shared/item.model';
+import { ClaimedMyDSpaceResultListElementComponent } from './claimed-my-dspace-result-list-element.component';
+import { ClaimedTaskMyDSpaceResult } from '../../../object-collection/shared/claimed-task-my-dspace-result.model';
+import { ClaimedTask } from '../../../../core/tasks/models/claimed-task-object.model';
+import { RemoteData } from '../../../../core/data/remote-data';
+import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type';
+import { Workflowitem } from '../../../../core/submission/models/workflowitem.model';
+
+let component: ClaimedMyDSpaceResultListElementComponent;
+let fixture: ComponentFixture<ClaimedMyDSpaceResultListElementComponent>;
+
+const compIndex = 1;
+
+const mockResultObject: ClaimedTaskMyDSpaceResult = new ClaimedTaskMyDSpaceResult();
+mockResultObject.hitHighlights = {};
+
+const item = Object.assign(new Item(), {
+  bitstreams: observableOf({}),
+  metadata: {
+    'dc.title': [
+      {
+        language: 'en_US',
+        value: 'This is just another title'
+      }
+    ],
+    'dc.type': [
+      {
+        language: null,
+        value: 'Article'
+      }
+    ],
+    'dc.contributor.author': [
+      {
+        language: 'en_US',
+        value: 'Smith, Donald'
+      }
+    ],
+    'dc.date.issued': [
+      {
+        language: null,
+        value: '2015-06-26'
+      }
+    ]
+  }
+});
+const rdItem = new RemoteData(false, false, true, null, item);
+const workflowitem = Object.assign(new Workflowitem(), { item: observableOf(rdItem) });
+const rdWorkflowitem = new RemoteData(false, false, true, null, workflowitem);
+mockResultObject.indexableObject = Object.assign(new ClaimedTask(), { workflowitem: observableOf(rdWorkflowitem) });
+
+describe('ClaimedMyDSpaceResultListElementComponent', () => {
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [NoopAnimationsModule],
+      declarations: [ClaimedMyDSpaceResultListElementComponent],
+      providers: [
+        { provide: 'objectElementProvider', useValue: (mockResultObject) },
+        { provide: 'indexElementProvider', useValue: (compIndex) }
+      ],
+      schemas: [NO_ERRORS_SCHEMA]
+    }).overrideComponent(ClaimedMyDSpaceResultListElementComponent, {
+      set: { changeDetection: ChangeDetectionStrategy.Default }
+    }).compileComponents();
+  }));
+
+  beforeEach(async(() => {
+    fixture = TestBed.createComponent(ClaimedMyDSpaceResultListElementComponent);
+    component = fixture.componentInstance;
+  }));
+
+  beforeEach(() => {
+    component.dso = mockResultObject.indexableObject;
+    fixture.detectChanges();
+  });
+
+  it('should init item properly', () => {
+    expect(component.workflowitem).toEqual(workflowitem);
+  });
+
+  it('should have properly status', () => {
+    expect(component.status).toEqual(MyDspaceItemStatusType.VALIDATION);
+  });
+});
diff --git a/src/app/shared/object-list/my-dspace-result-list-element/claimed-my-dspace-result/claimed-my-dspace-result-list-element.component.ts b/src/app/shared/object-list/my-dspace-result-list-element/claimed-my-dspace-result/claimed-my-dspace-result-list-element.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0725f98897e355abb3d0dd73d16977b3e10f6557
--- /dev/null
+++ b/src/app/shared/object-list/my-dspace-result-list-element/claimed-my-dspace-result/claimed-my-dspace-result-list-element.component.ts
@@ -0,0 +1,64 @@
+import { Component } from '@angular/core';
+import { Location, LocationStrategy, PathLocationStrategy } from '@angular/common';
+
+import { Observable } from 'rxjs';
+import { find } from 'rxjs/operators';
+
+import { renderElementsFor } from '../../../object-collection/shared/dso-element-decorator';
+import { MyDSpaceResultListElementComponent, } from '../my-dspace-result-list-element.component';
+import { ViewMode } from '../../../../core/shared/view-mode.model';
+import { RemoteData } from '../../../../core/data/remote-data';
+import { isNotUndefined } from '../../../empty.util';
+import { Workflowitem } from '../../../../core/submission/models/workflowitem.model';
+import { ClaimedTask } from '../../../../core/tasks/models/claimed-task-object.model';
+import { ClaimedTaskMyDSpaceResult } from '../../../object-collection/shared/claimed-task-my-dspace-result.model';
+import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type';
+import { SetViewMode } from '../../../view-mode';
+
+/**
+ * This component renders claimed task object for the mydspace result in the list view.
+ */
+@Component({
+  selector: 'ds-claimed-my-dspace-result-list-element',
+  styleUrls: ['../my-dspace-result-list-element.component.scss'],
+  templateUrl: './claimed-my-dspace-result-list-element.component.html',
+  providers: [Location, { provide: LocationStrategy, useClass: PathLocationStrategy }]
+})
+
+@renderElementsFor(ClaimedTaskMyDSpaceResult, SetViewMode.List)
+@renderElementsFor(ClaimedTask, SetViewMode.List)
+export class ClaimedMyDSpaceResultListElementComponent extends MyDSpaceResultListElementComponent<ClaimedTaskMyDSpaceResult, ClaimedTask> {
+
+  /**
+   * A boolean representing if to show submitter information
+   */
+  public showSubmitter = true;
+
+  /**
+   * Represent item's status
+   */
+  public status = MyDspaceItemStatusType.VALIDATION;
+
+  /**
+   * The workflowitem object that belonging to the result object
+   */
+  public workflowitem: Workflowitem;
+
+  /**
+   * Initialize all instance variables
+   */
+  ngOnInit() {
+    this.initWorkflowItem(this.dso.workflowitem as Observable<RemoteData<Workflowitem>>);
+  }
+
+  /**
+   * Retrieve workflowitem from result object
+   */
+  initWorkflowItem(wfi$: Observable<RemoteData<Workflowitem>>) {
+    wfi$.pipe(
+      find((rd: RemoteData<Workflowitem>) => (rd.hasSucceeded && isNotUndefined(rd.payload)))
+    ).subscribe((rd: RemoteData<Workflowitem>) => {
+      this.workflowitem = rd.payload;
+    });
+  }
+}
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
new file mode 100644
index 0000000000000000000000000000000000000000..bcd5c3c02715cd0832e69c209044aebdfa4a5eed
--- /dev/null
+++ b/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.html
@@ -0,0 +1,32 @@
+<ng-container *ngIf="item" @fadeInOut>
+  <ng-container *ngIf="status">
+    <ds-mydspace-item-status [status]="status"></ds-mydspace-item-status>
+  </ng-container>
+  <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>
+      <span class="text-muted">
+        <ds-truncatable-part [id]="item.id" [minLines]="1">
+          (<span *ngIf="item.hasMetadata('dc.publisher')" class="item-list-publisher"
+                 [innerHTML]="item.firstMetadataValue('dc.publisher') + ', '"></span>
+          <span class="item-list-date" [innerHTML]="item.firstMetadataValue('dc.date.issued') || ('mydspace.results.no-date' | translate)"></span>)
+          <span *ngIf="item.hasMetadata(['dc.contributor.author', 'dc.creator', 'dc.contributor.*']);"
+                class="item-list-authors">
+              <span *ngIf="item.allMetadataValues(['dc.contributor.author', 'dc.creator', 'dc.contributor.*']).length === 0">{{'mydspace.results.no-authors' | translate}}</span>
+              <span *ngFor="let author of item.allMetadataValues(['dc.contributor.author', 'dc.creator', 'dc.contributor.*']); let last=last;">
+                <span [innerHTML]="author"><span [innerHTML]="author"></span></span>
+              </span>
+          </span>
+
+          </ds-truncatable-part>
+      </span>
+
+      <ds-truncatable-part [id]="item.id" [minLines]="1" class="item-list-abstract">
+        <span [ngClass]="{'text-muted': !item.firstMetadataValue('dc.description.abstract')}"
+              [innerHTML]="(item.firstMetadataValue('dc.description.abstract')) || ('mydspace.results.no-abstract' | translate)"></span>
+      </ds-truncatable-part>
+
+    </div>
+  </ds-truncatable>
+  <ds-item-submitter *ngIf="showSubmitter" [object]="object.indexableObject"></ds-item-submitter>
+</ng-container>
diff --git a/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.scss b/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
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
new file mode 100644
index 0000000000000000000000000000000000000000..17b0279c7a54088682fe13a0ae61e618522861c3
--- /dev/null
+++ b/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.spec.ts
@@ -0,0 +1,131 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
+import { By } from '@angular/platform-browser';
+
+import { of as observableOf } from 'rxjs';
+
+import { TruncatePipe } from '../../../utils/truncate.pipe';
+import { Item } from '../../../../core/shared/item.model';
+import { ItemListPreviewComponent } from './item-list-preview.component';
+import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
+import { MockTranslateLoader } from '../../../mocks/mock-translate-loader';
+
+let component: ItemListPreviewComponent;
+let fixture: ComponentFixture<ItemListPreviewComponent>;
+
+const mockItemWithAuthorAndDate: Item = Object.assign(new Item(), {
+  bitstreams: observableOf({}),
+  metadata: {
+    'dc.contributor.author': [
+      {
+        language: 'en_US',
+        value: 'Smith, Donald'
+      }
+    ],
+    'dc.date.issued': [
+      {
+        language: null,
+        value: '2015-06-26'
+      }
+    ]
+  }
+});
+const mockItemWithoutAuthorAndDate: Item = Object.assign(new Item(), {
+  bitstreams: observableOf({}),
+  metadata: {
+    'dc.title': [
+      {
+        language: 'en_US',
+        value: 'This is just another title'
+      }
+    ],
+    'dc.type': [
+      {
+        language: null,
+        value: 'Article'
+      }
+    ]
+  }
+});
+
+describe('ItemListPreviewComponent', () => {
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [
+        TranslateModule.forRoot({
+          loader: {
+            provide: TranslateLoader,
+            useClass: MockTranslateLoader
+          }
+        }),
+      ],
+      declarations: [ItemListPreviewComponent, TruncatePipe],
+      providers: [
+        { provide: 'objectElementProvider', useValue: { mockItemWithAuthorAndDate } }
+
+      ],
+
+      schemas: [NO_ERRORS_SCHEMA]
+    }).overrideComponent(ItemListPreviewComponent, {
+      set: { changeDetection: ChangeDetectionStrategy.Default }
+    }).compileComponents();
+  }));
+
+  beforeEach(async(() => {
+    fixture = TestBed.createComponent(ItemListPreviewComponent);
+    component = fixture.componentInstance;
+
+  }));
+
+  beforeEach(() => {
+    component.object = { hitHighlights: {} } as any;
+  });
+
+  describe('When the item has an author', () => {
+    beforeEach(() => {
+      component.item = mockItemWithAuthorAndDate;
+      fixture.detectChanges();
+    });
+
+    it('should show the author paragraph', () => {
+      const itemAuthorField = fixture.debugElement.query(By.css('span.item-list-authors'));
+      expect(itemAuthorField).not.toBeNull();
+    });
+  });
+
+  describe('When the item has no author', () => {
+    beforeEach(() => {
+      component.item = mockItemWithoutAuthorAndDate;
+      fixture.detectChanges();
+    });
+
+    it('should not show the author paragraph', () => {
+      const itemAuthorField = fixture.debugElement.query(By.css('span.item-list-authors'));
+      expect(itemAuthorField).toBeNull();
+    });
+  });
+
+  describe('When the item has an issuedate', () => {
+    beforeEach(() => {
+      component.item = mockItemWithAuthorAndDate;
+      fixture.detectChanges();
+    });
+
+    it('should show the issuedate span', () => {
+      const dateField = fixture.debugElement.query(By.css('span.item-list-date'));
+      expect(dateField).not.toBeNull();
+    });
+  });
+
+  describe('When the item has no issuedate', () => {
+    beforeEach(() => {
+      component.item = mockItemWithoutAuthorAndDate;
+      fixture.detectChanges();
+    });
+
+    it('should show the issuedate empty placeholder', () => {
+      const dateField = fixture.debugElement.query(By.css('span.item-list-date'));
+      expect(dateField).not.toBeNull();
+    });
+  });
+});
diff --git a/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.ts b/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..13876ab46a5444aa43b8caef1e634d6158cca689
--- /dev/null
+++ b/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.ts
@@ -0,0 +1,39 @@
+import { Component, Input } from '@angular/core';
+
+import { Item } from '../../../../core/shared/item.model';
+import { fadeInOut } from '../../../animations/fade';
+import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type';
+import { MyDSpaceResult } from '../../../../+my-dspace-page/my-dspace-result.model';
+
+/**
+ * This component show metadata for the given item object in the list view.
+ */
+@Component({
+  selector: 'ds-item-list-preview',
+  styleUrls: ['item-list-preview.component.scss'],
+  templateUrl: 'item-list-preview.component.html',
+  animations: [fadeInOut]
+})
+export class ItemListPreviewComponent {
+
+  /**
+   * The item to display
+   */
+  @Input() item: Item;
+
+  /**
+   * The mydspace result object
+   */
+  @Input() object: MyDSpaceResult<any>;
+
+  /**
+   * Represent item's status
+   */
+  @Input() status: MyDspaceItemStatusType;
+
+  /**
+   * A boolean representing if to show submitter information
+   */
+  @Input() showSubmitter = false;
+
+}
diff --git a/src/app/shared/object-list/my-dspace-result-list-element/item-my-dspace-result/item-my-dspace-result-list-element.component.html b/src/app/shared/object-list/my-dspace-result-list-element/item-my-dspace-result/item-my-dspace-result-list-element.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..e1b14354814627d6c93e6521d194b24d2a8c5a48
--- /dev/null
+++ b/src/app/shared/object-list/my-dspace-result-list-element/item-my-dspace-result/item-my-dspace-result-list-element.component.html
@@ -0,0 +1,5 @@
+<ds-item-list-preview [item]="dso"
+                      [object]="object"
+                      [status]="status"></ds-item-list-preview>
+
+<ds-item-actions [object]="dso"></ds-item-actions>
diff --git a/src/app/shared/object-list/my-dspace-result-list-element/item-my-dspace-result/item-my-dspace-result-list-element.component.scss b/src/app/shared/object-list/my-dspace-result-list-element/item-my-dspace-result/item-my-dspace-result-list-element.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..1d0786105ca4dc61051610513a3fdbe21424a6d5
--- /dev/null
+++ b/src/app/shared/object-list/my-dspace-result-list-element/item-my-dspace-result/item-my-dspace-result-list-element.component.scss
@@ -0,0 +1 @@
+@import '../../../../../styles/variables';
diff --git a/src/app/shared/object-list/my-dspace-result-list-element/item-my-dspace-result/item-my-dspace-result-list-element.component.spec.ts b/src/app/shared/object-list/my-dspace-result-list-element/item-my-dspace-result/item-my-dspace-result-list-element.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ea4f2d24f3d56a4301774b3f4063e405a2cc15d9
--- /dev/null
+++ b/src/app/shared/object-list/my-dspace-result-list-element/item-my-dspace-result/item-my-dspace-result-list-element.component.spec.ts
@@ -0,0 +1,78 @@
+import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { NoopAnimationsModule } from '@angular/platform-browser/animations';
+
+import { of as observableOf } from 'rxjs';
+
+import { Item } from '../../../../core/shared/item.model';
+import { ItemMyDSpaceResultListElementComponent } from './item-my-dspace-result-list-element.component';
+import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type';
+import { ItemMyDSpaceResult } from '../../../object-collection/shared/item-my-dspace-result.model';
+
+let component: ItemMyDSpaceResultListElementComponent;
+let fixture: ComponentFixture<ItemMyDSpaceResultListElementComponent>;
+
+const compIndex = 1;
+
+const mockResultObject: ItemMyDSpaceResult = new ItemMyDSpaceResult();
+mockResultObject.hitHighlights = {};
+
+mockResultObject.indexableObject = Object.assign(new Item(), {
+  bitstreams: observableOf({}),
+  metadata: {
+    'dc.title': [
+      {
+        language: 'en_US',
+        value: 'This is just another title'
+      }
+    ],
+    'dc.type': [
+      {
+        language: null,
+        value: 'Article'
+      }
+    ],
+    'dc.contributor.author': [
+      {
+        language: 'en_US',
+        value: 'Smith, Donald'
+      }
+    ],
+    'dc.date.issued': [
+      {
+        language: null,
+        value: '2015-06-26'
+      }
+    ]
+  }
+});
+
+describe('ItemMyDSpaceResultListElementComponent', () => {
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [NoopAnimationsModule],
+      declarations: [ItemMyDSpaceResultListElementComponent],
+      providers: [
+        { provide: 'objectElementProvider', useValue: (mockResultObject) },
+        { provide: 'indexElementProvider', useValue: (compIndex) }
+      ],
+      schemas: [NO_ERRORS_SCHEMA]
+    }).overrideComponent(ItemMyDSpaceResultListElementComponent, {
+      set: { changeDetection: ChangeDetectionStrategy.Default }
+    }).compileComponents();
+  }));
+
+  beforeEach(async(() => {
+    fixture = TestBed.createComponent(ItemMyDSpaceResultListElementComponent);
+    component = fixture.componentInstance;
+  }));
+
+  beforeEach(() => {
+    component.dso = mockResultObject.indexableObject;
+    fixture.detectChanges();
+  });
+
+  it('should have properly status', () => {
+    expect(component.status).toEqual(MyDspaceItemStatusType.ARCHIVED);
+  });
+});
diff --git a/src/app/shared/object-list/my-dspace-result-list-element/item-my-dspace-result/item-my-dspace-result-list-element.component.ts b/src/app/shared/object-list/my-dspace-result-list-element/item-my-dspace-result/item-my-dspace-result-list-element.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3f2a43960302028982d00054b138fdcf98f69d44
--- /dev/null
+++ b/src/app/shared/object-list/my-dspace-result-list-element/item-my-dspace-result/item-my-dspace-result-list-element.component.ts
@@ -0,0 +1,27 @@
+import { Component } from '@angular/core';
+
+import { renderElementsFor } from '../../../object-collection/shared/dso-element-decorator';
+import { MyDSpaceResultListElementComponent, } from '../my-dspace-result-list-element.component';
+import { Item } from '../../../../core/shared/item.model';
+import { ItemMyDSpaceResult } from '../../../object-collection/shared/item-my-dspace-result.model';
+import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type';
+import { SetViewMode } from '../../../view-mode';
+
+/**
+ * This component renders item object for the mydspace result in the list view.
+ */
+@Component({
+  selector: 'ds-workspaceitem-my-dspace-result-list-element',
+  styleUrls: ['../my-dspace-result-list-element.component.scss', './item-my-dspace-result-list-element.component.scss'],
+  templateUrl: './item-my-dspace-result-list-element.component.html'
+})
+
+@renderElementsFor(ItemMyDSpaceResult, SetViewMode.List)
+export class ItemMyDSpaceResultListElementComponent extends MyDSpaceResultListElementComponent<ItemMyDSpaceResult, Item> {
+
+  /**
+   * Represent item's status
+   */
+  public status = MyDspaceItemStatusType.ARCHIVED;
+
+}
diff --git a/src/app/shared/object-list/my-dspace-result-list-element/my-dspace-result-list-element.component.scss b/src/app/shared/object-list/my-dspace-result-list-element/my-dspace-result-list-element.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..4cd8a2b6977405d2118b9225410f190029161328
--- /dev/null
+++ b/src/app/shared/object-list/my-dspace-result-list-element/my-dspace-result-list-element.component.scss
@@ -0,0 +1 @@
+@import '../search-result-list-element/search-result-list-element.component.scss';
diff --git a/src/app/shared/object-list/my-dspace-result-list-element/my-dspace-result-list-element.component.ts b/src/app/shared/object-list/my-dspace-result-list-element/my-dspace-result-list-element.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b2a5bf14fdd1c904763633bb216a0af3ad297504
--- /dev/null
+++ b/src/app/shared/object-list/my-dspace-result-list-element/my-dspace-result-list-element.component.ts
@@ -0,0 +1,58 @@
+import { Component, Inject } from '@angular/core';
+
+import { MyDSpaceResult } from '../../../+my-dspace-page/my-dspace-result.model';
+import { AbstractListableElementComponent } from '../../object-collection/shared/object-collection-element/abstract-listable-element.component';
+import { ListableObject } from '../../object-collection/shared/listable-object.model';
+import { DSpaceObject } from '../../../core/shared/dspace-object.model';
+import { Metadata } from '../../../core/shared/metadata.utils';
+
+@Component({
+  selector: 'ds-my-dspace-result-list-element',
+  template: ``
+})
+export class MyDSpaceResultListElementComponent<T extends MyDSpaceResult<K>, K extends DSpaceObject> extends AbstractListableElementComponent<T> {
+
+  /**
+   * The result element object
+   */
+  dso: K;
+
+  /**
+   * The array index of the result element
+   */
+  dsoIndex: number;
+
+  /**
+   * Initialize instance variables
+   *
+   * @param {ListableObject} listable
+   * @param {number} index
+   */
+  public constructor(@Inject('objectElementProvider') public listable: ListableObject,
+                     @Inject('indexElementProvider') public index: number) {
+    super(listable);
+    this.dso = this.object.indexableObject;
+    this.dsoIndex = this.index;
+  }
+
+  /**
+   * Gets all matching metadata string values from hitHighlights or dso metadata, preferring hitHighlights.
+   *
+   * @param {string|string[]} keyOrKeys The metadata key(s) in scope. Wildcards are supported; see [[Metadata]].
+   * @returns {string[]} the matching string values or an empty array.
+   */
+  allMetadataValues(keyOrKeys: string | string[]): string[] {
+    return Metadata.allValues([this.object.hitHighlights, this.dso.metadata], keyOrKeys);
+  }
+
+  /**
+   * Gets the first matching metadata string value from hitHighlights or dso metadata, preferring hitHighlights.
+   *
+   * @param {string|string[]} keyOrKeys The metadata key(s) in scope. Wildcards are supported; see [[Metadata]].
+   * @returns {string} the first matching string value, or `undefined`.
+   */
+  firstMetadataValue(keyOrKeys: string | string[]): string {
+    return Metadata.firstValue([this.object.hitHighlights, this.dso.metadata], keyOrKeys);
+  }
+
+}
diff --git a/src/app/shared/object-list/my-dspace-result-list-element/pool-my-dspace-result/pool-my-dspace-result-list-element.component.html b/src/app/shared/object-list/my-dspace-result-list-element/pool-my-dspace-result/pool-my-dspace-result-list-element.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..5f0f1bb6d4845cc932afdb9427cd047c79efa4a3
--- /dev/null
+++ b/src/app/shared/object-list/my-dspace-result-list-element/pool-my-dspace-result/pool-my-dspace-result-list-element.component.html
@@ -0,0 +1,7 @@
+<ds-item-list-preview *ngIf="workflowitem"
+                      [item]="(workflowitem.item | async)?.payload"
+                      [object]="object"
+                      [showSubmitter]="showSubmitter"
+                      [status]="status"></ds-item-list-preview>
+
+<ds-pool-task-actions [object]="dso"></ds-pool-task-actions>
diff --git a/src/app/shared/object-list/my-dspace-result-list-element/pool-my-dspace-result/pool-my-dspace-result-list-element.component.spec.ts b/src/app/shared/object-list/my-dspace-result-list-element/pool-my-dspace-result/pool-my-dspace-result-list-element.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..36eb8f253ad7c0ac4407d3e2a2ec069df58086bd
--- /dev/null
+++ b/src/app/shared/object-list/my-dspace-result-list-element/pool-my-dspace-result/pool-my-dspace-result-list-element.component.spec.ts
@@ -0,0 +1,89 @@
+import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { NoopAnimationsModule } from '@angular/platform-browser/animations';
+
+import { of as observableOf } from 'rxjs';
+
+import { Item } from '../../../../core/shared/item.model';
+import { PoolMyDSpaceResultListElementComponent } from './pool-my-dspace-result-list-element.component';
+import { PoolTaskMyDSpaceResult } from '../../../object-collection/shared/pool-task-my-dspace-result.model';
+import { PoolTask } from '../../../../core/tasks/models/pool-task-object.model';
+import { RemoteData } from '../../../../core/data/remote-data';
+import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type';
+import { Workflowitem } from '../../../../core/submission/models/workflowitem.model';
+
+let component: PoolMyDSpaceResultListElementComponent;
+let fixture: ComponentFixture<PoolMyDSpaceResultListElementComponent>;
+
+const compIndex = 1;
+
+const mockResultObject: PoolTaskMyDSpaceResult = new PoolTaskMyDSpaceResult();
+mockResultObject.hitHighlights = {};
+
+const item = Object.assign(new Item(), {
+  bitstreams: observableOf({}),
+  metadata: {
+    'dc.title': [
+      {
+        language: 'en_US',
+        value: 'This is just another title'
+      }
+    ],
+    'dc.type': [
+      {
+        language: null,
+        value: 'Article'
+      }
+    ],
+    'dc.contributor.author': [
+      {
+        language: 'en_US',
+        value: 'Smith, Donald'
+      }
+    ],
+    'dc.date.issued': [
+      {
+        language: null,
+        value: '2015-06-26'
+      }
+    ]
+  }
+});
+const rdItem = new RemoteData(false, false, true, null, item);
+const workflowitem = Object.assign(new Workflowitem(), { item: observableOf(rdItem) });
+const rdWorkflowitem = new RemoteData(false, false, true, null, workflowitem);
+mockResultObject.indexableObject = Object.assign(new PoolTask(), { workflowitem: observableOf(rdWorkflowitem) });
+
+describe('PoolMyDSpaceResultListElementComponent', () => {
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [NoopAnimationsModule],
+      declarations: [PoolMyDSpaceResultListElementComponent],
+      providers: [
+        { provide: 'objectElementProvider', useValue: (mockResultObject) },
+        { provide: 'indexElementProvider', useValue: (compIndex) }
+      ],
+      schemas: [NO_ERRORS_SCHEMA]
+    }).overrideComponent(PoolMyDSpaceResultListElementComponent, {
+      set: { changeDetection: ChangeDetectionStrategy.Default }
+    }).compileComponents();
+  }));
+
+  beforeEach(async(() => {
+    fixture = TestBed.createComponent(PoolMyDSpaceResultListElementComponent);
+    component = fixture.componentInstance;
+  }));
+
+  beforeEach(() => {
+    component.dso = mockResultObject.indexableObject;
+    fixture.detectChanges();
+  });
+
+  it('should init item properly', () => {
+    expect(component.workflowitem).toEqual(workflowitem);
+  });
+
+  it('should have properly status', () => {
+    expect(component.status).toEqual(MyDspaceItemStatusType.WAITING_CONTROLLER);
+  });
+});
diff --git a/src/app/shared/object-list/my-dspace-result-list-element/pool-my-dspace-result/pool-my-dspace-result-list-element.component.ts b/src/app/shared/object-list/my-dspace-result-list-element/pool-my-dspace-result/pool-my-dspace-result-list-element.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2d78a7e2e8e2087957529689b1cacd79bb43b064
--- /dev/null
+++ b/src/app/shared/object-list/my-dspace-result-list-element/pool-my-dspace-result/pool-my-dspace-result-list-element.component.ts
@@ -0,0 +1,68 @@
+import { Component, Inject, OnInit } from '@angular/core';
+
+import { Observable } from 'rxjs';
+import { find } from 'rxjs/operators';
+
+import { renderElementsFor } from '../../../object-collection/shared/dso-element-decorator';
+import { MyDSpaceResultListElementComponent, } from '../my-dspace-result-list-element.component';
+import { ViewMode } from '../../../../core/shared/view-mode.model';
+import { RemoteData } from '../../../../core/data/remote-data';
+import { isNotUndefined } from '../../../empty.util';
+import { ListableObject } from '../../../object-collection/shared/listable-object.model';
+import { Workflowitem } from '../../../../core/submission/models/workflowitem.model';
+import { PoolTask } from '../../../../core/tasks/models/pool-task-object.model';
+import { PoolTaskMyDSpaceResult } from '../../../object-collection/shared/pool-task-my-dspace-result.model';
+import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type';
+import { SetViewMode } from '../../../view-mode';
+
+/**
+ * This component renders pool task object for the mydspace result in the list view.
+ */
+@Component({
+  selector: 'ds-pool-my-dspace-result-list-element',
+  styleUrls: ['../my-dspace-result-list-element.component.scss'],
+  templateUrl: './pool-my-dspace-result-list-element.component.html',
+})
+
+@renderElementsFor(PoolTaskMyDSpaceResult, SetViewMode.List)
+@renderElementsFor(PoolTask, SetViewMode.List)
+export class PoolMyDSpaceResultListElementComponent extends MyDSpaceResultListElementComponent<PoolTaskMyDSpaceResult, PoolTask> implements OnInit {
+
+  /**
+   * A boolean representing if to show submitter information
+   */
+  public showSubmitter = true;
+
+  /**
+   * Represent item's status
+   */
+  public status = MyDspaceItemStatusType.WAITING_CONTROLLER;
+
+  /**
+   * The workflowitem object that belonging to the result object
+   */
+  public workflowitem: Workflowitem;
+
+  constructor(@Inject('objectElementProvider') public listable: ListableObject,
+              @Inject('indexElementProvider') public index: number) {
+    super(listable, index);
+  }
+
+  /**
+   * Initialize all instance variables
+   */
+  ngOnInit() {
+    this.initWorkflowItem(this.dso.workflowitem as Observable<RemoteData<Workflowitem>>);
+  }
+
+  /**
+   * Retrieve workflowitem from result object
+   */
+  initWorkflowItem(wfi$: Observable<RemoteData<Workflowitem>>) {
+    wfi$.pipe(
+      find((rd: RemoteData<Workflowitem>) => (rd.hasSucceeded && isNotUndefined(rd.payload)))
+    ).subscribe((rd: RemoteData<Workflowitem>) => {
+      this.workflowitem = rd.payload;
+    });
+  }
+}
diff --git a/src/app/shared/object-list/my-dspace-result-list-element/workflowitem-my-dspace-result/workflowitem-my-dspace-result-list-element.component.html b/src/app/shared/object-list/my-dspace-result-list-element/workflowitem-my-dspace-result/workflowitem-my-dspace-result-list-element.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..782c5f9e56dd70db3a93262d7e92a958f5b212c6
--- /dev/null
+++ b/src/app/shared/object-list/my-dspace-result-list-element/workflowitem-my-dspace-result/workflowitem-my-dspace-result-list-element.component.html
@@ -0,0 +1,6 @@
+<ds-item-list-preview [item]="item"
+                      [object]="object"
+                      [status]="status"></ds-item-list-preview>
+
+<ds-workflowitem-actions [object]="dso"></ds-workflowitem-actions>
+
diff --git a/src/app/shared/object-list/my-dspace-result-list-element/workflowitem-my-dspace-result/workflowitem-my-dspace-result-list-element.component.spec.ts b/src/app/shared/object-list/my-dspace-result-list-element/workflowitem-my-dspace-result/workflowitem-my-dspace-result-list-element.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2bcd4d46b07c3a43d12f17df3d05e197107c5c94
--- /dev/null
+++ b/src/app/shared/object-list/my-dspace-result-list-element/workflowitem-my-dspace-result/workflowitem-my-dspace-result-list-element.component.spec.ts
@@ -0,0 +1,86 @@
+import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { NoopAnimationsModule } from '@angular/platform-browser/animations';
+
+import { of as observableOf } from 'rxjs';
+
+import { Item } from '../../../../core/shared/item.model';
+import { WorkflowitemMyDSpaceResultListElementComponent } from './workflowitem-my-dspace-result-list-element.component';
+import { WorkflowitemMyDSpaceResult } from '../../../object-collection/shared/workflowitem-my-dspace-result.model';
+import { Workflowitem } from '../../../../core/submission/models/workflowitem.model';
+import { RemoteData } from '../../../../core/data/remote-data';
+import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type';
+
+let component: WorkflowitemMyDSpaceResultListElementComponent;
+let fixture: ComponentFixture<WorkflowitemMyDSpaceResultListElementComponent>;
+
+const compIndex = 1;
+
+const mockResultObject: WorkflowitemMyDSpaceResult = new WorkflowitemMyDSpaceResult();
+mockResultObject.hitHighlights = {};
+
+const item = Object.assign(new Item(), {
+  bitstreams: observableOf({}),
+  metadata: {
+    'dc.title': [
+      {
+        language: 'en_US',
+        value: 'This is just another title'
+      }
+    ],
+    'dc.type': [
+      {
+        language: null,
+        value: 'Article'
+      }
+    ],
+    'dc.contributor.author': [
+      {
+        language: 'en_US',
+        value: 'Smith, Donald'
+      }
+    ],
+    'dc.date.issued': [
+      {
+        language: null,
+        value: '2015-06-26'
+      }
+    ]
+  }
+});
+const rd = new RemoteData(false, false, true, null, item);
+mockResultObject.indexableObject = Object.assign(new Workflowitem(), { item: observableOf(rd) });
+
+describe('WorkflowitemMyDSpaceResultListElementComponent', () => {
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [NoopAnimationsModule],
+      declarations: [WorkflowitemMyDSpaceResultListElementComponent],
+      providers: [
+        { provide: 'objectElementProvider', useValue: (mockResultObject) },
+        { provide: 'indexElementProvider', useValue: (compIndex) }
+      ],
+      schemas: [NO_ERRORS_SCHEMA]
+    }).overrideComponent(WorkflowitemMyDSpaceResultListElementComponent, {
+      set: { changeDetection: ChangeDetectionStrategy.Default }
+    }).compileComponents();
+  }));
+
+  beforeEach(async(() => {
+    fixture = TestBed.createComponent(WorkflowitemMyDSpaceResultListElementComponent);
+    component = fixture.componentInstance;
+  }));
+
+  beforeEach(() => {
+    component.dso = mockResultObject.indexableObject;
+    fixture.detectChanges();
+  });
+
+  it('should init item properly', () => {
+    expect(component.item).toEqual(item);
+  });
+
+  it('should have properly status', () => {
+    expect(component.status).toEqual(MyDspaceItemStatusType.WORKFLOW);
+  });
+});
diff --git a/src/app/shared/object-list/my-dspace-result-list-element/workflowitem-my-dspace-result/workflowitem-my-dspace-result-list-element.component.ts b/src/app/shared/object-list/my-dspace-result-list-element/workflowitem-my-dspace-result/workflowitem-my-dspace-result-list-element.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9a5038225cb0a8a39dddba86c3be18a1bf5bb2b0
--- /dev/null
+++ b/src/app/shared/object-list/my-dspace-result-list-element/workflowitem-my-dspace-result/workflowitem-my-dspace-result-list-element.component.ts
@@ -0,0 +1,58 @@
+import { Component } from '@angular/core';
+
+import { Observable } from 'rxjs';
+import { find } from 'rxjs/operators';
+
+import { renderElementsFor } from '../../../object-collection/shared/dso-element-decorator';
+import { MyDSpaceResultListElementComponent, } from '../my-dspace-result-list-element.component';
+import { ViewMode } from '../../../../core/shared/view-mode.model';
+import { RemoteData } from '../../../../core/data/remote-data';
+import { isNotUndefined } from '../../../empty.util';
+import { WorkflowitemMyDSpaceResult } from '../../../object-collection/shared/workflowitem-my-dspace-result.model';
+import { Workflowitem } from '../../../../core/submission/models/workflowitem.model';
+import { Item } from '../../../../core/shared/item.model';
+import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type';
+import { SetViewMode } from '../../../view-mode';
+
+/**
+ * This component renders workflowitem object for the mydspace result in the list view.
+ */
+@Component({
+  selector: 'ds-workflowitem-my-dspace-result-list-element',
+  styleUrls: ['../my-dspace-result-list-element.component.scss'],
+  templateUrl: './workflowitem-my-dspace-result-list-element.component.html',
+})
+
+@renderElementsFor(WorkflowitemMyDSpaceResult, SetViewMode.List)
+@renderElementsFor(Workflowitem, SetViewMode.List)
+export class WorkflowitemMyDSpaceResultListElementComponent extends MyDSpaceResultListElementComponent<WorkflowitemMyDSpaceResult, Workflowitem> {
+
+  /**
+   * The item object that belonging to the result object
+   */
+  public item: Item;
+
+  /**
+   * Represent item's status
+   */
+  public status = MyDspaceItemStatusType.WORKFLOW;
+
+  /**
+   * Initialize all instance variables
+   */
+  ngOnInit() {
+    this.initItem(this.dso.item as Observable<RemoteData<Item>>);
+  }
+
+  /**
+   * Retrieve item from result object
+   */
+  initItem(item$: Observable<RemoteData<Item>>) {
+    item$.pipe(
+      find((rd: RemoteData<Item>) => rd.hasSucceeded && isNotUndefined(rd.payload))
+    ).subscribe((rd: RemoteData<Item>) => {
+      this.item = rd.payload;
+    });
+  }
+
+}
diff --git a/src/app/shared/object-list/my-dspace-result-list-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-list-element.component.html b/src/app/shared/object-list/my-dspace-result-list-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-list-element.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..79a31770d6a6d2f2df3dbc242063bc802d84d2bb
--- /dev/null
+++ b/src/app/shared/object-list/my-dspace-result-list-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-list-element.component.html
@@ -0,0 +1,7 @@
+<ds-item-list-preview
+  *ngIf="status && item"
+  [item]="item"
+  [object]="object"
+  [status]="status"></ds-item-list-preview>
+
+<ds-workspaceitem-actions [object]="dso"></ds-workspaceitem-actions>
diff --git a/src/app/shared/object-list/my-dspace-result-list-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-list-element.component.scss b/src/app/shared/object-list/my-dspace-result-list-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-list-element.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..a32ecc6925bbf1049aebd493a14cfd63bd535e95
--- /dev/null
+++ b/src/app/shared/object-list/my-dspace-result-list-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-list-element.component.scss
@@ -0,0 +1,20 @@
+@import '../../../../../styles/variables';
+
+::-webkit-scrollbar-track
+{
+  -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3);
+  background-color: #F5F5F5;
+}
+
+::-webkit-scrollbar
+{
+  width: 12px;
+  background-color: #F5F5F5;
+}
+
+::-webkit-scrollbar-thumb
+{
+  -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,.3);
+  background-color: #555;
+}
+
diff --git a/src/app/shared/object-list/my-dspace-result-list-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-list-element.component.spec.ts b/src/app/shared/object-list/my-dspace-result-list-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-list-element.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..789b69da1b2c53dfbd7ccc65a5ebe2f73859d3e9
--- /dev/null
+++ b/src/app/shared/object-list/my-dspace-result-list-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-list-element.component.spec.ts
@@ -0,0 +1,86 @@
+import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { NoopAnimationsModule } from '@angular/platform-browser/animations';
+
+import { of as observableOf } from 'rxjs';
+
+import { Item } from '../../../../core/shared/item.model';
+import { WorkspaceitemMyDSpaceResultListElementComponent } from './workspaceitem-my-dspace-result-list-element.component';
+import { WorkspaceitemMyDSpaceResult } from '../../../object-collection/shared/workspaceitem-my-dspace-result.model';
+import { Workspaceitem } from '../../../../core/submission/models/workspaceitem.model';
+import { RemoteData } from '../../../../core/data/remote-data';
+import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type';
+
+let component: WorkspaceitemMyDSpaceResultListElementComponent;
+let fixture: ComponentFixture<WorkspaceitemMyDSpaceResultListElementComponent>;
+
+const compIndex = 1;
+
+const mockResultObject: WorkspaceitemMyDSpaceResult = new WorkspaceitemMyDSpaceResult();
+mockResultObject.hitHighlights = {};
+
+const item = Object.assign(new Item(), {
+  bitstreams: observableOf({}),
+  metadata: {
+    'dc.title': [
+      {
+        language: 'en_US',
+        value: 'This is just another title'
+      }
+    ],
+    'dc.type': [
+      {
+        language: null,
+        value: 'Article'
+      }
+    ],
+    'dc.contributor.author': [
+      {
+        language: 'en_US',
+        value: 'Smith, Donald'
+      }
+    ],
+    'dc.date.issued': [
+      {
+        language: null,
+        value: '2015-06-26'
+      }
+    ]
+  }
+});
+const rd = new RemoteData(false, false, true, null, item);
+mockResultObject.indexableObject = Object.assign(new Workspaceitem(), { item: observableOf(rd) });
+
+describe('WorkspaceitemMyDSpaceResultListElementComponent', () => {
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [NoopAnimationsModule],
+      declarations: [WorkspaceitemMyDSpaceResultListElementComponent],
+      providers: [
+        { provide: 'objectElementProvider', useValue: (mockResultObject) },
+        { provide: 'indexElementProvider', useValue: (compIndex) }
+      ],
+      schemas: [NO_ERRORS_SCHEMA]
+    }).overrideComponent(WorkspaceitemMyDSpaceResultListElementComponent, {
+      set: { changeDetection: ChangeDetectionStrategy.Default }
+    }).compileComponents();
+  }));
+
+  beforeEach(async(() => {
+    fixture = TestBed.createComponent(WorkspaceitemMyDSpaceResultListElementComponent);
+    component = fixture.componentInstance;
+  }));
+
+  beforeEach(() => {
+    component.dso = mockResultObject.indexableObject;
+    fixture.detectChanges();
+  });
+
+  it('should init item properly', () => {
+    expect(component.item).toEqual(item);
+  });
+
+  it('should have properly status', () => {
+    expect(component.status).toEqual(MyDspaceItemStatusType.WORKSPACE);
+  });
+});
diff --git a/src/app/shared/object-list/my-dspace-result-list-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-list-element.component.ts b/src/app/shared/object-list/my-dspace-result-list-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-list-element.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9ea647b4ba40a0d6a96eeefc01528dcb6b92af52
--- /dev/null
+++ b/src/app/shared/object-list/my-dspace-result-list-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-list-element.component.ts
@@ -0,0 +1,56 @@
+import { Component } from '@angular/core';
+
+import { Observable } from 'rxjs';
+import { find } from 'rxjs/operators';
+
+import { renderElementsFor } from '../../../object-collection/shared/dso-element-decorator';
+import { MyDSpaceResultListElementComponent, } from '../my-dspace-result-list-element.component';
+import { ViewMode } from '../../../../core/shared/view-mode.model';
+import { Workspaceitem } from '../../../../core/submission/models/workspaceitem.model';
+import { WorkspaceitemMyDSpaceResult } from '../../../object-collection/shared/workspaceitem-my-dspace-result.model';
+import { RemoteData } from '../../../../core/data/remote-data';
+import { isNotUndefined } from '../../../empty.util';
+import { Item } from '../../../../core/shared/item.model';
+import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type';
+import { SetViewMode } from '../../../view-mode';
+
+/**
+ * This component renders workspaceitem object for the mydspace result in the list view.
+ */
+@Component({
+  selector: 'ds-workspaceitem-my-dspace-result-list-element',
+  styleUrls: ['../my-dspace-result-list-element.component.scss', './workspaceitem-my-dspace-result-list-element.component.scss'],
+  templateUrl: './workspaceitem-my-dspace-result-list-element.component.html',
+})
+
+@renderElementsFor(WorkspaceitemMyDSpaceResult, SetViewMode.List)
+export class WorkspaceitemMyDSpaceResultListElementComponent extends MyDSpaceResultListElementComponent<WorkspaceitemMyDSpaceResult, Workspaceitem> {
+
+  /**
+   * The item object that belonging to the result object
+   */
+  item: Item;
+
+  /**
+   * Represent item's status
+   */
+  status = MyDspaceItemStatusType.WORKSPACE;
+
+  /**
+   * Initialize all instance variables
+   */
+  ngOnInit() {
+    this.initItem(this.dso.item as Observable<RemoteData<Item>>);
+  }
+
+  /**
+   * Retrieve item from result object
+   */
+  initItem(item$: Observable<RemoteData<Item>>) {
+    item$.pipe(
+      find((rd: RemoteData<Item>) => rd.hasSucceeded && isNotUndefined(rd.payload))
+    ).subscribe((rd: RemoteData<Item>) => {
+      this.item = rd.payload;
+    });
+  }
+}
diff --git a/src/app/shared/object-list/object-list.component.html b/src/app/shared/object-list/object-list.component.html
index 420886668a744c687ab327d3d397bdd0f70d5b92..1fdc06d5bf5cca762d4df7f53741cabcfbb4e19d 100644
--- a/src/app/shared/object-list/object-list.component.html
+++ b/src/app/shared/object-list/object-list.component.html
@@ -11,8 +11,8 @@
   (sortFieldChange)="onSortFieldChange($event)"
   (paginationChange)="onPaginationChange($event)">
   <ul *ngIf="objects?.hasSucceeded" class="list-unstyled">
-      <li *ngFor="let object of objects?.payload?.page" class="mt-4 mb-4">
-        <ds-wrapper-list-element [object]="object"></ds-wrapper-list-element>
+      <li *ngFor="let object of objects?.payload?.page; let i = index; let last = last" class="mt-4 mb-4" [class.border-bottom]="hasBorder && !last">
+        <ds-wrapper-list-element [object]="object" [index]="i"></ds-wrapper-list-element>
       </li>
   </ul>
 </ds-pagination>
diff --git a/src/app/shared/object-list/object-list.component.spec.ts b/src/app/shared/object-list/object-list.component.spec.ts
index 7e0b704a19846cfdef6a6b954be1be4719304449..12ad032e981eb3db6b2ccdcc5e902994015fe425 100644
--- a/src/app/shared/object-list/object-list.component.spec.ts
+++ b/src/app/shared/object-list/object-list.component.spec.ts
@@ -6,7 +6,7 @@ import { By } from '@angular/platform-browser';
 describe('ObjectListComponent', () => {
   let comp: ObjectListComponent;
   let fixture: ComponentFixture<ObjectListComponent>;
-  const testEvent = {test: 'test'}
+  const testEvent = {test: 'test'};
 
   beforeEach(async(() => {
     TestBed.configureTestingModule({
diff --git a/src/app/shared/object-list/object-list.component.ts b/src/app/shared/object-list/object-list.component.ts
index b0296d5ae157a1424200ab2e2728c56337cee0fd..afc376034fff9cf09291523b93d19608de7c32b6 100644
--- a/src/app/shared/object-list/object-list.component.ts
+++ b/src/app/shared/object-list/object-list.component.ts
@@ -25,6 +25,7 @@ export class ObjectListComponent {
 
   @Input() config: PaginationComponentOptions;
   @Input() sortConfig: SortOptions;
+  @Input() hasBorder = false;
   @Input() hideGear = false;
   @Input() hidePagerWhenSinglePage = true;
   private _objects: RemoteData<PaginatedList<ListableObject>>;
diff --git a/src/app/shared/object-list/search-result-list-element/collection-search-result/collection-search-result-list-element.component.spec.ts b/src/app/shared/object-list/search-result-list-element/collection-search-result/collection-search-result-list-element.component.spec.ts
index e897071a001e07da882f67a7262ca92eb980617e..7f5aaf5d9cd6d0263550e43b4c73fb1ccd84b30b 100644
--- a/src/app/shared/object-list/search-result-list-element/collection-search-result/collection-search-result-list-element.component.spec.ts
+++ b/src/app/shared/object-list/search-result-list-element/collection-search-result/collection-search-result-list-element.component.spec.ts
@@ -17,7 +17,7 @@ const truncatableServiceStub: any = {
 
 const mockCollectionWithAbstract: CollectionSearchResult = new CollectionSearchResult();
 mockCollectionWithAbstract.hitHighlights = {};
-mockCollectionWithAbstract.dspaceObject = Object.assign(new Collection(), {
+mockCollectionWithAbstract.indexableObject = Object.assign(new Collection(), {
   metadata: {
     'dc.description.abstract': [
       {
@@ -30,7 +30,7 @@ mockCollectionWithAbstract.dspaceObject = Object.assign(new Collection(), {
 
 const mockCollectionWithoutAbstract: CollectionSearchResult = new CollectionSearchResult();
 mockCollectionWithoutAbstract.hitHighlights = {};
-mockCollectionWithoutAbstract.dspaceObject = Object.assign(new Collection(), {
+mockCollectionWithoutAbstract.indexableObject = Object.assign(new Collection(), {
   metadata: {
     'dc.title': [
       {
@@ -63,7 +63,7 @@ describe('CollectionSearchResultListElementComponent', () => {
 
   describe('When the collection has an abstract', () => {
     beforeEach(() => {
-      collectionSearchResultListElementComponent.dso = mockCollectionWithAbstract.dspaceObject;
+      collectionSearchResultListElementComponent.dso = mockCollectionWithAbstract.indexableObject;
       fixture.detectChanges();
     });
 
@@ -75,7 +75,7 @@ describe('CollectionSearchResultListElementComponent', () => {
 
   describe('When the collection has no abstract', () => {
     beforeEach(() => {
-      collectionSearchResultListElementComponent.dso = mockCollectionWithoutAbstract.dspaceObject;
+      collectionSearchResultListElementComponent.dso = mockCollectionWithoutAbstract.indexableObject;
       fixture.detectChanges();
     });
 
diff --git a/src/app/shared/object-list/search-result-list-element/collection-search-result/collection-search-result-list-element.component.ts b/src/app/shared/object-list/search-result-list-element/collection-search-result/collection-search-result-list-element.component.ts
index 264f9ce1a980cdb54fc3318def361a1efbf841e4..2205155bbd47972d70798261e6e3abe396d52c16 100644
--- a/src/app/shared/object-list/search-result-list-element/collection-search-result/collection-search-result-list-element.component.ts
+++ b/src/app/shared/object-list/search-result-list-element/collection-search-result/collection-search-result-list-element.component.ts
@@ -4,8 +4,8 @@ import { renderElementsFor } from '../../../object-collection/shared/dso-element
 
 import { SearchResultListElementComponent } from '../search-result-list-element.component';
 import { Collection } from '../../../../core/shared/collection.model';
+import { SetViewMode } from '../../../view-mode';
 import { CollectionSearchResult } from '../../../object-collection/shared/collection-search-result.model';
-import { ViewMode } from '../../../../core/shared/view-mode.model';
 
 @Component({
   selector: 'ds-collection-search-result-list-element',
@@ -13,5 +13,5 @@ import { ViewMode } from '../../../../core/shared/view-mode.model';
   templateUrl: 'collection-search-result-list-element.component.html'
 })
 
-@renderElementsFor(CollectionSearchResult, ViewMode.List)
+@renderElementsFor(CollectionSearchResult, SetViewMode.List)
 export class CollectionSearchResultListElementComponent extends SearchResultListElementComponent<CollectionSearchResult, Collection> {}
diff --git a/src/app/shared/object-list/search-result-list-element/community-search-result/community-search-result-list-element.component.spec.ts b/src/app/shared/object-list/search-result-list-element/community-search-result/community-search-result-list-element.component.spec.ts
index 75d5966767e2c7517c3c0e0d66f3fdca0c4f2ad5..691a69dde4dd32af909e2e154bba74e091a7ce76 100644
--- a/src/app/shared/object-list/search-result-list-element/community-search-result/community-search-result-list-element.component.spec.ts
+++ b/src/app/shared/object-list/search-result-list-element/community-search-result/community-search-result-list-element.component.spec.ts
@@ -17,7 +17,7 @@ const truncatableServiceStub: any = {
 
 const mockCommunityWithAbstract: CommunitySearchResult = new CommunitySearchResult();
 mockCommunityWithAbstract.hitHighlights = {};
-mockCommunityWithAbstract.dspaceObject = Object.assign(new Community(), {
+mockCommunityWithAbstract.indexableObject = Object.assign(new Community(), {
   metadata: {
     'dc.description.abstract': [
       {
@@ -30,7 +30,7 @@ mockCommunityWithAbstract.dspaceObject = Object.assign(new Community(), {
 
 const mockCommunityWithoutAbstract: CommunitySearchResult = new CommunitySearchResult();
 mockCommunityWithoutAbstract.hitHighlights = {};
-mockCommunityWithoutAbstract.dspaceObject = Object.assign(new Community(), {
+mockCommunityWithoutAbstract.indexableObject = Object.assign(new Community(), {
   metadata: {
     'dc.title': [
       {
@@ -63,7 +63,7 @@ describe('CommunitySearchResultListElementComponent', () => {
 
   describe('When the community has an abstract', () => {
     beforeEach(() => {
-      communitySearchResultListElementComponent.dso = mockCommunityWithAbstract.dspaceObject;
+      communitySearchResultListElementComponent.dso = mockCommunityWithAbstract.indexableObject;
       fixture.detectChanges();
     });
 
@@ -75,7 +75,7 @@ describe('CommunitySearchResultListElementComponent', () => {
 
   describe('When the community has no abstract', () => {
     beforeEach(() => {
-      communitySearchResultListElementComponent.dso = mockCommunityWithoutAbstract.dspaceObject;
+      communitySearchResultListElementComponent.dso = mockCommunityWithoutAbstract.indexableObject;
       fixture.detectChanges();
     });
 
diff --git a/src/app/shared/object-list/search-result-list-element/community-search-result/community-search-result-list-element.component.ts b/src/app/shared/object-list/search-result-list-element/community-search-result/community-search-result-list-element.component.ts
index 227ff9a45d1ab484b46a7c7512a9104194c4c571..3f7e08d11c81e480a86e8af5457f904e16a9ade0 100644
--- a/src/app/shared/object-list/search-result-list-element/community-search-result/community-search-result-list-element.component.ts
+++ b/src/app/shared/object-list/search-result-list-element/community-search-result/community-search-result-list-element.component.ts
@@ -4,8 +4,8 @@ import { renderElementsFor } from '../../../object-collection/shared/dso-element
 
 import { SearchResultListElementComponent } from '../search-result-list-element.component';
 import { Community } from '../../../../core/shared/community.model';
+import { SetViewMode } from '../../../view-mode';
 import { CommunitySearchResult } from '../../../object-collection/shared/community-search-result.model';
-import { ViewMode } from '../../../../core/shared/view-mode.model';
 
 @Component({
   selector: 'ds-community-search-result-list-element',
@@ -13,7 +13,7 @@ import { ViewMode } from '../../../../core/shared/view-mode.model';
   templateUrl: 'community-search-result-list-element.component.html'
 })
 
-@renderElementsFor(CommunitySearchResult, ViewMode.List)
+@renderElementsFor(CommunitySearchResult, SetViewMode.List)
 export class CommunitySearchResultListElementComponent extends SearchResultListElementComponent<CommunitySearchResult, Community> {
 
 }
diff --git a/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.html b/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.html
index 6261220459e8ccbc4c2649127a8aa0f7df853f77..a2617a956f2a316ced6592f8cb2fb45e51c1bf26 100644
--- a/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.html
+++ b/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.html
@@ -1,24 +1,4 @@
-<ds-truncatable [id]="dso.id">
-        <a
-            [routerLink]="['/items/' + dso.id]" class="lead"
-            [innerHTML]="firstMetadataValue('dc.title')"></a>
-  <span class="text-muted">
-    <ds-truncatable-part [id]="dso.id" [minLines]="1">
-            (<span *ngIf="dso.hasMetadata('dc.publisher')" class="item-list-publisher"
-                   [innerHTML]="firstMetadataValue('dc.publisher') + ', '"></span><span
-      *ngIf="dso.hasMetadata('dc.date.issued')" class="item-list-date"
-      [innerHTML]="firstMetadataValue('dc.date.issued')"></span>)
-            <span *ngIf="dso.hasMetadata(['dc.contributor.author', 'dc.creator', 'dc.contributor.*'])"
-                  class="item-list-authors">
-                    <span *ngFor="let author of allMetadataValues(['dc.contributor.author', 'dc.creator', 'dc.contributor.*']); let last=last;">
-                        <span [innerHTML]="author"><span [innerHTML]="author"></span></span>
-                    </span>
-            </span>
-        </ds-truncatable-part>
-  </span>
-            <div *ngIf="dso.hasMetadata('dc.description.abstract')" class="item-list-abstract">
-                <ds-truncatable-part [id]="dso.id" [minLines]="3"><span
-                        [innerHTML]="firstMetadataValue('dc.description.abstract')"></span>
-                </ds-truncatable-part>
-            </div>
-</ds-truncatable>
+<div *ngIf="object && object.indexableObject && object.indexableObject.firstMetadataValue('relationship.type') as type">
+  <span class="badge badge-light">{{ type.toLowerCase() + '.listelement.badge' | translate }}</span>
+</div>
+<ds-item-type-switcher [object]="object" [viewMode]="viewMode"></ds-item-type-switcher>
diff --git a/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.spec.ts b/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.spec.ts
index 8567fc17825d1e7bee78649013e18727d8da09e4..ef4660fdd9740849a86f39fe3d43bbbbc1f661d3 100644
--- a/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.spec.ts
+++ b/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.spec.ts
@@ -8,6 +8,7 @@ import { Item } from '../../../../core/shared/item.model';
 import { TruncatableService } from '../../../truncatable/truncatable.service';
 import { NoopAnimationsModule } from '@angular/platform-browser/animations';
 import { ItemSearchResult } from '../../../object-collection/shared/item-search-result.model';
+import { TranslateModule } from '@ngx-translate/core';
 
 let itemSearchResultListElementComponent: ItemSearchResultListElementComponent;
 let fixture: ComponentFixture<ItemSearchResultListElementComponent>;
@@ -16,29 +17,25 @@ const truncatableServiceStub: any = {
   isCollapsed: (id: number) => observableOf(true),
 };
 
-const mockItemWithAuthorAndDate: ItemSearchResult = new ItemSearchResult();
-mockItemWithAuthorAndDate.hitHighlights = {};
-mockItemWithAuthorAndDate.dspaceObject = Object.assign(new Item(), {
+const type = 'authorOfPublication';
+
+const mockItemWithRelationshipType: ItemSearchResult = new ItemSearchResult();
+mockItemWithRelationshipType.hitHighlights = {};
+mockItemWithRelationshipType.indexableObject = Object.assign(new Item(), {
   bitstreams: observableOf({}),
   metadata: {
-    'dc.contributor.author': [
+    'relationship.type': [
       {
         language: 'en_US',
-        value: 'Smith, Donald'
-      }
-    ],
-    'dc.date.issued': [
-      {
-        language: null,
-        value: '2015-06-26'
+        value: type
       }
     ]
   }
 });
 
-const mockItemWithoutAuthorAndDate: ItemSearchResult = new ItemSearchResult();
-mockItemWithoutAuthorAndDate.hitHighlights = {};
-mockItemWithoutAuthorAndDate.dspaceObject = Object.assign(new Item(), {
+const mockItemWithoutRelationshipType: ItemSearchResult = new ItemSearchResult();
+mockItemWithoutRelationshipType.hitHighlights = {};
+mockItemWithoutRelationshipType.indexableObject = Object.assign(new Item(), {
   bitstreams: observableOf({}),
   metadata: {
     'dc.title': [
@@ -46,12 +43,6 @@ mockItemWithoutAuthorAndDate.dspaceObject = Object.assign(new Item(), {
         language: 'en_US',
         value: 'This is just another title'
       }
-    ],
-    'dc.type': [
-      {
-        language: null,
-        value: 'Article'
-      }
     ]
   }
 });
@@ -59,11 +50,11 @@ mockItemWithoutAuthorAndDate.dspaceObject = Object.assign(new Item(), {
 describe('ItemSearchResultListElementComponent', () => {
   beforeEach(async(() => {
     TestBed.configureTestingModule({
-      imports: [NoopAnimationsModule],
+      imports: [TranslateModule.forRoot(), NoopAnimationsModule],
       declarations: [ItemSearchResultListElementComponent, TruncatePipe],
       providers: [
         { provide: TruncatableService, useValue: truncatableServiceStub },
-        { provide: 'objectElementProvider', useValue: (mockItemWithoutAuthorAndDate) }
+        { provide: 'objectElementProvider', useValue: (mockItemWithoutRelationshipType) }
       ],
       schemas: [NO_ERRORS_SCHEMA]
     }).overrideComponent(ItemSearchResultListElementComponent, {
@@ -76,51 +67,28 @@ describe('ItemSearchResultListElementComponent', () => {
     itemSearchResultListElementComponent = fixture.componentInstance;
   }));
 
-  describe('When the item has an author', () => {
-    beforeEach(() => {
-      itemSearchResultListElementComponent.dso = mockItemWithAuthorAndDate.dspaceObject;
-      fixture.detectChanges();
-    });
-
-    it('should show the author paragraph', () => {
-      const itemAuthorField = fixture.debugElement.query(By.css('span.item-list-authors'));
-      expect(itemAuthorField).not.toBeNull();
-    });
-  });
-
-  describe('When the item has no author', () => {
-    beforeEach(() => {
-      itemSearchResultListElementComponent.dso = mockItemWithoutAuthorAndDate.dspaceObject;
-      fixture.detectChanges();
-    });
-
-    it('should not show the author paragraph', () => {
-      const itemAuthorField = fixture.debugElement.query(By.css('span.item-list-authors'));
-      expect(itemAuthorField).toBeNull();
-    });
-  });
-
-  describe('When the item has an issuedate', () => {
+  describe('When the item has a relationship type', () => {
     beforeEach(() => {
-      itemSearchResultListElementComponent.dso = mockItemWithAuthorAndDate.dspaceObject;
+      itemSearchResultListElementComponent.object = mockItemWithRelationshipType;
       fixture.detectChanges();
     });
 
-    it('should show the issuedate span', () => {
-      const itemAuthorField = fixture.debugElement.query(By.css('span.item-list-date'));
-      expect(itemAuthorField).not.toBeNull();
+    it('should show the relationship type badge', () => {
+      const badge = fixture.debugElement.query(By.css('span.badge'));
+      console.log(itemSearchResultListElementComponent.dso);
+      expect(badge.nativeElement.textContent).toContain(type.toLowerCase());
     });
   });
 
-  describe('When the item has no issuedate', () => {
+  describe('When the item has no relationship type', () => {
     beforeEach(() => {
-      itemSearchResultListElementComponent.dso = mockItemWithoutAuthorAndDate.dspaceObject;
+      itemSearchResultListElementComponent.object = mockItemWithoutRelationshipType;
       fixture.detectChanges();
     });
 
-    it('should not show the issuedate span', () => {
-      const dateField = fixture.debugElement.query(By.css('span.item-list-date'));
-      expect(dateField).toBeNull();
+    it('should not show a badge', () => {
+      const badge = fixture.debugElement.query(By.css('span.badge'));
+      expect(badge).toBeNull();
     });
   });
 });
diff --git a/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.ts b/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.ts
index a0cbc2469c6ced4b40bca3940e3c51f80300dec8..5bd3c8ff5a833420a68b1ff4399ba600a6695ee2 100644
--- a/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.ts
+++ b/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.ts
@@ -1,11 +1,14 @@
 import { Component } from '@angular/core';
+import { DSpaceObject } from '../../../../core/shared/dspace-object.model';
+import { Item } from '../../../../core/shared/item.model';
+import { focusBackground } from '../../../animations/focus';
+import { hasValue } from '../../../empty.util';
 
 import { renderElementsFor } from '../../../object-collection/shared/dso-element-decorator';
-import { SearchResultListElementComponent } from '../search-result-list-element.component';
-import { Item } from '../../../../core/shared/item.model';
 import { ItemSearchResult } from '../../../object-collection/shared/item-search-result.model';
-import { focusBackground } from '../../../animations/focus';
-import { ViewMode } from '../../../../core/shared/view-mode.model';
+import { SetViewMode } from '../../../view-mode';
+import { SearchResultListElementComponent } from '../search-result-list-element.component';
+import { ItemViewMode } from '../../../items/item-type-decorator';
 
 @Component({
   selector: 'ds-item-search-result-list-element',
@@ -15,6 +18,8 @@ import { ViewMode } from '../../../../core/shared/view-mode.model';
 
 })
 
-@renderElementsFor(ItemSearchResult, ViewMode.List)
+@renderElementsFor(ItemSearchResult, SetViewMode.List)
 export class ItemSearchResultListElementComponent extends SearchResultListElementComponent<ItemSearchResult, Item> {
+  viewMode = ItemViewMode.Element;
+
 }
diff --git a/src/app/shared/object-list/search-result-list-element/search-result-list-element.component.ts b/src/app/shared/object-list/search-result-list-element/search-result-list-element.component.ts
index 525d39e798d723b2764b533621c2fa75e247522b..227d375f2a32a0d4b564c2108e8fcb76c77576c7 100644
--- a/src/app/shared/object-list/search-result-list-element/search-result-list-element.component.ts
+++ b/src/app/shared/object-list/search-result-list-element/search-result-list-element.component.ts
@@ -3,6 +3,7 @@ import { Observable } from 'rxjs';
 
 import { SearchResult } from '../../../+search-page/search-result.model';
 import { DSpaceObject } from '../../../core/shared/dspace-object.model';
+import { hasValue } from '../../empty.util';
 import { ListableObject } from '../../object-collection/shared/listable-object.model';
 import { AbstractListableElementComponent } from '../../object-collection/shared/object-collection-element/abstract-listable-element.component';
 import { TruncatableService } from '../../truncatable/truncatable.service';
@@ -16,9 +17,11 @@ import { Metadata } from '../../../core/shared/metadata.utils';
 export class SearchResultListElementComponent<T extends SearchResult<K>, K extends DSpaceObject> extends AbstractListableElementComponent<T> {
   dso: K;
 
-  public constructor(@Inject('objectElementProvider') public listable: ListableObject, private truncatableService: TruncatableService) {
+  public constructor(@Inject('objectElementProvider') public listable: ListableObject, protected truncatableService: TruncatableService) {
     super(listable);
-    this.dso = this.object.dspaceObject;
+    if (hasValue(this.object)) {
+      this.dso = this.object.indexableObject;
+    }
   }
 
   /**
diff --git a/src/app/shared/object-list/wrapper-list-element/wrapper-list-element.component.ts b/src/app/shared/object-list/wrapper-list-element/wrapper-list-element.component.ts
index e95def7cf7401a1081bae3a56df5fba1c20e23aa..17e6f0fd851f3c3c3f7e40666f4a1b31d5457ba7 100644
--- a/src/app/shared/object-list/wrapper-list-element/wrapper-list-element.component.ts
+++ b/src/app/shared/object-list/wrapper-list-element/wrapper-list-element.component.ts
@@ -1,8 +1,8 @@
 import { Component, Injector, Input, OnInit } from '@angular/core';
+import { SetViewMode } from '../../view-mode';
 import { GenericConstructor } from '../../../core/shared/generic-constructor';
 import { rendersDSOType } from '../../object-collection/shared/dso-element-decorator'
 import { ListableObject } from '../../object-collection/shared/listable-object.model';
-import { ViewMode } from '../../../core/shared/view-mode.model';
 
 @Component({
   selector: 'ds-wrapper-list-element',
@@ -11,19 +11,23 @@ import { ViewMode } from '../../../core/shared/view-mode.model';
 })
 export class WrapperListElementComponent implements OnInit {
   @Input() object: ListableObject;
+  @Input() index: number;
   objectInjector: Injector;
 
   constructor(private injector: Injector) {}
 
   ngOnInit(): void {
     this.objectInjector = Injector.create({
-        providers: [{ provide: 'objectElementProvider', useFactory: () => (this.object), deps:[] }],
+        providers: [
+          { provide: 'objectElementProvider', useFactory: () => (this.object), deps:[] },
+          { provide: 'indexElementProvider', useFactory: () => (this.index), deps:[] }
+        ],
         parent: this.injector
     });
   }
 
   getListElement(): string {
     const f: GenericConstructor<ListableObject> = this.object.constructor as GenericConstructor<ListableObject>;
-    return rendersDSOType(f, ViewMode.List);
+    return rendersDSOType(f, SetViewMode.List);
   }
 }
diff --git a/src/app/shared/pagination/pagination-component-options.model.ts b/src/app/shared/pagination/pagination-component-options.model.ts
index 30ed2becd24f009101c9e5b4fee95d5df69df321..4f8a3c5c8cbe68cb58175cf5802ec6ecc3516c6f 100644
--- a/src/app/shared/pagination/pagination-component-options.model.ts
+++ b/src/app/shared/pagination/pagination-component-options.model.ts
@@ -12,11 +12,19 @@ export class PaginationComponentOptions extends NgbPaginationConfig {
    */
   currentPage = 1;
 
+  /**
+   * Maximum number of pages to display.
+   */
+  maxSize = 10;
+
   /**
    * A number array that represents options for a context pagination limit.
    */
-  pageSizeOptions: number[] = [5, 10, 20, 40, 60, 80, 100];
+  pageSizeOptions: number[] = [1, 5, 10, 20, 40, 60, 80, 100];
 
+  /**
+   * Number of items per page.
+   */
   pageSize: number;
 
 }
diff --git a/src/app/shared/pagination/pagination.component.html b/src/app/shared/pagination/pagination.component.html
index 7336866e5c4ead38d3491a6ef0b6980370773fd8..22a58dd7fc94e645fe1f4aba4c4b37fd02bf7fca 100644
--- a/src/app/shared/pagination/pagination.component.html
+++ b/src/app/shared/pagination/pagination.component.html
@@ -1,7 +1,7 @@
 <div *ngIf="currentPageState == undefined || currentPageState == currentPage">
   <div  class="pagination-masked clearfix top">
     <div class="row">
-      <div class="col pagination-info">
+      <div *ngIf="!hidePaginationDetail" class="col-auto pagination-info">
         <span class="align-middle hidden-xs-down">{{ 'pagination.showing.label' | translate }}</span>
         <span class="align-middle" *ngIf="collectionSize">{{ 'pagination.showing.detail' | translate:getShowingDetails(collectionSize)}}</span>
       </div>
diff --git a/src/app/shared/pagination/pagination.component.spec.ts b/src/app/shared/pagination/pagination.component.spec.ts
index 7b340b2eed1f27bdc605f80cedac5810cf07aeb5..dfbef9123ab3b63dd707f63a6301068cf3966b82 100644
--- a/src/app/shared/pagination/pagination.component.spec.ts
+++ b/src/app/shared/pagination/pagination.component.spec.ts
@@ -262,7 +262,7 @@ describe('Pagination component', () => {
 
     changePage(testFixture, 3);
     tick();
-    expect(routerStub.navigate).toHaveBeenCalledWith([], { queryParams: { pageId: 'test', page: 3, pageSize: 10, sortDirection: 'ASC', sortField: 'dc.title' }, queryParamsHandling: 'merge' });
+    expect(routerStub.navigate).toHaveBeenCalledWith([], { queryParams: { pageId: 'test', page: '3', pageSize: 10, sortDirection: 'ASC', sortField: 'dc.title' }, queryParamsHandling: 'merge' });
 
   }));
 
diff --git a/src/app/shared/pagination/pagination.component.ts b/src/app/shared/pagination/pagination.component.ts
index 0f2907a7bfc71a52b989318d459bf7f50cba5d5d..e5cb1f448af47db72303c315789f451440729543 100644
--- a/src/app/shared/pagination/pagination.component.ts
+++ b/src/app/shared/pagination/pagination.component.ts
@@ -8,16 +8,19 @@ import {
   Output,
   ViewEncapsulation
 } from '@angular/core'
-
 import { ActivatedRoute, Router } from '@angular/router';
 
 import { Subscription, Observable } from 'rxjs';
+import { isNumeric } from 'rxjs/internal-compatibility';
+import { isEqual, isObject, transform } from 'lodash';
+
 import { HostWindowService } from '../host-window.service';
 import { HostWindowState } from '../host-window.reducer';
 import { PaginationComponentOptions } from './pagination-component-options.model';
 import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model';
 import { hasValue, isNotEmpty } from '../empty.util';
 import { PageInfo } from '../../core/shared/page-info.model';
+import { difference } from '../object.util';
 
 /**
  * The default pagination controls component.
@@ -76,11 +79,16 @@ export class PaginationComponent implements OnDestroy, OnInit {
   @Output() sortFieldChange: EventEmitter<string> = new EventEmitter<string>();
 
   /**
-   * An event fired when the sort field is changed.
+   * An event fired when the pagination is changed.
    * Event's payload equals to the newly selected sort field.
    */
   @Output() paginationChange: EventEmitter<any> = new EventEmitter<any>();
 
+  /**
+   * Option for hiding the pagination detail
+   */
+  @Input() public hidePaginationDetail = false;
+
   /**
    * Option for hiding the gear
    */
@@ -168,7 +176,7 @@ export class PaginationComponent implements OnDestroy, OnInit {
     this.subs.push(this.route.queryParams
       .subscribe((queryParams) => {
         if (this.isEmptyPaginationParams(queryParams)) {
-          this.initializeConfig();
+          this.initializeConfig(queryParams);
         } else {
           this.currentQueryParams = queryParams;
           const fixedProperties = this.validateParams(queryParams);
@@ -197,7 +205,7 @@ export class PaginationComponent implements OnDestroy, OnInit {
   /**
    * Initializes all default variables
    */
-  private initializeConfig() {
+  private initializeConfig(queryParams: any = {}) {
     // Set initial values
     this.id = this.paginationOptions.id || null;
     this.pageSizeOptions = this.paginationOptions.pageSizeOptions;
@@ -207,13 +215,13 @@ export class PaginationComponent implements OnDestroy, OnInit {
       this.sortDirection = this.sortOptions.direction;
       this.sortField = this.sortOptions.field;
     }
-    this.currentQueryParams = {
+    this.currentQueryParams = Object.assign({}, queryParams, {
       pageId: this.id,
       page: this.currentPage,
       pageSize: this.pageSize,
       sortDirection: this.sortDirection,
       sortField: this.sortField
-    };
+    });
   }
 
   /**
@@ -235,7 +243,7 @@ export class PaginationComponent implements OnDestroy, OnInit {
    *    The page being navigated to.
    */
   public doPageChange(page: number) {
-    this.updateRoute({ page: page });
+    this.updateRoute({ page: page.toString() });
   }
 
   /**
@@ -333,10 +341,23 @@ export class PaginationComponent implements OnDestroy, OnInit {
    * Method to update the route parameters
    */
   private updateRoute(params: {}) {
-    this.router.navigate([], {
-      queryParams: Object.assign({}, this.currentQueryParams, params),
-      queryParamsHandling: 'merge'
-    });
+    if (isNotEmpty(difference(params, this.currentQueryParams))) {
+      this.router.navigate([], {
+        queryParams: Object.assign({}, this.currentQueryParams, params),
+        queryParamsHandling: 'merge'
+      });
+    }
+  }
+
+  private difference(object, base) {
+    const changes = (o, b) => {
+      return transform(o, (result, value, key) => {
+        if (!isEqual(value, b[key]) && isNotEmpty(value)) {
+          result[key] = (isObject(value) && isObject(b[key])) ? changes(value, b[key]) : value;
+        }
+      });
+    };
+    return changes(object, base);
   }
 
   /**
@@ -418,7 +439,7 @@ export class PaginationComponent implements OnDestroy, OnInit {
    */
   private validatePage(page: any): number {
     let result = this.currentPage;
-    if (!isNaN(page)) {
+    if (isNumeric(page)) {
       result = +page;
     }
     return result;
diff --git a/src/app/shared/roles/role.directive.ts b/src/app/shared/roles/role.directive.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d71e520e351cff1aeb12ee45f90be063af5f8313
--- /dev/null
+++ b/src/app/shared/roles/role.directive.ts
@@ -0,0 +1,114 @@
+import {
+  ChangeDetectorRef,
+  Directive,
+  Input,
+  OnChanges,
+  OnDestroy,
+  SimpleChanges,
+  TemplateRef,
+  ViewContainerRef
+} from '@angular/core';
+
+import { combineLatest, Observable, Subscription } from 'rxjs';
+import { filter, first, map } from 'rxjs/operators';
+
+import { hasValue } from '../empty.util';
+import { RoleService } from '../../core/roles/role.service';
+import { RoleType } from '../../core/roles/role-types';
+
+@Directive({
+  selector: '[dsShowOnlyForRole],[dsShowExceptForRole]'
+})
+/**
+ * Structural Directive for showing or hiding a template based on current user role
+ */
+export class RoleDirective implements OnChanges, OnDestroy {
+
+  /**
+   * The role or list of roles that can show template
+   */
+  @Input() dsShowOnlyForRole: RoleType | RoleType[];
+
+  /**
+   * The role or list of roles that cannot show template
+   */
+  @Input() dsShowExceptForRole: RoleType | RoleType[];
+
+  private subs: Subscription[] = [];
+
+  constructor(
+    private roleService: RoleService,
+    private viewContainer: ViewContainerRef,
+    private changeDetector: ChangeDetectorRef,
+    private templateRef: TemplateRef<any>
+  ) {
+  }
+
+  ngOnChanges(changes: SimpleChanges): void {
+    const onlyChanges = changes.dsShowOnlyForRole;
+    const exceptChanges = changes.dsShowExceptForRole;
+    this.hasRoles(this.dsShowOnlyForRole);
+    if (changes.dsShowOnlyForRole) {
+      this.validateOnly()
+    } else if (changes.dsShowExceptForRole) {
+      this.validateExcept()
+    }
+  }
+
+  ngOnDestroy(): void {
+    this.subs
+      .filter((subscription) => hasValue(subscription))
+      .forEach((subscription) => subscription.unsubscribe());
+  }
+
+  /**
+   * Show template in view container
+   */
+  private showTemplateBlockInView(): void {
+    this.viewContainer.clear();
+    if (!this.templateRef) {
+      return;
+    }
+
+    this.viewContainer.createEmbeddedView(this.templateRef);
+    this.changeDetector.markForCheck();
+  }
+
+  /**
+   * Validate the list of roles that can show template
+   */
+  private validateOnly(): void  {
+    this.subs.push(this.hasRoles(this.dsShowOnlyForRole).pipe(filter((hasRole) => hasRole))
+      .subscribe((hasRole) => {
+        this.showTemplateBlockInView();
+      }));
+  }
+
+  /**
+   * Validate the list of roles that cannot show template
+   */
+  private validateExcept(): void  {
+    this.subs.push(this.hasRoles(this.dsShowExceptForRole).pipe(filter((hasRole) => !hasRole))
+      .subscribe((hasRole) => {
+        this.showTemplateBlockInView();
+      }));
+  }
+
+  /**
+   * Check if current user role is included in the specified role list
+   *
+   * @param roles
+   *    The role or the list of roles
+   * @returns {Observable<boolean>}
+   *    observable of true if current user role is included in the specified role list, observable of false otherwise
+   */
+  private hasRoles(roles: RoleType | RoleType[]): Observable<boolean> {
+    const toValidate: RoleType[] = (Array.isArray(roles)) ? roles : [roles];
+    const checks: Array<Observable<boolean>> = toValidate.map((role) => this.roleService.checkRole(role));
+
+    return combineLatest(checks).pipe(
+      map((permissions: boolean[]) => permissions.includes(true)),
+      first()
+    )
+  }
+}
diff --git a/src/app/shared/search-form/search-form.component.spec.ts b/src/app/shared/search-form/search-form.component.spec.ts
index b164abee1ff3a301110a8f167255d8ac8726f6b1..a60aeb8054d11b2e405e2921595f2f7ee1e0505a 100644
--- a/src/app/shared/search-form/search-form.component.spec.ts
+++ b/src/app/shared/search-form/search-form.component.spec.ts
@@ -8,6 +8,7 @@ import { RouterTestingModule } from '@angular/router/testing';
 import { Community } from '../../core/shared/community.model';
 import { TranslateModule } from '@ngx-translate/core';
 import { DSpaceObject } from '../../core/shared/dspace-object.model';
+import { SearchService } from '../../+search-page/search-service/search.service';
 
 describe('SearchFormComponent', () => {
   let comp: SearchFormComponent;
@@ -18,6 +19,12 @@ describe('SearchFormComponent', () => {
   beforeEach(async(() => {
     TestBed.configureTestingModule({
       imports: [FormsModule, RouterTestingModule, TranslateModule.forRoot()],
+      providers: [
+        {
+          provide: SearchService,
+          useValue: {}
+        }
+      ],
       declarations: [SearchFormComponent]
     }).compileComponents();
   }));
diff --git a/src/app/shared/search-form/search-form.component.ts b/src/app/shared/search-form/search-form.component.ts
index 21a90daed471cd62dff7c1e90263e8423953ff04..10c3a3ede7bd803f68e0fd21c288470b9bfce268 100644
--- a/src/app/shared/search-form/search-form.component.ts
+++ b/src/app/shared/search-form/search-form.component.ts
@@ -3,6 +3,8 @@ import { DSpaceObject } from '../../core/shared/dspace-object.model';
 import { Router } from '@angular/router';
 import { hasValue, isNotEmpty } from '../empty.util';
 import { QueryParamsHandling } from '@angular/router/src/config';
+import { MYDSPACE_ROUTE } from '../../+my-dspace-page/my-dspace-page.component';
+import { SearchService } from '../../+search-page/search-service/search.service';
 
 /**
  * This component renders a simple item page.
@@ -25,6 +27,11 @@ export class SearchFormComponent {
    */
   @Input() query: string;
 
+  /**
+   * True when the search component should show results on the current page
+   */
+  @Input() inPlaceSearch;
+
   /**
    * The currently selected scope object's UUID
    */
@@ -38,7 +45,7 @@ export class SearchFormComponent {
    */
   @Input() scopes: DSpaceObject[];
 
-  constructor(private router: Router) {
+  constructor(private router: Router, private searchService: SearchService) {
   }
 
   /**
@@ -62,14 +69,9 @@ export class SearchFormComponent {
    * @param data Updated parameters
    */
   updateSearch(data: any) {
-    const newUrl = hasValue(this.currentUrl) ? this.currentUrl : '/search';
-    let handling: QueryParamsHandling = '' ;
-    if (this.currentUrl === '/search') {
-      handling = 'merge';
-    }
-    this.router.navigate([newUrl], {
+    this.router.navigate(this.getSearchLinkParts(), {
       queryParams: Object.assign({}, { page: 1 }, data),
-      queryParamsHandling: handling
+      queryParamsHandling: 'merge'
     });
   }
 
@@ -80,4 +82,23 @@ export class SearchFormComponent {
     return isNotEmpty(object);
   }
 
+  /**
+   * @returns {string} The base path to the search page, or the current page when inPlaceSearch is true
+   */
+  public getSearchLink(): string {
+    if (this.inPlaceSearch) {
+      return './';
+    }
+    return this.searchService.getSearchLink();
+  }
+
+  /**
+   * @returns {string[]} The base path to the search page, or the current page when inPlaceSearch is true, split in separate pieces
+   */
+  public getSearchLinkParts(): string[] {
+    if (this.inPlaceSearch) {
+      return [];
+    }
+    return this.getSearchLink().split('/');
+  }
 }
diff --git a/src/app/shared/services/route.service.spec.ts b/src/app/shared/services/route.service.spec.ts
index 7d249cb12ae1252aedb9543b210b83e3f40d8af8..c9b3710ee6c74ae9eecd01a298779f32165ede03 100644
--- a/src/app/shared/services/route.service.spec.ts
+++ b/src/app/shared/services/route.service.spec.ts
@@ -29,6 +29,9 @@ describe('RouteService', () => {
     select: jasmine.createSpy('select')
   });
 
+  const router = new MockRouter();
+  router.setParams(convertToParamMap(paramObject));
+
   paramObject[paramName1] = paramValue1;
   paramObject[paramName2] = [paramValue2a, paramValue2b];
 
@@ -42,7 +45,7 @@ describe('RouteService', () => {
             queryParamMap: observableOf(convertToParamMap(paramObject))
           },
         },
-        { provide: Router, useValue: new MockRouter() },
+        { provide: Router, useValue: router },
         { provide: Store, useValue: store },
       ]
     });
diff --git a/src/app/shared/services/route.service.ts b/src/app/shared/services/route.service.ts
index 48fc3ba0b004082fe1c6f2155c21baaa04eda9b9..a94b7e56daa8c24d760967d630c5118cdec5db4e 100644
--- a/src/app/shared/services/route.service.ts
+++ b/src/app/shared/services/route.service.ts
@@ -1,9 +1,16 @@
+import { distinctUntilChanged, filter, map, mergeMap } from 'rxjs/operators';
 import { Injectable } from '@angular/core';
-import { ActivatedRoute, NavigationEnd, Params, Router, } from '@angular/router';
+import {
+  ActivatedRoute,
+  NavigationEnd,
+  Params,
+  Router,
+  RouterStateSnapshot,
+} from '@angular/router';
 
-import { distinctUntilChanged, filter, map } from 'rxjs/operators';
 import { Observable } from 'rxjs';
 import { select, Store } from '@ngrx/store';
+import { isEqual } from 'lodash';
 
 import { AppState } from '../../app.reducer';
 import { AddUrlToHistoryAction } from '../history/history.actions';
@@ -14,8 +21,11 @@ import { historySelector } from '../history/selectors';
  */
 @Injectable()
 export class RouteService {
+  params: Observable<Params>;
 
   constructor(private route: ActivatedRoute, private router: Router, private store: Store<AppState>) {
+    this.subscribeToRouterParams();
+
   }
 
   /**
@@ -23,7 +33,7 @@ export class RouteService {
    * @param paramName The name of the parameter to look for
    */
   getQueryParameterValues(paramName: string): Observable<string[]> {
-    return this.route.queryParamMap.pipe(
+    return this.getQueryParamMap().pipe(
       map((params) => [...params.getAll(paramName)]),
       distinctUntilChanged((a, b) => JSON.stringify(a) === JSON.stringify(b))
     );
@@ -34,7 +44,7 @@ export class RouteService {
    * @param paramName The name of the parameter to look for
    */
   getQueryParameterValue(paramName: string): Observable<string> {
-    return this.route.queryParamMap.pipe(
+    return this.getQueryParamMap().pipe(
       map((params) => params.get(paramName)),
       distinctUntilChanged()
     );
@@ -45,7 +55,7 @@ export class RouteService {
    * @param paramName The name of the parameter to look for
    */
   hasQueryParam(paramName: string): Observable<boolean> {
-    return this.route.queryParamMap.pipe(
+    return this.getQueryParamMap().pipe(
       map((params) => params.has(paramName)),
       distinctUntilChanged()
     );
@@ -57,18 +67,26 @@ export class RouteService {
    * @param paramValue The value of the parameter to look for
    */
   hasQueryParamWithValue(paramName: string, paramValue: string): Observable<boolean> {
-    return this.route.queryParamMap.pipe(
+    return this.getQueryParamMap().pipe(
       map((params) => params.getAll(paramName).indexOf(paramValue) > -1),
       distinctUntilChanged()
     );
   }
 
+  getRouteParameterValue(paramName: string): Observable<string> {
+    return this.params.pipe(map((params) => params[paramName]),distinctUntilChanged(),);
+  }
+
+  getRouteDataValue(datafield: string): Observable<any> {
+    return this.route.data.pipe(map((data) => data[datafield]),distinctUntilChanged(),);
+  }
+
   /**
    * Retrieves all query parameters of which the parameter name starts with the given prefix
    * @param prefix The prefix of the parameter name to look for
    */
   getQueryParamsWithPrefix(prefix: string): Observable<Params> {
-    return this.route.queryParamMap.pipe(
+    return this.getQueryParamMap().pipe(
       map((qparams) => {
         const params = {};
         qparams.keys
@@ -82,6 +100,19 @@ export class RouteService {
     );
   }
 
+  public getQueryParamMap(): Observable<any> {
+    return this.route.queryParamMap.pipe(
+      map((paramMap) => {
+        const snapshot: RouterStateSnapshot = this.router.routerState.snapshot;
+        // Due to an Angular bug, sometimes change of QueryParam is not detected so double checks with route snapshot
+        if (!isEqual(paramMap, snapshot.root.queryParamMap)) {
+          return snapshot.root.queryParamMap;
+        } else {
+          return paramMap;
+        }
+      }))
+  }
+
   public saveRouting(): void {
     this.router.events
       .pipe(filter((event) => event instanceof NavigationEnd))
@@ -90,6 +121,18 @@ export class RouteService {
       });
   }
 
+  subscribeToRouterParams() {
+    this.params = this.router.events.pipe(
+      mergeMap((event) => {
+        let active = this.route;
+        while (active.firstChild) {
+          active = active.firstChild;
+        }
+        return active.params;
+      })
+    );
+  }
+
   public getHistory(): Observable<string[]> {
     return this.store.pipe(select(historySelector));
   }
@@ -99,5 +142,4 @@ export class RouteService {
       map((history: string[]) => history[history.length - 2] || '')
     );
   }
-
 }
diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts
index 4b0f7834374f11e6844e845c8ed25b84ef0b6e26..4331b5e7e0ec6dd8e5c86c34dddbacc6aab8fd7d 100644
--- a/src/app/shared/shared.module.ts
+++ b/src/app/shared/shared.module.ts
@@ -9,6 +9,16 @@ import { NgbDatepickerModule, NgbModule, NgbTimepickerModule, NgbTypeaheadModule
 import { TranslateModule } from '@ngx-translate/core';
 
 import { NgxPaginationModule } from 'ngx-pagination';
+import { ItemTypeSwitcherComponent } from './items/switcher/item-type-switcher.component';
+import { OrgUnitMetadataListElementComponent } from './object-list/item-list-element/item-types/orgunit/orgunit-metadata-list-element.component';
+import { TypedItemSearchResultListElementComponent } from './object-list/item-list-element/item-types/typed-item-search-result-list-element.component';
+import { PublicationListElementComponent } from './object-list/item-list-element/item-types/publication/publication-list-element.component';
+import { OrgUnitListElementComponent } from './object-list/item-list-element/item-types/orgunit/orgunit-list-element.component';
+import { PersonListElementComponent } from './object-list/item-list-element/item-types/person/person-list-element.component';
+import { ProjectListElementComponent } from './object-list/item-list-element/item-types/project/project-list-element.component';
+import { JournalListElementComponent } from './object-list/item-list-element/item-types/journal/journal-list-element.component';
+import { JournalVolumeListElementComponent } from './object-list/item-list-element/item-types/journal-volume/journal-volume-list-element.component';
+import { JournalIssueListElementComponent } from './object-list/item-list-element/item-types/journal-issue/journal-issue-list-element.component';
 
 import { FileUploadModule } from 'ng2-file-upload';
 
@@ -77,6 +87,20 @@ import { DsDatePickerComponent } from './form/builder/ds-dynamic-form-ui/models/
 import { DsDynamicLookupComponent } from './form/builder/ds-dynamic-form-ui/models/lookup/dynamic-lookup.component';
 import { MockAdminGuard } from './mocks/mock-admin-guard.service';
 import { AlertComponent } from './alert/alert.component';
+import { MyDSpaceResultListElementComponent } from './object-list/my-dspace-result-list-element/my-dspace-result-list-element.component';
+import { MyDSpaceResultDetailElementComponent } from './object-detail/my-dspace-result-detail-element/my-dspace-result-detail-element.component';
+import { ClaimedTaskActionsComponent } from './mydspace-actions/claimed-task/claimed-task-actions.component';
+import { PoolTaskActionsComponent } from './mydspace-actions/pool-task/pool-task-actions.component';
+import { ObjectDetailComponent } from './object-detail/object-detail.component';
+import { WrapperDetailElementComponent } from './object-detail/wrapper-detail-element/wrapper-detail-element.component';
+import { ItemDetailPreviewComponent } from './object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component';
+import { MyDSpaceItemStatusComponent } from './object-collection/shared/mydspace-item-status/my-dspace-item-status.component';
+import { WorkspaceitemActionsComponent } from './mydspace-actions/workspaceitem/workspaceitem-actions.component';
+import { WorkflowitemActionsComponent } from './mydspace-actions/workflowitem/workflowitem-actions.component';
+import { ItemSubmitterComponent } from './object-collection/shared/mydspace-item-submitter/item-submitter.component';
+import { ItemActionsComponent } from './mydspace-actions/item/item-actions.component';
+import { ClaimedTaskActionsApproveComponent } from './mydspace-actions/claimed-task/approve/claimed-task-actions-approve.component';
+import { ClaimedTaskActionsRejectComponent } from './mydspace-actions/claimed-task/reject/claimed-task-actions-reject.component';
 import { ObjNgFor } from './utils/object-ngfor.pipe';
 import { BrowseByComponent } from './browse-by/browse-by.component';
 import { BrowseEntryListElementComponent } from './object-list/browse-entry-list-element/browse-entry-list-element.component';
@@ -89,11 +113,16 @@ import { ObjectKeysPipe } from './utils/object-keys-pipe';
 import { MomentModule } from 'ngx-moment';
 import { AuthorityConfidenceStateDirective } from './authority-confidence/authority-confidence-state.directive';
 import { MenuModule } from './menu/menu.module';
+import { LangSwitchComponent } from './lang-switch/lang-switch.component';
+import { PlainTextMetadataListElementComponent } from './object-list/metadata-representation-list-element/plain-text/plain-text-metadata-list-element.component';
+import { ItemMetadataListElementComponent } from './object-list/metadata-representation-list-element/item/item-metadata-list-element.component';
+import { TooltipModule } from 'ngx-bootstrap';
+import { PersonMetadataListElementComponent } from './object-list/item-list-element/item-types/person/person-metadata-list-element.component';
+import { MetadataRepresentationListElementComponent } from './object-list/metadata-representation-list-element/metadata-representation-list-element.component';
 import { ComColFormComponent } from './comcol-forms/comcol-form/comcol-form.component';
 import { CreateComColPageComponent } from './comcol-forms/create-comcol-page/create-comcol-page.component';
 import { EditComColPageComponent } from './comcol-forms/edit-comcol-page/edit-comcol-page.component';
 import { DeleteComColPageComponent } from './comcol-forms/delete-comcol-page/delete-comcol-page.component';
-import { LangSwitchComponent } from './lang-switch/lang-switch.component';
 import { ObjectValuesPipe } from './utils/object-values-pipe';
 import { InListValidator } from './utils/in-list-validator.directive';
 import { AutoFocusDirective } from './utils/auto-focus.directive';
@@ -110,7 +139,13 @@ import { ItemSearchResultListElementComponent } from './object-list/search-resul
 import { EditItemSelectorComponent } from './dso-selector/modal-wrappers/edit-item-selector/edit-item-selector.component';
 import { EditCommunitySelectorComponent } from './dso-selector/modal-wrappers/edit-community-selector/edit-community-selector.component';
 import { EditCollectionSelectorComponent } from './dso-selector/modal-wrappers/edit-collection-selector/edit-collection-selector.component';
-import { DSOSelectorModalWrapperComponent } from './dso-selector/modal-wrappers/dso-selector-modal-wrapper.component';
+import { ItemListPreviewComponent } from './object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component';
+import { MetadataFieldWrapperComponent } from '../+item-page/field-components/metadata-field-wrapper/metadata-field-wrapper.component';
+import { MetadataValuesComponent } from '../+item-page/field-components/metadata-values/metadata-values.component';
+import { RoleDirective } from './roles/role.directive';
+import { UserMenuComponent } from './auth-nav-menu/user-menu/user-menu.component';
+import { ClaimedTaskActionsReturnToPoolComponent } from './mydspace-actions/claimed-task/return-to-pool/claimed-task-actions-return-to-pool.component';
+import { ItemDetailPreviewFieldComponent } from './object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview-field/item-detail-preview-field.component';
 
 const MODULES = [
   // Do NOT include UniversalModule, HttpModule, or JsonpModule here
@@ -135,6 +170,10 @@ const MODULES = [
   MenuModule
 ];
 
+const ROOT_MODULES = [
+  TooltipModule.forRoot()
+];
+
 const PIPES = [
   // put shared pipes here
   EnumKeysPipe,
@@ -153,6 +192,7 @@ const COMPONENTS = [
   // put shared components here
   AlertComponent,
   AuthNavMenuComponent,
+  UserMenuComponent,
   ChipsComponent,
   ComcolPageContentComponent,
   ComcolPageHeaderComponent,
@@ -182,9 +222,11 @@ const COMPONENTS = [
   LogOutComponent,
   NumberPickerComponent,
   ObjectListComponent,
+  ObjectDetailComponent,
+  ObjectGridComponent,
   AbstractListableElementComponent,
   WrapperListElementComponent,
-  ObjectGridComponent,
+  WrapperDetailElementComponent,
   WrapperGridElementComponent,
   ObjectCollectionComponent,
   PaginationComponent,
@@ -193,6 +235,19 @@ const COMPONENTS = [
   GridThumbnailComponent,
   UploaderComponent,
   WrapperListElementComponent,
+  ItemListPreviewComponent,
+  MyDSpaceItemStatusComponent,
+  ItemSubmitterComponent,
+  ItemDetailPreviewComponent,
+  ItemDetailPreviewFieldComponent,
+  ClaimedTaskActionsComponent,
+  ClaimedTaskActionsApproveComponent,
+  ClaimedTaskActionsRejectComponent,
+  ClaimedTaskActionsReturnToPoolComponent,
+  ItemActionsComponent,
+  PoolTaskActionsComponent,
+  WorkflowitemActionsComponent,
+  WorkspaceitemActionsComponent,
   ViewModeSwitchComponent,
   TruncatableComponent,
   TruncatablePartComponent,
@@ -208,6 +263,9 @@ const COMPONENTS = [
   CommunitySearchResultListElementComponent,
   CollectionSearchResultListElementComponent,
   ItemSearchResultListElementComponent,
+  TypedItemSearchResultListElementComponent,
+  ItemTypeSwitcherComponent,
+  BrowseByComponent
 ];
 
 const ENTRY_COMPONENTS = [
@@ -215,6 +273,7 @@ const ENTRY_COMPONENTS = [
   ItemListElementComponent,
   CollectionListElementComponent,
   CommunityListElementComponent,
+  MyDSpaceResultListElementComponent,
   SearchResultListElementComponent,
   CommunitySearchResultListElementComponent,
   CollectionSearchResultListElementComponent,
@@ -223,7 +282,18 @@ const ENTRY_COMPONENTS = [
   CollectionGridElementComponent,
   CommunityGridElementComponent,
   SearchResultGridElementComponent,
+  PublicationListElementComponent,
+  PersonListElementComponent,
+  PersonMetadataListElementComponent,
+  OrgUnitMetadataListElementComponent,
+  OrgUnitListElementComponent,
+  ProjectListElementComponent,
+  JournalListElementComponent,
+  JournalVolumeListElementComponent,
+  JournalIssueListElementComponent,
   BrowseEntryListElementComponent,
+  MyDSpaceResultDetailElementComponent,
+  SearchResultGridElementComponent,
   DsDynamicListComponent,
   DsDynamicLookupComponent,
   DsDynamicScrollableDropdownComponent,
@@ -243,6 +313,15 @@ const ENTRY_COMPONENTS = [
   EditCommunitySelectorComponent,
   EditCollectionSelectorComponent,
   EditItemSelectorComponent,
+  StartsWithTextComponent,
+  PlainTextMetadataListElementComponent,
+  ItemMetadataListElementComponent,
+  MetadataRepresentationListElementComponent
+];
+
+const SHARED_ITEM_PAGE_COMPONENTS = [
+  MetadataFieldWrapperComponent,
+  MetadataValuesComponent,
 ];
 
 const PROVIDERS = [
@@ -261,18 +340,21 @@ const DIRECTIVES = [
   ClickOutsideDirective,
   AuthorityConfidenceStateDirective,
   InListValidator,
-  AutoFocusDirective
+  AutoFocusDirective,
+  RoleDirective
 ];
 
 @NgModule({
   imports: [
-    ...MODULES
+    ...MODULES,
+    ...ROOT_MODULES
   ],
   declarations: [
     ...PIPES,
     ...COMPONENTS,
     ...DIRECTIVES,
     ...ENTRY_COMPONENTS,
+    ...SHARED_ITEM_PAGE_COMPONENTS
   ],
   providers: [
     ...PROVIDERS
@@ -281,6 +363,7 @@ const DIRECTIVES = [
     ...MODULES,
     ...PIPES,
     ...COMPONENTS,
+    ...SHARED_ITEM_PAGE_COMPONENTS,
     ...DIRECTIVES
   ],
   entryComponents: [
diff --git a/src/app/shared/testing/eperson-mock.ts b/src/app/shared/testing/eperson-mock.ts
index ef27f4983d42ed526047c5d9a1b818838971bf61..c822fc15d68b10d56dccc27f77121b53ffbf7d43 100644
--- a/src/app/shared/testing/eperson-mock.ts
+++ b/src/app/shared/testing/eperson-mock.ts
@@ -13,26 +13,30 @@ export const EPersonMock: EPerson = Object.assign(new EPerson(),{
   id: 'testid',
   uuid: 'testid',
   type: 'eperson',
-  metadata: [
-    {
-      key: 'dc.title',
-      language: null,
-      value: 'User Test'
-    },
-    {
-      key: 'eperson.firstname',
-      language: null,
-      value: 'User'
-    },
-    {
-      key: 'eperson.lastname',
-      language: null,
-      value: 'Test'
-    },
-    {
-      key: 'eperson.language',
-      language: null,
-      value: 'en'
-    }
-  ]
+  metadata: {
+    'dc.title': [
+      {
+        language: null,
+        value: 'User Test'
+      }
+    ],
+    'eperson.firstname': [
+      {
+        language: null,
+        value: 'User'
+      }
+    ],
+    'eperson.lastname': [
+      {
+        language: null,
+        value: 'Test'
+      },
+    ],
+    'eperson.language': [
+      {
+        language: null,
+        value: 'en'
+      },
+    ]
+  }
 });
diff --git a/src/app/shared/testing/route-service-stub.ts b/src/app/shared/testing/route-service-stub.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e3cdd9d8c67b56351ddbd0f18fa77a48167c446f
--- /dev/null
+++ b/src/app/shared/testing/route-service-stub.ts
@@ -0,0 +1,32 @@
+import { of as observableOf } from 'rxjs/internal/observable/of';
+
+export const routeServiceStub: any = {
+  /* tslint:disable:no-empty */
+  hasQueryParamWithValue: (param: string, value: string) => {
+  },
+  hasQueryParam: (param: string) => {
+  },
+  removeQueryParameterValue: (param: string, value: string) => {
+  },
+  addQueryParameterValue: (param: string, value: string) => {
+  },
+  getQueryParameterValues: (param: string) => {
+    return observableOf({});
+  },
+  getQueryParamsWithPrefix: (param: string) => {
+    return observableOf({});
+  },
+  getQueryParamMap: () => {
+    return observableOf(new Map())
+  },
+  getQueryParameterValue: () => {
+    return observableOf({})
+  },
+  getRouteParameterValue: (param) => {
+    return observableOf('')
+  },
+  getRouteDataValue: (param) => {
+    return observableOf({})
+  }
+  /* tslint:enable:no-empty */
+};
diff --git a/src/app/shared/testing/router-stub.ts b/src/app/shared/testing/router-stub.ts
index 31c09c41e30031da9f4f5943b811967c6d14e066..8630e16b2e83dbfbd250db5aa4d2dfbfda57a868 100644
--- a/src/app/shared/testing/router-stub.ts
+++ b/src/app/shared/testing/router-stub.ts
@@ -1,9 +1,11 @@
-
+import { of as observableOf } from 'rxjs';
 export class RouterStub {
   url: string;
+  routeReuseStrategy = {shouldReuseRoute: {}};
   //noinspection TypeScriptUnresolvedFunction
   navigate = jasmine.createSpy('navigate');
   parseUrl = jasmine.createSpy('parseUrl');
+  events = observableOf({});
   navigateByUrl(url): void {
     this.url = url;
   }
diff --git a/src/app/shared/testing/search-configuration-service-stub.ts b/src/app/shared/testing/search-configuration-service-stub.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4c9402afb1411f176479e387db1d9f37e8b4ddf9
--- /dev/null
+++ b/src/app/shared/testing/search-configuration-service-stub.ts
@@ -0,0 +1,19 @@
+import { BehaviorSubject, of as observableOf } from 'rxjs';
+
+export class SearchConfigurationServiceStub {
+
+  private searchOptions: BehaviorSubject<any> = new BehaviorSubject<any>({});
+  private paginatedSearchOptions: BehaviorSubject<any> = new BehaviorSubject<any>({});
+
+  getCurrentFrontendFilters() {
+    return observableOf([]);
+  }
+
+  getCurrentScope(a) {
+    return observableOf('test-id');
+  }
+
+  getCurrentConfiguration(a) {
+    return observableOf(a);
+  }
+}
diff --git a/src/app/shared/testing/search-service-stub.ts b/src/app/shared/testing/search-service-stub.ts
index 2a46e42ef581c8cce3d363a475d455740cefaa08..d886604ef2ee309231a78788388be5902cff674f 100644
--- a/src/app/shared/testing/search-service-stub.ts
+++ b/src/app/shared/testing/search-service-stub.ts
@@ -1,22 +1,22 @@
 import {of as observableOf,  Observable ,  BehaviorSubject } from 'rxjs';
-import { ViewMode } from '../../core/shared/view-mode.model';
+import { SetViewMode } from '../view-mode';
 
 export class SearchServiceStub {
 
-  private _viewMode: ViewMode;
+  private _viewMode: SetViewMode;
   private subject?: BehaviorSubject<any> = new BehaviorSubject(this.testViewMode);
 
   viewMode = this.subject.asObservable();
 
   constructor(private searchLink: string = '/search') {
-    this.setViewMode(ViewMode.List);
+    this.setViewMode(SetViewMode.List);
   }
 
-  getViewMode(): Observable<ViewMode> {
+  getViewMode(): Observable<SetViewMode> {
     return this.viewMode;
   }
 
-  setViewMode(viewMode: ViewMode) {
+  setViewMode(viewMode: SetViewMode) {
     this.testViewMode = viewMode;
   }
 
@@ -24,11 +24,11 @@ export class SearchServiceStub {
     return null;
   }
 
-  get testViewMode(): ViewMode {
+  get testViewMode(): SetViewMode {
     return this._viewMode;
   }
 
-  set testViewMode(viewMode: ViewMode) {
+  set testViewMode(viewMode: SetViewMode) {
     this._viewMode = viewMode;
     this.subject.next(viewMode);
   }
diff --git a/src/app/shared/truncatable/truncatable.component.html b/src/app/shared/truncatable/truncatable.component.html
index c03e93c2cec45ce3d96a701b43fe075c5c465f2e..b524e5e754ff5ce16258c92c73232c4d743e8627 100644
--- a/src/app/shared/truncatable/truncatable.component.html
+++ b/src/app/shared/truncatable/truncatable.component.html
@@ -1,3 +1,3 @@
-<div dsDragClick (actualClick)="toggle()" (mouseenter)="hoverExpand()" (mouseleave)="hoverCollapse">
+<div dsDragClick (actualClick)="toggle()" (mouseenter)="hoverExpand()" (mouseleave)="hoverCollapse()">
     <ng-content></ng-content>
-</div>
\ No newline at end of file
+</div>
diff --git a/src/app/shared/uploader/uploader.component.html b/src/app/shared/uploader/uploader.component.html
index 2ec1321afcd7ac7b27842b083087911696b90edb..5168404c83b2ef633ed870168490b688f96d836b 100644
--- a/src/app/shared/uploader/uploader.component.html
+++ b/src/app/shared/uploader/uploader.component.html
@@ -18,7 +18,7 @@
          [uploader]="uploader"
          (fileOver)="fileOverBase($event)"
          class="well ds-base-drop-zone mt-1 mb-3 text-muted">
-      <p class="text-center m-0 pt-2" [hidden]="uploader?.queue?.length !== 0">
+      <p class="text-center m-0 p-0 d-flex justify-content-center align-items-center" *ngIf="uploader?.queue?.length === 0">
         <span><i class="fas fa-cloud-upload" aria-hidden="true"></i> {{dropMsg | translate}} {{'uploader.or' | translate}}
           <label class="btn btn-link m-0 p-0">
             <input class="d-none" type="file" ng2FileSelect [uploader]="uploader" multiple  />
diff --git a/src/app/shared/uploader/uploader.component.ts b/src/app/shared/uploader/uploader.component.ts
index 641901b4880a729fb192d540c2c251aa111875f9..ad52f4a93f9f6446a884c41c47f32a126a80cb07 100644
--- a/src/app/shared/uploader/uploader.component.ts
+++ b/src/app/shared/uploader/uploader.component.ts
@@ -1,5 +1,3 @@
-
-import {of as observableOf,  Observable } from 'rxjs';
 import {
   ChangeDetectionStrategy,
   ChangeDetectorRef,
@@ -11,6 +9,7 @@ import {
   ViewEncapsulation,
 } from '@angular/core'
 
+import { of as observableOf } from 'rxjs';
 import { FileUploader } from 'ng2-file-upload';
 import { uniqueId } from 'lodash';
 import { ScrollToConfigOptions, ScrollToService } from '@nicky-lenaers/ngx-scroll-to';
@@ -115,6 +114,9 @@ export class UploaderComponent {
     this.uploader.onAfterAddingFile = ((item) => {
       item.withCredentials = false;
     });
+    if (isUndefined(this.onBeforeUpload)) {
+      this.onBeforeUpload = () => {return};
+    }
     this.uploader.onBeforeUploadItem = () => {
       this.onBeforeUpload();
       this.isOverDocumentDropZone = observableOf(false);
diff --git a/src/app/shared/view-mode-switch/view-mode-switch.component.html b/src/app/shared/view-mode-switch/view-mode-switch.component.html
index 893047557834d725620e550df3712c97ac108671..905cf29baccbf327d19ac749bd43afe8db44f1ab 100644
--- a/src/app/shared/view-mode-switch/view-mode-switch.component.html
+++ b/src/app/shared/view-mode-switch/view-mode-switch.component.html
@@ -1,5 +1,6 @@
 <div class="btn-group" data-toggle="buttons">
-    <a routerLink="."
+    <a *ngIf="isToShow(viewModeEnum.List)"
+       routerLink="."
        [queryParams]="{view: 'list'}"
        queryParamsHandling="merge"
        (click)="switchViewTo(viewModeEnum.List)"
@@ -8,13 +9,24 @@
        class="btn btn-secondary">
           <i class="fas fa-list" title="{{'search.view-switch.show-list' | translate}}"></i>
     </a>
-    <a routerLink="."
+    <a *ngIf="isToShow(viewModeEnum.Grid)"
+       routerLink="."
        [queryParams]="{view: 'grid'}"
        queryParamsHandling="merge"
        (click)="switchViewTo(viewModeEnum.Grid)"
        routerLinkActive="active"
-       [class.active]="currentMode !== viewModeEnum.List"
+       [class.active]="currentMode === viewModeEnum.Grid"
        class="btn btn-secondary">
           <i class="fas fa-th-large" title="{{'search.view-switch.show-grid' | translate}}"></i>
     </a>
-</div>
\ No newline at end of file
+    <a *ngIf="isToShow(viewModeEnum.Detail)"
+       routerLink="."
+       [queryParams]="{view: 'detail'}"
+       queryParamsHandling="merge"
+       (click)="switchViewTo(viewModeEnum.Detail)"
+       routerLinkActive="active"
+       [class.active]="currentMode === viewModeEnum.Detail"
+       class="btn btn-secondary">
+      <i class="far fa-square" title="{{'search.view-switch.show-detail' | translate}}"></i>
+    </a>
+</div>
diff --git a/src/app/shared/view-mode-switch/view-mode-switch.component.spec.ts b/src/app/shared/view-mode-switch/view-mode-switch.component.spec.ts
index 1211b4bc888941a80573858e54b8d88b66da8d30..2fe405de3f2e4a4296d5261e5fe560e77f97cd9b 100644
--- a/src/app/shared/view-mode-switch/view-mode-switch.component.spec.ts
+++ b/src/app/shared/view-mode-switch/view-mode-switch.component.spec.ts
@@ -8,8 +8,8 @@ import { ChangeDetectionStrategy, Component } from '@angular/core';
 
 import { SearchService } from '../../+search-page/search-service/search.service';
 import { ViewModeSwitchComponent } from './view-mode-switch.component';
+import { SetViewMode } from '../view-mode';
 import { SearchServiceStub } from '../testing/search-service-stub';
-import { ViewMode } from '../../core/shared/view-mode.model';
 
 @Component({ template: '' })
 class DummyComponent { }
@@ -55,19 +55,19 @@ describe('ViewModeSwitchComponent', () => {
   });
 
   it('should set list button as active when on list mode', fakeAsync(() => {
-    searchService.setViewMode(ViewMode.List);
+    searchService.setViewMode(SetViewMode.List);
     tick();
     fixture.detectChanges();
-    expect(comp.currentMode).toBe(ViewMode.List);
+    expect(comp.currentMode).toBe(SetViewMode.List);
     expect(listButton.classList).toContain('active');
     expect(gridButton.classList).not.toContain('active');
   }));
 
   it('should set grid button as active when on grid mode', fakeAsync(() => {
-    searchService.setViewMode(ViewMode.Grid);
+    searchService.setViewMode(SetViewMode.Grid);
     tick();
     fixture.detectChanges();
-    expect(comp.currentMode).toBe(ViewMode.Grid);
+    expect(comp.currentMode).toBe(SetViewMode.Grid);
     expect(listButton.classList).not.toContain('active');
     expect(gridButton.classList).toContain('active');
   }));
diff --git a/src/app/shared/view-mode-switch/view-mode-switch.component.ts b/src/app/shared/view-mode-switch/view-mode-switch.component.ts
index 07c47435ffe14d7c075f490bbd7d61179bb7005d..dc355c6409c1c0caa60e2588f92197717416173b 100644
--- a/src/app/shared/view-mode-switch/view-mode-switch.component.ts
+++ b/src/app/shared/view-mode-switch/view-mode-switch.component.ts
@@ -1,7 +1,10 @@
+import { Component, Input, OnDestroy, OnInit } from '@angular/core';
+
 import { Subscription } from 'rxjs';
-import { Component, OnInit, OnDestroy } from '@angular/core';
-import { SearchService } from './../../+search-page/search-service/search.service';
+
+import { SearchService } from '../../+search-page/search-service/search.service';
 import { ViewMode } from '../../core/shared/view-mode.model';
+import { isEmpty } from '../empty.util';
 
 /**
  * Component to switch between list and grid views.
@@ -12,6 +15,13 @@ import { ViewMode } from '../../core/shared/view-mode.model';
   templateUrl: './view-mode-switch.component.html'
 })
 export class ViewModeSwitchComponent implements OnInit, OnDestroy {
+  @Input() viewModeList: ViewMode[];
+
+  /**
+   * True when the search component should show results on the current page
+   */
+  @Input() inPlaceSearch;
+
   currentMode: ViewMode = ViewMode.List;
   viewModeEnum = ViewMode;
   private sub: Subscription;
@@ -20,13 +30,17 @@ export class ViewModeSwitchComponent implements OnInit, OnDestroy {
   }
 
   ngOnInit(): void {
+    if (isEmpty(this.viewModeList)) {
+      this.viewModeList = [ViewMode.List, ViewMode.Grid];
+    }
+
     this.sub = this.searchService.getViewMode().subscribe((viewMode: ViewMode) => {
       this.currentMode = viewMode;
     });
   }
 
   switchViewTo(viewMode: ViewMode) {
-    this.searchService.setViewMode(viewMode);
+    this.searchService.setViewMode(viewMode, this.getSearchLinkParts());
   }
 
   ngOnDestroy() {
@@ -34,4 +48,29 @@ export class ViewModeSwitchComponent implements OnInit, OnDestroy {
       this.sub.unsubscribe();
     }
   }
+
+  isToShow(viewMode: ViewMode) {
+    return this.viewModeList && this.viewModeList.includes(viewMode);
+  }
+
+  /**
+   * @returns {string} The base path to the search page, or the current page when inPlaceSearch is true
+   */
+  public getSearchLink(): string {
+    if (this.inPlaceSearch) {
+      return './';
+    }
+    return this.searchService.getSearchLink();
+  }
+
+  /**
+   * @returns {string[]} The base path to the search page, or the current page when inPlaceSearch is true, split in separate pieces
+   */
+  public getSearchLinkParts(): string[] {
+    if (this.searchService) {
+      return [];
+    }
+    return this.getSearchLink().split('/');
+  }
+
 }
diff --git a/src/app/shared/view-mode.ts b/src/app/shared/view-mode.ts
new file mode 100644
index 0000000000000000000000000000000000000000..826d467d6f228dbfed9ef60a027b479cad3384de
--- /dev/null
+++ b/src/app/shared/view-mode.ts
@@ -0,0 +1,15 @@
+/**
+ * Enum used for defining the view-mode of a set of elements
+ * List   Display the elements in a (vertical) list
+ * Grid   Display the elements in a grid
+ */
+export enum SetViewMode {
+  List = 'list',
+  Grid = 'grid',
+  Detail = 'detail'
+}
+
+/**
+ * ViewMode refers to either a SetViewMode or ElementViewMode
+ */
+export type ViewMode = SetViewMode;
diff --git a/src/app/submission/form/footer/submission-form-footer.component.html b/src/app/submission/form/footer/submission-form-footer.component.html
index 0d58456b24900d62fc1949bf91754cb53d807622..4d4892b6dc8a1704b491c10667c0969e0f701f9a 100644
--- a/src/app/submission/form/footer/submission-form-footer.component.html
+++ b/src/app/submission/form/footer/submission-form-footer.component.html
@@ -17,7 +17,7 @@
       <span>{{'submission.general.save' | translate}}</span>
     </button>
     <button type="button"
-            class="btn btn-info ml-1 mr-1"
+            class="btn btn-info"
             id="saveForLater"
             [disabled]="(processingSaveStatus | async)"
             (click)="saveLater($event)">
diff --git a/src/app/thumbnail/thumbnail.component.html b/src/app/thumbnail/thumbnail.component.html
index 36f22d932c63724f4d431d1a11b60313c496ed59..87fd0251f55c45eea112c7db1210acbd32aa72b7 100644
--- a/src/app/thumbnail/thumbnail.component.html
+++ b/src/app/thumbnail/thumbnail.component.html
@@ -1,4 +1,4 @@
 <div class="thumbnail">
-  <img *ngIf="thumbnail && thumbnail.content" [src]="thumbnail.content" (error)="errorHandler($event)" class="img-fluid"/>
-  <img *ngIf="!thumbnail || !thumbnail.content" [src]="holderSource | dsSafeUrl" class="img-fluid"/>
+  <img [src]="src | dsSafeUrl" (error)="errorHandler($event)" class="img-fluid"/>
 </div>
+
diff --git a/src/app/thumbnail/thumbnail.component.scss b/src/app/thumbnail/thumbnail.component.scss
index da97dd7a62e610066229abae8f4c43a981b82780..938832770093130dadf3faa0c0fc92502c10ef8a 100644
--- a/src/app/thumbnail/thumbnail.component.scss
+++ b/src/app/thumbnail/thumbnail.component.scss
@@ -1 +1,4 @@
 @import '../../styles/variables.scss';
+img {
+  max-width: 100%;
+}
diff --git a/src/app/thumbnail/thumbnail.component.spec.ts b/src/app/thumbnail/thumbnail.component.spec.ts
index fc145d2397965f3c0c7aa9a8420664beae435627..f2be55d52cb5a32d370b75bada24e628b32af394 100644
--- a/src/app/thumbnail/thumbnail.component.spec.ts
+++ b/src/app/thumbnail/thumbnail.component.spec.ts
@@ -36,7 +36,7 @@ describe('ThumbnailComponent', () => {
   it('should display placeholder', () => {
     fixture.detectChanges();
     const image: HTMLElement = de.query(By.css('img')).nativeElement;
-    expect(image.getAttribute('src')).toBe(comp.holderSource);
+    expect(image.getAttribute('src')).toBe(comp.defaultImage);
   });
 
 });
diff --git a/src/app/thumbnail/thumbnail.component.ts b/src/app/thumbnail/thumbnail.component.ts
index cac1909b2b4cec7aa2a1f500f6b18258b3aeb264..9700e018218d8feff1b7ae2bdf76633872ce84c6 100644
--- a/src/app/thumbnail/thumbnail.component.ts
+++ b/src/app/thumbnail/thumbnail.component.ts
@@ -1,5 +1,6 @@
-import { Component, Input } from '@angular/core';
+import { Component, Input, OnInit } from '@angular/core';
 import { Bitstream } from '../core/shared/bitstream.model';
+import { hasValue } from '../shared/empty.util';
 
 /**
  * This component renders a given Bitstream as a thumbnail.
@@ -12,19 +13,26 @@ import { Bitstream } from '../core/shared/bitstream.model';
   styleUrls: ['./thumbnail.component.scss'],
   templateUrl: './thumbnail.component.html'
 })
-export class ThumbnailComponent {
+export class ThumbnailComponent implements OnInit {
 
   @Input() thumbnail: Bitstream;
 
-  data: any = {};
-
   /**
    * The default 'holder.js' image
    */
-  holderSource = 'data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2293%22%20height%3D%22120%22%20viewBox%3D%220%200%2093%20120%22%20preserveAspectRatio%3D%22none%22%3E%3C!--%0ASource%20URL%3A%20holder.js%2F93x120%3Ftext%3DNo%20Thumbnail%0ACreated%20with%20Holder.js%202.8.2.%0ALearn%20more%20at%20http%3A%2F%2Fholderjs.com%0A(c)%202012-2015%20Ivan%20Malopinsky%20-%20http%3A%2F%2Fimsky.co%0A--%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%3C!%5BCDATA%5B%23holder_1543e460b05%20text%20%7B%20fill%3A%23AAAAAA%3Bfont-weight%3Abold%3Bfont-family%3AArial%2C%20Helvetica%2C%20Open%20Sans%2C%20sans-serif%2C%20monospace%3Bfont-size%3A10pt%20%7D%20%5D%5D%3E%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_1543e460b05%22%3E%3Crect%20width%3D%2293%22%20height%3D%22120%22%20fill%3D%22%23EEEEEE%22%2F%3E%3Cg%3E%3Ctext%20x%3D%2235.6171875%22%20y%3D%2257%22%3ENo%3C%2Ftext%3E%3Ctext%20x%3D%2210.8125%22%20y%3D%2272%22%3EThumbnail%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E';
+  @Input() defaultImage? = 'data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2293%22%20height%3D%22120%22%20viewBox%3D%220%200%2093%20120%22%20preserveAspectRatio%3D%22none%22%3E%3C!--%0ASource%20URL%3A%20holder.js%2F93x120%3Ftext%3DNo%20Thumbnail%0ACreated%20with%20Holder.js%202.8.2.%0ALearn%20more%20at%20http%3A%2F%2Fholderjs.com%0A(c)%202012-2015%20Ivan%20Malopinsky%20-%20http%3A%2F%2Fimsky.co%0A--%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%3C!%5BCDATA%5B%23holder_1543e460b05%20text%20%7B%20fill%3A%23AAAAAA%3Bfont-weight%3Abold%3Bfont-family%3AArial%2C%20Helvetica%2C%20Open%20Sans%2C%20sans-serif%2C%20monospace%3Bfont-size%3A10pt%20%7D%20%5D%5D%3E%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_1543e460b05%22%3E%3Crect%20width%3D%2293%22%20height%3D%22120%22%20fill%3D%22%23EEEEEE%22%2F%3E%3Cg%3E%3Ctext%20x%3D%2235.6171875%22%20y%3D%2257%22%3ENo%3C%2Ftext%3E%3Ctext%20x%3D%2210.8125%22%20y%3D%2272%22%3EThumbnail%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E';
 
+  src: string;
   errorHandler(event) {
-    event.currentTarget.src = this.holderSource;
+    event.currentTarget.src = this.defaultImage;
+  }
+
+  ngOnInit(): void {
+      if (hasValue(this.thumbnail) && this.thumbnail.content) {
+        this.src = this.thumbnail.content;
+      } else {
+        this.src = this.defaultImage
+      }
   }
 
 }
diff --git a/src/main.browser.ts b/src/main.browser.ts
index 8409a96485372b5ab9920795bafbb3a02a7c3dcf..264399a4b843d457211e587eb927ea5c3539752c 100644
--- a/src/main.browser.ts
+++ b/src/main.browser.ts
@@ -27,7 +27,7 @@ export function main() {
 
   addGoogleAnalytics();
 
-  return platformBrowserDynamic().bootstrapModule(BrowserAppModule);
+  return platformBrowserDynamic().bootstrapModule(BrowserAppModule, {preserveWhitespaces:true});
 }
 
 function addGoogleAnalytics() {
diff --git a/src/styles/_custom_variables.scss b/src/styles/_custom_variables.scss
index 716002327a45bf17c6218e5308727cf2c15f49c6..be03d719c55e96acf4b739423ed607baac57b2c8 100644
--- a/src/styles/_custom_variables.scss
+++ b/src/styles/_custom_variables.scss
@@ -5,7 +5,7 @@ $button-height: $input-btn-padding-y * 2 + $input-btn-line-height + calculateRem
 $card-height-percentage:98%;
 $card-thumbnail-height:240px;
 $dropdown-menu-max-height: 200px;
-$drop-zone-area-height: 42px;
+$drop-zone-area-height: 44px;
 $drop-zone-area-z-index: 1025;
 $drop-zone-area-inner-z-index: 1021;
 $login-logo-height:72px;
diff --git a/src/tsconfig.browser.json b/src/tsconfig.browser.json
index f7140b9fe4d82fec7046682f4cb5455a183ad4bd..9eef264c94937a8b8f9bd611d0d4a53aeaf73484 100644
--- a/src/tsconfig.browser.json
+++ b/src/tsconfig.browser.json
@@ -1,6 +1,7 @@
 {
   "extends": "../tsconfig.json",
   "angularCompilerOptions": {
-    "entryModule": "./modules/app/browser-app.module#BrowserAppModule"
+    "entryModule": "./modules/app/browser-app.module#BrowserAppModule",
+    "preserveWhitespaces": true
   }
 }
diff --git a/src/tsconfig.server.aot.json b/src/tsconfig.server.aot.json
index de753624f39282f8a0216c0df89451cc01366b50..23f7854344c2c690cd495a117832f55f5c328fb1 100644
--- a/src/tsconfig.server.aot.json
+++ b/src/tsconfig.server.aot.json
@@ -1,7 +1,8 @@
 {
   "extends": "./tsconfig.server.json",
   "angularCompilerOptions": {
-    "entryModule": "./modules/app/server-app.module#ServerAppModule"
+    "entryModule": "./modules/app/server-app.module#ServerAppModule",
+    "preserveWhitespaces": true
   },
   "exclude": []
 }
diff --git a/src/tsconfig.server.json b/src/tsconfig.server.json
index 480b685a2adec7ff01595aa2b39e9a2c0231804e..9881f1de5e4889a7cef8b5a553b5d91492d958ec 100644
--- a/src/tsconfig.server.json
+++ b/src/tsconfig.server.json
@@ -1,6 +1,7 @@
 {
   "extends": "../tsconfig.json",
   "angularCompilerOptions": {
-    "entryModule": "./modules/app/server-app.module#ServerAppModule"
+    "entryModule": "./modules/app/server-app.module#ServerAppModule",
+    "preserveWhitespaces": true
   }
 }
diff --git a/webpack/run-webpack.js b/webpack/run-webpack.js
new file mode 100644
index 0000000000000000000000000000000000000000..93f17b4619fbf5a91b40c9735295a3ce621c3560
--- /dev/null
+++ b/webpack/run-webpack.js
@@ -0,0 +1,13 @@
+const path = require('path');
+const child_process = require('child_process');
+
+const heapSize = 4096;
+const webpackPath = path.join('node_modules', 'webpack', 'bin', 'webpack.js');
+
+const params = [
+  '--max_old_space_size=' + heapSize,
+  webpackPath,
+  ...process.argv.slice(2)
+];
+
+child_process.spawn('node', params, { stdio:'inherit' });