diff --git a/resources/i18n/en.json5 b/resources/i18n/en.json5
index 299c2afe634626e79f0f017cec35d230b8e2ac74..fc4c0aee5709a969da5b9458651c4a5d702a6cd0 100644
--- a/resources/i18n/en.json5
+++ b/resources/i18n/en.json5
@@ -244,6 +244,8 @@
 
   "collection.create.head": "Create a Collection",
 
+  "collection.create.notifications.success": "Successfully created the Collection",
+
   "collection.create.sub-head": "Create a Collection for Community {{ parent }}",
 
   "collection.delete.cancel": "Cancel",
@@ -302,6 +304,46 @@
 
 
 
+  "collection.edit.logo.label": "Collection logo",
+
+  "collection.edit.logo.notifications.add.error": "Uploading Collection logo failed. Please verify the content before retrying.",
+
+  "collection.edit.logo.notifications.add.success": "Upload Collection logo successful.",
+
+  "collection.edit.logo.notifications.delete.success.title": "Logo deleted",
+
+  "collection.edit.logo.notifications.delete.success.content": "Successfully deleted the collection's logo",
+
+  "collection.edit.logo.notifications.delete.error.title": "Error deleting logo",
+
+  "collection.edit.logo.upload": "Drop a Collection Logo to upload",
+
+
+
+  "collection.edit.notifications.success": "Successfully edited the Collection",
+
+  "collection.edit.return": "Return",
+
+
+
+  "collection.edit.tabs.curate.head": "Curate",
+
+  "collection.edit.tabs.curate.title": "Collection Edit - Curate",
+
+  "collection.edit.tabs.metadata.head": "Edit Metadata",
+
+  "collection.edit.tabs.metadata.title": "Collection Edit - Metadata",
+
+  "collection.edit.tabs.roles.head": "Assign Roles",
+
+  "collection.edit.tabs.roles.title": "Collection Edit - Roles",
+
+  "collection.edit.tabs.source.head": "Content Source",
+
+  "collection.edit.tabs.source.title": "Collection Edit - Content Source",
+
+
+
   "collection.form.abstract": "Short Description",
 
   "collection.form.description": "Introductory text (HTML)",
@@ -350,6 +392,8 @@
 
   "community.create.head": "Create a Community",
 
+  "community.create.notifications.success": "Successfully created the Community",
+
   "community.create.sub-head": "Create a Sub-Community for Community {{ parent }}",
 
   "community.delete.cancel": "Cancel",
@@ -368,6 +412,44 @@
 
   "community.edit.head": "Edit Community",
 
+
+
+  "community.edit.logo.label": "Community logo",
+
+  "community.edit.logo.notifications.add.error": "Uploading Community logo failed. Please verify the content before retrying.",
+
+  "community.edit.logo.notifications.add.success": "Upload Community logo successful.",
+
+  "community.edit.logo.notifications.delete.success.title": "Logo deleted",
+
+  "community.edit.logo.notifications.delete.success.content": "Successfully deleted the community's logo",
+
+  "community.edit.logo.notifications.delete.error.title": "Error deleting logo",
+
+  "community.edit.logo.upload": "Drop a Community Logo to upload",
+
+
+
+  "community.edit.notifications.success": "Successfully edited the Community",
+
+  "community.edit.return": "Return",
+
+
+
+  "community.edit.tabs.curate.head": "Curate",
+
+  "community.edit.tabs.curate.title": "Community Edit - Curate",
+
+  "community.edit.tabs.metadata.head": "Edit Metadata",
+
+  "community.edit.tabs.metadata.title": "Community Edit - Metadata",
+
+  "community.edit.tabs.roles.head": "Assign Roles",
+
+  "community.edit.tabs.roles.title": "Community Edit - Roles",
+
+
+
   "community.form.abstract": "Short Description",
 
   "community.form.description": "Introductory text (HTML)",
@@ -1771,7 +1853,7 @@
 
   "uploader.drag-message": "Drag & Drop your files here",
 
-  "uploader.or": ", or",
+  "uploader.or": ", or ",
 
   "uploader.processing": "Processing",
 
diff --git a/src/app/+collection-page/collection-form/collection-form.component.ts b/src/app/+collection-page/collection-form/collection-form.component.ts
index bc9eaab9947d81ea9806bca907c4d2e3bbfec993..59433e49a097746574b423b865c885105f5082a5 100644
--- a/src/app/+collection-page/collection-form/collection-form.component.ts
+++ b/src/app/+collection-page/collection-form/collection-form.component.ts
@@ -1,7 +1,19 @@
 import { Component, Input } from '@angular/core';
-import { DynamicFormControlModel, DynamicInputModel, DynamicTextAreaModel } from '@ng-dynamic-forms/core';
+import {
+  DynamicFormControlModel,
+  DynamicFormService,
+  DynamicInputModel,
+  DynamicTextAreaModel
+} from '@ng-dynamic-forms/core';
 import { Collection } from '../../core/shared/collection.model';
 import { ComColFormComponent } from '../../shared/comcol-forms/comcol-form/comcol-form.component';
+import { Location } from '@angular/common';
+import { TranslateService } from '@ngx-translate/core';
+import { NotificationsService } from '../../shared/notifications/notifications.service';
+import { CommunityDataService } from '../../core/data/community-data.service';
+import { AuthService } from '../../core/auth/auth.service';
+import { RequestService } from '../../core/data/request.service';
+import { ObjectCacheService } from '../../core/cache/object-cache.service';
 
 /**
  * Form used for creating and editing collections
@@ -20,7 +32,7 @@ export class CollectionFormComponent extends ComColFormComponent<Collection> {
   /**
    * @type {Collection.type} This is a collection-type form
    */
-  protected type = Collection.type;
+  type = Collection.type;
 
   /**
    * The dynamic form fields used for creating/editing a collection
@@ -63,4 +75,15 @@ export class CollectionFormComponent extends ComColFormComponent<Collection> {
       name: 'dc.description.provenance',
     }),
   ];
+
+  public constructor(protected location: Location,
+                     protected formService: DynamicFormService,
+                     protected translate: TranslateService,
+                     protected notificationsService: NotificationsService,
+                     protected authService: AuthService,
+                     protected dsoService: CommunityDataService,
+                     protected requestService: RequestService,
+                     protected objectCache: ObjectCacheService) {
+    super(location, formService, translate, notificationsService, authService, requestService, objectCache);
+  }
 }
diff --git a/src/app/+collection-page/collection-page-routing.module.ts b/src/app/+collection-page/collection-page-routing.module.ts
index 66c623657dd76a2006d1a7f3c8d06d7d5601784c..2df7997e1eb7cb42922f7b5af6529a7299673100 100644
--- a/src/app/+collection-page/collection-page-routing.module.ts
+++ b/src/app/+collection-page/collection-page-routing.module.ts
@@ -5,7 +5,6 @@ import { CollectionPageComponent } from './collection-page.component';
 import { CollectionPageResolver } from './collection-page.resolver';
 import { CreateCollectionPageComponent } from './create-collection-page/create-collection-page.component';
 import { AuthenticatedGuard } from '../core/auth/authenticated.guard';
-import { EditCollectionPageComponent } from './edit-collection-page/edit-collection-page.component';
 import { CreateCollectionPageGuard } from './create-collection-page/create-collection-page.guard';
 import { DeleteCollectionPageComponent } from './delete-collection-page/delete-collection-page.component';
 import { URLCombiner } from '../core/url-combiner/url-combiner';
@@ -39,12 +38,8 @@ const COLLECTION_EDIT_PATH = ':id/edit';
       },
       {
         path: COLLECTION_EDIT_PATH,
-        pathMatch: 'full',
-        component: EditCollectionPageComponent,
-        canActivate: [AuthenticatedGuard],
-        resolve: {
-          dso: CollectionPageResolver
-        }
+        loadChildren: './edit-collection-page/edit-collection-page.module#EditCollectionPageModule',
+        canActivate: [AuthenticatedGuard]
       },
       {
         path: ':id/delete',
diff --git a/src/app/+collection-page/collection-page.component.html b/src/app/+collection-page/collection-page.component.html
index 12d5c200fd04012df99b9d70e4a1be593a2f5f6d..98552ed40b707fd8448f86f863db7a6b89239b7d 100644
--- a/src/app/+collection-page/collection-page.component.html
+++ b/src/app/+collection-page/collection-page.component.html
@@ -5,15 +5,17 @@
             <div *ngIf="collectionRD?.payload as collection">
                 <ds-view-tracker [object]="collection"></ds-view-tracker>
                 <header class="comcol-header border-bottom mb-4 pb-4">
+                  <!-- Collection Name -->
+                <ds-comcol-page-header
+                        [name]="collection.name">
+                </ds-comcol-page-header>
                     <!-- Collection logo -->
                     <ds-comcol-page-logo *ngIf="logoRD$"
-                        [logo]="(logoRD$ | async)?.payload" [alternateText]="'Collection Logo'">
+                                     [logo]="(logoRD$ | async)?.payload"
+                                     [alternateText]="'Collection Logo'"
                         [alternateText]="'Collection Logo'">
                     </ds-comcol-page-logo>
-                <!-- Collection Name -->
-                <ds-comcol-page-header
-                        [name]="collection.name">
-                </ds-comcol-page-header>
+
                  <!-- Handle -->
                 <ds-comcol-page-handle
                         [content]="collection.handle"
diff --git a/src/app/+collection-page/collection-page.module.ts b/src/app/+collection-page/collection-page.module.ts
index d9e1d9465ee21ad7d9a83283fa847ca5e80f68ad..03daae68ae124ddae1abd27421e66b23a9589785 100644
--- a/src/app/+collection-page/collection-page.module.ts
+++ b/src/app/+collection-page/collection-page.module.ts
@@ -7,7 +7,6 @@ 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 { EditCollectionPageComponent } from './edit-collection-page/edit-collection-page.component';
 import { DeleteCollectionPageComponent } from './delete-collection-page/delete-collection-page.component';
 import { CollectionItemMapperComponent } from './collection-item-mapper/collection-item-mapper.component';
 import { SearchService } from '../core/shared/search/search.service';
@@ -23,11 +22,13 @@ import { StatisticsModule } from '../statistics/statistics.module';
   declarations: [
     CollectionPageComponent,
     CreateCollectionPageComponent,
-    EditCollectionPageComponent,
     DeleteCollectionPageComponent,
     CollectionFormComponent,
     CollectionItemMapperComponent
   ],
+  exports: [
+    CollectionFormComponent
+  ],
   providers: [
     SearchService,
   ]
diff --git a/src/app/+collection-page/create-collection-page/create-collection-page.component.html b/src/app/+collection-page/create-collection-page/create-collection-page.component.html
index b3f4361bc63ca755463dcde6b52265e165274a5d..800d2858461cf63c80a1b98eaccaa90ec1d21c55 100644
--- a/src/app/+collection-page/create-collection-page/create-collection-page.component.html
+++ b/src/app/+collection-page/create-collection-page/create-collection-page.component.html
@@ -4,5 +4,5 @@
             <h2 id="sub-header" class="border-bottom pb-2">{{'collection.create.sub-head' | translate:{ parent: (parentRD$| async)?.payload.name } }}</h2>
         </div>
     </div>
-    <ds-collection-form (submitForm)="onSubmit($event)"></ds-collection-form>
+    <ds-collection-form (submitForm)="onSubmit($event)" (finish)="navigateToNewPage()"></ds-collection-form>
 </div>
diff --git a/src/app/+collection-page/create-collection-page/create-collection-page.component.spec.ts b/src/app/+collection-page/create-collection-page/create-collection-page.component.spec.ts
index e223b11c6568ec422d950f9841c187778534e3a7..869a89d5e0b605d90c4a1cb224bbde27a22160ec 100644
--- a/src/app/+collection-page/create-collection-page/create-collection-page.component.spec.ts
+++ b/src/app/+collection-page/create-collection-page/create-collection-page.component.spec.ts
@@ -10,6 +10,8 @@ import { CollectionDataService } from '../../core/data/collection-data.service';
 import { of as observableOf } from 'rxjs';
 import { CommunityDataService } from '../../core/data/community-data.service';
 import { CreateCollectionPageComponent } from './create-collection-page.component';
+import { NotificationsService } from '../../shared/notifications/notifications.service';
+import { NotificationsServiceStub } from '../../shared/testing/notifications-service-stub';
 
 describe('CreateCollectionPageComponent', () => {
   let comp: CreateCollectionPageComponent;
@@ -27,6 +29,7 @@ describe('CreateCollectionPageComponent', () => {
         },
         { provide: RouteService, useValue: { getQueryParameterValue: () => observableOf('1234') } },
         { provide: Router, useValue: {} },
+        { provide: NotificationsService, useValue: new NotificationsServiceStub() }
       ],
       schemas: [NO_ERRORS_SCHEMA]
     }).compileComponents();
diff --git a/src/app/+collection-page/create-collection-page/create-collection-page.component.ts b/src/app/+collection-page/create-collection-page/create-collection-page.component.ts
index 2cab36d2851ccc5b5fbc8ae102779405bd9bb1e4..ae31b94c3dde271428cd61f4e7de173684cc5bfa 100644
--- a/src/app/+collection-page/create-collection-page/create-collection-page.component.ts
+++ b/src/app/+collection-page/create-collection-page/create-collection-page.component.ts
@@ -5,6 +5,8 @@ import { Router } from '@angular/router';
 import { CreateComColPageComponent } from '../../shared/comcol-forms/create-comcol-page/create-comcol-page.component';
 import { Collection } from '../../core/shared/collection.model';
 import { CollectionDataService } from '../../core/data/collection-data.service';
+import { NotificationsService } from '../../shared/notifications/notifications.service';
+import { TranslateService } from '@ngx-translate/core';
 
 /**
  * Component that represents the page where a user can create a new Collection
@@ -16,13 +18,16 @@ import { CollectionDataService } from '../../core/data/collection-data.service';
 })
 export class CreateCollectionPageComponent extends CreateComColPageComponent<Collection> {
   protected frontendURL = '/collections/';
+  protected type = Collection.type;
 
   public constructor(
     protected communityDataService: CommunityDataService,
     protected collectionDataService: CollectionDataService,
     protected routeService: RouteService,
-    protected router: Router
+    protected router: Router,
+    protected notificationsService: NotificationsService,
+    protected translate: TranslateService
   ) {
-    super(collectionDataService, communityDataService, routeService, router);
+    super(collectionDataService, communityDataService, routeService, router, notificationsService, translate);
   }
 }
diff --git a/src/app/+collection-page/edit-collection-page/collection-curate/collection-curate.component.html b/src/app/+collection-page/edit-collection-page/collection-curate/collection-curate.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/app/+collection-page/edit-collection-page/collection-curate/collection-curate.component.ts b/src/app/+collection-page/edit-collection-page/collection-curate/collection-curate.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d7deaea9826ab1f8d328aaff55dcce06b85551cf
--- /dev/null
+++ b/src/app/+collection-page/edit-collection-page/collection-curate/collection-curate.component.ts
@@ -0,0 +1,12 @@
+import { Component } from '@angular/core';
+
+/**
+ * Component for managing a collection's curation tasks
+ */
+@Component({
+  selector: 'ds-collection-curate',
+  templateUrl: './collection-curate.component.html',
+})
+export class CollectionCurateComponent {
+  /* TODO: Implement Collection Edit - Curate */
+}
diff --git a/src/app/+collection-page/edit-collection-page/collection-metadata/collection-metadata.component.html b/src/app/+collection-page/edit-collection-page/collection-metadata/collection-metadata.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..6f3a63790dab5eaa3b8a026729166e3302670217
--- /dev/null
+++ b/src/app/+collection-page/edit-collection-page/collection-metadata/collection-metadata.component.html
@@ -0,0 +1,6 @@
+<ds-collection-form (submitForm)="onSubmit($event)"
+                    [dso]="(dsoRD$ | async)?.payload"
+                    (finish)="navigateToHomePage()"></ds-collection-form>
+<a class="btn btn-danger"
+   [routerLink]="'/collections/' + (dsoRD$ | async)?.payload.uuid + '/delete'">{{'collection.edit.delete'
+  | translate}}</a>
diff --git a/src/app/+collection-page/edit-collection-page/collection-metadata/collection-metadata.component.spec.ts b/src/app/+collection-page/edit-collection-page/collection-metadata/collection-metadata.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..71cb06394f8499d5fa2959578b0e11c42bdee622
--- /dev/null
+++ b/src/app/+collection-page/edit-collection-page/collection-metadata/collection-metadata.component.spec.ts
@@ -0,0 +1,42 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { TranslateModule } from '@ngx-translate/core';
+import { SharedModule } from '../../../shared/shared.module';
+import { CommonModule } from '@angular/common';
+import { RouterTestingModule } from '@angular/router/testing';
+import { CollectionDataService } from '../../../core/data/collection-data.service';
+import { ActivatedRoute } from '@angular/router';
+import { of as observableOf } from 'rxjs/internal/observable/of';
+import { NO_ERRORS_SCHEMA } from '@angular/core';
+import { CollectionMetadataComponent } from './collection-metadata.component';
+import { NotificationsService } from '../../../shared/notifications/notifications.service';
+import { NotificationsServiceStub } from '../../../shared/testing/notifications-service-stub';
+
+describe('CollectionMetadataComponent', () => {
+  let comp: CollectionMetadataComponent;
+  let fixture: ComponentFixture<CollectionMetadataComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [TranslateModule.forRoot(), SharedModule, CommonModule, RouterTestingModule],
+      declarations: [CollectionMetadataComponent],
+      providers: [
+        { provide: CollectionDataService, useValue: {} },
+        { provide: ActivatedRoute, useValue: { parent: { data: observableOf({ dso: { payload: {} } }) } } },
+        { provide: NotificationsService, useValue: new NotificationsServiceStub() }
+      ],
+      schemas: [NO_ERRORS_SCHEMA]
+    }).compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(CollectionMetadataComponent);
+    comp = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  describe('frontendURL', () => {
+    it('should have the right frontendURL set', () => {
+      expect((comp as any).frontendURL).toEqual('/collections/');
+    })
+  });
+});
diff --git a/src/app/+collection-page/edit-collection-page/collection-metadata/collection-metadata.component.ts b/src/app/+collection-page/edit-collection-page/collection-metadata/collection-metadata.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..af2ab7d0a7c2ffcf9b0d76bd1c722c30d2fdda94
--- /dev/null
+++ b/src/app/+collection-page/edit-collection-page/collection-metadata/collection-metadata.component.ts
@@ -0,0 +1,29 @@
+import { Component } from '@angular/core';
+import { ComcolMetadataComponent } from '../../../shared/comcol-forms/edit-comcol-page/comcol-metadata/comcol-metadata.component';
+import { Collection } from '../../../core/shared/collection.model';
+import { CollectionDataService } from '../../../core/data/collection-data.service';
+import { ActivatedRoute, Router } from '@angular/router';
+import { NotificationsService } from '../../../shared/notifications/notifications.service';
+import { TranslateService } from '@ngx-translate/core';
+
+/**
+ * Component for editing a collection's metadata
+ */
+@Component({
+  selector: 'ds-collection-metadata',
+  templateUrl: './collection-metadata.component.html',
+})
+export class CollectionMetadataComponent extends ComcolMetadataComponent<Collection> {
+  protected frontendURL = '/collections/';
+  protected type = Collection.type;
+
+  public constructor(
+    protected collectionDataService: CollectionDataService,
+    protected router: Router,
+    protected route: ActivatedRoute,
+    protected notificationsService: NotificationsService,
+    protected translate: TranslateService
+  ) {
+    super(collectionDataService, router, route, notificationsService, translate);
+  }
+}
diff --git a/src/app/+collection-page/edit-collection-page/collection-roles/collection-roles.component.html b/src/app/+collection-page/edit-collection-page/collection-roles/collection-roles.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/app/+collection-page/edit-collection-page/collection-roles/collection-roles.component.ts b/src/app/+collection-page/edit-collection-page/collection-roles/collection-roles.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..39f72fd2cecac2cd02341cf203259bc79e5b40ea
--- /dev/null
+++ b/src/app/+collection-page/edit-collection-page/collection-roles/collection-roles.component.ts
@@ -0,0 +1,12 @@
+import { Component } from '@angular/core';
+
+/**
+ * Component for managing a collection's roles
+ */
+@Component({
+  selector: 'ds-collection-roles',
+  templateUrl: './collection-roles.component.html',
+})
+export class CollectionRolesComponent {
+  /* TODO: Implement Collection Edit - Roles */
+}
diff --git a/src/app/+collection-page/edit-collection-page/collection-source/collection-source.component.html b/src/app/+collection-page/edit-collection-page/collection-source/collection-source.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/app/+collection-page/edit-collection-page/collection-source/collection-source.component.ts b/src/app/+collection-page/edit-collection-page/collection-source/collection-source.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6ec5be884d68d03de274b017baaeded26c6c0a21
--- /dev/null
+++ b/src/app/+collection-page/edit-collection-page/collection-source/collection-source.component.ts
@@ -0,0 +1,12 @@
+import { Component } from '@angular/core';
+
+/**
+ * Component for managing the content source of the collection
+ */
+@Component({
+  selector: 'ds-collection-source',
+  templateUrl: './collection-source.component.html',
+})
+export class CollectionSourceComponent {
+  /* TODO: Implement Collection Edit - Content Source */
+}
diff --git a/src/app/+collection-page/edit-collection-page/edit-collection-page.component.html b/src/app/+collection-page/edit-collection-page/edit-collection-page.component.html
deleted file mode 100644
index c389c681ce5cd81f2d1dc56547b6a9929751c1de..0000000000000000000000000000000000000000
--- a/src/app/+collection-page/edit-collection-page/edit-collection-page.component.html
+++ /dev/null
@@ -1,11 +0,0 @@
-<div class="container">
-  <div class="row">
-    <div class="col-12 pb-4">
-      <h2 id="header" class="border-bottom pb-2">{{ 'collection.edit.head' | translate }}</h2>
-      <ds-collection-form (submitForm)="onSubmit($event)" [dso]="(dsoRD$ | async)?.payload"></ds-collection-form>
-      <a class="btn btn-danger"
-         [routerLink]="'/collections/' + (dsoRD$ | async)?.payload.uuid + '/delete'">{{'collection.edit.delete'
-        | translate}}</a>
-    </div>
-  </div>
-</div>
diff --git a/src/app/+collection-page/edit-collection-page/edit-collection-page.component.scss b/src/app/+collection-page/edit-collection-page/edit-collection-page.component.scss
deleted file mode 100644
index 8b137891791fe96927ad78e64b0aad7bded08bdc..0000000000000000000000000000000000000000
--- a/src/app/+collection-page/edit-collection-page/edit-collection-page.component.scss
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/src/app/+collection-page/edit-collection-page/edit-collection-page.component.spec.ts b/src/app/+collection-page/edit-collection-page/edit-collection-page.component.spec.ts
index 193cb293e4d3252e74669664e00ee9f7b54dfe51..9f915d2d7a44638fa9d334ecc4b701cac4d78bc7 100644
--- a/src/app/+collection-page/edit-collection-page/edit-collection-page.component.spec.ts
+++ b/src/app/+collection-page/edit-collection-page/edit-collection-page.component.spec.ts
@@ -13,13 +13,29 @@ describe('EditCollectionPageComponent', () => {
   let comp: EditCollectionPageComponent;
   let fixture: ComponentFixture<EditCollectionPageComponent>;
 
+  const routeStub = {
+    data: observableOf({
+      dso: { payload: {} }
+    }),
+    routeConfig: {
+      children: []
+    },
+    snapshot: {
+      firstChild: {
+        routeConfig: {
+          path: 'mockUrl'
+        }
+      }
+    }
+  };
+
   beforeEach(async(() => {
     TestBed.configureTestingModule({
       imports: [TranslateModule.forRoot(), SharedModule, CommonModule, RouterTestingModule],
       declarations: [EditCollectionPageComponent],
       providers: [
         { provide: CollectionDataService, useValue: {} },
-        { provide: ActivatedRoute, useValue: { data: observableOf({ dso: { payload: {} } }) } },
+        { provide: ActivatedRoute, useValue: routeStub },
       ],
       schemas: [NO_ERRORS_SCHEMA]
     }).compileComponents();
@@ -31,9 +47,9 @@ describe('EditCollectionPageComponent', () => {
     fixture.detectChanges();
   });
 
-  describe('frontendURL', () => {
-    it('should have the right frontendURL set', () => {
-      expect((comp as any).frontendURL).toEqual('/collections/');
+  describe('type', () => {
+    it('should have the right type set', () => {
+      expect((comp as any).type).toEqual('collection');
     })
   });
 });
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 ba70bd26c6ba5566dafa5114805d0dc7c8752c50..209ce5149a1c8499923ebc1df6cc49d789ea5b1e 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
@@ -2,24 +2,30 @@ 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 { Collection } from '../../core/shared/collection.model';
-import { CollectionDataService } from '../../core/data/collection-data.service';
+import { getCollectionPageRoute } from '../collection-page-routing.module';
 
 /**
  * Component that represents the page where a user can edit an existing Collection
  */
 @Component({
   selector: 'ds-edit-collection',
-  styleUrls: ['./edit-collection-page.component.scss'],
-  templateUrl: './edit-collection-page.component.html'
+  templateUrl: '../../shared/comcol-forms/edit-comcol-page/edit-comcol-page.component.html'
 })
 export class EditCollectionPageComponent extends EditComColPageComponent<Collection> {
-  protected frontendURL = '/collections/';
+  type = 'collection';
 
   public constructor(
-    protected collectionDataService: CollectionDataService,
     protected router: Router,
     protected route: ActivatedRoute
   ) {
-    super(collectionDataService, router, route);
+    super(router, route);
+  }
+
+  /**
+   * Get the collection page url
+   * @param collection The collection for which the url is requested
+   */
+  getPageUrl(collection: Collection): string {
+    return getCollectionPageRoute(collection.id)
   }
 }
diff --git a/src/app/+collection-page/edit-collection-page/edit-collection-page.module.ts b/src/app/+collection-page/edit-collection-page/edit-collection-page.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f442aae4d684210314d6bf93089df31b02de6be9
--- /dev/null
+++ b/src/app/+collection-page/edit-collection-page/edit-collection-page.module.ts
@@ -0,0 +1,32 @@
+import { NgModule } from '@angular/core';
+import { EditCollectionPageComponent } from './edit-collection-page.component';
+import { CommonModule } from '@angular/common';
+import { SharedModule } from '../../shared/shared.module';
+import { EditCollectionPageRoutingModule } from './edit-collection-page.routing.module';
+import { CollectionMetadataComponent } from './collection-metadata/collection-metadata.component';
+import { CollectionPageModule } from '../collection-page.module';
+import { CollectionRolesComponent } from './collection-roles/collection-roles.component';
+import { CollectionCurateComponent } from './collection-curate/collection-curate.component';
+import { CollectionSourceComponent } from './collection-source/collection-source.component';
+
+/**
+ * Module that contains all components related to the Edit Collection page administrator functionality
+ */
+@NgModule({
+  imports: [
+    CommonModule,
+    SharedModule,
+    EditCollectionPageRoutingModule,
+    CollectionPageModule
+  ],
+  declarations: [
+    EditCollectionPageComponent,
+    CollectionMetadataComponent,
+    CollectionRolesComponent,
+    CollectionCurateComponent,
+    CollectionSourceComponent
+  ]
+})
+export class EditCollectionPageModule {
+
+}
diff --git a/src/app/+collection-page/edit-collection-page/edit-collection-page.routing.module.ts b/src/app/+collection-page/edit-collection-page/edit-collection-page.routing.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fcfced9d810a52a2802886fcd3256f9fac51d1b8
--- /dev/null
+++ b/src/app/+collection-page/edit-collection-page/edit-collection-page.routing.module.ts
@@ -0,0 +1,61 @@
+import { RouterModule } from '@angular/router';
+import { NgModule } from '@angular/core';
+import { EditCollectionPageComponent } from './edit-collection-page.component';
+import { CollectionPageResolver } from '../collection-page.resolver';
+import { CollectionMetadataComponent } from './collection-metadata/collection-metadata.component';
+import { CollectionRolesComponent } from './collection-roles/collection-roles.component';
+import { CollectionSourceComponent } from './collection-source/collection-source.component';
+import { CollectionCurateComponent } from './collection-curate/collection-curate.component';
+
+/**
+ * Routing module that handles the routing for the Edit Collection page administrator functionality
+ */
+@NgModule({
+  imports: [
+    RouterModule.forChild([
+      {
+        path: '',
+        component: EditCollectionPageComponent,
+        resolve: {
+          dso: CollectionPageResolver
+        },
+        children: [
+          {
+            path: '',
+            redirectTo: 'metadata',
+            pathMatch: 'full'
+          },
+          {
+            path: 'metadata',
+            component: CollectionMetadataComponent,
+            data: {
+              title: 'collection.edit.tabs.metadata.title',
+              hideReturnButton: true
+            }
+          },
+          {
+            path: 'roles',
+            component: CollectionRolesComponent,
+            data: { title: 'collection.edit.tabs.roles.title' }
+          },
+          {
+            path: 'source',
+            component: CollectionSourceComponent,
+            data: { title: 'collection.edit.tabs.source.title' }
+          },
+          {
+            path: 'curate',
+            component: CollectionCurateComponent,
+            data: { title: 'collection.edit.tabs.curate.title' }
+          }
+        ]
+      }
+    ])
+  ],
+  providers: [
+    CollectionPageResolver,
+  ]
+})
+export class EditCollectionPageRoutingModule {
+
+}
diff --git a/src/app/+community-page/community-form/community-form.component.ts b/src/app/+community-page/community-form/community-form.component.ts
index 2932d7f1bb795937f9e0397b0669cf141e393b02..e9bd2f66c8ffd60b352717012ea78671b7d12ac5 100644
--- a/src/app/+community-page/community-form/community-form.component.ts
+++ b/src/app/+community-page/community-form/community-form.component.ts
@@ -1,7 +1,19 @@
 import { Component, Input } from '@angular/core';
-import { DynamicFormControlModel, DynamicInputModel, DynamicTextAreaModel } from '@ng-dynamic-forms/core';
+import {
+  DynamicFormControlModel,
+  DynamicFormService,
+  DynamicInputModel,
+  DynamicTextAreaModel
+} from '@ng-dynamic-forms/core';
 import { Community } from '../../core/shared/community.model';
 import { ComColFormComponent } from '../../shared/comcol-forms/comcol-form/comcol-form.component';
+import { Location } from '@angular/common';
+import { TranslateService } from '@ngx-translate/core';
+import { NotificationsService } from '../../shared/notifications/notifications.service';
+import { CommunityDataService } from '../../core/data/community-data.service';
+import { AuthService } from '../../core/auth/auth.service';
+import { RequestService } from '../../core/data/request.service';
+import { ObjectCacheService } from '../../core/cache/object-cache.service';
 
 /**
  * Form used for creating and editing communities
@@ -20,7 +32,7 @@ export class CommunityFormComponent extends ComColFormComponent<Community> {
   /**
    * @type {Community.type} This is a community-type form
    */
-  protected type = Community.type;
+  type = Community.type;
 
   /**
    * The dynamic form fields used for creating/editing a community
@@ -55,4 +67,15 @@ export class CommunityFormComponent extends ComColFormComponent<Community> {
       name: 'dc.description.tableofcontents',
     }),
   ];
+
+  public constructor(protected location: Location,
+                     protected formService: DynamicFormService,
+                     protected translate: TranslateService,
+                     protected notificationsService: NotificationsService,
+                     protected authService: AuthService,
+                     protected dsoService: CommunityDataService,
+                     protected requestService: RequestService,
+                     protected objectCache: ObjectCacheService) {
+    super(location, formService, translate, notificationsService, authService, requestService, objectCache);
+  }
 }
diff --git a/src/app/+community-page/community-page-routing.module.ts b/src/app/+community-page/community-page-routing.module.ts
index cecd17ec10844aa4912882f52fb73f119f5d5f48..df548e061729ce9d795855d1fa0bb286aede0b08 100644
--- a/src/app/+community-page/community-page-routing.module.ts
+++ b/src/app/+community-page/community-page-routing.module.ts
@@ -5,7 +5,6 @@ import { CommunityPageComponent } from './community-page.component';
 import { CommunityPageResolver } from './community-page.resolver';
 import { CreateCommunityPageComponent } from './create-community-page/create-community-page.component';
 import { AuthenticatedGuard } from '../core/auth/authenticated.guard';
-import { EditCommunityPageComponent } from './edit-community-page/edit-community-page.component';
 import { CreateCommunityPageGuard } from './create-community-page/create-community-page.guard';
 import { DeleteCommunityPageComponent } from './delete-community-page/delete-community-page.component';
 import { URLCombiner } from '../core/url-combiner/url-combiner';
@@ -38,12 +37,8 @@ const COMMUNITY_EDIT_PATH = ':id/edit';
       },
       {
         path: COMMUNITY_EDIT_PATH,
-        pathMatch: 'full',
-        component: EditCommunityPageComponent,
-        canActivate: [AuthenticatedGuard],
-        resolve: {
-          dso: CommunityPageResolver
-        }
+        loadChildren: './edit-community-page/edit-community-page.module#EditCommunityPageModule',
+        canActivate: [AuthenticatedGuard]
       },
       {
         path: ':id/delete',
diff --git a/src/app/+community-page/community-page.component.html b/src/app/+community-page/community-page.component.html
index 5bd7089e820e03be9484cb0f9665b28b26a4160b..dfd1ce93d900e5d735afc44506574b4688bebfc9 100644
--- a/src/app/+community-page/community-page.component.html
+++ b/src/app/+community-page/community-page.component.html
@@ -3,12 +3,11 @@
     <div *ngIf="communityRD?.payload; let communityPayload">
       <ds-view-tracker [object]="communityPayload"></ds-view-tracker>
       <header class="comcol-header border-bottom mb-4 pb-4">
+        <!-- Community name -->
+        <ds-comcol-page-header [name]="communityPayload.name"></ds-comcol-page-header>
         <!-- Community logo -->
         <ds-comcol-page-logo *ngIf="logoRD$" [logo]="(logoRD$ | async)?.payload" [alternateText]="'Community Logo'">
         </ds-comcol-page-logo>
-
-        <!-- Community name -->
-        <ds-comcol-page-header [name]="communityPayload.name"></ds-comcol-page-header>
         <!-- Handle -->
         <ds-comcol-page-handle [content]="communityPayload.handle" [title]="'community.page.handle'">
         </ds-comcol-page-handle>
diff --git a/src/app/+community-page/community-page.module.ts b/src/app/+community-page/community-page.module.ts
index 8b02471fc2f709fd5459f8a68ffc902957277f89..1228783c3bdf00250838f3a7e9776f00be9dcd05 100644
--- a/src/app/+community-page/community-page.module.ts
+++ b/src/app/+community-page/community-page.module.ts
@@ -9,7 +9,6 @@ import { CommunityPageRoutingModule } from './community-page-routing.module';
 import { CommunityPageSubCommunityListComponent } from './sub-community-list/community-page-sub-community-list.component';
 import { CreateCommunityPageComponent } from './create-community-page/create-community-page.component';
 import { CommunityFormComponent } from './community-form/community-form.component';
-import { EditCommunityPageComponent } from './edit-community-page/edit-community-page.component';
 import { DeleteCommunityPageComponent } from './delete-community-page/delete-community-page.component';
 import { StatisticsModule } from '../statistics/statistics.module';
 
@@ -25,9 +24,11 @@ import { StatisticsModule } from '../statistics/statistics.module';
     CommunityPageSubCollectionListComponent,
     CommunityPageSubCommunityListComponent,
     CreateCommunityPageComponent,
-    EditCommunityPageComponent,
     DeleteCommunityPageComponent,
     CommunityFormComponent
+  ],
+  exports: [
+    CommunityFormComponent
   ]
 })
 
diff --git a/src/app/+community-page/create-community-page/create-community-page.component.html b/src/app/+community-page/create-community-page/create-community-page.component.html
index 55a080d2a15ab8891c077b41faeef6242805e24a..4f75771f6df97c43b1a4715de214edb9a901491b 100644
--- a/src/app/+community-page/create-community-page/create-community-page.component.html
+++ b/src/app/+community-page/create-community-page/create-community-page.component.html
@@ -7,5 +7,5 @@
       </ng-container>
     </div>
   </div>
-  <ds-community-form (submitForm)="onSubmit($event)"></ds-community-form>
+  <ds-community-form (submitForm)="onSubmit($event)" (finish)="navigateToNewPage()"></ds-community-form>
 </div>
diff --git a/src/app/+community-page/create-community-page/create-community-page.component.spec.ts b/src/app/+community-page/create-community-page/create-community-page.component.spec.ts
index dead5a5c3baeb2ff97cc9977c1672d2421e74097..d0de8ec71c611102e2bb1d5af400367cb1b8e313 100644
--- a/src/app/+community-page/create-community-page/create-community-page.component.spec.ts
+++ b/src/app/+community-page/create-community-page/create-community-page.component.spec.ts
@@ -10,6 +10,8 @@ import { CollectionDataService } from '../../core/data/collection-data.service';
 import { of as observableOf } from 'rxjs';
 import { CommunityDataService } from '../../core/data/community-data.service';
 import { CreateCommunityPageComponent } from './create-community-page.component';
+import { NotificationsService } from '../../shared/notifications/notifications.service';
+import { NotificationsServiceStub } from '../../shared/testing/notifications-service-stub';
 
 describe('CreateCommunityPageComponent', () => {
   let comp: CreateCommunityPageComponent;
@@ -23,6 +25,7 @@ describe('CreateCommunityPageComponent', () => {
         { provide: CommunityDataService, useValue: { findById: () => observableOf({}) } },
         { provide: RouteService, useValue: { getQueryParameterValue: () => observableOf('1234') } },
         { provide: Router, useValue: {} },
+        { provide: NotificationsService, useValue: new NotificationsServiceStub() }
       ],
       schemas: [NO_ERRORS_SCHEMA]
     }).compileComponents();
diff --git a/src/app/+community-page/create-community-page/create-community-page.component.ts b/src/app/+community-page/create-community-page/create-community-page.component.ts
index fd5f18442a957db4cf1b2946c95c8ed1193b13fb..30a2acbb0d8b19e14e7add4d0bb4ea6c9f329f2a 100644
--- a/src/app/+community-page/create-community-page/create-community-page.component.ts
+++ b/src/app/+community-page/create-community-page/create-community-page.component.ts
@@ -4,6 +4,8 @@ import { CommunityDataService } from '../../core/data/community-data.service';
 import { RouteService } from '../../core/services/route.service';
 import { Router } from '@angular/router';
 import { CreateComColPageComponent } from '../../shared/comcol-forms/create-comcol-page/create-comcol-page.component';
+import { NotificationsService } from '../../shared/notifications/notifications.service';
+import { TranslateService } from '@ngx-translate/core';
 
 /**
  * Component that represents the page where a user can create a new Community
@@ -15,12 +17,15 @@ import { CreateComColPageComponent } from '../../shared/comcol-forms/create-comc
 })
 export class CreateCommunityPageComponent extends CreateComColPageComponent<Community> {
   protected frontendURL = '/communities/';
+  protected type = Community.type;
 
   public constructor(
     protected communityDataService: CommunityDataService,
     protected routeService: RouteService,
-    protected router: Router
+    protected router: Router,
+    protected notificationsService: NotificationsService,
+    protected translate: TranslateService
   ) {
-    super(communityDataService, communityDataService, routeService, router);
+    super(communityDataService, communityDataService, routeService, router, notificationsService, translate);
   }
 }
diff --git a/src/app/+community-page/edit-community-page/community-curate/community-curate.component.html b/src/app/+community-page/edit-community-page/community-curate/community-curate.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/app/+community-page/edit-community-page/community-curate/community-curate.component.ts b/src/app/+community-page/edit-community-page/community-curate/community-curate.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6151d3fe9ad7f66809db5008404ba8911451ddf0
--- /dev/null
+++ b/src/app/+community-page/edit-community-page/community-curate/community-curate.component.ts
@@ -0,0 +1,12 @@
+import { Component } from '@angular/core';
+
+/**
+ * Component for managing a community's curation tasks
+ */
+@Component({
+  selector: 'ds-community-curate',
+  templateUrl: './community-curate.component.html',
+})
+export class CommunityCurateComponent {
+  /* TODO: Implement Community Edit - Curate */
+}
diff --git a/src/app/+community-page/edit-community-page/community-metadata/community-metadata.component.html b/src/app/+community-page/edit-community-page/community-metadata/community-metadata.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..6b441dbabdb96a7507fee247c7d69c52d8d99587
--- /dev/null
+++ b/src/app/+community-page/edit-community-page/community-metadata/community-metadata.component.html
@@ -0,0 +1,6 @@
+<ds-community-form (submitForm)="onSubmit($event)"
+                   [dso]="(dsoRD$ | async)?.payload"
+                   (finish)="navigateToHomePage()"></ds-community-form>
+<a class="btn btn-danger"
+   [routerLink]="'/communities/' + (dsoRD$ | async)?.payload.uuid + '/delete'">{{'community.edit.delete'
+  | translate}}</a>
diff --git a/src/app/+community-page/edit-community-page/community-metadata/community-metadata.component.spec.ts b/src/app/+community-page/edit-community-page/community-metadata/community-metadata.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..abeafb4e237374b628832f682fafcb7baff85009
--- /dev/null
+++ b/src/app/+community-page/edit-community-page/community-metadata/community-metadata.component.spec.ts
@@ -0,0 +1,42 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { TranslateModule } from '@ngx-translate/core';
+import { SharedModule } from '../../../shared/shared.module';
+import { CommonModule } from '@angular/common';
+import { RouterTestingModule } from '@angular/router/testing';
+import { ActivatedRoute } from '@angular/router';
+import { of as observableOf } from 'rxjs/internal/observable/of';
+import { NO_ERRORS_SCHEMA } from '@angular/core';
+import { CommunityMetadataComponent } from './community-metadata.component';
+import { CommunityDataService } from '../../../core/data/community-data.service';
+import { NotificationsService } from '../../../shared/notifications/notifications.service';
+import { NotificationsServiceStub } from '../../../shared/testing/notifications-service-stub';
+
+describe('CommunityMetadataComponent', () => {
+  let comp: CommunityMetadataComponent;
+  let fixture: ComponentFixture<CommunityMetadataComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [TranslateModule.forRoot(), SharedModule, CommonModule, RouterTestingModule],
+      declarations: [CommunityMetadataComponent],
+      providers: [
+        { provide: CommunityDataService, useValue: {} },
+        { provide: ActivatedRoute, useValue: { parent: { data: observableOf({ dso: { payload: {} } }) } } },
+        { provide: NotificationsService, useValue: new NotificationsServiceStub() }
+      ],
+      schemas: [NO_ERRORS_SCHEMA]
+    }).compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(CommunityMetadataComponent);
+    comp = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  describe('frontendURL', () => {
+    it('should have the right frontendURL set', () => {
+      expect((comp as any).frontendURL).toEqual('/communities/');
+    })
+  });
+});
diff --git a/src/app/+community-page/edit-community-page/community-metadata/community-metadata.component.ts b/src/app/+community-page/edit-community-page/community-metadata/community-metadata.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c4bb88289fa990c139010e8646e4f41936f2a69e
--- /dev/null
+++ b/src/app/+community-page/edit-community-page/community-metadata/community-metadata.component.ts
@@ -0,0 +1,29 @@
+import { Component } from '@angular/core';
+import { ComcolMetadataComponent } from '../../../shared/comcol-forms/edit-comcol-page/comcol-metadata/comcol-metadata.component';
+import { ActivatedRoute, Router } from '@angular/router';
+import { Community } from '../../../core/shared/community.model';
+import { CommunityDataService } from '../../../core/data/community-data.service';
+import { NotificationsService } from '../../../shared/notifications/notifications.service';
+import { TranslateService } from '@ngx-translate/core';
+
+/**
+ * Component for editing a community's metadata
+ */
+@Component({
+  selector: 'ds-community-metadata',
+  templateUrl: './community-metadata.component.html',
+})
+export class CommunityMetadataComponent extends ComcolMetadataComponent<Community> {
+  protected frontendURL = '/communities/';
+  protected type = Community.type;
+
+  public constructor(
+    protected communityDataService: CommunityDataService,
+    protected router: Router,
+    protected route: ActivatedRoute,
+    protected notificationsService: NotificationsService,
+    protected translate: TranslateService
+  ) {
+    super(communityDataService, router, route, notificationsService, translate);
+  }
+}
diff --git a/src/app/+community-page/edit-community-page/community-roles/community-roles.component.html b/src/app/+community-page/edit-community-page/community-roles/community-roles.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/app/+community-page/edit-community-page/community-roles/community-roles.component.ts b/src/app/+community-page/edit-community-page/community-roles/community-roles.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..afa1fe14d1aa322d7de89f4dd1da29d26563de79
--- /dev/null
+++ b/src/app/+community-page/edit-community-page/community-roles/community-roles.component.ts
@@ -0,0 +1,12 @@
+import { Component } from '@angular/core';
+
+/**
+ * Component for managing a community's roles
+ */
+@Component({
+  selector: 'ds-community-roles',
+  templateUrl: './community-roles.component.html',
+})
+export class CommunityRolesComponent {
+  /* TODO: Implement Community Edit - Roles */
+}
diff --git a/src/app/+community-page/edit-community-page/edit-community-page.component.html b/src/app/+community-page/edit-community-page/edit-community-page.component.html
deleted file mode 100644
index cedb771c14f8aea6a29de214c41b33ab9a607851..0000000000000000000000000000000000000000
--- a/src/app/+community-page/edit-community-page/edit-community-page.component.html
+++ /dev/null
@@ -1,12 +0,0 @@
-<div class="container">
-    <div class="row">
-        <div class="col-12 pb-4">
-            <h2 id="header" class="border-bottom pb-2">{{ 'community.edit.head' | translate }}</h2>
-            <ds-community-form (submitForm)="onSubmit($event)"
-                               [dso]="(dsoRD$ | async)?.payload"></ds-community-form>
-            <a class="btn btn-danger"
-               [routerLink]="'/communities/' + (dsoRD$ | async)?.payload.uuid + '/delete'">{{'community.edit.delete'
-                | translate}}</a>
-        </div>
-    </div>
-</div>
diff --git a/src/app/+community-page/edit-community-page/edit-community-page.component.scss b/src/app/+community-page/edit-community-page/edit-community-page.component.scss
deleted file mode 100644
index 8b137891791fe96927ad78e64b0aad7bded08bdc..0000000000000000000000000000000000000000
--- a/src/app/+community-page/edit-community-page/edit-community-page.component.scss
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/src/app/+community-page/edit-community-page/edit-community-page.component.spec.ts b/src/app/+community-page/edit-community-page/edit-community-page.component.spec.ts
index 54f2133ce7854c950a6336a71395a3f2515b5021..b61924dd00813354cf53fab22e55754d71ef8f16 100644
--- a/src/app/+community-page/edit-community-page/edit-community-page.component.spec.ts
+++ b/src/app/+community-page/edit-community-page/edit-community-page.component.spec.ts
@@ -13,13 +13,29 @@ describe('EditCommunityPageComponent', () => {
   let comp: EditCommunityPageComponent;
   let fixture: ComponentFixture<EditCommunityPageComponent>;
 
+  const routeStub = {
+    data: observableOf({
+      dso: { payload: {} }
+    }),
+    routeConfig: {
+      children: []
+    },
+    snapshot: {
+      firstChild: {
+        routeConfig: {
+          path: 'mockUrl'
+        }
+      }
+    }
+  };
+
   beforeEach(async(() => {
     TestBed.configureTestingModule({
       imports: [TranslateModule.forRoot(), SharedModule, CommonModule, RouterTestingModule],
       declarations: [EditCommunityPageComponent],
       providers: [
         { provide: CommunityDataService, useValue: {} },
-        { provide: ActivatedRoute, useValue: { data: observableOf({ dso: { payload: {} } }) } },
+        { provide: ActivatedRoute, useValue: routeStub },
       ],
       schemas: [NO_ERRORS_SCHEMA]
     }).compileComponents();
@@ -31,9 +47,9 @@ describe('EditCommunityPageComponent', () => {
     fixture.detectChanges();
   });
 
-  describe('frontendURL', () => {
-    it('should have the right frontendURL set', () => {
-      expect((comp as any).frontendURL).toEqual('/communities/');
+  describe('type', () => {
+    it('should have the right type set', () => {
+      expect((comp as any).type).toEqual('community');
     })
   });
 });
diff --git a/src/app/+community-page/edit-community-page/edit-community-page.component.ts b/src/app/+community-page/edit-community-page/edit-community-page.component.ts
index 9f49ac49dd1f8d4c00c5c5968e25351e4fba8a13..c0adfe0ff1f62d9a02145326a1df4299b812ac6c 100644
--- a/src/app/+community-page/edit-community-page/edit-community-page.component.ts
+++ b/src/app/+community-page/edit-community-page/edit-community-page.component.ts
@@ -1,25 +1,31 @@
 import { Component } from '@angular/core';
 import { Community } from '../../core/shared/community.model';
-import { CommunityDataService } from '../../core/data/community-data.service';
 import { ActivatedRoute, Router } from '@angular/router';
 import { EditComColPageComponent } from '../../shared/comcol-forms/edit-comcol-page/edit-comcol-page.component';
+import { getCommunityPageRoute } from '../community-page-routing.module';
 
 /**
  * Component that represents the page where a user can edit an existing Community
  */
 @Component({
   selector: 'ds-edit-community',
-  styleUrls: ['./edit-community-page.component.scss'],
-  templateUrl: './edit-community-page.component.html'
+  templateUrl: '../../shared/comcol-forms/edit-comcol-page/edit-comcol-page.component.html'
 })
 export class EditCommunityPageComponent extends EditComColPageComponent<Community> {
-  protected frontendURL = '/communities/';
+  type = 'community';
 
   public constructor(
-    protected communityDataService: CommunityDataService,
     protected router: Router,
     protected route: ActivatedRoute
   ) {
-    super(communityDataService, router, route);
+    super(router, route);
+  }
+
+  /**
+   * Get the community page url
+   * @param community The community for which the url is requested
+   */
+  getPageUrl(community: Community): string {
+    return getCommunityPageRoute(community.id)
   }
 }
diff --git a/src/app/+community-page/edit-community-page/edit-community-page.module.ts b/src/app/+community-page/edit-community-page/edit-community-page.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f9a1e11a1432d5e89b6fa0fd7ac6a829b331a9bc
--- /dev/null
+++ b/src/app/+community-page/edit-community-page/edit-community-page.module.ts
@@ -0,0 +1,30 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { SharedModule } from '../../shared/shared.module';
+import { EditCommunityPageRoutingModule } from './edit-community-page.routing.module';
+import { CommunityPageModule } from '../community-page.module';
+import { EditCommunityPageComponent } from './edit-community-page.component';
+import { CommunityCurateComponent } from './community-curate/community-curate.component';
+import { CommunityMetadataComponent } from './community-metadata/community-metadata.component';
+import { CommunityRolesComponent } from './community-roles/community-roles.component';
+
+/**
+ * Module that contains all components related to the Edit Community page administrator functionality
+ */
+@NgModule({
+  imports: [
+    CommonModule,
+    SharedModule,
+    EditCommunityPageRoutingModule,
+    CommunityPageModule
+  ],
+  declarations: [
+    EditCommunityPageComponent,
+    CommunityCurateComponent,
+    CommunityMetadataComponent,
+    CommunityRolesComponent
+  ]
+})
+export class EditCommunityPageModule {
+
+}
diff --git a/src/app/+community-page/edit-community-page/edit-community-page.routing.module.ts b/src/app/+community-page/edit-community-page/edit-community-page.routing.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1182db2de1e362989a44bfd34281e6c1af4c9cc9
--- /dev/null
+++ b/src/app/+community-page/edit-community-page/edit-community-page.routing.module.ts
@@ -0,0 +1,55 @@
+import { CommunityPageResolver } from '../community-page.resolver';
+import { EditCommunityPageComponent } from './edit-community-page.component';
+import { RouterModule } from '@angular/router';
+import { NgModule } from '@angular/core';
+import { CommunityMetadataComponent } from './community-metadata/community-metadata.component';
+import { CommunityRolesComponent } from './community-roles/community-roles.component';
+import { CommunityCurateComponent } from './community-curate/community-curate.component';
+
+/**
+ * Routing module that handles the routing for the Edit Community page administrator functionality
+ */
+@NgModule({
+  imports: [
+    RouterModule.forChild([
+      {
+        path: '',
+        component: EditCommunityPageComponent,
+        resolve: {
+          dso: CommunityPageResolver
+        },
+        children: [
+          {
+            path: '',
+            redirectTo: 'metadata',
+            pathMatch: 'full'
+          },
+          {
+            path: 'metadata',
+            component: CommunityMetadataComponent,
+            data: {
+              title: 'community.edit.tabs.metadata.title',
+              hideReturnButton: true
+            }
+          },
+          {
+            path: 'roles',
+            component: CommunityRolesComponent,
+            data: { title: 'community.edit.tabs.roles.title' }
+          },
+          {
+            path: 'curate',
+            component: CommunityCurateComponent,
+            data: { title: 'community.edit.tabs.curate.title' }
+          }
+        ]
+      }
+    ])
+  ],
+  providers: [
+    CommunityPageResolver,
+  ]
+})
+export class EditCommunityPageRoutingModule {
+
+}
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
index 1d5564a29564ad29cd59e64060a134413ead47b6..81d66bb5f706939908be5207d202e1870535a998 100644
--- 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
@@ -33,12 +33,7 @@ export class MyDSpaceNewSubmissionComponent implements OnDestroy, OnInit {
   /**
    * The UploaderOptions object
    */
-  public uploadFilesOptions: UploaderOptions = {
-    url: '',
-    authToken: null,
-    disableMultipart: false,
-    itemAlias: null
-  };
+  public uploadFilesOptions: UploaderOptions = new UploaderOptions();
 
   /**
    * Subscription to unsubscribe from
diff --git a/src/app/core/data/comcol-data.service.ts b/src/app/core/data/comcol-data.service.ts
index 867ee24fc102f584dc2af65a3f70e7a6ef4a0acd..2ce0362a4e85e69c09b0f38e84e285fdddcc7c61 100644
--- a/src/app/core/data/comcol-data.service.ts
+++ b/src/app/core/data/comcol-data.service.ts
@@ -1,32 +1,41 @@
 import {
   distinctUntilChanged,
-  filter, first,
-  map,
-  mergeMap,
-  share,
-  switchMap,
+  filter, first,map, mergeMap, share, switchMap,
   take,
   tap
 } from 'rxjs/operators';
-import { merge as observableMerge, Observable, throwError as observableThrowError } from 'rxjs';
+import { merge as observableMerge, Observable, throwError as observableThrowError, combineLatest as observableCombineLatest } from 'rxjs';
 import { hasValue, isEmpty, isNotEmpty } from '../../shared/empty.util';
 import { NormalizedCommunity } from '../cache/models/normalized-community.model';
 import { ObjectCacheService } from '../cache/object-cache.service';
 import { CommunityDataService } from './community-data.service';
 
 import { DataService } from './data.service';
+import { DeleteRequest, FindListOptions, FindByIDRequest, RestRequest } from './request.models';
 import { PaginatedList } from './paginated-list';
 import { RemoteData } from './remote-data';
-import { FindListOptions, FindByIDRequest } from './request.models';
 import { HALEndpointService } from '../shared/hal-endpoint.service';
-import { getResponseFromEntry } from '../shared/operators';
+import {
+  configureRequest,
+  getRemoteDataPayload,
+  getResponseFromEntry,
+  getSucceededRemoteData
+} from '../shared/operators';
 import { CacheableObject } from '../cache/object-cache.reducer';
+import { RestResponse } from '../cache/response.models';
+import { Bitstream } from '../shared/bitstream.model';
+import { DSpaceObject } from '../shared/dspace-object.model';
 
 export abstract class ComColDataService<T extends CacheableObject> extends DataService<T> {
   protected abstract cds: CommunityDataService;
   protected abstract objectCache: ObjectCacheService;
   protected abstract halService: HALEndpointService;
 
+  /**
+   * Linkpath of endpoint to delete the logo
+   */
+  protected logoDeleteLinkpath = 'bitstreams';
+
   /**
    * Get the scoped endpoint URL by fetching the object with
    * the given scopeID and returning its HAL link with this
@@ -76,4 +85,33 @@ export abstract class ComColDataService<T extends CacheableObject> extends DataS
     return this.findList(href$, options);
   }
 
+  /**
+   * Get the endpoint for the community or collection's logo
+   * @param id  The community or collection's ID
+   */
+  public getLogoEndpoint(id: string): Observable<string> {
+    return this.halService.getEndpoint(this.linkPath).pipe(
+      switchMap((href: string) => this.halService.getEndpoint('logo', `${href}/${id}`))
+    )
+  }
+
+  /**
+   * Delete the logo from the community or collection
+   * @param dso The object to delete the logo from
+   */
+  public deleteLogo(dso: DSpaceObject): Observable<RestResponse> {
+    const logo$ = (dso as any).logo;
+    if (hasValue(logo$)) {
+      return observableCombineLatest(
+        logo$.pipe(getSucceededRemoteData(), getRemoteDataPayload(), take(1)),
+        this.halService.getEndpoint(this.logoDeleteLinkpath)
+      ).pipe(
+        map(([logo, href]: [Bitstream, string]) => `${href}/${logo.id}`),
+        map((href: string) => new DeleteRequest(this.requestService.generateRequestId(), href)),
+        configureRequest(this.requestService),
+        switchMap((restRequest: RestRequest) => this.requestService.getByUUID(restRequest.uuid)),
+        getResponseFromEntry()
+      );
+    }
+  }
 }
diff --git a/src/app/shared/comcol-forms/comcol-form/comcol-form.component.html b/src/app/shared/comcol-forms/comcol-form/comcol-form.component.html
index 6c6793706317b804ecb54f14daa1c264c7330f1f..09f7e459e47e533846e10c7a660d3efe9bf9d7c2 100644
--- a/src/app/shared/comcol-forms/comcol-form/comcol-form.component.html
+++ b/src/app/shared/comcol-forms/comcol-form/comcol-form.component.html
@@ -1,3 +1,38 @@
+<div class="container-fluid">
+  <div class="row">
+    <div class="col-12 d-inline-block">
+      <label>{{type.value + '.edit.logo.label' | translate}}</label>
+    </div>
+    <ng-container *ngVar="(dso?.logo | async)?.payload as logo">
+      <div class="col-12 d-inline-block alert" [ngClass]="{'alert-danger': markLogoForDeletion}" id="logo-section">
+        <div class="row">
+          <div class="col-8 d-inline-block">
+            <ds-comcol-page-logo [logo]="logo"></ds-comcol-page-logo>
+          </div>
+          <div class="col-4 d-inline-block">
+            <div *ngIf="logo" class="btn-group btn-group-sm float-right" role="group">
+              <button *ngIf="!markLogoForDeletion" type="button" class="btn btn-danger" (click)="deleteLogo()">
+                <i class="fas fa-trash" aria-hidden="true"></i>
+              </button>
+              <button *ngIf="markLogoForDeletion" type="button" class="btn btn-warning" (click)="undoDeleteLogo()">
+                <i class="fas fa-undo" aria-hidden="true"></i>
+              </button>
+            </div>
+          </div>
+        </div>
+      </div>
+      <div *ngIf="!logo" class="col-12 d-inline-block">
+        <ds-uploader  *ngIf="initializedUploaderOptions | async"
+                      [dropMsg]="type.value + '.edit.logo.upload'"
+                      [dropOverDocumentMsg]="type.value + '.edit.logo.upload'"
+                      [enableDragOverDocument]="true"
+                      [uploadFilesOptions]="uploadFilesOptions"
+                      (onCompleteItem)="onCompleteItem()"
+                      (onUploadError)="onUploadError()"></ds-uploader>
+      </div>
+    </ng-container>
+  </div>
+</div>
 <ds-form *ngIf="formModel"
          [formId]="'comcol-form-id'"
          [formModel]="formModel" (submitForm)="onSubmit()" (cancel)="onCancel()"></ds-form>
diff --git a/src/app/shared/comcol-forms/comcol-form/comcol-form.component.spec.ts b/src/app/shared/comcol-forms/comcol-form/comcol-form.component.spec.ts
index 27d50d6f9bb7cda1c039d13fbfdac1c2a1ff448a..3ed55a1a38ecc664a4808b79f7f988a0228f785b 100644
--- a/src/app/shared/comcol-forms/comcol-form/comcol-form.component.spec.ts
+++ b/src/app/shared/comcol-forms/comcol-form/comcol-form.component.spec.ts
@@ -9,6 +9,19 @@ import { Community } from '../../../core/shared/community.model';
 import { ComColFormComponent } from './comcol-form.component';
 import { DSpaceObject } from '../../../core/shared/dspace-object.model';
 import { hasValue } from '../../empty.util';
+import { VarDirective } from '../../utils/var.directive';
+import { NotificationsService } from '../../notifications/notifications.service';
+import { NotificationsServiceStub } from '../../testing/notifications-service-stub';
+import { AuthService } from '../../../core/auth/auth.service';
+import { AuthServiceMock } from '../../mocks/mock-auth.service';
+import { of as observableOf } from 'rxjs';
+import { RemoteData } from '../../../core/data/remote-data';
+import { RestRequestMethod } from '../../../core/data/rest-request-method';
+import { ErrorResponse, RestResponse } from '../../../core/cache/response.models';
+import { RequestError } from '../../../core/data/request.models';
+import { RequestService } from '../../../core/data/request.service';
+import { ObjectCacheService } from '../../../core/cache/object-cache.service';
+import { By } from '@angular/platform-browser';
 
 describe('ComColFormComponent', () => {
   let comp: ComColFormComponent<DSpaceObject>;
@@ -47,71 +60,264 @@ describe('ComColFormComponent', () => {
     })
   ];
 
+  const logoEndpoint = 'rest/api/logo/endpoint';
+  const dsoService = Object.assign({
+    getLogoEndpoint: () => observableOf(logoEndpoint),
+    deleteLogo: () => observableOf({})
+  });
+  const notificationsService = new NotificationsServiceStub();
+
   /* tslint:disable:no-empty */
   const locationStub = jasmine.createSpyObj('location', ['back']);
   /* tslint:enable:no-empty */
 
+  const requestServiceStub = jasmine.createSpyObj({
+    removeByHrefSubstring: {}
+  });
+  const objectCacheStub = jasmine.createSpyObj({
+    remove: {}
+  });
+
   beforeEach(async(() => {
     TestBed.configureTestingModule({
       imports: [TranslateModule.forRoot(), RouterTestingModule],
-      declarations: [ComColFormComponent],
+      declarations: [ComColFormComponent, VarDirective],
       providers: [
         { provide: Location, useValue: locationStub },
-        { provide: DynamicFormService, useValue: formServiceStub }
+        { provide: DynamicFormService, useValue: formServiceStub },
+        { provide: NotificationsService, useValue: notificationsService },
+        { provide: AuthService, useValue: new AuthServiceMock() },
+        { provide: RequestService, useValue: requestServiceStub },
+        { provide: ObjectCacheService, useValue: objectCacheStub }
       ],
       schemas: [NO_ERRORS_SCHEMA]
     }).compileComponents();
   }));
 
-  beforeEach(() => {
-    fixture = TestBed.createComponent(ComColFormComponent);
-    comp = fixture.componentInstance;
-    comp.formModel = [];
-    comp.dso = new Community();
-    fixture.detectChanges();
-    location = (comp as any).location;
-  });
-
-  describe('onSubmit', () => {
+  describe('when the dso doesn\'t contain an ID (newly created)', () => {
     beforeEach(() => {
-      spyOn(comp.submitForm, 'emit');
-      comp.formModel = formModel;
+      initComponent(new Community());
     });
 
-    it('should emit the new version of the community', () => {
-      comp.dso = Object.assign(
-        new Community(),
-        {
-          metadata: {
-            ...titleMD,
-            ...randomMD
-          }
-        }
-      );
+    it('should initialize the uploadFilesOptions with a placeholder url', () => {
+      expect(comp.uploadFilesOptions.url.length).toBeGreaterThan(0);
+    });
 
-      comp.onSubmit();
+    describe('onSubmit', () => {
+      beforeEach(() => {
+        spyOn(comp.submitForm, 'emit');
+        comp.formModel = formModel;
+      });
 
-      expect(comp.submitForm.emit).toHaveBeenCalledWith(
-        Object.assign(
-          {},
+      it('should emit the new version of the community', () => {
+        comp.dso = Object.assign(
           new Community(),
           {
             metadata: {
-              ...newTitleMD,
-              ...randomMD,
-              ...abstractMD
-            },
-            type: Community.type
-          },
-        )
-      );
-    })
-  });
+              ...titleMD,
+              ...randomMD
+            }
+          }
+        );
+
+        comp.onSubmit();
 
-  describe('onCancel', () => {
-    it('should call the back method on the Location service', () => {
+        expect(comp.submitForm.emit).toHaveBeenCalledWith(
+          {
+            dso: Object.assign(
+              {},
+              new Community(),
+              {
+                metadata: {
+                  ...newTitleMD,
+                  ...randomMD,
+                  ...abstractMD
+                },
+                type: Community.type
+              },
+            ),
+            uploader: {},
+            deleteLogo: false
+          }
+        );
+      })
+    });
+
+    describe('onCancel', () => {
+      it('should call the back method on the Location service', () => {
         comp.onCancel();
         expect(locationStub.back).toHaveBeenCalled();
+      });
+    });
+
+    describe('onCompleteItem', () => {
+      beforeEach(() => {
+        spyOn(comp.finish, 'emit');
+        comp.onCompleteItem();
+      });
+
+      it('should show a success notification', () => {
+        expect(notificationsService.success).toHaveBeenCalled();
+      });
+
+      it('should emit finish', () => {
+        expect(comp.finish.emit).toHaveBeenCalled();
+      });
+
+      it('should remove the object\'s cache', () => {
+        expect(requestServiceStub.removeByHrefSubstring).toHaveBeenCalled();
+        expect(objectCacheStub.remove).toHaveBeenCalled();
+      });
+    });
+
+    describe('onUploadError', () => {
+      beforeEach(() => {
+        spyOn(comp.finish, 'emit');
+        comp.onUploadError();
+      });
+
+      it('should show an error notification', () => {
+        expect(notificationsService.error).toHaveBeenCalled();
+      });
+
+      it('should emit finish', () => {
+        expect(comp.finish.emit).toHaveBeenCalled();
+      });
     });
   });
+
+  describe('when the dso contains an ID (being edited)', () => {
+    describe('and the dso doesn\'t contain a logo', () => {
+      beforeEach(() => {
+        initComponent(Object.assign(new Community(), {
+          id: 'community-id',
+          logo: observableOf(new RemoteData(false, false, true, null, undefined))
+        }));
+      });
+
+      it('should initialize the uploadFilesOptions with the logo\'s endpoint url', () => {
+        expect(comp.uploadFilesOptions.url).toEqual(logoEndpoint);
+      });
+
+      it('should initialize the uploadFilesOptions with a POST method', () => {
+        expect(comp.uploadFilesOptions.method).toEqual(RestRequestMethod.POST);
+      });
+    });
+
+    describe('and the dso contains a logo', () => {
+      beforeEach(() => {
+        initComponent(Object.assign(new Community(), {
+          id: 'community-id',
+          logo: observableOf(new RemoteData(false, false, true, null, {}))
+        }));
+      });
+
+      it('should initialize the uploadFilesOptions with the logo\'s endpoint url', () => {
+        expect(comp.uploadFilesOptions.url).toEqual(logoEndpoint);
+      });
+
+      it('should initialize the uploadFilesOptions with a PUT method', () => {
+        expect(comp.uploadFilesOptions.method).toEqual(RestRequestMethod.PUT);
+      });
+
+      describe('submit with logo marked for deletion', () => {
+        beforeEach(() => {
+          comp.markLogoForDeletion = true;
+        });
+
+        describe('when dsoService.deleteLogo returns a successful response', () => {
+          const response = new RestResponse(true, 200, 'OK');
+
+          beforeEach(() => {
+            spyOn(dsoService, 'deleteLogo').and.returnValue(observableOf(response));
+            comp.onSubmit();
+          });
+
+          it('should display a success notification', () => {
+            expect(notificationsService.success).toHaveBeenCalled();
+          });
+        });
+
+        describe('when dsoService.deleteLogo returns an error response', () => {
+          const response = new ErrorResponse(new RequestError('errorMessage'));
+
+          beforeEach(() => {
+            spyOn(dsoService, 'deleteLogo').and.returnValue(observableOf(response));
+            comp.onSubmit();
+          });
+
+          it('should display an error notification', () => {
+            expect(notificationsService.error).toHaveBeenCalled();
+          });
+        });
+      });
+
+      describe('deleteLogo', () => {
+        beforeEach(() => {
+          comp.deleteLogo();
+          fixture.detectChanges();
+        });
+
+        it('should set markLogoForDeletion to true', () => {
+          expect(comp.markLogoForDeletion).toEqual(true);
+        });
+
+        it('should mark the logo section with a danger alert', () => {
+          const logoSection = fixture.debugElement.query(By.css('#logo-section.alert-danger'));
+          expect(logoSection).toBeTruthy();
+        });
+
+        it('should hide the delete button', () => {
+          const button = fixture.debugElement.query(By.css('#logo-section .btn-danger'));
+          expect(button).not.toBeTruthy();
+        });
+
+        it('should show the undo button', () => {
+          const button = fixture.debugElement.query(By.css('#logo-section .btn-warning'));
+          expect(button).toBeTruthy();
+        });
+      });
+
+      describe('undoDeleteLogo', () => {
+        beforeEach(() => {
+          comp.markLogoForDeletion = true;
+          comp.undoDeleteLogo();
+          fixture.detectChanges();
+        });
+
+        it('should set markLogoForDeletion to false', () => {
+          expect(comp.markLogoForDeletion).toEqual(false);
+        });
+
+        it('should disable the danger alert on the logo section', () => {
+          const logoSection = fixture.debugElement.query(By.css('#logo-section.alert-danger'));
+          expect(logoSection).not.toBeTruthy();
+        });
+
+        it('should show the delete button', () => {
+          const button = fixture.debugElement.query(By.css('#logo-section .btn-danger'));
+          expect(button).toBeTruthy();
+        });
+
+        it('should hide the undo button', () => {
+          const button = fixture.debugElement.query(By.css('#logo-section .btn-warning'));
+          expect(button).not.toBeTruthy();
+        });
+      });
+    });
+  });
+
+  function initComponent(dso: Community) {
+    fixture = TestBed.createComponent(ComColFormComponent);
+    comp = fixture.componentInstance;
+    comp.formModel = [];
+    comp.dso = dso;
+    (comp as any).type = Community.type;
+    comp.uploaderComponent = Object.assign({
+      uploader: {}
+    });
+    (comp as any).dsoService = dsoService;
+    fixture.detectChanges();
+    location = (comp as any).location;
+  }
 });
diff --git a/src/app/shared/comcol-forms/comcol-form/comcol-form.component.ts b/src/app/shared/comcol-forms/comcol-form/comcol-form.component.ts
index dc8b924142f4ecb76cc79f167869232f6aa474ac..435ef61d723a22dcaa595c5af47159f6fae4d376 100644
--- a/src/app/shared/comcol-forms/comcol-form/comcol-form.component.ts
+++ b/src/app/shared/comcol-forms/comcol-form/comcol-form.component.ts
@@ -1,4 +1,4 @@
-import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
+import { Component, EventEmitter, Input, OnDestroy, OnInit, Output, ViewChild } from '@angular/core';
 import { Location } from '@angular/common';
 import {
   DynamicFormControlModel,
@@ -11,8 +11,24 @@ import { TranslateService } from '@ngx-translate/core';
 import { DSpaceObject } from '../../../core/shared/dspace-object.model';
 import { MetadataMap, MetadataValue } from '../../../core/shared/metadata.models';
 import { ResourceType } from '../../../core/shared/resource-type';
-import { isNotEmpty } from '../../empty.util';
+import { hasValue, isNotEmpty } from '../../empty.util';
+import { UploaderOptions } from '../../uploader/uploader-options.model';
+import { NotificationsService } from '../../notifications/notifications.service';
+import { ComColDataService } from '../../../core/data/comcol-data.service';
+import { Subscription } from 'rxjs/internal/Subscription';
+import { AuthService } from '../../../core/auth/auth.service';
 import { Community } from '../../../core/shared/community.model';
+import { Collection } from '../../../core/shared/collection.model';
+import { UploaderComponent } from '../../uploader/uploader.component';
+import { FileUploader } from 'ng2-file-upload';
+import { ErrorResponse, RestResponse } from '../../../core/cache/response.models';
+import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject';
+import { RemoteData } from '../../../core/data/remote-data';
+import { Bitstream } from '../../../core/shared/bitstream.model';
+import { combineLatest as observableCombineLatest } from 'rxjs';
+import { RestRequestMethod } from '../../../core/data/rest-request-method';
+import { RequestService } from '../../../core/data/request.service';
+import { ObjectCacheService } from '../../../core/cache/object-cache.service';
 
 /**
  * A form for creating and editing Communities or Collections
@@ -22,7 +38,13 @@ import { Community } from '../../../core/shared/community.model';
   styleUrls: ['./comcol-form.component.scss'],
   templateUrl: './comcol-form.component.html'
 })
-export class ComColFormComponent<T extends DSpaceObject> implements OnInit {
+export class ComColFormComponent<T extends DSpaceObject> implements OnInit, OnDestroy {
+
+  /**
+   * The logo uploader component
+   */
+  @ViewChild(UploaderComponent) uploaderComponent: UploaderComponent;
+
   /**
    * DSpaceObject that the form represents
    */
@@ -31,7 +53,7 @@ export class ComColFormComponent<T extends DSpaceObject> implements OnInit {
   /**
    * Type of DSpaceObject that the form represents
    */
-  protected type: ResourceType;
+  type: ResourceType;
 
   /**
    * @type {string} Key prefix used to generate form labels
@@ -54,14 +76,56 @@ export class ComColFormComponent<T extends DSpaceObject> implements OnInit {
   formGroup: FormGroup;
 
   /**
-   * Emits DSO when the form is submitted
-   * @type {EventEmitter<any>}
+   * The uploader configuration options
+   * @type {UploaderOptions}
+   */
+  uploadFilesOptions: UploaderOptions = Object.assign(new UploaderOptions(), {
+    autoUpload: false
+  });
+
+  /**
+   * Emits DSO and Uploader when the form is submitted
    */
-  @Output() submitForm: EventEmitter<any> = new EventEmitter();
+  @Output() submitForm: EventEmitter<{
+    dso: T,
+    uploader: FileUploader,
+    deleteLogo: boolean
+  }> = new EventEmitter();
 
-  public constructor(private location: Location,
-                     private formService: DynamicFormService,
-                     private translate: TranslateService) {
+  /**
+   * Fires an event when the logo has finished uploading (with or without errors) or was removed
+   */
+  @Output() finish: EventEmitter<any> = new EventEmitter();
+
+  /**
+   * Observable keeping track whether or not the uploader has finished initializing
+   * Used to start rendering the uploader component
+   */
+  initializedUploaderOptions = new BehaviorSubject(false);
+
+  /**
+   * Is the logo marked to be deleted?
+   */
+  markLogoForDeletion = false;
+
+  /**
+   * Array to track all subscriptions and unsubscribe them onDestroy
+   * @type {Array}
+   */
+  protected subs: Subscription[] = [];
+
+  /**
+   * The service used to fetch from or send data to
+   */
+  protected dsoService: ComColDataService<Community | Collection>;
+
+  public constructor(protected location: Location,
+                     protected formService: DynamicFormService,
+                     protected translate: TranslateService,
+                     protected notificationsService: NotificationsService,
+                     protected authService: AuthService,
+                     protected requestService: RequestService,
+                     protected objectCache: ObjectCacheService) {
   }
 
   ngOnInit(): void {
@@ -77,13 +141,56 @@ export class ComColFormComponent<T extends DSpaceObject> implements OnInit {
       .subscribe(() => {
         this.updateFieldTranslations();
       });
+
+    if (hasValue(this.dso.id)) {
+      this.subs.push(
+        observableCombineLatest(
+          this.dsoService.getLogoEndpoint(this.dso.id),
+          (this.dso as any).logo
+        ).subscribe(([href, logoRD]: [string, RemoteData<Bitstream>]) => {
+          this.uploadFilesOptions.url = href;
+          this.uploadFilesOptions.authToken = this.authService.buildAuthHeader();
+          // If the object already contains a logo, send out a PUT request instead of POST for setting a new logo
+          if (hasValue(logoRD.payload)) {
+            this.uploadFilesOptions.method = RestRequestMethod.PUT;
+          }
+          this.initializedUploaderOptions.next(true);
+        })
+      );
+    } else {
+      // Set a placeholder URL to not break the uploader component. This will be replaced once the object is created.
+      this.uploadFilesOptions.url = 'placeholder';
+      this.uploadFilesOptions.authToken = this.authService.buildAuthHeader();
+      this.initializedUploaderOptions.next(true);
+    }
   }
 
   /**
    * Checks which new fields were added and sends the updated version of the DSO to the parent component
    */
   onSubmit() {
-    const formMetadata = {} as MetadataMap;
+    if (this.markLogoForDeletion && hasValue(this.dso.id)) {
+      this.dsoService.deleteLogo(this.dso).subscribe((response: RestResponse) => {
+        if (response.isSuccessful) {
+          this.notificationsService.success(
+            this.translate.get(this.type.value + '.edit.logo.notifications.delete.success.title'),
+            this.translate.get(this.type.value + '.edit.logo.notifications.delete.success.content')
+          );
+        } else {
+          const errorResponse = response as ErrorResponse;
+          this.notificationsService.error(
+            this.translate.get(this.type.value + '.edit.logo.notifications.delete.error.title'),
+            errorResponse.errorMessage
+          );
+        }
+        (this.dso as any).logo = undefined;
+        this.uploadFilesOptions.method = RestRequestMethod.POST;
+        this.refreshCache();
+        this.finish.emit();
+      });
+    }
+
+    const formMetadata = {}  as MetadataMap;
     this.formModel.forEach((fieldModel: DynamicInputModel) => {
       const value: MetadataValue = {
           value: fieldModel.value as string,
@@ -103,7 +210,11 @@ export class ComColFormComponent<T extends DSpaceObject> implements OnInit {
       },
       type: Community.type
     });
-    this.submitForm.emit(updatedDSO);
+    this.submitForm.emit({
+      dso: updatedDSO,
+      uploader: hasValue(this.uploaderComponent) ? this.uploaderComponent.uploader : undefined,
+      deleteLogo: this.markLogoForDeletion
+    });
   }
 
   /**
@@ -123,7 +234,59 @@ export class ComColFormComponent<T extends DSpaceObject> implements OnInit {
     );
   }
 
+  /**
+   * Mark the logo to be deleted
+   * Send out a delete request to remove the logo from the community/collection and display notifications
+   */
+  deleteLogo() {
+    this.markLogoForDeletion = true;
+  }
+
+  /**
+   * Undo marking the logo to be deleted
+   */
+  undoDeleteLogo() {
+    this.markLogoForDeletion = false;
+  }
+
+  /**
+   * Refresh the object's cache to ensure the latest version
+   */
+  private refreshCache() {
+    this.requestService.removeByHrefSubstring(this.dso.self);
+    this.objectCache.remove(this.dso.self);
+  }
+
+  /**
+   * The request was successful, display a success notification
+   */
+  public onCompleteItem() {
+    this.refreshCache();
+    this.notificationsService.success(null, this.translate.get(this.type.value + '.edit.logo.notifications.add.success'));
+    this.finish.emit();
+  }
+
+  /**
+   * The request was unsuccessful, display an error notification
+   */
+  public onUploadError() {
+    this.notificationsService.error(null, this.translate.get(this.type.value + '.edit.logo.notifications.add.error'));
+    this.finish.emit();
+  }
+
+  /**
+   * Cancel the form and return to the previous page
+   */
   onCancel() {
     this.location.back();
   }
+
+  /**
+   * Unsubscribe from open subscriptions
+   */
+  ngOnDestroy(): void {
+    this.subs
+      .filter((subscription) => hasValue(subscription))
+      .forEach((subscription) => subscription.unsubscribe());
+  }
 }
diff --git a/src/app/shared/comcol-forms/create-comcol-page/create-comcol-page.component.spec.ts b/src/app/shared/comcol-forms/create-comcol-page/create-comcol-page.component.spec.ts
index 6ad2e5b5e1ba7a6268b7a868ddc4509d7acb92c2..717979891fd6b166df93206646f0dc6d6a387ca6 100644
--- a/src/app/shared/comcol-forms/create-comcol-page/create-comcol-page.component.spec.ts
+++ b/src/app/shared/comcol-forms/create-comcol-page/create-comcol-page.component.spec.ts
@@ -11,11 +11,13 @@ import { RouterTestingModule } from '@angular/router/testing';
 import { NO_ERRORS_SCHEMA } from '@angular/core';
 import { DSpaceObject } from '../../../core/shared/dspace-object.model';
 import { CreateComColPageComponent } from './create-comcol-page.component';
-import { DataService } from '../../../core/data/data.service';
 import {
   createFailedRemoteDataObject$,
   createSuccessfulRemoteDataObject$
 } from '../../testing/utils';
+import { ComColDataService } from '../../../core/data/comcol-data.service';
+import { NotificationsService } from '../../notifications/notifications.service';
+import { NotificationsServiceStub } from '../../testing/notifications-service-stub';
 
 describe('CreateComColPageComponent', () => {
   let comp: CreateComColPageComponent<DSpaceObject>;
@@ -31,6 +33,8 @@ describe('CreateComColPageComponent', () => {
   let routeServiceStub;
   let routerStub;
 
+  const logoEndpoint = 'rest/api/logo/endpoint';
+
   function initializeVars() {
     community = Object.assign(new Community(), {
       uuid: 'a20da287-e174-466a-9926-f66b9300d347',
@@ -56,8 +60,8 @@ describe('CreateComColPageComponent', () => {
           value: community.name
         }]
       })),
-      create: (com, uuid?) => createSuccessfulRemoteDataObject$(newCommunity)
-
+      create: (com, uuid?) => createSuccessfulRemoteDataObject$(newCommunity),
+      getLogoEndpoint: () => observableOf(logoEndpoint)
     };
 
     routeServiceStub = {
@@ -74,10 +78,11 @@ describe('CreateComColPageComponent', () => {
     TestBed.configureTestingModule({
       imports: [TranslateModule.forRoot(), SharedModule, CommonModule, RouterTestingModule],
       providers: [
-        { provide: DataService, useValue: communityDataServiceStub },
+        { provide: ComColDataService, useValue: communityDataServiceStub },
         { provide: CommunityDataService, useValue: communityDataServiceStub },
         { provide: RouteService, useValue: routeServiceStub },
         { provide: Router, useValue: routerStub },
+        { provide: NotificationsService, useValue: new NotificationsServiceStub() }
       ],
       schemas: [NO_ERRORS_SCHEMA]
     }).compileComponents();
@@ -86,6 +91,7 @@ describe('CreateComColPageComponent', () => {
   beforeEach(() => {
     fixture = TestBed.createComponent(CreateComColPageComponent);
     comp = fixture.componentInstance;
+    (comp as any).type = Community.type;
     fixture.detectChanges();
     dsoDataService = (comp as any).dsoDataService;
     communityDataService = (comp as any).communityDataService;
@@ -95,27 +101,86 @@ describe('CreateComColPageComponent', () => {
 
   describe('onSubmit', () => {
     let data;
-    beforeEach(() => {
-      data = Object.assign(new Community(), {
-        metadata: [{
-          key: 'dc.title',
-          value: 'test'
-        }]
+
+    describe('with an empty queue in the uploader', () => {
+      beforeEach(() => {
+        data = {
+          dso: Object.assign(new Community(), {
+            metadata: [{
+              key: 'dc.title',
+              value: 'test'
+            }]
+          }),
+          uploader: {
+            options: {
+              url: ''
+            },
+            queue: [],
+            /* tslint:disable:no-empty */
+            uploadAll: () => {}
+            /* tslint:enable:no-empty */
+          }
+        };
+      });
+
+      it('should navigate when successful', () => {
+        spyOn(router, 'navigate');
+        comp.onSubmit(data);
+        fixture.detectChanges();
+        expect(router.navigate).toHaveBeenCalled();
+      });
+
+      it('should not navigate on failure', () => {
+        spyOn(router, 'navigate');
+        spyOn(dsoDataService, 'create').and.returnValue(createFailedRemoteDataObject$(newCommunity));
+        comp.onSubmit(data);
+        fixture.detectChanges();
+        expect(router.navigate).not.toHaveBeenCalled();
       });
-    });
-    it('should navigate when successful', () => {
-      spyOn(router, 'navigate');
-      comp.onSubmit(data);
-      fixture.detectChanges();
-      expect(router.navigate).toHaveBeenCalled();
     });
 
-    it('should not navigate on failure', () => {
-      spyOn(router, 'navigate');
-      spyOn(dsoDataService, 'create').and.returnValue(createFailedRemoteDataObject$(newCommunity));
-      comp.onSubmit(data);
-      fixture.detectChanges();
-      expect(router.navigate).not.toHaveBeenCalled();
+    describe('with at least one item in the uploader\'s queue', () => {
+      beforeEach(() => {
+        data = {
+          dso: Object.assign(new Community(), {
+            metadata: [{
+              key: 'dc.title',
+              value: 'test'
+            }]
+          }),
+          uploader: {
+            options: {
+              url: ''
+            },
+            queue: [
+              {}
+            ],
+            /* tslint:disable:no-empty */
+            uploadAll: () => {}
+            /* tslint:enable:no-empty */
+          }
+        };
+      });
+
+      it('should not navigate', () => {
+        spyOn(router, 'navigate');
+        comp.onSubmit(data);
+        fixture.detectChanges();
+        expect(router.navigate).not.toHaveBeenCalled();
+      });
+
+      it('should set the uploader\'s url to the logo\'s endpoint', () => {
+        comp.onSubmit(data);
+        fixture.detectChanges();
+        expect(data.uploader.options.url).toEqual(logoEndpoint);
+      });
+
+      it('should call the uploader\'s uploadAll', () => {
+        spyOn(data.uploader, 'uploadAll');
+        comp.onSubmit(data);
+        fixture.detectChanges();
+        expect(data.uploader.uploadAll).toHaveBeenCalled();
+      });
     });
   });
 });
diff --git a/src/app/shared/comcol-forms/create-comcol-page/create-comcol-page.component.ts b/src/app/shared/comcol-forms/create-comcol-page/create-comcol-page.component.ts
index e07f2a5a0ab3cd13a3d6987a9cdd902ff2813e28..7b23c5949872649694d13bf4452d2e0ef70a90d0 100644
--- a/src/app/shared/comcol-forms/create-comcol-page/create-comcol-page.component.ts
+++ b/src/app/shared/comcol-forms/create-comcol-page/create-comcol-page.component.ts
@@ -3,13 +3,17 @@ import { Community } from '../../../core/shared/community.model';
 import { CommunityDataService } from '../../../core/data/community-data.service';
 import { Observable } from 'rxjs';
 import { RouteService } from '../../../core/services/route.service';
-import { Router } from '@angular/router';
+import { ActivatedRoute, Router } from '@angular/router';
 import { RemoteData } from '../../../core/data/remote-data';
-import { isNotEmpty, isNotUndefined } from '../../empty.util';
+import { hasValue, isNotEmpty, isNotUndefined } from '../../empty.util';
 import { take } from 'rxjs/operators';
 import { getSucceededRemoteData } from '../../../core/shared/operators';
 import { DSpaceObject } from '../../../core/shared/dspace-object.model';
 import { DataService } from '../../../core/data/data.service';
+import { ComColDataService } from '../../../core/data/comcol-data.service';
+import { NotificationsService } from '../../notifications/notifications.service';
+import { TranslateService } from '@ngx-translate/core';
+import { ResourceType } from '../../../core/shared/resource-type';
 
 /**
  * Component representing the create page for communities and collections
@@ -34,11 +38,23 @@ export class CreateComColPageComponent<TDomain extends DSpaceObject> implements
    */
   public parentRD$: Observable<RemoteData<Community>>;
 
+  /**
+   * The UUID of the newly created object
+   */
+  private newUUID: string;
+
+  /**
+   * The type of the dso
+   */
+  protected type: ResourceType;
+
   public constructor(
-    protected dsoDataService: DataService<TDomain>,
+    protected dsoDataService: ComColDataService<TDomain>,
     protected parentDataService: CommunityDataService,
     protected routeService: RouteService,
-    protected router: Router
+    protected router: Router,
+    protected notificationsService: NotificationsService,
+    protected translate: TranslateService
   ) {
 
   }
@@ -53,20 +69,40 @@ export class CreateComColPageComponent<TDomain extends DSpaceObject> implements
   }
 
   /**
-   * @param {TDomain} dso The updated version of the DSO
    * Creates a new DSO based on the submitted user data and navigates to the new object's home page
+   * @param event   The event returned by the community/collection form. Contains the new dso and logo uploader
    */
-  onSubmit(dso: TDomain) {
+  onSubmit(event) {
+    const dso = event.dso;
+    const uploader = event.uploader;
+
     this.parentUUID$.pipe(take(1)).subscribe((uuid: string) => {
       this.dsoDataService.create(dso, uuid)
         .pipe(getSucceededRemoteData())
         .subscribe((dsoRD: RemoteData<TDomain>) => {
           if (isNotUndefined(dsoRD)) {
-            const newUUID = dsoRD.payload.uuid;
-            this.router.navigate([this.frontendURL + newUUID]);
+            this.newUUID = dsoRD.payload.uuid;
+            if (uploader.queue.length > 0) {
+              this.dsoDataService.getLogoEndpoint(this.newUUID).pipe(take(1)).subscribe((href: string) => {
+                uploader.options.url = href;
+                uploader.uploadAll();
+              });
+            } else {
+              this.navigateToNewPage();
+            }
+            this.notificationsService.success(null, this.translate.get(this.type.value + '.create.notifications.success'));
           }
         });
     });
   }
 
+  /**
+   * Navigate to the page of the newly created object
+   */
+  navigateToNewPage() {
+    if (hasValue(this.newUUID)) {
+      this.router.navigate([this.frontendURL + this.newUUID]);
+    }
+  }
+
 }
diff --git a/src/app/shared/comcol-forms/edit-comcol-page/comcol-metadata/comcol-metadata.component.spec.ts b/src/app/shared/comcol-forms/edit-comcol-page/comcol-metadata/comcol-metadata.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5711aa4e701433c6656479a3e6306e906cc471f3
--- /dev/null
+++ b/src/app/shared/comcol-forms/edit-comcol-page/comcol-metadata/comcol-metadata.component.spec.ts
@@ -0,0 +1,189 @@
+import { DSpaceObject } from '../../../../core/shared/dspace-object.model';
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { CommunityDataService } from '../../../../core/data/community-data.service';
+import { ActivatedRoute, Router } from '@angular/router';
+import { Community } from '../../../../core/shared/community.model';
+import { of as observableOf } from 'rxjs/internal/observable/of';
+import { RemoteData } from '../../../../core/data/remote-data';
+import { TranslateModule } from '@ngx-translate/core';
+import { SharedModule } from '../../../shared.module';
+import { CommonModule } from '@angular/common';
+import { RouterTestingModule } from '@angular/router/testing';
+import { DataService } from '../../../../core/data/data.service';
+import { NO_ERRORS_SCHEMA } from '@angular/core';
+import { ComcolMetadataComponent } from './comcol-metadata.component';
+import { createFailedRemoteDataObject$, createSuccessfulRemoteDataObject$ } from '../../../testing/utils';
+import { ComColDataService } from '../../../../core/data/comcol-data.service';
+import { NotificationsServiceStub } from '../../../testing/notifications-service-stub';
+import { NotificationsService } from '../../../notifications/notifications.service';
+
+describe('ComColMetadataComponent', () => {
+  let comp: ComcolMetadataComponent<DSpaceObject>;
+  let fixture: ComponentFixture<ComcolMetadataComponent<DSpaceObject>>;
+  let dsoDataService: CommunityDataService;
+  let router: Router;
+
+  let community;
+  let newCommunity;
+  let communityDataServiceStub;
+  let routerStub;
+  let routeStub;
+
+  const logoEndpoint = 'rest/api/logo/endpoint';
+
+  function initializeVars() {
+    community = Object.assign(new Community(), {
+      uuid: 'a20da287-e174-466a-9926-f66b9300d347',
+      metadata: [{
+        key: 'dc.title',
+        value: 'test community'
+      }]
+    });
+
+    newCommunity = Object.assign(new Community(), {
+      uuid: '1ff59938-a69a-4e62-b9a4-718569c55d48',
+      metadata: [{
+        key: 'dc.title',
+        value: 'new community'
+      }]
+    });
+
+    communityDataServiceStub = {
+      update: (com, uuid?) => createSuccessfulRemoteDataObject$(newCommunity),
+      getLogoEndpoint: () => observableOf(logoEndpoint)
+    };
+
+    routerStub = {
+      navigate: (commands) => commands
+    };
+
+    routeStub = {
+      parent: {
+        data: observableOf({
+          dso: new RemoteData(false, false, true, null, community)
+        })
+      }
+    };
+
+  }
+
+  beforeEach(async(() => {
+    initializeVars();
+    TestBed.configureTestingModule({
+      imports: [TranslateModule.forRoot(), SharedModule, CommonModule, RouterTestingModule],
+      providers: [
+        { provide: ComColDataService, useValue: communityDataServiceStub },
+        { provide: Router, useValue: routerStub },
+        { provide: ActivatedRoute, useValue: routeStub },
+        { provide: NotificationsService, useValue: new NotificationsServiceStub() }
+      ],
+      schemas: [NO_ERRORS_SCHEMA]
+    }).compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(ComcolMetadataComponent);
+    comp = fixture.componentInstance;
+    (comp as any).type = Community.type;
+    fixture.detectChanges();
+    dsoDataService = (comp as any).dsoDataService;
+    router = (comp as any).router;
+  });
+
+  describe('onSubmit', () => {
+    let data;
+
+    describe('with an empty queue in the uploader', () => {
+      beforeEach(() => {
+        data = {
+          dso: Object.assign(new Community(), {
+            metadata: [{
+              key: 'dc.title',
+              value: 'test'
+            }]
+          }),
+          uploader: {
+            options: {
+              url: ''
+            },
+            queue: [],
+            /* tslint:disable:no-empty */
+            uploadAll: () => {}
+            /* tslint:enable:no-empty */
+          }
+        }
+      });
+
+      it('should navigate when successful', () => {
+        spyOn(router, 'navigate');
+        comp.onSubmit(data);
+        fixture.detectChanges();
+        expect(router.navigate).toHaveBeenCalled();
+      });
+
+      it('should not navigate on failure', () => {
+        spyOn(router, 'navigate');
+        spyOn(dsoDataService, 'update').and.returnValue(createFailedRemoteDataObject$(newCommunity));
+        comp.onSubmit(data);
+        fixture.detectChanges();
+        expect(router.navigate).not.toHaveBeenCalled();
+      });
+    });
+
+    describe('with at least one item in the uploader\'s queue', () => {
+      beforeEach(() => {
+        data = {
+          dso: Object.assign(new Community(), {
+            metadata: [{
+              key: 'dc.title',
+              value: 'test'
+            }]
+          }),
+          uploader: {
+            options: {
+              url: ''
+            },
+            queue: [
+              {}
+            ],
+            /* tslint:disable:no-empty */
+            uploadAll: () => {}
+            /* tslint:enable:no-empty */
+          }
+        }
+      });
+
+      it('should not navigate', () => {
+        spyOn(router, 'navigate');
+        comp.onSubmit(data);
+        fixture.detectChanges();
+        expect(router.navigate).not.toHaveBeenCalled();
+      });
+
+      it('should set the uploader\'s url to the logo\'s endpoint', () => {
+        comp.onSubmit(data);
+        fixture.detectChanges();
+        expect(data.uploader.options.url).toEqual(logoEndpoint);
+      });
+
+      it('should call the uploader\'s uploadAll', () => {
+        spyOn(data.uploader, 'uploadAll');
+        comp.onSubmit(data);
+        fixture.detectChanges();
+        expect(data.uploader.uploadAll).toHaveBeenCalled();
+      });
+    });
+  });
+
+  describe('navigateToHomePage', () => {
+    beforeEach(() => {
+      spyOn(router, 'navigate');
+      comp.navigateToHomePage();
+    });
+
+    it('should navigate', () => {
+      expect(router.navigate).toHaveBeenCalled();
+    });
+  });
+
+});
diff --git a/src/app/shared/comcol-forms/edit-comcol-page/comcol-metadata/comcol-metadata.component.ts b/src/app/shared/comcol-forms/edit-comcol-page/comcol-metadata/comcol-metadata.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1031fead10fc4f4c4ff04347cc677786c10d2a31
--- /dev/null
+++ b/src/app/shared/comcol-forms/edit-comcol-page/comcol-metadata/comcol-metadata.component.ts
@@ -0,0 +1,85 @@
+import { Component, OnInit } from '@angular/core';
+import { DSpaceObject } from '../../../../core/shared/dspace-object.model';
+import { Observable } from 'rxjs/internal/Observable';
+import { RemoteData } from '../../../../core/data/remote-data';
+import { ActivatedRoute, Router } from '@angular/router';
+import { first, map, take } from 'rxjs/operators';
+import { getSucceededRemoteData } from '../../../../core/shared/operators';
+import { hasValue, isNotUndefined } from '../../../empty.util';
+import { DataService } from '../../../../core/data/data.service';
+import { ResourceType } from '../../../../core/shared/resource-type';
+import { ComColDataService } from '../../../../core/data/comcol-data.service';
+import { NotificationsService } from '../../../notifications/notifications.service';
+import { TranslateService } from '@ngx-translate/core';
+
+@Component({
+  selector: 'ds-comcol-metadata',
+  template: ''
+})
+export class ComcolMetadataComponent<TDomain extends DSpaceObject> implements OnInit {
+  /**
+   * Frontend endpoint for this type of DSO
+   */
+  protected frontendURL: string;
+  /**
+   * The initial DSO object
+   */
+  public dsoRD$: Observable<RemoteData<TDomain>>;
+
+  /**
+   * The type of the dso
+   */
+  protected type: ResourceType;
+
+  public constructor(
+    protected dsoDataService: ComColDataService<TDomain>,
+    protected router: Router,
+    protected route: ActivatedRoute,
+    protected notificationsService: NotificationsService,
+    protected translate: TranslateService
+  ) {
+  }
+
+  ngOnInit(): void {
+    this.dsoRD$ = this.route.parent.data.pipe(first(), map((data) => data.dso));
+  }
+
+  /**
+   * Updates an existing DSO based on the submitted user data and navigates to the edited object's home page
+   * @param event   The event returned by the community/collection form. Contains the new dso and logo uploader
+   */
+  onSubmit(event) {
+    const dso = event.dso;
+    const uploader = event.uploader;
+    const deleteLogo = event.deleteLogo;
+
+    this.dsoDataService.update(dso)
+      .pipe(getSucceededRemoteData())
+      .subscribe((dsoRD: RemoteData<TDomain>) => {
+        if (isNotUndefined(dsoRD)) {
+          const newUUID = dsoRD.payload.uuid;
+          if (hasValue(uploader) && uploader.queue.length > 0) {
+            this.dsoDataService.getLogoEndpoint(newUUID).pipe(take(1)).subscribe((href: string) => {
+              uploader.options.url = href;
+              uploader.uploadAll();
+            });
+          } else if (!deleteLogo) {
+            this.router.navigate([this.frontendURL + newUUID]);
+          }
+          this.notificationsService.success(null, this.translate.get(this.type.value + '.edit.notifications.success'));
+        }
+      });
+  }
+
+  /**
+   * Navigate to the home page of the object
+   */
+  navigateToHomePage() {
+    this.dsoRD$.pipe(
+      getSucceededRemoteData(),
+      take(1)
+    ).subscribe((dsoRD: RemoteData<TDomain>) => {
+      this.router.navigate([this.frontendURL + dsoRD.payload.id]);
+      });
+  }
+}
diff --git a/src/app/shared/comcol-forms/edit-comcol-page/edit-comcol-page.component.html b/src/app/shared/comcol-forms/edit-comcol-page/edit-comcol-page.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..aa6290ea9fff3a524531f6fb9ae6a2d2d8cc0d64
--- /dev/null
+++ b/src/app/shared/comcol-forms/edit-comcol-page/edit-comcol-page.component.html
@@ -0,0 +1,24 @@
+<div class="container">
+  <div class="row">
+    <div class="col-12">
+      <h2 class="border-bottom">{{ type + '.edit.head' | translate }}</h2>
+      <div class="pt-2">
+        <ul class="nav nav-tabs justify-content-start mb-2">
+          <li *ngFor="let page of pages" class="nav-item">
+            <a class="nav-link"
+               [ngClass]="{'active' : page === currentPage}"
+               [routerLink]="['./' + page]">
+              {{ type + '.edit.tabs.' + page + '.head' | translate}}
+            </a>
+          </li>
+        </ul>
+        <div class="tab-pane active">
+          <div class="mb-4">
+            <router-outlet></router-outlet>
+          </div>
+          <a *ngIf="!hideReturnButton" [routerLink]="getPageUrl((dsoRD$ | async)?.payload)" class="btn btn-outline-secondary">{{ type + '.edit.return' | translate }}</a>
+        </div>
+      </div>
+    </div>
+  </div>
+</div>
diff --git a/src/app/shared/comcol-forms/edit-comcol-page/edit-comcol-page.component.spec.ts b/src/app/shared/comcol-forms/edit-comcol-page/edit-comcol-page.component.spec.ts
index 03f751599f8e53b81b426435d6b31b0c67424e78..d1b87db7ae77b942a023168495de8abcb80e39b0 100644
--- a/src/app/shared/comcol-forms/edit-comcol-page/edit-comcol-page.component.spec.ts
+++ b/src/app/shared/comcol-forms/edit-comcol-page/edit-comcol-page.component.spec.ts
@@ -1,5 +1,4 @@
 import { async, ComponentFixture, TestBed } from '@angular/core/testing';
-import { CommunityDataService } from '../../../core/data/community-data.service';
 import { ActivatedRoute, Router } from '@angular/router';
 import { TranslateModule } from '@ngx-translate/core';
 import { of as observableOf } from 'rxjs';
@@ -10,21 +9,13 @@ import { RouterTestingModule } from '@angular/router/testing';
 import { NO_ERRORS_SCHEMA } from '@angular/core';
 import { DSpaceObject } from '../../../core/shared/dspace-object.model';
 import { EditComColPageComponent } from './edit-comcol-page.component';
-import { DataService } from '../../../core/data/data.service';
-import {
-  createFailedRemoteDataObject$,
-  createSuccessfulRemoteDataObject$
-} from '../../testing/utils';
 
 describe('EditComColPageComponent', () => {
   let comp: EditComColPageComponent<DSpaceObject>;
   let fixture: ComponentFixture<EditComColPageComponent<DSpaceObject>>;
-  let dsoDataService: CommunityDataService;
   let router: Router;
 
   let community;
-  let newCommunity;
-  let communityDataServiceStub;
   let routerStub;
   let routeStub;
 
@@ -37,25 +28,33 @@ describe('EditComColPageComponent', () => {
       }]
     });
 
-    newCommunity = Object.assign(new Community(), {
-      uuid: '1ff59938-a69a-4e62-b9a4-718569c55d48',
-      metadata: [{
-        key: 'dc.title',
-        value: 'new community'
-      }]
-    });
-
-    communityDataServiceStub = {
-      update: (com, uuid?) => createSuccessfulRemoteDataObject$(newCommunity)
-
-    };
-
     routerStub = {
-      navigate: (commands) => commands
+      navigate: (commands) => commands,
+      events: observableOf({}),
+      url: 'mockUrl'
     };
 
     routeStub = {
-      data: observableOf(community)
+      data: observableOf({
+        dso: community
+      }),
+      routeConfig: {
+        children: [
+          {
+            path: 'mockUrl',
+            data: {
+              hideReturnButton: false
+            }
+          }
+        ]
+      },
+      snapshot: {
+        firstChild: {
+          routeConfig: {
+            path: 'mockUrl'
+          }
+        }
+      }
     };
 
   }
@@ -65,7 +64,6 @@ describe('EditComColPageComponent', () => {
     TestBed.configureTestingModule({
       imports: [TranslateModule.forRoot(), SharedModule, CommonModule, RouterTestingModule],
       providers: [
-        { provide: DataService, useValue: communityDataServiceStub },
         { provide: Router, useValue: routerStub },
         { provide: ActivatedRoute, useValue: routeStub },
       ],
@@ -77,33 +75,16 @@ describe('EditComColPageComponent', () => {
     fixture = TestBed.createComponent(EditComColPageComponent);
     comp = fixture.componentInstance;
     fixture.detectChanges();
-    dsoDataService = (comp as any).dsoDataService;
     router = (comp as any).router;
   });
 
-  describe('onSubmit', () => {
-    let data;
+  describe('getPageUrl', () => {
+    let url;
     beforeEach(() => {
-      data = Object.assign(new Community(), {
-        metadata: [{
-          key: 'dc.title',
-          value: 'test'
-        }]
-      });
+      url = comp.getPageUrl(community);
     });
-    it('should navigate when successful', () => {
-      spyOn(router, 'navigate');
-      comp.onSubmit(data);
-      fixture.detectChanges();
-      expect(router.navigate).toHaveBeenCalled();
-    });
-
-    it('should not navigate on failure', () => {
-      spyOn(router, 'navigate');
-      spyOn(dsoDataService, 'update').and.returnValue(createFailedRemoteDataObject$(newCommunity));
-      comp.onSubmit(data);
-      fixture.detectChanges();
-      expect(router.navigate).not.toHaveBeenCalled();
+    it('should return the current url as a fallback', () => {
+      expect(url).toEqual(routerStub.url);
     });
   });
 });
diff --git a/src/app/shared/comcol-forms/edit-comcol-page/edit-comcol-page.component.ts b/src/app/shared/comcol-forms/edit-comcol-page/edit-comcol-page.component.ts
index 24181b5e6175adee254d86b47e8cd2c85b317e81..0f9d4c55b471960198a97b662fdaf53f60d9496e 100644
--- a/src/app/shared/comcol-forms/edit-comcol-page/edit-comcol-page.component.ts
+++ b/src/app/shared/comcol-forms/edit-comcol-page/edit-comcol-page.component.ts
@@ -2,7 +2,7 @@ import { Component, OnInit } from '@angular/core';
 import { Observable } from 'rxjs';
 import { ActivatedRoute, Router } from '@angular/router';
 import { RemoteData } from '../../../core/data/remote-data';
-import { isNotUndefined } from '../../empty.util';
+import { isNotEmpty, isNotUndefined } from '../../empty.util';
 import { first, map } from 'rxjs/operators';
 import { getSucceededRemoteData } from '../../../core/shared/operators';
 import { DataService } from '../../../core/data/data.service';
@@ -17,37 +17,54 @@ import { DSpaceObject } from '../../../core/shared/dspace-object.model';
 })
 export class EditComColPageComponent<TDomain extends DSpaceObject> implements OnInit {
   /**
-   * Frontend endpoint for this type of DSO
+   * The type of DSpaceObject (used to create i18n messages)
    */
-  protected frontendURL: string;
+  public type: string;
+
+  /**
+   * The current page outlet string
+   */
+  public currentPage: string;
+
+  /**
+   * All possible page outlet strings
+   */
+  public pages: string[];
+
   /**
-   * The initial DSO object
+   * The DSO to render the edit page for
    */
   public dsoRD$: Observable<RemoteData<TDomain>>;
 
+  /**
+   * Hide the default return button?
+   */
+  public hideReturnButton: boolean;
+
   public constructor(
-    protected dsoDataService: DataService<TDomain>,
     protected router: Router,
     protected route: ActivatedRoute
   ) {
+    this.router.events.subscribe(() => {
+      this.currentPage = this.route.snapshot.firstChild.routeConfig.path;
+      this.hideReturnButton = this.route.routeConfig.children
+        .find((child: any) => child.path === this.currentPage).data.hideReturnButton;
+    });
   }
 
   ngOnInit(): void {
+    this.pages = this.route.routeConfig.children
+      .map((child: any) => child.path)
+      .filter((path: string) => isNotEmpty(path)); // ignore reroutes
     this.dsoRD$ = this.route.data.pipe(first(), map((data) => data.dso));
   }
 
   /**
-   * @param {TDomain} dso The updated version of the DSO
-   * Updates an existing DSO based on the submitted user data and navigates to the edited object's home page
+   * Get the dso's page url
+   * This method is expected to be overridden in the edit community/collection page components
+   * @param dso The DSpaceObject for which the url is requested
    */
-  onSubmit(dso: TDomain) {
-    this.dsoDataService.update(dso)
-      .pipe(getSucceededRemoteData())
-      .subscribe((dsoRD: RemoteData<TDomain>) => {
-        if (isNotUndefined(dsoRD)) {
-          const newUUID = dsoRD.payload.uuid;
-          this.router.navigate([this.frontendURL + newUUID]);
-        }
-      });
+  getPageUrl(dso: TDomain): string {
+    return this.router.url;
   }
 }
diff --git a/src/app/shared/mocks/mock-auth.service.ts b/src/app/shared/mocks/mock-auth.service.ts
index 6258e4aa21a570bd60ce2ae3be1d79746be323dc..a168ffd8e5230528719ab945abb95172895176fe 100644
--- a/src/app/shared/mocks/mock-auth.service.ts
+++ b/src/app/shared/mocks/mock-auth.service.ts
@@ -3,4 +3,7 @@ export class AuthServiceMock {
   public checksAuthenticationToken() {
     return
   }
+  public buildAuthHeader() {
+    return 'auth-header';
+  }
 }
diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts
index 85d001286d2c5bd3db184b74d2017373e75e6d7f..eb73514d7612bd40c10494726000ee61db4c9625 100644
--- a/src/app/shared/shared.module.ts
+++ b/src/app/shared/shared.module.ts
@@ -154,6 +154,8 @@ import { DsDynamicDisabledComponent } from './form/builder/ds-dynamic-form-ui/mo
 import { DsDynamicLookupRelationSearchTabComponent } from './form/builder/ds-dynamic-form-ui/relation-lookup-modal/search-tab/dynamic-lookup-relation-search-tab.component';
 import { DsDynamicLookupRelationSelectionTabComponent } from './form/builder/ds-dynamic-form-ui/relation-lookup-modal/selection-tab/dynamic-lookup-relation-selection-tab.component';
 import { PageSizeSelectorComponent } from './page-size-selector/page-size-selector.component';
+import { AbstractTrackableComponent } from './trackable/abstract-trackable.component';
+import { ComcolMetadataComponent } from './comcol-forms/edit-comcol-page/comcol-metadata/comcol-metadata.component';
 import { ItemSelectComponent } from './object-select/item-select/item-select.component';
 import { CollectionSelectComponent } from './object-select/collection-select/collection-select.component';
 import { FilterInputSuggestionsComponent } from './input-suggestions/filter-suggestions/filter-input-suggestions.component';
@@ -326,6 +328,8 @@ const COMPONENTS = [
   CollectionGridElementComponent,
   CommunityGridElementComponent,
   BrowseByComponent,
+  AbstractTrackableComponent,
+  ComcolMetadataComponent,
   ItemTypeBadgeComponent,
   ItemSelectComponent,
   CollectionSelectComponent,
@@ -402,6 +406,7 @@ const SHARED_ITEM_PAGE_COMPONENTS = [
 const PROVIDERS = [
   TruncatableService,
   MockAdminGuard,
+  AbstractTrackableComponent,
   {
     provide: DYNAMIC_FORM_CONTROL_MAP_FN,
     useValue: dsDynamicFormControlMapFn
diff --git a/src/app/shared/trackable/abstract-trackable.component.spec.ts b/src/app/shared/trackable/abstract-trackable.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..37550922630c7c9a5320ffc33e6ada3fbcb29562
--- /dev/null
+++ b/src/app/shared/trackable/abstract-trackable.component.spec.ts
@@ -0,0 +1,101 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { AbstractTrackableComponent } from './abstract-trackable.component';
+import { INotification, Notification } from '../notifications/models/notification.model';
+import { NotificationType } from '../notifications/models/notification-type';
+import { of as observableOf } from 'rxjs';
+import { TranslateModule } from '@ngx-translate/core';
+import { ObjectUpdatesService } from '../../core/data/object-updates/object-updates.service';
+import { NotificationsService } from '../notifications/notifications.service';
+import { NO_ERRORS_SCHEMA } from '@angular/core';
+import { TestScheduler } from 'rxjs/testing';
+import { getTestScheduler } from 'jasmine-marbles';
+
+describe('AbstractTrackableComponent', () => {
+  let comp: AbstractTrackableComponent;
+  let fixture: ComponentFixture<AbstractTrackableComponent>;
+  let objectUpdatesService;
+  let scheduler: TestScheduler;
+
+  const infoNotification: INotification = new Notification('id', NotificationType.Info, 'info');
+  const warningNotification: INotification = new Notification('id', NotificationType.Warning, 'warning');
+  const successNotification: INotification = new Notification('id', NotificationType.Success, 'success');
+
+  const notificationsService = jasmine.createSpyObj('notificationsService',
+    {
+      info: infoNotification,
+      warning: warningNotification,
+      success: successNotification
+    }
+  );
+
+  const url = 'http://test-url.com/test-url';
+
+  beforeEach(async(() => {
+    objectUpdatesService = jasmine.createSpyObj('objectUpdatesService',
+      {
+        saveAddFieldUpdate: {},
+        discardFieldUpdates: {},
+        reinstateFieldUpdates: observableOf(true),
+        initialize: {},
+        hasUpdates: observableOf(true),
+        isReinstatable: observableOf(false), // should always return something --> its in ngOnInit
+        isValidPage: observableOf(true)
+      }
+    );
+
+    scheduler = getTestScheduler();
+
+    TestBed.configureTestingModule({
+      imports: [TranslateModule.forRoot()],
+      declarations: [AbstractTrackableComponent],
+      providers: [
+        {provide: ObjectUpdatesService, useValue: objectUpdatesService},
+        {provide: NotificationsService, useValue: notificationsService},
+      ], schemas: [
+        NO_ERRORS_SCHEMA
+      ]
+    }).compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(AbstractTrackableComponent);
+    comp = fixture.componentInstance;
+    comp.url = url;
+
+    fixture.detectChanges();
+  });
+
+  it('should discard object updates', () => {
+    comp.discard();
+
+    expect(objectUpdatesService.discardFieldUpdates).toHaveBeenCalledWith(url, infoNotification);
+  });
+  it('should undo the discard of object updates', () => {
+    comp.reinstate();
+
+    expect(objectUpdatesService.reinstateFieldUpdates).toHaveBeenCalledWith(url);
+  });
+
+  describe('isReinstatable', () => {
+    beforeEach(() => {
+      objectUpdatesService.isReinstatable.and.returnValue(observableOf(true));
+    });
+
+    it('should return an observable that emits true', () => {
+      const expected = '(a|)';
+      scheduler.expectObservable(comp.isReinstatable()).toBe(expected, {a: true});
+    });
+  });
+
+  describe('hasChanges', () => {
+    beforeEach(() => {
+      objectUpdatesService.hasUpdates.and.returnValue(observableOf(true));
+    });
+
+    it('should return an observable that emits true', () => {
+      const expected = '(a|)';
+      scheduler.expectObservable(comp.hasChanges()).toBe(expected, {a: true});
+    });
+  });
+
+});
diff --git a/src/app/shared/trackable/abstract-trackable.component.ts b/src/app/shared/trackable/abstract-trackable.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..cd1b425f1058387864523fd35747aac4aa257340
--- /dev/null
+++ b/src/app/shared/trackable/abstract-trackable.component.ts
@@ -0,0 +1,78 @@
+import { ObjectUpdatesService } from '../../core/data/object-updates/object-updates.service';
+import { NotificationsService } from '../notifications/notifications.service';
+import { TranslateService } from '@ngx-translate/core';
+import { Observable } from 'rxjs';
+import { Component } from '@angular/core';
+
+/**
+ * Abstract Component that is able to track changes made in the inheriting component using the ObjectUpdateService
+ */
+@Component({
+  selector: 'ds-abstract-trackable',
+  template: ''
+})
+export class AbstractTrackableComponent {
+
+  /**
+   * The time span for being able to undo discarding changes
+   */
+  public discardTimeOut: number;
+  public message: string;
+  public url: string;
+  public notificationsPrefix = 'static-pages.form.notification';
+
+  constructor(
+    public objectUpdatesService: ObjectUpdatesService,
+    public notificationsService: NotificationsService,
+    public translateService: TranslateService,
+  ) {
+
+  }
+
+  /**
+   * Request the object updates service to discard all current changes to this item
+   * Shows a notification to remind the user that they can undo this
+   */
+  discard() {
+    const undoNotification = this.notificationsService.info(this.getNotificationTitle('discarded'), this.getNotificationContent('discarded'), {timeOut: this.discardTimeOut});
+    this.objectUpdatesService.discardFieldUpdates(this.url, undoNotification);
+  }
+
+  /**
+   * Request the object updates service to undo discarding all changes to this item
+   */
+  reinstate() {
+    this.objectUpdatesService.reinstateFieldUpdates(this.url);
+  }
+
+  /**
+   * Checks whether or not the object is currently reinstatable
+   */
+  isReinstatable(): Observable<boolean> {
+    return this.objectUpdatesService.isReinstatable(this.url);
+  }
+
+  /**
+   * Checks whether or not there are currently updates for this object
+   */
+  hasChanges(): Observable<boolean> {
+    return this.objectUpdatesService.hasUpdates(this.url);
+  }
+
+  /**
+   * Get translated notification title
+   * @param key
+   */
+  private getNotificationTitle(key: string) {
+    return this.translateService.instant(this.notificationsPrefix + key + '.title');
+  }
+
+  /**
+   * Get translated notification content
+   * @param key
+   */
+  private getNotificationContent(key: string) {
+    return this.translateService.instant(this.notificationsPrefix + key + '.content');
+
+  }
+}
diff --git a/src/app/shared/uploader/uploader-options.model.ts b/src/app/shared/uploader/uploader-options.model.ts
index 0bd6412b1777934e56ab3c261484ea95a80b7cb9..f195b0930e5ecbef072d9bd469dad44348be291c 100644
--- a/src/app/shared/uploader/uploader-options.model.ts
+++ b/src/app/shared/uploader/uploader-options.model.ts
@@ -1,3 +1,4 @@
+import { RestRequestMethod } from '../../core/data/rest-request-method';
 
 export class UploaderOptions {
   /**
@@ -9,5 +10,15 @@ export class UploaderOptions {
 
   disableMultipart = false;
 
-  itemAlias: string;
+  itemAlias: string = null;
+
+  /**
+   * Automatically send out an upload request when adding files
+   */
+  autoUpload = true;
+
+  /**
+   * The request method to use for the file upload request
+   */
+  method: RestRequestMethod = RestRequestMethod.POST;
 }
diff --git a/src/app/shared/uploader/uploader.component.html b/src/app/shared/uploader/uploader.component.html
index 9d994313c6a64b37bb773c75807f959a652e80ff..36078fbeb4308f170dc8b5e0b3c87009224ffec5 100644
--- a/src/app/shared/uploader/uploader.component.html
+++ b/src/app/shared/uploader/uploader.component.html
@@ -19,23 +19,24 @@
          (fileOver)="fileOverBase($event)"
          class="well ds-base-drop-zone mt-1 mb-3 text-muted">
       <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  />
-            {{'uploader.browse' | translate}}
-          </label>
-        </span>
+        <span><i class="fas fa-cloud-upload" aria-hidden="true"></i> {{dropMsg | translate}} {{'uploader.or' | translate}}</span>
+        <label class="btn btn-link m-0 p-0 ml-1">
+          <input class="d-none" type="file" ng2FileSelect [uploader]="uploader" multiple  />
+          {{'uploader.browse' | translate}}
+        </label>
       </p>
       <div *ngIf="(isOverBaseDropZone | async) || uploader?.queue?.length !== 0">
         <div class="m-1">
           <div class="upload-item-top">
-            <span class="filename">{{'uploader.queue-length' | translate}}: {{ uploader?.queue?.length }} | {{ uploader?.queue[0]?.file.name }}</span>
+            <span class="filename">
+              <span *ngIf="!uploader.options.disableMultipart">{{'uploader.queue-length' | translate}}: {{ uploader?.queue?.length }} | </span>{{ uploader?.queue[0]?.file.name }}
+            </span>
             <div class="btn-group btn-group-sm float-right" role="group">
               <button type="button" class="btn btn-danger" (click)="uploader.clearQueue()" [disabled]="!uploader.queue.length">
                 <i class="fas fa-trash" aria-hidden="true"></i>
               </button>
             </div>
-            <span *ngIf="uploader.progress < 100" class="float-right mr-3">{{ uploader.progress }}%</span>
+            <span *ngIf="uploader.progress < 100 && !(uploader.progress === 0 && !uploader.options.autoUpload)" class="float-right mr-3">{{ uploader.progress }}%</span>
             <span *ngIf="uploader.progress === 100" class="float-right mr-3">{{'uploader.processing' | translate}}...</span>
           </div>
           <div class="ds-base-drop-zone-progress clearfix mt-2">
diff --git a/src/app/shared/uploader/uploader.component.spec.ts b/src/app/shared/uploader/uploader.component.spec.ts
index a36bd7241bb02827b30f803f4d3823d88601f7d2..dcdac911bfa722ae5fab1ad144281aa92616f54c 100644
--- a/src/app/shared/uploader/uploader.component.spec.ts
+++ b/src/app/shared/uploader/uploader.component.spec.ts
@@ -64,12 +64,12 @@ describe('Chips component', () => {
   template: ``
 })
 class TestComponent {
-  public uploadFilesOptions: UploaderOptions = {
+  public uploadFilesOptions: UploaderOptions = Object.assign(new UploaderOptions(), {
     url: 'http://test',
     authToken: null,
     disableMultipart: false,
     itemAlias: null
-  };
+  });
 
   /* tslint:disable:no-empty */
   public onBeforeUpload = () => {
diff --git a/src/app/shared/uploader/uploader.component.ts b/src/app/shared/uploader/uploader.component.ts
index ad52f4a93f9f6446a884c41c47f32a126a80cb07..935d196d0848c18aef0243d45909f9fce1c1df7f 100644
--- a/src/app/shared/uploader/uploader.component.ts
+++ b/src/app/shared/uploader/uploader.component.ts
@@ -95,7 +95,8 @@ export class UploaderComponent {
       disableMultipart: this.uploadFilesOptions.disableMultipart,
       itemAlias: this.uploadFilesOptions.itemAlias,
       removeAfterUpload: true,
-      autoUpload: true
+      autoUpload: this.uploadFilesOptions.autoUpload,
+      method: this.uploadFilesOptions.method
     });
 
     if (isUndefined(this.enableDragOverDocument)) {
@@ -117,7 +118,10 @@ export class UploaderComponent {
     if (isUndefined(this.onBeforeUpload)) {
       this.onBeforeUpload = () => {return};
     }
-    this.uploader.onBeforeUploadItem = () => {
+    this.uploader.onBeforeUploadItem = (item) => {
+      if (item.url !== this.uploader.options.url) {
+        item.url = this.uploader.options.url;
+      }
       this.onBeforeUpload();
       this.isOverDocumentDropZone = observableOf(false);
 
diff --git a/src/app/submission/form/submission-form.component.ts b/src/app/submission/form/submission-form.component.ts
index 1732075bf83978695cf87510f861997c27abd4f9..3ea07f9ae76220218e9235e79be5675c47eae650 100644
--- a/src/app/submission/form/submission-form.component.ts
+++ b/src/app/submission/form/submission-form.component.ts
@@ -77,12 +77,7 @@ export class SubmissionFormComponent implements OnChanges, OnDestroy {
    * The uploader configuration options
    * @type {UploaderOptions}
    */
-  public uploadFilesOptions: UploaderOptions = {
-    url: '',
-    authToken: null,
-    disableMultipart: false,
-    itemAlias: null
-  };
+  public uploadFilesOptions: UploaderOptions = new UploaderOptions();
 
   /**
    * A boolean representing if component is active
diff --git a/src/app/submission/form/submission-upload-files/submission-upload-files.component.spec.ts b/src/app/submission/form/submission-upload-files/submission-upload-files.component.spec.ts
index 60a572df54fdc2ad6bfbb6ce21f578a9b4231c80..34d291f0e469e8d06098d00908cd421e29d0b0b3 100644
--- a/src/app/submission/form/submission-upload-files/submission-upload-files.component.spec.ts
+++ b/src/app/submission/form/submission-upload-files/submission-upload-files.component.spec.ts
@@ -28,6 +28,7 @@ import { SubmissionJsonPatchOperationsServiceStub } from '../../../shared/testin
 import { SubmissionJsonPatchOperationsService } from '../../../core/submission/submission-json-patch-operations.service';
 import { SharedModule } from '../../../shared/shared.module';
 import { createTestComponent } from '../../../shared/testing/utils';
+import { UploaderOptions } from '../../../shared/uploader/uploader-options.model';
 
 describe('SubmissionUploadFilesComponent Component', () => {
 
@@ -112,12 +113,12 @@ describe('SubmissionUploadFilesComponent Component', () => {
       comp.submissionId = submissionId;
       comp.collectionId = collectionId;
       comp.sectionId = 'upload';
-      comp.uploadFilesOptions = {
+      comp.uploadFilesOptions = Object.assign(new UploaderOptions(),{
         url: '',
         authToken: null,
         disableMultipart: false,
         itemAlias: null
-      };
+      });
 
     });
 
@@ -208,11 +209,11 @@ class TestComponent {
   submissionId = mockSubmissionId;
   collectionId = mockSubmissionCollectionId;
   sectionId = 'upload';
-  uploadFilesOptions = {
+  uploadFilesOptions = Object.assign(new UploaderOptions(), {
     url: '',
     authToken: null,
     disableMultipart: false,
     itemAlias: null
-  };
+  });
 
 }